> For AI agents: the complete documentation index is available at /zh/llms.txt, the full documentation bundle is available at /zh/llms-full.txt.

# NormalModuleFactory 钩子

`NormalModuleFactory` 被 [Compiler](/zh/api/javascript-api/compiler.md) 用来生成模块（`NormalModule`）。从每个入口模块（`entry`）开始，它解析模块的依赖请求，得到依赖的最终路径，从而根据最终路径创建 `NormalModule`，并进一步解析新创建出的模块的依赖请求，递归此过程，以此将每个模块通过 `NormalModuleFactory` 创建为 `NormalModule`。

`NormalModuleFactory` 提供以下生命周期钩子。它们可以像 `Compiler` 钩子一样被使用：

```js
NormalModuleFactory.hooks.someHook.tap(/* ... */);
```

所有钩子均继承自 `Tapable`，除了 `tap()`，你还可以使用 `tapAsync()` 和 `tapPromise()`，具体取决于钩子的类型。

## 导入属性


[Added in v2.2.3](https://github.com/web-infra-dev/rspack/releases/tag/v2.2.3)

**类型：** `Readonly<Record<string, string>>`

`ResolveData.attributes` 保存触发本次解析的依赖上的[导入属性](https://github.com/tc39/proposal-import-attributes)，例如 `import data from './data.json' with { type: 'json' }` 对应 `{ type: 'json' }`。[`beforeResolve`](#beforeresolve)、[`factorize`](#factorize)、[`resolve`](#resolve) 和 [`afterResolve`](#afterresolve) 四个钩子均可读取该字段；当请求不带任何导入属性时，它的值为 `undefined`。

```js
compiler.hooks.compilation.tap(
  'MyPlugin',
  (compilation, { normalModuleFactory }) => {
    normalModuleFactory.hooks.beforeResolve.tap('MyPlugin', (resolveData) => {
      if (resolveData.attributes?.type === 'json') {
        // ...
      }
    });
  },
);
```

## `beforeResolve`

`AsyncSeriesBailHook<[ResolveData]>`

当遇到新的依赖请求时调用。可以通过返回 `false` 来忽略依赖项。否则，应该返回 `undefined` 以继续。

`beforeResolve` 在模块解析过程的最开始阶段触发，用于在模块解析之前，拦截并修改模块的请求信息。通过这个钩子，可以对模块请求进行预处理、过滤或阻止某些模块的解析。

```js
compiler.hooks.compilation.tap(
  'MyPlugin',
  (compilation, { normalModuleFactory }) => {
    normalModuleFactory.hooks.beforeResolve.tap('MyPlugin', (resolveData) => {
      // 访问和修改模块请求信息
      console.log(JSON.stringify(resolveData, null, 2));
    });
  },
);
```


ResolveData.ts

## `factorize`

`AsyncSeriesBailHook<[ResolveData]>`

在请求开始解析之前调用，返回 `undefined` 以继续。

`factorize` 用于在模块实例化之前插入自定义逻辑，修改模块的创建过程。

```js
compiler.hooks.compilation.tap(
  'MyPlugin',
  (compilation, { normalModuleFactory }) => {
    normalModuleFactory.hooks.factorize.tap('MyPlugin', (resolveData) => {
      // 访问和修改模块请求信息
      console.log(JSON.stringify(resolveData, null, 2));
    });
  },
);
```


ResolveData.ts

:::warning
目前不支持返回模块实例。这个钩子会影响模块的创建过程，请谨慎使用。
:::

## `resolve`

`AsyncSeriesBailHook<[ResolveData]>`

在请求被解析之前调用，返回 `undefined` 以继续。`resolve` 用于在模块解析开始之前，拦截并修改模块的请求信息。通过这个钩子可以对模块请求进行预处理。

```js
compiler.hooks.compilation.tap(
  'MyPlugin',
  (compilation, { normalModuleFactory }) => {
    normalModuleFactory.hooks.resolve.tap('MyPlugin', (resolveData) => {
      // 访问和修改模块请求信息
      console.log(JSON.stringify(resolveData, null, 2));
    });
  },
);
```


ResolveData.ts

:::warning
目前不支持返回模块实例和 `false`。
:::

## `afterResolve`

`AsyncSeriesBailHook<[ResolveData], false | void>`

在模块标识符被解析后调用。

返回 `false` 可以忽略该请求并停止后续回调。返回 `undefined` 或不返回值则继续执行。

`afterResolve` 用于在模块解析完成之后，进一步处理或修改模块的解析结果。它在模块解析过程的末尾触发，意味着在这个阶段，模块的路径、请求信息等已经确定。

```js
compiler.hooks.compilation.tap(
  'MyPlugin',
  (compilation, { normalModuleFactory }) => {
    normalModuleFactory.hooks.afterResolve.tap('MyPlugin', (resolveData) => {
      if (resolveData.request === './optional-module.js') {
        return false;
      }

      // 访问和修改解析后的模块信息
      console.log(JSON.stringify(resolveData, null, 2));
    });
  },
);
```


ResolveData.ts

## `createModule`

`AsyncSeriesBailHook<[NormalModuleCreateData, {}]>`

在请求解析完成后、Rspack 创建 normal module 实例之前调用。返回 `undefined` 表示继续默认的模块创建流程。

```js
compiler.hooks.compilation.tap(
  'MyPlugin',
  (compilation, { normalModuleFactory }) => {
    normalModuleFactory.hooks.createModule.tap('MyPlugin', (createData) => {
      // 查看模块创建数据
      console.log(createData.resourceResolveData.resource);
    });
  },
);
```

## `resolveForScheme`

`HookMap<AsyncSeriesBailHook<[ResourceDataWithData], true | void>>`

在解析带有 scheme（URI）的模块标识符之前调用。

`resolveForScheme` 通常用于处理具有特定协议的模块标识符，例如 `file://`、`https://` 等。

通过 `.for(scheme)` 选择协议，例如 `.for('https')`，再在返回的钩子上注册回调。处理完资源后返回 `true`，可以跳过该协议的后续回调。返回 `undefined` 或不返回值则继续执行下一个回调。

```js
compiler.hooks.compilation.tap(
  'MyPlugin',
  (compilation, { normalModuleFactory }) => {
    normalModuleFactory.hooks.resolveForScheme
      .for('https')
      .tap('MyPlugin', (resourceData) => {
        console.log(JSON.stringify(resourceData, null, 2));
      });
  },
);
```


ResourceDataWithData.ts