/
parse.ts
81 lines (65 loc) · 2.21 KB
/
parse.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
import { OptionExpectedValueError, OptionHelpRequested } from './errors.ts'
import { Options } from './options.ts'
import { isDefault, isLong, split, strip } from './util.ts'
/**
* Process an options object and throw errors so that they can be
* managed manually.
*
* @param target The decorated options object.
* @param argv Arguments values.
* @throws An AggregateError with {@link OptionValidationError} when there are multiple errors
* or an {@link OptionHelpRequested} error.
* The `.message` property can be printed and is the same as the managed version {@link decarg}.
* @returns The populated options object when successful, otherwise throws.
*/
export function parse<T extends object>(target: T, argv = process.argv.slice(1)) {
const [[exec, ...args], rest] = split('--', argv)
const options = new Options(target, exec)
if (rest) options.find('--')?.set(...rest)
if (args.includes('-h') || args.includes('--help')) {
throw new OptionHelpRequested(options)
}
const errors: Error[] = []
for (let i = 0; i < args.length; i++) {
try {
const arg = args[i]
if (isDefault(arg)) {
options.default?.set(arg)
continue
}
const [name, value] = split('=', strip(arg)) as [string, string]
// --long
if (isLong(arg)) {
const opt = options.find(name)
if (!opt) continue
if (opt.arg.type === Boolean) opt.set(value ?? true)
else opt.set(value ?? args[++i])
continue
}
// shorts -cFg
const shorts = name.split('')
// when there is -cFg=value try to set g=value and eat 'g'
if (value != null) {
options.find(shorts.pop())?.set(value)
}
// iterate rest of the shorts
for (const short of shorts) {
const opt = options.find(short)
if (!opt) continue
if (opt.arg.type === Boolean) opt.set(true)
else opt.set(args[++i])
}
} catch (error) {
errors.push(error as Error)
}
}
for (const arg of options.options) {
if (target[arg.arg.property as keyof T] == null) {
errors.push(new OptionExpectedValueError(arg))
}
}
if (errors.length) {
throw new AggregateError(errors, options.help())
}
return target
}