cli-kiss 0.2.1 → 0.2.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 +1 -2
- package/dist/index.d.ts +527 -830
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/docs/.vitepress/config.mts +3 -0
- package/docs/guide/01_getting_started.md +9 -9
- package/docs/guide/02_commands.md +6 -11
- package/docs/guide/03_options.md +4 -6
- package/docs/guide/04_positionals.md +6 -8
- package/docs/guide/05_types.md +8 -10
- package/docs/guide/06_run.md +6 -7
- package/docs/index.md +4 -4
- package/package.json +1 -1
- package/src/lib/Command.ts +189 -256
- package/src/lib/Operation.ts +68 -82
- package/src/lib/Option.ts +115 -132
- package/src/lib/Positional.ts +95 -100
- package/src/lib/Reader.ts +53 -78
- package/src/lib/Run.ts +40 -58
- package/src/lib/Type.ts +81 -141
- package/src/lib/Typo.ts +121 -164
- package/src/lib/Usage.ts +56 -18
- package/tests/unit.command.execute.ts +83 -71
- package/tests/unit.command.usage.ts +230 -109
- package/tests/unit.runner.cycle.ts +56 -7
package/src/lib/Positional.ts
CHANGED
|
@@ -9,28 +9,40 @@ import {
|
|
|
9
9
|
} from "./Typo";
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
|
-
*
|
|
13
|
-
* line — together with its parsing and usage-generation logic.
|
|
12
|
+
* A bare (non-option) positional argument with its parsing and usage-generation logic.
|
|
14
13
|
*
|
|
15
|
-
*
|
|
16
|
-
* {@link positionalVariadics} and
|
|
17
|
-
* {@link operation},
|
|
14
|
+
* Created with {@link positionalRequired}, {@link positionalOptional}, or
|
|
15
|
+
* {@link positionalVariadics} and passed via the `positionals` array of
|
|
16
|
+
* {@link operation}, consumed in declaration order.
|
|
18
17
|
*
|
|
19
|
-
* @typeParam Value -
|
|
18
|
+
* @typeParam Value - Decoded value type.
|
|
20
19
|
*/
|
|
21
20
|
export type Positional<Value> = {
|
|
22
|
-
/**
|
|
21
|
+
/**
|
|
22
|
+
* Returns metadata used to render the `Positionals:` section of help.
|
|
23
|
+
*/
|
|
23
24
|
generateUsage(): PositionalUsage;
|
|
24
25
|
/**
|
|
25
|
-
* Consumes the next positional token
|
|
26
|
-
*
|
|
26
|
+
* Consumes the next positional token from `readerPositionals`.
|
|
27
|
+
* Returns a decoder that produces the final value.
|
|
28
|
+
*/
|
|
29
|
+
consumeAndMakeDecoder(
|
|
30
|
+
readerPositionals: ReaderPositionals,
|
|
31
|
+
): PositionalDecoder<Value>;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Produced by {@link Positional.consumeAndMakeDecoder}.
|
|
36
|
+
*
|
|
37
|
+
* @typeParam Value - Decoded value type.
|
|
38
|
+
*/
|
|
39
|
+
export type PositionalDecoder<Value> = {
|
|
40
|
+
/**
|
|
41
|
+
* Returns the decoded positional value.
|
|
27
42
|
*
|
|
28
|
-
* @
|
|
29
|
-
* remaining positional tokens.
|
|
30
|
-
* @throws {@link TypoError} if the positional is required but absent, or if the raw
|
|
31
|
-
* value fails type decoding.
|
|
43
|
+
* @throws {@link TypoError} if decoding failed.
|
|
32
44
|
*/
|
|
33
|
-
|
|
45
|
+
decodeValue(): Value;
|
|
34
46
|
};
|
|
35
47
|
|
|
36
48
|
/**
|
|
@@ -38,40 +50,32 @@ export type Positional<Value> = {
|
|
|
38
50
|
* `Positionals:` section of the help output produced by {@link usageToStyledLines}.
|
|
39
51
|
*/
|
|
40
52
|
export type PositionalUsage = {
|
|
41
|
-
/**
|
|
53
|
+
/**
|
|
54
|
+
* Help text.
|
|
55
|
+
*/
|
|
42
56
|
description: string | undefined;
|
|
43
57
|
/**
|
|
44
|
-
*
|
|
45
|
-
* Suitable for short caveats such as `"defaults to 'world'"`.
|
|
58
|
+
* Short note shown in parentheses.
|
|
46
59
|
*/
|
|
47
60
|
hint: string | undefined;
|
|
48
61
|
/**
|
|
49
|
-
*
|
|
50
|
-
* Required
|
|
51
|
-
* use square-bracket notation (e.g. `"[FILE]"`); variadic ones append `...`
|
|
52
|
-
* (e.g. `"[ITEM]..."`).
|
|
62
|
+
* Placeholder label shown in the usage line and the `Positionals:` section.
|
|
63
|
+
* Required: `<NAME>`, optional: `[NAME]`, variadic: `[NAME]...`.
|
|
53
64
|
*/
|
|
54
65
|
label: Uppercase<string>;
|
|
55
66
|
};
|
|
56
67
|
|
|
57
68
|
/**
|
|
58
|
-
* Creates a required positional
|
|
69
|
+
* Creates a required positional — missing token throws {@link TypoError}.
|
|
70
|
+
* Label defaults to uppercased `type.content` in angle brackets (e.g. `<STRING>`).
|
|
59
71
|
*
|
|
60
|
-
*
|
|
61
|
-
* `definition.type`. If no token is available, a {@link TypoError} is thrown immediately
|
|
62
|
-
* during parsing (i.e. inside {@link OperationDescriptor.createFactory}).
|
|
72
|
+
* @typeParam Value - Type produced by the decoder.
|
|
63
73
|
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
* @
|
|
68
|
-
*
|
|
69
|
-
* @param definition - Configuration for the positional.
|
|
70
|
-
* @param definition.description - Human-readable description for the help output.
|
|
71
|
-
* @param definition.hint - Optional supplementary note shown in parentheses.
|
|
72
|
-
* @param definition.label - Custom label shown in the usage line (without angle brackets).
|
|
73
|
-
* Defaults to the uppercased `type.content`.
|
|
74
|
-
* @param definition.type - The {@link Type} used to decode the raw string token.
|
|
74
|
+
* @param definition - Positional configuration.
|
|
75
|
+
* @param definition.description - Help text.
|
|
76
|
+
* @param definition.hint - Short note shown in parentheses.
|
|
77
|
+
* @param definition.label - Label without brackets; defaults to uppercased `type.content`.
|
|
78
|
+
* @param definition.type - Decoder for the raw token.
|
|
75
79
|
* @returns A {@link Positional}`<Value>`.
|
|
76
80
|
*
|
|
77
81
|
* @example
|
|
@@ -99,7 +103,7 @@ export function positionalRequired<Value>(definition: {
|
|
|
99
103
|
label: label as Uppercase<string>,
|
|
100
104
|
};
|
|
101
105
|
},
|
|
102
|
-
|
|
106
|
+
consumeAndMakeDecoder(readerPositionals: ReaderPositionals) {
|
|
103
107
|
const positional = readerPositionals.consumePositional();
|
|
104
108
|
if (positional === undefined) {
|
|
105
109
|
throw new TypoError(
|
|
@@ -109,32 +113,27 @@ export function positionalRequired<Value>(definition: {
|
|
|
109
113
|
),
|
|
110
114
|
);
|
|
111
115
|
}
|
|
112
|
-
return
|
|
116
|
+
return {
|
|
117
|
+
decodeValue() {
|
|
118
|
+
return decodeValue(label, definition.type, positional);
|
|
119
|
+
},
|
|
120
|
+
};
|
|
113
121
|
},
|
|
114
122
|
};
|
|
115
123
|
}
|
|
116
124
|
|
|
117
125
|
/**
|
|
118
|
-
* Creates an optional positional
|
|
119
|
-
*
|
|
120
|
-
*
|
|
121
|
-
* The parser consumes the next available positional token. If no token is available,
|
|
122
|
-
* `definition.default()` is called to supply the fallback value. If the default factory
|
|
123
|
-
* throws, a {@link TypoError} is produced.
|
|
126
|
+
* Creates an optional positional — absent token falls back to `default()`.
|
|
127
|
+
* Label defaults to uppercased `type.content` in square brackets (e.g. `[STRING]`).
|
|
124
128
|
*
|
|
125
|
-
*
|
|
126
|
-
* wrapped in square brackets (e.g. `[STRING]`). Supply `label` to override.
|
|
129
|
+
* @typeParam Value - Type produced by the decoder (or the default).
|
|
127
130
|
*
|
|
128
|
-
* @
|
|
129
|
-
*
|
|
130
|
-
* @param definition -
|
|
131
|
-
* @param definition.
|
|
132
|
-
* @param definition.
|
|
133
|
-
* @param definition.
|
|
134
|
-
* Defaults to the uppercased `type.content`.
|
|
135
|
-
* @param definition.type - The {@link Type} used to decode the raw string token.
|
|
136
|
-
* @param definition.default - Factory called when the positional is absent to supply the
|
|
137
|
-
* default value. Throw from this factory to make omission an error.
|
|
131
|
+
* @param definition - Positional configuration.
|
|
132
|
+
* @param definition.description - Help text.
|
|
133
|
+
* @param definition.hint - Short note shown in parentheses.
|
|
134
|
+
* @param definition.label - Label without brackets; defaults to uppercased `type.content`.
|
|
135
|
+
* @param definition.type - Decoder for the raw token.
|
|
136
|
+
* @param definition.default - Value when absent. Throw to make it required.
|
|
138
137
|
* @returns A {@link Positional}`<Value>`.
|
|
139
138
|
*
|
|
140
139
|
* @example
|
|
@@ -165,52 +164,42 @@ export function positionalOptional<Value>(definition: {
|
|
|
165
164
|
label: label as Uppercase<string>,
|
|
166
165
|
};
|
|
167
166
|
},
|
|
168
|
-
|
|
167
|
+
consumeAndMakeDecoder(readerPositionals: ReaderPositionals) {
|
|
169
168
|
const positional = readerPositionals.consumePositional();
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
new
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
169
|
+
return {
|
|
170
|
+
decodeValue() {
|
|
171
|
+
if (positional === undefined) {
|
|
172
|
+
try {
|
|
173
|
+
return definition.default();
|
|
174
|
+
} catch (error) {
|
|
175
|
+
throw new TypoError(
|
|
176
|
+
new TypoText(
|
|
177
|
+
new TypoString(label, typoStyleUserInput),
|
|
178
|
+
new TypoString(`: Failed to get default value`),
|
|
179
|
+
),
|
|
180
|
+
error,
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
return decodeValue(label, definition.type, positional);
|
|
185
|
+
},
|
|
186
|
+
};
|
|
184
187
|
},
|
|
185
188
|
};
|
|
186
189
|
}
|
|
187
190
|
|
|
188
191
|
/**
|
|
189
|
-
* Creates a variadic positional
|
|
190
|
-
*
|
|
191
|
-
*
|
|
192
|
-
* The parser greedily consumes tokens until either there are no more tokens or it
|
|
193
|
-
* encounters the optional `endDelimiter` sentinel string, which is consumed but not
|
|
194
|
-
* included in the result. Each token is decoded independently with `definition.type`.
|
|
195
|
-
*
|
|
196
|
-
* If absent entirely, the result is an empty array `[]`.
|
|
192
|
+
* Creates a variadic positional that collects zero or more remaining tokens into an array.
|
|
193
|
+
* Stops at `endDelimiter` (consumed, not included). Label: `[TYPE]...` notation.
|
|
197
194
|
*
|
|
198
|
-
*
|
|
199
|
-
* wrapped in square brackets followed by `...` (e.g. `[STRING]...`). When an
|
|
200
|
-
* `endDelimiter` is configured, the delimiter is also shown (e.g. `[STRING]...["--"]`).
|
|
201
|
-
* Supply `label` to override the base label.
|
|
195
|
+
* @typeParam Value - Type produced by the decoder for each token.
|
|
202
196
|
*
|
|
203
|
-
* @
|
|
204
|
-
*
|
|
205
|
-
* @param definition -
|
|
206
|
-
* @param definition.
|
|
207
|
-
*
|
|
208
|
-
*
|
|
209
|
-
* @param definition.description - Human-readable description for the help output.
|
|
210
|
-
* @param definition.hint - Optional supplementary note shown in parentheses.
|
|
211
|
-
* @param definition.label - Custom label shown in the usage line (without brackets).
|
|
212
|
-
* Defaults to the uppercased `type.content`.
|
|
213
|
-
* @param definition.type - The {@link Type} used to decode each raw string token.
|
|
197
|
+
* @param definition - Positional configuration.
|
|
198
|
+
* @param definition.endDelimiter - Sentinel token that stops collection (consumed, not included).
|
|
199
|
+
* @param definition.description - Help text.
|
|
200
|
+
* @param definition.hint - Short note shown in parentheses.
|
|
201
|
+
* @param definition.label - Label without brackets; defaults to uppercased `type.content`.
|
|
202
|
+
* @param definition.type - Decoder applied to each token.
|
|
214
203
|
* @returns A {@link Positional}`<Array<Value>>`.
|
|
215
204
|
*
|
|
216
205
|
* @example
|
|
@@ -243,8 +232,8 @@ export function positionalVariadics<Value>(definition: {
|
|
|
243
232
|
: "")) as Uppercase<string>,
|
|
244
233
|
};
|
|
245
234
|
},
|
|
246
|
-
|
|
247
|
-
const positionals
|
|
235
|
+
consumeAndMakeDecoder(readerPositionals: ReaderPositionals) {
|
|
236
|
+
const positionals = new Array<string>();
|
|
248
237
|
while (true) {
|
|
249
238
|
const positional = readerPositionals.consumePositional();
|
|
250
239
|
if (
|
|
@@ -253,9 +242,15 @@ export function positionalVariadics<Value>(definition: {
|
|
|
253
242
|
) {
|
|
254
243
|
break;
|
|
255
244
|
}
|
|
256
|
-
positionals.push(
|
|
245
|
+
positionals.push(positional);
|
|
257
246
|
}
|
|
258
|
-
return
|
|
247
|
+
return {
|
|
248
|
+
decodeValue() {
|
|
249
|
+
return positionals.map((positional) => {
|
|
250
|
+
return decodeValue(label, definition.type, positional);
|
|
251
|
+
});
|
|
252
|
+
},
|
|
253
|
+
};
|
|
259
254
|
},
|
|
260
255
|
};
|
|
261
256
|
}
|
|
@@ -263,10 +258,10 @@ export function positionalVariadics<Value>(definition: {
|
|
|
263
258
|
function decodeValue<Value>(
|
|
264
259
|
label: string,
|
|
265
260
|
type: Type<Value>,
|
|
266
|
-
|
|
261
|
+
input: string,
|
|
267
262
|
): Value {
|
|
268
263
|
return TypoError.tryWithContext(
|
|
269
|
-
() => type.decoder(
|
|
264
|
+
() => type.decoder(input),
|
|
270
265
|
() =>
|
|
271
266
|
new TypoText(
|
|
272
267
|
new TypoString(label, typoStyleUserInput),
|
package/src/lib/Reader.ts
CHANGED
|
@@ -7,35 +7,26 @@ import {
|
|
|
7
7
|
} from "./Typo";
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
|
-
*
|
|
11
|
-
* {@link ReaderArgs}
|
|
12
|
-
*
|
|
13
|
-
* Keys are returned by {@link ReaderArgs.registerOption} and passed back to
|
|
14
|
-
* {@link ReaderArgs.getOptionValues} to retrieve the parsed values. The internal
|
|
15
|
-
* representation is intentionally opaque — treat it as a handle, not a string.
|
|
10
|
+
* Opaque key identifying a registered option within a {@link ReaderArgs} instance.
|
|
11
|
+
* Returned by {@link ReaderArgs.registerOption}; passed to {@link ReaderArgs.getOptionValues}.
|
|
16
12
|
*/
|
|
17
13
|
export type ReaderOptionKey = (string | { __brand: "ReaderOptionKey" }) & {
|
|
18
14
|
__brand: "ReaderOptionKey";
|
|
19
15
|
};
|
|
20
16
|
|
|
21
17
|
/**
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
* {@link ReaderArgs} implements both `ReaderOptions` and {@link ReaderPositionals}.
|
|
25
|
-
* The two interfaces are exposed separately so that option and positional parsing logic
|
|
26
|
-
* can depend only on the capability they need.
|
|
18
|
+
* Option registration and query interface, implemented by {@link ReaderArgs}.
|
|
19
|
+
* Exposed separately from {@link ReaderPositionals} so parsers depend only on what they need.
|
|
27
20
|
*/
|
|
28
21
|
export type ReaderOptions = {
|
|
29
22
|
/**
|
|
30
|
-
* Registers
|
|
23
|
+
* Registers an option so the parser can recognise it.
|
|
31
24
|
*
|
|
32
|
-
* @param definition.longs -
|
|
33
|
-
* @param definition.shorts -
|
|
34
|
-
* @param definition.valued -
|
|
35
|
-
*
|
|
36
|
-
* @
|
|
37
|
-
* @throws `Error` if any of the given names has already been registered, or if a
|
|
38
|
-
* short name overlaps (is a prefix of, or has as a prefix, another registered short).
|
|
25
|
+
* @param definition.longs - Long-form names (without `--`).
|
|
26
|
+
* @param definition.shorts - Short-form names (without `-`).
|
|
27
|
+
* @param definition.valued - `true` if the option takes a value; `false` for flags.
|
|
28
|
+
* @returns A {@link ReaderOptionKey} for later retrieval.
|
|
29
|
+
* @throws `Error` if a name is already registered or short names overlap.
|
|
39
30
|
*/
|
|
40
31
|
registerOption(definition: {
|
|
41
32
|
longs: Array<string>;
|
|
@@ -45,48 +36,34 @@ export type ReaderOptions = {
|
|
|
45
36
|
/**
|
|
46
37
|
* Returns all values collected for the option identified by `key`.
|
|
47
38
|
*
|
|
48
|
-
* @param key -
|
|
49
|
-
* @returns
|
|
50
|
-
*
|
|
51
|
-
* @throws `Error` if `key` was not previously registered on this instance.
|
|
39
|
+
* @param key - Key from {@link ReaderOptions.registerOption}.
|
|
40
|
+
* @returns Raw string values, one per occurrence; empty if never provided.
|
|
41
|
+
* @throws `Error` if `key` was not registered.
|
|
52
42
|
*/
|
|
53
43
|
getOptionValues(key: ReaderOptionKey): Array<string>;
|
|
54
44
|
};
|
|
55
45
|
|
|
56
46
|
/**
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
* {@link ReaderArgs} implements both {@link ReaderOptions} and `ReaderPositionals`.
|
|
47
|
+
* Positional token consumption interface, implemented by {@link ReaderArgs}.
|
|
60
48
|
*/
|
|
61
49
|
export type ReaderPositionals = {
|
|
62
50
|
/**
|
|
63
|
-
*
|
|
64
|
-
* any option tokens (which are parsed as side-effects).
|
|
51
|
+
* Returns the next positional token, parsing intervening options as side-effects.
|
|
65
52
|
*
|
|
66
|
-
* @returns The next positional
|
|
67
|
-
*
|
|
68
|
-
* @throws {@link TypoError} if an unrecognised option token is encountered while
|
|
69
|
-
* scanning for the next positional.
|
|
53
|
+
* @returns The next positional, or `undefined` when exhausted.
|
|
54
|
+
* @throws {@link TypoError} on an unrecognised option.
|
|
70
55
|
*/
|
|
71
56
|
consumePositional(): string | undefined;
|
|
72
57
|
};
|
|
73
58
|
|
|
74
59
|
/**
|
|
75
|
-
*
|
|
76
|
-
*
|
|
60
|
+
* Core argument parser: converts raw CLI tokens into named options and positionals.
|
|
61
|
+
* Options must be registered before {@link ReaderArgs.consumePositional} is called.
|
|
77
62
|
*
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
* whether each token is an option name, an option value, or a bare positional.
|
|
63
|
+
* Supported syntax: `--name`, `--name value`, `--name=value`,
|
|
64
|
+
* `-n`, `-n value`, `-nvalue`, `-abc` (stacked), `--` (end-of-options).
|
|
81
65
|
*
|
|
82
|
-
*
|
|
83
|
-
* - Long options: `--name`, `--name value`, `--name=value`
|
|
84
|
-
* - Short options: `-n`, `-n value`, `-n=value`, `-nvalue`, `-abc` (stacked flags)
|
|
85
|
-
* - End-of-options separator: `--` — all subsequent tokens are treated as positionals.
|
|
86
|
-
*
|
|
87
|
-
* In most cases you do not need to use `ReaderArgs` directly; it is created internally
|
|
88
|
-
* by {@link runAndExit}. It is exposed for advanced use cases such as building
|
|
89
|
-
* custom runners.
|
|
66
|
+
* Created internally by {@link runAndExit}; exposed for advanced / custom runners.
|
|
90
67
|
*/
|
|
91
68
|
export class ReaderArgs {
|
|
92
69
|
#args: ReadonlyArray<string>;
|
|
@@ -98,8 +75,7 @@ export class ReaderArgs {
|
|
|
98
75
|
#resultByKey: Map<ReaderOptionKey, Array<string>>;
|
|
99
76
|
|
|
100
77
|
/**
|
|
101
|
-
* @param args -
|
|
102
|
-
* The array is not modified; a read cursor is maintained internally.
|
|
78
|
+
* @param args - Raw CLI tokens (e.g. `process.argv.slice(2)`). Not mutated.
|
|
103
79
|
*/
|
|
104
80
|
constructor(args: ReadonlyArray<string>) {
|
|
105
81
|
this.#args = args;
|
|
@@ -112,22 +88,15 @@ export class ReaderArgs {
|
|
|
112
88
|
}
|
|
113
89
|
|
|
114
90
|
/**
|
|
115
|
-
* Registers
|
|
116
|
-
*
|
|
117
|
-
*
|
|
118
|
-
* `getOptionValues(key)` after parsing will return values collected under any of the
|
|
119
|
-
* registered names.
|
|
120
|
-
*
|
|
121
|
-
* Short names support stacking (e.g. `-abc` is parsed as `-a -b -c`) and inline
|
|
122
|
-
* values (e.g. `-nvalue`). Short names must not be a prefix of, nor have as a prefix,
|
|
123
|
-
* any other registered short name — the parser uses prefix matching to parse stacked
|
|
124
|
-
* shorts, so overlapping prefixes would be ambiguous.
|
|
91
|
+
* Registers an option; all `longs` and `shorts` share the same key.
|
|
92
|
+
* Short names support stacking (e.g. `-abc`) and inline values (e.g. `-nvalue`),
|
|
93
|
+
* but must not be prefixes of one another.
|
|
125
94
|
*
|
|
126
95
|
* @param definition.longs - Long-form names (without `--`).
|
|
127
96
|
* @param definition.shorts - Short-form names (without `-`).
|
|
128
|
-
* @param definition.valued - `true` if the option
|
|
129
|
-
* @returns
|
|
130
|
-
* @throws `Error` if any name is already registered or
|
|
97
|
+
* @param definition.valued - `true` if the option takes a value; `false` for flags.
|
|
98
|
+
* @returns A {@link ReaderOptionKey} for {@link ReaderArgs.getOptionValues}.
|
|
99
|
+
* @throws `Error` if any name is already registered or short names overlap.
|
|
131
100
|
*/
|
|
132
101
|
registerOption(definition: {
|
|
133
102
|
longs: Array<string>;
|
|
@@ -139,12 +108,17 @@ export class ReaderArgs {
|
|
|
139
108
|
...definition.shorts.map((short) => `-${short}`),
|
|
140
109
|
].join(", ") as ReaderOptionKey;
|
|
141
110
|
for (const long of definition.longs) {
|
|
111
|
+
if (!this.#isValidOptionName(long)) {
|
|
112
|
+
throw new Error(`Invalid option name: --${long}`);
|
|
113
|
+
}
|
|
142
114
|
if (this.#keyByLong.has(long)) {
|
|
143
115
|
throw new Error(`Option already registered: --${long}`);
|
|
144
116
|
}
|
|
145
|
-
this.#keyByLong.set(long, key);
|
|
146
117
|
}
|
|
147
118
|
for (const short of definition.shorts) {
|
|
119
|
+
if (!this.#isValidOptionName(short)) {
|
|
120
|
+
throw new Error(`Invalid option name: -${short}`);
|
|
121
|
+
}
|
|
148
122
|
if (this.#keyByShort.has(short)) {
|
|
149
123
|
throw new Error(`Option already registered: -${short}`);
|
|
150
124
|
}
|
|
@@ -163,6 +137,11 @@ export class ReaderArgs {
|
|
|
163
137
|
);
|
|
164
138
|
}
|
|
165
139
|
}
|
|
140
|
+
}
|
|
141
|
+
for (const long of definition.longs) {
|
|
142
|
+
this.#keyByLong.set(long, key);
|
|
143
|
+
}
|
|
144
|
+
for (const short of definition.shorts) {
|
|
166
145
|
this.#keyByShort.set(short, key);
|
|
167
146
|
}
|
|
168
147
|
this.#valuedByKey.set(key, definition.valued);
|
|
@@ -171,13 +150,11 @@ export class ReaderArgs {
|
|
|
171
150
|
}
|
|
172
151
|
|
|
173
152
|
/**
|
|
174
|
-
* Returns all
|
|
153
|
+
* Returns all values collected for the option key.
|
|
175
154
|
*
|
|
176
|
-
* @param key -
|
|
177
|
-
* @returns
|
|
178
|
-
*
|
|
179
|
-
* literal value strings.
|
|
180
|
-
* @throws `Error` if `key` was not registered on this instance.
|
|
155
|
+
* @param key - Key from {@link ReaderArgs.registerOption}.
|
|
156
|
+
* @returns String values, one per occurrence.
|
|
157
|
+
* @throws `Error` if `key` was not registered.
|
|
181
158
|
*/
|
|
182
159
|
getOptionValues(key: ReaderOptionKey): Array<string> {
|
|
183
160
|
const optionResult = this.#resultByKey.get(key);
|
|
@@ -188,18 +165,12 @@ export class ReaderArgs {
|
|
|
188
165
|
}
|
|
189
166
|
|
|
190
167
|
/**
|
|
191
|
-
*
|
|
192
|
-
*
|
|
193
|
-
*
|
|
194
|
-
* Option tokens encountered during the scan are recorded in the internal results map
|
|
195
|
-
* (equivalent to recording their values against their key). Any unrecognised option token
|
|
196
|
-
* causes a {@link TypoError} to be thrown immediately.
|
|
197
|
-
*
|
|
198
|
-
* After `--` is encountered, all remaining tokens are treated as positionals.
|
|
168
|
+
* Returns the next bare positional token.
|
|
169
|
+
* Parse intervening options as side-effects.
|
|
170
|
+
* All tokens after `--` are treated as positionals.
|
|
199
171
|
*
|
|
200
|
-
* @returns The next positional
|
|
201
|
-
*
|
|
202
|
-
* @throws {@link TypoError} if an unrecognised option (long or short) is encountered.
|
|
172
|
+
* @returns The next positional, or `undefined` when exhausted.
|
|
173
|
+
* @throws {@link TypoError} on an unrecognised option.
|
|
203
174
|
*/
|
|
204
175
|
consumePositional(): string | undefined {
|
|
205
176
|
while (true) {
|
|
@@ -348,4 +319,8 @@ export class ReaderArgs {
|
|
|
348
319
|
#acknowledgeOption(key: ReaderOptionKey, value: string) {
|
|
349
320
|
this.getOptionValues(key).push(value);
|
|
350
321
|
}
|
|
322
|
+
|
|
323
|
+
#isValidOptionName(name: string): boolean {
|
|
324
|
+
return name.length > 0 && !name.includes("=");
|
|
325
|
+
}
|
|
351
326
|
}
|