Skip to content

Commit 4325ac6

Browse files
authored
fix: correctly split the argv string (#7533)
1 parent 7f7ff11 commit 4325ac6

File tree

2 files changed

+72
-1
lines changed

2 files changed

+72
-1
lines changed

packages/vitest/src/node/cli/cac.ts

+27-1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ function addCommand(cli: CAC | Command, name: string, option: CLIOption<any>) {
2222
`Expected a single value for option "${command}", received [${received}]`,
2323
)
2424
}
25+
value = removeQuotes(value)
2526
if (option.transform) {
2627
return option.transform(value)
2728
}
@@ -196,11 +197,36 @@ export function createCLI(options: CliParseOptions = {}): CAC {
196197
return cli
197198
}
198199

200+
function removeQuotes<T>(str: T): T {
201+
if (typeof str !== 'string') {
202+
if (Array.isArray(str)) {
203+
return str.map(removeQuotes) as unknown as T
204+
}
205+
return str
206+
}
207+
if (str.startsWith('"') && str.endsWith('"')) {
208+
return str.slice(1, -1) as unknown as T
209+
}
210+
if (str.startsWith(`'`) && str.endsWith(`'`)) {
211+
return str.slice(1, -1) as unknown as T
212+
}
213+
return str
214+
}
215+
216+
function splitArgv(argv: string): string[] {
217+
const reg = /(['"])(?:(?!\1).)+\1/g
218+
argv = argv.replace(reg, match => match.replace(/\s/g, '\x00'))
219+
return argv.split(' ').map((arg: string) => {
220+
arg = arg.replace(/\0/g, ' ')
221+
return removeQuotes(arg)
222+
})
223+
}
224+
199225
export function parseCLI(argv: string | string[], config: CliParseOptions = {}): {
200226
filter: string[]
201227
options: CliOptions
202228
} {
203-
const arrayArgs = typeof argv === 'string' ? argv.split(' ') : argv
229+
const arrayArgs = typeof argv === 'string' ? splitArgv(argv) : argv
204230
if (arrayArgs[0] !== 'vitest') {
205231
throw new Error(`Expected "vitest" as the first argument, received "${arrayArgs[0]}"`)
206232
}

test/core/test/cli-test.test.ts

+45
Original file line numberDiff line numberDiff line change
@@ -411,6 +411,51 @@ test('public parseCLI works correctly', () => {
411411
},
412412
})
413413

414+
expect(parseCLI('vitest --project="space 1"')).toEqual({
415+
filter: [],
416+
options: {
417+
'project': ['space 1'],
418+
'--': [],
419+
'color': true,
420+
},
421+
})
422+
423+
expect(parseCLI('vitest "--project=space 1"')).toEqual({
424+
filter: [],
425+
options: {
426+
'project': ['space 1'],
427+
'--': [],
428+
'color': true,
429+
},
430+
})
431+
432+
expect(parseCLI('vitest --project "space 1"')).toEqual({
433+
filter: [],
434+
options: {
435+
'project': ['space 1'],
436+
'--': [],
437+
'color': true,
438+
},
439+
})
440+
441+
expect(parseCLI('vitest --project="space 1" --project="space 2"')).toEqual({
442+
filter: [],
443+
options: {
444+
'project': ['space 1', 'space 2'],
445+
'--': [],
446+
'color': true,
447+
},
448+
})
449+
450+
expect(parseCLI('vitest ./test-1.js ./test-2.js --project="space 1" --project="space 2" --project="space 3"')).toEqual({
451+
filter: ['./test-1.js', './test-2.js'],
452+
options: {
453+
'project': ['space 1', 'space 2', 'space 3'],
454+
'--': [],
455+
'color': true,
456+
},
457+
})
458+
414459
expect(parseCLI('vitest --exclude=docs --exclude=demo')).toEqual({
415460
filter: [],
416461
options: {

0 commit comments

Comments
 (0)