From 00e2a37e0a607bb1047c0746dae0a95436a0f9fe Mon Sep 17 00:00:00 2001 From: neverland Date: Mon, 15 Sep 2025 21:15:11 +0800 Subject: [PATCH 1/3] feat(CLI): add `--config-loader` option --- packages/core/src/cli/commands.ts | 9 ++++++++ packages/core/src/cli/init.ts | 1 + packages/core/src/config.ts | 5 +++++ tests/integration/cli/build/build.test.ts | 21 +++++++++++++++++++ tests/scripts/index.ts | 4 ++-- tests/scripts/rstest.ts | 2 +- tests/scripts/shared.ts | 2 +- tests/scripts/tsconfig.json | 10 +++++++++ tests/tsconfig.json | 3 +-- website/docs/en/guide/basic/cli.mdx | 17 ++++++++------- .../docs/en/guide/basic/configure-rslib.mdx | 20 ++++++++++++++++++ website/docs/zh/guide/basic/cli.mdx | 17 ++++++++------- .../docs/zh/guide/basic/configure-rslib.mdx | 20 ++++++++++++++++++ 13 files changed, 109 insertions(+), 22 deletions(-) create mode 100644 tests/scripts/tsconfig.json diff --git a/packages/core/src/cli/commands.ts b/packages/core/src/cli/commands.ts index 77fe7188a..0949eb95b 100644 --- a/packages/core/src/cli/commands.ts +++ b/packages/core/src/cli/commands.ts @@ -1,5 +1,6 @@ import type { RsbuildMode } from '@rsbuild/core'; import cac, { type CAC } from 'cac'; +import type { ConfigLoader } from '../config'; import { logger } from '../utils/logger'; import { build } from './build'; import { init } from './init'; @@ -13,6 +14,7 @@ export type CommonOptions = { envDir?: string; envMode?: string; lib?: string[]; + configLoader?: ConfigLoader; }; export type BuildOptions = CommonOptions & { @@ -39,6 +41,13 @@ const applyCommonOptions = (cli: CAC) => { '--env-mode ', 'specify the env mode to load the `.env.[mode]` file', ) + .option( + '--config-loader ', + 'Set the config file loader (jiti | native)', + { + default: 'jiti', + }, + ) .option('--env-dir ', 'specify the directory to load `.env` files') .option( '--lib ', diff --git a/packages/core/src/cli/init.ts b/packages/core/src/cli/init.ts index c737efa7f..86f4a8e16 100644 --- a/packages/core/src/cli/init.ts +++ b/packages/core/src/cli/init.ts @@ -31,6 +31,7 @@ export async function init(options: CommonOptions): Promise<{ cwd: root, path: options.config, envMode: options.envMode, + loader: options.configLoader, }); config.source ||= {}; diff --git a/packages/core/src/config.ts b/packages/core/src/config.ts index fd8dde6dc..fb3014788 100644 --- a/packages/core/src/config.ts +++ b/packages/core/src/config.ts @@ -119,14 +119,18 @@ const resolveConfigPath = (root: string, customConfig?: string): string => { throw new Error(`${DEFAULT_CONFIG_NAME} not found in ${root}`); }; +export type ConfigLoader = 'jiti' | 'native'; + export async function loadConfig({ cwd = process.cwd(), path, envMode, + loader, }: { cwd?: string; path?: string; envMode?: string; + loader?: ConfigLoader; }): Promise<{ content: RslibConfig; filePath: string; @@ -136,6 +140,7 @@ export async function loadConfig({ cwd: dirname(configFilePath), path: configFilePath, envMode, + loader, }); return { content: content as RslibConfig, filePath: configFilePath }; diff --git a/tests/integration/cli/build/build.test.ts b/tests/integration/cli/build/build.test.ts index 130b96f32..d9e04c776 100644 --- a/tests/integration/cli/build/build.test.ts +++ b/tests/integration/cli/build/build.test.ts @@ -82,6 +82,27 @@ describe('build command', async () => { `); }); + test('--config-loader', async () => { + // Skip Node.js <= 22.18 + if (!process.features.typescript) { + return; + } + + await fse.remove(path.join(__dirname, 'dist')); + runCliSync('build --config-loader native', { + cwd: __dirname, + }); + + const files = await globContentJSON(path.join(__dirname, 'dist')); + const fileNames = Object.keys(files).sort(); + expect(fileNames).toMatchInlineSnapshot(` + [ + "/tests/integration/cli/build/dist/cjs/index.cjs", + "/tests/integration/cli/build/dist/esm/index.js", + ] + `); + }); + test('--root', async () => { await fse.remove(path.join(__dirname, 'dist')); runCliSync('build --root custom-root', { diff --git a/tests/scripts/index.ts b/tests/scripts/index.ts index 68c9a9a09..16677da42 100644 --- a/tests/scripts/index.ts +++ b/tests/scripts/index.ts @@ -1,2 +1,2 @@ -export * from './helper'; -export * from './shared'; +export * from './helper.ts'; +export * from './shared.ts'; diff --git a/tests/scripts/rstest.ts b/tests/scripts/rstest.ts index 781a6f0e4..610253e53 100644 --- a/tests/scripts/rstest.ts +++ b/tests/scripts/rstest.ts @@ -1,6 +1,6 @@ // This file can only be imported by @rstest/core test files import { expect } from '@rstest/core'; -import { getFileBySuffix } from './shared'; +import { getFileBySuffix } from './shared.ts'; export function expectFileContainContent( files: Record, diff --git a/tests/scripts/shared.ts b/tests/scripts/shared.ts index 1d949c926..3ece9a887 100644 --- a/tests/scripts/shared.ts +++ b/tests/scripts/shared.ts @@ -15,7 +15,7 @@ import { } from '@rsbuild/core'; import type { Format, LibConfig, RslibConfig } from '@rslib/core'; import { build, loadConfig } from '@rslib/core'; -import { globContentJSON } from './helper'; +import { globContentJSON } from './helper.ts'; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); diff --git a/tests/scripts/tsconfig.json b/tests/scripts/tsconfig.json new file mode 100644 index 000000000..dc77422a5 --- /dev/null +++ b/tests/scripts/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "module": "nodenext", + "moduleResolution": "nodenext", + "allowImportingTsExtensions": true + }, + "include": ["./*.ts"], + "exclude": ["**/node_modules", "**/.*/"] +} diff --git a/tests/tsconfig.json b/tests/tsconfig.json index 6e2a2e939..a339e5521 100644 --- a/tests/tsconfig.json +++ b/tests/tsconfig.json @@ -11,8 +11,7 @@ "integration/**/rslib.config.ts", "integration/**/rslib.config.js", "benchmark/**/*.ts", - "playwright.config.ts", - "scripts" + "playwright.config.ts" ], "exclude": ["**/node_modules", "**/.*/"], "references": [ diff --git a/website/docs/en/guide/basic/cli.mdx b/website/docs/en/guide/basic/cli.mdx index 8ed22fa3f..27e572d44 100644 --- a/website/docs/en/guide/basic/cli.mdx +++ b/website/docs/en/guide/basic/cli.mdx @@ -26,14 +26,15 @@ Commands: Rslib CLI provides several common flags that can be used with all commands: -| Flag | Description | -| ----------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `-c, --config ` | Specify the configuration file, can be a relative or absolute path, see [Specify config file](/guide/basic/configure-rslib#specify-config-file) | -| `--env-dir ` | Specify the directory to load `.env` files, see [Rsbuild - Env directory](https://rsbuild.rs/guide/advanced/env-vars#env-directory) | -| `--env-mode ` | Specify the env mode to load the `.env.[mode]` file, see [Rsbuild - Env mode](https://rsbuild.rs/guide/advanced/env-vars#env-mode) | -| `-h, --help` | Display help for command | -| `--lib ` | Specify the library to run commands (repeatable, e.g. `--lib esm --lib cjs`), see [lib.id](/config/lib/id) to learn how to get or set the ID of the library | -| `-r, --root ` | Specify the project root directory, can be an absolute path or a path relative to cwd | +| Flag | Description | +| -------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `-c, --config ` | Specify the configuration file, can be a relative or absolute path, see [Specify config file](/guide/basic/configure-rslib#specify-config-file) | +| `--config-loader ` | Set the config file loader (`jiti` \| `native`), see [Set config loader](/guide/basic/configure-rslib#specify-config-loader) | +| `--env-dir ` | Specify the directory to load `.env` files, see [Rsbuild - Env directory](https://rsbuild.rs/guide/advanced/env-vars#env-directory) | +| `--env-mode ` | Specify the env mode to load the `.env.[mode]` file, see [Rsbuild - Env mode](https://rsbuild.rs/guide/advanced/env-vars#env-mode) | +| `-h, --help` | Display help for command | +| `--lib ` | Specify the library to run commands (repeatable, e.g. `--lib esm --lib cjs`), see [lib.id](/config/lib/id) to learn how to get or set the ID of the library | +| `-r, --root ` | Specify the project root directory, can be an absolute path or a path relative to cwd | ## rslib build diff --git a/website/docs/en/guide/basic/configure-rslib.mdx b/website/docs/en/guide/basic/configure-rslib.mdx index 583a67751..cc2a75973 100644 --- a/website/docs/en/guide/basic/configure-rslib.mdx +++ b/website/docs/en/guide/basic/configure-rslib.mdx @@ -125,6 +125,26 @@ You can also abbreviate the `--config` option to `-c`: rslib build -c rslib.prod.config.mjs ``` +## Specify config loader + +When you use a configuration file with the `.ts`, `.mts`, and `.cts` extensions, Rslib 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. + +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. + +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: + +```bash +# Node.js >= v22.18.0 +# No need to set --experimental-strip-types +npx rslib build --config-loader native + +# Node.js v22.6.0 - v22.17.0 +# Need to set --experimental-strip-types +NODE_OPTIONS="--experimental-strip-types" npx rslib build --config-loader native +``` + +> See [Node.js - Running TypeScript Natively](https://nodejs.org/en/learn/typescript/run-natively#running-typescript-natively) for more details. + ## 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: diff --git a/website/docs/zh/guide/basic/cli.mdx b/website/docs/zh/guide/basic/cli.mdx index 68505df85..f586b3a79 100644 --- a/website/docs/zh/guide/basic/cli.mdx +++ b/website/docs/zh/guide/basic/cli.mdx @@ -26,14 +26,15 @@ Commands: Rslib CLI 提供了一些公共选项,可以用于所有命令: -| 选项 | 描述 | -| ----------------------- | ------------------------------------------------------------------------------------------------------------------------- | -| `-c, --config ` | 指定配置文件路径,可以为相对路径或绝对路径,详见 [指定配置文件](/guide/basic/configure-rslib#指定配置文件) | -| `--env-dir ` | 指定目录来加载 `.env` 文件,详见 [Rsbuild - Env 目录](https://rsbuild.rs/zh/guide/advanced/env-vars#env-目录) | -| `--env-mode ` | 指定 env 模式来加载 `.env.[mode]` 文件,详见 [Rsbuild - Env 模式](https://rsbuild.rs/zh/guide/advanced/env-vars#env-模式) | -| `-h, --help` | 显示命令帮助 | -| `--lib ` | 指定运行命令的库(可重复,例如:`--lib esm --lib cjs`),查看 [lib.id](/config/lib/id) 了解如何获取或设置库的 ID | -| `-r, --root ` | 指定项目根目录,可以是绝对路径或者相对于 cwd 的路径 | +| 选项 | 描述 | +| -------------------------- | ------------------------------------------------------------------------------------------------------------------------- | +| `-c, --config ` | 指定配置文件路径,可以为相对路径或绝对路径,详见 [指定配置文件](/guide/basic/configure-rslib#指定配置文件) | +| `--config-loader ` | 指定配置文件加载方式(`jiti` \| `native`),详见 [指定加载方式](/guide/basic/configure-rslib#指定加载方式) | +| `--env-dir ` | 指定目录来加载 `.env` 文件,详见 [Rsbuild - Env 目录](https://rsbuild.rs/zh/guide/advanced/env-vars#env-目录) | +| `--env-mode ` | 指定 env 模式来加载 `.env.[mode]` 文件,详见 [Rsbuild - Env 模式](https://rsbuild.rs/zh/guide/advanced/env-vars#env-模式) | +| `-h, --help` | 显示命令帮助 | +| `--lib ` | 指定运行命令的库(可重复,例如:`--lib esm --lib cjs`),查看 [lib.id](/config/lib/id) 了解如何获取或设置库的 ID | +| `-r, --root ` | 指定项目根目录,可以是绝对路径或者相对于 cwd 的路径 | ## rslib build diff --git a/website/docs/zh/guide/basic/configure-rslib.mdx b/website/docs/zh/guide/basic/configure-rslib.mdx index 96159eafb..1b3733d23 100644 --- a/website/docs/zh/guide/basic/configure-rslib.mdx +++ b/website/docs/zh/guide/basic/configure-rslib.mdx @@ -125,6 +125,26 @@ Rslib CLI 通过 `--config` 选项来指定配置文件,可以设置为相对 rslib build -c rslib.prod.config.mjs ``` +## 指定加载方式 + +当你使用 `.ts`, `.mts` 和 `.cts` 后缀的配置文件时,Rslib 会使用 [jiti](https://github.com/unjs/jiti) 来加载配置文件,提供 ESM 与 CommonJS 的互操作性,模块解析的行为与 Node.js 原生行为存在一定差异。 + +如果你使用的 JavaScript 运行时已经原生支持 TypeScript,可以使用 `--config-loader native` 选项来使用 Node.js 原生 loader 来加载配置文件。这可以保证模块解析的行为与 Node.js 原生行为一致,并且性能更好。 + +例如,Node.js 从 v22.6.0 开始已经原生支持 TypeScript,你可以如下命令来使用 Node.js 原生 loader 来加载配置文件: + +```bash +# Node.js >= v22.18.0 +# 不需要设置 --experimental-strip-types +npx rslib build --config-loader native + +# Node.js v22.6.0 - v22.17.0 +# 需要设置 --experimental-strip-types +NODE_OPTIONS="--experimental-strip-types" npx rslib build --config-loader native +``` + +> 详见 [Node.js - Running TypeScript Natively](https://nodejs.org/en/learn/typescript/run-natively#running-typescript-natively)。 + ## 使用环境变量 在配置文件中,你可以使用 `process.env.NODE_ENV` 等 Node.js 环境变量,来动态写入不同的配置: From 6dd5e9cd2c00999ff50251be3b6a70fa2951ae17 Mon Sep 17 00:00:00 2001 From: neverland Date: Mon, 15 Sep 2025 21:15:58 +0800 Subject: [PATCH 2/3] fix --- website/docs/en/guide/basic/cli.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/en/guide/basic/cli.mdx b/website/docs/en/guide/basic/cli.mdx index 27e572d44..a56370593 100644 --- a/website/docs/en/guide/basic/cli.mdx +++ b/website/docs/en/guide/basic/cli.mdx @@ -29,7 +29,7 @@ Rslib CLI provides several common flags that can be used with all commands: | Flag | Description | | -------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | | `-c, --config ` | Specify the configuration file, can be a relative or absolute path, see [Specify config file](/guide/basic/configure-rslib#specify-config-file) | -| `--config-loader ` | Set the config file loader (`jiti` \| `native`), see [Set config loader](/guide/basic/configure-rslib#specify-config-loader) | +| `--config-loader ` | Set the config file loader (`jiti` \| `native`), see [Specify config loader](/guide/basic/configure-rslib#specify-config-loader) | | `--env-dir ` | Specify the directory to load `.env` files, see [Rsbuild - Env directory](https://rsbuild.rs/guide/advanced/env-vars#env-directory) | | `--env-mode ` | Specify the env mode to load the `.env.[mode]` file, see [Rsbuild - Env mode](https://rsbuild.rs/guide/advanced/env-vars#env-mode) | | `-h, --help` | Display help for command | From 231e9d663efe72be0192f4b1cd1d3084b368a3de Mon Sep 17 00:00:00 2001 From: neverland Date: Mon, 15 Sep 2025 21:27:10 +0800 Subject: [PATCH 3/3] fix --- tests/scripts/tsconfig.json | 3 +-- tests/tsconfig.json | 3 ++- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/scripts/tsconfig.json b/tests/scripts/tsconfig.json index dc77422a5..ccc22ef7e 100644 --- a/tests/scripts/tsconfig.json +++ b/tests/scripts/tsconfig.json @@ -2,8 +2,7 @@ "extends": "../tsconfig.json", "compilerOptions": { "module": "nodenext", - "moduleResolution": "nodenext", - "allowImportingTsExtensions": true + "moduleResolution": "nodenext" }, "include": ["./*.ts"], "exclude": ["**/node_modules", "**/.*/"] diff --git a/tests/tsconfig.json b/tests/tsconfig.json index a339e5521..896d51c56 100644 --- a/tests/tsconfig.json +++ b/tests/tsconfig.json @@ -3,7 +3,8 @@ "compilerOptions": { "noEmit": true, "composite": true, - "allowJs": true + "allowJs": true, + "allowImportingTsExtensions": true }, "include": [ "e2e/**/*.ts",