Skip to content

Commit 7ae6d14

Browse files
authored
feat: add check command (#304)
1 parent bfe9ffd commit 7ae6d14

3 files changed

Lines changed: 140 additions & 1 deletion

File tree

packages/rstack/src/cli/commands.ts

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { join } from 'node:path';
22
import { color } from 'rslog';
33
import { getConfigState } from '../config.ts';
4-
import { insertConfigArg, parseCliArgs } from './args.ts';
4+
import { insertConfigArg, parseArgs, parseCliArgs } from './args.ts';
55

66
declare global {
77
const RSTACK_VERSION: string;
@@ -20,6 +20,7 @@ ${color.cyan('Commands')}:
2020
doc Serve or build docs
2121
fmt, format Format code
2222
lint Lint code
23+
check Run static checks, including linting and formatting
2324
test Run tests
2425
staged Run tasks on staged Git files
2526
setup Install Git hooks
@@ -32,6 +33,17 @@ ${color.cyan('Options')}:
3233
-h, --help Display this help message
3334
-v, --version Display version number`;
3435

36+
const checkHelpMessage = `Rstack v${RSTACK_VERSION}
37+
38+
${color.cyan('Usage')}:
39+
${color.yellow(' $ rs check [options]')}
40+
41+
Run static checks, including linting and formatting.
42+
43+
${color.cyan('Options')}:
44+
--type-check Enable TypeScript type checking
45+
-h, --help Display this help message`;
46+
3547
async function runRsbuildCLI(args: string[]): Promise<void> {
3648
const argv = [
3749
process.execPath,
@@ -106,6 +118,34 @@ async function runRslintCLI(args: string[]): Promise<void> {
106118
await runCLI({ argv });
107119
}
108120

121+
async function runCheckCLI(args: string[]): Promise<void> {
122+
const { values } = parseArgs({
123+
args,
124+
options: {
125+
'type-check': { type: 'boolean' },
126+
help: { type: 'boolean', short: 'h' },
127+
},
128+
allowPositionals: false,
129+
strict: true,
130+
});
131+
132+
if (values.help) {
133+
console.log(checkHelpMessage);
134+
return;
135+
}
136+
137+
await runRslintCLI(values.typeCheck ? ['--type-check'] : []);
138+
if (process.exitCode) {
139+
return;
140+
}
141+
142+
const { runFmtCLI } = await import(
143+
/* rspackChunkName: 'fmt' */
144+
'../fmt/cli.ts'
145+
);
146+
await runFmtCLI(['--check']);
147+
}
148+
109149
export async function setupCommands(): Promise<void> {
110150
const { args, configPath } = parseCliArgs(process.argv.slice(2));
111151
const command = args[0];
@@ -142,6 +182,11 @@ export async function setupCommands(): Promise<void> {
142182
return;
143183
}
144184

185+
if (command === 'check') {
186+
await runCheckCLI(args.slice(1));
187+
return;
188+
}
189+
145190
if (command === 'fmt' || command === 'format') {
146191
const { runFmtCLI } = await import(
147192
/* rspackChunkName: 'fmt' */
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Rstest Snapshot v1
2+
3+
exports[`displays check help without loading config 1`] = `
4+
"Rstack v<version>
5+
6+
Usage:
7+
$ rs check [options]
8+
9+
Run static checks, including linting and formatting.
10+
11+
Options:
12+
--type-check Enable TypeScript type checking
13+
-h, --help Display this help message
14+
"
15+
`;
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import { expect, test } from 'rstack/test';
2+
import { setupFmtTest } from './fmt/helpers.ts';
3+
4+
const { runCLI, writeProjectFile } = setupFmtTest();
5+
const runCheck = (args: string[] = []) => runCLI(['check', ...args]);
6+
7+
const writeLintConfig = (): void => {
8+
writeProjectFile(
9+
'rstack.config.ts',
10+
`import { define } from "rstack";
11+
12+
define.lint([
13+
{
14+
files: ["**/*.{js,ts}"],
15+
rules: { "no-debugger": "error" },
16+
},
17+
]);
18+
`,
19+
);
20+
};
21+
22+
test('displays check help without loading config', () => {
23+
writeProjectFile('rstack.config.ts', 'throw new Error("must not load");\n');
24+
25+
const result = runCheck(['--help']);
26+
27+
expect(result.stdout.replace(/^Rstack v.+/u, 'Rstack v<version>')).toMatchSnapshot();
28+
});
29+
30+
test('runs lint followed by a formatting check', () => {
31+
writeLintConfig();
32+
writeProjectFile('src/index.ts', 'const value=true');
33+
34+
const unformatted = runCheck();
35+
36+
expect(unformatted.status).toBe(1);
37+
expect(unformatted.stdout).toContain('Checking formatting...');
38+
expect(unformatted.stderr).toContain('Formatting issues found in 1 file.');
39+
40+
writeProjectFile('src/index.ts', 'const value = true;\n');
41+
const formatted = runCheck();
42+
43+
expect(formatted.status).toBe(0);
44+
expect(formatted.stdout).toContain('No issues found.');
45+
expect(formatted.stderr).toBe('');
46+
});
47+
48+
test('enables type checking only with --type-check', () => {
49+
writeLintConfig();
50+
writeProjectFile(
51+
'tsconfig.json',
52+
`{
53+
"compilerOptions": {
54+
"strict": true
55+
},
56+
"include": ["src"]
57+
}
58+
`,
59+
);
60+
writeProjectFile('src/index.ts', 'const value: string = 1;\n');
61+
62+
const withoutTypeCheck = runCheck();
63+
const withTypeCheck = runCheck(['--type-check']);
64+
65+
expect(withoutTypeCheck.status).toBe(0);
66+
expect(withTypeCheck.status).toBe(1);
67+
expect(`${withTypeCheck.stdout}\n${withTypeCheck.stderr}`).toContain('TS2322');
68+
});
69+
70+
test('does not run the formatting check when lint fails', () => {
71+
writeLintConfig();
72+
writeProjectFile('src/index.js', 'debugger;\n');
73+
74+
const result = runCheck();
75+
76+
expect(result.status).toBe(1);
77+
expect(`${result.stdout}\n${result.stderr}`).toContain("Unexpected 'debugger' statement");
78+
expect(result.stdout).not.toContain('Checking formatting...');
79+
});

0 commit comments

Comments
 (0)