Skip to content

Commit 398ee5f

Browse files
authored
feat: native runtime support for loading config (#4486)
1 parent bceca14 commit 398ee5f

File tree

12 files changed

+123
-27
lines changed

12 files changed

+123
-27
lines changed
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { execSync } from 'node:child_process';
2+
import path from 'node:path';
3+
import { globContentJSON, rspackOnlyTest } from '@e2e/helper';
4+
import { expect, test } from '@playwright/test';
5+
6+
rspackOnlyTest('should use Node.js native loader to load config', async () => {
7+
// TODO: fix this test on Windows
8+
if (process.platform === 'win32') {
9+
test.skip();
10+
}
11+
12+
execSync('npx rsbuild build --config-loader native', {
13+
cwd: __dirname,
14+
env: {
15+
...process.env,
16+
NODE_OPTIONS: '--experimental-strip-types',
17+
},
18+
});
19+
20+
const outputs = await globContentJSON(path.join(__dirname, 'dist-custom'));
21+
const outputFiles = Object.keys(outputs);
22+
23+
expect(outputFiles.length > 1).toBeTruthy();
24+
});
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import type { RsbuildConfig } from '@rsbuild/core';
2+
3+
export default {
4+
output: {
5+
distPath: {
6+
root: 'dist-custom',
7+
},
8+
},
9+
} satisfies RsbuildConfig;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
console.log('hello!');

packages/core/src/cli/commands.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import cac, { type CAC, type Command } from 'cac';
2+
import type { ConfigLoader } from '../config';
23
import { logger } from '../logger';
34
import { onBeforeRestartServer } from '../server/restart';
45
import type { RsbuildMode } from '../types';
@@ -8,6 +9,7 @@ export type CommonOptions = {
89
root?: string;
910
mode?: RsbuildMode;
1011
config?: string;
12+
configLoader?: ConfigLoader;
1113
envDir?: string;
1214
envMode?: string;
1315
open?: boolean | string;
@@ -36,6 +38,13 @@ const applyCommonOptions = (cli: CAC) => {
3638
'-c, --config <config>',
3739
'specify the configuration file, can be a relative or absolute path',
3840
)
41+
.option(
42+
'--config-loader <loader>',
43+
'specify the loader to load the config file, can be `jiti` or `native`',
44+
{
45+
default: 'jiti',
46+
},
47+
)
3948
.option(
4049
'-r, --root <root>',
4150
'specify the project root directory, can be an absolute path or a path relative to cwd',

packages/core/src/cli/init.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ export async function init({
4141
cwd: root,
4242
path: commonOpts.config,
4343
envMode: commonOpts.envMode,
44+
loader: commonOpts.configLoader,
4445
});
4546

4647
const command = process.argv[2];

packages/core/src/config.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -425,6 +425,13 @@ export type LoadConfigOptions = {
425425
* @default process.env.NODE_ENV
426426
*/
427427
envMode?: string;
428+
/**
429+
* Specify the config loader, can be `jiti` or `native`.
430+
* - 'jiti': Use `jiti` as loader, which supports TypeScript and ESM out of the box
431+
* - 'native': Use native Node.js loader, requires TypeScript support in Node.js >= 22.6
432+
* @default 'jiti'
433+
*/
434+
loader?: ConfigLoader;
428435
};
429436

430437
export type LoadConfigResult = {
@@ -439,11 +446,14 @@ export type LoadConfigResult = {
439446
filePath: string | null;
440447
};
441448

449+
export type ConfigLoader = 'jiti' | 'native';
450+
442451
export async function loadConfig({
443452
cwd = process.cwd(),
444453
path,
445454
envMode,
446455
meta,
456+
loader = 'jiti',
447457
}: LoadConfigOptions = {}): Promise<LoadConfigResult> {
448458
const configFilePath = resolveConfigPath(cwd, path);
449459

@@ -461,11 +471,17 @@ export async function loadConfig({
461471

462472
let configExport: RsbuildConfigExport;
463473

464-
if (/\.(?:js|mjs|cjs)$/.test(configFilePath)) {
474+
if (/\.(?:js|mjs|cjs)$/.test(configFilePath) || loader === 'native') {
465475
try {
466476
const exportModule = await import(`${configFilePath}?t=${Date.now()}`);
467477
configExport = exportModule.default ? exportModule.default : exportModule;
468478
} catch (err) {
479+
if (loader === 'native') {
480+
logger.error(
481+
`Failed to load file with native loader: ${color.dim(configFilePath)}`,
482+
);
483+
throw err;
484+
}
469485
logger.debug(
470486
`Failed to load file with dynamic import: ${color.dim(configFilePath)}`,
471487
);

website/docs/en/api/javascript-api/core.mdx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ function loadConfig(params?: {
5656
path?: string;
5757
meta?: Record<string, unknown>;
5858
envMode?: string;
59+
loader?: 'jiti' | 'native';
5960
}): Promise<{
6061
content: RsbuildConfig;
6162
filePath: string | null;

website/docs/en/guide/basic/cli.mdx

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,15 +27,16 @@ Commands:
2727

2828
Rsbuild CLI provides several common flags that can be used with all commands:
2929

30-
| Flag | Description |
31-
| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
32-
| `-c, --config <config>` | Specify the configuration file, can be a relative or absolute path, see [Specify config file](/guide/basic/configure-rsbuild#specify-config-file) |
33-
| `-r, --root <root>` | Specify the project root directory |
34-
| `-m, --mode <mode>` | Specify the build mode (`development`, `production` or `none`), see [mode](/config/mode) |
35-
| `--env-mode <mode>` | Specify the env mode to load the `.env.[mode]` file, see [Env mode](/guide/advanced/env-vars#env-mode) |
36-
| `--env-dir <dir>` | Specify the directory to load `.env` files, see [Env directory](/guide/advanced/env-vars#env-directory) |
37-
| `--environment <name>` | Specify the name of environment to build, see [Build specified environment](/guide/advanced/environments#build-specified-environment) |
38-
| `-h, --help` | Display help for command |
30+
| Flag | Description |
31+
| -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
32+
| `-c, --config <config>` | Specify the configuration file, can be a relative or absolute path, see [Specify config file](/guide/basic/configure-rsbuild#specify-config-file) |
33+
| `--config-loader <loader>` | Specify the config loader, can be `jiti` or `native`, see [Specify config loader](/guide/basic/configure-rsbuild#specify-config-loader) |
34+
| `-r, --root <root>` | Specify the project root directory |
35+
| `-m, --mode <mode>` | Specify the build mode (`development`, `production` or `none`), see [mode](/config/mode) |
36+
| `--env-mode <mode>` | Specify the env mode to load the `.env.[mode]` file, see [Env mode](/guide/advanced/env-vars#env-mode) |
37+
| `--env-dir <dir>` | Specify the directory to load `.env` files, see [Env directory](/guide/advanced/env-vars#env-directory) |
38+
| `--environment <name>` | Specify the name of environment to build, see [Build specified environment](/guide/advanced/environments#build-specified-environment) |
39+
| `-h, --help` | Display help for command |
3940

4041
## rsbuild dev
4142

website/docs/en/guide/basic/configure-rsbuild.mdx

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -93,10 +93,6 @@ export default defineConfig({
9393
});
9494
```
9595

96-
:::tip
97-
When you use the `.ts`, `.mts`, and `.cts` extensions, Rsbuild will use [jiti](https://github.com/unjs/jiti) to load configuration files, providing interoperability between ESM and CommonJS. The behavior of module resolution differs slightly from the native behavior of Node.js.
98-
:::
99-
10096
## Specify config file
10197

10298
Rsbuild CLI uses the `--config` option to specify the config file, which can be set to a relative path or an absolute path.
@@ -117,6 +113,26 @@ You can also abbreviate the `--config` option to `-c`:
117113
rsbuild build -c rsbuild.prod.config.mjs
118114
```
119115

116+
## Specify config loader
117+
118+
When you use a configuration file with the `.ts`, `.mts`, and `.cts` extensions, Rsbuild will use [jiti](https://github.com/unjs/jiti) to load configuration files, providing interoperability between ESM and CommonJS. The behavior of module resolution differs slightly from the native behavior of Node.js.
119+
120+
If your JavaScript runtime already natively supports TypeScript, you can use the `--config-loader native` option to use the Node.js native loader to load the configuration file. This can ensure that the module resolution behavior is consistent with the native behavior of Node.js and has better performance.
121+
122+
For example, Node.js v22.6.0+ already natively supports TypeScript, you can use the following command to use the Node.js native loader to load the configuration file:
123+
124+
```bash
125+
# Node.js v23.6.0+
126+
# No need to set --experimental-strip-types
127+
npx rsbuild build --config-loader native
128+
129+
# Node.js v22.6.0 - v23.5.0
130+
# Need to set --experimental-strip-types
131+
NODE_OPTIONS="--experimental-strip-types" npx rsbuild build --config-loader native
132+
```
133+
134+
> See [Node.js - Running TypeScript Natively](https://nodejs.org/en/learn/typescript/run-natively#running-typescript-natively) for more details.
135+
120136
## Using environment variables
121137

122138
In the configuration file, you can use Node.js environment variables such as `process.env.NODE_ENV` to dynamically set different configurations:

website/docs/zh/api/javascript-api/core.mdx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ function loadConfig(params?: {
5656
path?: string;
5757
meta?: Record<string, unknown>;
5858
envMode?: string;
59+
loader?: 'jiti' | 'native';
5960
}): Promise<{
6061
content: RsbuildConfig;
6162
filePath: string | null;

website/docs/zh/guide/basic/cli.mdx

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,15 +27,16 @@ Commands:
2727

2828
Rsbuild CLI 提供了一些公共选项,可以用于所有命令:
2929

30-
| 选项 | 描述 |
31-
| ----------------------- | ------------------------------------------------------------------------------------------------------------ |
32-
| `-c, --config <config>` | 指定配置文件路径,可以为相对路径或绝对路径,详见 [指定配置文件](/guide/basic/configure-rsbuild#指定配置文件) |
33-
| `-r, --root <root>` | 指定项目根目录,可以是绝对路径或者相对于 cwd 的路径 |
34-
| `-m, --mode <mode>` | 指定构建模式,可以是 `development``production``none`,详见 [mode](/config/mode) |
35-
| `--env-mode <mode>` | 指定 env 模式来加载 `.env.[mode]` 文件,详见 [Env 模式](/guide/advanced/env-vars#env-模式) |
36-
| `--env-dir <dir>` | 指定目录来加载 `.env` 文件,详见 [Env 目录](/guide/advanced/env-vars#env-目录) |
37-
| `--environment <name>` | 指定需要构建的 environment 名称,详见 [构建指定环境](/guide/advanced/environments#构建指定环境) |
38-
| `-h, --help` | 显示命令帮助 |
30+
| 选项 | 描述 |
31+
| -------------------------- | ----------------------------------------------------------------------------------------------------------------- |
32+
| `-c, --config <config>` | 指定配置文件路径,可以为相对路径或绝对路径,详见 [指定配置文件](/guide/basic/configure-rsbuild#指定配置文件) |
33+
| `--config-loader <loader>` | 指定配置文件加载方式,可以为 `jiti``native`,详见 [指定加载方式](/guide/basic/configure-rsbuild#指定加载方式) |
34+
| `-r, --root <root>` | 指定项目根目录,可以是绝对路径或者相对于 cwd 的路径 |
35+
| `-m, --mode <mode>` | 指定构建模式,可以是 `development``production``none`,详见 [mode](/config/mode) |
36+
| `--env-mode <mode>` | 指定 env 模式来加载 `.env.[mode]` 文件,详见 [Env 模式](/guide/advanced/env-vars#env-模式) |
37+
| `--env-dir <dir>` | 指定目录来加载 `.env` 文件,详见 [Env 目录](/guide/advanced/env-vars#env-目录) |
38+
| `--environment <name>` | 指定需要构建的 environment 名称,详见 [构建指定环境](/guide/advanced/environments#构建指定环境) |
39+
| `-h, --help` | 显示命令帮助 |
3940

4041
## rsbuild dev
4142

website/docs/zh/guide/basic/configure-rsbuild.mdx

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -93,10 +93,6 @@ export default defineConfig({
9393
});
9494
```
9595

96-
:::tip
97-
当你使用 `.ts`, `.mts``.cts` 后缀时,Rsbuild 会使用 [jiti](https://github.com/unjs/jiti) 来加载配置文件,提供 ESM 与 CommonJS 的互操作性,模块解析的行为与 Node.js 原生行为存在一定差异。
98-
:::
99-
10096
## 指定配置文件
10197

10298
Rsbuild CLI 通过 `--config` 选项来指定配置文件,可以设置为相对路径或绝对路径。
@@ -117,6 +113,26 @@ Rsbuild CLI 通过 `--config` 选项来指定配置文件,可以设置为相
117113
rsbuild build -c rsbuild.prod.config.mjs
118114
```
119115

116+
## 指定加载方式
117+
118+
当你使用 `.ts`, `.mts``.cts` 后缀的配置文件时,Rsbuild 会使用 [jiti](https://github.com/unjs/jiti) 来加载配置文件,提供 ESM 与 CommonJS 的互操作性,模块解析的行为与 Node.js 原生行为存在一定差异。
119+
120+
如果你使用的 JavaScript 运行时已经原生支持 TypeScript,可以使用 `--config-loader native` 选项来使用 Node.js 原生 loader 来加载配置文件。这可以保证模块解析的行为与 Node.js 原生行为一致,并且性能更好。
121+
122+
例如,Node.js 从 v22.6.0 开始已经原生支持 TypeScript,你可以如下命令来使用 Node.js 原生 loader 来加载配置文件:
123+
124+
```bash
125+
# Node.js v23.6.0+
126+
# 不需要设置 --experimental-strip-types
127+
npx rsbuild build --config-loader native
128+
129+
# Node.js v22.6.0 - v23.5.0
130+
# 需要设置 --experimental-strip-types
131+
NODE_OPTIONS="--experimental-strip-types" npx rsbuild build --config-loader native
132+
```
133+
134+
> 详见 [Node.js - Running TypeScript Natively](https://nodejs.org/en/learn/typescript/run-natively#running-typescript-natively)
135+
120136
## 使用环境变量
121137

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

0 commit comments

Comments
 (0)