Skip to content

Commit 05a31d5

Browse files
authored
feat(fmt): add --ignore-unknown (#190)
1 parent 3525ebc commit 05a31d5

8 files changed

Lines changed: 118 additions & 12 deletions

File tree

packages/rstack/src/fmt/cli.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ interface ParsedFmtCLIArgs {
1212
mode: FmtMode;
1313
patterns: string[];
1414
ignorePaths: string[];
15+
ignoreUnknown: boolean;
1516
noErrorOnUnmatchedPattern: boolean;
1617
maxWorkers?: number;
1718
help: boolean;
@@ -31,6 +32,7 @@ ${color.cyan('Options')}:
3132
--check Check whether files are formatted
3233
--list-different Print paths of unformatted files
3334
--ignore-path <path> Path to an additional ignore file (repeatable)
35+
--ignore-unknown Ignore unknown files
3436
--no-error-on-unmatched-pattern Do not error when no files match
3537
--parallel-workers <count> Number of parallel workers
3638
--stdin-filepath <path> Format stdin as if it were saved at <path>
@@ -57,6 +59,7 @@ const parseFmtCLIArgs = (args: string[]): ParsedFmtCLIArgs => {
5759
check: { type: 'boolean' },
5860
'list-different': { type: 'boolean' },
5961
'ignore-path': { type: 'string', multiple: true },
62+
'ignore-unknown': { type: 'boolean' },
6063
'no-error-on-unmatched-pattern': { type: 'boolean' },
6164
'parallel-workers': { type: 'string' },
6265
'stdin-filepath': { type: 'string' },
@@ -76,6 +79,7 @@ const parseFmtCLIArgs = (args: string[]): ParsedFmtCLIArgs => {
7679

7780
const mode = check ? 'check' : listDifferent ? 'list-different' : 'write';
7881
const ignorePaths = values.ignorePath ?? [];
82+
const ignoreUnknown = values.ignoreUnknown ?? false;
7983
const noErrorOnUnmatchedPattern = values.noErrorOnUnmatchedPattern ?? false;
8084
const parallelWorkers = values.parallelWorkers;
8185
const maxWorkers = parseMaxWorkers(parallelWorkers);
@@ -98,6 +102,7 @@ const parseFmtCLIArgs = (args: string[]): ParsedFmtCLIArgs => {
98102
mode,
99103
patterns: positionals,
100104
ignorePaths,
105+
ignoreUnknown,
101106
noErrorOnUnmatchedPattern,
102107
maxWorkers,
103108
help,
@@ -228,6 +233,7 @@ const runFmtCLI = async (args: string[]): Promise<void> => {
228233
const {
229234
help,
230235
ignorePaths,
236+
ignoreUnknown,
231237
maxWorkers,
232238
mode,
233239
noErrorOnUnmatchedPattern,
@@ -248,6 +254,7 @@ const runFmtCLI = async (args: string[]): Promise<void> => {
248254
filepath: stdinFilepath,
249255
cwd,
250256
ignorePaths,
257+
ignoreUnknown,
251258
loadConfig: () => loadFmtConfig(cwd),
252259
});
253260
return;
@@ -282,6 +289,12 @@ const runFmtCLI = async (args: string[]): Promise<void> => {
282289
});
283290

284291
if (result.processedFileCount === 0) {
292+
if (ignoreUnknown) {
293+
if (mode === 'check') {
294+
logger.success('No supported files to check.');
295+
}
296+
return;
297+
}
285298
reportNoSupportedFiles(patterns);
286299
return;
287300
}

packages/rstack/src/fmt/stdin.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ interface RunFmtStdinOptions {
1111
cwd: string;
1212
/** Ignore files resolved from `cwd`. */
1313
ignorePaths?: string[];
14+
/** Skip input when no parser can be inferred from `filepath`. */
15+
ignoreUnknown?: boolean;
1416
/** Loads the project config; its failures surface only after stdin is drained. */
1517
loadConfig: () => Promise<ResolvedFmtConfig>;
1618
}
@@ -51,6 +53,7 @@ const runFmtStdin = async ({
5153
filepath,
5254
cwd,
5355
ignorePaths,
56+
ignoreUnknown,
5457
loadConfig,
5558
}: RunFmtStdinOptions): Promise<void> => {
5659
const configPromise = loadConfig();
@@ -90,6 +93,9 @@ const runFmtStdin = async ({
9093
const result = await formatFmtSource(file, () => source);
9194

9295
if (result.status === 'unsupported') {
96+
if (ignoreUnknown) {
97+
return;
98+
}
9399
throw new Error(`No parser could be inferred for "${filepath}".`);
94100
}
95101

packages/rstack/tests/cli/fmt/index.test.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -527,6 +527,14 @@ test('returns exit code 2 when no parser can be inferred for stdin', () => {
527527
expect(result.stderr).toContain('No parser could be inferred for "data.unknown".');
528528
});
529529

530+
test('ignores stdin when no parser can be inferred with --ignore-unknown', () => {
531+
const result = runFmtStdin(['--stdin-filepath', 'data.unknown', '--ignore-unknown'], 'value');
532+
533+
expect(result.status).toBe(0);
534+
expect(result.stdout).toBe('');
535+
expect(result.stderr).toBe('');
536+
});
537+
530538
test('returns exit code 2 for stdin parse errors', () => {
531539
const result = runFmtStdin(['--stdin-filepath', 'index.ts'], 'const value = ;');
532540

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

639+
test('ignores unsupported files with --ignore-unknown', () => {
640+
writeProjectFile('notes.unknown', 'plain text');
641+
642+
for (const modeArgs of [[], ['--check'], ['--list-different']]) {
643+
const result = runFmt([...modeArgs, '--ignoreUnknown', 'notes.unknown']);
644+
645+
expect(result.status).toBe(0);
646+
expect(result.stdout).toBe(
647+
modeArgs.includes('--check')
648+
? 'start Checking formatting...\nsuccess No supported files to check.\n'
649+
: '',
650+
);
651+
expect(result.stderr).toBe('');
652+
}
653+
});
654+
655+
test('does not treat unmatched patterns as unknown files', () => {
656+
const result = runFmt(['--ignore-unknown', 'missing/**/*.unknown']);
657+
658+
expect(result.status).toBe(2);
659+
expect(result.stdout).toBe('');
660+
expect(result.stderr).toContain('No supported files matched "missing/**/*.unknown"');
661+
});
662+
631663
test('does not treat unsupported files as unmatched patterns', () => {
632664
writeProjectFile('notes.unknown', 'plain text');
633665

packages/rstack/tests/cli/staged/fmt.test.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,25 @@ test('still rejects staged files unsupported by rs fmt', () => {
104104
expect(`${result.stdout}\n${result.stderr}`).toContain('No supported files matched');
105105
});
106106

107+
test('allows staged files unsupported by rs fmt with --ignore-unknown', () => {
108+
writeProjectFile(
109+
'rstack.config.ts',
110+
`import { define } from 'rstack';
111+
112+
define.staged({
113+
'*': 'rs fmt --ignore-unknown',
114+
});
115+
`,
116+
);
117+
writeProjectFile('notes.unknown', 'plain text');
118+
git(['add', '--', 'notes.unknown']);
119+
120+
const result = runStaged();
121+
122+
expect(result.status).toBe(0);
123+
expect(`${result.stdout}\n${result.stderr}`).not.toContain('No supported files matched');
124+
});
125+
107126
test('propagates rs fmt failures', () => {
108127
writeProjectFile('invalid.ts', 'const value = ;');
109128
git(['add', '--', 'invalid.ts']);

packages/rstack/tests/fmt/__snapshots__/cli.test.ts.snap

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ Options:
1111
--check Check whether files are formatted
1212
--list-different Print paths of unformatted files
1313
--ignore-path <path> Path to an additional ignore file (repeatable)
14+
--ignore-unknown Ignore unknown files
1415
--no-error-on-unmatched-pattern Do not error when no files match
1516
--parallel-workers <count> Number of parallel workers
1617
--stdin-filepath <path> Format stdin as if it were saved at <path>

packages/rstack/tests/fmt/cli.test.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ test('uses write mode by default', () => {
2222
mode: 'write',
2323
patterns: [],
2424
ignorePaths: [],
25+
ignoreUnknown: false,
2526
noErrorOnUnmatchedPattern: false,
2627
maxWorkers: undefined,
2728
help: false,
@@ -37,6 +38,7 @@ test.each([
3738
mode,
3839
patterns: [],
3940
ignorePaths: [],
41+
ignoreUnknown: false,
4042
noErrorOnUnmatchedPattern: false,
4143
maxWorkers: undefined,
4244
help: false,
@@ -48,6 +50,7 @@ test('configures parallel worker count', () => {
4850
mode: 'write',
4951
patterns: [],
5052
ignorePaths: [],
53+
ignoreUnknown: false,
5154
noErrorOnUnmatchedPattern: false,
5255
maxWorkers: 3,
5356
help: false,
@@ -70,6 +73,7 @@ test('preserves file paths and globs', () => {
7073
mode: 'check',
7174
patterns,
7275
ignorePaths: [],
76+
ignoreUnknown: false,
7377
noErrorOnUnmatchedPattern: false,
7478
maxWorkers: undefined,
7579
help: false,
@@ -81,6 +85,7 @@ test('treats arguments after the terminator as paths', () => {
8185
mode: 'check',
8286
patterns: ['--write', '--help'],
8387
ignorePaths: [],
88+
ignoreUnknown: false,
8489
noErrorOnUnmatchedPattern: false,
8590
maxWorkers: undefined,
8691
help: false,
@@ -102,11 +107,16 @@ test('parses --no-error-on-unmatched-pattern', () => {
102107
expect(parseFmtCLIArgs(['--no-error-on-unmatched-pattern']).noErrorOnUnmatchedPattern).toBe(true);
103108
});
104109

110+
test.each(['--ignore-unknown', '--ignoreUnknown'])('parses %s', (option) => {
111+
expect(parseFmtCLIArgs([option]).ignoreUnknown).toBe(true);
112+
});
113+
105114
test('parses --stdin-filepath', () => {
106115
expect(parseFmtCLIArgs(['--stdin-filepath', 'src/index.ts'])).toEqual({
107116
mode: 'write',
108117
patterns: [],
109118
ignorePaths: [],
119+
ignoreUnknown: false,
110120
noErrorOnUnmatchedPattern: false,
111121
maxWorkers: undefined,
112122
help: false,
@@ -119,6 +129,7 @@ test('accepts a worker count with --stdin-filepath', () => {
119129
mode: 'write',
120130
patterns: [],
121131
ignorePaths: [],
132+
ignoreUnknown: false,
122133
noErrorOnUnmatchedPattern: false,
123134
maxWorkers: 2,
124135
help: false,

website/docs/en/guide/cli/fmt.mdx

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,11 @@ rs fmt . --check
4343

4444
The command uses the following exit codes:
4545

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

5252
### `-h, --help`
5353

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

8888
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`.
8989

90+
### `--ignore-unknown`
91+
92+
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:
93+
94+
```bash
95+
rs fmt --ignore-unknown '**/*'
96+
```
97+
98+
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.
99+
100+
When used with `--stdin-filepath`, unsupported input is skipped without writing output.
101+
90102
### `--list-different`
91103

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

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

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

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

website/docs/zh/guide/cli/fmt.mdx

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,11 @@ rs fmt . --check
4343

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

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

5252
### `-h, --help`
5353

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

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

90+
### `--ignore-unknown`
91+
92+
忽略无法推断 parser 的匹配文件。即使所有匹配文件的类型均未知,该选项也可以让命令成功退出:
93+
94+
```bash
95+
rs fmt --ignore-unknown '**/*'
96+
```
97+
98+
此选项不会忽略未匹配路径或 glob 的错误。如果集成需要同时容忍这两种情况,可以将它与 [`--no-error-on-unmatched-pattern`](#--no-error-on-unmatched-pattern) 一起使用。
99+
100+
`--stdin-filepath` 一起使用时,不支持的输入会被跳过,且不会输出内容。
101+
90102
### `--list-different`
91103

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

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

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

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

0 commit comments

Comments
 (0)