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

# 配置 \{#configuration}

Rstack CLI 将项目所用工具的配置集中到一份文件中。通过 `define.*()` API 定义项目实际需要的配置即可。

## 配置文件 \{#configuration-file}

在项目根目录创建 `rstack.config.ts`，并调用对应的 `define.*()` API：

```ts title="rstack.config.ts"
// Configuration guide: https://rstack.rs/config
import { define } from 'rstack';

define.app({
  // Rsbuild 配置
});

define.test({
  // Rstest 配置
});

define.lint({
  // Rslint 配置
});

define.fmt({
  // 格式化配置
});
```

配置文件无需默认导出。每个 `define.*()` API 最多调用一次；重复定义同一类型的配置会抛出错误。

Rstack CLI 默认会查找使用以下任一文件名的配置文件：

- `rstack.config.ts`
- `rstack.config.js`
- `rstack.config.mts`
- `rstack.config.mjs`

所有 `rs` 命令都支持全局的 `-c, --config` 选项，用于加载其他名称或位置的配置文件：

```bash
rs build --config ./configs/rstack.config.ts
```

## 导入依赖 \{#loading-dependencies-on-demand}

Rstack 将项目的构建、测试、代码检查、格式化等配置集中在一个 `rstack.config.*` 文件中。命令加载配置文件时，会同时加载所有顶层 `import`，即使当前命令用不到对应的配置。因此，在顶层导入所有工具和插件可能会增加 `rs lint`、`rs fmt` 等命令的启动开销。

请根据配置内容选择导入方式：

- 如果配置只用于一个应用及其测试、一个库及其测试或一个文档站点，优先使用更简洁的静态导入。
- 如果同一配置还包含 lint、格式化或暂存文件检查，可在相应的异步配置函数中动态导入依赖，让检查命令跳过这些依赖。

```ts title="rstack.config.ts"
import { define } from 'rstack';

define.app(async () => {
  const { pluginReact } = await import('@rsbuild/plugin-react');
  return {
    plugins: [pluginReact()],
  };
});

define.lint(({ js }) => [js.configs.recommended]);

define.fmt({
  singleQuote: true,
});
```

## 配置 API \{#configuration-apis}

各 API 沿用底层工具的配置格式。使用 Rstack CLI 已重导出的 API 和辅助函数时，推荐从 `rstack/app`、`rstack/lib`、`rstack/test` 和 `rstack/lint` 入口导入。

| API                                 | 底层工具                                                                    | 对应命令                                                                                                           |
| ----------------------------------- | ----------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- |
| [`define.app()`](#define-app)       | [Rsbuild](https://rsbuild.rs/zh/config/)                                | [`rs dev`](/zh/guide/cli/dev.md)、[`rs build`](/zh/guide/cli/build.md)、[`rs preview`](/zh/guide/cli/preview.md) |
| [`define.lib()`](#define-lib)       | [Rslib](https://rslib.rs/zh/config/)                                    | [`rs lib`](/zh/guide/cli/lib.md)                                                                               |
| [`define.doc()`](#define-doc)       | [Rspress](https://rspress.rs/zh/api/config/config-basic)                | [`rs doc`](/zh/guide/cli/doc.md)                                                                               |
| [`define.test()`](#define-test)     | [Rstest](https://rstest.rs/zh/config/)                                  | [`rs test`](/zh/guide/cli/test.md)                                                                             |
| [`define.lint()`](#define-lint)     | [Rslint](https://rslint.rs/config/)                                     | [`rs lint`](/zh/guide/cli/lint.md)                                                                             |
| [`define.fmt()`](#define-fmt)       | [Prettier](https://prettier.io/docs/options)                            | [`rs fmt`](/zh/guide/cli/fmt.md)                                                                               |
| [`define.staged()`](#define-staged) | [lint-staged](https://github.com/lint-staged/lint-staged#configuration) | [`rs staged`](/zh/guide/cli/staged.md)                                                                         |

### `define.app()` \{#define-app}

定义应用的 [Rsbuild 配置](https://rsbuild.rs/zh/config/)，支持传入配置对象或配置函数。配置函数接收 Rsbuild 的标准配置参数。

```ts title="rstack.config.ts"
import { define } from 'rstack';

define.app({
  html: {
    title: 'My App',
  },
  output: {
    distPath: {
      root: 'dist',
    },
  },
});
```

### `define.lib()` \{#define-lib}

定义库的 [Rslib 配置](https://rslib.rs/zh/config/)，支持传入配置对象或配置函数。配置函数接收 Rslib 的标准配置参数。

```ts title="rstack.config.ts"
import { define } from 'rstack';

define.lib({
  dts: true,
  format: 'esm',
});
```

### `define.doc()` \{#define-doc}

定义文档站点的 [Rspress 配置](https://rspress.rs/zh/api/config/config-basic)，支持传入配置对象或异步配置函数。

```ts title="rstack.config.ts"
import { define } from 'rstack';

define.doc({
  root: 'docs',
  title: 'My Site',
});
```

`@rspress/core` 是 Rstack CLI 的可选依赖。每个使用 `rs doc` 命令的项目都需要安装该依赖：


```sh [npm]
npm install -D @rspress/core
```

```sh [yarn]
yarn add -D @rspress/core
```

```sh [pnpm]
pnpm add -D @rspress/core
```

```sh [bun]
bun add -D @rspress/core
```

```sh [deno]
deno add -D npm:@rspress/core
```

### `define.test()` \{#define-test}

定义 [Rstest 配置](https://rstest.rs/zh/config/)，支持传入配置对象或配置函数。

```ts title="rstack.config.ts"
import { define } from 'rstack';

define.app({
  // 共享的应用配置
});

define.test({
  setupFiles: ['./tests/rstest.setup.ts'],
  testEnvironment: 'happy-dom',
});
```

未设置 `extends` 时，Rstack CLI 会通过 Rsbuild 适配器让测试配置自动继承 `define.app()`；如果未定义应用配置，则通过 Rslib 适配器回退到 `define.lib()`。二者同时存在时，应用配置的优先级更高。显式设置 `extends` 可关闭自动继承。

如果测试根配置未定义 `extends` 且包含 `projects`，Rstack CLI 会为每个未自行设置 `extends` 的内联项目应用自动继承。函数形式的应用或库配置只会解析一次，并由这些项目共享。字符串形式的项目会原样传给 Rstest；它们会独立加载外部配置，不继承当前应用或库的配置。

这些规则同样适用于[公共配置](#shared-configurations)：Rstack 先合并测试配置，再判断是否需要继承合并后的 App 或 Lib 配置。

> 如需了解更多测试相关用法，请参阅[测试](/zh/guide/testing.md)。

### `define.lint()` \{#define-lint}

定义 [Rslint 配置](https://rslint.rs/config/)。可以直接传入配置，也可以传入同步或异步函数。函数会接收 `rstack/lint` 的全部导出，因此无需手动导入预设和插件。

```ts title="rstack.config.ts"
import { define } from 'rstack';

define.lint(({ js, ts }) => [
  js.configs.recommended,
  ts.configs.recommendedTypeChecked,
]);
```

### `define.fmt()` \{#define-fmt}

定义 [`rs fmt`](/zh/guide/cli/fmt.md) 的格式化配置。可以直接传入配置对象，也可以传入返回配置对象的同步或异步函数。

```ts title="rstack.config.ts"
import { define } from 'rstack';

define.fmt({
  printWidth: 100,
  singleQuote: true,
});
```

详细用法请参考[格式化](/zh/guide/formatting.md)指南。

### `define.staged()` \{#define-staged}

定义用于处理 Git 暂存文件的 [lint-staged 配置](https://github.com/lint-staged/lint-staged#configuration)。支持传入从 glob 匹配模式映射到任务的配置对象，也支持传入任务生成函数。任务可以是 lint-staged 支持的命令、命令数组或函数。

```ts title="rstack.config.ts"
import { define } from 'rstack';

define.staged({
  '*.{js,jsx,ts,tsx}': ['rs lint', 'rs fmt'],
  '*.{json,jsonc,md,mdx,css,html,yml,yaml}': 'rs fmt',
});
```

请通过 `define.staged()` 或[公共配置](#shared-configurations)提供 staged 配置，否则 `rs staged` 会报错。

## 公共配置 \{#shared-configurations}

使用 `define.extends()` 可以在多个项目之间共享构建、测试、lint 和格式化配置。公共配置是一个普通对象，各字段的用法与对应的 `define.*()` API 一致。在 TypeScript 中，可以用 `RstackConfig` 检查配置类型：

```ts title="shared.ts"
import type { RstackConfig } from 'rstack';

export const sharedConfig: RstackConfig = {
  test: {
    retry: 2,
  },
  lint: ({ js, ts }) => [js.configs.recommended, ts.configs.recommended],
  fmt: {
    singleQuote: true,
    printWidth: 80,
  },
};
```

从本地模块或 npm 包导入公共配置，再添加项目自己的配置：

```ts title="rstack.config.ts"
import { define } from 'rstack';
import { sharedConfig } from './shared.ts';

define.extends([sharedConfig]);

define.fmt({
  printWidth: 100,
});
```

项目会继承公共配置，仅将 `printWidth` 改为 `100`。

每次加载配置时，`define.extends()` 最多调用一次，多个公共配置请放在同一个数组中。

### 合并顺序 \{#merge-order}

配置按以下顺序应用：

1. `define.extends()` 数组中的公共配置，从左到右。
2. 项目自己的 `define.*()` 配置，即使这些调用写在 `define.extends()` 之前。

各工具的[合并规则](#merge-rules)决定同一字段的值是合并还是替换。

公共配置也可以继承其他公共配置：

```ts title="team.ts"
import type { RstackConfig } from 'rstack';
import { sharedConfig } from './shared.ts';

export const teamConfig: RstackConfig = {
  extends: [sharedConfig],
  test: {
    retry: 3,
  },
};
```

被继承的配置先应用。使用 `define.extends([teamConfig])` 时，顺序为 `sharedConfig → teamConfig → 项目配置`。

重复引用不会去重。例如，`a` 和 `b` 都继承 `base` 时，`define.extends([a, b])` 的顺序为 `base → a → base → b → 项目配置`。重复的插件由各工具按自身规则处理。循环继承会抛出错误，并指出循环引用的位置。

### 合并规则 \{#merge-rules}

Rstack 分别合并各个工具的配置：

| 字段       | 合并方式                                                    |
| -------- | ------------------------------------------------------- |
| `app`    | 使用 Rsbuild 的 `mergeRsbuildConfig`。                      |
| `lib`    | 使用 Rslib 的 `mergeRslibConfig`。                          |
| `test`   | 使用 Rstest 的 `mergeRstestConfig`。                        |
| `lint`   | 按顺序拼接配置数组，由 Rslint 应用其中的规则。                             |
| `fmt`    | 浅合并，同名选项由后面的值覆盖。数组和对象（包括 `overrides`）整体替换。              |
| `doc`    | 使用 Rspress 的 `mergeDocConfig`：递归合并对象、追加数组，普通函数由后面的函数覆盖。 |
| `staged` | 按 glob 模式浅合并，同名模式的任务整体替换，包括命令数组和任务函数。                   |

`doc` 的合并规则同样适用于 `builderConfig`：后面的 `builderConfig.tools.rspack` 回调会覆盖前面的回调，回调数组则按顺序拼接。数组与已定义的非数组值合并时，Rspress 会将后者作为一个数组元素参与拼接。

合并 `staged` 时，只要任一配置是顶层任务生成函数，就由后一个配置整体替换前一个。例如，「glob 映射 → 函数 → 新的 glob 映射」最终只保留新的映射。任务函数由 lint-staged 传入暂存文件列表后执行。

### 依赖加载 \{#loading-dependencies}

公共配置也可以在工具配置函数中[动态导入依赖](#loading-dependencies-on-demand)，让各命令只加载需要的依赖。

```ts title="shared.ts"
import type { RstackConfig } from 'rstack';

export const sharedConfig: RstackConfig = {
  app: async () => {
    const { pluginReact } = await import('@rsbuild/plugin-react');
    return {
      plugins: [pluginReact()],
    };
  },
};
```

### 路径解析 \{#resolving-paths}

相对路径沿用各工具的解析规则，不会自动改为以公共模块所在目录为基准。引用公共配置自带的文件时，请使用绝对路径：

```ts title="shared.ts"
import { fileURLToPath } from 'node:url';
import type { RstackConfig } from 'rstack';

export const sharedConfig: RstackConfig = {
  test: {
    setupFiles: [fileURLToPath(new URL('./setup.ts', import.meta.url))],
  },
};
```

发布包中需要包含引用的文件。如果该文件经过编译，请将路径改为编译产物的位置。
