Tools Config

This section describes some low-level tools configurations in Modern.js Builder.

tools.autoprefixer

  • Type: Object | Function
  • Default:
{
  flexbox: 'no-2009',
  // Depends on the browserslist config in the project
  // and the `output.overrideBrowserslist` (higher priority) config
  overrideBrowserslist: browserslist,
}

You can modify the config of autoprefixer by tools.autoprefixer.

Object Type

When tools.autoprefixer is configured as Object type, it is merged with the default config through Object.assign. For example:

export default {
  tools: {
    autoprefixer: {
      flexbox: 'no-2009',
    },
  },
};

Function Type

When tools.autoprefixer is a Function, the default config is passed as the first parameter and can be directly modified or returned as the final result. For example:

export default {
  tools: {
    autoprefixer(config) {
      // modify flexbox config
      config.flexbox = 'no-2009';
    },
  },
};

tools.babel

  • Type: Object | Function
  • Default: undefined

With tools.babel you can modify the options of babel-loader.

Usage Scenarios

Please note the limitations of tools.babel in the following usage scenarios:

  • Rspack scenario: When using Rspack as the bundler, using the tools.babel option will significantly slow down the Rspack's build speed. This is because Rspack defaults to using SWC for compilation, and configuring Babel will cause the code to be compiled twice, resulting in additional compilation overhead.
  • webpack + SWC scenario: When using webpack as the bundler, if you use Builder's SWC plugin for code compilation, the tools.babel option will not take effect.

Function Type

When tools.babel is of type Function, the default Babel configuration will be passed as the first parameter. You can directly modify the configuration object or return an object as the final babel-loader configuration.

export default {
  tools: {
    babel(config) {
      // Add a Babel plugin
      // note: the plugin have been added to the default config to support antd load on demand
      config.plugins.push([
        'babel-plugin-import',
        {
          libraryName: 'xxx-components',
          libraryDirectory: 'es',
          style: true,
        },
      ]);
    },
  },
};

The second parameter of the tools.babel function provides some more convenient utility functions. Please continue reading the documentation below.

TIP

The above example is just for reference, usually you don't need to manually configure babel-plugin-import, because the Builder already provides a more general source.transformImport configuration.

Object Type

When tools.babel's type is Object, the config will be shallow merged with default config by Object.assign.

CAUTION

Note that Object.assign is a shallow copy and will completely overwrite the built-in presets or plugins array, please use it with caution.

export default {
  tools: {
    babel: {
      plugins: [
        [
          'babel-plugin-import',
          {
            libraryName: 'xxx-components',
            libraryDirectory: 'es',
            style: true,
          },
        ],
      ],
    },
  },
};

Util Functions

When tools.babel is a Function, the tool functions available for the second parameter are as follows:

addPlugins

  • Type: (plugins: BabelPlugin[]) => void

Add some Babel plugins. For example:

export default {
  tools: {
    babel(config, { addPlugins }) {
      addPlugins([
        [
          'babel-plugin-import',
          {
            libraryName: 'xxx-components',
            libraryDirectory: 'es',
            style: true,
          },
        ],
      ]);
    },
  },
};

addPresets

  • Type: (presets: BabelPlugin[]) => void

Add Babel preset configuration. (No need to add presets in most cases)

export default {
  tools: {
    babel(config, { addPresets }) {
      addPresets(['@babel/preset-env']);
    },
  },
};

removePlugins

  • Type: (plugins: string | string[]) => void

To remove the Babel plugin, just pass in the name of the plugin to be removed, you can pass in a single string or an array of strings.

export default {
  tools: {
    babel(config, { removePlugins }) {
      removePlugins('babel-plugin-import');
    },
  },
};

removePresets

  • Type: (presets: string | string[]) => void

To remove the Babel preset configuration, pass in the name of the preset to be removed, you can pass in a single string or an array of strings.

export default {
  tools: {
    babel(config, { removePresets }) {
      removePresets('@babel/preset-env');
    },
  },
};

modifyPresetEnvOptions

  • Type: (options: PresetEnvOptions) => void

Modify the configuration of @babel/preset-env, the configuration you pass in will be shallowly merged with default config. For example:

export default {
  tools: {
    babel(config, { modifyPresetEnvOptions }) {
      modifyPresetEnvOptions({
        targets: {
          browsers: ['last 2 versions'],
        },
      });
    },
  },
};

modifyPresetReactOptions

  • Type: (options: PresetReactOptions) => void

Modify the configuration of @babel/preset-react, the configuration you pass in will be shallowly merged with default config. For example:

export default {
  tools: {
    babel(config, { modifyPresetReactOptions }) {
      modifyPresetReactOptions({
        pragma: 'React.createElement',
      });
    },
  },
};

addIncludes

Deprecated, please use source.include instead, both have the same functionality.

addExcludes

Deprecated, please use source.exclude instead, both have the same functionality.

Debugging Babel Configuration

After modifying the babel-loader configuration through tools.babel, you can view the final generated configuration in Builder debug mode.

First, enable debug mode by using the DEBUG=builder parameter:

# Debug development mode
DEBUG=builder pnpm dev

# Debug production mode
DEBUG=builder pnpm build

Then open the generated (webpack|rspack).config.web.js file and search for the babel-loader keyword to see the complete babel-loader configuration.

tools.bundlerChain

  • Type: Function | undefined
  • Default: undefined

You can modify the webpack and Rspack configuration by configuring tools.bundlerChain which is type of Function. The function receives two parameters, the first is the original bundler chain object, and the second is an object containing some utils.

What is BundlerChain

Bundler chain is a subset of webpack chain, which contains part of the webpack chain API that you can use to modify both webpack and Rspack configuration.

Configurations modified via bundler chain will work on both webpack and Rspack builds. Note that the bundler chain only supports modifying the configuration of the non-differentiated parts of webpack and Rspack. For example, modifying the devtool configuration option (webpack and Rspack have the same devtool property value type), or adding an Rspack-compatible webpack plugin.

tools.bundlerChain is executed earlier than tools.webpackChain / tools.webpack / tools.rspack and thus will be overridden by changes in others.

For more information, please refer to Rsbuild#tools.bundlerChain

tools.cssExtract

  • Type: Object | Function
  • Default:
const defaultOptions = {
  // The loader options
  loaderOptions: {},
  // The plugin options
  pluginOptions: {
    // The default value of cssPath is `static/css`
    // while the default value of cssFilename is `[name].[contenthash:8].css`
    filename: `${cssPath}/${cssFilename}`,
    chunkFilename: `${cssPath}/async/${cssFilename}`,
    ignoreOrder: true,
  },
};
  • Bundler: only support webpack

The config of mini-css-extract-plugin can be modified through tools.cssExtract.

Object Type

When this value is an Object, it is merged with the default config via Object.assign. For example:

export default {
  tools: {
    cssExtract: {
      pluginOptions: {
        filename: 'static/css/[name].[contenthash:8].css',
      },
    },
  },
};

Function Type

When the value a Function, the default config is passed in as the first parameter. You can modify the config object directly, or return an object as the final config. For example:

export default {
  tools: {
    cssExtract: config => {
      config.pluginOptions.filename = 'static/css/[name].[contenthash:8].css';
      return config;
    },
  },
};

For more config details, please refer to mini-css-extract-plugin.

tools.cssLoader

  • Type: Object | Function
  • Default: undefined

The config of css-loader can be modified through tools.cssLoader. The default config is as follows:

{
  modules: {
    auto: true,
    exportLocalsConvention: 'camelCase',
    localIdentName: config.output.cssModuleLocalIdentName,
    // isServer indicates node (SSR) build
    // isWebWorker indicates web worker build
    exportOnlyLocals: isServer || isWebWorker,
  },
  // CSS Source Map enabled by default in development environment
  sourceMap: isDev,
  // importLoaders is `1` when compiling css files, and is `2` when compiling sass/less files
  importLoaders: 1 || 2,
}
TIP

When using Rspack as the bundler, this configuration is only supported when set disableCssExtract is true.

To modify CSS Modules configuration, it is recommended to use the output.cssModules configuration.

Object Type

When this value is an Object, it is merged with the default config via deep merge. For example:

export default {
  tools: {
    cssLoader: {
      modules: {
        exportOnlyLocals: true,
      },
    },
  },
};

Function Type

When the value is a Function, the default config is passed in as the first parameter. You can modify the config object directly, or return an object as the final config. For example:

export default {
  tools: {
    cssLoader: config => {
      config.modules.exportOnlyLocals = true;
      return config;
    },
  },
};

tools.devServer

  • Type: Object
  • Default: {}

The config of DevServer can be modified through tools.devServer.

TIP

Modern.js does not directly use webpack-dev-server or @rspack/dev-server, but implement DevServer based on webpack-dev-middleware.

Options

after

  • Type: Array
  • Default: []

Provides the ability to execute custom middleware after all other middleware internally within the server.

export default {
  tools: {
    devServer: {
      after: [
        async (req, res, next) => {
          console.log('after dev middleware');
          next();
        },
      ],
    },
  },
};

webpack-dev-server uses Express as the server-side framework. Modern.js does not use any framework, and the req and res in the above middleware are all native Node objects. Therefore, the Express middleware used in webpack-dev-server may not be directly usable in Modern.js.

If you want to migrate the Express middleware used in webpack-dev-server, you can use the following method to pass the Express app as middleware:

import expressMiddleware from 'my-express-middleware';
import express from 'express';

// init Express app
const app = express();
app.use(expressMiddleware);

export default {
  tools: {
    devServer: {
      after: [app],
    },
  },
};

before

  • Type: Array
  • Default: []

Provides the ability to execute custom middleware prior to all other middleware internally within the server.

export default {
  tools: {
    devServer: {
      before: [
        async (req, res, next) => {
          console.log('before dev middleware');
          next();
        },
      ],
    },
  },
};

client

WARNING

Deprecated: This configuration is deprecated, please use dev.client instead.

  • Type:
{
    /** Specify a protocol to use */
    protocol?: string;
    /** The path which the middleware is serving the event stream on */
    path?: string;
    /** Specify a port number to listen for requests on */
    port?: string;
    /** Specify a host to use */
    host?: string;
}
  • Default:
const defaultConfig = {
  path: '/webpack-hmr',
  // By default it is set to the port number of the dev server
  port: '',
  // By default it is set to "location.hostname"
  host: '',
  // By default it is set to "location.protocol === 'https:' ? 'wss' : 'ws'""
  protocol: '',
};

The config of HMR client, which are usually used to set the WebSocket URL of HMR.

compress

  • Type: boolean
  • Default: true

Whether to enable gzip compression for served static assets.

If you want to disable the gzip compression, you can set compress to false:

export default {
  tools: {
    devServer: {
      compress: false,
    },
  },
};

devMiddleware

  • Type:
{
  writeToDisk: boolean | ((filename: string) => boolean);
}
  • Default:
{
  writeToDisk: (file: string) => !file.includes('.hot-update.'),
}

The config of devMiddleware. Current options is the subset of webpack-dev-middleware.

headers

  • Type: Record<string, string>
  • Default: undefined

Adds headers to all responses.

export default {
  tools: {
    devServer: {
      headers: {
        'X-Custom-Foo': 'bar',
      },
    },
  },
};

historyApiFallback

  • Type: boolean | ConnectHistoryApiFallbackOptions
  • Default: false

The index.html page will likely have to be served in place of any 404 responses. Enable devServer.historyApiFallback by setting it to true:

export default {
  tools: {
    devServer: {
      historyApiFallback: true,
    },
  },
};

For more options and information, see the connect-history-api-fallback documentation.

hot

  • Type: boolean
  • Default: true

Enable Hot Module Replacement feature.

https

  • Type: boolean | { key: string; cert: string }
  • Default: false

By default, DevServer will be served over HTTP. It can optionally be served over HTTPS by setting devServer.https to true, and will disable the HTTP server.

You can also manually pass in the certificate and corresponding private key required by the HTTPS server:

export default {
  tools: {
    devServer: {
      https: {
        key: fs.readFileSync('certificates/private.pem'),
        cert: fs.readFileSync('certificates/public.pem'),
      },
    },
  },
};

