Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions e2e/cases/javascript-api/dev-server-restart/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import fs from 'node:fs';
import path from 'node:path';
import { expect, test } from '@e2e/helper';
import { createRsbuild, loadConfig, type RestartContext } from '@rsbuild/core';
import { getRandomPort } from '@rstackjs/test-utils';

const configDep = path.join(import.meta.dirname, 'test-temp-config-dep.mjs');
const configDepContent = 'export const sharedConfig = {};\n';

test.beforeAll(() => {
fs.writeFileSync(configDep, configDepContent);
});

test.afterAll(() => {
fs.rmSync(configDep, { force: true });
});

test('should watch loaded config dependencies for restart', async () => {
const result = await loadConfig({ cwd: import.meta.dirname });

result.content.server = {
port: await getRandomPort(),
};

const rsbuild = await createRsbuild({
cwd: import.meta.dirname,
config: result,
});

let restartContext: RestartContext | undefined;

rsbuild.onRestart((context) => {
restartContext = context;
});

const server = await rsbuild.createDevServer({ runCompile: false });
let version = 0;

try {
await expect
.poll(() => {
if (!restartContext) {
fs.writeFileSync(configDep, `${configDepContent}// ${++version}\n`);
}
return restartContext;
})
.toEqual({
action: 'dev',
filePath: configDep,
});
} finally {
await server.close();
}
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { sharedConfig } from './test-temp-config-dep.mjs';

export default sharedConfig;
1 change: 1 addition & 0 deletions e2e/cases/javascript-api/dev-server-restart/src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
console.log('restart config files');
14 changes: 1 addition & 13 deletions packages/core/src/cli/init.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import path from 'node:path';
import { createRsbuild } from '../createRsbuild';
import { castArray } from '../helpers';
import { ensureAbsolutePath } from '../helpers/path';
import { loadConfig as baseLoadConfig } from '../loadConfig';
import { defaultLogger } from '../logger';
Expand Down Expand Up @@ -47,7 +46,7 @@ const loadConfig = async (root: string) => {
loader: options.configLoader,
command,
});
const { content: config, filePath, dependencies } = result;
const { content: config } = result;

config.dev ||= {};
config.source ||= {};
Expand Down Expand Up @@ -111,17 +110,6 @@ const loadConfig = async (root: string) => {
config.dev.cliShortcuts = true;
}

// watch the config file
if (filePath) {
config.dev.watchFiles = [
...(config.dev.watchFiles ? castArray(config.dev.watchFiles) : []),
{
paths: [filePath, ...dependencies],
type: 'restart',
},
];
}

return result;
};

