@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.
Files changed (2) hide show
  1. package/lib/index.js +156 -67
  2. 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 = ({ args = process.argv.slice(2), argvLevel = 0, placeholder = '<>', command, strings = {} } = {}) => {
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
- const printHelp = (error) => {
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
- if (error) {
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
- const printHelp = (error) => {
168
+ helpUsage.push(`${opening}${strings?.options?.help?.placeholder || 'options'}${closing}`);
133
169
 
134
- if (typeof options?.command?.description === 'string') {
135
- print();
136
- print(options.command.description);
137
- }
170
+ let shortOptions = {};
138
171
 
139
- print();
140
- print.nn(
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
- if (options.help?.postfix) print.nn(options.help.postfix);
175
+ const keyPathSchema = keyPaths(schema).get(keyPath);
146
176
 
147
- print();
177
+ if (keyPathSchema.short) {
178
+
179
+ if (shortOptions[keyPathSchema.short]) {
180
+ throw new Error(`Short option "${keyPathSchema.short}" already used.`);
181
+ }
148
182
 
149
- if (allKeyPaths.length) {
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?.options || 'Options:');
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.type === Boolean) {
176
- defaultDescription = keyPathSchema.default === true ? 'enabled' : 'disabled';
222
+ if (keyPathSchema.secret === true) {
223
+ defaultDescription = '`********`';
177
224
  } else {
178
- defaultDescription = `\`${keyPathSchema.defaultDescription ?? keyPathSchema.default}\``;
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
- return [`--${caseit(keyPath, 'kebab')}`, description];
191
-
192
- })));
193
-
194
- }
195
-
196
- print();
197
-
198
- if (error) {
243
+ let option = optionKeys.reverse().join(', ');
199
244
 
200
- (error.errors || [error])
201
- .forEach((error) => {
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
- print();
249
+ return [option, description];
210
250
 
211
- }
251
+ })));
212
252
 
213
- process.exit(error ? 1 : 0);
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, 2) !== '--') {
265
+ if (args[idx].slice(0, 1) !== '-') {
225
266
  rest.push(args[idx]);
226
267
  continue;
227
268
  }
228
269
 
229
- const arg = args[idx].slice(2);
270
+ shortOptions = Object.fromEntries(
271
+ Object.entries(shortOptions)
272
+ .map(([key, value]) => [value, key]));
230
273
 
231
- const key = caseit(arg);
274
+ let key = args[idx].slice(1);
232
275
 
233
- if (key === 'help') printHelp();
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
- printHelp(error);
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
- pop: async () => {
285
- if (args.length === 0) return;
286
- return args.shift();
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
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trenskow/arguments-parser",
3
- "version": "0.2.12",
3
+ "version": "0.2.14",
4
4
  "description": "Yet another arguments parser.",
5
5
  "main": "index.js",
6
6
  "type": "module",