@thi.ng/args 2.2.45 → 2.2.46
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/CHANGELOG.md +1 -1
- package/README.md +1 -1
- package/api.js +17 -17
- package/args.js +90 -199
- package/coerce.js +45 -37
- package/package.json +10 -7
- package/parse.js +101 -99
- package/usage.js +64 -65
package/CHANGELOG.md
CHANGED
package/README.md
CHANGED
package/api.js
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
const DEFAULT_THEME = {
|
|
2
|
+
default: 95,
|
|
3
|
+
hint: 90,
|
|
4
|
+
multi: 90,
|
|
5
|
+
param: 96,
|
|
6
|
+
required: 33
|
|
7
7
|
};
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
}
|
|
16
|
-
deref() {
|
|
17
|
-
return this.value;
|
|
18
|
-
}
|
|
8
|
+
class Tuple {
|
|
9
|
+
constructor(value) {
|
|
10
|
+
this.value = value;
|
|
11
|
+
}
|
|
12
|
+
deref() {
|
|
13
|
+
return this.value;
|
|
14
|
+
}
|
|
19
15
|
}
|
|
16
|
+
export {
|
|
17
|
+
DEFAULT_THEME,
|
|
18
|
+
Tuple
|
|
19
|
+
};
|
package/args.js
CHANGED
|
@@ -1,214 +1,105 @@
|
|
|
1
1
|
import { identity } from "@thi.ng/api/fn";
|
|
2
2
|
import { repeat } from "@thi.ng/strings/repeat";
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
coerceFloat,
|
|
5
|
+
coerceFloats,
|
|
6
|
+
coerceHexInt,
|
|
7
|
+
coerceHexInts,
|
|
8
|
+
coerceInt,
|
|
9
|
+
coerceInts,
|
|
10
|
+
coerceJson,
|
|
11
|
+
coerceKV,
|
|
12
|
+
coerceOneOf,
|
|
13
|
+
coerceTuple
|
|
14
|
+
} from "./coerce.js";
|
|
4
15
|
const $single = (coerce, hint) => (spec) => ({
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
16
|
+
coerce,
|
|
17
|
+
hint,
|
|
18
|
+
group: "main",
|
|
19
|
+
...spec
|
|
9
20
|
});
|
|
10
21
|
const $multi = (coerce, hint) => (spec) => ({
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
22
|
+
hint: $hint(hint, spec.delim),
|
|
23
|
+
multi: true,
|
|
24
|
+
coerce,
|
|
25
|
+
group: "main",
|
|
26
|
+
...spec
|
|
16
27
|
});
|
|
17
28
|
const $hint = (hint, delim) => hint + (delim ? `[${delim}..]` : "");
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
*/
|
|
24
|
-
export const flag = (spec) => ({
|
|
25
|
-
flag: true,
|
|
26
|
-
default: false,
|
|
27
|
-
group: "flags",
|
|
28
|
-
...spec,
|
|
29
|
+
const flag = (spec) => ({
|
|
30
|
+
flag: true,
|
|
31
|
+
default: false,
|
|
32
|
+
group: "flags",
|
|
33
|
+
...spec
|
|
29
34
|
});
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
export const strings = $multi(identity, "STR");
|
|
44
|
-
/**
|
|
45
|
-
* Returns a full {@link ArgSpec} for a floating point value arg. The value
|
|
46
|
-
* will be autoatically coerced into a number using {@link coerceFloat}.
|
|
47
|
-
*
|
|
48
|
-
* @param spec -
|
|
49
|
-
*/
|
|
50
|
-
export const float = $single(coerceFloat, "NUM");
|
|
51
|
-
/**
|
|
52
|
-
* Returns a full {@link ArgSpec} for a single hex integer value arg. The value
|
|
53
|
-
* will be autoatically coerced into a number using {@link coerceHexInt}.
|
|
54
|
-
*
|
|
55
|
-
* @param spec -
|
|
56
|
-
*/
|
|
57
|
-
export const hex = $single(coerceHexInt, "HEX");
|
|
58
|
-
/**
|
|
59
|
-
* Returns a full {@link ArgSpec} for a single integer value arg. The value
|
|
60
|
-
* will be autoatically coerced into a number using {@link coerceInt}.
|
|
61
|
-
*
|
|
62
|
-
* @param spec -
|
|
63
|
-
*/
|
|
64
|
-
export const int = $single(coerceInt, "INT");
|
|
65
|
-
/**
|
|
66
|
-
* Multi-arg version of {@link float}. Returns a full {@link ArgSpec} for a
|
|
67
|
-
* multi floating point value arg. This argument can be provided mutiple times
|
|
68
|
-
* with values being coerced into numbers and collected into an array.
|
|
69
|
-
*
|
|
70
|
-
* @param spec -
|
|
71
|
-
*/
|
|
72
|
-
export const floats = $multi(coerceFloats, "NUM");
|
|
73
|
-
/**
|
|
74
|
-
* Multi-arg version of {@link hex}. Returns a full {@link ArgSpec} for a multi
|
|
75
|
-
* hex integer value arg. This argument can be provided mutiple times with
|
|
76
|
-
* values being coerced into numbers and collected into an array.
|
|
77
|
-
*
|
|
78
|
-
* @param spec -
|
|
79
|
-
*/
|
|
80
|
-
export const hexes = $multi(coerceHexInts, "HEX");
|
|
81
|
-
/**
|
|
82
|
-
* Multi-arg version of {@link int}. Returns a full {@link ArgSpec} for a multi
|
|
83
|
-
* integer value arg. This argument can be provided mutiple times with values
|
|
84
|
-
* being coerced into numbers and collected into an array.
|
|
85
|
-
*
|
|
86
|
-
* @param spec -
|
|
87
|
-
*/
|
|
88
|
-
export const ints = $multi(coerceInts, "INT");
|
|
89
|
-
/**
|
|
90
|
-
* Returns full {@link ArgSpec} for a JSON value arg. The raw CLI value string
|
|
91
|
-
* will be automcatically coerced using {@link coerceJson}.
|
|
92
|
-
*
|
|
93
|
-
* @param spec -
|
|
94
|
-
*/
|
|
95
|
-
export const json = (spec) => ({
|
|
96
|
-
coerce: coerceJson,
|
|
97
|
-
hint: "JSON",
|
|
98
|
-
group: "main",
|
|
99
|
-
...spec,
|
|
35
|
+
const string = $single(identity, "STR");
|
|
36
|
+
const strings = $multi(identity, "STR");
|
|
37
|
+
const float = $single(coerceFloat, "NUM");
|
|
38
|
+
const hex = $single(coerceHexInt, "HEX");
|
|
39
|
+
const int = $single(coerceInt, "INT");
|
|
40
|
+
const floats = $multi(coerceFloats, "NUM");
|
|
41
|
+
const hexes = $multi(coerceHexInts, "HEX");
|
|
42
|
+
const ints = $multi(coerceInts, "INT");
|
|
43
|
+
const json = (spec) => ({
|
|
44
|
+
coerce: coerceJson,
|
|
45
|
+
hint: "JSON",
|
|
46
|
+
group: "main",
|
|
47
|
+
...spec
|
|
100
48
|
});
|
|
101
49
|
const $desc = (opts, prefix) => `${prefix ? prefix + ": " : ""}${opts.map((x) => `"${x}"`).join(", ")}`;
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
*/
|
|
109
|
-
export const oneOf = (opts, spec) => ({
|
|
110
|
-
coerce: coerceOneOf(opts),
|
|
111
|
-
hint: "ID",
|
|
112
|
-
group: "main",
|
|
113
|
-
...spec,
|
|
114
|
-
desc: $desc(opts, spec.desc),
|
|
50
|
+
const oneOf = (opts, spec) => ({
|
|
51
|
+
coerce: coerceOneOf(opts),
|
|
52
|
+
hint: "ID",
|
|
53
|
+
group: "main",
|
|
54
|
+
...spec,
|
|
55
|
+
desc: $desc(opts, spec.desc)
|
|
115
56
|
});
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
*/
|
|
124
|
-
export const oneOfMulti = (opts, spec) => ({
|
|
125
|
-
coerce: (xs) => xs.map(coerceOneOf(opts)),
|
|
126
|
-
hint: $hint("ID", spec.delim),
|
|
127
|
-
multi: true,
|
|
128
|
-
group: "main",
|
|
129
|
-
...spec,
|
|
130
|
-
desc: $desc(opts, spec.desc),
|
|
57
|
+
const oneOfMulti = (opts, spec) => ({
|
|
58
|
+
coerce: (xs) => xs.map(coerceOneOf(opts)),
|
|
59
|
+
hint: $hint("ID", spec.delim),
|
|
60
|
+
multi: true,
|
|
61
|
+
group: "main",
|
|
62
|
+
...spec,
|
|
63
|
+
desc: $desc(opts, spec.desc)
|
|
131
64
|
});
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
* are allowed and will receive a `"true"` as their value. However, if `strict`
|
|
139
|
-
* is true, only full KV pairs are allowed.
|
|
140
|
-
*
|
|
141
|
-
* @param spec -
|
|
142
|
-
* @param delim -
|
|
143
|
-
*/
|
|
144
|
-
export const kvPairs = (spec, delim = "=", strict) => ({
|
|
145
|
-
coerce: coerceKV(delim, strict),
|
|
146
|
-
hint: `key${delim}val`,
|
|
147
|
-
multi: true,
|
|
148
|
-
group: "main",
|
|
149
|
-
...spec,
|
|
65
|
+
const kvPairs = (spec, delim = "=", strict) => ({
|
|
66
|
+
coerce: coerceKV(delim, strict),
|
|
67
|
+
hint: `key${delim}val`,
|
|
68
|
+
multi: true,
|
|
69
|
+
group: "main",
|
|
70
|
+
...spec
|
|
150
71
|
});
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
* @param delim -
|
|
158
|
-
* @param strict -
|
|
159
|
-
*/
|
|
160
|
-
export const kvPairsMulti = (spec, delim = "=", strict) => ({
|
|
161
|
-
coerce: coerceKV(delim, strict, true),
|
|
162
|
-
hint: `key${delim}val(s)`,
|
|
163
|
-
multi: true,
|
|
164
|
-
group: "main",
|
|
165
|
-
...spec,
|
|
72
|
+
const kvPairsMulti = (spec, delim = "=", strict) => ({
|
|
73
|
+
coerce: coerceKV(delim, strict, true),
|
|
74
|
+
hint: `key${delim}val(s)`,
|
|
75
|
+
multi: true,
|
|
76
|
+
group: "main",
|
|
77
|
+
...spec
|
|
166
78
|
});
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
*
|
|
173
|
-
* @remarks
|
|
174
|
-
* An error will be thrown if the number of extracted values differs from the
|
|
175
|
-
* specified tuple size or any value coercion fails.
|
|
176
|
-
*
|
|
177
|
-
* @example
|
|
178
|
-
* ```ts
|
|
179
|
-
* parse({ a: tuple(coerceInt, 2, {})}, ["--a", "1,2"])
|
|
180
|
-
* // {
|
|
181
|
-
* // result: { a: Tuple { value: [1, 2] } },
|
|
182
|
-
* // index: 2,
|
|
183
|
-
* // rest: [],
|
|
184
|
-
* // done: true
|
|
185
|
-
* // }
|
|
186
|
-
* ```
|
|
187
|
-
*
|
|
188
|
-
* @param coerce -
|
|
189
|
-
* @param size -
|
|
190
|
-
* @param spec -
|
|
191
|
-
* @param delim -
|
|
192
|
-
*/
|
|
193
|
-
export const tuple = (coerce, size, spec, delim = ",") => ({
|
|
194
|
-
coerce: coerceTuple(coerce, size, delim),
|
|
195
|
-
hint: [...repeat("N", size)].join(delim),
|
|
196
|
-
group: "main",
|
|
197
|
-
...spec,
|
|
79
|
+
const tuple = (coerce, size2, spec, delim = ",") => ({
|
|
80
|
+
coerce: coerceTuple(coerce, size2, delim),
|
|
81
|
+
hint: [...repeat("N", size2)].join(delim),
|
|
82
|
+
group: "main",
|
|
83
|
+
...spec
|
|
198
84
|
});
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
85
|
+
const size = (size2, spec, delim = "x") => tuple(coerceInt, size2, spec, delim);
|
|
86
|
+
const vec = (size2, spec, delim = ",") => tuple(coerceFloat, size2, spec, delim);
|
|
87
|
+
export {
|
|
88
|
+
flag,
|
|
89
|
+
float,
|
|
90
|
+
floats,
|
|
91
|
+
hex,
|
|
92
|
+
hexes,
|
|
93
|
+
int,
|
|
94
|
+
ints,
|
|
95
|
+
json,
|
|
96
|
+
kvPairs,
|
|
97
|
+
kvPairsMulti,
|
|
98
|
+
oneOf,
|
|
99
|
+
oneOfMulti,
|
|
100
|
+
size,
|
|
101
|
+
string,
|
|
102
|
+
strings,
|
|
103
|
+
tuple,
|
|
104
|
+
vec
|
|
105
|
+
};
|
package/coerce.js
CHANGED
|
@@ -2,42 +2,50 @@ import { isHex } from "@thi.ng/checks/is-hex";
|
|
|
2
2
|
import { isNumericFloat, isNumericInt } from "@thi.ng/checks/is-numeric";
|
|
3
3
|
import { illegalArgs } from "@thi.ng/errors/illegal-arguments";
|
|
4
4
|
import { Tuple } from "./api.js";
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
acc[x] = multi ? ["true"] : "true";
|
|
34
|
-
}
|
|
35
|
-
return acc;
|
|
36
|
-
}, {});
|
|
5
|
+
const coerceString = (x) => x;
|
|
6
|
+
const coerceFloat = (x) => isNumericFloat(x) ? parseFloat(x) : illegalArgs(`not a numeric value: ${x}`);
|
|
7
|
+
const coerceFloats = (xs) => xs.map(coerceFloat);
|
|
8
|
+
const coerceHexInt = (x) => isHex(x) ? parseInt(x, 16) : illegalArgs(`not a hex value: ${x}`);
|
|
9
|
+
const coerceHexInts = (xs) => xs.map(coerceHexInt);
|
|
10
|
+
const coerceInt = (x) => isNumericInt(x) ? parseInt(x) : illegalArgs(`not an integer: ${x}`);
|
|
11
|
+
const coerceInts = (xs) => xs.map(coerceInt);
|
|
12
|
+
const coerceJson = (x) => JSON.parse(x);
|
|
13
|
+
const coerceOneOf = (xs) => (x) => xs.includes(x) ? x : illegalArgs(`invalid option: ${x}`);
|
|
14
|
+
function coerceKV(delim = "=", strict = false, multi = false) {
|
|
15
|
+
return (pairs) => pairs.reduce((acc, x) => {
|
|
16
|
+
const idx = x.indexOf(delim);
|
|
17
|
+
strict && idx < 1 && illegalArgs(
|
|
18
|
+
`got '${x}', but expected a 'key${delim}value' pair`
|
|
19
|
+
);
|
|
20
|
+
if (idx > 0) {
|
|
21
|
+
const id = x.substring(0, idx);
|
|
22
|
+
const val = x.substring(idx + 1);
|
|
23
|
+
if (multi) {
|
|
24
|
+
acc[id] ? acc[id].push(val) : acc[id] = [val];
|
|
25
|
+
} else {
|
|
26
|
+
acc[id] = val;
|
|
27
|
+
}
|
|
28
|
+
} else {
|
|
29
|
+
acc[x] = multi ? ["true"] : "true";
|
|
30
|
+
}
|
|
31
|
+
return acc;
|
|
32
|
+
}, {});
|
|
37
33
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
34
|
+
const coerceTuple = (coerce, size, delim = ",") => (src) => {
|
|
35
|
+
const parts = src.split(delim);
|
|
36
|
+
parts.length !== size && illegalArgs(`got '${src}', but expected a tuple of ${size} values`);
|
|
37
|
+
return new Tuple(parts.map(coerce));
|
|
38
|
+
};
|
|
39
|
+
export {
|
|
40
|
+
coerceFloat,
|
|
41
|
+
coerceFloats,
|
|
42
|
+
coerceHexInt,
|
|
43
|
+
coerceHexInts,
|
|
44
|
+
coerceInt,
|
|
45
|
+
coerceInts,
|
|
46
|
+
coerceJson,
|
|
47
|
+
coerceKV,
|
|
48
|
+
coerceOneOf,
|
|
49
|
+
coerceString,
|
|
50
|
+
coerceTuple
|
|
43
51
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@thi.ng/args",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.46",
|
|
4
4
|
"description": "Declarative, functional & typechecked CLI argument/options parser, value coercions etc.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"module": "./index.js",
|
|
@@ -24,7 +24,9 @@
|
|
|
24
24
|
"author": "Karsten Schmidt (https://thi.ng)",
|
|
25
25
|
"license": "Apache-2.0",
|
|
26
26
|
"scripts": {
|
|
27
|
-
"build": "yarn
|
|
27
|
+
"build": "yarn build:esbuild && yarn build:decl",
|
|
28
|
+
"build:decl": "tsc --declaration --emitDeclarationOnly",
|
|
29
|
+
"build:esbuild": "esbuild --format=esm --platform=neutral --target=es2022 --tsconfig=tsconfig.json --outdir=. src/**/*.ts",
|
|
28
30
|
"clean": "rimraf --glob '*.js' '*.d.ts' '*.map' doc",
|
|
29
31
|
"doc": "typedoc --excludePrivate --excludeInternal --out doc src/index.ts",
|
|
30
32
|
"doc:ae": "mkdir -p .ae/doc .ae/temp && api-extractor run --local --verbose",
|
|
@@ -33,13 +35,14 @@
|
|
|
33
35
|
"test": "bun test"
|
|
34
36
|
},
|
|
35
37
|
"dependencies": {
|
|
36
|
-
"@thi.ng/api": "^8.9.
|
|
37
|
-
"@thi.ng/checks": "^3.4.
|
|
38
|
-
"@thi.ng/errors": "^2.4.
|
|
39
|
-
"@thi.ng/strings": "^3.7.
|
|
38
|
+
"@thi.ng/api": "^8.9.12",
|
|
39
|
+
"@thi.ng/checks": "^3.4.12",
|
|
40
|
+
"@thi.ng/errors": "^2.4.6",
|
|
41
|
+
"@thi.ng/strings": "^3.7.3"
|
|
40
42
|
},
|
|
41
43
|
"devDependencies": {
|
|
42
44
|
"@microsoft/api-extractor": "^7.38.3",
|
|
45
|
+
"esbuild": "^0.19.8",
|
|
43
46
|
"rimraf": "^5.0.5",
|
|
44
47
|
"tools": "^0.0.1",
|
|
45
48
|
"typedoc": "^0.25.4",
|
|
@@ -94,5 +97,5 @@
|
|
|
94
97
|
"thi.ng": {
|
|
95
98
|
"year": 2018
|
|
96
99
|
},
|
|
97
|
-
"gitHead": "
|
|
100
|
+
"gitHead": "5e7bafedfc3d53bc131469a28de31dd8e5b4a3ff\n"
|
|
98
101
|
}
|
package/parse.js
CHANGED
|
@@ -3,118 +3,120 @@ import { defError } from "@thi.ng/errors/deferror";
|
|
|
3
3
|
import { illegalArgs } from "@thi.ng/errors/illegal-arguments";
|
|
4
4
|
import { camel } from "@thi.ng/strings/case";
|
|
5
5
|
import { usage } from "./usage.js";
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
throw new ParseError(e.message);
|
|
6
|
+
const ParseError = defError(() => "parse error");
|
|
7
|
+
const parse = (specs, argv, opts) => {
|
|
8
|
+
opts = { start: 2, showUsage: true, help: ["--help", "-h"], ...opts };
|
|
9
|
+
try {
|
|
10
|
+
return parseOpts(specs, argv, opts);
|
|
11
|
+
} catch (e) {
|
|
12
|
+
if (opts.showUsage) {
|
|
13
|
+
console.log(
|
|
14
|
+
e.message + "\n\n" + usage(specs, opts.usageOpts)
|
|
15
|
+
);
|
|
17
16
|
}
|
|
17
|
+
throw new ParseError(e.message);
|
|
18
|
+
}
|
|
18
19
|
};
|
|
19
20
|
const parseOpts = (specs, argv, opts) => {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
i++;
|
|
44
|
-
}
|
|
21
|
+
const aliases = aliasIndex(specs);
|
|
22
|
+
const acc = {};
|
|
23
|
+
let id;
|
|
24
|
+
let spec;
|
|
25
|
+
let i = opts.start;
|
|
26
|
+
for (; i < argv.length; ) {
|
|
27
|
+
const a = argv[i];
|
|
28
|
+
if (!id) {
|
|
29
|
+
if (opts.help.includes(a)) {
|
|
30
|
+
console.log(usage(specs, opts.usageOpts));
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
const state = parseKey(specs, aliases, acc, a);
|
|
34
|
+
id = state.id;
|
|
35
|
+
spec = state.spec;
|
|
36
|
+
i = i + ~~(state.state < 2);
|
|
37
|
+
if (state.state)
|
|
38
|
+
break;
|
|
39
|
+
} else {
|
|
40
|
+
if (parseValue(spec, acc, id, a))
|
|
41
|
+
break;
|
|
42
|
+
id = null;
|
|
43
|
+
i++;
|
|
45
44
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
45
|
+
}
|
|
46
|
+
id && illegalArgs(`missing value for: --${id}`);
|
|
47
|
+
return {
|
|
48
|
+
result: processResults(specs, acc),
|
|
49
|
+
index: i,
|
|
50
|
+
rest: argv.slice(i),
|
|
51
|
+
done: i >= argv.length
|
|
52
|
+
};
|
|
53
53
|
};
|
|
54
|
-
const aliasIndex = (specs) => Object.entries(specs).reduce(
|
|
54
|
+
const aliasIndex = (specs) => Object.entries(specs).reduce(
|
|
55
|
+
(acc, [k, v]) => v.alias ? (acc[v.alias] = k, acc) : acc,
|
|
56
|
+
{}
|
|
57
|
+
);
|
|
55
58
|
const parseKey = (specs, aliases, acc, a) => {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
// stop parsing if fn returns false
|
|
74
|
-
if (spec.fn && !spec.fn("true"))
|
|
75
|
-
return { state: 1, spec };
|
|
76
|
-
}
|
|
77
|
-
return { state: 0, id, spec };
|
|
59
|
+
if (a[0] === "-") {
|
|
60
|
+
let id;
|
|
61
|
+
if (a[1] === "-") {
|
|
62
|
+
if (a === "--")
|
|
63
|
+
return { state: 1 };
|
|
64
|
+
id = camel(a.substring(2));
|
|
65
|
+
} else {
|
|
66
|
+
id = aliases[a.substring(1)];
|
|
67
|
+
!id && illegalArgs(`unknown option: ${a}`);
|
|
68
|
+
}
|
|
69
|
+
const spec = specs[id];
|
|
70
|
+
!spec && illegalArgs(id);
|
|
71
|
+
if (spec.flag) {
|
|
72
|
+
acc[id] = true;
|
|
73
|
+
id = void 0;
|
|
74
|
+
if (spec.fn && !spec.fn("true"))
|
|
75
|
+
return { state: 1, spec };
|
|
78
76
|
}
|
|
79
|
-
|
|
80
|
-
|
|
77
|
+
return { state: 0, id, spec };
|
|
78
|
+
}
|
|
79
|
+
return { state: 2 };
|
|
81
80
|
};
|
|
82
81
|
const parseValue = (spec, acc, id, a) => {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
return spec.fn && !spec.fn(a);
|
|
82
|
+
/^-[a-z]/i.test(a) && illegalArgs(`missing value for: --${id}`);
|
|
83
|
+
if (spec.multi) {
|
|
84
|
+
isArray(acc[id]) ? acc[id].push(a) : acc[id] = [a];
|
|
85
|
+
} else {
|
|
86
|
+
acc[id] = a;
|
|
87
|
+
}
|
|
88
|
+
return spec.fn && !spec.fn(a);
|
|
91
89
|
};
|
|
92
90
|
const processResults = (specs, acc) => {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
else if (spec.coerce) {
|
|
105
|
-
coerceValue(spec, acc, id);
|
|
106
|
-
}
|
|
91
|
+
let spec;
|
|
92
|
+
for (let id in specs) {
|
|
93
|
+
spec = specs[id];
|
|
94
|
+
if (acc[id] === void 0) {
|
|
95
|
+
if (spec.default !== void 0) {
|
|
96
|
+
acc[id] = spec.default;
|
|
97
|
+
} else if (spec.optional === false) {
|
|
98
|
+
illegalArgs(`missing arg: --${id}`);
|
|
99
|
+
}
|
|
100
|
+
} else if (spec.coerce) {
|
|
101
|
+
coerceValue(spec, acc, id);
|
|
107
102
|
}
|
|
108
|
-
|
|
103
|
+
}
|
|
104
|
+
return acc;
|
|
109
105
|
};
|
|
110
106
|
const coerceValue = (spec, acc, id) => {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
catch (e) {
|
|
118
|
-
throw new Error(`arg --${id}: ${e.message}`);
|
|
107
|
+
try {
|
|
108
|
+
if (spec.multi && spec.delim) {
|
|
109
|
+
acc[id] = acc[id].reduce(
|
|
110
|
+
(acc2, x) => (acc2.push(...x.split(spec.delim)), acc2),
|
|
111
|
+
[]
|
|
112
|
+
);
|
|
119
113
|
}
|
|
114
|
+
acc[id] = spec.coerce(acc[id]);
|
|
115
|
+
} catch (e) {
|
|
116
|
+
throw new Error(`arg --${id}: ${e.message}`);
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
export {
|
|
120
|
+
ParseError,
|
|
121
|
+
parse
|
|
120
122
|
};
|
package/usage.js
CHANGED
|
@@ -4,73 +4,72 @@ import { padRight } from "@thi.ng/strings/pad-right";
|
|
|
4
4
|
import { repeat } from "@thi.ng/strings/repeat";
|
|
5
5
|
import { stringify } from "@thi.ng/strings/stringify";
|
|
6
6
|
import { SPLIT_ANSI, wordWrapLines } from "@thi.ng/strings/word-wrap";
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
...
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
7
|
+
import {
|
|
8
|
+
DEFAULT_THEME
|
|
9
|
+
} from "./api.js";
|
|
10
|
+
const usage = (specs, opts = {}) => {
|
|
11
|
+
opts = {
|
|
12
|
+
lineWidth: 80,
|
|
13
|
+
paramWidth: 32,
|
|
14
|
+
showDefaults: true,
|
|
15
|
+
prefix: "",
|
|
16
|
+
suffix: "",
|
|
17
|
+
groups: ["flags", "main"],
|
|
18
|
+
...opts
|
|
19
|
+
};
|
|
20
|
+
const theme = opts.color !== false ? { ...DEFAULT_THEME, ...opts.color } : {};
|
|
21
|
+
const indent = repeat(" ", opts.paramWidth);
|
|
22
|
+
const format = (ids) => ids.map((id) => argUsage(id, specs[id], opts, theme, indent));
|
|
23
|
+
const sortedIDs = Object.keys(specs).sort();
|
|
24
|
+
const groups = opts.groups ? opts.groups.map(
|
|
25
|
+
(gid) => [
|
|
26
|
+
gid,
|
|
27
|
+
sortedIDs.filter((id) => specs[id].group === gid)
|
|
28
|
+
]
|
|
29
|
+
).filter((g) => !!g[1].length) : [["options", sortedIDs]];
|
|
30
|
+
return [
|
|
31
|
+
...wrap(opts.prefix, opts.lineWidth),
|
|
32
|
+
...groups.map(
|
|
33
|
+
([gid, ids]) => [
|
|
34
|
+
...opts.showGroupNames ? [`${capitalize(gid)}:
|
|
35
|
+
`] : [],
|
|
36
|
+
...format(ids),
|
|
37
|
+
""
|
|
38
|
+
].join("\n")
|
|
39
|
+
),
|
|
40
|
+
...wrap(opts.suffix, opts.lineWidth)
|
|
41
|
+
].join("\n");
|
|
41
42
|
};
|
|
42
43
|
const argUsage = (id, spec, opts, theme, indent) => {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
argDefault(spec, opts, theme);
|
|
54
|
-
return (padRight(opts.paramWidth)(params, lengthAnsi(params)) +
|
|
55
|
-
wrap(body, opts.lineWidth - opts.paramWidth)
|
|
56
|
-
.map((l, i) => (i > 0 ? indent + l : l))
|
|
57
|
-
.join("\n"));
|
|
44
|
+
const hint = argHint(spec, theme);
|
|
45
|
+
const alias = argAlias(spec, theme, hint);
|
|
46
|
+
const name = ansi(`--${kebab(id)}`, theme.param);
|
|
47
|
+
const params = `${alias}${name}${hint}`;
|
|
48
|
+
const isRequired = spec.optional === false && spec.default === void 0;
|
|
49
|
+
const prefixes = [];
|
|
50
|
+
isRequired && prefixes.push("required");
|
|
51
|
+
spec.multi && prefixes.push("multiple");
|
|
52
|
+
const body = argPrefix(prefixes, theme, isRequired) + (spec.desc || "") + argDefault(spec, opts, theme);
|
|
53
|
+
return padRight(opts.paramWidth)(params, lengthAnsi(params)) + wrap(body, opts.lineWidth - opts.paramWidth).map((l, i) => i > 0 ? indent + l : l).join("\n");
|
|
58
54
|
};
|
|
59
55
|
const argHint = (spec, theme) => spec.hint ? ansi(" " + spec.hint, theme.hint) : "";
|
|
60
56
|
const argAlias = (spec, theme, hint) => spec.alias ? `${ansi("-" + spec.alias, theme.param)}${hint}, ` : "";
|
|
61
|
-
const argPrefix = (prefixes, theme, isRequired) => prefixes.length
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
57
|
+
const argPrefix = (prefixes, theme, isRequired) => prefixes.length ? ansi(
|
|
58
|
+
`[${prefixes.join(", ")}] `,
|
|
59
|
+
isRequired ? theme.required : theme.multi
|
|
60
|
+
) : "";
|
|
61
|
+
const argDefault = (spec, opts, theme) => opts.showDefaults && spec.default != null && spec.default !== false ? ansi(
|
|
62
|
+
` (default: ${stringify(true)(
|
|
63
|
+
spec.defaultHint != void 0 ? spec.defaultHint : spec.default
|
|
64
|
+
)})`,
|
|
65
|
+
theme.default
|
|
66
|
+
) : "";
|
|
67
|
+
const ansi = (x, col) => col != null ? `\x1B[${col}m${x}\x1B[0m` : x;
|
|
68
|
+
const wrap = (str, width) => str ? wordWrapLines(str, {
|
|
69
|
+
width,
|
|
70
|
+
splitter: SPLIT_ANSI,
|
|
71
|
+
hard: true
|
|
72
|
+
}) : [];
|
|
73
|
+
export {
|
|
74
|
+
usage
|
|
75
|
+
};
|