Skip to content

Commit 2843c89

Browse files
committed
docs: split and polish shared configuration guide
1 parent d459434 commit 2843c89

6 files changed

Lines changed: 42 additions & 232 deletions

File tree

‎website/docs/en/guide/api-reference.mdx‎

Lines changed: 0 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ Rstack CLI provides a unified configuration API and re-exports the public APIs o
1111
| Import path | Contents | Use case |
1212
| ------------------------ | ------------------------------------------------- | --------------------------------------- |
1313
| `rstack` | Rstack CLI configuration API | Register tool configurations |
14-
| `rstack/config` | Configuration loader and its types | Load shared and project configurations |
1514
| `rstack/app` | Public APIs from `@rsbuild/core` | Build applications and extend Rsbuild |
1615
| `rstack/lib` | Public APIs from `@rslib/core` | Build libraries and extend Rslib |
1716
| `rstack/test` | Public APIs from `@rstest/core` | Write tests and configure test projects |
@@ -26,68 +25,6 @@ Rstack CLI provides a unified configuration API and re-exports the public APIs o
2625

2726
Import `define` from `rstack` to register tool configurations in `rstack.config.ts`; see [Configuration APIs](./configuration#configuration-apis) for details.
2827

29-
### `define.extends()` \{#define-extends}
30-
31-
Registers shared configurations to apply before the project's own tool configurations:
32-
33-
```ts
34-
import { define } from 'rstack';
35-
import { sharedConfig } from './shared.ts';
36-
37-
define.extends([sharedConfig]);
38-
```
39-
40-
- **Type:** `(configs: readonly RstackConfig[]) => void`
41-
- Accepts configuration objects. For a parameterized preset, call the preset function and pass its return value.
42-
- Can be called at most once per configuration load, including calls with an empty array.
43-
- Applies shared configurations from left to right, then project definitions, regardless of call order.
44-
45-
See [Shared configurations](./configuration#shared-configurations) for nested inheritance, merge rules, and examples.
46-
47-
### `RstackConfig`
48-
49-
A type for authoring shared configurations. Its optional `app`, `lib`, `doc`, `test`, `lint`, `fmt`, and `staged` fields accept the same inputs as the corresponding `define.*()` APIs. Its optional `extends` field accepts `readonly RstackConfig[]` for nested inheritance.
50-
51-
```ts
52-
import type { RstackConfig } from 'rstack';
53-
54-
export const sharedConfig: RstackConfig = {
55-
fmt: {
56-
singleQuote: true,
57-
},
58-
};
59-
```
60-
61-
## Loading configurations
62-
63-
### `loadRstackConfig()` \{#loadrstackconfig}
64-
65-
Import `loadRstackConfig` from `rstack/config` to load configurations programmatically:
66-
67-
```ts
68-
import { loadRstackConfig } from 'rstack/config';
69-
70-
const { configs, filePath, dependencies } = await loadRstackConfig({
71-
cwd: process.cwd(),
72-
configFilePath: './rstack.config.ts',
73-
});
74-
```
75-
76-
Both options are optional:
77-
78-
- `cwd`: the directory used to search for a configuration file and resolve relative configuration file paths. Defaults to the current working directory.
79-
- `configFilePath`: a relative or absolute configuration file path. If omitted, uses the CLI's `--config` path when set; otherwise, searches for the [default configuration file names](./configuration#configuration-file) in `cwd`.
80-
81-
The returned object contains:
82-
83-
- `configs`: effective tool definitions, including shared configurations and project definitions. A tool's field is absent if it was never defined.
84-
- `filePath`: the loaded configuration file's path, or `null` if no file was found.
85-
- `dependencies`: configuration dependencies reported by the loader.
86-
87-
Configuration functions remain unevaluated. A field may be an async function even when all its inputs were objects, because merging is deferred until that tool is needed. Consumers must resolve definitions with the appropriate tool parameters and follow the tool's configuration and execution flow; this API does not return fully resolved runtime configurations. In particular, loading alone does not apply Rstack's automatic test inheritance or formatting normalization. Staged functions remain task generators that receive file names.
88-
89-
`rstack/config` also exports the `Configs`, `LoadedRstackConfig`, and `LoadRstackConfigOptions` types.
90-
9128
## Re-exports
9229

9330
The tool-specific subpaths below re-export the public APIs from their corresponding core packages. Using these entry points keeps imports unified and APIs aligned with the tool versions integrated by Rstack CLI.

‎website/docs/en/guide/configuration.mdx‎

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ define.fmt({
7676

7777
## Shared configurations
7878

79-
Use `define.extends()` to reuse configurations across projects. A shared configuration is a plain object typed as `RstackConfig`. It can contain `app`, `lib`, `doc`, `test`, `lint`, `fmt`, and `staged`, each accepting the same input as its corresponding `define.*()` API.
79+
Use `define.extends()` to share build, test, lint, and formatting settings across projects. Define a shared configuration as a plain object with the `RstackConfig` type. Its fields accept the same values as the corresponding `define.*()` APIs:
8080

8181
```ts title="shared.ts"
8282
import type { RstackConfig } from 'rstack';
@@ -93,7 +93,7 @@ export const sharedConfig: RstackConfig = {
9393
};
9494
```
9595

96-
Import the object from a local module or an npm package, then customize the settings your project needs:
96+
Import the shared configuration from a local module or npm package, then add any project-specific settings:
9797

9898
```ts title="rstack.config.ts"
9999
import { define } from 'rstack';
@@ -106,16 +106,16 @@ define.fmt({
106106
});
107107
```
108108

109-
This project inherits the test and lint configurations and formats with `singleQuote: true` and `printWidth: 100`. You do not need to call `define.test()` or `define.lint()` to enable inherited settings.
109+
The project uses the shared test and lint settings, keeps `singleQuote: true`, and changes `printWidth` to `100`. Inherited settings take effect without additional `define.test()` or `define.lint()` calls.
110110

111-
Call `define.extends()` at most once per configuration load, with all shared configurations in one array. A second call throws, even if the first call used an empty array. Call it while loading `rstack.config.*`, just like the other `define.*()` APIs.
111+
Like the other `define.*()` APIs, `define.extends()` must be called while loading `rstack.config.*`. Call it once with all shared configurations in a single array. Calling it again throws an error, even if the first array was empty.
112112

113113
### Merge order
114114

115115
Configurations are applied in this order:
116116

117117
1. Shared configurations in the `define.extends()` array, from left to right.
118-
2. Project configurations registered with `define.*()`, regardless of where those calls appear relative to `define.extends()`.
118+
2. The project's own `define.*()` configurations, even if they appear before `define.extends()` in the file.
119119

120120
Later configurations take precedence according to each tool's [merge rules](#merge-rules).
121121

@@ -135,7 +135,7 @@ export const teamConfig: RstackConfig = {
135135

136136
Inherited configurations are applied before the object that extends them. Here, the order is `sharedConfig → teamConfig → project configuration`.
137137

138-
Each occurrence of a shared configuration is applied. For example, if both `a` and `b` extend `base`, `define.extends([a, b])` applies `base → a → base → b → project configuration`. This can append arrays more than once; plugin deduplication follows the underlying tool's behavior. Circular inheritance throws an error that identifies the reference path.
138+
Shared configurations are applied each time they appear. If both `a` and `b` extend `base`, for example, `define.extends([a, b])` applies `base → a → base → b → project configuration`. Array entries may therefore be added more than once; each tool determines how to handle duplicate plugins. Circular inheritance throws an error with the location of the circular reference.
139139

140140
### Merge rules
141141

@@ -151,15 +151,15 @@ Rstack merges configurations separately for each tool:
151151
| `doc` | Uses Rspress's `mergeDocConfig`: recursively merges objects, appends arrays, and replaces ordinary functions with later functions. |
152152
| `staged` | Shallow merge by glob pattern: later tasks replace earlier tasks for the same pattern, including command arrays and task functions. |
153153

154-
For `doc`, when both values are defined and either is an array, Rspress treats the other value as a one-item array before concatenating them. These rules also apply to `builderConfig`: Rstack does not additionally call `mergeRsbuildConfig`. For example, a later `builderConfig.tools.rspack` callback replaces an earlier callback, while callback arrays are concatenated.
154+
For `doc`, Rspress also handles a mix of array and non-array values: if both are defined, it wraps the non-array value in an array before concatenating them. `builderConfig` follows these same Rspress rules. For example, a later `builderConfig.tools.rspack` callback replaces an earlier one, while callback arrays are concatenated.
155155

156-
For `staged`, if either the accumulated configuration or the next configuration is a top-level task-generator function, the next configuration replaces the accumulated configuration entirely. Subsequent glob mappings merge from that replacement; they do not restore earlier tasks. Task functions run when lint-staged supplies the staged file list, not while merging configurations.
156+
For `staged`, a top-level task-generator function replaces all earlier settings. A later configuration also replaces that function in full. Any subsequent glob mappings then merge as usual. For example, with `glob mappings → function → new glob mappings`, only the new mappings remain. Task functions run when lint-staged passes them the list of staged files.
157157

158158
### App and lib presets
159159

160-
A developer infrastructure team can maintain two presets: one for App + Test + Lint + Format, and another for Lib + Test + Lint + Format. Both can extend the `sharedConfig` above.
160+
A developer infrastructure team can provide separate presets for applications and libraries. Each preset combines its build settings with shared test, lint, and formatting settings.
161161

162-
A preset can be a regular function when consumers need options. Call it to produce an object before passing it to `define.extends()`:
162+
The application preset below extends `sharedConfig` and accepts a page title. To make a preset configurable, export a regular function that returns a `RstackConfig` object:
163163

164164
```ts title="app-preset.ts"
165165
import type { RstackConfig } from 'rstack';
@@ -184,6 +184,8 @@ export function appPreset(options: { title: string }): RstackConfig {
184184
}
185185
```
186186

187+
The library preset can use a plain object:
188+
187189
```ts title="lib-preset.ts"
188190
import type { RstackConfig } from 'rstack';
189191
import { sharedConfig } from './shared.ts';
@@ -218,13 +220,15 @@ import { define } from 'rstack';
218220
define.extends([libPreset]);
219221
```
220222

221-
Make the presets' dependencies available: for this example, the App preset needs `@rsbuild/plugin-react`, and application projects need `happy-dom` for their tests.
223+
Declare `@rsbuild/plugin-react` as a dependency of the application preset, and install `happy-dom` in applications that use it for testing.
222224

223225
### Loading dependencies and resolving paths
224226

225-
Shared configurations follow the same [dependency loading guidance](#loading-dependencies-on-demand) as project configurations. Only the tool configuration functions needed by a command are evaluated. In the App preset above, `rs lint` and `rs fmt` do not load the React plugin; `rs test` may load it when inheriting the App configuration. The outer `appPreset()` function runs when the configuration file is loaded, so keep tool-specific imports inside the relevant configuration function.
227+
Shared configurations follow the same [dependency loading guidance](#loading-dependencies-on-demand) as project configurations. A command evaluates only the tool configuration functions it needs. In the application preset above, `rs lint` and `rs fmt` skip the React plugin import. `rs test` loads it when inheriting the application configuration.
228+
229+
The outer `appPreset()` function runs as soon as the configuration file loads. Keep tool-specific imports inside the relevant configuration function, such as `app: async () => { ... }`.
226230

227-
Relative paths follow each tool's existing rules. They are not automatically resolved relative to the shared configuration module. For a file shipped with a preset, use an absolute path derived from that module:
231+
Each tool keeps its existing rules for relative paths, so moving a setting into a preset does not change its path base. To reference a file shipped with the preset, resolve an absolute path from `import.meta.url`:
228232

229233
```ts title="shared.ts"
230234
import { fileURLToPath } from 'node:url';
@@ -237,7 +241,9 @@ export const sharedConfig: RstackConfig = {
237241
};
238242
```
239243

240-
Include `setup.ts` in the published package at that location, or adjust the path to the built file. Existing local configuration dependency tracking also applies to shared modules: editing an imported local shared configuration reloads App, Lib, and Doc configurations in their development or watch modes.
244+
Publish `setup.ts` at that location in the package, or update the path to point to its compiled output.
245+
246+
Local shared modules are also tracked as configuration dependencies. Editing an imported shared module reloads the App, Lib, or Doc configuration in development or watch mode.
241247

242248
## Configuration APIs
243249

@@ -323,7 +329,7 @@ When `extends` is omitted, Rstack CLI automatically connects the test configurat
323329

324330
If the root test configuration does not define `extends` and contains `projects`, Rstack CLI applies automatic inheritance to each inline project that omits its own `extends`. A function-based application or library configuration is resolved once and shared by those projects. String project entries are passed to Rstest unchanged; they load their external configurations independently and do not inherit the current application or library configuration.
325331

326-
When using shared configurations, these rules apply after merging all test configurations and use the merged App or Lib configuration. See [Shared test configurations](./testing#shared-test-configurations).
332+
These rules also apply to shared configurations: Rstack first merges the test settings, then uses the merged App or Lib configuration for automatic inheritance.
327333

328334
> For more guidance on testing, see [Testing](./testing).
329335

‎website/docs/en/guide/testing.mdx‎

Lines changed: 0 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -107,44 +107,6 @@ define.test({
107107

108108
For multiple projects, setting `extends` on the root `define.test()` configuration disables automatic inheritance for every project. Setting it on an inline project disables inheritance only for that project.
109109

110-
### Shared test configurations
111-
112-
[`define.extends()`](./configuration#shared-configurations) shares configurations across tools. The `extends` field inside a test configuration is Rstest's own inheritance option. These operate at different levels.
113-
114-
Rstack first merges all shared and project test configurations using `mergeRstestConfig`. It then checks the resulting `extends` and `projects` fields to decide whether automatic inheritance is needed. If it is, Rstack uses the merged App configuration, or the merged Lib configuration when no App configuration is defined.
115-
116-
```ts title="shared.ts"
117-
import type { RstackConfig } from 'rstack';
118-
119-
export const sharedConfig: RstackConfig = {
120-
app: {
121-
resolve: {
122-
alias: {
123-
'@': './src',
124-
},
125-
},
126-
},
127-
test: {
128-
retry: 2,
129-
},
130-
};
131-
```
132-
133-
```ts title="rstack.config.ts"
134-
import { define } from 'rstack';
135-
import { sharedConfig } from './shared.ts';
136-
137-
define.extends([sharedConfig]);
138-
139-
define.test({
140-
testEnvironment: 'happy-dom',
141-
});
142-
```
143-
144-
Tests use `retry: 2`, run in happy-dom, and inherit the `@` alias from the shared App configuration. A local `define.app()` call is not required.
145-
146-
The same opt-out rules apply to shared configurations: an explicit `extends` in the merged test configuration, including `extends: undefined`, disables automatic inheritance. Inline projects are checked after merging; external string projects still load independently. If no project needs automatic inheritance, Rstack does not evaluate App or Lib configuration functions for it.
147-
148110
## Multiple projects
149111

150112
Set Rstest's [`projects`](https://rstest.rs/config/test/projects) option to run multiple test configurations together. Entries can be inline projects or strings that Rstest resolves as external projects.

0 commit comments

Comments
 (0)