编译钩子

信息

Rspack 的主要编译逻辑在 Rust 端运行。出于稳定性、性能和架构等因素,使用钩子时,Rust 端编译对象在传输到 JavaScript 端后,对这些对象的修改不会同步到 Rust 端。因此,大多数钩子是“只读”的。

概述

buildModule

只读

在模块构建开始之前触发。

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

executeModule

只读

如果存在编译时执行模块,此钩子将在执行它们时被调用。

  • 类型: SyncHook<[ExecuteModuleArgument, ExecuteModuleContext]>
  • 参数
    • ExecuteModuleArgument: 编译时执行模块的参数
    • ExecuteModuleContext: 编译时执行模块的上下文

succeedModule

只读

模块构建成功时执行。

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

finishModules

只读

所有模块都已成功构建且无错误时调用。

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

seal

编译停止接受新模块并开始优化模块时调用。

  • 类型: SyncHook<[]>

optimizeModules

只读

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

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

afterOptimizeModules

只读

模块优化完成后调用。

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

optimizeTree

只读

优化依赖树之前调用。

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

optimizeChunkModules

只读

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

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

additionalTreeRuntimeRequirements

树运行时需求收集完成后调用。

  • 类型: SyncHook<[Chunk, Set<RuntimeGlobals>]>
  • 参数
    • 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) => {
              // add a runtime module which define `__webpack_require__.h`
              set.add(RuntimeGlobals.getFullHash);
            },
          );
        });
      },
    },
  ],
};
index.js
// will print hash of this compilation
console.log(__webpack_require__.h);

runtimeRequirementInTree

在将运行时模块添加到编译时调用。

  • 类型: HookMap<SyncBailHook<[Chunk, Set<RuntimeGlobals>]>>
  • 参数
    • 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) => {
              // add a runtime module to access public path
              set.add(RuntimeGlobals.publicPath);
              compilation.addRuntimeModule(chunk, new CustomRuntimeModule());
            });
        });
      },
    },
  ],
};
index.js
// will print "/subpath/index.js"
console.log(__webpack_require__.mock('index.js'));

runtimeModule

运行时模块被添加到编译后调用。

  • 类型: SyncHook<[RuntimeModule, Chunk]>
  • 参数
    • RuntimeModule: 运行时模块实例
    • 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
// will print "/override/public/path"
console.log(__webpack_require__.p);

processAssets

在发出之前处理资产。

  • 类型: AsyncSeriesHook<Assets>
  • 钩子参数
    • name: string — 插件的名称
    • stage: Stage — 要插入的阶段(请参见下面的 处理资产阶段
  • 参数
    • Assets: Record<string, Source>: 一个普通对象,其中键是资产的路径名,值是资产的数据,由 Source 表示。

处理资产示例

  • PROCESS_ASSETS_STAGE_ADDITIONAL 阶段发出一个新的资产
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);
    },
  );
});
  • 更新现有资产
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);
    },
  );
});
  • 删除资产
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);
      }
    },
  );
});

处理资产阶段

以下是支持的阶段列表。Rspack 将按从上到下的顺序依次执行这些阶段。请根据您需要执行的操作选择合适的阶段。

  • PROCESS_ASSETS_STAGE_ADDITIONAL — 向编译中添加额外的资产。
  • PROCESS_ASSETS_STAGE_PRE_PROCESS — 对资产进行基本预处理。
  • PROCESS_ASSETS_STAGE_DERIVED — 从现有资产派生新资产。
  • PROCESS_ASSETS_STAGE_ADDITIONS — 向现有资产添加额外的部分,例如横幅或初始化代码。
  • PROCESS_ASSETS_STAGE_OPTIMIZE — 以一般方式优化现有资产。
  • PROCESS_ASSETS_STAGE_OPTIMIZE_COUNT — 优化现有资产的数量,例如通过合并它们。
  • PROCESS_ASSETS_STAGE_OPTIMIZE_COMPATIBILITY — 优化现有资产的兼容性,例如添加 polyfills 或供应商前缀。
  • PROCESS_ASSETS_STAGE_OPTIMIZE_SIZE — 优化现有资产的大小,例如通过最小化或省略空白。
  • PROCESS_ASSETS_STAGE_DEV_TOOLING — 向资产添加开发工具,例如通过提取源映射。
  • PROCESS_ASSETS_STAGE_OPTIMIZE_INLINE — 通过将资产内联到其他资产中来优化现有资产的数量。
  • PROCESS_ASSETS_STAGE_SUMMARIZE — 总结现有资产列表。
  • PROCESS_ASSETS_STAGE_OPTIMIZE_HASH — 优化资产的哈希值,例如通过生成资产内容的实际哈希值。
  • PROCESS_ASSETS_STAGE_OPTIMIZE_TRANSFER — 优化现有资产的传输,例如通过准备压缩(gzip)文件作为单独的资产。
  • PROCESS_ASSETS_STAGE_ANALYSE — 分析现有资产。
  • PROCESS_ASSETS_STAGE_REPORT — 创建用于报告目的的资产。

afterProcessAssets

只读

processAssets 钩子无错误完成之后调用。

  • 类型: SyncHook<Assets>
  • 参数
    • Assets: Record<string, Source>: 资产实例列表

afterSeal

只读

密封阶段后调用。

  • 类型: AsyncSeriesHook<[]>

chunkHash

只读

触发以发出每个块的哈希值。

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

chunkAsset

只读

从块中添加资产到编译时触发。

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

childCompiler

只读

设置子编译器后执行。

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

statsPreset

只读

此钩子类似于在使用预设时触发的动作列表。它接受一个选项对象。当插件管理预设时,它应该谨慎地更改此对象中的设置,而不要替换现有设置。

  • 类型: SyncHook<[Partial<StatsOptions>, CreateStatsOptionsContext]>
  • 参数
    • Partial<StatsOptions>:统计选项
    • CreateStatsOptionsContext:统计上下文

以下是一个说明性的插件示例

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>:统计选项
    • CreateStatsOptionsContext:统计上下文

以下是一个说明性的插件示例

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 类的访问。

  • 类型: SyncHook<[StatsFactory, StatsOptions]>
  • 参数
    • StatsFactory:统计工厂实例,有关更多详细信息,请参见统计工厂钩子
    • StatsOptions:统计选项

statsPrinter

只读

此钩子提供对特定选项的 StatsPrinter 类的访问。

  • 类型: SyncHook<[StatsPrinter, StatsOptions]>
  • 参数
    • StatsPrinter:统计打印机实例,有关更多详细信息,请参见统计打印机钩子
    • StatsOptions:统计选项