Builtin swc-loader

builtin:swc-loader is the Rust version of swc-loader, aiming to deliver better performance. The Loader's configuration is aligned with the JS version of swc-loader.

Example

If you need to use builtin:swc-loader in your project, configure it as follows:

To transpile ts files:

module.exports = {
  module: {
    rules: [
      {
        test: /\.ts$/,
        exclude: [/node_modules/],
        loader: 'builtin:swc-loader',
        options: {
          jsc: {
            parser: {
              syntax: 'typescript',
            },
          },
        },
        type: 'javascript/auto',
      },
    ],
  },
};

Or to transpile jsx files:

module.exports = {
  module: {
    rules: [
      {
        test: /\.jsx$/,
        use: {
          loader: 'builtin:swc-loader',
          options: {
            jsc: {
              parser: {
                syntax: 'ecmascript',
                jsx: true,
              },
              transform: {
                react: {
                  pragma: 'React.createElement',
                  pragmaFrag: 'React.Fragment',
                  throwIfNamespace: true,
                  development: false,
                  useBuiltins: false,
                },
              },
            },
          },
        },
        type: 'javascript/auto',
      },
    ],
  },
};

Additionally, you can directly refer to example-builtin-swc-loader for more usage guidelines.

Type declaration

You can enable type hints using the SwcLoaderOptions type exported by @rspack/core:

  • rspack.config.js:
module.exports = {
  module: {
    rules: [
      {
        test: /\.js$/,
        use: {
          loader: 'builtin:swc-loader',
          /** @type {import('@rspack/core').SwcLoaderOptions} */
          options: {
            // some options
          },
        },
      },
    ],
  },
};
  • rspack.config.ts:
import type { SwcLoaderOptions } from '@rspack/core';

export default {
  module: {
    rules: [
      {
        test: /\.js$/,
        use: {
          loader: 'builtin:swc-loader',
          options: {
            // some options
          } satisfies SwcLoaderOptions,
        },
      },
    ],
  },
};

Options

The following is an introduction to some SWC configurations and Rspack specific configurations. Please refer to the SWC Configurations for the complete options.

jsc.experimental.plugins

Added in v0.5.8
Stability: Experimental
WARNING

The wasm plugin is deeply coupled with the version of SWC, you need to choose a wasm plugin that is compatible with the corresponding version of SWC in order to function normally. you can see more compatible info about how to choose right wasm plugin version in selecting-swc-core.

Rspack supports load wasm plugin in builtin:swc-loader, you can specify the plugin name like

module.exports = {
  module: {
    rules: [
      {
        test: /\.js$/,
        use: {
          loader: 'builtin:swc-loader',
          options: {
            jsc: {
              experimental: {
                plugins: [
                  [
                    '@swc/plugin-remove-console',
                    {
                      exclude: ['error'],
                    },
                  ],
                ],
              },
            },
          },
        },
      },
    ],
  },
};

this is an example of wasm plugin usage.

rspackExperiments

Added in v0.4.0
Stability: Experimental

Experimental features provided by rspack.

rspackExperiments.import

Added in v0.3.4
Stability: Experimental

Ported from babel-plugin-import, configurations are basically the same.

Function can't be used in configurations, such as customName, customStyleName, they will cause some performance overhead as these functions must be called from Rust , inspired by modularize_imports, some simple function can be replaced by template string instead. Therefore, the function type configuration such as customName, customStyleName can be passed in strings as templates to replace functions and improve performance.

For example:

import { MyButton as Btn } from 'foo';

Apply following configurations:

rspack.config.js
module.exports = {
  module: {
    rules: [
      {
        use: 'builtin:swc-loader',
        options: {
          ...
          rspackExperiments: {
            import: [{
               libraryName: 'foo',
               customName: 'foo/es/{{ member }}',
            }]
          }
        }
      }
    ]
  }
};

{{ member }} will be replaced by the imported specifier:

import Btn from 'foo/es/MyButton';

Template customName: 'foo/es/{{ member }}' is the same as customName: (member) => `foo/es/${member}` , but template string has no performance overhead of Node-API.

The template used here is handlebars. There are some useful builtin helpers, Take the above import statement as an example:

rspack.config.js
module.exports = {
  module: {
    rules: [
      {
        use: 'builtin:swc-loader',
        options: {
          ...
          rspackExperiments: {
            import: [{
               libraryName: 'foo',
               customName: 'foo/es/{{ kebabCase member }}',
            }]
          }
        }
      }
    ]
  }
};

Transformed to:

import Btn from 'foo/es/my-button';

In addition to kebabCase, there are camelCase, snakeCase, upperCase, lowerCase and legacyKebabCase/legacySnakeCase can be used as well.

The legacyKebabCase/legacySnakeCase works as babel-plugin-import versions before 1.13.7.

You can check the document of babel-plugin-import for other configurations.

Taking the classic 4.x version of ant-design as an example, we only need to configure it as follows:

rspack.config.js
module.exports = {
  module: {
    rules: [
      {
        use: 'builtin:swc-loader',
        options: {
          ...
          rspackExperiments: {
            import: [
              {
                libraryName: 'antd',
                style: '{{member}}/style/index.css',
            },
            ]
          }
        }
      }
    ]
  }
};

The above configuration will transform import { Button } from 'antd'; to:

import Button from 'antd/es/button';
import 'antd/es/button/style/index.css';

Then you can see the style file is automatically imported and applied on the page.

Of course, if you have already configured support for less, you can simply use the following configuration:

