cli-kiss 0.2.2 → 0.2.4
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 +711 -1046
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/docs/.vitepress/config.mts +1 -0
- package/docs/.vitepress/theme/index.ts +4 -0
- package/docs/.vitepress/theme/style.css +4 -0
- package/docs/guide/01_getting_started.md +4 -4
- package/docs/guide/02_commands.md +72 -41
- package/docs/guide/03_options.md +13 -14
- package/docs/guide/04_positionals.md +6 -8
- package/docs/guide/05_types.md +9 -11
- package/docs/guide/06_run.md +4 -5
- package/docs/index.md +8 -3
- package/docs/public/hero.png +0 -0
- package/package.json +1 -1
- package/src/lib/Command.ts +151 -275
- package/src/lib/Operation.ts +57 -95
- package/src/lib/Option.ts +194 -181
- package/src/lib/Positional.ts +54 -112
- package/src/lib/Reader.ts +155 -156
- package/src/lib/Run.ts +64 -69
- package/src/lib/Type.ts +89 -145
- package/src/lib/Typo.ts +131 -195
- package/src/lib/Usage.ts +203 -69
- package/tests/unit.Reader.aliases.ts +31 -15
- package/tests/unit.Reader.commons.ts +99 -43
- package/tests/unit.Reader.shortBig.ts +75 -31
- package/tests/unit.command.execute.ts +146 -91
- package/tests/unit.command.usage.ts +235 -114
- package/tests/unit.runner.cycle.ts +50 -20
- package/tests/unit.runner.errors.ts +19 -3
|
@@ -2,6 +2,7 @@ import { it } from "@jest/globals";
|
|
|
2
2
|
import {
|
|
3
3
|
command,
|
|
4
4
|
Command,
|
|
5
|
+
commandChained,
|
|
5
6
|
commandWithSubcommands,
|
|
6
7
|
operation,
|
|
7
8
|
optionFlag,
|
|
@@ -19,10 +20,16 @@ import {
|
|
|
19
20
|
usageToStyledLines,
|
|
20
21
|
} from "../src";
|
|
21
22
|
|
|
22
|
-
const
|
|
23
|
+
const rootCommand = commandChained<any, any, any>(
|
|
23
24
|
{
|
|
24
25
|
description: "Root command description",
|
|
25
|
-
details: ["Root command details.", "
|
|
26
|
+
details: ["Root command details.", "Root second line of command details."],
|
|
27
|
+
examples: [
|
|
28
|
+
{
|
|
29
|
+
explanation: "Example usage of the root command",
|
|
30
|
+
commandArgs: [{ positional: "42" }, { option: { short: "b" } }],
|
|
31
|
+
},
|
|
32
|
+
],
|
|
26
33
|
},
|
|
27
34
|
operation(
|
|
28
35
|
{
|
|
@@ -30,20 +37,7 @@ const cmd = commandWithSubcommands<string, any, any>(
|
|
|
30
37
|
booleanFlag: optionFlag({
|
|
31
38
|
short: "b",
|
|
32
39
|
long: "boolean-flag",
|
|
33
|
-
description: "
|
|
34
|
-
}),
|
|
35
|
-
stringOption: optionSingleValue({
|
|
36
|
-
short: "s",
|
|
37
|
-
long: "string-option",
|
|
38
|
-
type: typeString,
|
|
39
|
-
default: () => undefined,
|
|
40
|
-
label: "COOL-STUFF",
|
|
41
|
-
description: "Root string-option description",
|
|
42
|
-
}),
|
|
43
|
-
complexOption: optionRepeatable({
|
|
44
|
-
long: "complex-option",
|
|
45
|
-
type: typeTuple([typeNumber, typeList(typeString)]),
|
|
46
|
-
description: "Root complex-option description",
|
|
40
|
+
description: "boolean-flag description",
|
|
47
41
|
}),
|
|
48
42
|
},
|
|
49
43
|
positionals: [
|
|
@@ -52,107 +46,193 @@ const cmd = commandWithSubcommands<string, any, any>(
|
|
|
52
46
|
description: "Required positional number 1",
|
|
53
47
|
type: typeNumber,
|
|
54
48
|
}),
|
|
55
|
-
positionalRequired({
|
|
56
|
-
label: "POS-2",
|
|
57
|
-
description: "Required positional number 2",
|
|
58
|
-
type: typeNumber,
|
|
59
|
-
}),
|
|
60
49
|
],
|
|
61
50
|
},
|
|
62
|
-
async (context, inputs)
|
|
51
|
+
async function (context, inputs) {
|
|
63
52
|
return { at: "root", context, inputs };
|
|
64
53
|
},
|
|
65
54
|
),
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
"Subcommand 1 details.",
|
|
72
|
-
"Second line of subcommand 1 details.",
|
|
73
|
-
],
|
|
74
|
-
},
|
|
75
|
-
operation(
|
|
55
|
+
commandWithSubcommands<any, any, any>(
|
|
56
|
+
{
|
|
57
|
+
description: "Mid command description",
|
|
58
|
+
details: ["Mid command details.", "Mid second line of command details."],
|
|
59
|
+
examples: [
|
|
76
60
|
{
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
type: typeString,
|
|
83
|
-
}),
|
|
61
|
+
explanation: "Example usage of the mid command",
|
|
62
|
+
commandArgs: [
|
|
63
|
+
{ positional: "42" },
|
|
64
|
+
{ option: { short: "b" } },
|
|
65
|
+
{ positional: "43" },
|
|
84
66
|
],
|
|
85
67
|
},
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
),
|
|
90
|
-
),
|
|
91
|
-
sub2: command(
|
|
68
|
+
],
|
|
69
|
+
},
|
|
70
|
+
operation(
|
|
92
71
|
{
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
72
|
+
options: {
|
|
73
|
+
stringOption: optionSingleValue({
|
|
74
|
+
short: "s",
|
|
75
|
+
long: "string-option",
|
|
76
|
+
type: typeString,
|
|
77
|
+
default: () => undefined,
|
|
78
|
+
label: "COOL-STUFF",
|
|
79
|
+
description: "string-option description",
|
|
80
|
+
}),
|
|
81
|
+
complexOption: optionRepeatable({
|
|
82
|
+
long: "complex-option",
|
|
83
|
+
type: typeTuple([typeNumber, typeList(typeString)]),
|
|
84
|
+
description: "complex-option description",
|
|
85
|
+
}),
|
|
86
|
+
},
|
|
87
|
+
positionals: [
|
|
88
|
+
positionalRequired({
|
|
89
|
+
label: "POS-2",
|
|
90
|
+
description: "Required positional number 2",
|
|
91
|
+
type: typeNumber,
|
|
92
|
+
}),
|
|
98
93
|
],
|
|
99
94
|
},
|
|
100
|
-
|
|
95
|
+
async function (context, inputs) {
|
|
96
|
+
return { at: "root", context, inputs };
|
|
97
|
+
},
|
|
98
|
+
),
|
|
99
|
+
{
|
|
100
|
+
sub1: command(
|
|
101
101
|
{
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
label: "OPT-POS",
|
|
119
|
-
description: "Optional positional string",
|
|
120
|
-
hint: "Optional positional hint",
|
|
121
|
-
type: typeString,
|
|
122
|
-
default: () => "42",
|
|
123
|
-
}),
|
|
124
|
-
positionalVariadics({
|
|
125
|
-
label: "VARIADIC",
|
|
126
|
-
description: "Variadic positionals strings",
|
|
127
|
-
type: typeString,
|
|
128
|
-
}),
|
|
102
|
+
description: "Subcommand 1 description",
|
|
103
|
+
details: [
|
|
104
|
+
"Subcommand 1 details.",
|
|
105
|
+
"Subcommand 1 second line of details.",
|
|
106
|
+
],
|
|
107
|
+
examples: [
|
|
108
|
+
{
|
|
109
|
+
explanation: "Example usage of subcommand 1",
|
|
110
|
+
commandArgs: [
|
|
111
|
+
{ option: { short: "b" } },
|
|
112
|
+
{ positional: "42" },
|
|
113
|
+
{ positional: "43" },
|
|
114
|
+
{ subcommand: "sub1" },
|
|
115
|
+
{ positional: "valid" },
|
|
116
|
+
],
|
|
117
|
+
},
|
|
129
118
|
],
|
|
130
119
|
},
|
|
131
|
-
|
|
132
|
-
|
|
120
|
+
operation(
|
|
121
|
+
{
|
|
122
|
+
options: {},
|
|
123
|
+
positionals: [
|
|
124
|
+
positionalRequired({
|
|
125
|
+
label: "POS-STRING",
|
|
126
|
+
description: "Required positional string",
|
|
127
|
+
type: typeString,
|
|
128
|
+
}),
|
|
129
|
+
],
|
|
130
|
+
},
|
|
131
|
+
async function (context, inputs) {
|
|
132
|
+
return { at: "sub1", context, inputs };
|
|
133
|
+
},
|
|
134
|
+
),
|
|
135
|
+
),
|
|
136
|
+
sub2: command(
|
|
137
|
+
{
|
|
138
|
+
description: "Subcommand 2 description",
|
|
139
|
+
hint: "Subcommand 2 hint",
|
|
140
|
+
details: [
|
|
141
|
+
"Subcommand 2 details.",
|
|
142
|
+
"Subcommand 2 second line of details.",
|
|
143
|
+
],
|
|
144
|
+
examples: [
|
|
145
|
+
{
|
|
146
|
+
explanation: "Example usage of subcommand 2",
|
|
147
|
+
commandArgs: [
|
|
148
|
+
{ positional: "40" },
|
|
149
|
+
{ positional: "41" },
|
|
150
|
+
{ subcommand: "sub2" },
|
|
151
|
+
{ option: { long: "dudu", value: "hello" } },
|
|
152
|
+
{ positional: "50" },
|
|
153
|
+
],
|
|
154
|
+
},
|
|
155
|
+
],
|
|
133
156
|
},
|
|
157
|
+
operation(
|
|
158
|
+
{
|
|
159
|
+
options: {
|
|
160
|
+
duduValue: optionSingleValue({
|
|
161
|
+
long: "dudu",
|
|
162
|
+
type: typeString,
|
|
163
|
+
default: () => "duduDefault",
|
|
164
|
+
hint: "Dudu option hint",
|
|
165
|
+
description: "Dudu option description",
|
|
166
|
+
}),
|
|
167
|
+
},
|
|
168
|
+
positionals: [
|
|
169
|
+
positionalRequired({
|
|
170
|
+
label: "POS-NUMBER",
|
|
171
|
+
description: "Required positional number",
|
|
172
|
+
type: typeNumber,
|
|
173
|
+
}),
|
|
174
|
+
positionalOptional({
|
|
175
|
+
label: "OPT-POS",
|
|
176
|
+
description: "Optional positional string",
|
|
177
|
+
hint: "Optional positional hint",
|
|
178
|
+
type: typeString,
|
|
179
|
+
default: () => "42",
|
|
180
|
+
}),
|
|
181
|
+
positionalVariadics({
|
|
182
|
+
label: "VARIADIC",
|
|
183
|
+
description: "Variadic positionals strings",
|
|
184
|
+
type: typeString,
|
|
185
|
+
}),
|
|
186
|
+
],
|
|
187
|
+
},
|
|
188
|
+
async function (context, inputs) {
|
|
189
|
+
return { at: "sub2", context, inputs };
|
|
190
|
+
},
|
|
191
|
+
),
|
|
134
192
|
),
|
|
135
|
-
|
|
136
|
-
|
|
193
|
+
},
|
|
194
|
+
),
|
|
137
195
|
);
|
|
138
196
|
|
|
139
|
-
it("run", async ()
|
|
140
|
-
const usage1 = await getUsage([],
|
|
141
|
-
const usage2 = await getUsage(["50"
|
|
142
|
-
const usage3 = await getUsage(["
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
197
|
+
it("run", async function () {
|
|
198
|
+
const usage1 = await getUsage([], rootCommand);
|
|
199
|
+
const usage2 = await getUsage(["50"], rootCommand);
|
|
200
|
+
const usage3 = await getUsage(["50", "51"], rootCommand);
|
|
201
|
+
const usage4 = await getUsage(["50", "51", "sub1"], rootCommand);
|
|
202
|
+
const usage5 = await getUsage(["50", "51", "sub1", "valid"], rootCommand);
|
|
203
|
+
const usage6 = await getUsage(
|
|
204
|
+
["40", "41", "sub2", "--doesn't-exist"],
|
|
205
|
+
rootCommand,
|
|
206
|
+
);
|
|
207
|
+
const usage7 = await getUsage(
|
|
208
|
+
["40", "41", "sub2", "not-a-number"],
|
|
209
|
+
rootCommand,
|
|
210
|
+
);
|
|
149
211
|
|
|
150
|
-
|
|
151
|
-
"{{Usage:}@darkMagenta}+ {{my-cli}@darkCyan}+ {{<POS-1>}@darkBlue}+ {{
|
|
212
|
+
const usageRoot = [
|
|
213
|
+
"{{Usage:}@darkMagenta}+ {{my-cli}@darkCyan}+ {{<POS-1>}@darkBlue}+ {{[REST]...}@darkBlue}+",
|
|
152
214
|
"",
|
|
153
215
|
"{Root command description}+",
|
|
154
216
|
"{{Root command details.}-}*",
|
|
155
|
-
"{{
|
|
217
|
+
"{{Root second line of command details.}-}*",
|
|
218
|
+
"",
|
|
219
|
+
"{{Positionals:}@darkGreen}+",
|
|
220
|
+
" {{<POS-1>}@darkBlue}+ Required positional number 1",
|
|
221
|
+
"",
|
|
222
|
+
"{{Options:}@darkGreen}+",
|
|
223
|
+
" {{-b}@darkCyan}+, {{--boolean-flag}@darkCyan}+{{[=no]}-}* boolean-flag description",
|
|
224
|
+
"",
|
|
225
|
+
"{{Examples:}@darkGreen}+",
|
|
226
|
+
" {{# Example usage of the root command}-}*",
|
|
227
|
+
" {{my-cli}@darkCyan}+ {{42}@darkBlue}+ {{-b}@darkCyan}+",
|
|
228
|
+
"",
|
|
229
|
+
];
|
|
230
|
+
const usageMid = [
|
|
231
|
+
"{{Usage:}@darkMagenta}+ {{my-cli}@darkCyan}+ {{<POS-1>}@darkBlue}+ {{<POS-2>}@darkBlue}+ {{<SUBCOMMAND>}@darkBlue}+",
|
|
232
|
+
"",
|
|
233
|
+
"{Mid command description}+",
|
|
234
|
+
"{{Mid command details.}-}*",
|
|
235
|
+
"{{Mid second line of command details.}-}*",
|
|
156
236
|
"",
|
|
157
237
|
"{{Positionals:}@darkGreen}+",
|
|
158
238
|
" {{<POS-1>}@darkBlue}+ Required positional number 1",
|
|
@@ -163,17 +243,21 @@ it("run", async () => {
|
|
|
163
243
|
" {{sub2}@darkCyan}+ Subcommand 2 description {{(Subcommand 2 hint)}-}*",
|
|
164
244
|
"",
|
|
165
245
|
"{{Options:}@darkGreen}+",
|
|
166
|
-
" {{-b}@darkCyan}+, {{--boolean-flag}@darkCyan}+{{[=no]}-}*
|
|
167
|
-
" {{-s}@darkCyan}+, {{--string-option}@darkCyan}+ {{<COOL-STUFF>}@darkBlue}+
|
|
168
|
-
" {{--complex-option}@darkCyan}+ {{<NUMBER,STRING[,STRING]...>}@darkBlue}+
|
|
246
|
+
" {{-b}@darkCyan}+, {{--boolean-flag}@darkCyan}+{{[=no]}-}* boolean-flag description",
|
|
247
|
+
" {{-s}@darkCyan}+, {{--string-option}@darkCyan}+ {{<COOL-STUFF>}@darkBlue}+ string-option description",
|
|
248
|
+
" {{--complex-option}@darkCyan}+ {{<NUMBER,STRING[,STRING]...>}@darkBlue}+{{ [*]}-}* complex-option description",
|
|
169
249
|
"",
|
|
170
|
-
|
|
171
|
-
|
|
250
|
+
"{{Examples:}@darkGreen}+",
|
|
251
|
+
" {{# Example usage of the mid command}-}*",
|
|
252
|
+
" {{my-cli}@darkCyan}+ {{42}@darkBlue}+ {{-b}@darkCyan}+ {{43}@darkBlue}+",
|
|
253
|
+
"",
|
|
254
|
+
];
|
|
255
|
+
const usageSub1 = [
|
|
172
256
|
"{{Usage:}@darkMagenta}+ {{my-cli}@darkCyan}+ {{<POS-1>}@darkBlue}+ {{<POS-2>}@darkBlue}+ {{sub1}@darkCyan}+ {{<POS-STRING>}@darkBlue}+",
|
|
173
257
|
"",
|
|
174
258
|
"{Subcommand 1 description}+",
|
|
175
259
|
"{{Subcommand 1 details.}-}*",
|
|
176
|
-
"{{
|
|
260
|
+
"{{Subcommand 1 second line of details.}-}*",
|
|
177
261
|
"",
|
|
178
262
|
"{{Positionals:}@darkGreen}+",
|
|
179
263
|
" {{<POS-1>}@darkBlue}+ Required positional number 1",
|
|
@@ -181,17 +265,21 @@ it("run", async () => {
|
|
|
181
265
|
" {{<POS-STRING>}@darkBlue}+ Required positional string",
|
|
182
266
|
"",
|
|
183
267
|
"{{Options:}@darkGreen}+",
|
|
184
|
-
" {{-b}@darkCyan}+, {{--boolean-flag}@darkCyan}+{{[=no]}-}*
|
|
185
|
-
" {{-s}@darkCyan}+, {{--string-option}@darkCyan}+ {{<COOL-STUFF>}@darkBlue}+
|
|
186
|
-
" {{--complex-option}@darkCyan}+ {{<NUMBER,STRING[,STRING]...>}@darkBlue}+
|
|
268
|
+
" {{-b}@darkCyan}+, {{--boolean-flag}@darkCyan}+{{[=no]}-}* boolean-flag description",
|
|
269
|
+
" {{-s}@darkCyan}+, {{--string-option}@darkCyan}+ {{<COOL-STUFF>}@darkBlue}+ string-option description",
|
|
270
|
+
" {{--complex-option}@darkCyan}+ {{<NUMBER,STRING[,STRING]...>}@darkBlue}+{{ [*]}-}* complex-option description",
|
|
271
|
+
"",
|
|
272
|
+
"{{Examples:}@darkGreen}+",
|
|
273
|
+
" {{# Example usage of subcommand 1}-}*",
|
|
274
|
+
" {{my-cli}@darkCyan}+ {{-b}@darkCyan}+ {{42}@darkBlue}+ {{43}@darkBlue}+ {{sub1}@darkCyan}+ {{valid}@darkBlue}+",
|
|
187
275
|
"",
|
|
188
|
-
]
|
|
189
|
-
|
|
276
|
+
];
|
|
277
|
+
const usageSub2 = [
|
|
190
278
|
"{{Usage:}@darkMagenta}+ {{my-cli}@darkCyan}+ {{<POS-1>}@darkBlue}+ {{<POS-2>}@darkBlue}+ {{sub2}@darkCyan}+ {{<POS-NUMBER>}@darkBlue}+ {{[OPT-POS]}@darkBlue}+ {{[VARIADIC]...}@darkBlue}+",
|
|
191
279
|
"",
|
|
192
280
|
"{Subcommand 2 description}+ {{(Subcommand 2 hint)}-}*",
|
|
193
281
|
"{{Subcommand 2 details.}-}*",
|
|
194
|
-
"{{
|
|
282
|
+
"{{Subcommand 2 second line of details.}-}*",
|
|
195
283
|
"",
|
|
196
284
|
"{{Positionals:}@darkGreen}+",
|
|
197
285
|
" {{<POS-1>}@darkBlue}+ Required positional number 1",
|
|
@@ -201,12 +289,34 @@ it("run", async () => {
|
|
|
201
289
|
" {{[VARIADIC]...}@darkBlue}+ Variadic positionals strings",
|
|
202
290
|
"",
|
|
203
291
|
"{{Options:}@darkGreen}+",
|
|
204
|
-
" {{-b}@darkCyan}+, {{--boolean-flag}@darkCyan}+{{[=no]}-}*
|
|
205
|
-
" {{-s}@darkCyan}+, {{--string-option}@darkCyan}+ {{<COOL-STUFF>}@darkBlue}+
|
|
206
|
-
" {{--complex-option}@darkCyan}+ {{<NUMBER,STRING[,STRING]...>}@darkBlue}+
|
|
207
|
-
" {{--dudu}@darkCyan}+ {{<STRING>}@darkBlue}+
|
|
292
|
+
" {{-b}@darkCyan}+, {{--boolean-flag}@darkCyan}+{{[=no]}-}* boolean-flag description",
|
|
293
|
+
" {{-s}@darkCyan}+, {{--string-option}@darkCyan}+ {{<COOL-STUFF>}@darkBlue}+ string-option description",
|
|
294
|
+
" {{--complex-option}@darkCyan}+ {{<NUMBER,STRING[,STRING]...>}@darkBlue}+{{ [*]}-}* complex-option description",
|
|
295
|
+
" {{--dudu}@darkCyan}+ {{<STRING>}@darkBlue}+ Dudu option description {{(Dudu option hint)}-}*",
|
|
208
296
|
"",
|
|
209
|
-
|
|
297
|
+
"{{Examples:}@darkGreen}+",
|
|
298
|
+
" {{# Example usage of subcommand 2}-}*",
|
|
299
|
+
" {{my-cli}@darkCyan}+ {{40}@darkBlue}+ {{41}@darkBlue}+ {{sub2}@darkCyan}+ {{--dudu}@darkCyan}+{{=}-}*{{hello}@darkBlue}+ {{50}@darkBlue}+",
|
|
300
|
+
"",
|
|
301
|
+
];
|
|
302
|
+
|
|
303
|
+
/*
|
|
304
|
+
console.log(usage1.join("\n"));
|
|
305
|
+
console.log(usage2.join("\n"));
|
|
306
|
+
console.log(usage3.join("\n"));
|
|
307
|
+
console.log(usage4.join("\n"));
|
|
308
|
+
console.log(usage5.join("\n"));
|
|
309
|
+
console.log(usage6.join("\n"));
|
|
310
|
+
console.log(usage7.join("\n"));
|
|
311
|
+
*/
|
|
312
|
+
|
|
313
|
+
expect(usage1).toStrictEqual(usageRoot);
|
|
314
|
+
expect(usage2).toStrictEqual(usageMid);
|
|
315
|
+
expect(usage3).toStrictEqual(usageMid);
|
|
316
|
+
expect(usage4).toStrictEqual(usageSub1);
|
|
317
|
+
expect(usage5).toStrictEqual(usageSub1);
|
|
318
|
+
expect(usage6).toStrictEqual(usageSub2);
|
|
319
|
+
expect(usage7).toStrictEqual(usageSub2);
|
|
210
320
|
});
|
|
211
321
|
|
|
212
322
|
async function getUsage<Context, Result>(
|
|
@@ -214,10 +324,21 @@ async function getUsage<Context, Result>(
|
|
|
214
324
|
command: Command<Context, Result>,
|
|
215
325
|
) {
|
|
216
326
|
const readerArgs = new ReaderArgs(args);
|
|
217
|
-
const
|
|
327
|
+
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
|
+
*/
|
|
218
339
|
return usageToStyledLines({
|
|
219
340
|
cliName: "my-cli",
|
|
220
|
-
|
|
341
|
+
usage: commandDecoder.generateUsage(),
|
|
221
342
|
typoSupport: TypoSupport.mock(),
|
|
222
343
|
});
|
|
223
344
|
}
|
|
@@ -10,16 +10,13 @@ import {
|
|
|
10
10
|
positionalRequired,
|
|
11
11
|
positionalVariadics,
|
|
12
12
|
runAndExit,
|
|
13
|
-
|
|
13
|
+
typeMapped,
|
|
14
14
|
typeOneOf,
|
|
15
15
|
typeString,
|
|
16
16
|
typeUrl,
|
|
17
17
|
} from "../src";
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
// TODO - unit test for errors styling
|
|
21
|
-
|
|
22
|
-
it("run", async () => {
|
|
19
|
+
it("run", async function () {
|
|
23
20
|
const rootUsage = [
|
|
24
21
|
"Usage: my-cli <REQUIRED1> <SUBCOMMAND>",
|
|
25
22
|
"",
|
|
@@ -33,7 +30,7 @@ it("run", async () => {
|
|
|
33
30
|
"",
|
|
34
31
|
"Options:",
|
|
35
32
|
" --flag[=no] Option flag description",
|
|
36
|
-
" --repeatable <STRING>
|
|
33
|
+
" --repeatable <STRING> [*] Option repeatable description",
|
|
37
34
|
" --single-value <NUMBER-ENUM> Option single value description",
|
|
38
35
|
"",
|
|
39
36
|
].join("\n");
|
|
@@ -50,9 +47,9 @@ it("run", async () => {
|
|
|
50
47
|
"",
|
|
51
48
|
"Options:",
|
|
52
49
|
" --flag[=no] Option flag description",
|
|
53
|
-
" --repeatable <STRING>
|
|
50
|
+
" --repeatable <STRING> [*] Option repeatable description",
|
|
54
51
|
" --single-value <NUMBER-ENUM> Option single value description",
|
|
55
|
-
" --url <URL>
|
|
52
|
+
" --url <URL> [*] Option url description",
|
|
56
53
|
"",
|
|
57
54
|
].join("\n");
|
|
58
55
|
|
|
@@ -104,7 +101,7 @@ it("run", async () => {
|
|
|
104
101
|
await testCase(
|
|
105
102
|
["--invalid1", "--invalid2", "required1", "--invalid3"],
|
|
106
103
|
[],
|
|
107
|
-
[rootUsage, "Error:
|
|
104
|
+
[rootUsage, "Error: Unexpected unknown option: --invalid1"],
|
|
108
105
|
1,
|
|
109
106
|
);
|
|
110
107
|
await testCase(
|
|
@@ -138,13 +135,13 @@ it("run", async () => {
|
|
|
138
135
|
await testCase(
|
|
139
136
|
["--url", "https://example.com"],
|
|
140
137
|
[],
|
|
141
|
-
[rootUsage, "Error:
|
|
138
|
+
[rootUsage, "Error: Unexpected unknown option: --url"],
|
|
142
139
|
1,
|
|
143
140
|
);
|
|
144
141
|
await testCase(
|
|
145
142
|
["required1", "--url", "https://example.com"],
|
|
146
143
|
[],
|
|
147
|
-
[rootUsage, "Error:
|
|
144
|
+
[rootUsage, "Error: Unexpected unknown option: --url"],
|
|
148
145
|
1,
|
|
149
146
|
);
|
|
150
147
|
await testCase(
|
|
@@ -190,13 +187,13 @@ it("run", async () => {
|
|
|
190
187
|
await testCase(
|
|
191
188
|
["--invalid", "required1", "subcommand", "required2"],
|
|
192
189
|
[],
|
|
193
|
-
[rootUsage, "Error:
|
|
190
|
+
[rootUsage, "Error: Unexpected unknown option: --invalid"],
|
|
194
191
|
1,
|
|
195
192
|
);
|
|
196
193
|
await testCase(
|
|
197
194
|
["required1", "subcommand", "required2", "--nope"],
|
|
198
195
|
[],
|
|
199
|
-
[subcommandUsage, "Error:
|
|
196
|
+
[subcommandUsage, "Error: Unexpected unknown option: --nope"],
|
|
200
197
|
1,
|
|
201
198
|
);
|
|
202
199
|
await testCase(
|
|
@@ -218,7 +215,31 @@ it("run", async () => {
|
|
|
218
215
|
1,
|
|
219
216
|
);
|
|
220
217
|
|
|
221
|
-
// Test invalid positional type value
|
|
218
|
+
// Test invalid positional type value (should keep parsing best-effort even on invalid values)
|
|
219
|
+
await testCase(
|
|
220
|
+
["invalid"],
|
|
221
|
+
[],
|
|
222
|
+
[rootUsage, "Error: <SUBCOMMAND>: Is required, but was not provided"],
|
|
223
|
+
1,
|
|
224
|
+
);
|
|
225
|
+
await testCase(
|
|
226
|
+
["invalid", "subcommand"],
|
|
227
|
+
[],
|
|
228
|
+
[
|
|
229
|
+
subcommandUsage,
|
|
230
|
+
'Error: <REQUIRED1>: STRING-ENUM: Invalid value: "invalid" (expected one of: "required1" | "required1-bis")',
|
|
231
|
+
],
|
|
232
|
+
1,
|
|
233
|
+
);
|
|
234
|
+
await testCase(
|
|
235
|
+
["invalid", "subcommand", "required2"],
|
|
236
|
+
[],
|
|
237
|
+
[
|
|
238
|
+
subcommandUsage,
|
|
239
|
+
'Error: <REQUIRED1>: STRING-ENUM: Invalid value: "invalid" (expected one of: "required1" | "required1-bis")',
|
|
240
|
+
],
|
|
241
|
+
1,
|
|
242
|
+
);
|
|
222
243
|
await testCase(
|
|
223
244
|
["required1", "subcommand", "invalid"],
|
|
224
245
|
[],
|
|
@@ -228,6 +249,15 @@ it("run", async () => {
|
|
|
228
249
|
],
|
|
229
250
|
1,
|
|
230
251
|
);
|
|
252
|
+
await testCase(
|
|
253
|
+
["invalid", "subcommand", "invalid"],
|
|
254
|
+
[],
|
|
255
|
+
[
|
|
256
|
+
subcommandUsage,
|
|
257
|
+
'Error: <REQUIRED1>: STRING-ENUM: Invalid value: "invalid" (expected one of: "required1" | "required1-bis")',
|
|
258
|
+
],
|
|
259
|
+
1,
|
|
260
|
+
);
|
|
231
261
|
|
|
232
262
|
// Test root command option invalid values (must not block parsing)
|
|
233
263
|
await testCase(
|
|
@@ -253,7 +283,7 @@ it("run", async () => {
|
|
|
253
283
|
await testCase(
|
|
254
284
|
["--url", "not-a-url", "required1", "subcommand", "required2"],
|
|
255
285
|
[],
|
|
256
|
-
[rootUsage, "Error:
|
|
286
|
+
[rootUsage, "Error: Unexpected unknown option: --url"],
|
|
257
287
|
1,
|
|
258
288
|
);
|
|
259
289
|
await testCase(
|
|
@@ -326,7 +356,7 @@ async function testCase(
|
|
|
326
356
|
}),
|
|
327
357
|
optionSingleValue: optionSingleValue({
|
|
328
358
|
long: "single-value",
|
|
329
|
-
type:
|
|
359
|
+
type: typeMapped(typeOneOf("STRING-ENUM", ["42", "43"]), {
|
|
330
360
|
content: "NUMBER-ENUM",
|
|
331
361
|
decoder: (value) => Number(value),
|
|
332
362
|
}),
|
|
@@ -336,13 +366,13 @@ async function testCase(
|
|
|
336
366
|
},
|
|
337
367
|
positionals: [
|
|
338
368
|
positionalRequired({
|
|
339
|
-
type:
|
|
369
|
+
type: typeOneOf("STRING-ENUM", ["required1", "required1-bis"]),
|
|
340
370
|
label: "REQUIRED1",
|
|
341
371
|
description: "Required1 positional description",
|
|
342
372
|
}),
|
|
343
373
|
],
|
|
344
374
|
},
|
|
345
|
-
async ()
|
|
375
|
+
async function () {
|
|
346
376
|
console.log("Has executed root command");
|
|
347
377
|
},
|
|
348
378
|
),
|
|
@@ -377,7 +407,7 @@ async function testCase(
|
|
|
377
407
|
}),
|
|
378
408
|
],
|
|
379
409
|
},
|
|
380
|
-
async ()
|
|
410
|
+
async function () {
|
|
381
411
|
console.log("Has executed subcommand");
|
|
382
412
|
},
|
|
383
413
|
),
|
|
@@ -405,7 +435,7 @@ function makeMocked<P, R>(returns: Array<R>) {
|
|
|
405
435
|
const history = new Array<P>();
|
|
406
436
|
return {
|
|
407
437
|
history,
|
|
408
|
-
call
|
|
438
|
+
call(p: P) {
|
|
409
439
|
history.push(p);
|
|
410
440
|
if (history.length > returns.length) {
|
|
411
441
|
throw new Error(
|
|
@@ -10,15 +10,31 @@ import {
|
|
|
10
10
|
typeUrl,
|
|
11
11
|
} from "../src";
|
|
12
12
|
|
|
13
|
-
it("run", async ()
|
|
13
|
+
it("run", async function () {
|
|
14
14
|
await testCase(
|
|
15
15
|
["hello"],
|
|
16
16
|
'{{Error:}@darkRed}+ Unexpected argument: {{"hello"}@darkYellow}+',
|
|
17
17
|
);
|
|
18
|
+
await testCase(
|
|
19
|
+
["--nope"],
|
|
20
|
+
"{{Error:}@darkRed}+ Unexpected unknown option: {{--nope}@darkYellow}+",
|
|
21
|
+
);
|
|
18
22
|
await testCase(
|
|
19
23
|
["--flag", "--flag"],
|
|
20
24
|
"{{Error:}@darkRed}+ {{--flag}@darkCyan}+: Must not be set multiple times",
|
|
21
25
|
);
|
|
26
|
+
await testCase(
|
|
27
|
+
["--flag", "--no-flag"],
|
|
28
|
+
"{{Error:}@darkRed}+ {{--flag}@darkCyan}+: Must not be set in combination with: {{--no-flag}@darkCyan}+",
|
|
29
|
+
);
|
|
30
|
+
await testCase(
|
|
31
|
+
["--no-flag", "--no-flag"],
|
|
32
|
+
"{{Error:}@darkRed}+ {{--no-flag}@darkCyan}+: Must not be set multiple times",
|
|
33
|
+
);
|
|
34
|
+
await testCase(
|
|
35
|
+
["--single-value=a", "--single-value=b"],
|
|
36
|
+
"{{Error:}@darkRed}+ {{--single-value}@darkCyan}+: Requires a single value, but got multiple",
|
|
37
|
+
);
|
|
22
38
|
await testCase(
|
|
23
39
|
["--flag=invalid"],
|
|
24
40
|
'{{Error:}@darkRed}+ {{--flag}@darkCyan}+: {{<BOOLEAN>}@darkBlue}+: {{Boolean}@darkMagenta}+: Invalid value: {{"invalid"}@darkYellow}+',
|
|
@@ -59,7 +75,7 @@ async function testCase(args: Array<string>, error: string) {
|
|
|
59
75
|
},
|
|
60
76
|
positionals: [],
|
|
61
77
|
},
|
|
62
|
-
async ()
|
|
78
|
+
async function () {},
|
|
63
79
|
),
|
|
64
80
|
);
|
|
65
81
|
console.log = onLogStdOut.call;
|
|
@@ -79,7 +95,7 @@ function makeMocked<P, R>(returns: Array<R>) {
|
|
|
79
95
|
const history = new Array<P>();
|
|
80
96
|
return {
|
|
81
97
|
history,
|
|
82
|
-
call
|
|
98
|
+
call(p: P) {
|
|
83
99
|
history.push(p);
|
|
84
100
|
if (history.length > returns.length) {
|
|
85
101
|
throw new Error(
|