cli-kiss 0.2.8 → 0.2.9
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 +8 -3
- package/dist/index.d.ts +89 -69
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/docs/guide/01_getting_started.md +2 -2
- package/docs/guide/02_commands.md +2 -2
- package/docs/guide/04_positionals.md +9 -9
- package/docs/guide/05_input_types.md +8 -6
- package/package.json +1 -1
- package/src/lib/Command.ts +8 -3
- package/src/lib/Operation.ts +13 -5
- package/src/lib/Option.ts +22 -20
- package/src/lib/Positional.ts +17 -12
- package/src/lib/Reader.ts +14 -20
- package/src/lib/Run.ts +2 -2
- package/src/lib/Type.ts +145 -120
- package/src/lib/Typo.ts +1 -1
- package/tests/unit.Reader.commons.ts +29 -42
- package/tests/unit.Reader.parsings.ts +3 -3
- package/tests/unit.Reader.shortBig.ts +29 -40
- package/tests/unit.command.aliases.ts +10 -22
- package/tests/unit.command.execute.ts +5 -5
- package/tests/unit.command.usage.ts +8 -8
- package/tests/unit.runner.cycle.ts +31 -18
package/src/lib/Type.ts
CHANGED
|
@@ -12,9 +12,20 @@ import {
|
|
|
12
12
|
* Decodes a raw CLI string into a typed value.
|
|
13
13
|
* A pair of a human-readable `content` name and a `decoder` function.
|
|
14
14
|
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
15
|
+
* Primitive Types:
|
|
16
|
+
* - {@link typeString}
|
|
17
|
+
* - {@link typeBoolean}
|
|
18
|
+
* - {@link typeNumber},
|
|
19
|
+
* - {@link typeInteger}
|
|
20
|
+
* - {@link typeDatetime}
|
|
21
|
+
* - {@link typeUrl}
|
|
22
|
+
* - {@link typePath}
|
|
23
|
+
* - {@link typeChoice}
|
|
24
|
+
*
|
|
25
|
+
* Composed Types:
|
|
26
|
+
* - {@link typeMapped}
|
|
27
|
+
* - {@link typeTuple}
|
|
28
|
+
* - {@link typeList}
|
|
18
29
|
*
|
|
19
30
|
* @typeParam Value - Type produced by the decoder.
|
|
20
31
|
*/
|
|
@@ -33,6 +44,22 @@ export type Type<Value> = {
|
|
|
33
44
|
decoder(input: string): Value;
|
|
34
45
|
};
|
|
35
46
|
|
|
47
|
+
/**
|
|
48
|
+
* A named type that accepts any string as input.
|
|
49
|
+
* @param name - Name shown in help and errors (e.g. `"my-value"`).
|
|
50
|
+
* @example
|
|
51
|
+
* ```ts
|
|
52
|
+
* typeString("greeting").decoder("hello") // → "hello"
|
|
53
|
+
* typeString("greeting").decoder("") // → ""
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
56
|
+
export function typeString(name?: string): Type<string> {
|
|
57
|
+
return {
|
|
58
|
+
content: name ?? "string",
|
|
59
|
+
decoder: (input: string) => input,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
36
63
|
/**
|
|
37
64
|
* Decodes a string to `boolean` (case-insensitive).
|
|
38
65
|
* Used by {@link optionFlag} for `--flag=<value>`.
|
|
@@ -41,10 +68,11 @@ export type Type<Value> = {
|
|
|
41
68
|
* ```ts
|
|
42
69
|
* typeBoolean("flag").decoder("true") // → true
|
|
43
70
|
* typeBoolean("flag").decoder("yes") // → true
|
|
44
|
-
* typeBoolean("flag").decoder("
|
|
45
|
-
* typeBoolean("flag").decoder("
|
|
46
|
-
* typeBoolean("flag").decoder("
|
|
71
|
+
* typeBoolean("flag").decoder("Y") // → true
|
|
72
|
+
* typeBoolean("flag").decoder("FALSE") // → false
|
|
73
|
+
* typeBoolean("flag").decoder("NO") // → false
|
|
47
74
|
* typeBoolean("flag").decoder("n") // → false
|
|
75
|
+
* typeBoolean("flag").decoder("maybe") // throws
|
|
48
76
|
* ```
|
|
49
77
|
*/
|
|
50
78
|
export function typeBoolean(name?: string): Type<boolean> {
|
|
@@ -62,36 +90,6 @@ export function typeBoolean(name?: string): Type<boolean> {
|
|
|
62
90
|
},
|
|
63
91
|
};
|
|
64
92
|
}
|
|
65
|
-
const typeBooleanValuesTrue = new Set(["true", "yes", "on", "y"]);
|
|
66
|
-
const typeBooleanValuesFalse = new Set(["false", "no", "off", "n"]);
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Parses a date/time string via `Date.parse`.
|
|
70
|
-
* Accepts any format supported by `Date.parse`, including ISO 8601.
|
|
71
|
-
*
|
|
72
|
-
* @example
|
|
73
|
-
* ```ts
|
|
74
|
-
* typeDatetime("my-datetime").decoder("2024-01-15") // → Date object for 2024-01-15
|
|
75
|
-
* typeDatetime("my-datetime").decoder("2024-01-15T13:45:30Z") // → Date object for 2024-01-15 13:45:30 UTC
|
|
76
|
-
* typeDatetime("my-datetime").decoder("not a date") // throws TypoError
|
|
77
|
-
* ```
|
|
78
|
-
*/
|
|
79
|
-
export function typeDatetime(name?: string): Type<Date> {
|
|
80
|
-
return {
|
|
81
|
-
content: name ?? "datetime",
|
|
82
|
-
decoder(input: string) {
|
|
83
|
-
try {
|
|
84
|
-
const timestampMs = Date.parse(input);
|
|
85
|
-
if (isNaN(timestampMs)) {
|
|
86
|
-
throw new Error();
|
|
87
|
-
}
|
|
88
|
-
return new Date(timestampMs);
|
|
89
|
-
} catch {
|
|
90
|
-
throwInvalidValue("a valid ISO_8601 datetime", input);
|
|
91
|
-
}
|
|
92
|
-
},
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
93
|
|
|
96
94
|
/**
|
|
97
95
|
* Parses a string to `number` via `Number()`; `NaN` throws.
|
|
@@ -145,118 +143,68 @@ export function typeInteger(name?: string): Type<bigint> {
|
|
|
145
143
|
}
|
|
146
144
|
|
|
147
145
|
/**
|
|
148
|
-
* Parses
|
|
149
|
-
*
|
|
146
|
+
* Parses a date/time string via `Date.parse`.
|
|
147
|
+
* Accepts any format supported by `Date.parse`, including ISO 8601.
|
|
150
148
|
*
|
|
151
149
|
* @example
|
|
152
150
|
* ```ts
|
|
153
|
-
*
|
|
154
|
-
*
|
|
151
|
+
* typeDatetime("start").decoder("2024-01-15") // → Date object for 2024-01-15
|
|
152
|
+
* typeDatetime("start").decoder("2024-01-15T13:45:30Z") // → Date object for 2024-01-15 13:45:30 UTC
|
|
153
|
+
* typeDatetime("start").decoder("not a date") // throws
|
|
155
154
|
* ```
|
|
156
155
|
*/
|
|
157
|
-
export function
|
|
156
|
+
export function typeDatetime(name?: string): Type<Date> {
|
|
158
157
|
return {
|
|
159
|
-
content: name ?? "
|
|
158
|
+
content: name ?? "datetime",
|
|
160
159
|
decoder(input: string) {
|
|
161
160
|
try {
|
|
162
|
-
|
|
161
|
+
const timestampMs = Date.parse(input);
|
|
162
|
+
if (isNaN(timestampMs)) {
|
|
163
|
+
throw new Error();
|
|
164
|
+
}
|
|
165
|
+
return new Date(timestampMs);
|
|
163
166
|
} catch {
|
|
164
|
-
throwInvalidValue("
|
|
167
|
+
throwInvalidValue("a valid ISO_8601 datetime", input);
|
|
165
168
|
}
|
|
166
169
|
},
|
|
167
170
|
};
|
|
168
171
|
}
|
|
169
172
|
|
|
170
173
|
/**
|
|
171
|
-
*
|
|
172
|
-
*
|
|
173
|
-
* @example
|
|
174
|
-
* ```ts
|
|
175
|
-
* type("greeting").decoder("hello") // → "hello"
|
|
176
|
-
* type("greeting").decoder("") // → ""
|
|
177
|
-
* ```
|
|
178
|
-
*/
|
|
179
|
-
export function type(name?: string): Type<string> {
|
|
180
|
-
return {
|
|
181
|
-
content: name ?? "string",
|
|
182
|
-
decoder: (input: string) => input,
|
|
183
|
-
};
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
/**
|
|
187
|
-
* Chains `before`'s decoder with an `after` transformation.
|
|
188
|
-
* `before` errors are prefixed with `"from: <content>"` for traceability.
|
|
189
|
-
*
|
|
190
|
-
* @typeParam Before - Intermediate type from `before.decoder`.
|
|
191
|
-
* @typeParam After - Final type from `after.decoder`.
|
|
192
|
-
*
|
|
193
|
-
* @param name - Name shown in help and errors (e.g. `"my-value"`).
|
|
194
|
-
* @param before - Base type to decode the raw string.
|
|
195
|
-
* @param mapper - Transforms `before`'s output to the final value; errors are wrapped with context.
|
|
196
|
-
* @returns A {@link Type}`<After>`.
|
|
174
|
+
* Parses an absolute URL string to a `URL` object.
|
|
175
|
+
* Relative or malformed URLs throws.
|
|
197
176
|
*
|
|
198
177
|
* @example
|
|
199
178
|
* ```ts
|
|
200
|
-
*
|
|
201
|
-
*
|
|
202
|
-
* return n;
|
|
203
|
-
* });
|
|
204
|
-
* // "--port 8080" → 8080
|
|
205
|
-
* // "--port 99999" → TypoError: --port: <PORT>: Port: Out of range
|
|
179
|
+
* typeUrl("my-url").decoder("https://example.com") // → URL { href: "https://example.com/", ... }
|
|
180
|
+
* typeUrl("my-url").decoder("not-a-url") // throws
|
|
206
181
|
* ```
|
|
207
182
|
*/
|
|
208
|
-
export function
|
|
209
|
-
name: string,
|
|
210
|
-
before: Type<Before>,
|
|
211
|
-
mapper: (value: Before) => After,
|
|
212
|
-
): Type<After> {
|
|
213
|
-
return {
|
|
214
|
-
content: name,
|
|
215
|
-
decoder: (input: string) => {
|
|
216
|
-
return mapper(
|
|
217
|
-
TypoError.tryWithContext(
|
|
218
|
-
() => before.decoder(input),
|
|
219
|
-
() =>
|
|
220
|
-
new TypoText(
|
|
221
|
-
new TypoString("from: "),
|
|
222
|
-
new TypoString(before.content, typoStyleLogic),
|
|
223
|
-
),
|
|
224
|
-
),
|
|
225
|
-
);
|
|
226
|
-
},
|
|
227
|
-
};
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
/**
|
|
231
|
-
* Adds a name to a {@link Type} for clearer error messages and help text.
|
|
232
|
-
*
|
|
233
|
-
* @param name - Name to use for the type.
|
|
234
|
-
* @param type - Base type to name.
|
|
235
|
-
* @returns A {@link Type} with the given name.
|
|
236
|
-
*/
|
|
237
|
-
export function typeRenamed<Value>(
|
|
238
|
-
type: Type<Value>,
|
|
239
|
-
name: string,
|
|
240
|
-
): Type<Value> {
|
|
183
|
+
export function typeUrl(name?: string): Type<URL> {
|
|
241
184
|
return {
|
|
242
|
-
content: name,
|
|
243
|
-
decoder
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
new TypoString(type.content, typoStyleLogic),
|
|
250
|
-
),
|
|
251
|
-
);
|
|
185
|
+
content: name ?? "url",
|
|
186
|
+
decoder(input: string) {
|
|
187
|
+
try {
|
|
188
|
+
return new URL(input);
|
|
189
|
+
} catch {
|
|
190
|
+
throwInvalidValue("an URL", input);
|
|
191
|
+
}
|
|
252
192
|
},
|
|
253
193
|
};
|
|
254
194
|
}
|
|
255
195
|
|
|
256
196
|
/**
|
|
257
197
|
* Creates a {@link Type} for filesystem paths with optional existence checks.
|
|
198
|
+
*
|
|
258
199
|
* @param checks - Optional checks for path existence and type (file/directory).
|
|
259
200
|
* @returns A {@link Type}`<string>` representing the path.
|
|
201
|
+
*
|
|
202
|
+
* @example
|
|
203
|
+
* ```ts
|
|
204
|
+
* const typeInputFile = typePath("input-file", { checkSyncExistAs: "file" });
|
|
205
|
+
* typeInputFile.decoder("data.txt"); // → "data.txt" if it exists and is a file, otherwise throws
|
|
206
|
+
* typeInputFile.decoder("somedir"); // throws if "somedir" exists and is a directory
|
|
207
|
+
* ```
|
|
260
208
|
*/
|
|
261
209
|
export function typePath(
|
|
262
210
|
name?: string,
|
|
@@ -365,6 +313,78 @@ export function typeChoice<const Value extends string>(
|
|
|
365
313
|
};
|
|
366
314
|
}
|
|
367
315
|
|
|
316
|
+
/**
|
|
317
|
+
* Chains `before`'s decoder with an `after` transformation.
|
|
318
|
+
* `before` errors are prefixed with `"from: <content>"` for traceability.
|
|
319
|
+
*
|
|
320
|
+
* @typeParam Before - Intermediate type from `before.decoder`.
|
|
321
|
+
* @typeParam After - Final type from `after.decoder`.
|
|
322
|
+
*
|
|
323
|
+
* @param name - Name shown in help and errors (e.g. `"my-value"`).
|
|
324
|
+
* @param before - Base type to decode the raw string.
|
|
325
|
+
* @param mapper - Transforms `before`'s output to the final value; errors are wrapped with context.
|
|
326
|
+
* @returns A {@link Type}`<After>`.
|
|
327
|
+
*
|
|
328
|
+
* @example
|
|
329
|
+
* ```ts
|
|
330
|
+
* const typePort = typeMapped("port", typeNumber(), (n) => {
|
|
331
|
+
* if (n < 1 || n > 65535) {
|
|
332
|
+
* throw new Error("Out of range");
|
|
333
|
+
* }
|
|
334
|
+
* return n;
|
|
335
|
+
* });
|
|
336
|
+
* typePort.decoder("8080"); // → 8080
|
|
337
|
+
* typePort.decoder("70000"); // throws
|
|
338
|
+
* ```
|
|
339
|
+
*/
|
|
340
|
+
export function typeMapped<Before, After>(
|
|
341
|
+
name: string,
|
|
342
|
+
before: Type<Before>,
|
|
343
|
+
mapper: (value: Before) => After,
|
|
344
|
+
): Type<After> {
|
|
345
|
+
return {
|
|
346
|
+
content: name,
|
|
347
|
+
decoder: (input: string) => {
|
|
348
|
+
return mapper(
|
|
349
|
+
TypoError.tryWithContext(
|
|
350
|
+
() => before.decoder(input),
|
|
351
|
+
() =>
|
|
352
|
+
new TypoText(
|
|
353
|
+
new TypoString("from: "),
|
|
354
|
+
new TypoString(before.content, typoStyleLogic),
|
|
355
|
+
),
|
|
356
|
+
),
|
|
357
|
+
);
|
|
358
|
+
},
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* Adds a name to a {@link Type} for clearer error messages and help text.
|
|
364
|
+
*
|
|
365
|
+
* @param name - Name to use for the type.
|
|
366
|
+
* @param type - Base type to name.
|
|
367
|
+
* @returns A {@link Type} with the given name.
|
|
368
|
+
*/
|
|
369
|
+
export function typeRenamed<Value>(
|
|
370
|
+
type: Type<Value>,
|
|
371
|
+
name: string,
|
|
372
|
+
): Type<Value> {
|
|
373
|
+
return {
|
|
374
|
+
content: name,
|
|
375
|
+
decoder: (input: string) => {
|
|
376
|
+
return TypoError.tryWithContext(
|
|
377
|
+
() => type.decoder(input),
|
|
378
|
+
() =>
|
|
379
|
+
new TypoText(
|
|
380
|
+
new TypoString("from: "),
|
|
381
|
+
new TypoString(type.content, typoStyleLogic),
|
|
382
|
+
),
|
|
383
|
+
);
|
|
384
|
+
},
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
|
|
368
388
|
/**
|
|
369
389
|
* Splits a delimited string into a typed tuple.
|
|
370
390
|
* Each part is decoded by the corresponding element type; wrong count or decode failure throws.
|
|
@@ -436,7 +456,9 @@ export function typeTuple<const Elements extends Array<any>>(
|
|
|
436
456
|
* typeNumbers.decoder("1,2,3") // → [1, 2, 3]
|
|
437
457
|
* typeNumbers.decoder("1,x,3") // throws
|
|
438
458
|
* const typePaths = typeList(typePath(), ":");
|
|
439
|
-
* typePaths.decoder("/
|
|
459
|
+
* typePaths.decoder("/bin:/usr") // → ["/bin", "/usr"]
|
|
460
|
+
* typePaths.decoder("/usr/bin") // → ["/usr/bin"]
|
|
461
|
+
* typePaths.decoder("") // → throws
|
|
440
462
|
* ```
|
|
441
463
|
*/
|
|
442
464
|
export function typeList<Value>(
|
|
@@ -470,3 +492,6 @@ function throwInvalidValue(kind: string, input: string): never {
|
|
|
470
492
|
),
|
|
471
493
|
);
|
|
472
494
|
}
|
|
495
|
+
|
|
496
|
+
const typeBooleanValuesTrue = new Set(["true", "yes", "on", "y"]);
|
|
497
|
+
const typeBooleanValuesFalse = new Set(["false", "no", "off", "n"]);
|
package/src/lib/Typo.ts
CHANGED
|
@@ -402,7 +402,7 @@ export class TypoSupport {
|
|
|
402
402
|
return TypoSupport.tty();
|
|
403
403
|
}
|
|
404
404
|
if (envForceColor === "3") {
|
|
405
|
-
return TypoSupport.tty();
|
|
405
|
+
return TypoSupport.tty();
|
|
406
406
|
}
|
|
407
407
|
if (envForceColor) {
|
|
408
408
|
return TypoSupport.tty();
|
|
@@ -1,9 +1,5 @@
|
|
|
1
1
|
import { expect, it } from "@jest/globals";
|
|
2
|
-
import {
|
|
3
|
-
ReaderArgs,
|
|
4
|
-
ReaderOptionNextGuard,
|
|
5
|
-
ReaderOptionRestGuard,
|
|
6
|
-
} from "../src";
|
|
2
|
+
import { ReaderArgs, ReaderOptionNextGuard } from "../src";
|
|
7
3
|
|
|
8
4
|
it("run", async function () {
|
|
9
5
|
const stream = new ReaderArgs([
|
|
@@ -78,59 +74,59 @@ it("run", async function () {
|
|
|
78
74
|
|
|
79
75
|
const kA = stream.registerOptionShort({
|
|
80
76
|
key: "a",
|
|
81
|
-
restGuard: optionFlagRestGuard,
|
|
82
77
|
nextGuard: optionFlagNextGuard,
|
|
78
|
+
consumeGroupRestAsValue: false,
|
|
83
79
|
});
|
|
84
80
|
const kB = stream.registerOptionShort({
|
|
85
81
|
key: "b",
|
|
86
|
-
restGuard: optionValuedRestGuard,
|
|
87
82
|
nextGuard: optionValuedNextGuard,
|
|
83
|
+
consumeGroupRestAsValue: true,
|
|
88
84
|
});
|
|
89
85
|
|
|
90
86
|
const kC = stream.registerOptionShort({
|
|
91
87
|
key: "c",
|
|
92
|
-
restGuard: optionFlagRestGuard,
|
|
93
88
|
nextGuard: optionFlagNextGuard,
|
|
89
|
+
consumeGroupRestAsValue: false,
|
|
94
90
|
});
|
|
95
91
|
const kD = stream.registerOptionShort({
|
|
96
92
|
key: "d",
|
|
97
|
-
restGuard: optionValuedRestGuard,
|
|
98
93
|
nextGuard: optionValuedNextGuard,
|
|
94
|
+
consumeGroupRestAsValue: true,
|
|
99
95
|
});
|
|
100
96
|
|
|
101
97
|
const kE = stream.registerOptionShort({
|
|
102
98
|
key: "e",
|
|
103
|
-
restGuard: optionFlagRestGuard,
|
|
104
99
|
nextGuard: optionFlagNextGuard,
|
|
100
|
+
consumeGroupRestAsValue: false,
|
|
105
101
|
});
|
|
106
102
|
const kF = stream.registerOptionShort({
|
|
107
103
|
key: "f",
|
|
108
|
-
restGuard: optionValuedRestGuard,
|
|
109
104
|
nextGuard: optionValuedNextGuard,
|
|
105
|
+
consumeGroupRestAsValue: true,
|
|
110
106
|
});
|
|
111
107
|
|
|
112
108
|
expect(stream.consumePositional()).toStrictEqual("positional-2");
|
|
113
109
|
|
|
114
110
|
const kG = stream.registerOptionShort({
|
|
115
111
|
key: "g",
|
|
116
|
-
restGuard: optionFlagRestGuard,
|
|
117
112
|
nextGuard: optionFlagNextGuard,
|
|
113
|
+
consumeGroupRestAsValue: false,
|
|
118
114
|
});
|
|
119
115
|
const kH = stream.registerOptionShort({
|
|
120
116
|
key: "h",
|
|
121
|
-
restGuard: optionValuedRestGuard,
|
|
122
117
|
nextGuard: optionValuedNextGuard,
|
|
118
|
+
consumeGroupRestAsValue: true,
|
|
123
119
|
});
|
|
124
120
|
|
|
125
121
|
const kI = stream.registerOptionShort({
|
|
126
122
|
key: "i",
|
|
127
|
-
restGuard: optionFlagRestGuard,
|
|
128
123
|
nextGuard: optionFlagNextGuard,
|
|
124
|
+
consumeGroupRestAsValue: false,
|
|
129
125
|
});
|
|
130
126
|
const kJ = stream.registerOptionShort({
|
|
131
127
|
key: "j",
|
|
132
|
-
restGuard: optionValuedRestGuard,
|
|
133
128
|
nextGuard: optionValuedNextGuard,
|
|
129
|
+
consumeGroupRestAsValue: true,
|
|
134
130
|
});
|
|
135
131
|
|
|
136
132
|
expect(stream.consumePositional()).toStrictEqual("positional-3");
|
|
@@ -141,56 +137,47 @@ it("run", async function () {
|
|
|
141
137
|
expect(stream.consumePositional()).toStrictEqual("positional-4");
|
|
142
138
|
expect(stream.consumePositional()).toStrictEqual(undefined);
|
|
143
139
|
|
|
144
|
-
expect(kFlagUnset()
|
|
145
|
-
expect(kFlagNo()
|
|
146
|
-
expect(kFlagNormal()
|
|
147
|
-
|
|
148
|
-
]);
|
|
149
|
-
expect(kFlagPositive().values).toStrictEqual([
|
|
150
|
-
{ inlined: "true", separated: [] },
|
|
151
|
-
]);
|
|
152
|
-
expect(kFlagNegative().values).toStrictEqual([
|
|
153
|
-
{ inlined: "false", separated: [] },
|
|
154
|
-
]);
|
|
140
|
+
expect(kFlagUnset()).toStrictEqual([]);
|
|
141
|
+
expect(kFlagNo()).toStrictEqual([{ inlined: null, separated: [] }]);
|
|
142
|
+
expect(kFlagNormal()).toStrictEqual([{ inlined: null, separated: [] }]);
|
|
143
|
+
expect(kFlagPositive()).toStrictEqual([{ inlined: "true", separated: [] }]);
|
|
144
|
+
expect(kFlagNegative()).toStrictEqual([{ inlined: "false", separated: [] }]);
|
|
155
145
|
|
|
156
|
-
expect(kOptionUnset()
|
|
157
|
-
expect(kOptionSplit()
|
|
146
|
+
expect(kOptionUnset()).toStrictEqual([]);
|
|
147
|
+
expect(kOptionSplit()).toStrictEqual([
|
|
158
148
|
{ inlined: null, separated: ["1.1"] },
|
|
159
149
|
{ inlined: null, separated: ["1.2"] },
|
|
160
150
|
]);
|
|
161
|
-
expect(kOptionJoin()
|
|
151
|
+
expect(kOptionJoin()).toStrictEqual([
|
|
162
152
|
{ inlined: "2.1", separated: [] },
|
|
163
153
|
{ inlined: "2.2", separated: [] },
|
|
164
154
|
]);
|
|
165
155
|
|
|
166
|
-
expect(kA()
|
|
167
|
-
expect(kB()
|
|
156
|
+
expect(kA()).toStrictEqual([{ inlined: null, separated: [] }]);
|
|
157
|
+
expect(kB()).toStrictEqual([
|
|
168
158
|
{ inlined: null, separated: ["3.1"] },
|
|
169
159
|
{ inlined: null, separated: ["3.2"] },
|
|
170
160
|
]);
|
|
171
161
|
|
|
172
|
-
expect(kC()
|
|
173
|
-
expect(kD()
|
|
162
|
+
expect(kC()).toStrictEqual([{ inlined: null, separated: [] }]);
|
|
163
|
+
expect(kD()).toStrictEqual([
|
|
174
164
|
{ inlined: "4.1", separated: [] },
|
|
175
165
|
{ inlined: "4.2", separated: [] },
|
|
176
166
|
]);
|
|
177
167
|
|
|
178
|
-
expect(kE()
|
|
179
|
-
expect(kF()
|
|
168
|
+
expect(kE()).toStrictEqual([{ inlined: null, separated: [] }]);
|
|
169
|
+
expect(kF()).toStrictEqual([
|
|
180
170
|
{ inlined: "5.1", separated: [] },
|
|
181
171
|
{ inlined: "5.2", separated: [] },
|
|
182
172
|
]);
|
|
183
173
|
|
|
184
|
-
expect(kG()
|
|
185
|
-
expect(kH()
|
|
174
|
+
expect(kG()).toStrictEqual([{ inlined: null, separated: [] }]);
|
|
175
|
+
expect(kH()).toStrictEqual([{ inlined: "FALSE", separated: [] }]);
|
|
186
176
|
|
|
187
|
-
expect(kI()
|
|
188
|
-
expect(kJ()
|
|
177
|
+
expect(kI()).toStrictEqual([{ inlined: null, separated: [] }]);
|
|
178
|
+
expect(kJ()).toStrictEqual([{ inlined: "TRUE", separated: [] }]);
|
|
189
179
|
});
|
|
190
180
|
|
|
191
|
-
const optionFlagRestGuard: ReaderOptionRestGuard = () => false;
|
|
192
181
|
const optionFlagNextGuard: ReaderOptionNextGuard = () => false;
|
|
193
|
-
|
|
194
|
-
const optionValuedRestGuard: ReaderOptionRestGuard = () => true;
|
|
195
182
|
const optionValuedNextGuard: ReaderOptionNextGuard = (value) =>
|
|
196
183
|
value.inlined === null && value.separated.length === 0;
|
|
@@ -11,7 +11,7 @@ it("run", async function () {
|
|
|
11
11
|
});
|
|
12
12
|
expect(readerArgs1.consumePositional()).toStrictEqual("C");
|
|
13
13
|
expect(readerArgs1.consumePositional()).toStrictEqual(undefined);
|
|
14
|
-
expect(kOptionVariadicStop()
|
|
14
|
+
expect(kOptionVariadicStop()).toStrictEqual([
|
|
15
15
|
{ inlined: "1", separated: ["A", "B", "STOP"] },
|
|
16
16
|
]);
|
|
17
17
|
|
|
@@ -21,7 +21,7 @@ it("run", async function () {
|
|
|
21
21
|
nextGuard: (_value, nextArg) => nextArg !== undefined,
|
|
22
22
|
});
|
|
23
23
|
expect(readerArgs2.consumePositional()).toStrictEqual(undefined);
|
|
24
|
-
expect(kOptionVariadicFull()
|
|
24
|
+
expect(kOptionVariadicFull()).toStrictEqual([
|
|
25
25
|
{ inlined: "1", separated: ["A", "B", "C"] },
|
|
26
26
|
]);
|
|
27
27
|
|
|
@@ -31,7 +31,7 @@ it("run", async function () {
|
|
|
31
31
|
nextGuard: (value) => value.separated.length < Number(value.inlined ?? "0"),
|
|
32
32
|
});
|
|
33
33
|
expect(readerArgs3.consumePositional()).toStrictEqual(undefined);
|
|
34
|
-
expect(kOptionVariadicKeyed()
|
|
34
|
+
expect(kOptionVariadicKeyed()).toStrictEqual([
|
|
35
35
|
{ inlined: "2", separated: ["A", "B"] },
|
|
36
36
|
{ inlined: "1", separated: ["C"] },
|
|
37
37
|
]);
|