From c7938dc3f87460fd9ec4f979384735a4f35ccfd9 Mon Sep 17 00:00:00 2001
From: Timeless0911 <1604889533@qq.com>
Date: Mon, 22 Sep 2025 19:15:46 +0800
Subject: [PATCH] feat(CLI): support `logLevel`
---
.vscode/settings.json | 4 +-
packages/core/src/cli/build.ts | 2 +
packages/core/src/cli/commands.ts | 7 +++-
packages/core/src/cli/init.ts | 4 ++
packages/core/src/cli/inspect.ts | 2 +
packages/core/src/cli/mf.ts | 2 +
packages/core/src/cli/prepare.ts | 19 ++++++++-
packages/core/src/config.ts | 7 +++-
packages/core/src/utils/logger.ts | 4 +-
.../tests/__snapshots__/config.test.ts.snap | 3 +-
packages/plugin-dts/src/dts.ts | 3 ++
packages/plugin-dts/src/index.ts | 12 +++++-
pnpm-lock.yaml | 2 +
tests/integration/cli/log-level/index.test.ts | 42 +++++++++++++++++++
tests/integration/cli/log-level/package.json | 6 +++
.../integration/cli/log-level/rslib.config.ts | 6 +++
tests/integration/cli/log-level/src/index.ts | 1 +
website/docs/en/config/rsbuild/_meta.json | 5 +++
website/docs/en/config/rsbuild/index.mdx | 2 +-
website/docs/en/config/rsbuild/log-level.mdx | 8 ++++
website/docs/en/guide/basic/cli.mdx | 1 +
website/docs/zh/config/rsbuild/_meta.json | 5 +++
website/docs/zh/config/rsbuild/index.mdx | 1 +
website/docs/zh/config/rsbuild/log-level.mdx | 8 ++++
website/docs/zh/guide/basic/cli.mdx | 1 +
25 files changed, 147 insertions(+), 10 deletions(-)
create mode 100644 tests/integration/cli/log-level/index.test.ts
create mode 100644 tests/integration/cli/log-level/package.json
create mode 100644 tests/integration/cli/log-level/rslib.config.ts
create mode 100644 tests/integration/cli/log-level/src/index.ts
create mode 100644 website/docs/en/config/rsbuild/log-level.mdx
create mode 100644 website/docs/zh/config/rsbuild/log-level.mdx
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 108fe84e3..732d0ca50 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -57,11 +57,11 @@
"json.schemas": [
{
"fileMatch": ["**/_meta.json"],
- "url": "./website/node_modules/rspress/meta-json-schema.json"
+ "url": "./website/node_modules/@rspress/core/meta-json-schema.json"
},
{
"fileMatch": ["**/_nav.json"],
- "url": "./website/node_modules/rspress/nav-json-schema.json"
+ "url": "./website/node_modules/@rspress/core/nav-json-schema.json"
}
]
}
diff --git a/packages/core/src/cli/build.ts b/packages/core/src/cli/build.ts
index 3dc4a64ea..bf95c5c1a 100644
--- a/packages/core/src/cli/build.ts
+++ b/packages/core/src/cli/build.ts
@@ -1,6 +1,7 @@
import { createRsbuild, type RsbuildInstance } from '@rsbuild/core';
import { composeRsbuildEnvironments, pruneEnvironments } from '../config';
import type { RslibConfig } from '../types/config';
+import { isDebug } from '../utils/logger';
import type { BuildOptions } from './commands';
import { onBeforeRestart } from './restart';
@@ -17,6 +18,7 @@ export async function build(
plugins: config.plugins,
dev: config.dev,
server: config.server,
+ logLevel: isDebug() ? 'info' : config.logLevel,
environments: pruneEnvironments(environments, options.lib),
},
});
diff --git a/packages/core/src/cli/commands.ts b/packages/core/src/cli/commands.ts
index ec551b52a..0358abe26 100644
--- a/packages/core/src/cli/commands.ts
+++ b/packages/core/src/cli/commands.ts
@@ -1,4 +1,4 @@
-import type { RsbuildMode } from '@rsbuild/core';
+import type { LogLevel, RsbuildMode } from '@rsbuild/core';
import cac, { type CAC } from 'cac';
import type { ConfigLoader } from '../config';
import { logger } from '../utils/logger';
@@ -15,6 +15,7 @@ export type CommonOptions = {
envMode?: string;
lib?: string[];
configLoader?: ConfigLoader;
+ logLevel?: LogLevel;
};
export type BuildOptions = CommonOptions & {
@@ -49,6 +50,10 @@ const applyCommonOptions = (cli: CAC) => {
},
)
.option('--env-dir
', 'specify the directory to load `.env` files')
+ .option(
+ '--log-level ',
+ 'set the log level (info | warn | error | silent)',
+ )
.option(
'--lib ',
'specify the library (repeatable, e.g. --lib esm --lib cjs)',
diff --git a/packages/core/src/cli/init.ts b/packages/core/src/cli/init.ts
index 86f4a8e16..2845675e1 100644
--- a/packages/core/src/cli/init.ts
+++ b/packages/core/src/cli/init.ts
@@ -44,6 +44,10 @@ export async function init(options: CommonOptions): Promise<{
config.root = root;
}
+ if (options.logLevel) {
+ config.logLevel = options.logLevel;
+ }
+
return {
config,
configFilePath,
diff --git a/packages/core/src/cli/inspect.ts b/packages/core/src/cli/inspect.ts
index 789c26925..c52201b3b 100644
--- a/packages/core/src/cli/inspect.ts
+++ b/packages/core/src/cli/inspect.ts
@@ -1,6 +1,7 @@
import { createRsbuild, type RsbuildInstance } from '@rsbuild/core';
import { composeRsbuildEnvironments, pruneEnvironments } from '../config';
import type { RslibConfig } from '../types/config';
+import { isDebug } from '../utils/logger';
import type { InspectOptions } from './commands';
export async function inspect(
@@ -16,6 +17,7 @@ export async function inspect(
plugins: config.plugins,
dev: config.dev,
server: config.server,
+ logLevel: isDebug() ? 'info' : config.logLevel,
environments: pruneEnvironments(environments, options.lib),
},
});
diff --git a/packages/core/src/cli/mf.ts b/packages/core/src/cli/mf.ts
index 2caaae57e..c8b3c3f51 100644
--- a/packages/core/src/cli/mf.ts
+++ b/packages/core/src/cli/mf.ts
@@ -2,6 +2,7 @@ import type { RsbuildInstance } from '@rsbuild/core';
import { createRsbuild } from '@rsbuild/core';
import { composeRsbuildEnvironments, pruneEnvironments } from '../config';
import type { RslibConfig } from '../types';
+import { isDebug } from '../utils/logger';
import type { CommonOptions } from './commands';
import { onBeforeRestart } from './restart';
@@ -53,6 +54,7 @@ async function initMFRsbuild(
plugins: config.plugins,
dev: config.dev,
server: config.server,
+ logLevel: isDebug() ? 'info' : config.logLevel,
environments: selectedEnvironments,
},
});
diff --git a/packages/core/src/cli/prepare.ts b/packages/core/src/cli/prepare.ts
index 064237fa7..644556eaf 100644
--- a/packages/core/src/cli/prepare.ts
+++ b/packages/core/src/cli/prepare.ts
@@ -1,4 +1,5 @@
-import { logger } from '../utils/logger';
+import type { LogLevel } from '@rsbuild/core';
+import { isDebug, logger } from '../utils/logger';
function initNodeEnv() {
if (!process.env.NODE_ENV) {
@@ -9,8 +10,22 @@ function initNodeEnv() {
}
}
+// ensure log level is set before any log is printed
+function setupLogLevel() {
+ const logLevelIndex = process.argv.findIndex(
+ (item) => item === '--log-level' || item === '--logLevel',
+ );
+ if (logLevelIndex !== -1) {
+ const level = process.argv[logLevelIndex + 1];
+ if (level && ['warn', 'error', 'silent'].includes(level) && !isDebug()) {
+ logger.level = level as LogLevel;
+ }
+ }
+}
+
export function prepareCli(): void {
initNodeEnv();
+ setupLogLevel();
// Print a blank line to keep the greet log nice.
// Some package managers automatically output a blank line, some do not.
@@ -20,7 +35,7 @@ export function prepareCli(): void {
npm_execpath.includes('npx-cli.js') ||
npm_execpath.includes('.bun')
) {
- console.log();
+ logger.log();
}
logger.greet(` Rslib v${RSLIB_VERSION}\n`);
diff --git a/packages/core/src/config.ts b/packages/core/src/config.ts
index 1140195f1..4f7b0bdfb 100644
--- a/packages/core/src/config.ts
+++ b/packages/core/src/config.ts
@@ -73,7 +73,7 @@ import {
pick,
readPackageJson,
} from './utils/helper';
-import { logger } from './utils/logger';
+import { isDebug, logger } from './utils/logger';
import {
ESX_TO_BROWSERSLIST,
transformSyntaxToBrowserslist,
@@ -1804,8 +1804,13 @@ export async function composeCreateRsbuildConfig(
plugins: sharedPlugins,
dev: _dev,
server: _server,
+ logLevel,
...sharedRsbuildConfig
} = rslibConfig;
+ // debug mode should always verbose logs
+ if (logLevel && !isDebug()) {
+ logger.level = logLevel;
+ }
if (!Array.isArray(libConfigsArray) || libConfigsArray.length === 0) {
throw new Error(
diff --git a/packages/core/src/utils/logger.ts b/packages/core/src/utils/logger.ts
index b6e9b3fbf..5814ffd04 100644
--- a/packages/core/src/utils/logger.ts
+++ b/packages/core/src/utils/logger.ts
@@ -21,7 +21,9 @@ export const isDebug = (): boolean => {
}
const values = process.env.DEBUG.toLocaleLowerCase().split(',');
- return ['rslib', 'rs*', 'rstack', '*'].some((key) => values.includes(key));
+ return ['rslib', 'rsbuild', 'rs*', 'rstack', '*'].some((key) =>
+ values.includes(key),
+ );
};
// setup the logger level
diff --git a/packages/core/tests/__snapshots__/config.test.ts.snap b/packages/core/tests/__snapshots__/config.test.ts.snap
index 777737d91..1689d7f39 100644
--- a/packages/core/tests/__snapshots__/config.test.ts.snap
+++ b/packages/core/tests/__snapshots__/config.test.ts.snap
@@ -287,7 +287,8 @@ exports[`Should compose create Rsbuild config correctly > Merge Rsbuild config i
name: 'rsbuild:nonce',
setup() {}
}
- ]
+ ],
+ logLevel: undefined
}"
`;
diff --git a/packages/plugin-dts/src/dts.ts b/packages/plugin-dts/src/dts.ts
index 028757672..68f2714da 100644
--- a/packages/plugin-dts/src/dts.ts
+++ b/packages/plugin-dts/src/dts.ts
@@ -136,7 +136,10 @@ export async function generateDts(data: DtsGenOptions): Promise {
extension: false,
},
tsgo,
+ loggerLevel,
} = data;
+ logger.level = loggerLevel;
+
if (!isWatch) {
logger.start(`generating declaration files... ${color.dim(`(${name})`)}`);
}
diff --git a/packages/plugin-dts/src/index.ts b/packages/plugin-dts/src/index.ts
index 757d1d9a9..d02d0ab64 100644
--- a/packages/plugin-dts/src/index.ts
+++ b/packages/plugin-dts/src/index.ts
@@ -1,7 +1,12 @@
import { type ChildProcess, fork } from 'node:child_process';
import { dirname, extname, join } from 'node:path';
import { fileURLToPath } from 'node:url';
-import { logger, type RsbuildConfig, type RsbuildPlugin } from '@rsbuild/core';
+import {
+ type LogLevel,
+ logger,
+ type RsbuildConfig,
+ type RsbuildPlugin,
+} from '@rsbuild/core';
import color from 'picocolors';
import type { ParsedCommandLine } from 'typescript';
@@ -66,6 +71,7 @@ export type DtsGenOptions = Omit & {
tsConfigResult: ParsedCommandLine;
userExternals?: NonNullable['externals'];
apiExtractorOptions?: ApiExtractorOptions;
+ loggerLevel: LogLevel;
};
interface TaskResult {
@@ -81,6 +87,9 @@ export const pluginDts = (options: PluginDtsOptions = {}): RsbuildPlugin => ({
name: PLUGIN_DTS_NAME,
setup(api) {
+ const loggerLevel = api.logger.level;
+ logger.level = loggerLevel;
+
let apiExtractorOptions = {};
if (options.bundle && typeof options.bundle === 'object') {
@@ -179,6 +188,7 @@ export const pluginDts = (options: PluginDtsOptions = {}): RsbuildPlugin => ({
name: environment.name,
cwd,
isWatch,
+ loggerLevel: loggerLevel as LogLevel,
};
childProcess.send(dtsGenOptions);
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index ddc971b24..af21a1537 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -699,6 +699,8 @@ importers:
tests/integration/cli/inspect: {}
+ tests/integration/cli/log-level: {}
+
tests/integration/cli/mf/build: {}
tests/integration/cli/mf/dev: {}
diff --git a/tests/integration/cli/log-level/index.test.ts b/tests/integration/cli/log-level/index.test.ts
new file mode 100644
index 000000000..7d37d7c77
--- /dev/null
+++ b/tests/integration/cli/log-level/index.test.ts
@@ -0,0 +1,42 @@
+import { describe } from 'node:test';
+import { stripVTControlCharacters as stripAnsi } from 'node:util';
+import { expect, test } from '@rstest/core';
+import { runCliSync } from 'test-helper';
+
+describe('log level', async () => {
+ test('should run build command with log level: info', async () => {
+ const stdout = stripAnsi(
+ runCliSync('build --log-level info', {
+ cwd: __dirname,
+ }).toString(),
+ );
+ expect(stdout).toContain('Rslib v');
+ expect(stdout).toContain('build started...');
+ expect(stdout).toContain('built in');
+ });
+
+ test('should run build command with log level: warn', async () => {
+ const stdout = stripAnsi(
+ runCliSync('build --log-level warn', {
+ cwd: __dirname,
+ }).toString(),
+ );
+ expect(stdout).not.toContain('Rslib v');
+ expect(stdout).not.toContain('build started...');
+ expect(stdout).not.toContain('built in');
+ });
+
+ test('should always print verbose logs when debug mode is enabled', async () => {
+ const stdout = stripAnsi(
+ runCliSync('build --log-level warn', {
+ cwd: __dirname,
+ env: {
+ ...process.env,
+ DEBUG: 'rsbuild',
+ },
+ }).toString(),
+ );
+ expect(stdout).toContain('creating compiler');
+ expect(stdout).toContain('config inspection completed');
+ });
+});
diff --git a/tests/integration/cli/log-level/package.json b/tests/integration/cli/log-level/package.json
new file mode 100644
index 000000000..ef984de26
--- /dev/null
+++ b/tests/integration/cli/log-level/package.json
@@ -0,0 +1,6 @@
+{
+ "name": "cli-log-level-test",
+ "version": "1.0.0",
+ "private": true,
+ "type": "module"
+}
diff --git a/tests/integration/cli/log-level/rslib.config.ts b/tests/integration/cli/log-level/rslib.config.ts
new file mode 100644
index 000000000..a1b1d8104
--- /dev/null
+++ b/tests/integration/cli/log-level/rslib.config.ts
@@ -0,0 +1,6 @@
+import { defineConfig } from '@rslib/core';
+import { generateBundleEsmConfig } from 'test-helper';
+
+export default defineConfig({
+ lib: [generateBundleEsmConfig()],
+});
diff --git a/tests/integration/cli/log-level/src/index.ts b/tests/integration/cli/log-level/src/index.ts
new file mode 100644
index 000000000..3329a7d97
--- /dev/null
+++ b/tests/integration/cli/log-level/src/index.ts
@@ -0,0 +1 @@
+export const foo = 'foo';
diff --git a/website/docs/en/config/rsbuild/_meta.json b/website/docs/en/config/rsbuild/_meta.json
index 5c06549da..238d78036 100644
--- a/website/docs/en/config/rsbuild/_meta.json
+++ b/website/docs/en/config/rsbuild/_meta.json
@@ -1,4 +1,9 @@
[
+ {
+ "type": "file",
+ "name": "log-level",
+ "label": "logLevel"
+ },
"resolve",
"source",
"output",
diff --git a/website/docs/en/config/rsbuild/index.mdx b/website/docs/en/config/rsbuild/index.mdx
index c31f585e5..6683c4acd 100644
--- a/website/docs/en/config/rsbuild/index.mdx
+++ b/website/docs/en/config/rsbuild/index.mdx
@@ -1,5 +1,4 @@
import { RsbuildDocBadge } from '@components/RsbuildDocBadge';
-import { Overview } from 'rspress/theme';
# Rsbuild configurations
@@ -11,6 +10,7 @@ To learn more about Rslib configurations, check out [Configure Rslib](/guide/bas
## Overview
+- [logLevel](/config/rsbuild/log-level): Specify the log level.
- [resolve](/config/rsbuild/resolve): Options for module resolution.
- [source](/config/rsbuild/source): Options for input source code.
- [output](/config/rsbuild/output): Options for build outputs.
diff --git a/website/docs/en/config/rsbuild/log-level.mdx b/website/docs/en/config/rsbuild/log-level.mdx
new file mode 100644
index 000000000..093dc7725
--- /dev/null
+++ b/website/docs/en/config/rsbuild/log-level.mdx
@@ -0,0 +1,8 @@
+import { RsbuildDocBadge } from '@components/RsbuildDocBadge';
+
+# logLevel
+
+- **Type:** `'info' | 'warn' | 'error' | 'silent'`
+- **Default:** `'info'`
+
+Specify the log level, the default value is `info`.
diff --git a/website/docs/en/guide/basic/cli.mdx b/website/docs/en/guide/basic/cli.mdx
index d620e69c6..315d42d87 100644
--- a/website/docs/en/guide/basic/cli.mdx
+++ b/website/docs/en/guide/basic/cli.mdx
@@ -34,6 +34,7 @@ Rslib CLI provides several common flags that can be used with all commands:
| `--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 |
+| `--log-level ` | Set the log level (`info` \| `warn` \| `error` \| `silent`), see [logLevel](/config/rsbuild/log-level) |
| `-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/zh/config/rsbuild/_meta.json b/website/docs/zh/config/rsbuild/_meta.json
index 5c06549da..238d78036 100644
--- a/website/docs/zh/config/rsbuild/_meta.json
+++ b/website/docs/zh/config/rsbuild/_meta.json
@@ -1,4 +1,9 @@
[
+ {
+ "type": "file",
+ "name": "log-level",
+ "label": "logLevel"
+ },
"resolve",
"source",
"output",
diff --git a/website/docs/zh/config/rsbuild/index.mdx b/website/docs/zh/config/rsbuild/index.mdx
index 6bcb06599..51dab9fe5 100644
--- a/website/docs/zh/config/rsbuild/index.mdx
+++ b/website/docs/zh/config/rsbuild/index.mdx
@@ -10,6 +10,7 @@ Rslib 继承了 Rsbuild 的配置,所以你也可以配置
+
+- **类型:** `'info' | 'warn' | 'error' | 'silent'`
+- **默认值:** `'info'`
+
+指定日志级别,默认值为 `info`。
diff --git a/website/docs/zh/guide/basic/cli.mdx b/website/docs/zh/guide/basic/cli.mdx
index ca51046df..341047a06 100644
--- a/website/docs/zh/guide/basic/cli.mdx
+++ b/website/docs/zh/guide/basic/cli.mdx
@@ -34,6 +34,7 @@ Rslib CLI 提供了一些公共选项,可以用于所有命令:
| `--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 |
+| `--log-level ` | 指定日志级别(`info` \| `warn` \| `error` \| `silent`),详见 [logLevel](/config/rsbuild/log-level) |
| `-r, --root ` | 指定项目根目录,可以是绝对路径或者相对于 cwd 的路径 |
## rslib build