Skip to content

Commit 113fe8b

Browse files
committed
refactor: extract CLI argument parsing
1 parent 94d87d4 commit 113fe8b

2 files changed

Lines changed: 71 additions & 69 deletions

File tree

packages/rstack/src/cli/args.ts

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import { parseArgs } from 'node:util';
2+
3+
type ParsedRstackArgs = {
4+
args: string[];
5+
configPath?: string;
6+
};
7+
8+
export function parseCliArgs(args: string[]): ParsedRstackArgs {
9+
const { tokens } = parseArgs({
10+
args,
11+
options: {
12+
config: { type: 'string', short: 'c' },
13+
},
14+
strict: false,
15+
allowPositionals: true,
16+
tokens: true,
17+
});
18+
19+
let removedIndexes: number[] | undefined;
20+
let configPath: string | undefined;
21+
22+
for (const token of tokens) {
23+
if (token.kind === 'option-terminator') {
24+
break;
25+
}
26+
27+
// parseArgs expands short option groups like `-abc`; only consume `-c` when it starts the raw arg.
28+
if (
29+
token.kind !== 'option' ||
30+
token.name !== 'config' ||
31+
(token.rawName === '-c' && !args[token.index].startsWith('-c'))
32+
) {
33+
continue;
34+
}
35+
36+
if (token.value === undefined || token.value.length === 0) {
37+
throw new Error(`Missing value for ${token.rawName}.`);
38+
}
39+
40+
configPath = token.value;
41+
const indexes = (removedIndexes ??= []);
42+
indexes.push(token.index);
43+
44+
if (!token.inlineValue) {
45+
indexes.push(token.index + 1);
46+
}
47+
}
48+
49+
if (!removedIndexes) {
50+
return { args };
51+
}
52+
53+
return {
54+
args: args.filter((_, index) => !removedIndexes.includes(index)),
55+
configPath,
56+
};
57+
}
58+
59+
export function insertConfigArg(args: string[], option: string, configPath: string): string[] {
60+
// Keep the injected config before `--`; arguments after it must remain positional for the child CLI.
61+
const terminatorIndex = args.indexOf('--');
62+
63+
if (terminatorIndex === -1) {
64+
return [...args, option, configPath];
65+
}
66+
67+
return [...args.slice(0, terminatorIndex), option, configPath, ...args.slice(terminatorIndex)];
68+
}

packages/rstack/src/cli/commands.ts

Lines changed: 3 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { join } from 'node:path';
2-
import { parseArgs } from 'node:util';
32
import { getConfigState } from '../config.js';
43
import { runStagedCLI } from '../staged.js';
4+
import { insertConfigArg, parseCliArgs } from './args.js';
55

66
declare global {
77
const RSTACK_VERSION: string;
@@ -29,73 +29,6 @@ Options:
2929
-h, --help Display this help message
3030
-v, --version Display version number`;
3131

32-
type ParsedRstackArgs = {
33-
args: string[];
34-
configPath?: string;
35-
};
36-
37-
function parseCliArgs(args: string[]): ParsedRstackArgs {
38-
const { tokens } = parseArgs({
39-
args,
40-
options: {
41-
config: { type: 'string', short: 'c' },
42-
},
43-
strict: false,
44-
allowPositionals: true,
45-
tokens: true,
46-
});
47-
48-
let removedIndexes: number[] | undefined;
49-
let configPath: string | undefined;
50-
51-
for (const token of tokens) {
52-
if (token.kind === 'option-terminator') {
53-
break;
54-
}
55-
56-
// parseArgs expands short option groups like `-abc`; only consume `-c` when it starts the raw arg.
57-
if (
58-
token.kind !== 'option' ||
59-
token.name !== 'config' ||
60-
(token.rawName === '-c' && !args[token.index].startsWith('-c'))
61-
) {
62-
continue;
63-
}
64-
65-
if (token.value === undefined || token.value.length === 0) {
66-
throw new Error(`Missing value for ${token.rawName}.`);
67-
}
68-
69-
configPath = token.value;
70-
const indexes = (removedIndexes ??= []);
71-
indexes.push(token.index);
72-
73-
if (!token.inlineValue) {
74-
indexes.push(token.index + 1);
75-
}
76-
}
77-
78-
if (!removedIndexes) {
79-
return { args };
80-
}
81-
82-
return {
83-
args: args.filter((_, index) => !removedIndexes.includes(index)),
84-
configPath,
85-
};
86-
}
87-
88-
function insertConfigArg(args: string[], option: string, configPath: string): string[] {
89-
// Keep the injected config before `--`; arguments after it must remain positional for the child CLI.
90-
const terminatorIndex = args.indexOf('--');
91-
92-
if (terminatorIndex === -1) {
93-
return [...args, option, configPath];
94-
}
95-
96-
return [...args.slice(0, terminatorIndex), option, configPath, ...args.slice(terminatorIndex)];
97-
}
98-
9932
async function runRsbuildCLI(args: string[]): Promise<void> {
10033
const argv = [
10134
process.execPath,
@@ -142,9 +75,10 @@ async function runRslintCLI(args: string[]): Promise<void> {
14275

14376
export async function setupCommands(): Promise<void> {
14477
const { args, configPath } = parseCliArgs(process.argv.slice(2));
145-
getConfigState().configPath = configPath;
14678
const command = args[0];
14779

80+
getConfigState().configPath = configPath;
81+
14882
if (!command || command === '-h' || command === '--help') {
14983
console.log(helpMessage);
15084
return;

0 commit comments

Comments
 (0)