Experiments

Enable and try out some experimental features.

  • Type: object

experiments.asyncWebAssembly

  • Type: boolean
  • Default: false

Support the new WebAssembly according to the updated specification, it makes a WebAssembly module an async module. And it is enabled by default when experiments.futureDefaults is set to true.

experiments.incrementalRebuild

Enable incremental rebuild. true will try to reuse the results of the last build when rebuild to improve build speed, supports configuration of different stages, the default is enabled.

0.5 removed this options and internally enabled this feature.

  • Type: boolean | { make?: boolean, emitAsset?: boolean }
  • Default: true

experiments.outputModule

  • Type: boolean
  • Default: true

Once enabled, Rspack will output ECMAScript module syntax whenever possible. For instance, import() to load chunks, ESM exports to expose chunk data, among others.

module.exports = {
  experiments: {
    outputModule: true,
  },
};

experiments.css

  • Type: boolean
  • Default: true

Once enabled, Rspack will enable native CSS support, and CSS related parser and generator options.

WARNING

Note that if you're using style-loader and css-loader, you should disable this option because style-loader and css-loader will conflict with native CSS.

experiments.futureDefaults

Use defaults of the next major Rspack and show warnings in any problematic places.

rspack.config.js
module.exports = {
  experiments: {
    futureDefaults: true,
  },
};

experiments.topLevelAwait

Enable support for top level await, top level await can only be used in modules with ModuleType is javascript/esm.

Enabled by default and can be turned off with this configuration.

experiments.lazyCompilation

  • Type:
type LazyCompilationOptions =
  | boolean
  | {
      /**
       * Enable lazy compilation for entries.
       */
      entries?: boolean;
      /**
       * Enable lazy compilation for dynamic imports.
       */
      imports?: boolean;
      /**
       * Specify which imported modules should be lazily compiled.
       */
      test?: RegExp | ((m: Module) => boolean);
    };
  • Default: false

Enable lazy compilation, which can greatly improve the dev startup performance of multi-page applications (MPA) or large single-page applications (SPA). For example, if you have twenty entry points, only the accessed entry points will be built. Or if there are many import() statements in the project, each module pointed to by import() will only be built when it is actually accessed.

If set to true, lazy compilation will be applied by default to both entry modules and modules pointed to by import(). You can decide whether it applies only to entries or only to import() through a configuration object. The entries option determines whether it applies to entries, while the import() option determines whether it applies to import().

rspack.config.js
const isDev = process.env.NODE_ENV === 'development';

module.exports = {
  experiments: {
    // only enabled in dev mode
    lazyCompilation: isDev,
  },
};

In addition, you can also configure a test parameter for more fine-grained control over which modules are lazily compiled. The test parameter can be a regular expression that matches only those modules that should be lazily compiled. It can also be a function where the input is of type 'Module' and returns a boolean value indicating whether it meets the criteria for lazy compilation logic.

TIP

The current lazy compilation aligns with the webpack implementation, and is still in the experimental stage. In some scenarios, lazy compilation might not work as expected, or the performance improvement may be insignificant.

Exclude HMR client

If you do not use Rspack's own dev server and instead use your own server as the dev server, you generally need to add another client modules in the entry configuration to enable capabilities such as HMR. It is best to exclude these client module from lazy compilation by configuring test.

If not excluded and lazy compilation of entry is enabled, this client will not be compiled when accessing the page for the first time, so an additional refresh is needed to make it take effect.

For example:

const rspack = require('@rspack/core');

const options = {
  experiments: {
    lazyCompilation: {
      test(module) {
        const isMyClient = module.nameForCondition().endsWith('dev-client.js');
        // make sure that dev-client.js won't be lazy compiled
        return !isMyClient;
      },
    },
  },
};
const compiler = rspack(options);

new compiler.webpack.EntryPlugin(compiler.context, 'dev-client.js', {
  // name: undefined means this is global entry
  name: undefined,
}).apply(compiler);

experiments.rspackFuture

  • Type: object

  • Default: See options down below for details

Used to control whether to enable Rspack future default options, check out the details here.

experiments.rspackFuture.disableTransformByDefault

  • Type: boolean

  • Introduced in Version: v0.3.5

  • Enabled by Default in Version: v0.4.0

  • Removed in Version: v0.5.0

  • Description:

    This feature primarily addresses three categories of problems: builtins code transformation features, target, and custom Rule.type.

    1. Removal of support for some builtins features:

    These configuration items should apply to the selected corresponding modules. Using builtins.(relay|react|emotion|pluginImport|decorator) is equivalent to enabling them globally. This approach is not recommended, so it will be deprecated.

    After enabling disableTransformByDefault, the aforementioned configuration items will become invalid. For a migration guide, refer to builtin:swc-loader/options.rspackExperiments.

    For instance, if we want the emotion transformation to apply to all jsx files:

    rspack.config.js
    module.exports = {
      module: {
        rules: [
          {
            test: /\.jsx$/,
            loader: 'builtin:swc-loader',
            options: {
              jsc: {
                parser: {
                  syntax: "ecmascript",
                  jsx: true
                }
              },
              rspackExperiments: {
                emotion: true // The same as `builtins`
              }
            }
            type: 'javascript/auto',
          },
        ],
      },
      experiments: {
        rspackFuture: {
          disableTransformByDefault: true
        }
      }
    };

    For the decorator configuration, you can find and replace in the SWC code, such as legacyDecorator, decoratorMetadata.

    1. target will not downgrade user-side code

    In previous versions, we typically set target: ["web", "es5"] to produce web-compatible and downgraded code. Now, when the disableTransformByDefault configuration item is enabled, the target will only be used to control runtime code (except for user-written code, Rspack generated code within node_modules, like chunk loading, etc.)

    This aligns with webpack's plugin-based design philosophy. You can migrate using builtin:swc-loader's env or target, and precisely control the modules that need to be transpiled:

    rspack.config.js
    module.exports = {
      module: {
        rules: [
          {
            test: /\.[cm]?js$/,
            exclude: /node_modules/,
            loader: 'builtin:swc-loader',
            options: {
              jsc: {
                parser: {
                  syntax: "ecmascript"
                },
                target: "es5" // Notice: `jsc.target` and `env` cannot be set at the same time.
              },
              env: { //  Notice: `jsc.target` and `env` cannot be set at the same time.
                targets: "chrome >= 48"
              }
            }
            type: 'javascript/auto',
          },
        ],
      },
      experiments: {
        rspackFuture: {
          disableTransformByDefault: true
        }
      }
    };

    Note: When disableTransformByDefault is not enabled, the rspack's built-in transpiler will transpile all content under node_modules. Therefore, after enabling disableTransformByDefault, with builtin:swc-loader combined with exclude: /node_modules/, ensure the code in node_modules has been downgraded; otherwise, compatibility issues may arise.

    1. Removed non-webpack compatible Rule.type

    These types have been removed:

    • "typescript"
    • "jsx"
    • "tsx"

    For JS-related types, only the following will be retained:

    • "javascript/auto"
    • "javascript/esm"
    • "javascript/dynamic"

    With disableTransformByDefault enabled, Rspack will only support input compliant with web standards, aligning with webpack's design philosophy.

    After discussions with webpack, both webpack and Rspack will offer more out-of-the-box configurations to support non-standard inputs like TypeScript.

    Since files with the extensions jsx, tsx, and ts are implicitly set for transpilation, an error will be prompted when disableTransformByDefault is enabled. You can migrate like this:

    rspack.config.js
    module.exports = {
      module: {
        rules: [
          {
            test: /\.ts$/,
            exclude: /node_modules/,
            loader: 'builtin:swc-loader',
            options: {
              jsc: {
                parser: {
                  syntax: "typescript"
                }
              }
            }
            type: 'javascript/auto',
          },
        ],
        {
          test: /\.tsx$/,
          exclude: /node_modules/,
          loader: 'builtin:swc-loader',
          options: {
            jsc: {
              parser: {
                syntax: "typescript",
                tsx: true
              }
            }
          }
          type: 'javascript/auto',
        },
        {
          test: /\.jsx$/,
          exclude: /node_modules/,
          loader: 'builtin:swc-loader',
          options: {
            jsc: {
              parser: {
                syntax: "ecmascript",
                jsx: true
              }
            }
          }
        }
      },
      experiments: {
        rspackFuture: {
          disableTransformByDefault: true
        }
      }
    };

experiments.rspackFuture.newResolver

  • Type: boolean
  • Introduced in Version: v0.3.7
  • Enabled by Default in Version: v0.4.0
  • Removed in Version: v0.5.0

This feature enables the new resolver implementation.

rspack.config.js
module.exports = {
  experiments: {
    rspackFuture: {
      newResolver: true,
    },
  },
};

The new resolver also supports tsconfig project references defined in tsconfig-paths-webpack-plugin. See resolve API for details.

rspack.config.js
module.exports = {
  resolve: {
    tsconfig: {
      configFile: path.resolve(__dirname, './tsconfig.json'),
      references: 'auto'
    },
  }
  experiments: {
    rspackFuture: {
      newResolver: true
    }
  }
}

experiments.rspackFuture.newTreeshaking

  • Type: boolean
  • Introduced in Version: v0.4.2
  • Enabled by Default in Version: v0.6.0
  • Removed in Version: v0.7.0
WARNING

newTreeshaking has been enabled and cannot go back to the deprecated tree shaking algorithm since version 0.7.0.

This feature enables the new treeshaking implementation the same as webpack, which would generate more efficient and smaller code

rspack.config.js
module.exports = {
  experiments: {
    rspackFuture: {
      newTreeshaking: true,
    },
  },
};

experiments.rspackFuture.disableApplyEntryLazily

  • Type: boolean
  • Introduced in Version: v0.4.5
  • Enabled by Default in Version: v0.5.0
  • Removed in Version: v0.6.0

When this feature is not enabled, options.entry can still make valid changes after compiler.hooks.afterEnvironment is called.

With this feature turned on, options.entry will no longer make valid changes after compiler.hooks.afterEnvironment call. This behavior is consistent with Webpack, so this configuration is unaffected for users developing applications in Rspack, but should be noted by developers of Rspack plugins or upper-level frameworks.

A common scenario is to prepend entries after the Compiler has been created:

const rspack = require('@rspack/core');
const compiler = rspack(options);

function prependEntry(compiler, additionalEntry) {
  for (const key in compiler.options.entry) {
    compiler.options.entry[key].import = [
      additionalEntry,
      ...(compiler.options.entry[key].import || []),
    ];
  }
}

prependEntry(compiler, 'dev-client.js');

Modifications will not take effect when this configuration is turned on, and you need to make the following changes:

const rspack = require('@rspack/core');
const compiler = rspack(options);

function prependEntry(compiler, additionalEntry) {
  new compiler.webpack.EntryPlugin(compiler.context, additionalEntry, {
    name: undefined,
  }).apply(compiler);
}

prependEntry(compiler, 'dev-client.js');