Plugin Hooks

本章节介绍 Rsbuild 插件可用的 plugin hooks。

总览

Common Hooks

Dev Hooks

仅在执行 rsbuild dev 命令或 rsbuild.startDevServer() 方法时调用。

Build Hooks

仅在执行 rsbuild build 命令或 rsbuild.build() 方法时调用。

  • onBeforeBuild:在执行生产环境构建前调用。
  • onAfterBuild:在执行生产环境构建后调用,可以获取到构建结果信息。

Preview Hooks

仅在执行 rsbuild preview 命令或 rsbuild.preview() 方法时调用。

Hooks 顺序

Dev Hooks

执行 rsbuild dev 命令或 rsbuild.startDevServer() 方法时,Rsbuild 会依次执行以下 hooks:

Build Hooks

执行 rsbuild build 命令或 rsbuild.build() 方法时,Rsbuild 会依次执行以下 hooks:

Preview Hooks

执行 rsbuild preview 命令或 rsbuild.preview() 方法时,Rsbuild 会依次执行以下 hooks:

回调函数顺序

默认行为

如果多个插件注册了相同的 hook,那么 hook 的回调函数会按照注册时的顺序执行。

在以下例子中,控制台会依次输出 '1''2'

const plugin1 = () => ({
  setup: (api) => {
    api.modifyRsbuildConfig(() => console.log('1'));
  },
});

const plugin2 = () => ({
  setup: (api) => {
    api.modifyRsbuildConfig(() => console.log('2'));
  },
});

rsbuild.addPlugins([plugin1, plugin2]);

order 字段

在注册 hook 时,可以通过 order 字段来声明 hook 的顺序。

type HookDescriptor<T extends (...args: any[]) => any> = {
  handler: T;
  order: 'pre' | 'post' | 'default';
};

在以下例子中,控制台会依次输出 '2''1',因为 plugin2 在调用 modifyRsbuildConfig 时设置了 order 为 pre

const plugin1 = () => ({
  setup: (api) => {
    api.modifyRsbuildConfig(() => console.log('1'));
  },
});

const plugin2 = () => ({
  setup: (api) => {
    api.modifyRsbuildConfig({
      handler: () => console.log('2'),
      order: 'pre',
    });
  },
});

rsbuild.addPlugins([plugin1, plugin2]);

Common Hooks

modifyRsbuildConfig

修改传递给 Rsbuild 的配置项,你可以直接修改传入的 config 对象,也可以返回一个新的对象来替换传入的对象。

  • 类型:
type ModifyRsbuildConfigUtils = {
  mergeRsbuildConfig: typeof mergeRsbuildConfig;
};

function ModifyRsbuildConfig(
  callback: (
    config: RsbuildConfig,
    utils: ModifyRsbuildConfigUtils,
  ) => MaybePromise<RsbuildConfig | void>,
): void;
  • 示例: 为某个配置项设置一个默认值:
const myPlugin = () => ({
  setup: (api) => {
    api.modifyRsbuildConfig((config) => {
      config.html ||= {};
      config.html.title = 'My Default Title';
    });
  },
});
  • 示例: 通过 mergeRsbuildConfig 合并配置多个对象,并返回合并后的对象。
import type { RsbuildConfig } from '@rsbuild/core';

const myPlugin = () => ({
  setup: (api) => {
    api.modifyRsbuildConfig((userConfig, { mergeRsbuildConfig }) => {
      const extraConfig: RsbuildConfig = {
        source: {
          // ...
        },
        output: {
          // ...
        },
      };

      // extraConfig 会覆盖 userConfig 里的字段,
      // 如果你不希望覆盖 userConfig,可以调整为 `mergeRsbuildConfig(extraConfig, userConfig)`
      return mergeRsbuildConfig(userConfig, extraConfig);
    });
  },
});

modifyRspackConfig

修改最终的 Rspack 配置对象,你可以直接修改传入的 config 对象,也可以返回一个新的对象来替换传入的对象。

  • 类型:
type ModifyRspackConfigUtils = {
  env: NodeEnv;
  isDev: boolean;
  isProd: boolean;
  target: RsbuildTarget;
  isServer: boolean;
  isWebWorker: boolean;
  rspack: Rspack;
};

function ModifyRspackConfig(
  callback: (
    config: RspackConfig,
    utils: ModifyRspackConfigUtils,
  ) => Promise<RspackConfig | void> | RspackConfig | void,
): void;
  • 示例:
