Migrating Babel to swc

Posted at Sun Jan 15 2023

If you happen to use meta-frameworks like Next.js or such, this post might not be too useful for you, feel free to skip!

I provided a basic starter for you to play with here: playground-react-webpack

Hi, I'd like to share my recent experience about migrating Babel to swc on professional medium-scale project.

Initial

If you used Babel for transpiling a web app, it's high chance that you also use Webpack and babel-loader as well.

To start migrating to swc, you can uninstall all babel's related packages, and install these swc packages:

npm i -D @swc/cli @swc/core swc-loader
  "@swc/cli": "0.1.59",
  "@swc/core": "^1.3.26",
  "swc-loader": "0.2.3",

With these packages alone, it already can support TypeScript + TSX, React automatic runtime, all just by defining them on .swcrc file, though I'll use config object on swc-loader directly.

Config

Now I can go ahead to remove any babel config, usually babel.config.js or .babelrc. After that, add swc config on your swc-loader, replacing the babel-loader place!

Also add TerserPlugin.swcMinify on your TerserPlugin initialization:

module.exports = {
  optimization: {
    minimizer: [
      new TerserPlugin({
        minify: TerserPlugin.swcMinify
      })
    ]
  },
  module: {
    rules: [
      {
        test: /\.[tj]sx?$/,
        exclude: /node_modules/,
        use: {
          loader: "swc-loader",
          options: {
            module: {
              type: "es6"
            },
            minify: process.env.NODE_ENV !== "development",
            isModule: true,
            jsc: {
              minify: {
                compress: true
                mangle: true,
                format: {
                  asciiOnly: true,
                  comments: /^ webpack/
                }
              },
              target: "es2016",
              parser: {
                syntax: "typescript",
                tsx: true
              },
              transform: {
                react: {
                  runtime: "automatic"
                }
              }
            }
          }
        }
      }
    ]
  }
};

The swcMinify part is very important, otherwise you'll end up with bigger bundle size than Babel!

You might be wondering what this line is for:

  comments: /^ webpack/

It's used to ignore JS comment removal during build, since some comments are required for library like @loadable/component. If you used @loadable/component, you absolutely need this!

... and that's all! You can try to boot up your development server to see if anything works as expected. Also try to run your production build as well.

You can also check the pull request example here: https://github.com/antonybudianto/playground-react-webpack/pull/1.

The initial migration is not too difficult so far, the challenge comes when you have used babel plugins and even harder if you code your own custom babel-plugin or presets.

Babel plugin or presets

If you have used any babel plugins/presets, then you cannot use them on swc, since swc now used Rust + WASM based plugin.

However, common babel plugins/presets like env, react, typescript are already supported by default, and some popular third-party plugins already supported:

  • emotion
  • jest
  • loadable-components

You can see the complete list here

Custom babel plugins or presets

If you made your own babel plugins/presets, you cannot use them directly as expected. You have to rewrite them with Rust language.

I'll try to cover about this in upcoming blog. Stay tuned!

Benchmark result

For hello world example, it saves build time from ~1.5s to just ~287ms.

For medium-scale project, it can saves from ~120s to just ~20s. It's about 6x faster! The larger the codebase, we can see the gap even better.

Conclusion

The migration process was mostly seamless, unless you used many babel plugins, especially custom made ones.

Thank you for reading! See you on another blog post.

Avatar of Antony

Antony Budianto

Software Engineering, Web, and some random life thoughts.