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

# NormalModuleFactory

`NormalModuleFactory` is used by the [Compiler](/api/javascript-api/compiler.md) to generate modules (`NormalModule`). Starting from each entry module (`entry`), it resolves the dependency requests of the modules to obtain the final paths of the dependencies. Based on these final paths, it creates `NormalModule` instances. It then further resolves the dependency requests of the newly created modules. This process is recursive, creating each module as a `NormalModule` through `NormalModuleFactory`.

`NormalModuleFactory` provides the following lifecycle hooks. These can be used just like `Compiler` hooks:

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

All hooks inherit from `Tapable`. In addition to `tap()`, you can also use `tapAsync()` and `tapPromise()`, depending on the type of the hook.

## Import attributes


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

**Type:** `Readonly<Record<string, string>>`

`ResolveData.attributes` holds the [import attributes](https://github.com/tc39/proposal-import-attributes) of the dependency that triggered the resolution, for example `{ type: 'json' }` for `import data from './data.json' with { type: 'json' }`. It is available on the [`beforeResolve`](#beforeresolve), [`factorize`](#factorize), [`resolve`](#resolve) and [`afterResolve`](#afterresolve) hooks, and is `undefined` when the request carries no attributes.

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

## `beforeResolve`

`AsyncSeriesBailHook<[ResolveData]>`

Called when a new dependency request is encountered. A dependency can be ignored by returning `false`. Otherwise, it should return `undefined` to proceed.

The `beforeResolve` hook is called at the very beginning of the module resolution process, allowing the module's request information to be intercepted and modified before the resolution takes place. This hook can be used to pre-process, filter or block the resolution of certain modules.

```js
compiler.hooks.compilation.tap(
  'MyPlugin',
  (compilation, { normalModuleFactory }) => {
    normalModuleFactory.hooks.beforeResolve.tap('MyPlugin', (resolveData) => {
      // access and modify module request information
      console.log(JSON.stringify(resolveData, null, 2));
    });
  },
);
```


ResolveData.ts

## `factorize`

`AsyncSeriesBailHook<[ResolveData]>`

Called before initiating resolve. It should return undefined to proceed.

The `factorize` hook is used to add custom logic before a module is instantiated, modifying the module creation process.

```js
compiler.hooks.compilation.tap(
  'MyPlugin',
  (compilation, { normalModuleFactory }) => {
    normalModuleFactory.hooks.factorize.tap('MyPlugin', (resolveData) => {
      // access and modify module request information
      console.log(JSON.stringify(resolveData, null, 2));
    });
  },
);
```


ResolveData.ts

:::warning
Returning module instance is not supported for now. This hook will affect the module creation process, so use it with caution.
:::

## `resolve`

`AsyncSeriesBailHook<[ResolveData]>`

Called before the request is resolved, it should return `undefined` to continue. The `resolve` hook can be used to intercept and modify module request information before module resolution begins. This hook allows for preprocessing of module requests.

```js
compiler.hooks.compilation.tap(
  'MyPlugin',
  (compilation, { normalModuleFactory }) => {
    normalModuleFactory.hooks.resolve.tap('MyPlugin', (resolveData) => {
      // access and modify module request information
      console.log(JSON.stringify(resolveData, null, 2));
    });
  },
);
```


ResolveData.ts

:::warning
Returning module instance or `false` is not supported for now.
:::

## `afterResolve`

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

Called after the module specifier is resolved.

Return `false` to ignore the request and stop subsequent taps. Return `undefined` or omit the return value to continue.

The `afterResolve` hook is used to further process or modify the results after the module has been resolved. It is called at the end of the module resolution process, which means that at this stage, the module's path, request information, etc., have already been determined.

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

      // access and modify the resolved module information
      console.log(JSON.stringify(resolveData, null, 2));
    });
  },
);
```


ResolveData.ts

## `createModule`

`AsyncSeriesBailHook<[NormalModuleCreateData, {}]>`

Called after a request has been resolved and before Rspack creates the normal module instance. Return `undefined` to continue with the default module creation flow.

```js
compiler.hooks.compilation.tap(
  'MyPlugin',
  (compilation, { normalModuleFactory }) => {
    normalModuleFactory.hooks.createModule.tap('MyPlugin', (createData) => {
      // inspect module creation data
      console.log(createData.resourceResolveData.resource);
    });
  },
);
```

## `resolveForScheme`

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

Called before a module specifier with scheme (URI) is resolved.

The `resolveForScheme` is typically used to handle module specifiers that have a specific protocol, such as `file://`, `https://`, etc.

Select a scheme with `.for(scheme)`, such as `.for('https')`, then register a tap on the returned hook. Return `true` when the resource has been handled to skip subsequent taps for that scheme. Return `undefined` or omit the return value to continue to the next tap.

```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