const myPlugin = () => ({
  setup: (api) => {
    api.modifyRspackConfig((config, utils) => {
      if (utils.env === 'development') {
        config.devtool = 'eval-cheap-source-map';
      }
    });
  },
});

modifyBundlerChain

rspack-chain 是一个用于配置 Rspack 的工具库。它提供了链式 API,使得配置 Rspack 变得更加灵活。通过使用 rspack-chain,你可以更方便地修改和扩展 Rspack 配置,而不需要直接操作复杂的配置对象。

modifyBundlerChain 用于调用 rspack-chain 来修改 Rspack 的配置。

  • 类型:
type ModifyBundlerChainUtils = {
  env: NodeEnv;
  isDev: boolean;
  isProd: boolean;
  target: RsbuildTarget;
  isServer: boolean;
  isWebWorker: boolean;
  CHAIN_ID: ChainIdentifier;
  HtmlPlugin: typeof import('html-webpack-plugin');
  bundler: {
    BannerPlugin: rspack.BannerPlugin;
    DefinePlugin: rspack.DefinePlugin;
    IgnorePlugin: rspack.IgnorePlugin;
    ProvidePlugin: rspack.ProvidePlugin;
    HotModuleReplacementPlugin: rspack.HotModuleReplacementPlugin;
  };
};

function ModifyBundlerChain(
  callback: (
    chain: RspackChain,
    utils: ModifyBundlerChainUtils,
  ) => Promise<void> | void,
): void;
  • 示例:
import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer';

const myPlugin = () => ({
  setup: (api) => {
    api.modifyBundlerChain((chain, utils) => {
      if (utils.env === 'development') {
        chain.devtool('eval');
      }

      chain.plugin('bundle-analyze').use(BundleAnalyzerPlugin);
    });
  },
});

modifyHTMLTags

修改注入到 HTML 中的标签。

  • 类型:
type HtmlBasicTag = {
  // 标签名
  tag: string;
  // 标签的属性
  attrs?: Record<string, string | boolean | null | undefined>;
  // 标签的 innerHTML
  children?: string;
};

type HTMLTags = {
  // 插入到 <head> 的标签组
  headTags: HtmlBasicTag[];
  // 插入到 <body> 的标签组
  bodyTags: HtmlBasicTag[];
};

type Context = {
  /**
   * Rspack 的 Compilation 对象
   */
  compilation: Rspack.Compilation;
  /**
   * 静态资源的 URL 前缀
   * @example 'https://example.com/'
   */
  assetPrefix: string;
  /**
   * HTML 文件的名称,相对于 dist 目录
   * @example 'index.html'
   */
  filename: string;
};

function ModifyHTMLTags(
  callback: (tags: HTMLTags, context: Context) => MaybePromise<HTMLTags>,
): void;
  • 示例:
const myPlugin = () => ({
  setup: (api) => {
    api.modifyHTMLTags(({ headTags, bodyTags }) => {
      headTags.push({
        tag: 'script',
        attrs: { src: 'https://example.com/foo.js' },
      });
      bodyTags.push({
        tag: 'script',
        children: 'console.log("hello world!");',
      });

      return { headTags, bodyTags };
    });
  },
});

onBeforeCreateCompiler

onBeforeCreateCompiler 是在创建底层 Compiler 实例前触发的回调函数,当你执行 rsbuild.startDevServerrsbuild.buildrsbuild.createCompiler 时,都会调用此钩子。

你可以通过 bundlerConfigs 参数获取到 Rspack 配置数组,数组中可能包含一份或多份 Rspack 配置,这取决于 Rsbuild output.targets 配置的值。

  • 类型:
function OnBeforeCreateCompiler(
  callback: (params: {
    bundlerConfigs: WebpackConfig[] | RspackConfig[];
  }) => Promise<void> | void,
): void;
  • 示例:
const myPlugin = () => ({
  setup: (api) => {
    api.onBeforeCreateCompiler(({ bundlerConfigs }) => {
      console.log('the bundler config is ', bundlerConfigs);
    });
  },
});

onAfterCreateCompiler

onAfterCreateCompiler 是在创建 Compiler 实例后、执行构建前触发的回调函数,当你执行 rsbuild.startDevServerrsbuild.buildrsbuild.createCompiler 时,都会调用此钩子。

你可以通过 compiler 参数获取到 Compiler 实例对象:

  • 类型:
function OnAfterCreateCompiler(callback: (params: {
  compiler: Compiler | MultiCompiler;
}) => Promise<void> | void;): void;
  • 示例:
const myPlugin = () => ({
  setup: (api) => {
    api.onAfterCreateCompiler(({ compiler }) => {
      console.log('the compiler is ', compiler);
    });
  },
});

Build Hooks

onBeforeBuild

onBeforeBuild 是在执行生产环境构建前触发的回调函数。

你可以通过 bundlerConfigs 参数获取到 Rspack 配置数组,数组中可能包含一份或多份 Rspack 配置,这取决于 Rsbuild output.targets 配置的值。

  • 类型:
function OnBeforeBuild(
  callback: (params: {
    bundlerConfigs?: WebpackConfig[] | RspackConfig[];
  }) => Promise<void> | void,
): void;
  • 示例:
const myPlugin = () => ({
  setup: (api) => {
    api.onBeforeBuild(({ bundlerConfigs }) => {
      console.log('the bundler config is ', bundlerConfigs);
    });
  },
});

onAfterBuild

onAfterBuild 是在执行生产环境构建后触发的回调函数,你可以通过 stats 参数获取到构建结果信息:

另外,在 watch 模式下你可以通过 isFirstCompile 来判断是否为首次构建。

  • 类型:
function OnAfterBuild(
  callback: (params: {
    isFirstCompile: boolean;
    stats?: Stats | MultiStats;
  }) => Promise<void> | void,
): void;
  • 示例:
const myPlugin = () => ({
  setup: (api) => {
    api.onAfterBuild(({ isFirstCompile, stats }) => {
      console.log(stats?.toJson(), isFirstCompile);
    });
  },
});

Dev Hooks

onBeforeStartDevServer

在启动开发服务器前调用。

  • 类型:
function OnBeforeStartDevServer(callback: () => Promise<void> | void): void;
  • 示例:
const myPlugin = () => ({
  setup: (api) => {
    api.onBeforeStartDevServer(() => {
      console.log('before start!');
    });
  },
});

onAfterStartDevServer

在启动开发服务器后调用。你可以通过 port 参数获得开发服务器监听的端口号,通过 routes 获得页面路由信息。

  • 类型:
type Routes = Array<{
  entryName: string;
  pathname: string;
}>;

function OnAfterStartDevServer(
  callback: (params: { port: number; routes: Routes }) => Promise<void> | void,
): void;
  • 示例:
const myPlugin = () => ({
  setup: (api) => {
    api.onAfterStartDevServer(({ port, routes }) => {
      console.log('this port is: ', port);
      console.log('this routes is: ', routes);
    });
  },
});

onDevCompileDone

在每次开发环境构建结束后调用,你可以通过 isFirstCompile 来判断是否为首次构建。

  • 类型:
function OnDevCompileDone(
  callback: (params: {
    isFirstCompile: boolean;
    stats: Stats | MultiStats;
  }) => Promise<void> | void,
): void;
  • 示例:
const myPlugin = () => ({
  setup: (api) => {
    api.onDevCompileDone(({ isFirstCompile }) => {
      if (isFirstCompile) {
        console.log('first compile!');
      } else {
        console.log('re-compile!');
      }
    });
  },
});

onCloseDevServer

关闭开发服务器时调用。

  • 类型:
function onCloseDevServer(callback: () => Promise<void> | void): void;
  • 示例:
rsbuild.onCloseDevServer(async () => {
  console.log('close dev server!');
});

Preview Hooks

onBeforeStartProdServer

在启动生产预览服务器前调用。

  • 类型:
function OnBeforeStartProdServer(callback: () => Promise<void> | void): void;
  • 示例:
const myPlugin = () => ({
  setup: (api) => {
    api.onBeforeStartProdServer(() => {
      console.log('before start!');
    });
  },
});

onAfterStartProdServer

在启动生产预览服务器后调用,你可以通过 port 参数获得生产服务器监听的端口号,通过 routes 获得页面路由信息。

  • 类型:
type Routes = Array<{
  entryName: string;
  pathname: string;
}>;

function OnAfterStartProdServer(
  callback: (params: { port: number; routes: Routes }) => Promise<void> | void,
): void;
  • 示例:
const myPlugin = () => ({
  setup: (api) => {
    api.onAfterStartProdServer(({ port, routes }) => {
      console.log('this port is: ', port);
      console.log('this routes is: ', routes);
    });
  },
});

Other Hooks

onExit

在进程即将退出时调用,这个钩子只能执行同步代码。

  • 类型:
function OnExit(callback: () => void): void;
  • 示例:
const myPlugin = () => ({
  setup: (api) => {
    api.onExit(() => {
      console.log('exit!');
    });
  },
});