rspack.config.js
module.exports = {
  module: {
    rules: [
      {
        use: 'builtin:swc-loader',
        options: {
          ...
          rspackExperiments: {
            import: [
              {
                libraryName: 'antd',
                style: true,
            },
            ]
          }
        }
      }
    ]
  }
};

The above configuration will transform import { Button } from 'antd'; to:

import Button from 'antd/es/button';
import 'antd/es/button/style';

rspackExperiments.relay

Added in v0.3.4
Stability: Experimental

Converted graphql call expression to corresponding require call expression.

  • Type:
export type RelayOptions =
  | undefined
  | boolean
  | {
      artifactDirectory?: string;
      language: 'javascript' | 'typescript' | 'flow'; // Default to 'javascript'
    };
  • Default: undefined

When this configuration is set to true, Rspack will attempt to locate relay.config.json, relay.config.js, and package.json files under the context directory in order, as detailed in the relay-compiler documentation. If none of these files are found, Rspack will use the default configuration. If specific configuration is passed, Rspack will not attempt to locate any relay config file, but use the received configuration directly.

rspackExperiments.emotion

Added in v0.3.4
Stability: Experimental

Using optimization for emotion in compile-time.

  • Type:
type EmotionOptions =
  | boolean
  | {
      sourceMap?: boolean;
      autoLabel?: 'never' | 'dev-only' | 'always';
      labelFormat?: string;
      importMap?: {
        [packageName: string]: {
          [exportName: string]: {
            canonicalImport?: [string, string];
          };
        };
      };
    };
  • Default: false

If you are using emotion in your project, and want functionality which is similar to @emotion/babel-plugin, you can enable rspackExperiments.emotion option, Rspack will use swc_emotion plugin to transpile your emotion code.

You can enable default options by specify rspackExperiments.emotion: true.

rspack.config.js
module.exports = {
  module: {
    rules: [
      {
        use: 'builtin:swc-loader',
        options: {
          ...
          rspackExperiments: {
            emotion: true
          }
        }
      }
    ]
  }
};

Each field name is the same with @emotion/babel-plugin, more detailed explanation (content below is from the official document of @emotion/babel-plugin):

autoLabel

  • Type: 'never' | 'dev-only' | 'always'
  • Default: 'dev-only'

This option is used for:

  • Automatically adds the label property to styles so that class names generated by css or styled include the name of the variable the result is assigned to.
  • Please note that non word characters in the variable will be removed (Eg. iconStyles$1 will become iconStyles1) because $ is not validCSS ClassName Selector.

Each possible value for this option produces different output code:

  • 'dev-only' - we optimize the production code, so there are no labels added there, but at the same time we keep labels for development environments,
  • 'always' - we always add labels when possible,
  • 'never' - we disable this entirely and no labels are added.

labelFormat

  • Type: string
  • Default: "[local]"

This option only works when 'autoLabel' is set to 'dev-only' or 'always'. It allows you to define the format of the resulting label. The format is defined via string where variable parts are enclosed in square brackets []. For example labelFormat: "my-classname--[local]", where '[local]' will be replaced with the name of the variable the result is assigned to.

Allowed values:

  • '[local]' - the name of the variable the result of the css or styled expression is assigned to.
  • '[filename]' - name of the file (without extension) where css or styled expression is located.
  • '[dirname]' - name of the directory containing the file where css or styled expression is located.

This format only affects the label property of the expression, meaning that the css prefix and hash will be prepended automatically.

sourceMap

  • Type: boolean

Whether to inject source maps. By default, this will be disabled in production mode, and enabled in development mode.

importMap

  • Type:
export type ImportMap =
  | undefined
  | {
      [packageName: string]: {
        [exportName: string]: {
          canonicalImport?: [string, string];
        };
      };
    };
  • Default: undefined

This option allows you to tell Rspack what imports it should look at to determine what it should transform so if you re-export Emotion's exports, you can still use the Babel transforms

rspackExperiments.styledComponents

Added in v0.3.7
Stability: Experimental
  • Type:
type StyledComponentsOptions = {
  displayName?: boolean;
  ssr?: boolean;
  fileName?: boolean;
  meaninglessFileNames?: string[];
  namespace?: string;
  topLevelImportPaths?: string[];
  transpileTemplateLiterals?: boolean;
  minify?: boolean;
  pure?: boolean;
  cssProps?: boolean;
};
  • Default: {}

Configure the compile-time support for styled-components. SWC's support for styled-components is essentially aligned with the official Babel plugin, so you can visit https://styled-components.com/docs/tooling#babel-plugin for more information.

Usually, you need to enable the JSX support of builtin:swc-loader to handle React components. The configuration may look like this:

/** @type {import('@rspack/core').Configuration}*/
module.exports = {
  module: {
    rules: [
      {
        test: /\.jsx?$/,
        loader: 'builtin:swc-loader',
        options: {
          jsc: {
            parser: {
              syntax: 'ecmascript',
              jsx: true,
            },
          },
          rspackExperiments: {
            styledComponents: {
              displayName: true,
              ssr: true,
              fileName: true,
              meaninglessFileNames: ['index', 'styles'],
              namespace: 'rspack-test',
              topLevelImportPaths: [
                '@xstyled/styled-components',
                '@xstyled/styled-components/*',
              ],
              transpileTemplateLiterals: true,
              minify: true,
              pure: true,
              cssProps: true,
            },
          },
        },
      },
    ],
  },
};