args-tokens 0.12.0 → 0.13.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -4
- package/lib/index.d.ts +50 -6
- package/lib/index.js +22 -5
- package/lib/parser-DxH6Mf-o.js +166 -0
- package/lib/parser.d.ts +71 -69
- package/lib/parser.js +3 -226
- package/lib/resolver-Di82Qgwx.js +179 -0
- package/lib/resolver.d.ts +68 -63
- package/lib/resolver.js +4 -236
- package/package.json +9 -5
- package/lib/parse.d.ts +0 -44
- package/lib/parse.js +0 -34
package/README.md
CHANGED
|
@@ -302,10 +302,6 @@ const tokens = parseArgs(['-a=1'], { allowCompatible: true }) // add `allowCompa
|
|
|
302
302
|
deepStrictEqual(tokensNode, tokens)
|
|
303
303
|
```
|
|
304
304
|
|
|
305
|
-
## 💁♀️ Showcases
|
|
306
|
-
|
|
307
|
-
- [pnpmc](https://github.com/kazupon/pnpmc): PNPM Catalogs Tooling
|
|
308
|
-
|
|
309
305
|
## 🙌 Contributing guidelines
|
|
310
306
|
|
|
311
307
|
If you are interested in contributing to `args-tokens`, I highly recommend checking out [the contributing guidelines](/CONTRIBUTING.md) here. You'll find all the relevant information such as [how to make a PR](/CONTRIBUTING.md#pull-request-guidelines), [how to setup development](/CONTRIBUTING.md#development-setup)) etc., there.
|
package/lib/index.d.ts
CHANGED
|
@@ -1,6 +1,50 @@
|
|
|
1
|
-
|
|
2
|
-
export { parseArgs } from './parser.js';
|
|
3
|
-
|
|
4
|
-
export
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
import { ParserOptions } from './parser.js';
|
|
2
|
+
export { ArgToken, parseArgs } from './parser.js';
|
|
3
|
+
import { ArgOptions, ArgValues } from './resolver.js';
|
|
4
|
+
export { ArgOptionSchema, OptionResolveError, resolveArgs } from './resolver.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Parse options for {@link parse} function.
|
|
8
|
+
*/
|
|
9
|
+
interface ParseOptions<O extends ArgOptions> extends ParserOptions {
|
|
10
|
+
/**
|
|
11
|
+
* Command line options, about details see {@link ArgOptions}.
|
|
12
|
+
*/
|
|
13
|
+
options?: O;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Parsed command line arguments.
|
|
17
|
+
*/
|
|
18
|
+
type ParsedArgs<T extends ArgOptions> = {
|
|
19
|
+
/**
|
|
20
|
+
* Parsed values, same as `values` in {@link resolveArgs}.
|
|
21
|
+
*/
|
|
22
|
+
values: ArgValues<T>
|
|
23
|
+
/**
|
|
24
|
+
* Positional arguments, same as `positionals` in {@link resolveArgs}.
|
|
25
|
+
*/
|
|
26
|
+
positionals: string[]
|
|
27
|
+
/**
|
|
28
|
+
* Validation errors, same as `errors` in {@link resolveArgs}.
|
|
29
|
+
*/
|
|
30
|
+
error: AggregateError | undefined
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Parse command line arguments.
|
|
34
|
+
* This function is a convenient API, that is used {@link parseArgs} and {@link resolveArgs} in internal.
|
|
35
|
+
* @example
|
|
36
|
+
* ```js
|
|
37
|
+
* import { parse } from 'args-tokens'
|
|
38
|
+
*
|
|
39
|
+
* const { values, positionals } = parse(process.argv.slice(2))
|
|
40
|
+
* console.log('values', values)
|
|
41
|
+
* console.log('positionals', positionals)
|
|
42
|
+
* ```
|
|
43
|
+
* @param args - command line arguments
|
|
44
|
+
* @param options - parse options, about details see {@link ParseOptions}
|
|
45
|
+
* @returns An object that contains the values of the arguments, positional arguments, and {@link AggregateError | validation errors}.
|
|
46
|
+
*/
|
|
47
|
+
declare function parse<O extends ArgOptions>(args: string[], options?: ParseOptions<O>): ParsedArgs<O>;
|
|
48
|
+
|
|
49
|
+
export { ArgOptions, ArgValues, ParserOptions, parse };
|
|
50
|
+
export type { ParseOptions, ParsedArgs };
|
package/lib/index.js
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import { parseArgs } from "./parser-DxH6Mf-o.js";
|
|
2
|
+
import { OptionResolveError, resolveArgs } from "./resolver-Di82Qgwx.js";
|
|
3
|
+
|
|
4
|
+
//#region src/parse.ts
|
|
5
|
+
const DEFAULT_OPTIONS = {
|
|
6
|
+
help: {
|
|
7
|
+
type: "boolean",
|
|
8
|
+
short: "h"
|
|
9
|
+
},
|
|
10
|
+
version: {
|
|
11
|
+
type: "boolean",
|
|
12
|
+
short: "v"
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
function parse(args, options = {}) {
|
|
16
|
+
const { options: argOptions, allowCompatible = false } = options;
|
|
17
|
+
const tokens = parseArgs(args, { allowCompatible });
|
|
18
|
+
return resolveArgs(argOptions || DEFAULT_OPTIONS, tokens);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
//#endregion
|
|
22
|
+
export { OptionResolveError, parse, parseArgs, resolveArgs };
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
|
|
2
|
+
//#region src/parser.ts
|
|
3
|
+
const HYPHEN_CHAR = "-";
|
|
4
|
+
const HYPHEN_CODE = HYPHEN_CHAR.codePointAt(0);
|
|
5
|
+
const EQUAL_CHAR = "=";
|
|
6
|
+
const EQUAL_CODE = EQUAL_CHAR.codePointAt(0);
|
|
7
|
+
const TERMINATOR = "--";
|
|
8
|
+
const SHORT_OPTION_PREFIX = HYPHEN_CHAR;
|
|
9
|
+
const LONG_OPTION_PREFIX = "--";
|
|
10
|
+
function parseArgs(args, options = {}) {
|
|
11
|
+
const { allowCompatible = false } = options;
|
|
12
|
+
const tokens = [];
|
|
13
|
+
const remainings = [...args];
|
|
14
|
+
let index = -1;
|
|
15
|
+
let groupCount = 0;
|
|
16
|
+
let hasShortValueSeparator = false;
|
|
17
|
+
while (remainings.length > 0) {
|
|
18
|
+
const arg = remainings.shift();
|
|
19
|
+
if (arg == void 0) break;
|
|
20
|
+
const nextArg = remainings[0];
|
|
21
|
+
if (groupCount > 0) groupCount--;
|
|
22
|
+
else index++;
|
|
23
|
+
if (arg === TERMINATOR) {
|
|
24
|
+
tokens.push({
|
|
25
|
+
kind: "option-terminator",
|
|
26
|
+
index
|
|
27
|
+
});
|
|
28
|
+
const mapped = remainings.map((arg$1) => {
|
|
29
|
+
return {
|
|
30
|
+
kind: "positional",
|
|
31
|
+
index: ++index,
|
|
32
|
+
value: arg$1
|
|
33
|
+
};
|
|
34
|
+
});
|
|
35
|
+
tokens.push(...mapped);
|
|
36
|
+
break;
|
|
37
|
+
}
|
|
38
|
+
if (isShortOption(arg)) {
|
|
39
|
+
const shortOption = arg.charAt(1);
|
|
40
|
+
let value;
|
|
41
|
+
let inlineValue;
|
|
42
|
+
if (groupCount) {
|
|
43
|
+
tokens.push({
|
|
44
|
+
kind: "option",
|
|
45
|
+
name: shortOption,
|
|
46
|
+
rawName: arg,
|
|
47
|
+
index,
|
|
48
|
+
value,
|
|
49
|
+
inlineValue
|
|
50
|
+
});
|
|
51
|
+
if (groupCount === 1 && hasOptionValue(nextArg)) {
|
|
52
|
+
value = remainings.shift();
|
|
53
|
+
if (hasShortValueSeparator) {
|
|
54
|
+
inlineValue = true;
|
|
55
|
+
hasShortValueSeparator = false;
|
|
56
|
+
}
|
|
57
|
+
tokens.push({
|
|
58
|
+
kind: "option",
|
|
59
|
+
index,
|
|
60
|
+
value,
|
|
61
|
+
inlineValue
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
} else tokens.push({
|
|
65
|
+
kind: "option",
|
|
66
|
+
name: shortOption,
|
|
67
|
+
rawName: arg,
|
|
68
|
+
index,
|
|
69
|
+
value,
|
|
70
|
+
inlineValue
|
|
71
|
+
});
|
|
72
|
+
if (value != null) ++index;
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
if (isShortOptionGroup(arg)) {
|
|
76
|
+
const expanded = [];
|
|
77
|
+
let shortValue = "";
|
|
78
|
+
for (let i = 1; i < arg.length; i++) {
|
|
79
|
+
const shortableOption = arg.charAt(i);
|
|
80
|
+
if (hasShortValueSeparator) shortValue += shortableOption;
|
|
81
|
+
else if (!allowCompatible && shortableOption.codePointAt(0) === EQUAL_CODE) hasShortValueSeparator = true;
|
|
82
|
+
else expanded.push(`${SHORT_OPTION_PREFIX}${shortableOption}`);
|
|
83
|
+
}
|
|
84
|
+
if (shortValue) expanded.push(shortValue);
|
|
85
|
+
remainings.unshift(...expanded);
|
|
86
|
+
groupCount = expanded.length;
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
if (isLongOption(arg)) {
|
|
90
|
+
const longOption = arg.slice(2);
|
|
91
|
+
tokens.push({
|
|
92
|
+
kind: "option",
|
|
93
|
+
name: longOption,
|
|
94
|
+
rawName: arg,
|
|
95
|
+
index,
|
|
96
|
+
value: void 0,
|
|
97
|
+
inlineValue: void 0
|
|
98
|
+
});
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
if (isLongOptionAndValue(arg)) {
|
|
102
|
+
const equalIndex = arg.indexOf(EQUAL_CHAR);
|
|
103
|
+
const longOption = arg.slice(2, equalIndex);
|
|
104
|
+
const value = arg.slice(equalIndex + 1);
|
|
105
|
+
tokens.push({
|
|
106
|
+
kind: "option",
|
|
107
|
+
name: longOption,
|
|
108
|
+
rawName: `${LONG_OPTION_PREFIX}${longOption}`,
|
|
109
|
+
index,
|
|
110
|
+
value,
|
|
111
|
+
inlineValue: true
|
|
112
|
+
});
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
tokens.push({
|
|
116
|
+
kind: "positional",
|
|
117
|
+
index,
|
|
118
|
+
value: arg
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
return tokens;
|
|
122
|
+
}
|
|
123
|
+
function isShortOption(arg) {
|
|
124
|
+
return arg.length === 2 && arg.codePointAt(0) === HYPHEN_CODE && arg.codePointAt(1) !== HYPHEN_CODE;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Check if `arg` is a short option group (e.g. `-abc`).
|
|
128
|
+
* @param arg the argument to check
|
|
129
|
+
* @returns whether `arg` is a short option group.
|
|
130
|
+
*/
|
|
131
|
+
function isShortOptionGroup(arg) {
|
|
132
|
+
if (arg.length <= 2) return false;
|
|
133
|
+
if (arg.codePointAt(0) !== HYPHEN_CODE) return false;
|
|
134
|
+
if (arg.codePointAt(1) === HYPHEN_CODE) return false;
|
|
135
|
+
return true;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Check if `arg` is a long option (e.g. `--foo`).
|
|
139
|
+
* @param arg the argument to check
|
|
140
|
+
* @returns whether `arg` is a long option.
|
|
141
|
+
*/
|
|
142
|
+
function isLongOption(arg) {
|
|
143
|
+
return hasLongOptionPrefix(arg) && !arg.includes(EQUAL_CHAR, 3);
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Check if `arg` is a long option with value (e.g. `--foo=bar`).
|
|
147
|
+
* @param arg the argument to check
|
|
148
|
+
* @returns whether `arg` is a long option.
|
|
149
|
+
*/
|
|
150
|
+
function isLongOptionAndValue(arg) {
|
|
151
|
+
return hasLongOptionPrefix(arg) && arg.includes(EQUAL_CHAR, 3);
|
|
152
|
+
}
|
|
153
|
+
function hasLongOptionPrefix(arg) {
|
|
154
|
+
return arg.length > 2 && ~arg.indexOf(LONG_OPTION_PREFIX);
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Check if a `value` is an option value.
|
|
158
|
+
* @param value a value to check
|
|
159
|
+
* @returns whether a `value` is an option value.
|
|
160
|
+
*/
|
|
161
|
+
function hasOptionValue(value) {
|
|
162
|
+
return !(value == null) && value.codePointAt(0) !== HYPHEN_CODE;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
//#endregion
|
|
166
|
+
export { hasLongOptionPrefix, isShortOption, parseArgs };
|
package/lib/parser.d.ts
CHANGED
|
@@ -1,77 +1,79 @@
|
|
|
1
1
|
/**
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
type ArgTokenKind =
|
|
2
|
+
* Argument token Kind.
|
|
3
|
+
* - `option`: option token, support short option (e.g. `-x`) and long option (e.g. `--foo`)
|
|
4
|
+
* - `option-terminator`: option terminator (`--`) token, see guideline 10 in https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap12.html
|
|
5
|
+
* - `positional`: positional token
|
|
6
|
+
*/
|
|
7
|
+
type ArgTokenKind = "option" | "option-terminator" | "positional";
|
|
8
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
|
-
|
|
9
|
+
* Argument token.
|
|
10
|
+
*/
|
|
11
|
+
interface ArgToken {
|
|
12
|
+
/**
|
|
13
|
+
* Argument token kind.
|
|
14
|
+
*/
|
|
15
|
+
kind: ArgTokenKind;
|
|
16
|
+
/**
|
|
17
|
+
* Argument token index, e.g `--foo bar` => `--foo` index is 0, `bar` index is 1.
|
|
18
|
+
*/
|
|
19
|
+
index: number;
|
|
20
|
+
/**
|
|
21
|
+
* Option name, e.g. `--foo` => `foo`, `-x` => `x`.
|
|
22
|
+
*/
|
|
23
|
+
name?: string;
|
|
24
|
+
/**
|
|
25
|
+
* Raw option name, e.g. `--foo` => `--foo`, `-x` => `-x`.
|
|
26
|
+
*/
|
|
27
|
+
rawName?: string;
|
|
28
|
+
/**
|
|
29
|
+
* Option value, e.g. `--foo=bar` => `bar`, `-x=bar` => `bar`.
|
|
30
|
+
* If the `allowCompatible` option is `true`, short option value will be same as Node.js `parseArgs` behavior.
|
|
31
|
+
*/
|
|
32
|
+
value?: string;
|
|
33
|
+
/**
|
|
34
|
+
* Inline value, e.g. `--foo=bar` => `true`, `-x=bar` => `true`.
|
|
35
|
+
*/
|
|
36
|
+
inlineValue?: boolean;
|
|
37
37
|
}
|
|
38
38
|
/**
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
39
|
+
* Parser Options.
|
|
40
|
+
*/
|
|
41
|
+
interface ParserOptions {
|
|
42
|
+
/**
|
|
43
|
+
* [Node.js parseArgs](https://nodejs.org/api/util.html#parseargs-tokens) tokens compatible mode.
|
|
44
|
+
* @default false
|
|
45
|
+
*/
|
|
46
|
+
allowCompatible?: boolean;
|
|
47
47
|
}
|
|
48
48
|
/**
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
49
|
+
* Parse command line arguments.
|
|
50
|
+
* @example
|
|
51
|
+
* ```js
|
|
52
|
+
* import { parseArgs } from 'args-tokens' // for Node.js and Bun
|
|
53
|
+
* // import { parseArgs } from 'jsr:@kazupon/args-tokens' // for Deno
|
|
54
|
+
*
|
|
55
|
+
* const tokens = parseArgs(['--foo', 'bar', '-x', '--bar=baz'])
|
|
56
|
+
* // do something with using tokens
|
|
57
|
+
* // ...
|
|
58
|
+
* console.log('tokens:', tokens)
|
|
59
|
+
* ```
|
|
60
|
+
* @param args command line arguments
|
|
61
|
+
* @param options parse options
|
|
62
|
+
* @returns Argument tokens.
|
|
63
|
+
*/
|
|
64
|
+
declare function parseArgs(args: string[], options?: ParserOptions): ArgToken[];
|
|
65
65
|
/**
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
66
|
+
* Check if `arg` is a short option (e.g. `-f`).
|
|
67
|
+
* @param arg the argument to check
|
|
68
|
+
* @returns whether `arg` is a short option.
|
|
69
|
+
*/
|
|
70
|
+
declare function isShortOption(arg: string): boolean;
|
|
71
71
|
/**
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
72
|
+
* Check if `arg` is a long option prefix (e.g. `--`).
|
|
73
|
+
* @param arg the argument to check
|
|
74
|
+
* @returns whether `arg` is a long option prefix.
|
|
75
|
+
*/
|
|
76
|
+
declare function hasLongOptionPrefix(arg: string): boolean;
|
|
77
|
+
|
|
78
|
+
export { hasLongOptionPrefix, isShortOption, parseArgs };
|
|
79
|
+
export type { ArgToken, ParserOptions };
|
package/lib/parser.js
CHANGED
|
@@ -1,226 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
// Repository url: https://github.com/nodejs/node (https://github.com/pkgjs/parseargs)
|
|
5
|
-
// Author: Node.js contributors
|
|
6
|
-
// Code url: https://github.com/nodejs/node/blob/main/lib/internal/util/parse_args/parse_args.js
|
|
7
|
-
const HYPHEN_CHAR = '-';
|
|
8
|
-
const HYPHEN_CODE = HYPHEN_CHAR.codePointAt(0);
|
|
9
|
-
const EQUAL_CHAR = '=';
|
|
10
|
-
const EQUAL_CODE = EQUAL_CHAR.codePointAt(0);
|
|
11
|
-
const TERMINATOR = '--';
|
|
12
|
-
const SHORT_OPTION_PREFIX = HYPHEN_CHAR;
|
|
13
|
-
const LONG_OPTION_PREFIX = '--';
|
|
14
|
-
/**
|
|
15
|
-
* Parse command line arguments
|
|
16
|
-
* @example
|
|
17
|
-
* ```js
|
|
18
|
-
* import { parseArgs } from 'args-tokens' // for Node.js and Bun
|
|
19
|
-
* // import { parseArgs } from 'jsr:@kazupon/args-tokens' // for Deno
|
|
20
|
-
*
|
|
21
|
-
* const tokens = parseArgs(['--foo', 'bar', '-x', '--bar=baz'])
|
|
22
|
-
* // do something with using tokens
|
|
23
|
-
* // ...
|
|
24
|
-
* console.log('tokens:', tokens)
|
|
25
|
-
* ```
|
|
26
|
-
* @param args command line arguments
|
|
27
|
-
* @param options parse options
|
|
28
|
-
* @returns argument tokens
|
|
29
|
-
*/
|
|
30
|
-
export function parseArgs(args, options = {}) {
|
|
31
|
-
const { allowCompatible = false } = options;
|
|
32
|
-
const tokens = [];
|
|
33
|
-
const remainings = [...args];
|
|
34
|
-
let index = -1;
|
|
35
|
-
let groupCount = 0;
|
|
36
|
-
let hasShortValueSeparator = false;
|
|
37
|
-
while (remainings.length > 0) {
|
|
38
|
-
const arg = remainings.shift();
|
|
39
|
-
if (arg == undefined) {
|
|
40
|
-
break;
|
|
41
|
-
}
|
|
42
|
-
const nextArg = remainings[0];
|
|
43
|
-
if (groupCount > 0) {
|
|
44
|
-
groupCount--;
|
|
45
|
-
}
|
|
46
|
-
else {
|
|
47
|
-
index++;
|
|
48
|
-
}
|
|
49
|
-
// check if `arg` is an options terminator.
|
|
50
|
-
// guideline 10 in https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap12.html
|
|
51
|
-
if (arg === TERMINATOR) {
|
|
52
|
-
tokens.push({
|
|
53
|
-
kind: 'option-terminator',
|
|
54
|
-
index
|
|
55
|
-
});
|
|
56
|
-
const mapped = remainings.map(arg => {
|
|
57
|
-
return { kind: 'positional', index: ++index, value: arg };
|
|
58
|
-
});
|
|
59
|
-
tokens.push(...mapped);
|
|
60
|
-
break;
|
|
61
|
-
}
|
|
62
|
-
if (isShortOption(arg)) {
|
|
63
|
-
const shortOption = arg.charAt(1);
|
|
64
|
-
let value;
|
|
65
|
-
let inlineValue;
|
|
66
|
-
if (groupCount) {
|
|
67
|
-
// e.g. `-abc`
|
|
68
|
-
tokens.push({
|
|
69
|
-
kind: 'option',
|
|
70
|
-
name: shortOption,
|
|
71
|
-
rawName: arg,
|
|
72
|
-
index,
|
|
73
|
-
value,
|
|
74
|
-
inlineValue
|
|
75
|
-
});
|
|
76
|
-
if (groupCount === 1 && hasOptionValue(nextArg)) {
|
|
77
|
-
value = remainings.shift();
|
|
78
|
-
if (hasShortValueSeparator) {
|
|
79
|
-
inlineValue = true;
|
|
80
|
-
hasShortValueSeparator = false;
|
|
81
|
-
}
|
|
82
|
-
tokens.push({
|
|
83
|
-
kind: 'option',
|
|
84
|
-
index,
|
|
85
|
-
value,
|
|
86
|
-
inlineValue
|
|
87
|
-
});
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
else {
|
|
91
|
-
// e.g. `-a`
|
|
92
|
-
tokens.push({
|
|
93
|
-
kind: 'option',
|
|
94
|
-
name: shortOption,
|
|
95
|
-
rawName: arg,
|
|
96
|
-
index,
|
|
97
|
-
value,
|
|
98
|
-
inlineValue
|
|
99
|
-
});
|
|
100
|
-
}
|
|
101
|
-
// eslint-disable-next-line unicorn/no-null
|
|
102
|
-
if (value != null) {
|
|
103
|
-
++index;
|
|
104
|
-
}
|
|
105
|
-
continue;
|
|
106
|
-
}
|
|
107
|
-
if (isShortOptionGroup(arg)) {
|
|
108
|
-
// expend short option group (e.g. `-abc` => `-a -b -c`, `-f=bar` => `-f bar`)
|
|
109
|
-
const expanded = [];
|
|
110
|
-
let shortValue = '';
|
|
111
|
-
for (let i = 1; i < arg.length; i++) {
|
|
112
|
-
const shortableOption = arg.charAt(i);
|
|
113
|
-
if (hasShortValueSeparator) {
|
|
114
|
-
shortValue += shortableOption;
|
|
115
|
-
}
|
|
116
|
-
else {
|
|
117
|
-
if (!allowCompatible && shortableOption.codePointAt(0) === EQUAL_CODE) {
|
|
118
|
-
hasShortValueSeparator = true;
|
|
119
|
-
}
|
|
120
|
-
else {
|
|
121
|
-
expanded.push(`${SHORT_OPTION_PREFIX}${shortableOption}`);
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
if (shortValue) {
|
|
126
|
-
expanded.push(shortValue);
|
|
127
|
-
}
|
|
128
|
-
remainings.unshift(...expanded);
|
|
129
|
-
groupCount = expanded.length;
|
|
130
|
-
continue;
|
|
131
|
-
}
|
|
132
|
-
if (isLongOption(arg)) {
|
|
133
|
-
// e.g. `--foo`
|
|
134
|
-
const longOption = arg.slice(2);
|
|
135
|
-
tokens.push({
|
|
136
|
-
kind: 'option',
|
|
137
|
-
name: longOption,
|
|
138
|
-
rawName: arg,
|
|
139
|
-
index,
|
|
140
|
-
value: undefined,
|
|
141
|
-
inlineValue: undefined
|
|
142
|
-
});
|
|
143
|
-
continue;
|
|
144
|
-
}
|
|
145
|
-
if (isLongOptionAndValue(arg)) {
|
|
146
|
-
// e.g. `--foo=bar`
|
|
147
|
-
const equalIndex = arg.indexOf(EQUAL_CHAR);
|
|
148
|
-
const longOption = arg.slice(2, equalIndex);
|
|
149
|
-
const value = arg.slice(equalIndex + 1);
|
|
150
|
-
tokens.push({
|
|
151
|
-
kind: 'option',
|
|
152
|
-
name: longOption,
|
|
153
|
-
rawName: `${LONG_OPTION_PREFIX}${longOption}`,
|
|
154
|
-
index,
|
|
155
|
-
value,
|
|
156
|
-
inlineValue: true
|
|
157
|
-
});
|
|
158
|
-
continue;
|
|
159
|
-
}
|
|
160
|
-
tokens.push({
|
|
161
|
-
kind: 'positional',
|
|
162
|
-
index,
|
|
163
|
-
value: arg
|
|
164
|
-
});
|
|
165
|
-
}
|
|
166
|
-
return tokens;
|
|
167
|
-
}
|
|
168
|
-
/**
|
|
169
|
-
* Check if `arg` is a short option (e.g. `-f`)
|
|
170
|
-
* @param arg the argument to check
|
|
171
|
-
* @returns whether `arg` is a short option
|
|
172
|
-
*/
|
|
173
|
-
export function isShortOption(arg) {
|
|
174
|
-
return (arg.length === 2 && arg.codePointAt(0) === HYPHEN_CODE && arg.codePointAt(1) !== HYPHEN_CODE);
|
|
175
|
-
}
|
|
176
|
-
/**
|
|
177
|
-
* Check if `arg` is a short option group (e.g. `-abc`)
|
|
178
|
-
* @param arg the argument to check
|
|
179
|
-
* @returns whether `arg` is a short option group
|
|
180
|
-
*/
|
|
181
|
-
function isShortOptionGroup(arg) {
|
|
182
|
-
if (arg.length <= 2) {
|
|
183
|
-
return false;
|
|
184
|
-
}
|
|
185
|
-
if (arg.codePointAt(0) !== HYPHEN_CODE) {
|
|
186
|
-
return false;
|
|
187
|
-
}
|
|
188
|
-
if (arg.codePointAt(1) === HYPHEN_CODE) {
|
|
189
|
-
return false;
|
|
190
|
-
}
|
|
191
|
-
return true;
|
|
192
|
-
}
|
|
193
|
-
/**
|
|
194
|
-
* Check if `arg` is a long option (e.g. `--foo`)
|
|
195
|
-
* @param arg the argument to check
|
|
196
|
-
* @returns whether `arg` is a long option
|
|
197
|
-
*/
|
|
198
|
-
function isLongOption(arg) {
|
|
199
|
-
return hasLongOptionPrefix(arg) && !arg.includes(EQUAL_CHAR, 3);
|
|
200
|
-
}
|
|
201
|
-
/**
|
|
202
|
-
* Check if `arg` is a long option with value (e.g. `--foo=bar`)
|
|
203
|
-
* @param arg the argument to check
|
|
204
|
-
* @returns whether `arg` is a long option
|
|
205
|
-
*/
|
|
206
|
-
function isLongOptionAndValue(arg) {
|
|
207
|
-
return hasLongOptionPrefix(arg) && arg.includes(EQUAL_CHAR, 3);
|
|
208
|
-
}
|
|
209
|
-
/**
|
|
210
|
-
* Check if `arg` is a long option prefix (e.g. `--`)
|
|
211
|
-
* @param arg the argument to check
|
|
212
|
-
* @returns whether `arg` is a long option prefix
|
|
213
|
-
*/
|
|
214
|
-
export function hasLongOptionPrefix(arg) {
|
|
215
|
-
// @ts-ignore -- NOTE: `~` is a bitwise NOT operator
|
|
216
|
-
return arg.length > 2 && ~arg.indexOf(LONG_OPTION_PREFIX);
|
|
217
|
-
}
|
|
218
|
-
/**
|
|
219
|
-
* Check if a `value` is an option value
|
|
220
|
-
* @param value a value to check
|
|
221
|
-
* @returns whether a `value` is an option value
|
|
222
|
-
*/
|
|
223
|
-
function hasOptionValue(value) {
|
|
224
|
-
// eslint-disable-next-line unicorn/no-null
|
|
225
|
-
return !(value == null) && value.codePointAt(0) !== HYPHEN_CODE;
|
|
226
|
-
}
|
|
1
|
+
import { hasLongOptionPrefix, isShortOption, parseArgs } from "./parser-DxH6Mf-o.js";
|
|
2
|
+
|
|
3
|
+
export { hasLongOptionPrefix, isShortOption, parseArgs };
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import { hasLongOptionPrefix, isShortOption } from "./parser-DxH6Mf-o.js";
|
|
2
|
+
|
|
3
|
+
//#region src/resolver.ts
|
|
4
|
+
function resolveArgs(options, tokens) {
|
|
5
|
+
const positionals = [];
|
|
6
|
+
const longOptionTokens = [];
|
|
7
|
+
const shortOptionTokens = [];
|
|
8
|
+
let currentLongOption;
|
|
9
|
+
let currentShortOption;
|
|
10
|
+
const expandableShortOptions = [];
|
|
11
|
+
function toShortValue() {
|
|
12
|
+
if (expandableShortOptions.length === 0) return void 0;
|
|
13
|
+
else {
|
|
14
|
+
const value = expandableShortOptions.map((token) => token.name).join("");
|
|
15
|
+
expandableShortOptions.length = 0;
|
|
16
|
+
return value;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function applyLongOptionValue(value = void 0) {
|
|
20
|
+
if (currentLongOption) {
|
|
21
|
+
currentLongOption.value = value;
|
|
22
|
+
longOptionTokens.push({ ...currentLongOption });
|
|
23
|
+
currentLongOption = void 0;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function applyShortOptionValue(value = void 0) {
|
|
27
|
+
if (currentShortOption) {
|
|
28
|
+
currentShortOption.value = value || toShortValue();
|
|
29
|
+
shortOptionTokens.push({ ...currentShortOption });
|
|
30
|
+
currentShortOption = void 0;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* analyze phase to resolve value
|
|
35
|
+
* separate tokens into positionals, long and short options, after that resolve values
|
|
36
|
+
*/
|
|
37
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
38
|
+
const token = tokens[i];
|
|
39
|
+
if (token.kind === "positional") {
|
|
40
|
+
positionals.push(token.value);
|
|
41
|
+
applyLongOptionValue(token.value);
|
|
42
|
+
applyShortOptionValue(token.value);
|
|
43
|
+
} else if (token.kind === "option") if (token.rawName) {
|
|
44
|
+
if (hasLongOptionPrefix(token.rawName)) {
|
|
45
|
+
if (token.inlineValue) longOptionTokens.push({ ...token });
|
|
46
|
+
else currentLongOption = { ...token };
|
|
47
|
+
applyShortOptionValue();
|
|
48
|
+
} else if (isShortOption(token.rawName)) if (currentShortOption) {
|
|
49
|
+
if (currentShortOption.index === token.index) expandableShortOptions.push({ ...token });
|
|
50
|
+
else {
|
|
51
|
+
currentShortOption.value = toShortValue();
|
|
52
|
+
shortOptionTokens.push({ ...currentShortOption });
|
|
53
|
+
currentShortOption = { ...token };
|
|
54
|
+
}
|
|
55
|
+
applyLongOptionValue();
|
|
56
|
+
} else {
|
|
57
|
+
currentShortOption = { ...token };
|
|
58
|
+
applyLongOptionValue();
|
|
59
|
+
}
|
|
60
|
+
} else {
|
|
61
|
+
if (currentShortOption && currentShortOption.index == token.index && token.inlineValue) {
|
|
62
|
+
currentShortOption.value = token.value;
|
|
63
|
+
shortOptionTokens.push({ ...currentShortOption });
|
|
64
|
+
currentShortOption = void 0;
|
|
65
|
+
}
|
|
66
|
+
applyLongOptionValue();
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
applyLongOptionValue();
|
|
70
|
+
applyShortOptionValue();
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* check if the last long or short option is not resolved
|
|
75
|
+
*/
|
|
76
|
+
applyLongOptionValue();
|
|
77
|
+
applyShortOptionValue();
|
|
78
|
+
/**
|
|
79
|
+
* resolve values
|
|
80
|
+
*/
|
|
81
|
+
const values = Object.create(null);
|
|
82
|
+
const errors = [];
|
|
83
|
+
for (const [option, schema] of Object.entries(options)) {
|
|
84
|
+
if (schema.required) {
|
|
85
|
+
const found = longOptionTokens.find((token) => token.name === option) || schema.short && shortOptionTokens.find((token) => token.name === schema.short);
|
|
86
|
+
if (!found) {
|
|
87
|
+
errors.push(createRequireError(option, schema));
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
for (let i = 0; i < longOptionTokens.length; i++) {
|
|
92
|
+
const token = longOptionTokens[i];
|
|
93
|
+
if (option === token.name && token.rawName != null && hasLongOptionPrefix(token.rawName)) {
|
|
94
|
+
const invalid = validateRequire(token, option, schema);
|
|
95
|
+
if (invalid) {
|
|
96
|
+
errors.push(invalid);
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
if (schema.type === "boolean") token.value = void 0;
|
|
100
|
+
else {
|
|
101
|
+
const invalid$1 = validateValue(token, option, schema);
|
|
102
|
+
if (invalid$1) {
|
|
103
|
+
errors.push(invalid$1);
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
values[option] = resolveOptionValue(token, schema);
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
for (let i = 0; i < shortOptionTokens.length; i++) {
|
|
112
|
+
const token = shortOptionTokens[i];
|
|
113
|
+
if (schema.short === token.name && token.rawName != null && isShortOption(token.rawName)) {
|
|
114
|
+
const invalid = validateRequire(token, option, schema);
|
|
115
|
+
if (invalid) {
|
|
116
|
+
errors.push(invalid);
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
if (schema.type === "boolean") token.value = void 0;
|
|
120
|
+
else {
|
|
121
|
+
const invalid$1 = validateValue(token, option, schema);
|
|
122
|
+
if (invalid$1) {
|
|
123
|
+
errors.push(invalid$1);
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
values[option] = resolveOptionValue(token, schema);
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
if (values[option] == null && schema.default != null) values[option] = schema.default;
|
|
132
|
+
}
|
|
133
|
+
return {
|
|
134
|
+
values,
|
|
135
|
+
positionals,
|
|
136
|
+
error: errors.length > 0 ? new AggregateError(errors) : void 0
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
function createRequireError(option, schema) {
|
|
140
|
+
return new OptionResolveError(`Option '--${option}' ${schema.short ? `or '-${schema.short}' ` : ""}is required`, option, schema);
|
|
141
|
+
}
|
|
142
|
+
var OptionResolveError = class extends Error {
|
|
143
|
+
name;
|
|
144
|
+
schema;
|
|
145
|
+
constructor(message, name, schema) {
|
|
146
|
+
super(message);
|
|
147
|
+
this.name = name;
|
|
148
|
+
this.schema = schema;
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
function validateRequire(token, option, schema) {
|
|
152
|
+
if (schema.required && schema.type !== "boolean" && !token.value) return createRequireError(option, schema);
|
|
153
|
+
}
|
|
154
|
+
function validateValue(token, option, schema) {
|
|
155
|
+
switch (schema.type) {
|
|
156
|
+
case "number": {
|
|
157
|
+
if (!isNumeric(token.value)) return createTypeError(option, schema);
|
|
158
|
+
break;
|
|
159
|
+
}
|
|
160
|
+
case "string": {
|
|
161
|
+
if (typeof token.value !== "string") return createTypeError(option, schema);
|
|
162
|
+
break;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
function isNumeric(str) {
|
|
167
|
+
return str.trim() !== "" && !isNaN(str);
|
|
168
|
+
}
|
|
169
|
+
function createTypeError(option, schema) {
|
|
170
|
+
return new OptionResolveError(`Option '--${option}' ${schema.short ? `or '-${schema.short}' ` : ""}should be '${schema.type}'`, option, schema);
|
|
171
|
+
}
|
|
172
|
+
function resolveOptionValue(token, schema) {
|
|
173
|
+
if (token.value) return schema.type === "number" ? +token.value : token.value;
|
|
174
|
+
if (schema.type === "boolean") return true;
|
|
175
|
+
return schema.type === "number" ? +(schema.default || "") : schema.default;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
//#endregion
|
|
179
|
+
export { OptionResolveError, resolveArgs };
|
package/lib/resolver.d.ts
CHANGED
|
@@ -1,72 +1,77 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { ArgToken } from './parser.js';
|
|
2
|
+
|
|
2
3
|
/**
|
|
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
|
-
default?: string | boolean | number;
|
|
4
|
+
* An option schema for an argument.
|
|
5
|
+
* This schema is similar to the schema of the `node:utils`.
|
|
6
|
+
* difference is that:
|
|
7
|
+
* - `multiple` property is not supported
|
|
8
|
+
* - `required` property and `description` property are added
|
|
9
|
+
* - `default` property type, not support multiple types
|
|
10
|
+
*/
|
|
11
|
+
interface ArgOptionSchema {
|
|
12
|
+
/**
|
|
13
|
+
* Type of argument.
|
|
14
|
+
*/
|
|
15
|
+
type: "string" | "boolean" | "number";
|
|
16
|
+
/**
|
|
17
|
+
* A single character alias for the option.
|
|
18
|
+
*/
|
|
19
|
+
short?: string;
|
|
20
|
+
/**
|
|
21
|
+
* A description of the argument.
|
|
22
|
+
*/
|
|
23
|
+
description?: string;
|
|
24
|
+
/**
|
|
25
|
+
* Whether the argument is required or not.
|
|
26
|
+
*/
|
|
27
|
+
required?: true;
|
|
28
|
+
/**
|
|
29
|
+
* The default value of the argument.
|
|
30
|
+
*/
|
|
31
|
+
default?: string | boolean | number;
|
|
32
32
|
}
|
|
33
33
|
/**
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
34
|
+
* An object that contains {@link ArgOptionSchema | options schema}.
|
|
35
|
+
*/
|
|
36
|
+
interface ArgOptions {
|
|
37
|
+
[option: string]: ArgOptionSchema;
|
|
38
38
|
}
|
|
39
39
|
/**
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
}> : {
|
|
45
|
-
[option: string]: string | boolean | number | undefined;
|
|
46
|
-
};
|
|
47
|
-
type ExtractOptionValue<O extends ArgOptionSchema> = O['type'] extends 'string' ? string : O['type'] extends 'boolean' ? boolean : O['type'] extends 'number' ? number : string | boolean | number;
|
|
48
|
-
type ResolveArgValues<O extends ArgOptions, V extends Record<keyof O, unknown>> = {
|
|
49
|
-
-readonly [Option in keyof O]?: V[Option];
|
|
50
|
-
} & FilterArgs<O, V, 'default'> & FilterArgs<O, V, 'required'> extends infer P ? {
|
|
51
|
-
[K in keyof P]: P[K];
|
|
52
|
-
} : never;
|
|
53
|
-
type FilterArgs<O extends ArgOptions, V extends Record<keyof O, unknown>, K extends keyof ArgOptionSchema> = {
|
|
54
|
-
[Option in keyof O as O[Option][K] extends {} ? Option : never]: V[Option];
|
|
40
|
+
* An object that contains the values of the arguments.
|
|
41
|
+
*/
|
|
42
|
+
type ArgValues<T> = T extends ArgOptions ? ResolveArgValues<T, { [Option in keyof T] : ExtractOptionValue<T[Option]> }> : {
|
|
43
|
+
[option: string]: string | boolean | number | undefined
|
|
55
44
|
};
|
|
45
|
+
type ExtractOptionValue<O extends ArgOptionSchema> = O["type"] extends "string" ? string : O["type"] extends "boolean" ? boolean : O["type"] extends "number" ? number : string | boolean | number;
|
|
46
|
+
type ResolveArgValues<
|
|
47
|
+
O extends ArgOptions,
|
|
48
|
+
V extends Record<keyof O, unknown>
|
|
49
|
+
> = { -readonly [Option in keyof O]? : V[Option] } & FilterArgs<O, V, "default"> & FilterArgs<O, V, "required"> extends infer P ? { [K in keyof P] : P[K] } : never;
|
|
50
|
+
type FilterArgs<
|
|
51
|
+
O extends ArgOptions,
|
|
52
|
+
V extends Record<keyof O, unknown>,
|
|
53
|
+
K extends keyof ArgOptionSchema
|
|
54
|
+
> = { [Option in keyof O as O[Option][K] extends {} ? Option : never] : V[Option] };
|
|
56
55
|
/**
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
56
|
+
* Resolve command line arguments.
|
|
57
|
+
* @param options - An options that contains {@link ArgOptionSchema | options schema}.
|
|
58
|
+
* @param tokens - An array of {@link ArgToken | tokens}.
|
|
59
|
+
* @returns An object that contains the values of the arguments, positional arguments, and {@link AggregateError | validation errors}.
|
|
60
|
+
*/
|
|
61
|
+
declare function resolveArgs<T extends ArgOptions>(options: T, tokens: ArgToken[]): {
|
|
62
|
+
values: ArgValues<T>
|
|
63
|
+
positionals: string[]
|
|
64
|
+
error: AggregateError | undefined
|
|
66
65
|
};
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
66
|
+
/**
|
|
67
|
+
* An error that occurs when resolving options.
|
|
68
|
+
* This error is thrown when the option is not valid.
|
|
69
|
+
*/
|
|
70
|
+
declare class OptionResolveError extends Error {
|
|
71
|
+
name: string;
|
|
72
|
+
schema: ArgOptionSchema;
|
|
73
|
+
constructor(message: string, name: string, schema: ArgOptionSchema);
|
|
71
74
|
}
|
|
72
|
-
|
|
75
|
+
|
|
76
|
+
export { OptionResolveError, resolveArgs };
|
|
77
|
+
export type { ArgOptionSchema, ArgOptions, ArgValues };
|
package/lib/resolver.js
CHANGED
|
@@ -1,236 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
* Resolve command line arguments
|
|
6
|
-
* @param options - An options that contains {@link ArgOptionSchema | options schema}.
|
|
7
|
-
* @param tokens - An array of {@link ArgToken | tokens}.
|
|
8
|
-
* @returns An object that contains the values of the arguments, positional arguments, and {@link AggregateError | validation errors}.
|
|
9
|
-
*/
|
|
10
|
-
export function resolveArgs(options, tokens) {
|
|
11
|
-
const positionals = [];
|
|
12
|
-
const longOptionTokens = [];
|
|
13
|
-
const shortOptionTokens = [];
|
|
14
|
-
let currentLongOption;
|
|
15
|
-
let currentShortOption;
|
|
16
|
-
const expandableShortOptions = [];
|
|
17
|
-
function toShortValue() {
|
|
18
|
-
if (expandableShortOptions.length === 0) {
|
|
19
|
-
return undefined;
|
|
20
|
-
}
|
|
21
|
-
else {
|
|
22
|
-
const value = expandableShortOptions.map(token => token.name).join('');
|
|
23
|
-
expandableShortOptions.length = 0;
|
|
24
|
-
return value;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
function applyLongOptionValue(value = undefined) {
|
|
28
|
-
if (currentLongOption) {
|
|
29
|
-
currentLongOption.value = value;
|
|
30
|
-
longOptionTokens.push({ ...currentLongOption });
|
|
31
|
-
currentLongOption = undefined;
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
function applyShortOptionValue(value = undefined) {
|
|
35
|
-
if (currentShortOption) {
|
|
36
|
-
currentShortOption.value = value || toShortValue();
|
|
37
|
-
shortOptionTokens.push({ ...currentShortOption });
|
|
38
|
-
currentShortOption = undefined;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
/**
|
|
42
|
-
* analyze phase to resolve value
|
|
43
|
-
* separate tokens into positionals, long and short options, after that resolve values
|
|
44
|
-
*/
|
|
45
|
-
// eslint-disable-next-line unicorn/no-for-loop
|
|
46
|
-
for (let i = 0; i < tokens.length; i++) {
|
|
47
|
-
const token = tokens[i];
|
|
48
|
-
if (token.kind === 'positional') {
|
|
49
|
-
positionals.push(token.value);
|
|
50
|
-
// check if previous option is not resolved
|
|
51
|
-
applyLongOptionValue(token.value);
|
|
52
|
-
applyShortOptionValue(token.value);
|
|
53
|
-
}
|
|
54
|
-
else if (token.kind === 'option') {
|
|
55
|
-
if (token.rawName) {
|
|
56
|
-
if (hasLongOptionPrefix(token.rawName)) {
|
|
57
|
-
if (token.inlineValue) {
|
|
58
|
-
longOptionTokens.push({ ...token });
|
|
59
|
-
}
|
|
60
|
-
else {
|
|
61
|
-
currentLongOption = { ...token };
|
|
62
|
-
}
|
|
63
|
-
// check if previous short option is not resolved
|
|
64
|
-
applyShortOptionValue();
|
|
65
|
-
}
|
|
66
|
-
else if (isShortOption(token.rawName)) {
|
|
67
|
-
if (currentShortOption) {
|
|
68
|
-
if (currentShortOption.index === token.index) {
|
|
69
|
-
expandableShortOptions.push({ ...token });
|
|
70
|
-
}
|
|
71
|
-
else {
|
|
72
|
-
currentShortOption.value = toShortValue();
|
|
73
|
-
shortOptionTokens.push({ ...currentShortOption });
|
|
74
|
-
currentShortOption = { ...token };
|
|
75
|
-
}
|
|
76
|
-
// check if previous long option is not resolved
|
|
77
|
-
applyLongOptionValue();
|
|
78
|
-
}
|
|
79
|
-
else {
|
|
80
|
-
currentShortOption = { ...token };
|
|
81
|
-
// check if previous long option is not resolved
|
|
82
|
-
applyLongOptionValue();
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
else {
|
|
87
|
-
// short option value
|
|
88
|
-
if (currentShortOption && currentShortOption.index == token.index && token.inlineValue) {
|
|
89
|
-
currentShortOption.value = token.value;
|
|
90
|
-
shortOptionTokens.push({ ...currentShortOption });
|
|
91
|
-
currentShortOption = undefined;
|
|
92
|
-
}
|
|
93
|
-
// check if previous long option is not resolved
|
|
94
|
-
applyLongOptionValue();
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
else {
|
|
98
|
-
// check if previous option is not resolved
|
|
99
|
-
applyLongOptionValue();
|
|
100
|
-
applyShortOptionValue();
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
/**
|
|
104
|
-
* check if the last long or short option is not resolved
|
|
105
|
-
*/
|
|
106
|
-
applyLongOptionValue();
|
|
107
|
-
applyShortOptionValue();
|
|
108
|
-
/**
|
|
109
|
-
* resolve values
|
|
110
|
-
*/
|
|
111
|
-
const values = Object.create(null);
|
|
112
|
-
const errors = [];
|
|
113
|
-
for (const [option, schema] of Object.entries(options)) {
|
|
114
|
-
if (schema.required) {
|
|
115
|
-
const found = longOptionTokens.find(token => token.name === option) ||
|
|
116
|
-
(schema.short && shortOptionTokens.find(token => token.name === schema.short));
|
|
117
|
-
if (!found) {
|
|
118
|
-
errors.push(createRequireError(option, schema));
|
|
119
|
-
continue;
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
// eslint-disable-next-line unicorn/no-for-loop
|
|
123
|
-
for (let i = 0; i < longOptionTokens.length; i++) {
|
|
124
|
-
const token = longOptionTokens[i];
|
|
125
|
-
// eslint-disable-next-line unicorn/no-null
|
|
126
|
-
if (option === token.name && token.rawName != null && hasLongOptionPrefix(token.rawName)) {
|
|
127
|
-
const invalid = validateRequire(token, option, schema);
|
|
128
|
-
if (invalid) {
|
|
129
|
-
errors.push(invalid);
|
|
130
|
-
continue;
|
|
131
|
-
}
|
|
132
|
-
if (schema.type === 'boolean') {
|
|
133
|
-
// NOTE: re-set value to undefined, because long boolean type option is set on analyze phase
|
|
134
|
-
token.value = undefined;
|
|
135
|
-
}
|
|
136
|
-
else {
|
|
137
|
-
const invalid = validateValue(token, option, schema);
|
|
138
|
-
if (invalid) {
|
|
139
|
-
errors.push(invalid);
|
|
140
|
-
continue;
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
144
|
-
;
|
|
145
|
-
values[option] = resolveOptionValue(token, schema);
|
|
146
|
-
continue;
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
// eslint-disable-next-line unicorn/no-for-loop
|
|
150
|
-
for (let i = 0; i < shortOptionTokens.length; i++) {
|
|
151
|
-
const token = shortOptionTokens[i];
|
|
152
|
-
// eslint-disable-next-line unicorn/no-null
|
|
153
|
-
if (schema.short === token.name && token.rawName != null && isShortOption(token.rawName)) {
|
|
154
|
-
const invalid = validateRequire(token, option, schema);
|
|
155
|
-
if (invalid) {
|
|
156
|
-
errors.push(invalid);
|
|
157
|
-
continue;
|
|
158
|
-
}
|
|
159
|
-
if (schema.type === 'boolean') {
|
|
160
|
-
// NOTE: re-set value to undefined, because short boolean type option is set on analyze phase
|
|
161
|
-
token.value = undefined;
|
|
162
|
-
}
|
|
163
|
-
else {
|
|
164
|
-
const invalid = validateValue(token, option, schema);
|
|
165
|
-
if (invalid) {
|
|
166
|
-
errors.push(invalid);
|
|
167
|
-
continue;
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
171
|
-
;
|
|
172
|
-
values[option] = resolveOptionValue(token, schema);
|
|
173
|
-
continue;
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
// eslint-disable-next-line unicorn/no-null
|
|
177
|
-
if (values[option] == null && schema.default != null) {
|
|
178
|
-
// check if the default value is in values
|
|
179
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
180
|
-
;
|
|
181
|
-
values[option] = schema.default;
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
// eslint-disable-next-line unicorn/error-message
|
|
185
|
-
return { values, positionals, error: errors.length > 0 ? new AggregateError(errors) : undefined };
|
|
186
|
-
}
|
|
187
|
-
function createRequireError(option, schema) {
|
|
188
|
-
return new OptionResolveError(`Option '--${option}' ${schema.short ? `or '-${schema.short}' ` : ''}is required`, option, schema);
|
|
189
|
-
}
|
|
190
|
-
export class OptionResolveError extends Error {
|
|
191
|
-
name;
|
|
192
|
-
schema;
|
|
193
|
-
constructor(message, name, schema) {
|
|
194
|
-
super(message);
|
|
195
|
-
this.name = name;
|
|
196
|
-
this.schema = schema;
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
function validateRequire(token, option, schema) {
|
|
200
|
-
if (schema.required && schema.type !== 'boolean' && !token.value) {
|
|
201
|
-
return createRequireError(option, schema);
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
function validateValue(token, option, schema) {
|
|
205
|
-
switch (schema.type) {
|
|
206
|
-
case 'number': {
|
|
207
|
-
if (!isNumeric(token.value)) {
|
|
208
|
-
return createTypeError(option, schema);
|
|
209
|
-
}
|
|
210
|
-
break;
|
|
211
|
-
}
|
|
212
|
-
case 'string': {
|
|
213
|
-
if (typeof token.value !== 'string') {
|
|
214
|
-
return createTypeError(option, schema);
|
|
215
|
-
}
|
|
216
|
-
break;
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
function isNumeric(str) {
|
|
221
|
-
// @ts-ignore
|
|
222
|
-
// eslint-disable-next-line unicorn/prefer-number-properties
|
|
223
|
-
return str.trim() !== '' && !isNaN(str);
|
|
224
|
-
}
|
|
225
|
-
function createTypeError(option, schema) {
|
|
226
|
-
return new OptionResolveError(`Option '--${option}' ${schema.short ? `or '-${schema.short}' ` : ''}should be '${schema.type}'`, option, schema);
|
|
227
|
-
}
|
|
228
|
-
function resolveOptionValue(token, schema) {
|
|
229
|
-
if (token.value) {
|
|
230
|
-
return schema.type === 'number' ? +token.value : token.value;
|
|
231
|
-
}
|
|
232
|
-
if (schema.type === 'boolean') {
|
|
233
|
-
return true;
|
|
234
|
-
}
|
|
235
|
-
return schema.type === 'number' ? +(schema.default || '') : schema.default;
|
|
236
|
-
}
|
|
1
|
+
import "./parser-DxH6Mf-o.js";
|
|
2
|
+
import { OptionResolveError, resolveArgs } from "./resolver-Di82Qgwx.js";
|
|
3
|
+
|
|
4
|
+
export { OptionResolveError, resolveArgs };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "args-tokens",
|
|
3
3
|
"description": "parseArgs tokens compatibility and more high-performance parser",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.13.3",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "kazuya kawaguchi",
|
|
7
7
|
"email": "kawakazu80@gmail.com"
|
|
@@ -66,11 +66,12 @@
|
|
|
66
66
|
},
|
|
67
67
|
"devDependencies": {
|
|
68
68
|
"@eslint/markdown": "^6.3.0",
|
|
69
|
-
"@kazupon/eslint-config": "^0.26.
|
|
69
|
+
"@kazupon/eslint-config": "^0.26.1",
|
|
70
70
|
"@kazupon/prettier-config": "^0.1.1",
|
|
71
71
|
"@types/node": "^22.13.14",
|
|
72
72
|
"@vitest/eslint-plugin": "^1.1.38",
|
|
73
73
|
"bumpp": "^10.1.0",
|
|
74
|
+
"deno": "^2.2.6",
|
|
74
75
|
"eslint": "^9.23.0",
|
|
75
76
|
"eslint-config-prettier": "^10.1.1",
|
|
76
77
|
"eslint-plugin-jsonc": "^2.20.0",
|
|
@@ -80,11 +81,12 @@
|
|
|
80
81
|
"eslint-plugin-yml": "^1.17.0",
|
|
81
82
|
"gh-changelogen": "^0.2.8",
|
|
82
83
|
"jsr": "^0.13.4",
|
|
83
|
-
"knip": "^5.46.
|
|
84
|
+
"knip": "^5.46.3",
|
|
84
85
|
"lint-staged": "^15.5.0",
|
|
85
86
|
"mitata": "^1.0.34",
|
|
86
87
|
"pkg-pr-new": "^0.0.41",
|
|
87
88
|
"prettier": "^3.5.3",
|
|
89
|
+
"tsdown": "^0.6.10",
|
|
88
90
|
"typescript": "^5.8.2",
|
|
89
91
|
"typescript-eslint": "^8.28.0",
|
|
90
92
|
"vitest": "^3.0.9"
|
|
@@ -106,7 +108,7 @@
|
|
|
106
108
|
"scripts": {
|
|
107
109
|
"bench:mitata": "node --expose-gc bench/mitata.js",
|
|
108
110
|
"bench:vitest": "vitest bench --run",
|
|
109
|
-
"build": "
|
|
111
|
+
"build": "tsdown",
|
|
110
112
|
"changelog": "gh-changelogen --repo=kazupon/eslint-config",
|
|
111
113
|
"dev": "pnpx @eslint/config-inspector --config eslint.config.ts",
|
|
112
114
|
"dev:eslint": "pnpx @eslint/config-inspector --config eslint.config.ts",
|
|
@@ -121,6 +123,8 @@
|
|
|
121
123
|
"lint:prettier": "prettier . --check",
|
|
122
124
|
"release": "bumpp --commit \"release: v%s\" --all --push --tag",
|
|
123
125
|
"test": "vitest run",
|
|
124
|
-
"typecheck": "
|
|
126
|
+
"typecheck": "pnpm run --parallel --color \"/^typecheck:/\"",
|
|
127
|
+
"typecheck:deno": "deno check src",
|
|
128
|
+
"typecheck:tsc": "tsc --noEmit"
|
|
125
129
|
}
|
|
126
130
|
}
|
package/lib/parse.d.ts
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import type { ParserOptions } from './parser';
|
|
2
|
-
import type { ArgOptions, ArgValues } from './resolver';
|
|
3
|
-
/**
|
|
4
|
-
* Parse options for {@link parse} function
|
|
5
|
-
*/
|
|
6
|
-
export interface ParseOptions<O extends ArgOptions> extends ParserOptions {
|
|
7
|
-
/**
|
|
8
|
-
* Command line options, about details see {@link ArgOptions}
|
|
9
|
-
*/
|
|
10
|
-
options?: O;
|
|
11
|
-
}
|
|
12
|
-
/**
|
|
13
|
-
* Parsed command line arguments
|
|
14
|
-
*/
|
|
15
|
-
export type ParsedArgs<T extends ArgOptions> = {
|
|
16
|
-
/**
|
|
17
|
-
* Parsed values, same as `values` in {@link resolveArgs}
|
|
18
|
-
*/
|
|
19
|
-
values: ArgValues<T>;
|
|
20
|
-
/**
|
|
21
|
-
* Positional arguments, same as `positionals` in {@link resolveArgs}
|
|
22
|
-
*/
|
|
23
|
-
positionals: string[];
|
|
24
|
-
/**
|
|
25
|
-
* Validation errors, same as `errors` in {@link resolveArgs}
|
|
26
|
-
*/
|
|
27
|
-
error: AggregateError | undefined;
|
|
28
|
-
};
|
|
29
|
-
/**
|
|
30
|
-
* Parse command line arguments
|
|
31
|
-
* @description This function is a convenient API, that is used {@link parseArgs} and {@link resolveArgs} in internal.
|
|
32
|
-
* @example
|
|
33
|
-
* ```js
|
|
34
|
-
* import { parse } from 'args-tokens'
|
|
35
|
-
*
|
|
36
|
-
* const { values, positionals } = parse(process.argv.slice(2))
|
|
37
|
-
* console.log('values', values)
|
|
38
|
-
* console.log('positionals', positionals)
|
|
39
|
-
* ```
|
|
40
|
-
* @param args - command line arguments
|
|
41
|
-
* @param options - parse options, about details see {@link ParseOptions}
|
|
42
|
-
* @returns An object that contains the values of the arguments, positional arguments, and {@link AggregateError | validation errors}.
|
|
43
|
-
*/
|
|
44
|
-
export declare function parse<O extends ArgOptions>(args: string[], options?: ParseOptions<O>): ParsedArgs<O>;
|
package/lib/parse.js
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
// SPDX-License-Identifier: MIT
|
|
2
|
-
// Modifier: kazuya kawaguchi (a.k.a. kazupon)
|
|
3
|
-
import { parseArgs } from './parser.js';
|
|
4
|
-
import { resolveArgs } from './resolver.js';
|
|
5
|
-
const DEFAULT_OPTIONS = {
|
|
6
|
-
help: {
|
|
7
|
-
type: 'boolean',
|
|
8
|
-
short: 'h'
|
|
9
|
-
},
|
|
10
|
-
version: {
|
|
11
|
-
type: 'boolean',
|
|
12
|
-
short: 'v'
|
|
13
|
-
}
|
|
14
|
-
};
|
|
15
|
-
/**
|
|
16
|
-
* Parse command line arguments
|
|
17
|
-
* @description This function is a convenient API, that is used {@link parseArgs} and {@link resolveArgs} in internal.
|
|
18
|
-
* @example
|
|
19
|
-
* ```js
|
|
20
|
-
* import { parse } from 'args-tokens'
|
|
21
|
-
*
|
|
22
|
-
* const { values, positionals } = parse(process.argv.slice(2))
|
|
23
|
-
* console.log('values', values)
|
|
24
|
-
* console.log('positionals', positionals)
|
|
25
|
-
* ```
|
|
26
|
-
* @param args - command line arguments
|
|
27
|
-
* @param options - parse options, about details see {@link ParseOptions}
|
|
28
|
-
* @returns An object that contains the values of the arguments, positional arguments, and {@link AggregateError | validation errors}.
|
|
29
|
-
*/
|
|
30
|
-
export function parse(args, options = {}) {
|
|
31
|
-
const { options: argOptions, allowCompatible = false } = options;
|
|
32
|
-
const tokens = parseArgs(args, { allowCompatible });
|
|
33
|
-
return resolveArgs(argOptions || DEFAULT_OPTIONS, tokens);
|
|
34
|
-
}
|