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

Is Babel configured differently in this TypeScript version of react-scripts? Babel plugin isn't running. #394

Closed
timjohnson-valtech opened this issue Sep 4, 2018 · 4 comments

Comments

@timjohnson-valtech
Copy link

I'm trying to get the Babel plugin for Emotion, babel-plugin-emotion, working with this TypeScript version of create-react-app. I'm thinking that the plugin doesn't run at all because Babel is configured differently here compared to the regular version of create-react-app. I've tried using react-app-rewired like I usually do and I've also tried adding the configuration for the plugin manually after ejecting too and it doesn't seem to run. I've tried the same steps but with the regular version of create-react-app and it all works fine.

Am I correct and it should be expected that Babel plugins don't get used in the latest version of the TypeScript version of create-react-app? If so, is there a way to enable that feature of Babel?

@elektronik2k5
Copy link

Am I correct and it should be expected that Babel plugins don't get used in the latest version of the TypeScript version of create-react-app? If so, is there a way to enable that feature of Babel?

That's correct. Babel isn't used in this project at all. You have two options, both require you to either eject or use react-app-rewired:

  1. Use new newly released Babel 7, which supports compiling (but not type checking) TypeScript.
  2. Run babel-loader after ts-loader

Another option for your particular problem (compatibility with Emotion) is to use styled-components instead. Styled is practically identical, but doesn't have source maps :(.
The TS version of a babel plugin is a transform: typescript-plugin-styled-components. While this avoids adding babel into the pipeline, you'll still need to either eject or rewire in order to add the TypeScript transformer via ts-loader.

@timjohnson-valtech
Copy link
Author

Thanks a lot for that info. I tried the second option that you suggested (run babel-loader before ts-loader), but I still can't get it to work.

config-overrides.js for react-app-rewired:

const { injectBabelPlugin } = require('react-app-rewired');

module.exports = function override(config, env) {

  const babelLoader = config.module.rules[1].oneOf[1];

  // remove babel-loader item
  config.module.rules[1].oneOf.splice(1, 1);

  // add babel-loader item back into the array, before ts-loader
  config.module.rules[1].oneOf.splice(2, 0, babelLoader);

  // Emotion babel plugin
  config = injectBabelPlugin(
    [
      'emotion',
      {
        autoLabel: true,
        sourceMap: true,
      },
    ],
    config
  );

  return config;
};

Any idea why that didn't work? I noticed in my ejected version of create-react-app-typescript that I don't have babel-core and some other packages for babel that I do see in my ejected version of create-react-app. Do those need to be installed too to get babel working like it does in create-react-app?

@ShaneMckenna23
Copy link

ShaneMckenna23 commented Sep 10, 2018

Hey Tim,

The above code doesn't work because of the webpack rule oneOf and the test regex. Babel loader by deafult only runs on js|jsx|mjs files while ts-loader by default only runs on ts|tsx files. Since these loaders are in the oneOf section it runs the first matching case. We want our webpack config to look like the one below.

{
  test: /\.(js|jsx|mjs|ts|tsx)$/,
  include: paths.appSrc,
  use: [
    {
      loader: require.resolve('babel-loader'),
      options: {
        plugins: ['emotion'],
        compact: true,
      },
    },
    {
      loader: require.resolve('ts-loader'),
      options: {
        // disable type checker - we will use it in fork plugin
        transpileOnly: true,
      },
    }
  ]
}

I created this config-override.js to inject the above.

const resolve = require("path").resolve;
const {
  injectBabelPlugin,
  getBabelLoader,
  getLoader,
  loaderNameMatches
} = require("react-app-rewired");

module.exports = function override(config, env) {

  const removeLoader = (rules, loader) => {
    const LoaderMatcher = function(rule) {
      return loaderNameMatches(rule, loader);
    };
    let loaderObj = getLoader(rules, LoaderMatcher)

    const removeLoaderRecursive = (rules) => {
      rules.forEach((rule,index) => {
        if(rule.loader){
          if(loaderObj.loader == rule.loader){
            rules.splice(index,1)
          }
        }else if(rule.use){
          removeLoaderRecursive(rule.use)
        }else if(rule.oneOf){
          removeLoaderRecursive(rule.oneOf)
        }
      })
    };

    removeLoaderRecursive(rules)
  }

  //remove default ts-loader
  removeLoader(config.module.rules, "ts-loader");
  
  // Emotion babel plugin
  config = injectBabelPlugin(
    [
      "emotion",
      {
        autoLabel: true,
        sourceMap: true
      }
    ],
    config
  );

  //Get babelLoader and modify to use on all filetypes and run ts-loader afterwards.
  let babelLoader = getBabelLoader(config.module.rules);
  babelLoader.test = /\.(js|jsx|mjs|ts|tsx)$/;
  babelLoader.use = [
    {
      loader: babelLoader.loader,
      options: babelLoader.options
    },
    {
      loader: require.resolve("ts-loader"),
      options: {
        // disable type checker - we will use it in fork plugin
        transpileOnly: true
      }
    }
  ];
  delete babelLoader.loader;
  delete babelLoader.options;

  return config;
};

I added this file to tslint exclude so there could be some lint errors. I opened a pull request on react-app-rewire to add the removeLoader function, seems like it would be useful. I might try make a package for the above during the week and try get it added to the community section, something like react-app-rewire-typescript-emotion.

@timjohnson-valtech
Copy link
Author

Thanks Shane that works!

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

3 participants