cli-kiss 0.2.3 → 0.2.5
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 -1
- package/dist/index.d.ts +696 -734
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/docs/.vitepress/config.mts +2 -3
- package/docs/.vitepress/theme/index.ts +4 -0
- package/docs/.vitepress/theme/style.css +4 -0
- package/docs/guide/01_getting_started.md +12 -13
- package/docs/guide/02_commands.md +71 -52
- package/docs/guide/03_options.md +25 -33
- package/docs/guide/04_positionals.md +45 -55
- package/docs/guide/05_types.md +66 -66
- package/docs/guide/06_run.md +28 -40
- package/docs/index.md +8 -3
- package/docs/public/favicon.ico +0 -0
- package/docs/public/hero.png +0 -0
- package/package.json +1 -1
- package/src/index.ts +0 -2
- package/src/lib/Command.ts +45 -123
- package/src/lib/Operation.ts +23 -32
- package/src/lib/Option.ts +150 -170
- package/src/lib/Positional.ts +44 -94
- package/src/lib/Reader.ts +123 -99
- package/src/lib/Run.ts +86 -45
- package/src/lib/Type.ts +246 -156
- package/src/lib/Typo.ts +98 -107
- package/src/lib/Usage.ts +163 -82
- package/tests/unit.Reader.aliases.ts +31 -15
- package/tests/unit.Reader.commons.ts +99 -43
- package/tests/unit.Reader.parsings.ts +50 -0
- package/tests/unit.Reader.shortBig.ts +75 -31
- package/tests/unit.command.execute.ts +86 -43
- package/tests/unit.command.usage.ts +88 -82
- package/tests/unit.runner.colors.ts +197 -0
- package/tests/unit.runner.cycle.ts +77 -63
- package/tests/unit.runner.errors.ts +23 -15
|
@@ -12,9 +12,10 @@ import {
|
|
|
12
12
|
positionalRequired,
|
|
13
13
|
positionalVariadics,
|
|
14
14
|
ReaderArgs,
|
|
15
|
+
type,
|
|
16
|
+
typeChoice,
|
|
15
17
|
typeList,
|
|
16
18
|
typeNumber,
|
|
17
|
-
typeString,
|
|
18
19
|
typeTuple,
|
|
19
20
|
TypoSupport,
|
|
20
21
|
usageToStyledLines,
|
|
@@ -34,6 +35,13 @@ const rootCommand = commandChained<any, any, any>(
|
|
|
34
35
|
operation(
|
|
35
36
|
{
|
|
36
37
|
options: {
|
|
38
|
+
choiceOption: optionSingleValue({
|
|
39
|
+
long: "choice-option",
|
|
40
|
+
type: typeChoice("choice", ["unset", "empty", "choice1", "choice2"]),
|
|
41
|
+
description: "choice-option description",
|
|
42
|
+
defaultWhenNotInlined: () => "empty",
|
|
43
|
+
defaultWhenNotDefined: () => "unset",
|
|
44
|
+
}),
|
|
37
45
|
booleanFlag: optionFlag({
|
|
38
46
|
short: "b",
|
|
39
47
|
long: "boolean-flag",
|
|
@@ -42,13 +50,12 @@ const rootCommand = commandChained<any, any, any>(
|
|
|
42
50
|
},
|
|
43
51
|
positionals: [
|
|
44
52
|
positionalRequired({
|
|
45
|
-
label: "POS-1",
|
|
46
53
|
description: "Required positional number 1",
|
|
47
|
-
type: typeNumber,
|
|
54
|
+
type: typeNumber("pos-1"),
|
|
48
55
|
}),
|
|
49
56
|
],
|
|
50
57
|
},
|
|
51
|
-
async (context, inputs)
|
|
58
|
+
async function (context, inputs) {
|
|
52
59
|
return { at: "root", context, inputs };
|
|
53
60
|
},
|
|
54
61
|
),
|
|
@@ -73,26 +80,24 @@ const rootCommand = commandChained<any, any, any>(
|
|
|
73
80
|
stringOption: optionSingleValue({
|
|
74
81
|
short: "s",
|
|
75
82
|
long: "string-option",
|
|
76
|
-
type:
|
|
77
|
-
|
|
78
|
-
label: "COOL-STUFF",
|
|
83
|
+
type: type("cool-stuff"),
|
|
84
|
+
defaultWhenNotDefined: () => undefined,
|
|
79
85
|
description: "string-option description",
|
|
80
86
|
}),
|
|
81
87
|
complexOption: optionRepeatable({
|
|
82
88
|
long: "complex-option",
|
|
83
|
-
type: typeTuple([typeNumber, typeList(
|
|
89
|
+
type: typeTuple([typeNumber(), typeList(type("string"))]),
|
|
84
90
|
description: "complex-option description",
|
|
85
91
|
}),
|
|
86
92
|
},
|
|
87
93
|
positionals: [
|
|
88
94
|
positionalRequired({
|
|
89
|
-
label: "POS-2",
|
|
90
95
|
description: "Required positional number 2",
|
|
91
|
-
type: typeNumber,
|
|
96
|
+
type: typeNumber("pos-2"),
|
|
92
97
|
}),
|
|
93
98
|
],
|
|
94
99
|
},
|
|
95
|
-
async (context, inputs)
|
|
100
|
+
async function (context, inputs) {
|
|
96
101
|
return { at: "root", context, inputs };
|
|
97
102
|
},
|
|
98
103
|
),
|
|
@@ -122,13 +127,12 @@ const rootCommand = commandChained<any, any, any>(
|
|
|
122
127
|
options: {},
|
|
123
128
|
positionals: [
|
|
124
129
|
positionalRequired({
|
|
125
|
-
label: "POS-STRING",
|
|
126
130
|
description: "Required positional string",
|
|
127
|
-
type:
|
|
131
|
+
type: type("pos-3.1"),
|
|
128
132
|
}),
|
|
129
133
|
],
|
|
130
134
|
},
|
|
131
|
-
async (context, inputs)
|
|
135
|
+
async function (context, inputs) {
|
|
132
136
|
return { at: "sub1", context, inputs };
|
|
133
137
|
},
|
|
134
138
|
),
|
|
@@ -148,7 +152,17 @@ const rootCommand = commandChained<any, any, any>(
|
|
|
148
152
|
{ positional: "40" },
|
|
149
153
|
{ positional: "41" },
|
|
150
154
|
{ subcommand: "sub2" },
|
|
151
|
-
{ option: { long: "dudu",
|
|
155
|
+
{ option: { long: "dudu", inlined: "hello" } },
|
|
156
|
+
{ positional: "50" },
|
|
157
|
+
],
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
explanation: "Example usage of subcommand 2",
|
|
161
|
+
commandArgs: [
|
|
162
|
+
{ positional: "40" },
|
|
163
|
+
{ positional: "41" },
|
|
164
|
+
{ subcommand: "sub2" },
|
|
165
|
+
{ option: { long: "dudu", separated: ["hello"] } },
|
|
152
166
|
{ positional: "50" },
|
|
153
167
|
],
|
|
154
168
|
},
|
|
@@ -159,33 +173,30 @@ const rootCommand = commandChained<any, any, any>(
|
|
|
159
173
|
options: {
|
|
160
174
|
duduValue: optionSingleValue({
|
|
161
175
|
long: "dudu",
|
|
162
|
-
type:
|
|
163
|
-
|
|
176
|
+
type: type("dudu-value"),
|
|
177
|
+
defaultWhenNotDefined: () => "duduDefault",
|
|
164
178
|
hint: "Dudu option hint",
|
|
165
179
|
description: "Dudu option description",
|
|
166
180
|
}),
|
|
167
181
|
},
|
|
168
182
|
positionals: [
|
|
169
183
|
positionalRequired({
|
|
170
|
-
label: "POS-NUMBER",
|
|
171
184
|
description: "Required positional number",
|
|
172
|
-
type: typeNumber,
|
|
185
|
+
type: typeNumber("pos-3.2"),
|
|
173
186
|
}),
|
|
174
187
|
positionalOptional({
|
|
175
|
-
label: "OPT-POS",
|
|
176
188
|
description: "Optional positional string",
|
|
177
189
|
hint: "Optional positional hint",
|
|
178
|
-
type:
|
|
190
|
+
type: type("pos-4"),
|
|
179
191
|
default: () => "42",
|
|
180
192
|
}),
|
|
181
193
|
positionalVariadics({
|
|
182
|
-
label: "VARIADIC",
|
|
183
194
|
description: "Variadic positionals strings",
|
|
184
|
-
type:
|
|
195
|
+
type: type("pos-5"),
|
|
185
196
|
}),
|
|
186
197
|
],
|
|
187
198
|
},
|
|
188
|
-
async (context, inputs)
|
|
199
|
+
async function (context, inputs) {
|
|
189
200
|
return { at: "sub2", context, inputs };
|
|
190
201
|
},
|
|
191
202
|
),
|
|
@@ -194,7 +205,7 @@ const rootCommand = commandChained<any, any, any>(
|
|
|
194
205
|
),
|
|
195
206
|
);
|
|
196
207
|
|
|
197
|
-
it("run", async ()
|
|
208
|
+
it("run", async function () {
|
|
198
209
|
const usage1 = await getUsage([], rootCommand);
|
|
199
210
|
const usage2 = await getUsage(["50"], rootCommand);
|
|
200
211
|
const usage3 = await getUsage(["50", "51"], rootCommand);
|
|
@@ -209,107 +220,113 @@ it("run", async () => {
|
|
|
209
220
|
rootCommand,
|
|
210
221
|
);
|
|
211
222
|
|
|
212
|
-
console.log(usage1.join("\n"));
|
|
213
|
-
console.log(usage2.join("\n"));
|
|
214
|
-
console.log(usage3.join("\n"));
|
|
215
|
-
console.log(usage4.join("\n"));
|
|
216
|
-
console.log(usage5.join("\n"));
|
|
217
|
-
console.log(usage6.join("\n"));
|
|
218
|
-
console.log(usage7.join("\n"));
|
|
219
|
-
/*
|
|
220
|
-
*/
|
|
221
|
-
|
|
222
223
|
const usageRoot = [
|
|
223
|
-
"{{Usage:}@darkMagenta}+ {{my-cli}@darkCyan}+ {{<
|
|
224
|
+
"{{Usage:}@darkMagenta}+ {{my-cli}@darkCyan}+ {{<pos-1>}@darkBlue}+ {{[REST]...}@darkBlue}+",
|
|
224
225
|
"",
|
|
225
226
|
"{Root command description}+",
|
|
226
227
|
"{{Root command details.}-}*",
|
|
227
228
|
"{{Root second line of command details.}-}*",
|
|
228
229
|
"",
|
|
229
230
|
"{{Positionals:}@darkGreen}+",
|
|
230
|
-
" {{<
|
|
231
|
+
" {{<pos-1>}@darkBlue}+ Required positional number 1",
|
|
231
232
|
"",
|
|
232
233
|
"{{Options:}@darkGreen}+",
|
|
233
|
-
"
|
|
234
|
+
" {{--choice-option}@darkCyan}+ {{<choice>}@darkBlue}+ choice-option description",
|
|
235
|
+
" {{-b}@darkCyan}+, {{--boolean-flag}@darkCyan}+{{[=no]}-}* boolean-flag description",
|
|
234
236
|
"",
|
|
235
237
|
"{{Examples:}@darkGreen}+",
|
|
236
|
-
"
|
|
237
|
-
"
|
|
238
|
+
" {{# Example usage of the root command}-}*",
|
|
239
|
+
" {{my-cli}@darkCyan}+ {{42}@darkBlue}+ {{-b}@darkCyan}+",
|
|
238
240
|
"",
|
|
239
241
|
];
|
|
240
242
|
const usageMid = [
|
|
241
|
-
"{{Usage:}@darkMagenta}+ {{my-cli}@darkCyan}+ {{<
|
|
243
|
+
"{{Usage:}@darkMagenta}+ {{my-cli}@darkCyan}+ {{<pos-1>}@darkBlue}+ {{<pos-2>}@darkBlue}+ {{<subcommand>}@darkBlue}+",
|
|
242
244
|
"",
|
|
243
245
|
"{Mid command description}+",
|
|
244
246
|
"{{Mid command details.}-}*",
|
|
245
247
|
"{{Mid second line of command details.}-}*",
|
|
246
248
|
"",
|
|
247
249
|
"{{Positionals:}@darkGreen}+",
|
|
248
|
-
" {{<
|
|
249
|
-
" {{<
|
|
250
|
+
" {{<pos-1>}@darkBlue}+ Required positional number 1",
|
|
251
|
+
" {{<pos-2>}@darkBlue}+ Required positional number 2",
|
|
250
252
|
"",
|
|
251
253
|
"{{Subcommands:}@darkGreen}+",
|
|
252
254
|
" {{sub1}@darkCyan}+ Subcommand 1 description",
|
|
253
255
|
" {{sub2}@darkCyan}+ Subcommand 2 description {{(Subcommand 2 hint)}-}*",
|
|
254
256
|
"",
|
|
255
257
|
"{{Options:}@darkGreen}+",
|
|
256
|
-
"
|
|
257
|
-
" {{-
|
|
258
|
-
"
|
|
258
|
+
" {{--choice-option}@darkCyan}+ {{<choice>}@darkBlue}+ choice-option description",
|
|
259
|
+
" {{-b}@darkCyan}+, {{--boolean-flag}@darkCyan}+{{[=no]}-}* boolean-flag description",
|
|
260
|
+
" {{-s}@darkCyan}+, {{--string-option}@darkCyan}+ {{<cool-stuff>}@darkBlue}+ string-option description",
|
|
261
|
+
" {{--complex-option}@darkCyan}+ {{<number,string[,string]...>}@darkBlue}+{{ [*]}-}* complex-option description",
|
|
259
262
|
"",
|
|
260
263
|
"{{Examples:}@darkGreen}+",
|
|
261
|
-
"
|
|
262
|
-
"
|
|
264
|
+
" {{# Example usage of the mid command}-}*",
|
|
265
|
+
" {{my-cli}@darkCyan}+ {{42}@darkBlue}+ {{-b}@darkCyan}+ {{43}@darkBlue}+",
|
|
263
266
|
"",
|
|
264
267
|
];
|
|
265
268
|
const usageSub1 = [
|
|
266
|
-
"{{Usage:}@darkMagenta}+ {{my-cli}@darkCyan}+ {{<
|
|
269
|
+
"{{Usage:}@darkMagenta}+ {{my-cli}@darkCyan}+ {{<pos-1>}@darkBlue}+ {{<pos-2>}@darkBlue}+ {{sub1}@darkCyan}+ {{<pos-3.1>}@darkBlue}+",
|
|
267
270
|
"",
|
|
268
271
|
"{Subcommand 1 description}+",
|
|
269
272
|
"{{Subcommand 1 details.}-}*",
|
|
270
273
|
"{{Subcommand 1 second line of details.}-}*",
|
|
271
274
|
"",
|
|
272
275
|
"{{Positionals:}@darkGreen}+",
|
|
273
|
-
" {{<
|
|
274
|
-
" {{<
|
|
275
|
-
" {{<
|
|
276
|
+
" {{<pos-1>}@darkBlue}+ Required positional number 1",
|
|
277
|
+
" {{<pos-2>}@darkBlue}+ Required positional number 2",
|
|
278
|
+
" {{<pos-3.1>}@darkBlue}+ Required positional string",
|
|
276
279
|
"",
|
|
277
280
|
"{{Options:}@darkGreen}+",
|
|
278
|
-
"
|
|
279
|
-
" {{-
|
|
280
|
-
"
|
|
281
|
+
" {{--choice-option}@darkCyan}+ {{<choice>}@darkBlue}+ choice-option description",
|
|
282
|
+
" {{-b}@darkCyan}+, {{--boolean-flag}@darkCyan}+{{[=no]}-}* boolean-flag description",
|
|
283
|
+
" {{-s}@darkCyan}+, {{--string-option}@darkCyan}+ {{<cool-stuff>}@darkBlue}+ string-option description",
|
|
284
|
+
" {{--complex-option}@darkCyan}+ {{<number,string[,string]...>}@darkBlue}+{{ [*]}-}* complex-option description",
|
|
281
285
|
"",
|
|
282
286
|
"{{Examples:}@darkGreen}+",
|
|
283
|
-
"
|
|
284
|
-
"
|
|
287
|
+
" {{# Example usage of subcommand 1}-}*",
|
|
288
|
+
" {{my-cli}@darkCyan}+ {{-b}@darkCyan}+ {{42}@darkBlue}+ {{43}@darkBlue}+ {{sub1}@darkCyan}+ {{valid}@darkBlue}+",
|
|
285
289
|
"",
|
|
286
290
|
];
|
|
287
291
|
const usageSub2 = [
|
|
288
|
-
"{{Usage:}@darkMagenta}+ {{my-cli}@darkCyan}+ {{<
|
|
292
|
+
"{{Usage:}@darkMagenta}+ {{my-cli}@darkCyan}+ {{<pos-1>}@darkBlue}+ {{<pos-2>}@darkBlue}+ {{sub2}@darkCyan}+ {{<pos-3.2>}@darkBlue}+ {{[pos-4]}@darkBlue}+ {{[pos-5]...}@darkBlue}+",
|
|
289
293
|
"",
|
|
290
294
|
"{Subcommand 2 description}+ {{(Subcommand 2 hint)}-}*",
|
|
291
295
|
"{{Subcommand 2 details.}-}*",
|
|
292
296
|
"{{Subcommand 2 second line of details.}-}*",
|
|
293
297
|
"",
|
|
294
298
|
"{{Positionals:}@darkGreen}+",
|
|
295
|
-
" {{<
|
|
296
|
-
" {{<
|
|
297
|
-
" {{<
|
|
298
|
-
" {{[
|
|
299
|
-
" {{[
|
|
299
|
+
" {{<pos-1>}@darkBlue}+ Required positional number 1",
|
|
300
|
+
" {{<pos-2>}@darkBlue}+ Required positional number 2",
|
|
301
|
+
" {{<pos-3.2>}@darkBlue}+ Required positional number",
|
|
302
|
+
" {{[pos-4]}@darkBlue}+ Optional positional string {{(Optional positional hint)}-}*",
|
|
303
|
+
" {{[pos-5]...}@darkBlue}+ Variadic positionals strings",
|
|
300
304
|
"",
|
|
301
305
|
"{{Options:}@darkGreen}+",
|
|
302
|
-
"
|
|
303
|
-
" {{-
|
|
304
|
-
"
|
|
305
|
-
" {{--
|
|
306
|
+
" {{--choice-option}@darkCyan}+ {{<choice>}@darkBlue}+ choice-option description",
|
|
307
|
+
" {{-b}@darkCyan}+, {{--boolean-flag}@darkCyan}+{{[=no]}-}* boolean-flag description",
|
|
308
|
+
" {{-s}@darkCyan}+, {{--string-option}@darkCyan}+ {{<cool-stuff>}@darkBlue}+ string-option description",
|
|
309
|
+
" {{--complex-option}@darkCyan}+ {{<number,string[,string]...>}@darkBlue}+{{ [*]}-}* complex-option description",
|
|
310
|
+
" {{--dudu}@darkCyan}+ {{<dudu-value>}@darkBlue}+ Dudu option description {{(Dudu option hint)}-}*",
|
|
306
311
|
"",
|
|
307
312
|
"{{Examples:}@darkGreen}+",
|
|
308
|
-
"
|
|
309
|
-
"
|
|
313
|
+
" {{# Example usage of subcommand 2}-}*",
|
|
314
|
+
" {{my-cli}@darkCyan}+ {{40}@darkBlue}+ {{41}@darkBlue}+ {{sub2}@darkCyan}+ {{--dudu}@darkCyan}+{{=}-}*{{hello}@darkBlue}+ {{50}@darkBlue}+",
|
|
315
|
+
" {{# Example usage of subcommand 2}-}*",
|
|
316
|
+
" {{my-cli}@darkCyan}+ {{40}@darkBlue}+ {{41}@darkBlue}+ {{sub2}@darkCyan}+ {{--dudu}@darkCyan}+ {{hello}@darkBlue}+ {{50}@darkBlue}+",
|
|
310
317
|
"",
|
|
311
318
|
];
|
|
312
319
|
|
|
320
|
+
/*
|
|
321
|
+
console.log(usage1.join("\n"));
|
|
322
|
+
console.log(usage2.join("\n"));
|
|
323
|
+
console.log(usage3.join("\n"));
|
|
324
|
+
console.log(usage4.join("\n"));
|
|
325
|
+
console.log(usage5.join("\n"));
|
|
326
|
+
console.log(usage6.join("\n"));
|
|
327
|
+
console.log(usage7.join("\n"));
|
|
328
|
+
*/
|
|
329
|
+
|
|
313
330
|
expect(usage1).toStrictEqual(usageRoot);
|
|
314
331
|
expect(usage2).toStrictEqual(usageMid);
|
|
315
332
|
expect(usage3).toStrictEqual(usageMid);
|
|
@@ -325,20 +342,9 @@ async function getUsage<Context, Result>(
|
|
|
325
342
|
) {
|
|
326
343
|
const readerArgs = new ReaderArgs(args);
|
|
327
344
|
const commandDecoder = command.consumeAndMakeDecoder(readerArgs);
|
|
328
|
-
/*
|
|
329
|
-
try {
|
|
330
|
-
const interpreter = commandDecoder.decodeAndMakeInterpreter();
|
|
331
|
-
const result = await interpreter.executeWithContext(
|
|
332
|
-
"" as unknown as Context,
|
|
333
|
-
);
|
|
334
|
-
console.log(result);
|
|
335
|
-
} catch (error) {
|
|
336
|
-
console.log(TypoSupport.tty().computeStyledErrorMessage(error));
|
|
337
|
-
}
|
|
338
|
-
*/
|
|
339
345
|
return usageToStyledLines({
|
|
340
346
|
cliName: "my-cli",
|
|
341
|
-
|
|
342
|
-
typoSupport: TypoSupport.
|
|
347
|
+
usage: commandDecoder.generateUsage(),
|
|
348
|
+
typoSupport: TypoSupport.mock(),
|
|
343
349
|
});
|
|
344
350
|
}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import { it } from "@jest/globals";
|
|
2
|
+
import {
|
|
3
|
+
command,
|
|
4
|
+
operation,
|
|
5
|
+
optionRepeatable,
|
|
6
|
+
positionalVariadics,
|
|
7
|
+
runAndExit,
|
|
8
|
+
type,
|
|
9
|
+
TypoSupport,
|
|
10
|
+
usageToStyledLines,
|
|
11
|
+
} from "../src";
|
|
12
|
+
|
|
13
|
+
const cliName = "my-cli";
|
|
14
|
+
|
|
15
|
+
it("run", async function () {
|
|
16
|
+
const usage = {
|
|
17
|
+
segments: [{ positional: "[string]..." }],
|
|
18
|
+
information: { description: "Description" },
|
|
19
|
+
subcommands: [],
|
|
20
|
+
options: [{ long: "option", label: "<string>", annotation: " [*]" }],
|
|
21
|
+
positionals: [{ label: "[string]...", description: "Variadics" }],
|
|
22
|
+
};
|
|
23
|
+
const usageNone = usageToStyledLines({
|
|
24
|
+
cliName,
|
|
25
|
+
usage,
|
|
26
|
+
typoSupport: TypoSupport.none(),
|
|
27
|
+
}).join("\n");
|
|
28
|
+
const usageMock = usageToStyledLines({
|
|
29
|
+
cliName,
|
|
30
|
+
usage,
|
|
31
|
+
typoSupport: TypoSupport.mock(),
|
|
32
|
+
}).join("\n");
|
|
33
|
+
const usageTty = usageToStyledLines({
|
|
34
|
+
cliName,
|
|
35
|
+
usage,
|
|
36
|
+
typoSupport: TypoSupport.tty(),
|
|
37
|
+
}).join("\n");
|
|
38
|
+
|
|
39
|
+
await testCase("flag", ["--color=auto", "--help"], [usageTty], [], 0);
|
|
40
|
+
await testCase("flag", ["--color=always", "--help"], [usageTty], [], 0);
|
|
41
|
+
await testCase("flag", ["--color=never", "--help"], [usageNone], [], 0);
|
|
42
|
+
await testCase("flag", ["--color=mock", "--help"], [usageMock], [], 0);
|
|
43
|
+
await testCase("flag", ["--help"], [usageTty], [], 0);
|
|
44
|
+
await testCase("env", ["--help"], [usageTty], [], 0);
|
|
45
|
+
await testCase("always", ["--help"], [usageTty], [], 0);
|
|
46
|
+
await testCase("never", ["--help"], [usageNone], [], 0);
|
|
47
|
+
await testCase("mock", ["--help"], [usageMock], [], 0);
|
|
48
|
+
|
|
49
|
+
process.env["NO_COLOR"] = "";
|
|
50
|
+
await testCase("flag", ["--color=auto", "--help"], [usageNone], [], 0);
|
|
51
|
+
await testCase("flag", ["--color=always", "--help"], [usageTty], [], 0);
|
|
52
|
+
await testCase("flag", ["--color=never", "--help"], [usageNone], [], 0);
|
|
53
|
+
await testCase("flag", ["--color=mock", "--help"], [usageMock], [], 0);
|
|
54
|
+
await testCase("flag", ["--help"], [usageNone], [], 0);
|
|
55
|
+
await testCase("env", ["--help"], [usageNone], [], 0);
|
|
56
|
+
await testCase("always", ["--help"], [usageTty], [], 0);
|
|
57
|
+
await testCase("never", ["--help"], [usageNone], [], 0);
|
|
58
|
+
await testCase("mock", ["--help"], [usageMock], [], 0);
|
|
59
|
+
delete process.env["NO_COLOR"];
|
|
60
|
+
|
|
61
|
+
process.env["FORCE_COLOR"] = "";
|
|
62
|
+
await testCase("flag", ["--color=auto", "--help"], [usageTty], [], 0);
|
|
63
|
+
await testCase("flag", ["--color=always", "--help"], [usageTty], [], 0);
|
|
64
|
+
await testCase("flag", ["--color=never", "--help"], [usageNone], [], 0);
|
|
65
|
+
await testCase("flag", ["--color=mock", "--help"], [usageMock], [], 0);
|
|
66
|
+
await testCase("flag", ["--help"], [usageTty], [], 0);
|
|
67
|
+
await testCase("env", ["--help"], [usageTty], [], 0);
|
|
68
|
+
await testCase("always", ["--help"], [usageTty], [], 0);
|
|
69
|
+
await testCase("never", ["--help"], [usageNone], [], 0);
|
|
70
|
+
await testCase("mock", ["--help"], [usageMock], [], 0);
|
|
71
|
+
delete process.env["FORCE_COLOR"];
|
|
72
|
+
|
|
73
|
+
process.env["FORCE_COLOR"] = "0";
|
|
74
|
+
await testCase("flag", ["--color=auto", "--help"], [usageNone], [], 0);
|
|
75
|
+
await testCase("flag", ["--color=always", "--help"], [usageTty], [], 0);
|
|
76
|
+
await testCase("flag", ["--color=never", "--help"], [usageNone], [], 0);
|
|
77
|
+
await testCase("flag", ["--color=mock", "--help"], [usageMock], [], 0);
|
|
78
|
+
await testCase("flag", ["--help"], [usageNone], [], 0);
|
|
79
|
+
await testCase("env", ["--help"], [usageNone], [], 0);
|
|
80
|
+
await testCase("always", ["--help"], [usageTty], [], 0);
|
|
81
|
+
await testCase("never", ["--help"], [usageNone], [], 0);
|
|
82
|
+
await testCase("mock", ["--help"], [usageMock], [], 0);
|
|
83
|
+
delete process.env["FORCE_COLOR"];
|
|
84
|
+
|
|
85
|
+
process.env["MOCK_COLOR"] = "";
|
|
86
|
+
await testCase("flag", ["--color=auto", "--help"], [usageMock], [], 0);
|
|
87
|
+
await testCase("flag", ["--color=always", "--help"], [usageTty], [], 0);
|
|
88
|
+
await testCase("flag", ["--color=never", "--help"], [usageNone], [], 0);
|
|
89
|
+
await testCase("flag", ["--color=mock", "--help"], [usageMock], [], 0);
|
|
90
|
+
await testCase("flag", ["--help"], [usageMock], [], 0);
|
|
91
|
+
await testCase("env", ["--help"], [usageMock], [], 0);
|
|
92
|
+
await testCase("always", ["--help"], [usageTty], [], 0);
|
|
93
|
+
await testCase("never", ["--help"], [usageNone], [], 0);
|
|
94
|
+
await testCase("mock", ["--help"], [usageMock], [], 0);
|
|
95
|
+
delete process.env["MOCK_COLOR"];
|
|
96
|
+
|
|
97
|
+
process.env["MOCK_COLOR"] = "";
|
|
98
|
+
await testCase(
|
|
99
|
+
"flag",
|
|
100
|
+
["--color=42"],
|
|
101
|
+
[],
|
|
102
|
+
[
|
|
103
|
+
usageMock,
|
|
104
|
+
'{{Error:}@darkRed}+ {{--color}@darkCyan}+: {{<color-mode>}@darkBlue}+: Invalid value: {{"42"}@darkYellow}+ (expected one of: {{"auto"}@darkYellow}+ | {{"always"}@darkYellow}+ | {{"never"}@darkYellow}+...)',
|
|
105
|
+
],
|
|
106
|
+
1,
|
|
107
|
+
);
|
|
108
|
+
delete process.env["MOCK_COLOR"];
|
|
109
|
+
|
|
110
|
+
const unexpected1 = "Error: Unexpected unknown option: --color";
|
|
111
|
+
await testCase("never", ["--color=auto"], [], [usageNone, unexpected1], 1);
|
|
112
|
+
await testCase("never", ["--color=always"], [], [usageNone, unexpected1], 1);
|
|
113
|
+
await testCase("never", ["--color=never"], [], [usageNone, unexpected1], 1);
|
|
114
|
+
await testCase("never", ["--color=mock"], [], [usageNone, unexpected1], 1);
|
|
115
|
+
process.env["FORCE_COLOR"] = "0";
|
|
116
|
+
await testCase("env", ["--color=auto"], [], [usageNone, unexpected1], 1);
|
|
117
|
+
await testCase("env", ["--color=always"], [], [usageNone, unexpected1], 1);
|
|
118
|
+
await testCase("env", ["--color=never"], [], [usageNone, unexpected1], 1);
|
|
119
|
+
await testCase("env", ["--color=mock"], [], [usageNone, unexpected1], 1);
|
|
120
|
+
delete process.env["FORCE_COLOR"];
|
|
121
|
+
|
|
122
|
+
const unexpected2 =
|
|
123
|
+
"{{Error:}@darkRed}+ Unexpected unknown option: {{--color}@darkYellow}+";
|
|
124
|
+
await testCase("mock", ["--color=auto"], [], [usageMock, unexpected2], 1);
|
|
125
|
+
await testCase("mock", ["--color=always"], [], [usageMock, unexpected2], 1);
|
|
126
|
+
await testCase("mock", ["--color=never"], [], [usageMock, unexpected2], 1);
|
|
127
|
+
await testCase("mock", ["--color=mock"], [], [usageMock, unexpected2], 1);
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
async function testCase(
|
|
131
|
+
colorSetup: "flag" | "env" | "always" | "never" | "mock",
|
|
132
|
+
cliArgs: Array<string>,
|
|
133
|
+
expectStdOut: Array<string>,
|
|
134
|
+
expectStdErr: Array<string>,
|
|
135
|
+
expectExit: number,
|
|
136
|
+
) {
|
|
137
|
+
const onLogStdOut = makeMocked<string, void>([
|
|
138
|
+
null as unknown as void,
|
|
139
|
+
null as unknown as void,
|
|
140
|
+
]);
|
|
141
|
+
const onLogStdErr = makeMocked<string, void>([
|
|
142
|
+
null as unknown as void,
|
|
143
|
+
null as unknown as void,
|
|
144
|
+
]);
|
|
145
|
+
const onExit = makeMocked<number, never>([null as never]);
|
|
146
|
+
const cmd = command<null, void>(
|
|
147
|
+
{ description: "Description" },
|
|
148
|
+
operation(
|
|
149
|
+
{
|
|
150
|
+
options: {
|
|
151
|
+
value: optionRepeatable({ long: "option", type: type() }),
|
|
152
|
+
},
|
|
153
|
+
positionals: [
|
|
154
|
+
positionalVariadics({ type: type(), description: "Variadics" }),
|
|
155
|
+
],
|
|
156
|
+
},
|
|
157
|
+
async function (_, { options: { value }, positionals: [rest] }) {
|
|
158
|
+
console.log(
|
|
159
|
+
`Has executed: ${JSON.stringify(value)}, ${JSON.stringify(rest)}`,
|
|
160
|
+
);
|
|
161
|
+
},
|
|
162
|
+
),
|
|
163
|
+
);
|
|
164
|
+
console.log = onLogStdOut.call;
|
|
165
|
+
console.error = onLogStdErr.call;
|
|
166
|
+
await runAndExit(cliName, cliArgs, null, cmd, {
|
|
167
|
+
buildVersion: "1.0.0",
|
|
168
|
+
onExit: onExit.call,
|
|
169
|
+
colorSetup,
|
|
170
|
+
});
|
|
171
|
+
expect({
|
|
172
|
+
stdOut: onLogStdOut.history,
|
|
173
|
+
stdErr: onLogStdErr.history,
|
|
174
|
+
}).toEqual({
|
|
175
|
+
stdOut: expectStdOut,
|
|
176
|
+
stdErr: expectStdErr,
|
|
177
|
+
});
|
|
178
|
+
expect(onExit.history).toEqual([expectExit]);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function makeMocked<P, R>(returns: Array<R>) {
|
|
182
|
+
const history = new Array<P>();
|
|
183
|
+
return {
|
|
184
|
+
history,
|
|
185
|
+
call(p: P) {
|
|
186
|
+
history.push(p);
|
|
187
|
+
if (history.length > returns.length) {
|
|
188
|
+
throw new Error(
|
|
189
|
+
`Mocked function called more times than expected. History: ${JSON.stringify(
|
|
190
|
+
history,
|
|
191
|
+
)}, returns: ${JSON.stringify(returns)}`,
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
return returns[history.length - 1]!;
|
|
195
|
+
},
|
|
196
|
+
};
|
|
197
|
+
}
|