Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
2 changes: 1 addition & 1 deletion packages/rstack/src/cli/commandHelp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ const HELP_DEFINITIONS = {
],
},
check: {
usage: 'rs check [options]',
usage: 'rs check [options] [files...]',
description: 'Run static checks, including lint and format',
sections: [
{
Expand Down
27 changes: 23 additions & 4 deletions packages/rstack/src/cli/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,16 @@ async function runRspressCLI(args: string[]): Promise<void> {

const RSLINT_CONFIG_PATH = join(import.meta.dirname, 'rslintConfig.js');

const SHELL_SAFE_ARGUMENT_REGEXP = /^[\w@%+=:,./-]+$/u;

const quoteShellArgument = (argument: string): string =>
SHELL_SAFE_ARGUMENT_REGEXP.test(argument)
? argument
: `'${argument.replaceAll("'", "'\"'\"'")}'`;
Comment thread
chenjiahan marked this conversation as resolved.
Outdated

const formatCommand = (args: readonly string[]): string =>
args.map(quoteShellArgument).join(' ');

async function runRslintCLI(args: string[]): Promise<void> {
if (hasHelpFlag(args)) {
return printCommandHelp('lint');
Expand All @@ -157,21 +167,27 @@ async function runRslintCLI(args: string[]): Promise<void> {
}

async function runCheckCLI(args: string[]): Promise<void> {
const { values } = parseArgs({
const { values, positionals } = parseArgs({
args,
options: {
'type-check': { type: 'boolean' },
help: { type: 'boolean', short: 'h' },
},
allowPositionals: false,
allowPositionals: true,
strict: true,
});

if (values.help) {
return printCommandHelp('check');
}

await runRslintCLI(values.typeCheck ? ['--type-check'] : []);
// Keep file arguments after `--` when forwarding them so names beginning
// with a hyphen are not reinterpreted as child-command options.
const fileArgs = positionals.length > 0 ? ['--', ...positionals] : [];
await runRslintCLI([
...(values.typeCheck ? ['--type-check'] : []),
...fileArgs,
]);
if (process.exitCode) {
return;
}
Expand All @@ -185,7 +201,10 @@ async function runCheckCLI(args: string[]): Promise<void> {
/* rspackChunkName: 'fmt' */
'../fmt/cli.ts'
);
await runFmtCLI(['--check'], { fixCommand: 'rs fmt', loadedConfig });
await runFmtCLI(['--check', ...fileArgs], {
fixCommand: formatCommand(['rs', 'fmt', ...fileArgs]),
loadedConfig,
});
}

export async function setupCommands(): Promise<void> {
Expand Down
2 changes: 1 addition & 1 deletion packages/rstack/tests/cli/__snapshots__/check.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ exports[`displays check help without loading config 1`] = `
"Rstack v<version>

Usage:
$ rs check [options]
$ rs check [options] [files...]

Run static checks, including lint and format

Expand Down
39 changes: 39 additions & 0 deletions packages/rstack/tests/cli/check.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,45 @@ test('runs lint followed by a formatting check', () => {
expect(formatted.stderr).toBe('');
});

test('passes file arguments to lint and the formatting check', () => {
writeLintConfig();
writeProjectFile('src/selected-a.ts', 'const selectedA = true;\n');
writeProjectFile('src/selected-b.ts', 'const selectedB = true;\n');
writeProjectFile('src/unselected-lint-error.ts', 'debugger;\n');
writeProjectFile('src/unselected-format-error.ts', 'const value=true');

const result = runCheck(['src/selected-a.ts', 'src/selected-b.ts']);

expect(result.status).toBe(0);
expect(result.stdout).toContain('Format check passed in');
expect(result.stdout).toContain('(2 files)');
expect(result.stderr).toBe('');
});

test('preserves file arguments in the formatting fix command', () => {
writeLintConfig();
writeProjectFile("src/selected file's.ts", 'const selected=true');

const result = runCheck(["src/selected file's.ts"]);

expect(result.status).toBe(1);
expect(result.stderr).toContain(
`Run rs fmt -- 'src/selected file'"'"'s.ts' to fix.`,
);
});

test('supports file arguments after the option terminator', () => {
writeLintConfig();
writeProjectFile('--selected.ts', 'const selected=true');

const result = runCheck(['--', '--selected.ts']);

expect(result.status).toBe(1);
expect(result.stderr).toContain(
'Formatting issues found in 1 file. Run rs fmt -- --selected.ts to fix.',
);
});

test('enables type checking only with --type-check', () => {
writeLintConfig();
writeProjectFile(
Expand Down
14 changes: 13 additions & 1 deletion website/docs/en/guide/cli/check.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,19 @@ The `rs check` command combines linting, formatting, and optional TypeScript typ
## Usage

```bash
rs check [options]
rs check [options] [files...]
```

Pass files or directories to limit both linting and formatting to those paths:

```bash
rs check src/index.ts packages/utils
```

The same file arguments are passed to both commands, so the example above is equivalent to:

```bash
rs lint src/index.ts packages/utils && rs fmt --check src/index.ts packages/utils
```

## Checks
Expand Down
14 changes: 13 additions & 1 deletion website/docs/zh/guide/cli/check.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,19 @@ description: '同时运行 lint 和格式检查,并可选启用 TypeScript 类
## 用法 \{#usage}

```bash
rs check [options]
rs check [options] [files...]
```

传入文件或目录,可以将 lint 和格式检查都限制在这些路径中:

```bash
rs check src/index.ts packages/utils
```

相同的文件参数会同时传给两个命令,因此上述示例等同于:

```bash
rs lint src/index.ts packages/utils && rs fmt --check src/index.ts packages/utils
```

## 检查内容 \{#checks}
Expand Down