Expand Down
7 changes: 5 additions & 2 deletions packages/core/src/restart.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,15 @@ export function watchFilesForRestart({
context: InternalContext;
action: RestartContext['action'];
}): WatchFilesResult | undefined {
if (!watchFiles.length) {
const defaultFiles = context.configFile
? [context.configFile, ...context.configFileDependencies]
: [];
Comment thread
chenjiahan marked this conversation as resolved.

if (!watchFiles.length && !defaultFiles.length) {
return;
}

const watchGroups: { files: string[]; options?: ChokidarOptions }[] = [];
const defaultFiles: string[] = [];

for (const { paths, options, type } of watchFiles) {
if (type !== 'restart' && type !== 'reload-server') {
Expand Down
4 changes: 3 additions & 1 deletion website/docs/en/api/javascript-api/core.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ const rsbuild = await createRsbuild({
});
```

When the complete `loadConfig` result is passed, the absolute paths of the config file and its imported dependencies are stored in [`rsbuild.context.configFile`](/api/javascript-api/instance#contextconfigfile) and [`rsbuild.context.configFileDependencies`](/api/javascript-api/instance#contextconfigfiledependencies), respectively, and used as build dependencies when persistent build cache is enabled.
When the complete `loadConfig` result is passed, the absolute paths of the config file and its imported dependencies are stored in [`rsbuild.context.configFile`](/api/javascript-api/instance#contextconfigfile) and [`rsbuild.context.configFileDependencies`](/api/javascript-api/instance#contextconfigfiledependencies), respectively. When persistent build cache is enabled, these files are also used as build dependencies. See [Configuration file watching](/guide/configuration/rsbuild#configuration-file-watching) for how Rsbuild watches them for restart requests.

### Load environment variables

Expand Down Expand Up @@ -105,6 +105,8 @@ const rsbuild = await createRsbuild({

The `restart` option allows the caller to control how Rsbuild restarts the current dev server or watch build. This is useful when the restart flow needs to be managed outside Rsbuild.

> See [Configuration file watching](/guide/configuration/rsbuild#configuration-file-watching) to learn what triggers a restart.

When a restart is requested, Rsbuild calls the [onRestart hook](/plugins/dev/hooks#onrestart), closes the current task resources, and then calls `restart`.

The function should return `true` when the replacement task starts successfully. If it returns `false` or throws an error, the restart watcher remains active so later file changes can retry the restart.
Expand Down
2 changes: 1 addition & 1 deletion website/docs/en/config/dev/watch-files.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ export default {
};
```

When using the JavaScript API, `rsbuild.startDevServer()`, `rsbuild.createDevServer()`, and `rsbuild.build({ watch: true })` install restart watchers, while regular builds and preview servers do not. When a watched file changes, Rsbuild calls the [onRestart hook](/plugins/dev/hooks#onrestart). By default, Rsbuild does not close or restart the current task; you can pass the [`restart` option](/api/javascript-api/core#restart) to handle restart requests.
> For details about automatic config file watching and restart behavior, see [Configuration file watching](/guide/configuration/rsbuild#configuration-file-watching).

### reload-server

Expand Down
8 changes: 8 additions & 0 deletions website/docs/en/guide/configuration/rsbuild.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,14 @@ When using Node.js's native loader, note the following limitations:

> See [Node.js - Running TypeScript Natively](https://nodejs.org/en/learn/typescript/run-natively#running-typescript-natively) for more details.

## Configuration file watching

When using the Rsbuild CLI, `rsbuild dev` and `rsbuild build --watch` automatically watch the loaded configuration file and its imported dependencies. When one of these files changes, Rsbuild reloads the configuration and restarts the current dev server or watch build.

When using the JavaScript API, pass the complete [`loadConfig`](/api/javascript-api/core#loadconfig) result to `createRsbuild` so Rsbuild can track and automatically watch these files.

`rsbuild.startDevServer()`, `rsbuild.createDevServer()`, and `rsbuild.build({ watch: true })` install restart watchers, while regular builds and preview servers do not. When a watched file changes, Rsbuild calls the [onRestart hook](/plugins/dev/hooks#onrestart). By default, Rsbuild does not close or restart the current task; you can pass the [`restart` option](/api/javascript-api/core#restart) to handle restart requests.

## Using environment variables

In the configuration file, you can use Node.js environment variables such as `process.env.NODE_ENV` to dynamically set different configurations:
Expand Down
6 changes: 4 additions & 2 deletions website/docs/zh/api/javascript-api/core.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ const rsbuild = await createRsbuild({
});
```

传入完整的 `loadConfig` 返回结果后,配置文件及其导入依赖的绝对路径会分别记录在 [`rsbuild.context.configFile`](/api/javascript-api/instance#contextconfigfile) 和 [`rsbuild.context.configFileDependencies`](/api/javascript-api/instance#contextconfigfiledependencies) 中,并在启用持久化构建缓存时作为构建依赖
传入完整的 `loadConfig` 返回结果后,配置文件及其导入依赖的绝对路径会分别记录在 [`rsbuild.context.configFile`](/api/javascript-api/instance#contextconfigfile) 和 [`rsbuild.context.configFileDependencies`](/api/javascript-api/instance#contextconfigfiledependencies) 中。启用持久化构建缓存时,这些文件也会作为构建依赖。关于 Rsbuild 如何监听这些文件,并在文件变化时请求重启,详见[配置文件监听](/guide/configuration/rsbuild#configuration-file-watching)

### 加载环境变量

Expand Down Expand Up @@ -103,7 +103,9 @@ const rsbuild = await createRsbuild({

- **版本:** v2.1.7 新增

`restart` 选项允许调用方控制 Rsbuild 如何重启当前 dev server 或监听构建,适用于需要在 Rsbuild 外部管理重启流程的场景。
`restart` 选项允许你控制 Rsbuild 如何重启当前 dev server 或监听构建,适用于需要在 Rsbuild 外部管理重启流程的场景。

> 查看 [配置文件监听](/guide/configuration/rsbuild#configuration-file-watching) 了解重启的触发方式。

当请求重启时,Rsbuild 会调用 [onRestart hook](/plugins/dev/hooks#onrestart),关闭当前任务的资源,然后调用 `restart`。

Expand Down
2 changes: 1 addition & 1 deletion website/docs/zh/config/dev/watch-files.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ export default {
};
```

使用 JavaScript API 时,`rsbuild.startDevServer()`、`rsbuild.createDevServer()` 和 `rsbuild.build({ watch: true })` 会安装 restart watcher,普通构建和预览服务器则不会安装。被监听的文件发生变化时,Rsbuild 会调用 [onRestart hook](/plugins/dev/hooks#onrestart)。默认情况下,Rsbuild 不会关闭或重启当前任务;你可以传入 [`restart` 选项](/api/javascript-api/core#restart) 来处理重启请求
> 关于配置文件自动监听及重启行为,详见[配置文件监听](/guide/configuration/rsbuild#configuration-file-watching)

### reload-server

Expand Down
8 changes: 8 additions & 0 deletions website/docs/zh/guide/configuration/rsbuild.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,14 @@ Rsbuild 提供了三种配置文件加载方式:

> 详见 [Node.js - Running TypeScript Natively](https://nodejs.org/en/learn/typescript/run-natively#running-typescript-natively)。

## 配置文件监听 \{#configuration-file-watching}

使用 Rsbuild CLI 时,`rsbuild dev` 和 `rsbuild build --watch` 会自动监听已加载的配置文件及其导入依赖。当其中任一文件发生变化时,Rsbuild 会重新加载配置,并重启当前 dev server 或监听构建。

使用 JavaScript API 时,请将完整的 [`loadConfig`](/api/javascript-api/core#loadconfig) 返回结果传入 `createRsbuild`,使 Rsbuild 可以追踪并自动监听这些文件。

`rsbuild.startDevServer()`、`rsbuild.createDevServer()` 和 `rsbuild.build({ watch: true })` 会安装 restart watcher,普通构建和预览服务器则不会安装。被监听的文件发生变化时,Rsbuild 会调用 [onRestart hook](/plugins/dev/hooks#onrestart)。默认情况下,Rsbuild 不会关闭或重启当前任务;你可以传入 [`restart` 选项](/api/javascript-api/core#restart) 来处理重启请求。

## 使用环境变量

在配置文件中,你可以使用 `process.env.NODE_ENV` 等 Node.js 环境变量,来动态写入不同的配置:
Expand Down