From f9c5be962e52d7b3a6423e8a2c430b0e0a3377e6 Mon Sep 17 00:00:00 2001 From: neverland Date: Wed, 2 Sep 2026 20:51:14 +0800 Subject: [PATCH] feat(cli): support --fix in rs check --- packages/rstack/src/cli/commandHelp.ts | 1 + packages/rstack/src/cli/commands.ts | 6 ++-- packages/rstack/src/fmt/cli.ts | 14 ++++---- .../cli/__snapshots__/check.test.ts.snap | 1 + packages/rstack/tests/cli/check.test.ts | 32 +++++++++++++++++-- website/docs/en/guide/cli/check.mdx | 16 ++++++++++ website/docs/zh/guide/cli/check.mdx | 16 ++++++++++ 7 files changed, 75 insertions(+), 11 deletions(-) diff --git a/packages/rstack/src/cli/commandHelp.ts b/packages/rstack/src/cli/commandHelp.ts index 6906eab..31af188 100644 --- a/packages/rstack/src/cli/commandHelp.ts +++ b/packages/rstack/src/cli/commandHelp.ts @@ -154,6 +154,7 @@ const HELP_DEFINITIONS = { { title: 'Options', items: [ + ['--fix', 'Automatically fix lint and formatting issues'], ['--type-check', 'Enable TypeScript type checking'], ...CONFIG_HELP_OPTIONS, ], diff --git a/packages/rstack/src/cli/commands.ts b/packages/rstack/src/cli/commands.ts index cd17024..dfd795f 100644 --- a/packages/rstack/src/cli/commands.ts +++ b/packages/rstack/src/cli/commands.ts @@ -160,6 +160,7 @@ async function runCheckCLI(args: string[]): Promise { const { values, positionals } = parseArgs({ args, options: { + fix: { type: 'boolean' }, 'type-check': { type: 'boolean' }, help: { type: 'boolean', short: 'h' }, }, @@ -175,6 +176,7 @@ async function runCheckCLI(args: string[]): Promise { // with a hyphen are not reinterpreted as child-command options. const fileArgs = positionals.length > 0 ? ['--', ...positionals] : []; await runRslintCLI([ + ...(values.fix ? ['--fix'] : []), ...(values.typeCheck ? ['--type-check'] : []), ...fileArgs, ]); @@ -191,8 +193,8 @@ async function runCheckCLI(args: string[]): Promise { /* rspackChunkName: 'fmt' */ '../fmt/cli.ts' ); - await runFmtCLI(['--check', ...fileArgs], { - fixCommand: 'rs fmt', + await runFmtCLI([values.fix ? '--write' : '--check', ...fileArgs], { + fixOption: '--fix', loadedConfig, }); } diff --git a/packages/rstack/src/fmt/cli.ts b/packages/rstack/src/fmt/cli.ts index 62885ca..f4c9640 100644 --- a/packages/rstack/src/fmt/cli.ts +++ b/packages/rstack/src/fmt/cli.ts @@ -31,8 +31,8 @@ interface ParsedFmtCLIArgs { } type RunFmtCLIOptions = { - /** Command shown to fix formatting issues found in check mode. */ - fixCommand?: string; + /** Option shown to fix issues by rerunning the current command. */ + fixOption?: string; /** Rstack config already loaded by the lint phase of `rs check`. */ loadedConfig?: LoadedRstackConfig; }; @@ -180,7 +180,7 @@ const logFmtResult = ( cwd: string, processedFileCount: number, durationMilliseconds: number, - fixCommand?: string, + fixOption?: string, ): void => { let writtenCount = 0; let differentCount = 0; @@ -234,8 +234,8 @@ const logFmtResult = ( if (differentCount > 0) { const differentFiles = formatFileCount(differentCount, true); const processedFiles = formatFileCount(processedFileCount); - const fixHint = fixCommand - ? `Run ${color.cyan(fixCommand)} to fix.` + const fixHint = fixOption + ? `Rerun this command with ${color.cyan(fixOption)} to fix.` : `Rerun this command without ${color.cyan('--check')} to fix.`; logger.error(`Formatting issues found in ${differentFiles}. ${fixHint}`); logger.info(`Checked ${processedFiles} in ${time}.`); @@ -261,7 +261,7 @@ const loadFmtConfig = async ( const runFmtCLI = async ( args: string[], - { fixCommand, loadedConfig }: RunFmtCLIOptions = {}, + { fixOption, loadedConfig }: RunFmtCLIOptions = {}, ): Promise => { const cwd = process.cwd(); const startTime = performance.now(); @@ -404,7 +404,7 @@ const runFmtCLI = async ( cwd, result.processedFileCount, durationMilliseconds, - fixCommand, + fixOption, ); process.exitCode = result.exitCode; } catch (error) { diff --git a/packages/rstack/tests/cli/__snapshots__/check.test.ts.snap b/packages/rstack/tests/cli/__snapshots__/check.test.ts.snap index 6b7533b..022f0fb 100644 --- a/packages/rstack/tests/cli/__snapshots__/check.test.ts.snap +++ b/packages/rstack/tests/cli/__snapshots__/check.test.ts.snap @@ -9,6 +9,7 @@ Usage: Run static checks, including lint and format Options: + --fix Automatically fix lint and formatting issues --type-check Enable TypeScript type checking -c, --config Specify Rstack config file path -h, --help Display this help message diff --git a/packages/rstack/tests/cli/check.test.ts b/packages/rstack/tests/cli/check.test.ts index 21c791c..a23019f 100644 --- a/packages/rstack/tests/cli/check.test.ts +++ b/packages/rstack/tests/cli/check.test.ts @@ -2,7 +2,7 @@ import { expect, test } from 'rstack/test'; import { normalizeHelpOutput } from '#test-helpers'; import { setupFmtTest } from './fmt/helpers.ts'; -const { runCLI, writeProjectFile } = setupFmtTest(); +const { readProjectFile, runCLI, writeProjectFile } = setupFmtTest(); const runCheck = (args: string[] = []) => runCLI(['check', ...args]); const writeLintConfig = (): void => { @@ -37,8 +37,9 @@ test('runs lint followed by a formatting check', () => { expect(unformatted.status).toBe(1); expect(unformatted.stdout).toContain('Checking formatting...'); expect(unformatted.stderr).toContain( - 'Formatting issues found in 1 file. Run rs fmt to fix.', + 'Formatting issues found in 1 file. Rerun this command with --fix to fix.', ); + expect(readProjectFile('src/index.ts')).toBe('const value=true'); writeProjectFile('src/index.ts', 'const value = true;\n'); const formatted = runCheck(); @@ -63,6 +64,33 @@ test('passes file arguments to lint and the formatting check', () => { expect(result.stderr).toBe(''); }); +test('fixes lint and formatting issues in the selected files', () => { + writeProjectFile( + 'rstack.config.ts', + `import { define } from "rstack"; + +define.lint([ + { + files: ["**/*.{js,ts}"], + rules: { curly: "error" }, + }, +]); +`, + ); + writeProjectFile('src/selected.ts', 'let value=true;if(value) value++'); + writeProjectFile('src/unselected.ts', 'const unselected=true'); + + const result = runCheck(['--fix', 'src/selected.ts']); + + expect(result.status).toBe(0); + expect(result.stdout).toContain('Formatting completed in'); + expect(result.stderr).toBe(''); + expect(readProjectFile('src/selected.ts')).toBe( + 'let value = true;\nif (value) {\n value++;\n}\n', + ); + expect(readProjectFile('src/unselected.ts')).toBe('const unselected=true'); +}); + test('supports file arguments after the option terminator', () => { writeLintConfig(); writeProjectFile('--selected.ts', 'const selected = true;\n'); diff --git a/website/docs/en/guide/cli/check.mdx b/website/docs/en/guide/cli/check.mdx index 7182461..871fc3c 100644 --- a/website/docs/en/guide/cli/check.mdx +++ b/website/docs/en/guide/cli/check.mdx @@ -36,6 +36,22 @@ Checks run sequentially. If linting fails, `rs check` stops without running the ## Options +### `--fix` + +Automatically fix linting and formatting issues: + +```bash +rs check --fix +``` + +This is equivalent to: + +```bash +rs lint --fix && rs fmt --write +``` + +If linting still fails after applying available fixes, `rs check` stops without running the formatter. + ### `--type-check` Enable TypeScript type checking as part of linting: diff --git a/website/docs/zh/guide/cli/check.mdx b/website/docs/zh/guide/cli/check.mdx index 9d2ec5f..0d766a4 100644 --- a/website/docs/zh/guide/cli/check.mdx +++ b/website/docs/zh/guide/cli/check.mdx @@ -36,6 +36,22 @@ rs lint && rs fmt --check ## 选项 \{#options} +### `--fix` + +自动修复 lint 和格式问题: + +```bash +rs check --fix +``` + +该命令等同于: + +```bash +rs lint --fix && rs fmt --write +``` + +如果应用可用的修复后 lint 仍然失败,`rs check` 会停止运行,不再执行格式化。 + ### `--type-check` 在 lint 过程中启用 TypeScript 类型检查: