cli-kiss 0.0.1
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/.github/workflows/ci.yml +21 -0
- package/.prettierrc +13 -0
- package/README.md +3 -0
- package/dist/index.d.ts +159 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/jest.config.ts +6 -0
- package/package.json +23 -0
- package/src/index.ts +7 -0
- package/src/lib/Argument.ts +93 -0
- package/src/lib/Command.ts +146 -0
- package/src/lib/Option.ts +140 -0
- package/src/lib/Processor.ts +87 -0
- package/src/lib/Reader.ts +285 -0
- package/src/lib/Run.ts +58 -0
- package/src/lib/Type.ts +59 -0
- package/src/lib/Usage.ts +104 -0
- package/tests/unit.Reader.aliases.ts +46 -0
- package/tests/unit.Reader.commons.ts +124 -0
- package/tests/unit.Reader.shortBig.ts +86 -0
- package/tests/unit.command.run.ts +139 -0
- package/tests/unit.command.usage.ts +153 -0
- package/tsconfig.json +27 -0
package/src/lib/Usage.ts
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { CommandUsage } from "./Command";
|
|
2
|
+
|
|
3
|
+
export function usageFormatter(
|
|
4
|
+
cliName: string,
|
|
5
|
+
commandUsage: CommandUsage,
|
|
6
|
+
): string {
|
|
7
|
+
const lines = new Array<string>();
|
|
8
|
+
if (commandUsage.description) {
|
|
9
|
+
lines.push("");
|
|
10
|
+
lines.push(commandUsage.description);
|
|
11
|
+
}
|
|
12
|
+
lines.push("");
|
|
13
|
+
lines.push(`Usage: ${cliName} ${commandUsage.breadcrumbs.join(" ")}`);
|
|
14
|
+
if (commandUsage.arguments.length > 0) {
|
|
15
|
+
lines.push("");
|
|
16
|
+
lines.push("Arguments:");
|
|
17
|
+
lines.push("");
|
|
18
|
+
const rows = new Array<Array<string>>();
|
|
19
|
+
for (const argumentUsage of commandUsage.arguments) {
|
|
20
|
+
const columns = new Array<string>();
|
|
21
|
+
columns.push("");
|
|
22
|
+
columns.push(argumentUsage.label);
|
|
23
|
+
columns.push("");
|
|
24
|
+
if (argumentUsage.description) {
|
|
25
|
+
columns.push(argumentUsage.description);
|
|
26
|
+
}
|
|
27
|
+
rows.push(columns);
|
|
28
|
+
}
|
|
29
|
+
pushGrid(lines, rows);
|
|
30
|
+
}
|
|
31
|
+
if (commandUsage.options.length > 0) {
|
|
32
|
+
lines.push("");
|
|
33
|
+
lines.push("Options:");
|
|
34
|
+
lines.push("");
|
|
35
|
+
const rows = new Array<Array<string>>();
|
|
36
|
+
for (const optionUsage of commandUsage.options) {
|
|
37
|
+
const columns = new Array<string>();
|
|
38
|
+
columns.push("");
|
|
39
|
+
if (optionUsage.short) {
|
|
40
|
+
columns.push(`-${optionUsage.short},`);
|
|
41
|
+
} else {
|
|
42
|
+
columns.push("");
|
|
43
|
+
}
|
|
44
|
+
if (optionUsage.label) {
|
|
45
|
+
columns.push(`--${optionUsage.long} ${optionUsage.label}`);
|
|
46
|
+
} else {
|
|
47
|
+
columns.push(`--${optionUsage.long}`);
|
|
48
|
+
}
|
|
49
|
+
columns.push("");
|
|
50
|
+
if (optionUsage.description) {
|
|
51
|
+
columns.push(optionUsage.description);
|
|
52
|
+
}
|
|
53
|
+
rows.push(columns);
|
|
54
|
+
}
|
|
55
|
+
pushGrid(lines, rows);
|
|
56
|
+
}
|
|
57
|
+
if (commandUsage.subcommands.length > 0) {
|
|
58
|
+
lines.push("");
|
|
59
|
+
lines.push("Subcommands:");
|
|
60
|
+
lines.push("");
|
|
61
|
+
const rows = new Array<Array<string>>();
|
|
62
|
+
for (const subcommand of commandUsage.subcommands) {
|
|
63
|
+
const columns = new Array<string>();
|
|
64
|
+
columns.push("");
|
|
65
|
+
columns.push(subcommand.name);
|
|
66
|
+
columns.push("");
|
|
67
|
+
if (subcommand.description) {
|
|
68
|
+
columns.push(subcommand.description);
|
|
69
|
+
}
|
|
70
|
+
rows.push(columns);
|
|
71
|
+
}
|
|
72
|
+
pushGrid(lines, rows);
|
|
73
|
+
}
|
|
74
|
+
lines.push("");
|
|
75
|
+
return lines.join("\n");
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function pushGrid(lines: Array<string>, rows: Array<Array<string>>) {
|
|
79
|
+
const widths = new Array<number>();
|
|
80
|
+
for (const row of rows) {
|
|
81
|
+
for (let columnIndex = 0; columnIndex < row.length; columnIndex++) {
|
|
82
|
+
const cell = row[columnIndex]!;
|
|
83
|
+
if (
|
|
84
|
+
widths[columnIndex] === undefined ||
|
|
85
|
+
cell.length > widths[columnIndex]!
|
|
86
|
+
) {
|
|
87
|
+
widths[columnIndex] = cell.length;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
for (const row of rows) {
|
|
92
|
+
const cells = new Array<string>();
|
|
93
|
+
for (let columnIndex = 0; columnIndex < row.length; columnIndex++) {
|
|
94
|
+
const cell = row[columnIndex]!;
|
|
95
|
+
if (columnIndex < row.length - 1) {
|
|
96
|
+
const padding = " ".repeat(widths[columnIndex]! - cell.length);
|
|
97
|
+
cells.push(cell + padding);
|
|
98
|
+
} else {
|
|
99
|
+
cells.push(cell);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
lines.push(cells.join(" "));
|
|
103
|
+
}
|
|
104
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { expect, it } from "@jest/globals";
|
|
2
|
+
import { ReaderTokenizer } from "../src";
|
|
3
|
+
|
|
4
|
+
it("run", async () => {
|
|
5
|
+
const readerTokenizer = new ReaderTokenizer([
|
|
6
|
+
"--option=1.1",
|
|
7
|
+
"--option-alias1=1.2",
|
|
8
|
+
"--option-alias2",
|
|
9
|
+
"1.3",
|
|
10
|
+
"-pts=1.4",
|
|
11
|
+
"-o",
|
|
12
|
+
"1.5",
|
|
13
|
+
"--flag-alias",
|
|
14
|
+
"-fa2",
|
|
15
|
+
]);
|
|
16
|
+
|
|
17
|
+
readerTokenizer.registerOption({
|
|
18
|
+
key: "option",
|
|
19
|
+
longs: ["option", "option-alias1", "option-alias2"],
|
|
20
|
+
shorts: ["pts", "o"],
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
readerTokenizer.registerFlag({
|
|
24
|
+
key: "flag1",
|
|
25
|
+
longs: ["flag1", "flag-alias"],
|
|
26
|
+
shorts: [],
|
|
27
|
+
});
|
|
28
|
+
readerTokenizer.registerFlag({
|
|
29
|
+
key: "flag2",
|
|
30
|
+
longs: ["flag2"],
|
|
31
|
+
shorts: ["fa2"],
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
expect(readerTokenizer.consumePositional()).toStrictEqual(undefined);
|
|
35
|
+
|
|
36
|
+
expect(readerTokenizer.consumeOption("option")).toStrictEqual([
|
|
37
|
+
"1.1",
|
|
38
|
+
"1.2",
|
|
39
|
+
"1.3",
|
|
40
|
+
"1.4",
|
|
41
|
+
"1.5",
|
|
42
|
+
]);
|
|
43
|
+
|
|
44
|
+
expect(readerTokenizer.consumeFlag("flag1")).toStrictEqual(true);
|
|
45
|
+
expect(readerTokenizer.consumeFlag("flag2")).toStrictEqual(true);
|
|
46
|
+
});
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { expect, it } from "@jest/globals";
|
|
2
|
+
import { ReaderTokenizer } from "../src";
|
|
3
|
+
|
|
4
|
+
it("run", async () => {
|
|
5
|
+
const stream = new ReaderTokenizer([
|
|
6
|
+
"positional-0",
|
|
7
|
+
"--flag-normal",
|
|
8
|
+
"--flag-positive=true",
|
|
9
|
+
"--flag-negative=false",
|
|
10
|
+
"positional-1",
|
|
11
|
+
"--option-split",
|
|
12
|
+
"1.1",
|
|
13
|
+
"--option-split",
|
|
14
|
+
"1.2",
|
|
15
|
+
"--option-join=2",
|
|
16
|
+
"-ab",
|
|
17
|
+
"3.1",
|
|
18
|
+
"-cd=4.1",
|
|
19
|
+
"-ef5.1",
|
|
20
|
+
"positional-2",
|
|
21
|
+
"-gh=false",
|
|
22
|
+
"-ij=true",
|
|
23
|
+
"-b",
|
|
24
|
+
"3.2",
|
|
25
|
+
"-d=4.2",
|
|
26
|
+
"-f5.2",
|
|
27
|
+
"positional-3",
|
|
28
|
+
"--",
|
|
29
|
+
"--not-a-flag",
|
|
30
|
+
"-mn",
|
|
31
|
+
"--",
|
|
32
|
+
"positional-4",
|
|
33
|
+
]);
|
|
34
|
+
|
|
35
|
+
expect(stream.consumePositional()).toStrictEqual("positional-0");
|
|
36
|
+
|
|
37
|
+
stream.registerFlag({
|
|
38
|
+
key: "flag-normal",
|
|
39
|
+
longs: ["flag-normal"],
|
|
40
|
+
shorts: [],
|
|
41
|
+
});
|
|
42
|
+
stream.registerFlag({
|
|
43
|
+
key: "flag-positive",
|
|
44
|
+
longs: ["flag-positive"],
|
|
45
|
+
shorts: [],
|
|
46
|
+
});
|
|
47
|
+
stream.registerFlag({
|
|
48
|
+
key: "flag-negative",
|
|
49
|
+
longs: ["flag-negative"],
|
|
50
|
+
shorts: [],
|
|
51
|
+
});
|
|
52
|
+
stream.registerFlag({
|
|
53
|
+
key: "flag-unset",
|
|
54
|
+
longs: ["flag-unset"],
|
|
55
|
+
shorts: [],
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
expect(stream.consumePositional()).toStrictEqual("positional-1");
|
|
59
|
+
|
|
60
|
+
stream.registerOption({
|
|
61
|
+
key: "option-split",
|
|
62
|
+
longs: ["option-split"],
|
|
63
|
+
shorts: [],
|
|
64
|
+
});
|
|
65
|
+
stream.registerOption({
|
|
66
|
+
key: "option-join",
|
|
67
|
+
longs: ["option-join"],
|
|
68
|
+
shorts: [],
|
|
69
|
+
});
|
|
70
|
+
stream.registerOption({
|
|
71
|
+
key: "option-unset",
|
|
72
|
+
longs: ["option-unset"],
|
|
73
|
+
shorts: [],
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
stream.registerFlag({ key: "a", longs: [], shorts: ["a"] });
|
|
77
|
+
stream.registerOption({ key: "b", longs: [], shorts: ["b"] });
|
|
78
|
+
|
|
79
|
+
stream.registerFlag({ key: "c", longs: [], shorts: ["c"] });
|
|
80
|
+
stream.registerOption({ key: "d", longs: [], shorts: ["d"] });
|
|
81
|
+
|
|
82
|
+
stream.registerFlag({ key: "e", longs: [], shorts: ["e"] });
|
|
83
|
+
stream.registerOption({ key: "f", longs: [], shorts: ["f"] });
|
|
84
|
+
|
|
85
|
+
expect(stream.consumePositional()).toStrictEqual("positional-2");
|
|
86
|
+
|
|
87
|
+
stream.registerFlag({ key: "g", longs: [], shorts: ["g"] });
|
|
88
|
+
stream.registerFlag({ key: "h", longs: [], shorts: ["h"] });
|
|
89
|
+
|
|
90
|
+
stream.registerFlag({ key: "i", longs: [], shorts: ["i"] });
|
|
91
|
+
stream.registerFlag({ key: "j", longs: [], shorts: ["j"] });
|
|
92
|
+
|
|
93
|
+
expect(stream.consumePositional()).toStrictEqual("positional-3");
|
|
94
|
+
|
|
95
|
+
expect(stream.consumePositional()).toStrictEqual("--not-a-flag");
|
|
96
|
+
expect(stream.consumePositional()).toStrictEqual("-mn");
|
|
97
|
+
expect(stream.consumePositional()).toStrictEqual("--");
|
|
98
|
+
expect(stream.consumePositional()).toStrictEqual("positional-4");
|
|
99
|
+
expect(stream.consumePositional()).toStrictEqual(undefined);
|
|
100
|
+
|
|
101
|
+
expect(stream.consumeFlag("flag-normal")).toStrictEqual(true);
|
|
102
|
+
expect(stream.consumeFlag("flag-positive")).toStrictEqual(true);
|
|
103
|
+
expect(stream.consumeFlag("flag-negative")).toStrictEqual(false);
|
|
104
|
+
expect(stream.consumeFlag("flag-unset")).toStrictEqual(undefined);
|
|
105
|
+
|
|
106
|
+
expect(stream.consumeOption("option-unset")).toStrictEqual([]);
|
|
107
|
+
expect(stream.consumeOption("option-split")).toStrictEqual(["1.1", "1.2"]);
|
|
108
|
+
expect(stream.consumeOption("option-join")).toStrictEqual(["2"]);
|
|
109
|
+
|
|
110
|
+
expect(stream.consumeFlag("a")).toStrictEqual(true);
|
|
111
|
+
expect(stream.consumeOption("b")).toStrictEqual(["3.1", "3.2"]);
|
|
112
|
+
|
|
113
|
+
expect(stream.consumeFlag("c")).toStrictEqual(true);
|
|
114
|
+
expect(stream.consumeOption("d")).toStrictEqual(["4.1", "4.2"]);
|
|
115
|
+
|
|
116
|
+
expect(stream.consumeFlag("e")).toStrictEqual(true);
|
|
117
|
+
expect(stream.consumeOption("f")).toStrictEqual(["5.1", "5.2"]);
|
|
118
|
+
|
|
119
|
+
expect(stream.consumeFlag("g")).toStrictEqual(true);
|
|
120
|
+
expect(stream.consumeFlag("h")).toStrictEqual(false);
|
|
121
|
+
|
|
122
|
+
expect(stream.consumeFlag("i")).toStrictEqual(true);
|
|
123
|
+
expect(stream.consumeFlag("j")).toStrictEqual(true);
|
|
124
|
+
});
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { expect, it } from "@jest/globals";
|
|
2
|
+
import { ReaderTokenizer } from "../src";
|
|
3
|
+
|
|
4
|
+
it("run", async () => {
|
|
5
|
+
const stream = new ReaderTokenizer([
|
|
6
|
+
"positional-0",
|
|
7
|
+
"-aasof-normal",
|
|
8
|
+
"-bbsof-positive=true",
|
|
9
|
+
"-ccsof-negative=false",
|
|
10
|
+
"positional-1",
|
|
11
|
+
"-ddsov-split",
|
|
12
|
+
"1.1",
|
|
13
|
+
"-eesov-split",
|
|
14
|
+
"1.2",
|
|
15
|
+
"-ffsov-join=2",
|
|
16
|
+
"positional-2",
|
|
17
|
+
]);
|
|
18
|
+
|
|
19
|
+
expect(stream.consumePositional()).toStrictEqual("positional-0");
|
|
20
|
+
|
|
21
|
+
stream.registerFlag({
|
|
22
|
+
key: "sof-normal",
|
|
23
|
+
shorts: ["sof-normal"],
|
|
24
|
+
longs: [],
|
|
25
|
+
});
|
|
26
|
+
stream.registerFlag({
|
|
27
|
+
key: "sof-positive",
|
|
28
|
+
longs: [],
|
|
29
|
+
shorts: ["sof-positive"],
|
|
30
|
+
});
|
|
31
|
+
stream.registerFlag({
|
|
32
|
+
key: "sof-negative",
|
|
33
|
+
longs: [],
|
|
34
|
+
shorts: ["sof-negative"],
|
|
35
|
+
});
|
|
36
|
+
stream.registerFlag({
|
|
37
|
+
key: "sof-unset",
|
|
38
|
+
longs: [],
|
|
39
|
+
shorts: ["sof-unset"],
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
stream.registerFlag({ key: "aa", longs: [], shorts: ["aa"] });
|
|
43
|
+
stream.registerFlag({ key: "bb", longs: [], shorts: ["bb"] });
|
|
44
|
+
stream.registerFlag({ key: "cc", longs: [], shorts: ["cc"] });
|
|
45
|
+
|
|
46
|
+
expect(stream.consumePositional()).toStrictEqual("positional-1");
|
|
47
|
+
|
|
48
|
+
stream.registerOption({
|
|
49
|
+
key: "sov-split",
|
|
50
|
+
longs: [],
|
|
51
|
+
shorts: ["sov-split"],
|
|
52
|
+
});
|
|
53
|
+
stream.registerOption({
|
|
54
|
+
key: "sov-join",
|
|
55
|
+
longs: [],
|
|
56
|
+
shorts: ["sov-join"],
|
|
57
|
+
});
|
|
58
|
+
stream.registerOption({
|
|
59
|
+
key: "sov-unset",
|
|
60
|
+
longs: [],
|
|
61
|
+
shorts: ["sov-unset"],
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
stream.registerFlag({ key: "dd", longs: [], shorts: ["dd"] });
|
|
65
|
+
stream.registerFlag({ key: "ee", longs: [], shorts: ["ee"] });
|
|
66
|
+
stream.registerFlag({ key: "ff", longs: [], shorts: ["ff"] });
|
|
67
|
+
|
|
68
|
+
expect(stream.consumePositional()).toStrictEqual("positional-2");
|
|
69
|
+
|
|
70
|
+
expect(stream.consumeFlag("sof-normal")).toStrictEqual(true);
|
|
71
|
+
expect(stream.consumeFlag("sof-positive")).toStrictEqual(true);
|
|
72
|
+
expect(stream.consumeFlag("sof-negative")).toStrictEqual(false);
|
|
73
|
+
expect(stream.consumeFlag("sof-unset")).toStrictEqual(undefined);
|
|
74
|
+
|
|
75
|
+
expect(stream.consumeFlag("aa")).toStrictEqual(true);
|
|
76
|
+
expect(stream.consumeFlag("bb")).toStrictEqual(true);
|
|
77
|
+
expect(stream.consumeFlag("cc")).toStrictEqual(true);
|
|
78
|
+
|
|
79
|
+
expect(stream.consumeOption("sov-unset")).toStrictEqual([]);
|
|
80
|
+
expect(stream.consumeOption("sov-split")).toStrictEqual(["1.1", "1.2"]);
|
|
81
|
+
expect(stream.consumeOption("sov-join")).toStrictEqual(["2"]);
|
|
82
|
+
|
|
83
|
+
expect(stream.consumeFlag("dd")).toStrictEqual(true);
|
|
84
|
+
expect(stream.consumeFlag("ee")).toStrictEqual(true);
|
|
85
|
+
expect(stream.consumeFlag("ff")).toStrictEqual(true);
|
|
86
|
+
});
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { expect, it } from "@jest/globals";
|
|
2
|
+
import {
|
|
3
|
+
argumentOptional,
|
|
4
|
+
argumentRequired,
|
|
5
|
+
argumentVariadics,
|
|
6
|
+
command,
|
|
7
|
+
commandWithSubcommands,
|
|
8
|
+
optionFlag,
|
|
9
|
+
optionRepeatable,
|
|
10
|
+
optionSingleValue,
|
|
11
|
+
processor,
|
|
12
|
+
runWithArgv,
|
|
13
|
+
typeNumber,
|
|
14
|
+
typeString,
|
|
15
|
+
} from "../src";
|
|
16
|
+
|
|
17
|
+
const cmd = commandWithSubcommands<string, any, any>(
|
|
18
|
+
"Root command description",
|
|
19
|
+
processor(
|
|
20
|
+
{
|
|
21
|
+
options: {
|
|
22
|
+
booleanFlag: optionFlag({ long: "boolean-flag", default: () => false }),
|
|
23
|
+
stringOption: optionSingleValue({
|
|
24
|
+
long: "string-option",
|
|
25
|
+
type: typeString,
|
|
26
|
+
default: () => undefined,
|
|
27
|
+
}),
|
|
28
|
+
numberOption: optionRepeatable({
|
|
29
|
+
long: "number-option",
|
|
30
|
+
type: typeNumber,
|
|
31
|
+
}),
|
|
32
|
+
},
|
|
33
|
+
arguments: [
|
|
34
|
+
argumentRequired({ type: typeNumber }),
|
|
35
|
+
argumentRequired({ type: typeNumber }),
|
|
36
|
+
],
|
|
37
|
+
},
|
|
38
|
+
async (context, inputs) => {
|
|
39
|
+
return { at: "root", context, inputs };
|
|
40
|
+
},
|
|
41
|
+
),
|
|
42
|
+
{
|
|
43
|
+
sub1: command(
|
|
44
|
+
"Subcommand 1 description",
|
|
45
|
+
processor(
|
|
46
|
+
{
|
|
47
|
+
options: {},
|
|
48
|
+
arguments: [argumentRequired({ type: typeString })],
|
|
49
|
+
},
|
|
50
|
+
async (context, inputs) => {
|
|
51
|
+
return { at: "sub1", context, inputs };
|
|
52
|
+
},
|
|
53
|
+
),
|
|
54
|
+
),
|
|
55
|
+
sub2: command(
|
|
56
|
+
"Subcommand 2 description",
|
|
57
|
+
processor(
|
|
58
|
+
{
|
|
59
|
+
options: {},
|
|
60
|
+
arguments: [
|
|
61
|
+
argumentRequired({ type: typeNumber }),
|
|
62
|
+
argumentOptional({ type: typeString, default: () => "42" }),
|
|
63
|
+
argumentVariadics({ type: typeString }),
|
|
64
|
+
],
|
|
65
|
+
},
|
|
66
|
+
async (context, inputs) => {
|
|
67
|
+
return { at: "sub2", context, inputs };
|
|
68
|
+
},
|
|
69
|
+
),
|
|
70
|
+
),
|
|
71
|
+
},
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
it("run", async () => {
|
|
75
|
+
const res1 = await runWithArgv(
|
|
76
|
+
["node", "script", "50", "51", "sub1", "final"],
|
|
77
|
+
"Run Context Input",
|
|
78
|
+
cmd,
|
|
79
|
+
);
|
|
80
|
+
expect(res1).toStrictEqual({
|
|
81
|
+
context: {
|
|
82
|
+
context: "Run Context Input",
|
|
83
|
+
inputs: {
|
|
84
|
+
options: {
|
|
85
|
+
booleanFlag: false,
|
|
86
|
+
stringOption: undefined,
|
|
87
|
+
numberOption: [],
|
|
88
|
+
},
|
|
89
|
+
arguments: [50, 51],
|
|
90
|
+
},
|
|
91
|
+
at: "root",
|
|
92
|
+
},
|
|
93
|
+
inputs: {
|
|
94
|
+
options: {},
|
|
95
|
+
arguments: ["final"],
|
|
96
|
+
},
|
|
97
|
+
at: "sub1",
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
const res2 = await runWithArgv(
|
|
101
|
+
[
|
|
102
|
+
"node",
|
|
103
|
+
"script",
|
|
104
|
+
"40",
|
|
105
|
+
"41",
|
|
106
|
+
"sub2",
|
|
107
|
+
"--string-option=hello",
|
|
108
|
+
"--number-option",
|
|
109
|
+
"123",
|
|
110
|
+
"--number-option",
|
|
111
|
+
"1234",
|
|
112
|
+
"88.88",
|
|
113
|
+
"a,b",
|
|
114
|
+
"final",
|
|
115
|
+
"--boolean-flag",
|
|
116
|
+
],
|
|
117
|
+
"Run Context Input",
|
|
118
|
+
cmd,
|
|
119
|
+
);
|
|
120
|
+
expect(res2).toStrictEqual({
|
|
121
|
+
context: {
|
|
122
|
+
context: "Run Context Input",
|
|
123
|
+
inputs: {
|
|
124
|
+
options: {
|
|
125
|
+
booleanFlag: true,
|
|
126
|
+
stringOption: "hello",
|
|
127
|
+
numberOption: [123, 1234],
|
|
128
|
+
},
|
|
129
|
+
arguments: [40, 41],
|
|
130
|
+
},
|
|
131
|
+
at: "root",
|
|
132
|
+
},
|
|
133
|
+
inputs: {
|
|
134
|
+
options: {},
|
|
135
|
+
arguments: [88.88, "a,b", ["final"]],
|
|
136
|
+
},
|
|
137
|
+
at: "sub2",
|
|
138
|
+
});
|
|
139
|
+
});
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { it } from "@jest/globals";
|
|
2
|
+
import {
|
|
3
|
+
argumentOptional,
|
|
4
|
+
argumentRequired,
|
|
5
|
+
argumentVariadics,
|
|
6
|
+
Command,
|
|
7
|
+
command,
|
|
8
|
+
commandWithSubcommands,
|
|
9
|
+
optionFlag,
|
|
10
|
+
optionRepeatable,
|
|
11
|
+
optionSingleValue,
|
|
12
|
+
processor,
|
|
13
|
+
ReaderTokenizer,
|
|
14
|
+
typeNumber,
|
|
15
|
+
typeString,
|
|
16
|
+
} from "../src";
|
|
17
|
+
import { usageFormatter } from "../src/lib/Usage";
|
|
18
|
+
|
|
19
|
+
const cmd = commandWithSubcommands<string, any, any>(
|
|
20
|
+
"Root command description",
|
|
21
|
+
processor(
|
|
22
|
+
{
|
|
23
|
+
options: {
|
|
24
|
+
booleanFlag: optionFlag({
|
|
25
|
+
short: "b",
|
|
26
|
+
long: "boolean-flag",
|
|
27
|
+
description: "Root boolean-flag description",
|
|
28
|
+
}),
|
|
29
|
+
stringOption: optionSingleValue({
|
|
30
|
+
short: "s",
|
|
31
|
+
long: "string-option",
|
|
32
|
+
type: typeString,
|
|
33
|
+
default: () => undefined,
|
|
34
|
+
label: "COOL_STUFF",
|
|
35
|
+
description: "Root string-option description",
|
|
36
|
+
}),
|
|
37
|
+
numberOption: optionRepeatable({
|
|
38
|
+
short: "n",
|
|
39
|
+
long: "number-option",
|
|
40
|
+
type: typeNumber,
|
|
41
|
+
description: "Root number-option description",
|
|
42
|
+
}),
|
|
43
|
+
},
|
|
44
|
+
arguments: [
|
|
45
|
+
argumentRequired({
|
|
46
|
+
label: "POSITIONAL-1",
|
|
47
|
+
description: "First positional argument",
|
|
48
|
+
type: typeNumber,
|
|
49
|
+
}),
|
|
50
|
+
argumentRequired({
|
|
51
|
+
label: "POSITIONAL-2",
|
|
52
|
+
description: "Second positional argument",
|
|
53
|
+
type: typeNumber,
|
|
54
|
+
}),
|
|
55
|
+
],
|
|
56
|
+
},
|
|
57
|
+
async (context, inputs) => {
|
|
58
|
+
return { at: "root", context, inputs };
|
|
59
|
+
},
|
|
60
|
+
),
|
|
61
|
+
{
|
|
62
|
+
sub1: command(
|
|
63
|
+
"Subcommand 1 description",
|
|
64
|
+
processor(
|
|
65
|
+
{
|
|
66
|
+
options: {},
|
|
67
|
+
arguments: [
|
|
68
|
+
argumentRequired({
|
|
69
|
+
label: "POS-STRING",
|
|
70
|
+
description: "Positional string argument",
|
|
71
|
+
type: typeString,
|
|
72
|
+
}),
|
|
73
|
+
],
|
|
74
|
+
},
|
|
75
|
+
async (context, inputs) => {
|
|
76
|
+
return { at: "sub1", context, inputs };
|
|
77
|
+
},
|
|
78
|
+
),
|
|
79
|
+
),
|
|
80
|
+
sub2: command(
|
|
81
|
+
"Subcommand 2 description",
|
|
82
|
+
processor(
|
|
83
|
+
{
|
|
84
|
+
options: {
|
|
85
|
+
duduValue: optionSingleValue({
|
|
86
|
+
long: "dudu",
|
|
87
|
+
type: typeString,
|
|
88
|
+
default: () => "duduDefault",
|
|
89
|
+
description: "Dudu option description",
|
|
90
|
+
}),
|
|
91
|
+
},
|
|
92
|
+
arguments: [
|
|
93
|
+
argumentRequired({
|
|
94
|
+
label: "POS-NUMBER",
|
|
95
|
+
description: "Positional number argument",
|
|
96
|
+
type: typeNumber,
|
|
97
|
+
}),
|
|
98
|
+
argumentOptional({
|
|
99
|
+
label: "OPT-POSITIONAL",
|
|
100
|
+
description: "Optional positional argument",
|
|
101
|
+
type: typeString,
|
|
102
|
+
default: () => "42",
|
|
103
|
+
}),
|
|
104
|
+
argumentVariadics({
|
|
105
|
+
label: "VARIADIC-POSITIONALS",
|
|
106
|
+
description: "Variadic positional arguments",
|
|
107
|
+
type: typeString,
|
|
108
|
+
}),
|
|
109
|
+
],
|
|
110
|
+
},
|
|
111
|
+
async (context, inputs) => {
|
|
112
|
+
return { at: "sub2", context, inputs };
|
|
113
|
+
},
|
|
114
|
+
),
|
|
115
|
+
),
|
|
116
|
+
},
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
it("run", async () => {
|
|
120
|
+
const res0 = getUsage([], cmd);
|
|
121
|
+
console.log(res0);
|
|
122
|
+
|
|
123
|
+
const res1 = getUsage(["50", "51", "sub1", "final"], cmd);
|
|
124
|
+
console.log(res1);
|
|
125
|
+
//expect(res1).toStrictEqual({});
|
|
126
|
+
|
|
127
|
+
const res2 = getUsage(
|
|
128
|
+
[
|
|
129
|
+
"40",
|
|
130
|
+
"41",
|
|
131
|
+
"sub2",
|
|
132
|
+
"--string-option=hello",
|
|
133
|
+
"--number-option",
|
|
134
|
+
"123",
|
|
135
|
+
"--number-option",
|
|
136
|
+
"1234",
|
|
137
|
+
"88.88",
|
|
138
|
+
"a,b",
|
|
139
|
+
"final",
|
|
140
|
+
"--boolean-flag",
|
|
141
|
+
],
|
|
142
|
+
cmd,
|
|
143
|
+
);
|
|
144
|
+
console.log(res2);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
function getUsage<Context, Result>(
|
|
148
|
+
argv: Array<string>,
|
|
149
|
+
command: Command<Context, Result>,
|
|
150
|
+
) {
|
|
151
|
+
const commandRunner = command.prepareRunner(new ReaderTokenizer(argv));
|
|
152
|
+
return usageFormatter("my-cli", commandRunner.computeUsage());
|
|
153
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "bundler",
|
|
6
|
+
"outDir": "dist",
|
|
7
|
+
"declaration": true,
|
|
8
|
+
"resolveJsonModule": true,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"strict": true,
|
|
11
|
+
"alwaysStrict": true,
|
|
12
|
+
"noImplicitOverride": true,
|
|
13
|
+
"noImplicitAny": true,
|
|
14
|
+
"noImplicitThis": true,
|
|
15
|
+
"noImplicitReturns": true,
|
|
16
|
+
"noPropertyAccessFromIndexSignature": true,
|
|
17
|
+
"noUnusedLocals": true,
|
|
18
|
+
"noUnusedParameters": true,
|
|
19
|
+
"noFallthroughCasesInSwitch": true,
|
|
20
|
+
"noUncheckedIndexedAccess": true,
|
|
21
|
+
"exactOptionalPropertyTypes": true,
|
|
22
|
+
"useUnknownInCatchVariables": true,
|
|
23
|
+
"erasableSyntaxOnly": true,
|
|
24
|
+
"forceConsistentCasingInFileNames": true
|
|
25
|
+
},
|
|
26
|
+
"include": ["src", "tests"]
|
|
27
|
+
}
|