cli-kiss 0.2.4 → 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/dist/index.d.ts +150 -164
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/docs/.vitepress/config.mts +1 -0
- package/docs/guide/01_getting_started.md +12 -13
- package/docs/guide/02_commands.md +12 -29
- package/docs/guide/03_options.md +16 -25
- 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/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 +14 -35
- package/src/lib/Operation.ts +13 -4
- package/src/lib/Option.ts +118 -162
- package/src/lib/Positional.ts +37 -62
- package/src/lib/Reader.ts +3 -3
- package/src/lib/Run.ts +74 -46
- package/src/lib/Type.ts +227 -141
- package/src/lib/Typo.ts +36 -24
- package/src/lib/Usage.ts +27 -42
- package/tests/unit.Reader.parsings.ts +50 -0
- package/tests/unit.command.execute.ts +13 -13
- package/tests/unit.command.usage.ts +60 -54
- package/tests/unit.runner.colors.ts +197 -0
- package/tests/unit.runner.cycle.ts +69 -55
- package/tests/unit.runner.errors.ts +12 -20
|
@@ -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
|
+
}
|
|
@@ -10,46 +10,46 @@ import {
|
|
|
10
10
|
positionalRequired,
|
|
11
11
|
positionalVariadics,
|
|
12
12
|
runAndExit,
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
type,
|
|
14
|
+
typeChoice,
|
|
15
|
+
typeConverted,
|
|
16
16
|
typeUrl,
|
|
17
17
|
} from "../src";
|
|
18
18
|
|
|
19
19
|
it("run", async function () {
|
|
20
20
|
const rootUsage = [
|
|
21
|
-
"Usage: my-cli <
|
|
21
|
+
"Usage: my-cli <required1> <subcommand>",
|
|
22
22
|
"",
|
|
23
23
|
"Root Description",
|
|
24
24
|
"",
|
|
25
25
|
"Positionals:",
|
|
26
|
-
" <
|
|
26
|
+
" <required1> Required1 positional description",
|
|
27
27
|
"",
|
|
28
28
|
"Subcommands:",
|
|
29
29
|
" subcommand Subcommand Description",
|
|
30
30
|
"",
|
|
31
31
|
"Options:",
|
|
32
|
-
" --flag[=no]
|
|
33
|
-
" --repeatable <
|
|
34
|
-
" --single-value <
|
|
32
|
+
" --flag[=no] Option flag description",
|
|
33
|
+
" --repeatable <string> [*] Option repeatable description",
|
|
34
|
+
" --single-value <enum(number)> Option single value description",
|
|
35
35
|
"",
|
|
36
36
|
].join("\n");
|
|
37
37
|
const subcommandUsage = [
|
|
38
|
-
"Usage: my-cli <
|
|
38
|
+
"Usage: my-cli <required1> subcommand <required2> [optional] [variadic]...",
|
|
39
39
|
"",
|
|
40
40
|
"Subcommand Description",
|
|
41
41
|
"",
|
|
42
42
|
"Positionals:",
|
|
43
|
-
" <
|
|
44
|
-
" <
|
|
45
|
-
" [
|
|
46
|
-
" [
|
|
43
|
+
" <required1> Required1 positional description",
|
|
44
|
+
" <required2> Required2 positional description",
|
|
45
|
+
" [optional] Optional positional description",
|
|
46
|
+
" [variadic]... Variadics positional description",
|
|
47
47
|
"",
|
|
48
48
|
"Options:",
|
|
49
|
-
" --flag[=no]
|
|
50
|
-
" --repeatable <
|
|
51
|
-
" --single-value <
|
|
52
|
-
" --url <
|
|
49
|
+
" --flag[=no] Option flag description",
|
|
50
|
+
" --repeatable <string> [*] Option repeatable description",
|
|
51
|
+
" --single-value <enum(number)> Option single value description",
|
|
52
|
+
" --url <url> [*] Option url description",
|
|
53
53
|
"",
|
|
54
54
|
].join("\n");
|
|
55
55
|
|
|
@@ -97,6 +97,20 @@ it("run", async function () {
|
|
|
97
97
|
await testCase(["--version", "--help"], [rootUsage], [], 0);
|
|
98
98
|
await testCase(["--help", "--version"], [rootUsage], [], 0);
|
|
99
99
|
|
|
100
|
+
// Weird help/version values inputs
|
|
101
|
+
await testCase(
|
|
102
|
+
["--help=invalid"],
|
|
103
|
+
[],
|
|
104
|
+
[rootUsage, `Error: --help: value: Not a boolean: "invalid"`],
|
|
105
|
+
1,
|
|
106
|
+
);
|
|
107
|
+
await testCase(
|
|
108
|
+
["--version=invalid"],
|
|
109
|
+
[],
|
|
110
|
+
[rootUsage, `Error: --version: value: Not a boolean: "invalid"`],
|
|
111
|
+
1,
|
|
112
|
+
);
|
|
113
|
+
|
|
100
114
|
// Test multiple errors at once (first one should show only)
|
|
101
115
|
await testCase(
|
|
102
116
|
["--invalid1", "--invalid2", "required1", "--invalid3"],
|
|
@@ -107,7 +121,7 @@ it("run", async function () {
|
|
|
107
121
|
await testCase(
|
|
108
122
|
["required1", "unknown", "-wut", "--flag", "--single-value"],
|
|
109
123
|
[],
|
|
110
|
-
[rootUsage, 'Error: <
|
|
124
|
+
[rootUsage, 'Error: <subcommand>: Invalid value: "unknown"'],
|
|
111
125
|
1,
|
|
112
126
|
);
|
|
113
127
|
|
|
@@ -115,19 +129,19 @@ it("run", async function () {
|
|
|
115
129
|
await testCase(
|
|
116
130
|
[],
|
|
117
131
|
[],
|
|
118
|
-
[rootUsage, "Error: <
|
|
132
|
+
[rootUsage, "Error: <required1>: Is required, but was not provided"],
|
|
119
133
|
1,
|
|
120
134
|
);
|
|
121
135
|
await testCase(
|
|
122
136
|
["required1"],
|
|
123
137
|
[],
|
|
124
|
-
[rootUsage, "Error: <
|
|
138
|
+
[rootUsage, "Error: <subcommand>: Is required, but was not provided"],
|
|
125
139
|
1,
|
|
126
140
|
);
|
|
127
141
|
await testCase(
|
|
128
142
|
["required1", "subcommand"],
|
|
129
143
|
[],
|
|
130
|
-
[subcommandUsage, "Error: <
|
|
144
|
+
[subcommandUsage, "Error: <required2>: Is required, but was not provided"],
|
|
131
145
|
1,
|
|
132
146
|
);
|
|
133
147
|
|
|
@@ -147,7 +161,7 @@ it("run", async function () {
|
|
|
147
161
|
await testCase(
|
|
148
162
|
["required1", "subcommand", "--url", "https://example.com"],
|
|
149
163
|
[],
|
|
150
|
-
[subcommandUsage, "Error: <
|
|
164
|
+
[subcommandUsage, "Error: <required2>: Is required, but was not provided"],
|
|
151
165
|
1,
|
|
152
166
|
);
|
|
153
167
|
await testCase(
|
|
@@ -167,7 +181,7 @@ it("run", async function () {
|
|
|
167
181
|
await testCase(
|
|
168
182
|
["--flag=42", "required1", "subcommand", "required2"],
|
|
169
183
|
[],
|
|
170
|
-
[subcommandUsage, 'Error: --flag:
|
|
184
|
+
[subcommandUsage, 'Error: --flag: value: Not a boolean: "42"'],
|
|
171
185
|
1,
|
|
172
186
|
);
|
|
173
187
|
await testCase(
|
|
@@ -182,6 +196,12 @@ it("run", async function () {
|
|
|
182
196
|
[],
|
|
183
197
|
0,
|
|
184
198
|
);
|
|
199
|
+
await testCase(
|
|
200
|
+
["--flag", "required1", "subcommand", "required2"],
|
|
201
|
+
["Has executed root command", "Has executed subcommand"],
|
|
202
|
+
[],
|
|
203
|
+
0,
|
|
204
|
+
);
|
|
185
205
|
|
|
186
206
|
// Test option parsing errors
|
|
187
207
|
await testCase(
|
|
@@ -219,7 +239,7 @@ it("run", async function () {
|
|
|
219
239
|
await testCase(
|
|
220
240
|
["invalid"],
|
|
221
241
|
[],
|
|
222
|
-
[rootUsage, "Error: <
|
|
242
|
+
[rootUsage, "Error: <subcommand>: Is required, but was not provided"],
|
|
223
243
|
1,
|
|
224
244
|
);
|
|
225
245
|
await testCase(
|
|
@@ -227,7 +247,7 @@ it("run", async function () {
|
|
|
227
247
|
[],
|
|
228
248
|
[
|
|
229
249
|
subcommandUsage,
|
|
230
|
-
'Error: <
|
|
250
|
+
'Error: <required1>: Invalid value: "invalid" (expected one of: "required1" | "required1-bis")',
|
|
231
251
|
],
|
|
232
252
|
1,
|
|
233
253
|
);
|
|
@@ -236,7 +256,7 @@ it("run", async function () {
|
|
|
236
256
|
[],
|
|
237
257
|
[
|
|
238
258
|
subcommandUsage,
|
|
239
|
-
'Error: <
|
|
259
|
+
'Error: <required1>: Invalid value: "invalid" (expected one of: "required1" | "required1-bis")',
|
|
240
260
|
],
|
|
241
261
|
1,
|
|
242
262
|
);
|
|
@@ -245,7 +265,7 @@ it("run", async function () {
|
|
|
245
265
|
[],
|
|
246
266
|
[
|
|
247
267
|
subcommandUsage,
|
|
248
|
-
'Error: <
|
|
268
|
+
'Error: <required2>: Invalid value: "invalid" (expected one of: "required2" | "required2-bis")',
|
|
249
269
|
],
|
|
250
270
|
1,
|
|
251
271
|
);
|
|
@@ -254,7 +274,7 @@ it("run", async function () {
|
|
|
254
274
|
[],
|
|
255
275
|
[
|
|
256
276
|
subcommandUsage,
|
|
257
|
-
'Error: <
|
|
277
|
+
'Error: <required1>: Invalid value: "invalid" (expected one of: "required1" | "required1-bis")',
|
|
258
278
|
],
|
|
259
279
|
1,
|
|
260
280
|
);
|
|
@@ -265,7 +285,7 @@ it("run", async function () {
|
|
|
265
285
|
[],
|
|
266
286
|
[
|
|
267
287
|
subcommandUsage,
|
|
268
|
-
'Error: --single-value: <
|
|
288
|
+
'Error: --single-value: <enum(number)>: from: enum(string): Invalid value: "dodo" (expected one of: "42" | "43")',
|
|
269
289
|
],
|
|
270
290
|
1,
|
|
271
291
|
);
|
|
@@ -274,7 +294,7 @@ it("run", async function () {
|
|
|
274
294
|
[],
|
|
275
295
|
[
|
|
276
296
|
subcommandUsage,
|
|
277
|
-
'Error: --single-value: <
|
|
297
|
+
'Error: --single-value: <enum(number)>: from: enum(string): Invalid value: "44" (expected one of: "42" | "43")',
|
|
278
298
|
],
|
|
279
299
|
1,
|
|
280
300
|
);
|
|
@@ -289,7 +309,7 @@ it("run", async function () {
|
|
|
289
309
|
await testCase(
|
|
290
310
|
["required1", "subcommand", "required2", "--url", "not-a-url"],
|
|
291
311
|
[],
|
|
292
|
-
[subcommandUsage, 'Error: --url: <
|
|
312
|
+
[subcommandUsage, 'Error: --url: <url>: Not an URL: "not-a-url"'],
|
|
293
313
|
1,
|
|
294
314
|
);
|
|
295
315
|
|
|
@@ -317,10 +337,7 @@ it("run", async function () {
|
|
|
317
337
|
"43",
|
|
318
338
|
],
|
|
319
339
|
[],
|
|
320
|
-
[
|
|
321
|
-
subcommandUsage,
|
|
322
|
-
"Error: --single-value: Requires a single value, but got multiple",
|
|
323
|
-
],
|
|
340
|
+
[subcommandUsage, "Error: --single-value: Must not be set multiple times"],
|
|
324
341
|
1,
|
|
325
342
|
);
|
|
326
343
|
});
|
|
@@ -351,29 +368,29 @@ async function testCase(
|
|
|
351
368
|
}),
|
|
352
369
|
optionRepeatable: optionRepeatable({
|
|
353
370
|
long: "repeatable",
|
|
354
|
-
type:
|
|
371
|
+
type: type(),
|
|
355
372
|
description: "Option repeatable description",
|
|
356
373
|
}),
|
|
357
374
|
optionSingleValue: optionSingleValue({
|
|
358
375
|
long: "single-value",
|
|
359
|
-
type:
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
376
|
+
type: typeConverted(
|
|
377
|
+
"enum(number)",
|
|
378
|
+
typeChoice("enum(string)", ["42", "43"]),
|
|
379
|
+
(value) => Number(value),
|
|
380
|
+
),
|
|
363
381
|
description: "Option single value description",
|
|
364
|
-
|
|
382
|
+
defaultWhenNotDefined: () => 42,
|
|
365
383
|
}),
|
|
366
384
|
},
|
|
367
385
|
positionals: [
|
|
368
386
|
positionalRequired({
|
|
369
|
-
type:
|
|
370
|
-
label: "REQUIRED1",
|
|
387
|
+
type: typeChoice("required1", ["required1", "required1-bis"]),
|
|
371
388
|
description: "Required1 positional description",
|
|
372
389
|
}),
|
|
373
390
|
],
|
|
374
391
|
},
|
|
375
|
-
async function () {
|
|
376
|
-
console.log(
|
|
392
|
+
async function (_, _inputs) {
|
|
393
|
+
console.log(`Has executed root command`);
|
|
377
394
|
},
|
|
378
395
|
),
|
|
379
396
|
{
|
|
@@ -385,30 +402,27 @@ async function testCase(
|
|
|
385
402
|
optionExtra: optionRepeatable({
|
|
386
403
|
long: "url",
|
|
387
404
|
description: "Option url description",
|
|
388
|
-
type: typeUrl,
|
|
405
|
+
type: typeUrl("url"),
|
|
389
406
|
}),
|
|
390
407
|
},
|
|
391
408
|
positionals: [
|
|
392
409
|
positionalRequired({
|
|
393
|
-
type:
|
|
394
|
-
label: "REQUIRED2",
|
|
410
|
+
type: typeChoice("required2", ["required2", "required2-bis"]),
|
|
395
411
|
description: "Required2 positional description",
|
|
396
412
|
}),
|
|
397
413
|
positionalOptional({
|
|
398
|
-
|
|
399
|
-
type: typeString,
|
|
414
|
+
type: type("optional"),
|
|
400
415
|
description: "Optional positional description",
|
|
401
416
|
default: () => "world !",
|
|
402
417
|
}),
|
|
403
418
|
positionalVariadics({
|
|
404
|
-
|
|
405
|
-
type: typeString,
|
|
419
|
+
type: type("variadic"),
|
|
406
420
|
description: "Variadics positional description",
|
|
407
421
|
}),
|
|
408
422
|
],
|
|
409
423
|
},
|
|
410
|
-
async function () {
|
|
411
|
-
console.log(
|
|
424
|
+
async function (_, _inputs) {
|
|
425
|
+
console.log(`Has executed subcommand`);
|
|
412
426
|
},
|
|
413
427
|
),
|
|
414
428
|
),
|
|
@@ -418,8 +432,8 @@ async function testCase(
|
|
|
418
432
|
console.error = onLogStdErr.call;
|
|
419
433
|
await runAndExit("my-cli", args, null, cmd, {
|
|
420
434
|
buildVersion: "1.0.0",
|
|
421
|
-
useTtyColors: false,
|
|
422
435
|
onExit: onExit.call,
|
|
436
|
+
colorSetup: "never",
|
|
423
437
|
});
|
|
424
438
|
expect({
|
|
425
439
|
stdOut: onLogStdOut.history,
|
|
@@ -24,28 +24,24 @@ it("run", async function () {
|
|
|
24
24
|
"{{Error:}@darkRed}+ {{--flag}@darkCyan}+: Must not be set multiple times",
|
|
25
25
|
);
|
|
26
26
|
await testCase(
|
|
27
|
-
["--flag"
|
|
28
|
-
|
|
29
|
-
);
|
|
30
|
-
await testCase(
|
|
31
|
-
["--no-flag", "--no-flag"],
|
|
32
|
-
"{{Error:}@darkRed}+ {{--no-flag}@darkCyan}+: Must not be set multiple times",
|
|
27
|
+
["--flag=invalid"],
|
|
28
|
+
'{{Error:}@darkRed}+ {{--flag}@darkCyan}+: {{value}@darkMagenta}+: Not a boolean: {{"invalid"}@darkYellow}+',
|
|
33
29
|
);
|
|
34
30
|
await testCase(
|
|
35
31
|
["--single-value=a", "--single-value=b"],
|
|
36
|
-
"{{Error:}@darkRed}+ {{--single-value}@darkCyan}+:
|
|
32
|
+
"{{Error:}@darkRed}+ {{--single-value}@darkCyan}+: Must not be set multiple times",
|
|
37
33
|
);
|
|
38
34
|
await testCase(
|
|
39
|
-
["--
|
|
40
|
-
|
|
35
|
+
["--single-value"],
|
|
36
|
+
"{{Error:}@darkRed}+ {{--single-value}@darkCyan}+: Requires a value, but got end of input",
|
|
41
37
|
);
|
|
42
38
|
await testCase(
|
|
43
39
|
["--single-value=invalid"],
|
|
44
|
-
'{{Error:}@darkRed}+ {{--single-value}@darkCyan}+: {{<
|
|
40
|
+
'{{Error:}@darkRed}+ {{--single-value}@darkCyan}+: {{<location>}@darkBlue}+: Not an URL: {{"invalid"}@darkYellow}+',
|
|
45
41
|
);
|
|
46
42
|
await testCase(
|
|
47
43
|
["--repeatable=invalid"],
|
|
48
|
-
'{{Error:}@darkRed}+ {{--repeatable}@darkCyan}+: {{<
|
|
44
|
+
'{{Error:}@darkRed}+ {{--repeatable}@darkCyan}+: {{<index>}@darkBlue}+: Not a number: {{"invalid"}@darkYellow}+',
|
|
49
45
|
);
|
|
50
46
|
});
|
|
51
47
|
|
|
@@ -58,19 +54,15 @@ async function testCase(args: Array<string>, error: string) {
|
|
|
58
54
|
operation(
|
|
59
55
|
{
|
|
60
56
|
options: {
|
|
61
|
-
optionFlag: optionFlag({
|
|
62
|
-
long: "flag",
|
|
63
|
-
}),
|
|
57
|
+
optionFlag: optionFlag({ long: "flag" }),
|
|
64
58
|
optionSingleValue: optionSingleValue({
|
|
65
|
-
label: "LOCATION",
|
|
66
59
|
long: "single-value",
|
|
67
|
-
type: typeUrl,
|
|
68
|
-
|
|
60
|
+
type: typeUrl("location"),
|
|
61
|
+
defaultWhenNotDefined: () => undefined,
|
|
69
62
|
}),
|
|
70
63
|
optionRepeatable: optionRepeatable({
|
|
71
|
-
label: "INDEX",
|
|
72
64
|
long: "repeatable",
|
|
73
|
-
type: typeNumber,
|
|
65
|
+
type: typeNumber("index"),
|
|
74
66
|
}),
|
|
75
67
|
},
|
|
76
68
|
positionals: [],
|
|
@@ -83,7 +75,7 @@ async function testCase(args: Array<string>, error: string) {
|
|
|
83
75
|
await runAndExit("my-cli", args, null, rootCommand, {
|
|
84
76
|
buildVersion: "1.0.0",
|
|
85
77
|
usageOnError: false,
|
|
86
|
-
|
|
78
|
+
colorSetup: "mock",
|
|
87
79
|
onExit: onExit.call,
|
|
88
80
|
});
|
|
89
81
|
expect(onLogStdOut.history).toEqual([]);
|