Compilation 钩子

INFO

Rspack 主要编译逻辑运行在 Rust 侧。出于稳定性、性能、架构等因素,在使用钩子时 Rust 侧编译对象传输到 JavaScript 侧后,对 JavaScript 侧的修改不会被同步到 Rust 侧。因此绝大部分钩子为“只读”。

Overview

buildModule

只读

在模块被构建之前调用。

  • 类型: SyncHook<[Module]>
  • 参数:
    • Module:模块实例

executeModule

只读

若存在编译期执行模块,将在模块被执行时调用。

  • 类型: SyncHook<[ExecuteModuleArgument, ExecuteModuleContext]>
  • 参数:
    • ExecuteModuleArgument:模块运行参数
    • ExecuteModuleContext:模块运行上下文

succeedModule

只读

在模块成功构建后调用。

  • 类型: SyncHook<[Module]>
  • 参数:
    • Module:模块实例

finishModules

只读

当所有模块都没有错误地构建完成时调用。

  • 类型: AsyncSeriesHook<[Module[]]>
  • 参数:
    • Module[]:所有模块列表

optimizeModules

只读

在模块优化阶段开始时调用。

  • 类型: SyncBailHook<[Module[]]>
  • 参数:
    • Module[]:所有模块列表

seal

停止接收新模块并开始优化前触发。

  • 类型: SyncHook<[]>

afterOptimizeModules

只读

在模块优化完成之后调用。

  • 类型: SyncBailHook<[Module[]]>
  • 参数:
    • Module[]:所有模块列表

optimizeTree

只读

在优化依赖树之前调用。

  • 类型: AsyncSeriesHook<[Chunk[], Module[]]>
  • 参数:
    • Chunk[]:Chunk 列表:
    • Module[]:模块列表

optimizeChunkModules

只读

在树优化之后,chunk 模块优化开始时调用。

  • 类型: AsyncSeriesBailHook<[Chunk[], Module[]]>
  • 参数:
    • Chunk[]:Chunk 列表:
    • Module[]:模块列表

additionalTreeRuntimeRequirements

在树运行时依赖计算完成后调用。

  • 类型: SyncHook<[Chunk, Set<RuntimeGlobals>]>
  • 参数:
    • Chunk:Chunk 实例
    • Set<RuntimeGlobals>:运行时依赖集合

可通过修改运行时依赖集合以添加额外的内置运行时模块:

rspack.config.js
module.exports = {
  entry: './index.js',
  plugins: [
    {
      apply(compiler) {
        const { RuntimeGlobals } = compiler.webpack;
        compiler.hooks.thisCompilation.tap('CustomPlugin', compilation => {
          compilation.hooks.additionalTreeRuntimeRequirements.tap(
            'CustomPlugin',
            (_, set) => {
              // 添加访问 compilation 哈希值的运行时模块
              set.add(RuntimeGlobals.getFullHash);
            },
          );
        });
      },
    },
  ],
};
index.js
// 将打印 compilation 的哈希值
console.log(__webpack_require__.h);

runtimeRequirementInTree

在根据运行时依赖以添加运行时模块时调用。

  • 类型: HookMap<SyncBailHook<[Chunk, Set<RuntimeGlobals>]>>
  • 参数:
    • Chunk:Chunk 实例
    • Set<RuntimeGlobals>:运行时依赖集合

可通过修改运行时依赖集合以添加额外的内置运行时模块,也可通过 compilation.addRuntimeModule 添加自定义运行时模块:

rspack.config.js
module.exports = {
  entry: './index.js',
  plugins: [
    {
      apply(compiler) {
        const { RuntimeGlobals, RuntimeModule } = compiler.webpack;
        class CustomRuntimeModule extends RuntimeModule {
          constructor() {
            super('custom');
          }

          generate() {
            const compilation = this.compilation;
            return `
            __webpack_require__.mock = function(file) {
              return ${RuntimeGlobals.publicPath} + "/subpath/" + file;
            };
          `;
          }
        }

        compiler.hooks.thisCompilation.tap('CustomPlugin', compilation => {
          compilation.hooks.runtimeRequirementInTree
            .for(RuntimeGlobals.ensureChunkHandlers)
            .tap('CustomPlugin', (chunk, set) => {
              // 添加访问 publicPath 的运行时模块
              set.add(RuntimeGlobals.publicPath);
              // 添加自定义运行时模块
              compilation.addRuntimeModule(chunk, new CustomRuntimeModule());
            });
        });
      },
    },
  ],
};
index.js
// 将打印 "/subpath/index.js"
console.log(__webpack_require__.mock('index.js'));

runtimeModule

在运行时模块被添加后调用。

  • 类型: SyncHook<[RuntimeModule, Chunk]>
  • 参数:
    • RuntimeModule:运行时模块
    • Chunk:Chunk 实例

可通过 source 字段修改该运行时模块生成的代码。

rspack.config.js
module.exports = {
  plugins: [
    {
      apply(compiler) {
        const { RuntimeGlobals } = compiler.webpack;
        compiler.hooks.compilation.tap('CustomPlugin', compilation => {
          compilation.hooks.runtimeModule.tap(
            'CustomPlugin',
            (module, chunk) => {
              if (module.name === 'public_path' && chunk.name === 'main') {
                const originSource = module.source.source.toString('utf-8');
                module.source.source = Buffer.from(
                  `${RuntimeGlobals.publicPath} = "/override/public/path";\n`,
                  'utf-8',
                );
              }
            },
          );
        });
      },
    },
  ],
};
index.js
// 将打印 "/override/public/path"
console.log(__webpack_require__.p);

processAssets

在产物输出之前进行修改产物。

  • 类型: AsyncSeriesHook<Assets>
  • hook 参数:
  • 参数:
    • Assets: Record<string, Source>: 一个对象,其中 key 是 asset 的路径名,值是由 Source 表示的 asset 数据。

Process assets 示例

  • PROCESS_ASSETS_STAGE_ADDITIONAL 阶段输出一个新的 asset:
compiler.hooks.thisCompilation.tap('MyPlugin', compilation => {
  compilation.hooks.processAssets.tap(
    {
      name: 'MyPlugin',
      stage: compilation.PROCESS_ASSETS_STAGE_ADDITIONAL,
    },
    assets => {
      const { RawSource } = compiler.webpack.sources;
      const source = new RawSource('This is a new asset!');
      compilation.emitAsset('new-asset.txt', source);
    },
  );
});
  • 更新一个已经存在的 asset:
compiler.hooks.thisCompilation.tap('MyPlugin', compilation => {
  compilation.hooks.processAssets.tap(
    {
      name: 'MyPlugin',
      stage: compilation.PROCESS_ASSETS_STAGE_ADDITIONS,
    },
    assets => {
      const asset = assets['foo.js'];
      if (!asset) {
        return;
      }

      const { RawSource } = compiler.webpack.sources;
      const oldContent = asset.source();
      const newContent = oldContent + '\nconsole.log("hello world!")';
      const source = new RawSource(newContent);

      compilation.updateAsset(assetName, source);
    },
  );
});
  • 移除一个 asset:
compiler.hooks.thisCompilation.tap('MyPlugin', compilation => {
  compilation.hooks.processAssets.tap(
    {
      name: 'MyPlugin',
      stage: compilation.PROCESS_ASSETS_STAGE_OPTIMIZE,
    },
    assets => {
      const assetName = 'unwanted-script.js';
      if (assets[assetName]) {
        compilation.deleteAsset(assetName);
      }
    },
  );
});

Process assets stages

下面是支持的 stage 列表,Rspack 会按由上至下的顺序依次执行这些 stages,请根据你需要进行的操作来选择合适的 stage。

  • PROCESS_ASSETS_STAGE_ADDITIONAL — 在编译中添加额外的 asset。
  • PROCESS_ASSETS_STAGE_PRE_PROCESS — asset 进行了基础的预处理。
  • PROCESS_ASSETS_STAGE_DERIVED — 从现有 asset 中派生新的 asset。
  • PROCESS_ASSETS_STAGE_ADDITIONS — 为现有的 asset 添加额外的内容,例如 banner 或初始代码。
  • PROCESS_ASSETS_STAGE_OPTIMIZE — 以通用的方式优化现有 asset。
  • PROCESS_ASSETS_STAGE_OPTIMIZE_COUNT — 优化现有 asset 的数量,例如,进行合并操作。
  • PROCESS_ASSETS_STAGE_OPTIMIZE_COMPATIBILITY — 优化现有 asset 的兼容性,例如添加 polyfills 或者 vendor prefixes。
  • PROCESS_ASSETS_STAGE_OPTIMIZE_SIZE — 优化现有 asset 的大小,例如进行压缩或者删除空格。
  • PROCESS_ASSETS_STAGE_DEV_TOOLING — 为 asset 添加开发者工具,例如,提取 source map。
  • PROCESS_ASSETS_STAGE_OPTIMIZE_INLINE — 将 asset 内联到其他 asset 中来优化现有 asset 数量。
  • PROCESS_ASSETS_STAGE_SUMMARIZE — 整理现有 asset 列表。
  • PROCESS_ASSETS_STAGE_OPTIMIZE_HASH — 优化 asset 的 hash 值,例如,生成基于 asset 内容的真实 hash 值。
  • PROCESS_ASSETS_STAGE_OPTIMIZE_TRANSFER — 优化已有 asset 的转换操作,例如对 asset 进行压缩,并作为独立的 asset。
  • PROCESS_ASSETS_STAGE_ANALYSE — 分析已有 asset。
  • PROCESS_ASSETS_STAGE_REPORT — 创建用于上报的 asset。

afterProcessAssets

只读

processAssets hook 无错误执行后调用。

  • 类型: SyncHook<Assets>
  • 参数:
    • Assets: Record<string, Source>:产物资源映射表

afterSeal

只读

在 seal 阶段结束后调用。

  • 类型: AsyncSeriesHook<[]>

chunkHash

只读

触发来为每个 chunk 生成 hash。

  • 类型: SyncHook<[Chunk, Hash]>
  • 参数:
    • Chunk:Chunk 实例
    • Hash:Chunk 哈希实例

chunkAsset

只读

一个 chunk 中的一个 asset 被添加到 compilation 时调用。

  • 类型: SyncHook<[Chunk, string]>
  • 参数:
    • Chunk:Chunk 实例
    • string:产物文件名

childCompiler

只读

创建子 compiler 之后调用。

  • 类型: SyncHook<[Compiler, string, number]>
  • 参数:
    • Compiler:子编译实例:
    • string:子编译名称
    • number:子编译索引

statsPreset

只读

当使用预设 stats 配置时触发。接收一个 stats 配置对象,当插件管理 stats 预设配置时,它应当在配置对象上仔细地修改,而非直接替换整个配置对象。

  • 类型: SyncHook<[Partial<StatsOptions>, CreateStatsOptionsContext]>
  • 参数:
    • Partial<StatsOptions>:Stats 配置
    • CreateStatsOptionsContext:Stats 上下文

以如下插件为例:

compilation.hooks.statsPreset.for('my-preset').tap('MyPlugin', options => {
  if (options.all === undefined) options.all = true;
});

该插件确保对于预设 "my-preset",如果 all 选项未定义,则默认为 true

statsNormalize

只读

此钩子用于将选项对象转换为便于后续钩子使用的格式。它还确保缺失的选项被设置为默认值。

  • 类型: SyncHook<[Partial<StatsOptions>, CreateStatsOptionsContext]>
  • 参数:
    • Partial<StatsOptions>:Stats 配置
    • CreateStatsOptionsContext:Stats 上下文

以如下插件为例:

compilation.hooks.statsNormalize.tap('MyPlugin', options => {
  if (options.myOption === undefined) options.myOption = [];

  if (!Array.isArray(options.myOption)) options.myOptions = [options.myOptions];
});

在这个插件中,如果 myOption 缺失,会将其设置为 []。此外,它确保 myOption 始终是一个数组,即使它最初被定义为单个值。

statsFactory

只读

此钩子提供了对 StatsFactory 的访问,以调用其钩子。该类用于构造 Stats 对象。

  • 类型: SyncHook<[StatsFactory, StatsOptions]>
  • 参数:

statsPrinter

只读

此钩子提供了对 StatsPrinter 的访问,以调用其钩子。该类用于打印 Stats 信息。

  • 类型: SyncHook<[StatsPrinter, StatsOptions]>
  • 参数: