Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
13 changes: 13 additions & 0 deletions packages/rstack/src/fmt/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ interface ParsedFmtCLIArgs {
mode: FmtMode;
patterns: string[];
ignorePaths: string[];
ignoreUnknown: boolean;
noErrorOnUnmatchedPattern: boolean;
maxWorkers?: number;
help: boolean;
Expand All @@ -31,6 +32,7 @@ ${color.cyan('Options')}:
--check Check whether files are formatted
--list-different Print paths of unformatted files
--ignore-path <path> Path to an additional ignore file (repeatable)
--ignore-unknown Ignore unknown files
--no-error-on-unmatched-pattern Do not error when no files match
--parallel-workers <count> Number of parallel workers
--stdin-filepath <path> Format stdin as if it were saved at <path>
Expand All @@ -57,6 +59,7 @@ const parseFmtCLIArgs = (args: string[]): ParsedFmtCLIArgs => {
check: { type: 'boolean' },
'list-different': { type: 'boolean' },
'ignore-path': { type: 'string', multiple: true },
'ignore-unknown': { type: 'boolean' },
'no-error-on-unmatched-pattern': { type: 'boolean' },
'parallel-workers': { type: 'string' },
'stdin-filepath': { type: 'string' },
Expand All @@ -76,6 +79,7 @@ const parseFmtCLIArgs = (args: string[]): ParsedFmtCLIArgs => {

const mode = check ? 'check' : listDifferent ? 'list-different' : 'write';
const ignorePaths = values.ignorePath ?? [];
const ignoreUnknown = values.ignoreUnknown ?? false;
const noErrorOnUnmatchedPattern = values.noErrorOnUnmatchedPattern ?? false;
const parallelWorkers = values.parallelWorkers;
const maxWorkers = parseMaxWorkers(parallelWorkers);
Expand All @@ -98,6 +102,7 @@ const parseFmtCLIArgs = (args: string[]): ParsedFmtCLIArgs => {
mode,
patterns: positionals,
ignorePaths,
ignoreUnknown,
noErrorOnUnmatchedPattern,
maxWorkers,
help,
Expand Down Expand Up @@ -228,6 +233,7 @@ const runFmtCLI = async (args: string[]): Promise<void> => {
const {
help,
ignorePaths,
ignoreUnknown,
maxWorkers,
mode,
noErrorOnUnmatchedPattern,
Expand All @@ -248,6 +254,7 @@ const runFmtCLI = async (args: string[]): Promise<void> => {
filepath: stdinFilepath,
cwd,
ignorePaths,
ignoreUnknown,
loadConfig: () => loadFmtConfig(cwd),
});
return;
Expand Down Expand Up @@ -282,6 +289,12 @@ const runFmtCLI = async (args: string[]): Promise<void> => {
});

if (result.processedFileCount === 0) {
if (ignoreUnknown) {
if (mode === 'check') {
logger.success('No supported files to check.');
}
return;
}
reportNoSupportedFiles(patterns);
return;
}
Expand Down
6 changes: 6 additions & 0 deletions packages/rstack/src/fmt/stdin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ interface RunFmtStdinOptions {
cwd: string;
/** Ignore files resolved from `cwd`. */
ignorePaths?: string[];
/** Skip input when no parser can be inferred from `filepath`. */
ignoreUnknown?: boolean;
/** Loads the project config; its failures surface only after stdin is drained. */
loadConfig: () => Promise<ResolvedFmtConfig>;
}
Expand Down Expand Up @@ -51,6 +53,7 @@ const runFmtStdin = async ({
filepath,
cwd,
ignorePaths,
ignoreUnknown,
loadConfig,
}: RunFmtStdinOptions): Promise<void> => {
const configPromise = loadConfig();
Expand Down Expand Up @@ -90,6 +93,9 @@ const runFmtStdin = async ({
const result = await formatFmtSource(file, () => source);

if (result.status === 'unsupported') {
if (ignoreUnknown) {
return;
}
throw new Error(`No parser could be inferred for "${filepath}".`);
}

Expand Down
32 changes: 32 additions & 0 deletions packages/rstack/tests/cli/fmt/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -527,6 +527,14 @@ test('returns exit code 2 when no parser can be inferred for stdin', () => {
expect(result.stderr).toContain('No parser could be inferred for "data.unknown".');
});

test('ignores stdin when no parser can be inferred with --ignore-unknown', () => {
const result = runFmtStdin(['--stdin-filepath', 'data.unknown', '--ignore-unknown'], 'value');

expect(result.status).toBe(0);
expect(result.stdout).toBe('');
expect(result.stderr).toBe('');
});

test('returns exit code 2 for stdin parse errors', () => {
const result = runFmtStdin(['--stdin-filepath', 'index.ts'], 'const value = ;');

Expand Down Expand Up @@ -628,6 +636,30 @@ test('returns exit code 2 when all matched files are unsupported', () => {
}
});

test('ignores unsupported files with --ignore-unknown', () => {
writeProjectFile('notes.unknown', 'plain text');

for (const modeArgs of [[], ['--check'], ['--list-different']]) {
const result = runFmt([...modeArgs, '--ignoreUnknown', 'notes.unknown']);

expect(result.status).toBe(0);
expect(result.stdout).toBe(
modeArgs.includes('--check')
? 'start Checking formatting...\nsuccess No supported files to check.\n'
: '',
);
expect(result.stderr).toBe('');
}
});

test('does not treat unmatched patterns as unknown files', () => {
const result = runFmt(['--ignore-unknown', 'missing/**/*.unknown']);

expect(result.status).toBe(2);
expect(result.stdout).toBe('');
expect(result.stderr).toContain('No supported files matched "missing/**/*.unknown"');
});

test('does not treat unsupported files as unmatched patterns', () => {
writeProjectFile('notes.unknown', 'plain text');

Expand Down
19 changes: 19 additions & 0 deletions packages/rstack/tests/cli/staged/fmt.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,25 @@ test('still rejects staged files unsupported by rs fmt', () => {
expect(`${result.stdout}\n${result.stderr}`).toContain('No supported files matched');
});

test('allows staged files unsupported by rs fmt with --ignore-unknown', () => {
writeProjectFile(
'rstack.config.ts',
`import { define } from 'rstack';

define.staged({
'*': 'rs fmt --ignore-unknown',
});
`,
);
writeProjectFile('notes.unknown', 'plain text');
git(['add', '--', 'notes.unknown']);

const result = runStaged();

expect(result.status).toBe(0);
expect(`${result.stdout}\n${result.stderr}`).not.toContain('No supported files matched');
});

test('propagates rs fmt failures', () => {
writeProjectFile('invalid.ts', 'const value = ;');
git(['add', '--', 'invalid.ts']);
Expand Down
1 change: 1 addition & 0 deletions packages/rstack/tests/fmt/__snapshots__/cli.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ Options:
--check Check whether files are formatted
--list-different Print paths of unformatted files
--ignore-path <path> Path to an additional ignore file (repeatable)
--ignore-unknown Ignore unknown files
--no-error-on-unmatched-pattern Do not error when no files match
--parallel-workers <count> Number of parallel workers
--stdin-filepath <path> Format stdin as if it were saved at <path>
Expand Down
11 changes: 11 additions & 0 deletions packages/rstack/tests/fmt/cli.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ test('uses write mode by default', () => {
mode: 'write',
patterns: [],
ignorePaths: [],
ignoreUnknown: false,
noErrorOnUnmatchedPattern: false,
maxWorkers: undefined,
help: false,
Expand All @@ -37,6 +38,7 @@ test.each([
mode,
patterns: [],
ignorePaths: [],
ignoreUnknown: false,
noErrorOnUnmatchedPattern: false,
maxWorkers: undefined,
help: false,
Expand All @@ -48,6 +50,7 @@ test('configures parallel worker count', () => {
mode: 'write',
patterns: [],
ignorePaths: [],
ignoreUnknown: false,
noErrorOnUnmatchedPattern: false,
maxWorkers: 3,
help: false,
Expand All @@ -70,6 +73,7 @@ test('preserves file paths and globs', () => {
mode: 'check',
patterns,
ignorePaths: [],
ignoreUnknown: false,
noErrorOnUnmatchedPattern: false,
maxWorkers: undefined,
help: false,
Expand All @@ -81,6 +85,7 @@ test('treats arguments after the terminator as paths', () => {
mode: 'check',
patterns: ['--write', '--help'],
ignorePaths: [],
ignoreUnknown: false,
noErrorOnUnmatchedPattern: false,
maxWorkers: undefined,
help: false,
Expand All @@ -102,11 +107,16 @@ test('parses --no-error-on-unmatched-pattern', () => {
expect(parseFmtCLIArgs(['--no-error-on-unmatched-pattern']).noErrorOnUnmatchedPattern).toBe(true);
});

test.each(['--ignore-unknown', '--ignoreUnknown'])('parses %s', (option) => {
expect(parseFmtCLIArgs([option]).ignoreUnknown).toBe(true);
});

test('parses --stdin-filepath', () => {
expect(parseFmtCLIArgs(['--stdin-filepath', 'src/index.ts'])).toEqual({
mode: 'write',
patterns: [],
ignorePaths: [],
ignoreUnknown: false,
noErrorOnUnmatchedPattern: false,
maxWorkers: undefined,
help: false,
Expand All @@ -119,6 +129,7 @@ test('accepts a worker count with --stdin-filepath', () => {
mode: 'write',
patterns: [],
ignorePaths: [],
ignoreUnknown: false,
noErrorOnUnmatchedPattern: false,
maxWorkers: 2,
help: false,
Expand Down
24 changes: 18 additions & 6 deletions website/docs/en/guide/cli/fmt.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,11 @@ rs fmt . --check

The command uses the following exit codes:

| Code | Meaning |
| ---- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `0` | All matched files are formatted; or no supported files matched, but [`--no-error-on-unmatched-pattern`](#--no-error-on-unmatched-pattern) was specified. |
| `1` | One or more matched files have formatting issues. |
| `2` | `rs fmt` could not run or encountered a formatting error. |
| Code | Meaning |
| ---- | -------------------------------------------------- |
| `0` | The command completed successfully. |
| `1` | One or more files have formatting issues. |
| `2` | The command could not run or encountered an error. |

### `-h, --help`

Expand Down Expand Up @@ -87,6 +87,18 @@ rs fmt --ignore-path .prettierignore --ignore-path config/format.ignore

Each file acts as a separate ignore source. See [Ignore order](../formatting#ignore-order) for how these sources combine with `.gitignore`, default ignore rules, and `ignorePatterns`.

### `--ignore-unknown`

Ignore matched files when no parser can be inferred. This allows the command to exit successfully even when every matched file has an unknown type:

```bash
rs fmt --ignore-unknown '**/*'
```

This option does not suppress errors for unmatched paths or globs. Combine it with [`--no-error-on-unmatched-pattern`](#--no-error-on-unmatched-pattern) when an integration needs to tolerate both cases.

When used with `--stdin-filepath`, unsupported input is skipped without writing output.

### `--list-different`

Print the paths of unformatted files without the summary produced by `--check`. This is useful when another command needs to consume the output:
Expand All @@ -99,7 +111,7 @@ The option uses the same exit codes as `--check` and cannot be combined with `--

### `--no-error-on-unmatched-pattern`

Exit successfully without diagnostics when no supported files match the provided paths or globs, including when all matching files are ignored:
Exit successfully without diagnostics when no files match the provided paths or globs, including when all matching files are ignored:

```bash
rs fmt --no-error-on-unmatched-pattern 'src/**/*.ts'
Expand Down
24 changes: 18 additions & 6 deletions website/docs/zh/guide/cli/fmt.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,11 @@ rs fmt . --check

该命令使用以下退出状态码:

| 状态码 | 含义 |
| ------ | -------------------------------------------------------------------------------------------------------------------------------- |
| `0` | 所有匹配的文件均已格式化;或未匹配到支持的文件,但指定了 [`--no-error-on-unmatched-pattern`](#--no-error-on-unmatched-pattern)。 |
| `1` | 一个或多个匹配的文件存在格式问题。 |
| `2` | `rs fmt` 无法运行或在格式化过程中遇到错误。 |
| 状态码 | 含义 |
| ------ | ---------------------------------- |
| `0` | 命令执行成功。 |
| `1` | 一个或多个文件存在格式问题。 |
| `2` | 命令无法运行或执行过程中遇到错误。 |

### `-h, --help`

Expand Down Expand Up @@ -87,6 +87,18 @@ rs fmt --ignore-path .prettierignore --ignore-path config/format.ignore

每个文件都是独立的忽略来源。关于这些来源与 `.gitignore`、默认忽略规则和 `ignorePatterns` 的组合方式,请参考[忽略顺序](../formatting#ignore-order)。

### `--ignore-unknown`

忽略无法推断 parser 的匹配文件。即使所有匹配文件的类型均未知,该选项也可以让命令成功退出:

```bash
rs fmt --ignore-unknown '**/*'
```

此选项不会忽略未匹配路径或 glob 的错误。如果集成需要同时容忍这两种情况,可以将它与 [`--no-error-on-unmatched-pattern`](#--no-error-on-unmatched-pattern) 一起使用。

与 `--stdin-filepath` 一起使用时,不支持的输入会被跳过,且不会输出内容。

### `--list-different`

输出未格式化文件的路径,但不提供 `--check` 的汇总信息。需要将结果交给其他命令处理时,可以使用此选项:
Expand All @@ -99,7 +111,7 @@ rs fmt . --list-different

### `--no-error-on-unmatched-pattern`

如果传入的路径或 glob 没有匹配任何支持的文件(包括所有匹配文件均被忽略的情况),则不输出诊断信息并成功退出:
如果传入的路径或 glob 没有匹配任何文件(包括所有匹配文件均被忽略的情况),则不输出诊断信息并成功退出:

```bash
rs fmt --no-error-on-unmatched-pattern 'src/**/*.ts'
Expand Down