liveReload

  • Type: boolean
  • Default: true

By default, the DevServer will reload/refresh the page when file changes are detected (devServer.hot option must be disabled in order for liveReload to take effect). Disable devServer.liveReload by setting it to false.

setupMiddlewares

  • Type:
Array<
  (
    middlewares: {
      unshift: (...handlers: RequestHandler[]) => void;
      push: (...handlers: RequestHandler[]) => void;
    },
    server: {
      sockWrite: (
        type: string,
        data?: string | boolean | Record<string, any>,
      ) => void;
    },
  ) => void
>;
  • Default: undefined

Provides the ability to execute a custom function and apply custom middlewares.

The order among several different types of middleware is: devServer.before => unshift => internal middlewares => push => devServer.after.

export default {
  tools: {
    devServer: {
      setupMiddlewares: [
        (middlewares, server) => {
          middlewares.unshift((req, res, next) => {
            next();
          });

          middlewares.push((req, res, next) => {
            next();
          });
        },
      ],
    },
  },
};

It is possible to use some server api to meet special scenario requirements:

  • sockWrite. Allow send some message to hmr client, and then the hmr client will take different actions depending on the message type. If you send a "content changed" message, the page will reload.
export default {
  tools: {
    devServer: {
      setupMiddlewares: [
        (middlewares, server) => {
          // add custom watch & trigger page reload when change
          watcher.on('change', changed => {
            server.sockWrite('content-changed');
          });
        },
      ],
    },
  },
};

proxy

  • Type: Record<string, string> | Record<string, ProxyDetail>
  • Default: undefined

Proxying some URLs.

export default {
  tools: {
    devServer: {
      proxy: {
        '/api': 'http://localhost:3000',
      },
    },
  },
};

A request to /api/users will now proxy the request to http://localhost:3000/api/users.

If you don't want /api to be passed along, we need to rewrite the path:

export default {
  tools: {
    devServer: {
      proxy: {
        '/api': {
          target: 'http://localhost:3000',
          pathRewrite: { '^/api': '' },
        },
      },
    },
  },
};

The DevServer Proxy makes use of the http-proxy-middleware package. Check out its documentation for more advanced usages.

The full type definition of DevServer Proxy is:

import type { Options as HttpProxyOptions } from 'http-proxy-middleware';

type Filter = string | string[] | ((pathname: string, req: Request) => boolean);

type ProxyDetail = HttpProxyOptions & {
  bypass?: (
    req: IncomingMessage,
    res: ServerResponse,
    proxyOptions: ProxyOptions,
  ) => string | undefined | null | false;
  context?: Filter;
};

type ProxyOptions =
  | Record<string, string>
  | Record<string, ProxyDetail>
  | ProxyDetail[]
  | ProxyDetail;

In addition to the http-proxy-middleware option, we also support the bypass and context configuration:

  • bypass: bypass the proxy based on the return value of a function.
    • Return null or undefined to continue processing the request with proxy.
    • Return false to produce a 404 error for the request.
    • Return a path to serve from, instead of continuing to proxy the request.
  • context: If you want to proxy multiple, specific paths to the same target, you can use an array of one or more objects with a context property.
// custom bypass
export default {
  tools: {
    devServer: {
      proxy: {
        '/api': {
          target: 'http://localhost:3000',
          bypass: function (req, res, proxyOptions) {
            if (req.headers.accept.indexOf('html') !== -1) {
              console.log('Skipping proxy for browser request.');
              return '/index.html';
            }
          },
        },
      },
    },
  },
};
// proxy multiple
export default {
  tools: {
    devServer: {
      proxy: [
        {
          context: ['/auth', '/api'],
          target: 'http://localhost:3000',
        },
      ],
    },
  },
};

watch

  • Type: boolean
  • Default: true

Whether to watch files change in directories such as mock/, server/, api/.

tools.htmlPlugin

  • Type: false | Object | Function
  • Default:
const defaultHtmlPluginOptions = {
  inject, // corresponding to the html.inject config
  favicon, // corresponding to html.favicon config
  filename, // generated based on output.distPath and entryName
  template, // defaults to the built-in HTML template path
  templateParameters, // corresponding to the html.templateParameters config
  chunks: [entryName],
  minify: { // generated based on output.disableMinimize
    removeComments: false,
    useShortDoctype: true,
    keepClosingSlash: true,
    collapseWhitespace: true,
    removeRedundantAttributes: true,
    removeScriptTypeAttributes: true,
    removeStyleLinkTypeAttributes: true,
    removeEmptyAttributes: true,
    minifyJS, // generated based on output.charset and tools.terser.terserOptions
    minifyCSS: true,
    minifyURLs: true,
  },
};

The configs of html-webpack-plugin or @rspack/plugin-html can be modified through tools.htmlPlugin.

Object Type

When tools.htmlPlugin is Object type, the value will be merged with the default config via Object.assign.

export default {
  tools: {
    htmlPlugin: {
      scriptLoading: 'blocking',
    },
  },
};

Function Type

When tools.htmlPlugin is a Function:

  • The first parameter is the default config, which can be modified directly.
  • The second parameter is also an object, containing the entry name and the entry value.
  • The Function can return a new object as the final config.
export default {
  tools: {
    htmlPlugin(config, { entryName, entryValue }) {
      if (entryName === 'main') {
        config.scriptLoading = 'blocking';
      }
    },
  },
};

Boolean Type

The built-in html-webpack-plugin plugins can be disabled by set tools.htmlPlugin to false.

export default {
  tools: {
    htmlPlugin: false,
  },
};

Disable JS/CSS minify

By default, Builder will compresses JavaScript/CSS code inside HTML during the production build to improve the page performance. This ability is often helpful when using custom templates or inserting custom scripts.

However, when output.enableInlineScripts or output.enableInlineStyles is turned on, inline JavaScript/CSS code will be repeatedly compressed, which will have a certain impact on build performance. You can modify the default minify behavior by modifying the tools.htmlPlugin.minify configuration.

export default {
  tools: {
    htmlPlugin: config => {
      if (typeof config.minify === 'object') {
        config.minify.minifyJS = false;
        config.minify.minifyCSS = false;
      }
    },
  },
};

