@trenskow/arguments-parser 0.2.12 → 0.2.14
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/lib/index.js +156 -67
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -17,19 +17,33 @@ plugins.use(() => ({
|
|
|
17
17
|
supportsType: () => true,
|
|
18
18
|
validatorsForType: () => ({
|
|
19
19
|
description: ['string'],
|
|
20
|
-
defaultDescription: ['string']
|
|
20
|
+
defaultDescription: ['string'],
|
|
21
|
+
secret: ['boolean'],
|
|
22
|
+
short: ['string'],
|
|
21
23
|
}),
|
|
22
24
|
validate: (data) => data,
|
|
23
25
|
formalize: (schema) => schema
|
|
24
26
|
}));
|
|
25
27
|
|
|
26
|
-
const argumentsParser = (
|
|
28
|
+
const argumentsParser = (
|
|
29
|
+
{
|
|
30
|
+
args = process.argv.slice(2),
|
|
31
|
+
argvLevel = 0,
|
|
32
|
+
placeholder = '<>',
|
|
33
|
+
command,
|
|
34
|
+
strings = {},
|
|
35
|
+
help: {
|
|
36
|
+
usage: helpUsage,
|
|
37
|
+
options: helpOptions,
|
|
38
|
+
} = {}
|
|
39
|
+
} = {}
|
|
40
|
+
) => {
|
|
27
41
|
|
|
28
42
|
const [ opening, closing ] = placeholder.split('');
|
|
29
43
|
|
|
30
44
|
const base = `${process.argv.slice(1, argvLevel + 2).map(((arg) => basename(arg))).join(' ')}`;
|
|
31
45
|
|
|
32
|
-
const list = (items) => {
|
|
46
|
+
const list = (print, items) => {
|
|
33
47
|
|
|
34
48
|
const maxLength = Object.keys(items)
|
|
35
49
|
.reduce((length, key) => Math.max(length, key.length), 0);
|
|
@@ -42,18 +56,49 @@ const argumentsParser = ({ args = process.argv.slice(2), argvLevel = 0, placehol
|
|
|
42
56
|
|
|
43
57
|
};
|
|
44
58
|
|
|
59
|
+
if (typeof helpUsage === 'undefined') {
|
|
60
|
+
helpUsage = [
|
|
61
|
+
strings?.help?.usage || 'Usage:',
|
|
62
|
+
base
|
|
63
|
+
];
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const printHelp = (error) => {
|
|
67
|
+
|
|
68
|
+
print();
|
|
69
|
+
|
|
70
|
+
print(helpUsage.join(' '));
|
|
71
|
+
|
|
72
|
+
if (helpOptions) {
|
|
73
|
+
print.nn(helpOptions);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (error) {
|
|
77
|
+
print();
|
|
78
|
+
print.bold((strings?.commands?.help?.error || 'Error: <message>')
|
|
79
|
+
.replace('<message>', error.message));
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
print();
|
|
83
|
+
|
|
84
|
+
process.exit(error ? 1 : 0);
|
|
85
|
+
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
const checkHelp = () => {
|
|
89
|
+
if (args[0] === '--help') printHelp();
|
|
90
|
+
};
|
|
91
|
+
|
|
45
92
|
return {
|
|
46
93
|
get base() {
|
|
47
94
|
return base;
|
|
48
95
|
},
|
|
49
96
|
command: async (commands) => {
|
|
50
97
|
|
|
51
|
-
|
|
98
|
+
helpUsage.push(`${opening}${strings?.commands?.help?.placeholder || 'command'}${closing}`);
|
|
99
|
+
|
|
100
|
+
helpOptions = print.toString((print) => {
|
|
52
101
|
|
|
53
|
-
print();
|
|
54
|
-
print((strings?.commands?.help?.usage || 'Usage: <base> <command>')
|
|
55
|
-
.replace('<base>', base)
|
|
56
|
-
.replace('<command>', `${opening}command${closing}`));
|
|
57
102
|
print();
|
|
58
103
|
print(strings?.commands?.help?.available || 'Available commands:');
|
|
59
104
|
print();
|
|
@@ -65,24 +110,15 @@ const argumentsParser = ({ args = process.argv.slice(2), argvLevel = 0, placehol
|
|
|
65
110
|
|
|
66
111
|
tools.sort((a, b) => a.name > b.name ? 1 : -1);
|
|
67
112
|
|
|
68
|
-
list(Object.fromEntries(tools
|
|
113
|
+
list(print, Object.fromEntries(tools
|
|
69
114
|
.map((tool) => [tool.name, tool.description || (strings?.commands?.help?.noDescription || 'No description')])));
|
|
70
115
|
|
|
71
|
-
|
|
72
|
-
print();
|
|
73
|
-
print();
|
|
74
|
-
print((strings?.commands?.help?.error || 'Error: <message>')
|
|
75
|
-
.replace('<message>', error.message));
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
print();
|
|
79
|
-
|
|
80
|
-
process.exit(error ? 1 : 0);
|
|
81
|
-
|
|
82
|
-
};
|
|
116
|
+
});
|
|
83
117
|
|
|
84
118
|
if (args.length === 0) printHelp();
|
|
85
119
|
|
|
120
|
+
checkHelp();
|
|
121
|
+
|
|
86
122
|
const tool = caseit(args[0]);
|
|
87
123
|
|
|
88
124
|
if (!commands[tool]) {
|
|
@@ -129,35 +165,46 @@ const argumentsParser = ({ args = process.argv.slice(2), argvLevel = 0, placehol
|
|
|
129
165
|
.all()
|
|
130
166
|
.filter((keyPath) => keyPath);
|
|
131
167
|
|
|
132
|
-
|
|
168
|
+
helpUsage.push(`${opening}${strings?.options?.help?.placeholder || 'options'}${closing}`);
|
|
133
169
|
|
|
134
|
-
|
|
135
|
-
print();
|
|
136
|
-
print(options.command.description);
|
|
137
|
-
}
|
|
170
|
+
let shortOptions = {};
|
|
138
171
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
(strings?.options?.help?.usage || 'Usage: <base> <options>')
|
|
142
|
-
.replace('<base>', base)
|
|
143
|
-
.replace('<options>', `${opening}options${closing}`));
|
|
172
|
+
allKeyPaths
|
|
173
|
+
.forEach((keyPath) => {
|
|
144
174
|
|
|
145
|
-
|
|
175
|
+
const keyPathSchema = keyPaths(schema).get(keyPath);
|
|
146
176
|
|
|
147
|
-
|
|
177
|
+
if (keyPathSchema.short) {
|
|
178
|
+
|
|
179
|
+
if (shortOptions[keyPathSchema.short]) {
|
|
180
|
+
throw new Error(`Short option "${keyPathSchema.short}" already used.`);
|
|
181
|
+
}
|
|
148
182
|
|
|
149
|
-
|
|
183
|
+
shortOptions[keyPath] = keyPathSchema.short;
|
|
184
|
+
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
if (allKeyPaths.length) {
|
|
190
|
+
|
|
191
|
+
helpOptions = print.toString((print) => {
|
|
150
192
|
|
|
151
193
|
print();
|
|
152
|
-
print(strings?.options?.help?.
|
|
194
|
+
print(strings?.options?.help?.title || 'Options:');
|
|
153
195
|
|
|
154
|
-
list(Object.fromEntries(allKeyPaths
|
|
196
|
+
list(print, Object.fromEntries(allKeyPaths
|
|
155
197
|
.map((keyPath) => {
|
|
156
198
|
|
|
157
199
|
const keyPathSchema = keyPaths(schema).get(keyPath);
|
|
158
200
|
|
|
201
|
+
let optionKeys = [`--${caseit(keyPath, 'kebab')}`];
|
|
159
202
|
let description = keyPathSchema.description || 'No description';
|
|
160
203
|
|
|
204
|
+
if (shortOptions[keyPath]) {
|
|
205
|
+
optionKeys.push(`-${shortOptions[keyPath]}`);
|
|
206
|
+
}
|
|
207
|
+
|
|
161
208
|
if (typeof keyPathSchema.enum !== 'undefined') {
|
|
162
209
|
description += ` (${Object.keys(keyPathSchema.enum).map((value) => `\`${value}\``).join(', ')})`;
|
|
163
210
|
}
|
|
@@ -172,10 +219,16 @@ const argumentsParser = ({ args = process.argv.slice(2), argvLevel = 0, placehol
|
|
|
172
219
|
|
|
173
220
|
let defaultDescription;
|
|
174
221
|
|
|
175
|
-
if (keyPathSchema.
|
|
176
|
-
defaultDescription =
|
|
222
|
+
if (keyPathSchema.secret === true) {
|
|
223
|
+
defaultDescription = '`********`';
|
|
177
224
|
} else {
|
|
178
|
-
|
|
225
|
+
|
|
226
|
+
if (keyPathSchema.type === Boolean) {
|
|
227
|
+
defaultDescription = keyPathSchema.default === true ? 'enabled' : 'disabled';
|
|
228
|
+
} else {
|
|
229
|
+
defaultDescription = `\`${keyPathSchema.defaultDescription ?? keyPathSchema.default}\``;
|
|
230
|
+
}
|
|
231
|
+
|
|
179
232
|
}
|
|
180
233
|
|
|
181
234
|
addition = ` ${(strings?.options?.help?.default || '(default: <default>)')
|
|
@@ -187,32 +240,20 @@ const argumentsParser = ({ args = process.argv.slice(2), argvLevel = 0, placehol
|
|
|
187
240
|
|
|
188
241
|
description += '.';
|
|
189
242
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
})));
|
|
193
|
-
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
print();
|
|
197
|
-
|
|
198
|
-
if (error) {
|
|
243
|
+
let option = optionKeys.reverse().join(', ');
|
|
199
244
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
if (error.keyPath) {
|
|
203
|
-
print.err(`--${caseit(error.keyPath.join('.'), 'kebab')}: ${error.message}`);
|
|
204
|
-
} else {
|
|
205
|
-
print.err(error.message);
|
|
245
|
+
if (keyPathSchema.type !== Boolean) {
|
|
246
|
+
option += ` ${strings?.options?.help?.argument || `${opening}${caseit(keyPath, 'kebab')}${closing}`}`;
|
|
206
247
|
}
|
|
207
|
-
});
|
|
208
248
|
|
|
209
|
-
|
|
249
|
+
return [option, description];
|
|
210
250
|
|
|
211
|
-
|
|
251
|
+
})));
|
|
212
252
|
|
|
213
|
-
|
|
253
|
+
});
|
|
254
|
+
}
|
|
214
255
|
|
|
215
|
-
|
|
256
|
+
checkHelp();
|
|
216
257
|
|
|
217
258
|
let data = {};
|
|
218
259
|
let rest = [];
|
|
@@ -221,16 +262,22 @@ const argumentsParser = ({ args = process.argv.slice(2), argvLevel = 0, placehol
|
|
|
221
262
|
|
|
222
263
|
for (idx = 0 ; idx < args.length ; idx++) {
|
|
223
264
|
|
|
224
|
-
if (args[idx].slice(0,
|
|
265
|
+
if (args[idx].slice(0, 1) !== '-') {
|
|
225
266
|
rest.push(args[idx]);
|
|
226
267
|
continue;
|
|
227
268
|
}
|
|
228
269
|
|
|
229
|
-
|
|
270
|
+
shortOptions = Object.fromEntries(
|
|
271
|
+
Object.entries(shortOptions)
|
|
272
|
+
.map(([key, value]) => [value, key]));
|
|
230
273
|
|
|
231
|
-
|
|
274
|
+
let key = args[idx].slice(1);
|
|
232
275
|
|
|
233
|
-
if (
|
|
276
|
+
if (args[idx].slice(1, 2) === '-') {
|
|
277
|
+
key = caseit(args[idx].slice(2));
|
|
278
|
+
} else if (shortOptions[key]) {
|
|
279
|
+
key = caseit(shortOptions[key]);
|
|
280
|
+
}
|
|
234
281
|
|
|
235
282
|
if (!keyPaths(schema).all().includes(key)) {
|
|
236
283
|
printHelp(
|
|
@@ -268,7 +315,11 @@ const argumentsParser = ({ args = process.argv.slice(2), argvLevel = 0, placehol
|
|
|
268
315
|
aggregatedErrors: 'flatten'
|
|
269
316
|
});
|
|
270
317
|
} catch (error) {
|
|
271
|
-
|
|
318
|
+
let message = error.message;
|
|
319
|
+
if (error.keyPath?.[0]) {
|
|
320
|
+
message = `${caseit(error.keyPath[0], 'kebab')}: ${message}`;
|
|
321
|
+
}
|
|
322
|
+
printHelp(new Error(message));
|
|
272
323
|
}
|
|
273
324
|
|
|
274
325
|
return Object.assign({}, data, {
|
|
@@ -281,9 +332,47 @@ const argumentsParser = ({ args = process.argv.slice(2), argvLevel = 0, placehol
|
|
|
281
332
|
});
|
|
282
333
|
|
|
283
334
|
},
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
335
|
+
values: async (schema) => {
|
|
336
|
+
|
|
337
|
+
schema = formalize(schema);
|
|
338
|
+
|
|
339
|
+
if (schema.type !== Object) {
|
|
340
|
+
throw new Error('Schema must be an object.');
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
const schemaKeyPaths = keyPaths(schema);
|
|
344
|
+
|
|
345
|
+
const allKeyPaths = schemaKeyPaths.all({ maxDepth: 2 })
|
|
346
|
+
.filter((keyPath) => keyPath);
|
|
347
|
+
|
|
348
|
+
let lastNonRequiredIndex = -1;
|
|
349
|
+
|
|
350
|
+
allKeyPaths
|
|
351
|
+
.forEach((keyPath, idx) => {
|
|
352
|
+
|
|
353
|
+
const schema = schemaKeyPaths.get(keyPath);
|
|
354
|
+
|
|
355
|
+
if (schema.required && idx > lastNonRequiredIndex + 1) {
|
|
356
|
+
throw new Error('Required arguments must come first.');
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
if (schema.type !== String) {
|
|
360
|
+
throw new Error('Schema must be an object of strings.');
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
helpUsage = helpUsage.concat([`${opening}${caseit(keyPath, 'kebab')}${closing}`]);
|
|
364
|
+
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
checkHelp();
|
|
368
|
+
|
|
369
|
+
try {
|
|
370
|
+
return await isvalid(Object.fromEntries(
|
|
371
|
+
allKeyPaths.map((keyPath, idx) => [keyPath, args[idx]])), schema);
|
|
372
|
+
} catch (error) {
|
|
373
|
+
return printHelp(error);
|
|
374
|
+
}
|
|
375
|
+
|
|
287
376
|
}
|
|
288
377
|
};
|
|
289
378
|
|