Skip to content
This repository has been archived by the owner on Nov 6, 2019. It is now read-only.

Really want to use this but can't figure out how #29

Open
martinmckenna opened this issue Oct 3, 2018 · 10 comments
Open

Really want to use this but can't figure out how #29

martinmckenna opened this issue Oct 3, 2018 · 10 comments

Comments

@martinmckenna
Copy link

martinmckenna commented Oct 3, 2018

I'm using webpack with ts-loader and I'd like to extract my react-intl strings to JSON files.

But I can't figure out how to get this plugin working correctly. I've tried looking at other issues and still can't find any help.

For starters, how do I tell this plugin where to create the JSON file? Also, how am I supposed to be using the onMsgExtracted property

This is what I've tried so far:

webpack.config

          {
            test: /\.(ts|tsx)$/,
            include: paths.appSrc,
            use: [
              {
                loader: require.resolve('ts-loader'),
                options: {
                  // disable type checker - we will use it in fork plugin
                  transpileOnly: true,
                  getCustomTransformers: () => ({
                    before: [
                      intl.transform({
                        idPrefix: "prefix",
                        onMsgExtracted: intl.aggregate({}),
                      }),
                    ]
                  })
                },
              },
            ],
          },

MyComponent.tsx

import { FormattedMessage } from 'react-intl';

export class SummaryPanel extends React.Component<Props, {}> {
  render() {
    return (
      <React.Fragment>
        <FormattedMessage
          defaultMessage="Contact Information"
          id="ContactInfo.heading"
        />
      </React.Fragment>
    );
  }
}
@longlho
Copy link
Owner

longlho commented Oct 3, 2018

I'll try to whip up an example tonight or tomorrow. To unblock you right now, for intl.aggregrate(msgs), msgs is just a result object you declare and everytime it sees an translation it'll modify the object inline.

The tricky part is to write out that result object to a file once webpack's done.

@martinmckenna
Copy link
Author

@longlho ah okay. So aggregate() works similiarly to array.reduce in that you pass it a accumulator? Or do you mean something different by “result object?”

Also thank you for working on an example. I’m very much looking to accomplish the functionality of babel-plugin-react-intl, and am hoping this lib will make things easier

@longlho
Copy link
Owner

longlho commented Oct 4, 2018 via email

@martinmckenna
Copy link
Author

martinmckenna commented Oct 4, 2018

Been trying some other attempts. Most recent failed attempt was to abstract the transformer into my own file:

webpack.config.js

              {
                loader: require.resolve('ts-loader'),
                options: {
                  // disable type checker - we will use it in fork plugin
                  transpileOnly: true,
                  getCustomTransformers: () => ({
                    before: [mergeMessages]
                  })
                },
              },

merge_messages.js

var fs = require("fs");
var ts = require('typescript');
var mkdirpSync = require("mkdirp").sync;
var intl = require('ts-transform-react-intl');

const outputDir = './build/';

var transformer = function (context) {
  console.log('running script');

  // aggregate all messages into one big object
  var messages = {};

  var intlTransformer = intl.transform({
    idPrefix: "prefix",
    onMsgExtracted: intl.aggregate(messages),
  });

  intlTransformer();

  console.log(messages);

  // Create a new directory that we want to write the aggregate messages to
  mkdirpSync(outputDir);
  // Write the messages to this directory
  fs.writeFileSync('data.json', `{ "en": ${JSON.stringify(messages, null, 2)} }`);
}

exports["default"] = transformer;

But the build errors out with Module build failed: TypeError: f is not a function

@longlho
Copy link
Owner

longlho commented Oct 4, 2018

yeah that's not gonna work. onMsgExtracted gets called on every file. What you need is at the end of the whole webpack flow, write the accumulated map out.

I initially did this w/ typescript compiler wrapper because it's synchronous & just run through the whole project. webpack is not synchronous so you'd almost need a hook into its lifecycle to know when it's done to write the file out. This is not an easy task (similar to https://github.com/webpack-contrib/extract-text-webpack-plugin where all CSS gets extracted to a file).

Some pointer would be https://webpack.js.org/api/compilation-hooks/ or https://webpack.js.org/api/plugins/#custom-hooks

The flow for webpack would be something like:

// Declare this out of scope
const msgs = {}

webpack({
...
{
                loader: require.resolve('ts-loader'),
                options: {
                  // disable type checker - we will use it in fork plugin
                  transpileOnly: true,
                  getCustomTransformers: () => ({
                    before: [mergeMessages]
                  })
                },
              },
plugins: {
...<something that hooks on done & output msgs to a JSON file>
}
})

@longlho
Copy link
Owner

longlho commented Oct 4, 2018

Oh looks like webpack takes in a callback webpack(config, doneCb) so u can output in doneCb maybe?

@martinmckenna
Copy link
Author

those are some good thoughts. I'll try creating a plugin that can hook into after compile tonight

@martinmckenna
Copy link
Author

Looks like hooking into the callbacks didn't work either.

The messages object is always empty

GenerateMessagesFile.js

'use strict';
var fs = require('fs');
var mkdirp = require('mkdirp');

const outputPath = '../build/messages/';

class GenerateMessageFile {
  constructor(messageObj) {
    this.messageObj = messageObj;
  };

  apply(compiler) {
    compiler.plugin('done', (compilation) => {
      console.log(this.messageObj);

      // Create a new directory that we want to write the aggregate messages to
      mkdirp.sync(outputPath);

      // Write the messages to this directory
      fs.writeFileSync(outputPath + 'messages.json', `{ "en": ${JSON.stringify(this.messageObj, null, 2)} }`);

    })
  }
}

module.exports = GenerateMessageFile;

webpack.config.js

          {
            test: /\.(ts|tsx)$/,
            include: paths.appSrc,
            use: [
              {
                loader: require.resolve('ts-loader'),
                options: {
                  // disable type checker - we will use it in fork plugin
                  transpileOnly: true,
                  getCustomTransformers: () => ({
                    before: [
                      intl.transform({
                        idPrefix: "prefix",
                        onMsgExtracted: intl.aggregate(messages),
                      }),
                    ]
                  })
                },
              },
            ],
          },

  plugins: [
    new GenerateMessageFile(messages),
 ]

@martinmckenna
Copy link
Author

martinmckenna commented Oct 5, 2018

Ah okay so it seems like this plugin will not pick up the text if you’re solely using the react-intl React Component FormattedMessage and that you need to use the API function.

The issue i’m running into now is that the out-of-scope messages var is undefined by the time my webpack hook runs

Would you mind if i just made a PR to allow for passing in a file path to write to? I think that functionality would be widely useful

@longlho
Copy link
Owner

longlho commented Oct 5, 2018 via email

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants