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

# Loader 上下文

Loader 上下文表示 loader 内部可用的属性，这些属性在 loader 中通过 `this` 属性进行访问。

## this.addBuildDependency()

- **类型：**

```ts
function addBuildDependency(file: string): void;
```

添加一个文件作为 loader 结果的构建依赖。当构建依赖发生变化时，Rspack 会使持久化缓存失效。

它适用于会影响 loader 行为或转换结果的文件，例如 loader 的配置文件。

```js title="loader.mjs"
import path from 'node:path';

export default function loader(source) {
  this.addBuildDependency(
    path.resolve(this.rootContext, 'custom-loader.config.js'),
  );
  return source;
}
```

:::tip
`this.addBuildDependency()` 不会将文件添加为 watch 依赖。如果文件变化时还应在 watch 模式下触发重新构建，请同时调用 [this.addDependency()](#thisadddependency)。
:::

## this.addContextDependency()

- **类型：**

```ts
function addContextDependency(directory: string): void;
```

添加目录作为 loader 结果的依赖，使目录中文件的任何变化可以被监听到。

例如，添加 `src/static` 目录作为依赖，当目录中的文件发生变化时，会触发重新构建。

```js title="loader.mjs"
import path from 'node:path';

export default function loader(source) {
  this.addContextDependency(path.resolve(this.rootContext, 'src/static'));
  return source;
}
```

## this.addDependency()

- **类型：**

```ts
function addDependency(file: string): void;
```

添加一个文件作为 loader 结果的依赖，使它们的任何变化可以被监听到。例如，`sass-loader`、`less-loader` 就使用了这个技巧，当导入的样式文件发生变化时就会重新编译。

```js title="loader.mjs"
import path from 'node:path';

export default function loader(source) {
  this.addDependency(path.resolve(this.rootContext, 'src/styles/foo.scss'));
  return source;
}
```

## this.addMissingDependency()

- **类型：**

```ts
function addMissingDependency(file: string): void;
```

添加一个当前不存在的文件作为 loader 结果的依赖，使它们的创建和任何变化可以被监听到。例如，当该路径下新建了文件时，会触发重新构建。

```js title="loader.mjs"
import path from 'node:path';

export default function loader(source) {
  this.addMissingDependency(
    path.resolve(this.rootContext, 'src/dynamic-file.json'),
  );
  return source;
}
```

## this.async()

- **类型：** `() => LoaderContextCallback`

告诉 Rspack 这个 loader 将会异步被调用。返回值为 [this.callback](#thiscallback)。

> 查看 [异步 loader](/zh/api/loader-api/writing-loaders.md#异步-loader) 了解如何使用。

## this.cacheable()

- **类型：**

```ts
function cacheable(flag: boolean = true): void;
```

默认情况下，当前模块经过整条 loader 链生成的最终构建结果可以被缓存。调用 `this.cacheable(false)` 会将该结果标记为不可缓存。

设置后，后续 loader 即使调用 `this.cacheable(true)` 或 `this.cacheable()` 也无法重新启用缓存。只有 [`this.clearDependencies()`](#thiscleardependencies) 会重置这个状态。

```js title="loader.mjs"
export default function loader(source) {
  this.cacheable(false);
  return source;
}
```

## this.callback()

- **类型：**

```ts
interface AdditionalData {
  [index: string]: any;
}

function callback(
  err?: Error | null,
  content?: string | Buffer,
  sourceMap?: string | Rspack.RawSourceMap,
  additionalData?: AdditionalData,
): void;
```

调用 `this.callback()` 返回 loader 的处理结果，同步或异步 loader 均可使用。各参数的含义如下：

1. `err`：loader 执行失败时传入 `Error`；执行成功时传入 `null` 或 `undefined`。
2. `content`：转换后的模块内容，可以是 `string` 或 `Buffer`。只报告错误时可以省略。
3. `sourceMap`：可选的 source map，可以是 `string` 或 `Rspack.RawSourceMap`。
4. `additionalData`：可选的附加数据。Rspack 会将它作为第三个参数传给 loader 链中的下一个 loader。

> 查看 [同步 loader](/zh/api/loader-api/writing-loaders.md#同步-loader) 了解如何使用。

:::warning
当这个函数被调用时，你应该返回 `undefined` 以避免 loader 结果的歧义。

`content`、`sourceMap` 和 `additionalData` 会传递给 loader 链中的下一个 loader。
:::

## this.clearDependencies()

- **类型：**

```ts
function clearDependencies(): void;
```

清除 loader 链中已收集的所有[文件依赖](#thisadddependency)、[上下文依赖](#thisaddcontextdependency)和[缺失依赖](#thisaddmissingdependency)，但不会清除 [build dependencies](#thisaddbuilddependency)。它还会将 [`cacheable`](#thiscacheable) 重置为 `true`，因此此前任何 loader 调用 `this.cacheable(false)` 产生的设置都会失效。

只有在当前 loader 会为最终结果重新注册全部所需依赖时，才应调用此方法。

## this.context

- **类型：** `string | null`

当前被处理的模块所在的目录路径，会随着每个被处理的模块的位置而变化。

例如，如果 loader 处理的是 `/project/src/components/Button.js`，那么 `this.context` 的值就是 `/project/src/components`。

```js title="loader.mjs"
export default function loader(source) {
  console.log(this.context); // '/project/src/components'
  return source;
}
```

如果正在处理的模块没有资源路径，`this.context` 的值为 `null`。

## this.loaderIndex

- **类型：** `number`

当前 loader 在 loaders 数组中的索引。

## this.loaders

- **类型：** `LoaderObject[]`

`this.loaders` 包含应用于当前模块的所有 loader。每一项都提供解析后的 `request`、`path`、`query` 和 `options` 等信息。

在 pitch 阶段可以修改这个数组，以调整 loader 链。可通过 [this.loaderIndex](#thisloaderindex) 定位当前 loader。

```js title="loader.mjs"
export function pitch() {
  const currentLoader = this.loaders[this.loaderIndex];
  console.log(currentLoader.request);
}
```

## this.data

- **类型：** `unknown`

用于在 pitch 和 normal 阶段之间共享数据。

## this.dependency()

- **类型：**

```ts
function dependency(file: string): void;
```

[this.addDependency()](#thisadddependency) 的别名。

## this.emitError()

- **类型：**

```ts
function emitError(error: Error): void;
```

发出一个错误。

与在 loader 中 `throw` 和 `this.callback(err)` 不同，它不会将当前模块标记为编译失败，只会向 Rspack 的 Compilation 添加一个错误，并在本次编译结束后显示在命令行中。

## this.emitWarning()

- **类型：**

```ts
function emitWarning(warning: Error): void;
```

发出一个警告。

## this.experiments.emitDiagnostic()


Stability: Experimental

- **类型：**

```ts
interface DiagnosticLocation {
  /** Text for highlighting the location */
  text?: string;
  /** 1-based line */
  line: number;
  /** 0-based column in bytes */
  column: number;
  /** Length in bytes */
  length: number;
}

interface Diagnostic {
  message: string;
  help?: string;
  sourceCode?: string;
  /**
   * 源代码的位置信息
   * 如果未提供 `sourceCode`，则位置信息将被省略
   */
  location?: DiagnosticLocation;
  /**
   * 可选展示的文件名
   * 如果设置，它将成为 stats 中的 `StatsError.file` 值
   */
  file?: string;
  severity: 'error' | 'warning';
}

function emitDiagnostic(diagnostic: Diagnostic): void;
```

格式化并输出错误或警告诊断信息，支持显示模块路径、源代码片段和行列号。

::: info
与在 loader 中 `throw` 和 `this.callback(err)` 不同，它不会将当前模块标记为编译失败，而是根据 `severity` 向 Rspack 的 Compilation 添加错误或警告，并在本次编译结束后显示在命令行中。
:::

- 基础示例：

只提供 `message` 和 `severity` 时，仅会输出基本的诊断信息。

```js title="loader.mjs"
/** @type {import("@rspack/core").LoaderDefinition} */
export default function () {
  this.experiments.emitDiagnostic({
    message: '`React` is not defined',
    severity: 'error',
  });
  this.experiments.emitDiagnostic({
    message: '`React` is not defined',
    severity: 'warning',
  });
  return '';
}
```

将会打印：

```
ERROR in (./loader.mjs!)
  × ModuleError: `React` is not defined

WARNING in (./loader.mjs!)
  ⚠ ModuleWarning: `React` is not defined
```

- 打印代码片段：

```js title="loader.mjs"
/** @type {import("@rspack/core").LoaderDefinition} */
export default function () {
  this.experiments.emitDiagnostic({
    message: '`React` is not defined',
    severity: 'error',
    sourceCode: `<div></div>`,
    location: {
      line: 1,
      column: 1,
      length: 3,
    },
    file: './some-file.js',
  });
  return '';
}
```

将会打印：

```
ERROR in ./some-file.js
 ./file.js 1:1-4
  × ModuleError: `React` is not defined
   ╭────
 1 │ <div></div>
   ·  ───
   ╰────
```

其中 `./some-file.js` 为传入的 `file` 字段。

## this.emitFile()

- **类型：**

```ts
function emitFile(
  name: string,
  content: string | Buffer,
  sourceMap?: string,
  assetInfo?: AssetInfo,
): void;
```

输出一个新文件。这个方法允许你在 loader 执行过程中创建新的文件。

- 基础示例：

```js title="loader.mjs"
export default function loader(source) {
  // 输出一个新文件，该文件将在产物目录中输出为 `foo.js`
  this.emitFile('foo.js', 'console.log("Hello, world!");');
  return source;
}
```

- 带有 asset info 的示例：

```js title="loader.mjs"
export default function loader(source) {
  this.emitFile(
    'foo.js',
    'console.log("Hello, world!");',
    undefined, // no sourcemap
    {
      sourceFilename: this.resourcePath,
    },
  );

  return source;
}
```

## this.fs

- **类型：** `InputFileSystem`

访问 `compilation` 对象的 `inputFileSystem` 属性。

## this.getContextDependencies()

- **类型：**

```ts
function getContextDependencies(): string[];
```

返回 loader 当前作为上下文依赖监听的所有目录，其中包括通过 [this.addContextDependency()](#thisaddcontextdependency) 添加的目录。

```js title="loader.mjs"
export default function loader(source) {
  const contextDependencies = this.getContextDependencies();
  console.log(contextDependencies);

  return source;
}
```

## this.getDependencies()

- **类型：**

```ts
function getDependencies(): string[];
```

返回 loader 当前作为依赖监听的所有文件，其中包括通过 [this.addDependency()](#thisadddependency) 或 [this.dependency()](#thisdependency) 添加的文件。

```js title="loader.mjs"
export default function loader(source) {
  const dependencies = this.getDependencies();
  console.log(dependencies);

  return source;
}
```

## this.getMissingDependencies()

- **类型：**

```ts
function getMissingDependencies(): string[];
```

返回 loader 当前监听的所有尚不存在的文件路径，其中包括通过 [this.addMissingDependency()](#thisaddmissingdependency) 添加的路径。当这些文件被创建时，可能会触发重新构建。

```js title="loader.mjs"
export default function loader(source) {
  const missingDependencies = this.getMissingDependencies();
  console.log(missingDependencies);

  return source;
}
```

这三个方法都会返回一个新数组。修改返回的数组不会影响 loader 已注册的依赖。如需清空这三类依赖列表，请使用 [this.clearDependencies()](#thiscleardependencies)。

## this.getOptions()

- **类型：**

```ts
function getOptions(schema?: any): OptionsType;
```

获取 loader 的使用者传入的选项。

例如：

```js title="rspack.config.mjs"
export default {
  module: {
    rules: [
      {
        test: /\.txt$/,
        use: {
          loader: './my-loader.mjs',
          options: {
            foo: 'bar',
          },
        },
      },
    ],
  },
};
```

在 `my-loader.mjs` 中获取传入的选项：

```js title="my-loader.mjs"
export default function myLoader(source) {
  const options = this.getOptions();
  console.log(options); // { foo: 'bar' }
  return source;
}
```

:::tip
当 loader 通过查询字符串配置时，例如 `loader: './my-loader?s=foo+bar'`，`this.getOptions()` 会使用 Node.js 的 `querystring.parse()` 解析该字符串。这意味着字面量 `+` 会被解码为空格，因此结果会是 `{ s: 'foo bar' }`。

如果你需要字面量加号，请将 `+` 编码为 `%2B`，或者优先在规则里使用 `options` 对象。若你需要读取原始查询字符串，请使用 [this.query](#thisquery)。
:::

在 TypeScript 中，你可以通过 `LoaderContext` 的泛型来设置 options 的类型。

```ts title="my-loader.ts"
import type { LoaderContext } from '@rspack/core';

type MyLoaderOptions = {
  foo: string;
};

export default function myLoader(
  this: LoaderContext<MyLoaderOptions>,
  source: string,
) {
  const options = this.getOptions();
  console.log(options); // { foo: 'bar' }
  return source;
}
```

:::tip
参数 `schema` 是可选的，在 Rspack 中不会被使用。

为了提供最佳性能，Rspack 不会执行 schema 验证。如果你的 loader 需要 schema 验证，请自行调用 [schema-utils](https://github.com/webpack/schema-utils) 或其他 schema 验证库。
:::

## this.getResolve()

- **类型：**

```ts
type ResolveFunction = {
  (
    context: string,
    request: string,
    callback: (
      err: Error | null,
      result?: string | false,
      resolveRequest?: ResolveRequest,
    ) => void,
  ): void;
  (context: string, request: string): Promise<string | false | undefined>;
};

function getResolve(options?: ResolveOptions): ResolveFunction;
```

创建一个类似于 [`this.resolve()`](#thisresolve) 的解析函数。可以通过 `options` 自定义解析行为；省略时使用不带额外选项的普通解析器。

返回的解析函数同时支持回调和 Promise 两种调用方式。不传回调函数时，它会返回 Promise。

```js title="loader.mjs"
export default async function loader(source) {
  const resolve = this.getResolve({
    extensions: ['.js', '.json'],
  });
  const result = await resolve(this.context, './dependency');

  console.log(result);
  return source;
}
```

## this.hot

- **类型：** `boolean`

是否启用了 HMR。

```js title="loader.mjs"
export default function (source) {
  console.log(this.hot); // true if HMR is enabled
  return source;
}
```

## this.importModule()

- **类型：**

```ts
interface ImportModuleOptions {
  /**
   * 指定模块的 layer
   */
  layer?: string;
  /**
   * 构建模块时使用的 public path
   */
  publicPath?: PublicPath;
  /**
   * 目标 base uri
   */
  baseUri?: string;
}

// 传入回调函数
function importModule<T = any>(
  request: string,
  options: ImportModuleOptions | undefined,
  callback: (err?: null | Error, exports?: T) => any,
): void;
// 不传入回调函数时，返回 Promise
function importModule<T = any>(
  request: string,
  options?: ImportModuleOptions,
): Promise<T>;
```

在构建过程中编译和执行一个模块。这是 child compiler 的轻量级替代方案。

在没有提供回调函数时，`importModule` 会返回一个 Promise。

```js title="loader.mjs"
import path from 'node:path';

export default async function loader(source) {
  const modulePath = path.resolve(this.rootContext, 'some-module.ts');
  const moduleExports = await this.importModule(modulePath, {
    // 可选参数
  });

  const result = someProcessing(source, moduleExports);
  return result;
}
```

或者你可以传递一个回调函数给它。

```js title="loader.mjs"
import path from 'node:path';

export default function loader(source) {
  const callback = this.async();
  const modulePath = path.resolve(this.rootContext, 'some-module.ts');

  this.importModule(
    modulePath,
    // 可选参数
    undefined,
    (err, moduleExports) => {
      if (err) {
        return callback(err);
      }

      const result = someProcessing(source, moduleExports);
      callback(null, result);
    },
  );
}
```

## this.query

- **类型：** `string | OptionsType`

该值取决于 loader 的配置方式：

- 如果当前 loader 配置了一个选项对象，`this.query` 将指向这个对象。
- 如果当前 loader 没有选项，但是通过查询字符串调用，这将是一个以 `?` 开头的字符串。

与 [this.getOptions()](#thisgetoptions) 不同，查询字符串形式在这里不会被解析。例如，`loader: './my-loader?s=foo+bar'` 时，`this.query === '?s=foo+bar'`。

## this.remainingRequest

- **类型：** `string`

`this.remainingRequest` 由 loader 链中位于当前 loader 之后的所有 loader 和当前资源组成，各部分使用 `!` 连接。

例如，假设 loader 链为：

```text
/path/to/loader1.mjs!/path/to/loader2.mjs!/path/to/resource.js
```

执行到 `loader1.mjs` 时，`this.remainingRequest` 为：

```text
/path/to/loader2.mjs!/path/to/resource.js
```

它通常用于构造内联请求，避免再次执行当前 loader。可以参考 [Inline matchResource](/zh/api/loader-api/inline-match-resource.md) 中的示例。

## this.currentRequest

- **类型：** `string`

`this.currentRequest` 由当前 loader、loader 链中位于它之后的所有 loader 和当前资源组成，各部分使用 `!` 连接。

例如，假设 loader 链为：

```text
/path/to/loader1.mjs!/path/to/loader2.mjs!/path/to/resource.js
```

执行到 `loader2.mjs` 时，`this.currentRequest` 为：

```text
/path/to/loader2.mjs!/path/to/resource.js
```

## this.previousRequest

- **类型：** `string`

`this.previousRequest` 由 loader 链中位于当前 loader 之前的所有 loader 组成，各部分使用 `!` 连接。它不包含当前资源。

例如，假设 loader 链为：

```text
/path/to/loader1.mjs!/path/to/loader2.mjs!/path/to/resource.js
```

执行到 `loader2.mjs` 时，`this.previousRequest` 为：

```text
/path/to/loader1.mjs
```

## this.request

- **类型：** `string`

完整的请求字符串，由所有 loader 和当前资源组成，各部分使用 `!` 连接。

例如，如果 `resource.js` 被 `loader1.mjs` 和 `loader2.mjs` 处理，那么 `this.request` 的值为 `/path/to/loader1.mjs!/path/to/loader2.mjs!/path/to/resource.js`。

## this.resolve()

- **类型：**

```ts
function resolve(
  context: string,
  request: string,
  callback: (
    err: Error | null,
    result?: string | false,
    resolveRequest?: ResolveRequest,
  ) => void,
): void;
```

解析一个模块标识符。

- `context` 必须是一个目录的绝对路径。此目录用作解析的起始位置。
- `request` 是要被解析的模块标识符。
- `callback` 会接收错误、解析后的路径（请求被忽略时为 `false`），以及可选的解析详情。

## this.mode

- **类型：** `Mode | undefined`

[`mode`](/zh/config/mode.md) 配置的值，可能为 `'production'`、`'development'`、`'none'` 或 `undefined`。如果没有配置 `mode`，`this.mode` 为 `undefined`，但 Rspack 仍会应用偏向生产环境的默认配置。

```js title="loader.mjs"
export default function loader(source) {
  console.log(this.mode); // 'production'、'development'、'none' 或 undefined
  return source;
}
```

## this.target

- **类型：** `Target | undefined`

默认情况下，Rspack 会根据 [`target`](/zh/config/target.md) 所描述的运行环境能力，为 loader 生成一个更简单的目标值。能够判断目标环境时，该值会是 `'web'`、`'node'`、`'nwjs'` 或 `'electron-main'` 等。因此，`this.target` 不一定与原始的 `target` 配置完全相同。

```js title="loader.mjs"
export default function loader(source) {
  console.log(this.target); // 'web' or other values
  return source;
}
```

## this.environment

- **类型：** `Environment`

描述目标运行环境支持的能力。默认情况下，该值是最终生效的 [`output.environment`](/zh/config/output.md#outputenvironment)：Rspack 会根据 [`target`](/zh/config/target.md) 判断目标环境支持的能力，再应用 `output.environment` 中的显式配置。

loader 可以根据这些信息选择输出环境支持的语法。

```js title="loader.mjs"
export default function loader(source) {
  if (this.environment.optionalChaining) {
    console.log('Optional chaining is supported');
  }

  return source;
}
```

## this.utils

- **类型：**

```ts
type Utils = {
  absolutify: (context: string, request: string) => string;
  contextify: (context: string, request: string) => string;
  createHash: (algorithm?: string) => Hash;
};
```

访问以下 utils：

- `absolutify`: 返回一个新的 request 字符串，尽可能使用绝对路径。
- `contextify`: 返回一个新的 request 字符串，尽可能避免使用绝对路径。
- `createHash`: 基于提供的哈希函数返回一个新的 Hash 对象。

```js title="loader.mjs"
export default function (content) {
  this.utils.contextify(
    this.context,
    this.utils.absolutify(this.context, './index.js'),
  );

  this.utils.absolutify(this.context, this.resourcePath);

  const mainHash = this.utils.createHash(
    this._compilation.outputOptions.hashFunction,
  );
  mainHash.update(content);
  mainHash.digest('hex');

  return content;
}
```

## this.resource

- **类型：** `string`

当前模块的路径字符串。比如 `'/abc/resource.js?query#hash'`。

```js title="loader.mjs"
export default function loader(source) {
  console.log(this.resource); // '/abc/resource.js?query#hash'
  return source;
}
```

## this.resourcePath

- **类型：** `string`

当前模块的路径字符串，不包括 query 和 fragment 参数。比如 `'/abc/resource.js?query#hash'` 中的 `'/abc/resource.js'`。

```js title="loader.mjs"
export default function loader(source) {
  console.log(this.resourcePath); // '/abc/resource.js'
  return source;
}
```

## this.resourceQuery

- **类型：** `string`

当前模块的路径字符串的 query 参数。比如 `'/abc/resource.js?query#hash'` 中的 `'?query'`。

```js title="loader.mjs"
export default function loader(source) {
  console.log(this.resourceQuery); // '?query'
  return source;
}
```

## this.resourceFragment

- **类型：** `string`

当前模块的路径字符串的 fragment 参数。比如 `'/abc/resource.js?query#hash'` 中的 `'#hash'`。

```js title="loader.mjs"
export default function loader(source) {
  console.log(this.resourceFragment); // '#hash'
  return source;
}
```

## this.rootContext

- **类型：** `string`

Rspack config 中通过 [context](/zh/config/context.md) 配置的基础路径。

```js title="loader.mjs"
export default function loader(source) {
  console.log(this.rootContext); // /path/to/project
  return source;
}
```

## this.sourceMap

- **类型：** `boolean`

是否应该生成 source map。

由于生成 source map 通常是一项耗费资源的任务，你应该检查是否需要 source map。

详见 [处理 Source Map](/zh/api/loader-api/writing-loaders.md#处理-source-map)。

## this.getLogger()

- **类型：**

```ts
function getLogger(name?: string): Logger;
```

获取此次编译过程的 logger，可通过该 logger 记录消息。

## this.version

- **类型：** `number`

loader API 的版本号。当前为 2。

这对于提供向后兼容性很有用。基于版本号，你可以为 breaking changes 指定自定义逻辑或降级方案。

## 内部属性

:::warning
注意，使用 `this._compiler` 和 `this._compilation` 这些 Rspack 的内部属性会使你的 loader 失去独立性。

理想情况下，loader 应该只关注文件转换逻辑，对于给定的输入，输出是确定的，不依赖 Rspack 的内部状态。依赖这些内部对象会增加不可预测的行为，使测试和维护变得困难。

因此，建议仅在没有其他替代方案时才考虑使用这些属性。
:::

### this.\_compiler

- **类型：** `Compiler`

访问当前的 Rspack [Compiler](/zh/api/javascript-api/compiler.md) 对象。

### this.\_compilation

- **类型：** `Compilation`

访问当前的 Rspack [Compilation](/zh/api/javascript-api/compilation.md) 对象。


本页改编自 [webpack 文档](https://webpack.docschina.org/api/loaders/#the-loader-context)，遵循 [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/)，且已作修改。