tools.less

  • Type: Object | Function
  • Default:
const defaultOptions = {
  lessOptions: {
    javascriptEnabled: true,
  },
  // CSS Source Map enabled by default in development environment
  sourceMap: isDev,
};

You can modify the config of less-loader via tools.less.

Object Type

When tools.less is configured as Object type, it is merged with the default config through Object.assign in a shallow way. It should be noted that lessOptions is merged through deepMerge in a deep way. For example:

export default {
  tools: {
    less: {
      lessOptions: {
        javascriptEnabled: false,
      },
    },
  },
};

Function Type

When tools.less is a Function, the default config is passed as the first parameter, which can be directly modified or returned as the final result. The second parameter provides some utility functions that can be called directly. For example:

export default {
  tools: {
    less(config) {
      // Modify the config of lessOptions
      config.lessOptions = {
        javascriptEnabled: false,
      };
    },
  },
};

Modifying Less Version

In some scenarios, if you need to use a specific version of Less instead of the built-in Less v4 in Builder, you can install the desired Less version in your project and set it up using the implementation option of the less-loader.

export default {
  tools: {
    less: {
      implementation: require('less'),
    },
  },
};

Util Function

addExcludes

  • Type: (excludes: RegExp | RegExp[]) => void

Used to specify which files less-loader does not compile, You can pass in one or more regular expressions to match the path of less files, for example:

export default {
  tools: {
    less(config, { addExcludes }) {
      addExcludes(/node_modules/);
    },
  },
};

tools.minifyCss

  • Type: Object | Function | undefined
  • Default:
const defaultOptions = {
  minimizerOptions: {
    preset: [
      'default',
      {
        mergeLonghand: false,
      },
    ],
  },
};

When building for production, Builder will minimize the CSS code through css-minimizer-webpack-plugin. The config of css-minimizer-webpack-plugin can be modified via tools.minifyCss.

Object Type

When tools.minifyCss is Object type, it will be merged with the default config via Object.assign.

For example, modify the preset config of cssnano:

export default {
  tools: {
    minifyCss: {
      minimizerOptions: {
        preset: require.resolve('cssnano-preset-simple'),
      },
    },
  },
};

Function Type

When tools.minifyCss is Function type, the default config is passed in as the first parameter, the config object can be modified directly, or a value can be returned as the final result.

export default {
   tools: {
    minifyCss: options => {
      options.minimizerOptions = {
        preset: require.resolve('cssnano-preset-simple'),
      },
    }
  }
};

tools.postcss

  • Type: Object | Function
  • Default:
const defaultOptions = {
  postcssOptions: {
    plugins: [
      // The following plugins are enabled by default
      require('postcss-nesting'),
      require('postcss-media-minmax'),
      require('postcss-flexbugs-fixes'),
      require('autoprefixer')({
        flexbox: 'no-2009',
      }),
      // The following plugins are only enabled when compatibility with legacy browsers is required
      require('postcss-custom-properties'),
      require('postcss-initial'),
      require('postcss-page-break'),
      require('postcss-font-variant'),
    ],
    // CSS Source Map enabled by default in development environment
    sourceMap: isDev,
  },
};

Builder integrates PostCSS by default, you can configure postcss-loader through tools.postcss.

Function Type

When the value is a Function, the internal default config is passed as the first parameter, and the config object can be modified directly without returning, or an object can be returned as the final result; the second parameter is a set of tool functions for modifying the postcss-loader config.

For example, you need to add a PostCSS plugin on the basis of the original plugin, and push a new plugin to the postcssOptions.plugins array:

export default {
  tools: {
    postcss: opts => {
      opts.postcssOptions.plugins.push(require('postcss-px-to-viewport'));
    },
  },
};

When you need to pass parameters to the PostCSS plugin, you can pass them in by function parameters:

export default {
  tools: {
    postcss: opts => {
      const viewportPlugin = require('postcss-px-to-viewport')({
        viewportWidth: 375,
      });
      opts.postcssOptions.plugins.push(viewportPlugin);
    },
  },
};

tools.postcss can return a config object and completely replace the default config:

export default {
  tools: {
    postcss: () => {
      return {
        postcssOptions: {
          plugins: [require('postcss-px-to-viewport')],
        },
      };
    },
  },
};

Object Type

When this value is an Object, it is merged with the default config via Object.assign. Note that Object.assign is a shallow copy and will completely overwrite the built-in presets or plugins array, please use it with caution.

export default {
  tools: {
    postcss: {
      // Because `Object.assign` is used, the default postcssOptions will be overwritten.
      postcssOptions: {
        plugins: [require('postcss-px-to-viewport')],
      },
    },
  },
};

Util Functions

addPlugins

  • Type: (plugins: PostCSSPlugin | PostCSSPlugin[]) => void

For adding additional PostCSS plugins, You can pass in a single PostCSS plugin, or an array of PostCSS plugins.

export default {
  tools: {
    postcss: (config, { addPlugins }) => {
      // Add a PostCSS Plugin
      addPlugins(require('postcss-preset-env'));
      // Add multiple PostCSS Plugins
      addPlugins([require('postcss-preset-env'), require('postcss-import')]);
    },
  },
};
TIP

Builder uses the PostCSS v8 version. When you use third-party PostCSS plugins, please pay attention to whether the PostCSS version is compatible. Some legacy plugins may not work in PostCSS v8.

tools.pug

  • Type: true | Object | Function | undefined
  • Default: false

Configure the Pug template engine.

Boolean Type

Pug template engine is not enabled by default, you can enable it by setting tools.pug to true.

export default {
  tools: {
    pug: true,
  },
};

When enabled, you can use index.pug as the template file in html.template config.

Object Type

When tools.terser is Object type, you can passing the Pug options:

export default {
  tools: {
    pug: {
      doctype: 'xml',
    },
  },
};

For detailed options, please refer to Pug API Reference.

Function Type

When tools.pug is Function type, the default configuration is passed in as the first parameter, the configuration object can be modified directly, or a value can be returned as the final result.

export default {
  tools: {
    pug(config) {
      config.doctype = 'xml';
    },
  },
};

tools.sass

  • Type: Object | Function
  • Default:
const defaultOptions = {
  // CSS Source Map enabled by default in development environment
  sourceMap: isDev,
};

You can modify the config of sass-loader via tools.sass.

Object Type

When tools.sass is Object type, it is merged with the default config through Object.assign. It should be noted that sassOptions is merged through deepMerge in a deep way.

For example:

export default {
  tools: {
    sass: {
      sourceMap: true,
    },
  },
};

Function Type

When tools.sass is a Function, the default config is passed as the first parameter, which can be directly modified or returned as the final result. The second parameter provides some utility functions that can be called directly. For Example:

export default {
  tools: {
    sass(config) {
      // Modify sourceMap config
      config.additionalData = async (content, loaderContext) => {
        // ...
      };
    },
  },
};

Modifying Sass Version

In some scenarios, if you need to use a specific version of Sass instead of the built-in Dart Sass v1 in Builder, you can install the desired Sass version in your project and set it up using the implementation option of the sass-loader.

export default {
  tools: {
    sass: {
      implementation: require('sass'),
    },
  },
};

Utility Function

addExcludes

  • Type: (excludes: RegExp | RegExp[]) => void

Used to specify which files sass-loader does not compile, You can pass in one or more regular expressions to match the path of sass files, for example:

export default {
  tools: {
    sass(config, { addExcludes }) {
      addExcludes(/node_modules/);
    },
  },
};

tools.styleLoader

  • Type: Object | Function
  • Default: {}

The config of style-loader can be set through tools.styleLoader.

It is worth noting that Builder does not enable style-loader by default. You can use output.disableCssExtract config to enable it.

Object Type

When this value is an Object, it is merged with the default config via Object.assign. For example:

export default {
  tools: {
    styleLoader: {
      loaderOptions: {
        insert: 'head',
      },
    },
  },
};

Function Type

When the value is a Function, the default config is passed in as the first parameter. You can modify the config object directly, or return an object as the final config. For example:

export default {
  tools: {
    styleLoader: config => {
      config.loaderOptions.insert = 'head';
      return config;
    },
  },
};

tools.styledComponents

  • Type: Object | Function
  • Default:
{
  displayName: true,
  // `isSSR` is true in SSR build
  ssr: isSSR,
  // `pure` is enabled in production to reduce bundle size
  pure: isProd,
  transpileTemplateLiterals: true,
}

tools.styledComponents config is corresponding to babel-plugin-styled-components, or @swc/plugin-styled-components when using SWC plugin.

When the value is an Object, use the Object.assign function to merge with the default config. For example:

export default {
  tools: {
    styledComponents: {
      pure: false,
    },
  },
};

When the config is a Function, the first parameter is the default configuration, and the second parameter provides some utility functions that can be called directly:

export default {
  tools: {
    styledComponents(config) {
      config.pure = false;
    },
  },
};

The feature is enabled by default, and you can configure tools.styledComponents to false to disable this behavior, which can improve build performance:

export default {
  tools: {
    styledComponents: false,
  },
};

tools.terser

  • Type: Object | Function | undefined
  • Default:
const defaultTerserOptions = {
  terserOptions: {
    mangle: {
      safari10: true,
    },
  },
};
  • Bundler: only support webpack

When building for production, Builder will minimize the JavaScript code through terser-webpack-plugin. The config of terser-webpack-plugin can be modified via tools.terser.

Object Type

When tools.terser is Object type, it will be merged with the default config via Object.assign.

For example, to exclude some files from minification:

export default {
  tools: {
    terser: {
      exclude: /\/excludes/,
    },
  },
};

Function Type

When tools.terser is Function type, the default config is passed in as the first parameter, the config object can be modified directly, or a value can be returned as the final result.

export default {
  tools: {
    terser: opts => {
      opts.exclude = /\/excludes/;
    },
  },
};
Disable code minification

If you need to disable code minification, you can use the output.disableMinimize configuration.

tools.tsLoader

  • Type: Object | Function | undefined
  • Default: undefined
  • Bundler: only support webpack
Alternatives for ts-loader

Using babel-loader or Rspack instead of ts-loader can significantly improve compilation speed and provide better extendability.

ts-loader cannot be used with certain features such as source.transformImport and tools.styledComponents provided by Babel & SWC.

ts-loader is not enabled by default in the project. When tools.tsLoader is not undefined, builder will use ts-loader instead of babel-loader to compile TypeScript code.

Object Type

When this value is an Object, it is merged with the default configuration via Object.assign.

The default configuration is as follows:

{
  "compilerOptions": {
    "target": "es5",
    "module": "ESNext"
  },
  "transpileOnly": true,
  "allowTsInNodeModules": true
}

You can override the default configuration via the tools.tsLoader configuration option:

export default {
  tools: {
    tsLoader: {
      allowTsInNodeModules: false,
    },
  },
};

Function Type

When this value is a Function, the default configuration is passed in as the first parameter, the configuration object can be modified directly, or an object can be returned as the final configuration.The second parameter is the util functions to modify the ts-loader configuration. For example:

export default {
  tools: {
    tsLoader: opts => {
      opts.allowTsInNodeModules = false;
    },
  },
};

Util Functions

addIncludes

Deprecated, please use source.include instead, both have the same functionality.

addExcludes

Deprecated, please use source.exclude instead, both have the same functionality.

tools.tsChecker

  • Type: Object | Function
  • Default:
const defaultOptions = {
  typescript: {
    // avoid OOM issue
    memoryLimit: 8192,
    // use tsconfig of user project
    configFile: tsconfigPath,
    // use typescript of user project
    typescriptPath: require.resolve('typescript'),
  },
  issue: {
    exclude: [
      { file: '**/*.(spec|test).ts' },
      { file: '**/node_modules/**/*' },
    ],
  },
  logger: {
    log() {
      // do nothing
      // we only want to display error messages
    },
    error(message: string) {
      console.error(message.replace(/ERROR/g, 'Type Error'));
    },
  },
},

By default, the fork-ts-checker-webpack-plugin is enabled for type checking. You can use output.disableTsChecker config to disable it.

Object Type

When the value of tsChecker is of type Object, it will be deeply merged with the default configuration.

export default {
  tools: {
    tsChecker: {
      issue: {
        exclude: [{ file: '**/some-folder/**/*.ts' }],
      },
    },
  },
};

Function Type

When the value of tsChecker is of type Function, the default configuration will be passed as the first argument. You can directly modify the configuration object or return an object as the final configuration.

export default {
  tools: {
    tsChecker(options) {
      (options?.issue?.exclude as unknown[]).push({
        file: '**/some-folder/**/*.ts',
      });
    },
  },
};

tools.webpack

  • Type: Object | Function | undefined
  • Default: undefined
  • Bundler: only support webpack

tools.webpack is used to configure webpack.

tools.bundlerChain is also used to modify the webpack configuration, and the function is more powerful. It is recommended to use tools.bundlerChain first.

Object Type

tools.webpack can be configured as an object to be deep merged with the built-in webpack configuration through webpack-merge.

For example, add resolve.alias configuration:

export default {
  tools: {
    webpack: {
      resolve: {
        alias: {
          '@util': 'src/util',
        },
      },
    },
  },
};

Function Type

tools.webpack can be configured as a function. The first parameter of this function is the built-in webpack configuration object, you can modify this object, and then return it. For example:

export default {
  tools: {
    webpack: config => {
      config.resolve.alias['@util'] = 'src/util';
      return config;
    },
  },
};
TIP

The object returned by the tools.webpack function is used directly as the final webpack configuration and is not merged with the built-in webpack configuration.

Utils

The second parameter of this function is an object, which contains some utility functions and properties, as follows:

env

  • Type: 'development' | 'production' | 'test'

The env parameter can be used to determine whether the current environment is development, production or test. For example:

export default {
  tools: {
    webpack: (config, { env }) => {
      if (env === 'development') {
        config.devtool = 'cheap-module-eval-source-map';
      }
      return config;
    },
  },
};

isProd

  • Type: boolean

The isProd parameter can be used to determine whether the current environment is production. For example:

export default {
  tools: {
    webpack: (config, { isProd }) => {
      if (isProd) {
        config.devtool = 'source-map';
      }
      return config;
    },
  },
};

target

  • Type: 'web' | 'node' | 'modern-web' | 'web-worker'

The target parameter can be used to determine the current target. For example:

export default {
  tools: {
    webpack: (config, { target }) => {
      if (target === 'node') {
        // ...
      }
      return config;
    },
  },
};

isServer

  • Type: boolean

Determines whether the target environment is node, equivalent to target === 'node'.

export default {
  tools: {
    webpack: (config, { isServer }) => {
      if (isServer) {
        // ...
      }
      return config;
    },
  },
};

isWebWorker

  • Type: boolean

Determines whether the target environment is web-worker, equivalent to target === 'web-worker'.

export default {
  tools: {
    webpack: (config, { isWebWorker }) => {
      if (isWebWorker) {
        // ...
      }
      return config;
    },
  },
};

webpack

  • Type: typeof import('webpack')

The webpack instance. For example:

export default {
  tools: {
    webpack: (config, { webpack }) => {
      config.plugins.push(new webpack.ProgressPlugin());
      return config;
    },
  },
};

HtmlWebpackPlugin

  • Type: typeof import('html-webpack-plugin')

The HtmlWebpackPlugin instance:

export default {
  tools: {
    webpack: (chain, { HtmlWebpackPlugin }) => {
      console.log(HtmlWebpackPlugin);
    },
  },
};

addRules

  • Type: (rules: RuleSetRule | RuleSetRule[]) => void

Add additional webpack rules.

For example:

export default {
  tools: {
    webpack: (config, { addRules }) => {
      // add a single rule
      addRules({
        test: /\.foo/,
        loader: require.resolve('foo-loader'),
      });

      // Add multiple rules as an array
      addRules([
        {
          test: /\.foo/,
          loader: require.resolve('foo-loader'),
        },
        {
          test: /\.bar/,
          loader: require.resolve('bar-loader'),
        },
      ]);
    },
  },
};

prependPlugins

  • Type: (plugins: WebpackPluginInstance | WebpackPluginInstance[]) => void

Add additional plugins to the head of the internal webpack plugins array, and the plugin will be executed first.

export default {
  tools: {
    webpack: (config, { prependPlugins, webpack }) => {
      // add a single plugin
      prependPlugins(
        new webpack.BannerPlugin({
          banner: 'hello world!',
        }),
      );

      // Add multiple plugins
      prependPlugins([new PluginA(), new PluginB()]);
    },
  },
};

appendPlugins

  • Type: (plugins: WebpackPluginInstance | WebpackPluginInstance[]) => void

Add additional plugins at the end of the internal webpack plugins array, the plugin will be executed last.

export default {
  tools: {
    webpack: (config, { appendPlugins, webpack }) => {
      // add a single plugin
      appendPlugins([
        new webpack.BannerPlugin({
          banner: 'hello world!',
        }),
      ]);

      // Add multiple plugins
      appendPlugins([new PluginA(), new PluginB()]);
    },
  },
};

removePlugin

  • Type: (name: string) => void

Remove the internal webpack plugin, the parameter is the constructor.name of the plugin.

For example, remove the internal fork-ts-checker-webpack-plugin:

export default {
  tools: {
    webpack: (config, { removePlugin }) => {
      removePlugin('ForkTsCheckerWebpackPlugin');
    },
  },
};

mergeConfig

  • Type: (...configs: WebpackConfig[]) => WebpackConfig

Used to merge multiple webpack configs, same as webpack-merge.

export default {
  tools: {
    webpack: (config, { mergeConfig }) => {
      return mergeConfig(config, {
        devtool: 'eval',
      });
    },
  },
};

getCompiledPath

  • Type: (name: string) => string

Get the path to the builder built-in dependencies, same as webpackChain#getCompiledPath.

tools.webpackChain

  • Type: Function | undefined
  • Default: undefined
  • Bundler: only support webpack

You can modify the webpack configuration by configuring tools.webpackChain which is type of Function. The function receives two parameters, the first is the original webpack chain object, and the second is an object containing some utils.

Compared with tools.webpack, webpack-chain not only supports chained calls, but also can locate built-in Rule or Plugin based on aliases, so as to achieve precise config modification. We recommend using tools.webpackChain instead of tools.webpack.

tools.webpackChain is executed earlier than tools.webpack and thus will be overridden by changes in tools.webpack.

Utils

env

  • Type: 'development' | 'production' | 'test'

The env parameter can be used to determine whether the current environment is development, production or test. For example:

export default {
  tools: {
    webpackChain: (chain, { env }) => {
      if (env === 'development') {
        chain.devtool('cheap-module-eval-source-map');
      }
    },
  },
};

isProd

  • Type: boolean

The isProd parameter can be used to determine whether the current environment is production. For example:

export default {
  tools: {
    webpackChain: (chain, { isProd }) => {
      if (isProd) {
        chain.devtool('source-map');
      }
    },
  },
};

target

  • Type: 'web' | 'node' | 'modern-web' | 'web-worker'

The target parameter can be used to determine the current environment. For example:

export default {
  tools: {
    webpackChain: (chain, { target }) => {
      if (target === 'node') {
        // ...
      }
    },
  },
};

isServer

  • Type: boolean

Determines whether the target environment is node, equivalent to target === 'node'.

export default {
  tools: {
    webpackChain: (chain, { isServer }) => {
      if (isServer) {
        // ...
      }
    },
  },
};

isWebWorker

  • Type: boolean

Determines whether the target environment is web-worker, equivalent to target === 'web-worker'.

export default {
  tools: {
    webpackChain: (chain, { isWebWorker }) => {
      if (isWebWorker) {
        // ...
      }
    },
  },
};

webpack

  • Type: typeof import('webpack')

The webpack instance. For example:

export default {
  tools: {
    webpackChain: (chain, { webpack }) => {
      chain.plugin('my-progress').use(webpack.ProgressPlugin);
    },
  },
};

HtmlWebpackPlugin

  • Type: typeof import('html-webpack-plugin')

The HtmlWebpackPlugin instance:

export default {
  tools: {
    webpackChain: (chain, { HtmlWebpackPlugin }) => {
      console.log(HtmlWebpackPlugin);
    },
  },
};

getCompiledPath

  • Type: (name: string) => string

Get the path to the builder built-in dependencies, such as:

  • sass
  • sass-loader
  • less
  • less-loader
  • css-loader
  • ...

This method is usually used when you need to reuse the same dependency with the builder.

TIP

Builder built-in dependencies are subject to change with version iterations, e.g. generate large version break changes. Please avoid using this API if it is not necessary.

export default {
  tools: {
    webpackChain: (chain, { getCompiledPath }) => {
      const loaderPath = getCompiledPath('less-loader');
      // ...
    },
  },
};

CHAIN_ID

Some common Chain IDs are predefined in the Builder, and you can use these IDs to locate the built-in Rule or Plugin.

TIP

Please note that some of the rules or plugins listed below are not available by default. They will only be included in the webpack configuration when you enable specific options or register certain plugins.

For example, the RULE.STYLUS rule exists only when the Stylus plugin is registered.

CHAIN_ID.RULE

ID Description
RULE.JS Rule for js
RULE.TS Rule for ts
RULE.CSS Rule for css
RULE.LESS Rule for less
RULE.SASS Rule for sass
RULE.STYLUS Rule for stylus(requires Stylus plugin)
RULE.SVG Rule for svg
RULE.PUG Rule for pug
RULE.TOML Rule for toml
RULE.YAML Rule for yaml
RULE.WASM Rule for WASM
RULE.NODE Rule for node
RULE.FONT Rule for font
RULE.IMAGE Rule for image
RULE.MEDIA Rule for media

CHAIN_ID.ONE_OF

ONE_OF.XXX can match a certain type of rule in the rule array.

ID Description
ONE_OF.SVG Rules for SVG, automatic choice between data URI and separate file
ONE_OF.SVG_URL Rules for SVG, output as a separate file
ONE_OF.SVG_INLINE Rules for SVG, inlined into bundles as data URIs
ONE_OF.SVG_ASSETS Rules for SVG, automatic choice between data URI and separate file

CHAIN_ID.USE

USE.XXX can match a certain loader.

ID Description
USE.TS correspond to ts-loader
USE.CSS correspond to css-loader
USE.LESS correspond to less-loader
USE.SASS correspond to sass-loader
USE.STYLUS correspond to stylus-loader
USE.PUG correspond to pug-loader
USE.VUE correspond to vue-loader
USE.TOML correspond to toml-loader
USE.YAML correspond to yaml-loader
USE.NODE correspond to node-loader
USE.URL correspond to url-loader
USE.SVGR correspond to @svgr/webpack
USE.BABEL correspond to babel-loader
USE.STYLE correspond to style-loader
USE.POSTCSS correspond to postcss-loader
USE.CSS_MODULES_TS correspond to css-modules-typescript-loader
USE.MINI_CSS_EXTRACT correspond to mini-css-extract-plugin.loader
USE.RESOLVE_URL_LOADER_FOR_SASS correspond to resolve-url-loader

CHAIN_ID.PLUGIN

PLUGIN.XXX can match a certain webpack plugin.

ID Description
PLUGIN.HMR correspond to HotModuleReplacementPlugin
PLUGIN.COPY correspond to CopyWebpackPlugin
PLUGIN.HTML correspond to HtmlWebpackPlugin, you need to splice the entry name when using: ${PLUGIN.HTML}-${entryName}
PLUGIN.DEFINE correspond to DefinePlugin
PLUGIN.IGNORE correspond to IgnorePlugin
PLUGIN.BANNER correspond to BannerPlugin
PLUGIN.PROGRESS correspond to Webpackbar
PLUGIN.APP_ICON correspond to AppIconPlugin
PLUGIN.MANIFEST correspond to WebpackManifestPlugin
PLUGIN.TS_CHECKER correspond to ForkTsCheckerWebpackPlugin
PLUGIN.INLINE_HTML correspond to InlineChunkHtmlPlugin
PLUGIN.BUNDLE_ANALYZER correspond to WebpackBundleAnalyzer
PLUGIN.MINI_CSS_EXTRACT correspond to MiniCssExtractPlugin
PLUGIN.VUE_LOADER_PLUGIN correspond to VueLoaderPlugin
PLUGIN.REACT_FAST_REFRESH correspond to ReactFastRefreshPlugin
PLUGIN.NODE_POLYFILL_PROVIDE correspond to ProvidePlugin for node polyfills
PLUGIN.SUBRESOURCE_INTEGRITY correspond to webpack-subresource-integrity
PLUGIN.ASSETS_RETRY correspond to webpack static asset retry plugin in Builder
PLUGIN.AUTO_SET_ROOT_SIZE correspond to automatically set root font size plugin in Builder

CHAIN_ID.MINIMIZER

MINIMIZER.XXX can match a certain minimizer.

ID Description
MINIMIZER.JS correspond to TerserWebpackPlugin
MINIMIZER.CSS correspond to CssMinimizerWebpackPlugin
MINIMIZER.ESBUILD correspond to ESBuildPlugin
MINIMIZER.SWC correspond to SwcWebpackPlugin

Examples

For usage examples, please refer to: WebpackChain usage examples.

tools.rspack

  • Type: Object | Function | undefined
  • Default: undefined
  • Bundler: only support Rspack

tools.rspack is used to configure Rspack.

Object Type

tools.rspack can be configured as an object to be deep merged with the built-in Rspack configuration through webpack-merge.

For example, add resolve.alias configuration:

export default {
  tools: {
    rspack: {
      resolve: {
        alias: {
          '@util': 'src/util',
        },
      },
    },
  },
};

Function Type

tools.rspack can be configured as a function. The first parameter of this function is the built-in Rspack configuration object, you can modify this object, and then return it. For example:

export default {
  tools: {
    rspack: config => {
      config.resolve.alias['@util'] = 'src/util';
      return config;
    },
  },
};
TIP

The object returned by the tools.rspack function is used directly as the final Rspack configuration and is not merged with the built-in Rspack configuration.

The second parameter of this function is an object, which contains some utility functions and properties, as follows:

Utils

env

  • Type: 'development' | 'production' | 'test'

The env parameter can be used to determine whether the current environment is development, production or test. For example:

export default {
  tools: {
    rspack: (config, { env }) => {
      if (env === 'development') {
        config.devtool = 'cheap-module-eval-source-map';
      }
      return config;
    },
  },
};

isProd

  • Type: boolean

The isProd parameter can be used to determine whether the current environment is production. For example:

export default {
  tools: {
    rspack: (config, { isProd }) => {
      if (isProd) {
        config.devtool = 'source-map';
      }
      return config;
    },
  },
};

target

  • Type: 'web' | 'node' | 'modern-web' | 'web-worker'

The target parameter can be used to determine the current target. For example:

export default {
  tools: {
    rspack: (config, { target }) => {
      if (target === 'node') {
        // ...
      }
      return config;
    },
  },
};

isServer

  • Type: boolean

Determines whether the target environment is node, equivalent to target === 'node'.

export default {
  tools: {
    rspack: (config, { isServer }) => {
      if (isServer) {
        // ...
      }
      return config;
    },
  },
};

isWebWorker

  • Type: boolean

Determines whether the target environment is web-worker, equivalent to target === 'web-worker'.

export default {
  tools: {
    rspack: (config, { isWebWorker }) => {
      if (isWebWorker) {
        // ...
      }
      return config;
    },
  },
};

rspack

  • Type: typeof import('@rspack/core')

The Rspack instance. For example:

export default {
  tools: {
    rspack: (config, { rspack }) => {
      config.plugins.push(new rspack.BannerPlugin());
      return config;
    },
  },
};

addRules

  • Type: (rules: RuleSetRule | RuleSetRule[]) => void

Add additional Rspack rules.

For example:

export default {
  tools: {
    rspack: (config, { addRules }) => {
      // add a single rule
      addRules({
        test: /\.foo/,
        loader: require.resolve('foo-loader'),
      });

      // Add multiple rules as an array
      addRules([
        {
          test: /\.foo/,
          loader: require.resolve('foo-loader'),
        },
        {
          test: /\.bar/,
          loader: require.resolve('bar-loader'),
        },
      ]);
    },
  },
};

prependPlugins

  • Type: (plugins: RspackPluginInstance | RspackPluginInstance[]) => void

Add additional plugins to the head of the internal Rspack plugins array, and the plugin will be executed first.

export default {
  tools: {
    rspack: (config, { prependPlugins }) => {
      // add a single plugin
      prependPlugins(new PluginA());

      // Add multiple plugins
      prependPlugins([new PluginA(), new PluginB()]);
    },
  },
};

appendPlugins

  • Type: (plugins: RspackPluginInstance | RspackPluginInstance[]) => void

Add additional plugins at the end of the internal Rspack plugins array, the plugin will be executed last.

export default {
  tools: {
    rspack: (config, { appendPlugins }) => {
      // add a single plugin
      appendPlugins([new PluginA()]);

      // Add multiple plugins
      appendPlugins([new PluginA(), new PluginB()]);
    },
  },
};

removePlugin

  • Type: (name: string) => void

Remove the internal Rspack plugin, the parameter is the constructor.name of the plugin.

For example, remove the internal webpack-bundle-analyzer:

export default {
  tools: {
    rspack: (config, { removePlugin }) => {
      removePlugin('BundleAnalyzerPlugin');
    },
  },
};

mergeConfig

  • Type: (...configs: RspackConfig[]) => RspackConfig

Used to merge multiple Rspack configs, same as webpack-merge.

export default {
  tools: {
    rspack: (config, { mergeConfig }) => {
      return mergeConfig(config, {
        devtool: 'eval',
      });
    },
  },
};

getCompiledPath

  • Type: (name: string) => string

Get the path to the builder built-in dependencies, such as:

  • sass
  • sass-loader
  • less
  • less-loader
  • ...

This method is usually used when you need to reuse the same dependency with the builder.

TIP

Builder built-in dependencies are subject to change with version iterations, e.g. generate large version break changes. Please avoid using this API if it is not necessary.

export default {
  tools: {
    rspack: (config, { getCompiledPath }) => {
      const loaderPath = getCompiledPath('less-loader');
      // ...
    },
  },
};
ON THIS PAGE