@trenskow/arguments-parser 0.3.115 → 0.3.117

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.
@@ -1,8 +1,8 @@
1
1
  {
2
- "cSpell.words": [
3
- "caseit",
4
- "isvalid",
5
- "Kristian",
6
- "trenskow"
7
- ]
2
+ "cSpell.words": [
3
+ "caseit",
4
+ "isvalid",
5
+ "Kristian",
6
+ "trenskow"
7
+ ]
8
8
  }
package/eslint.config.js CHANGED
@@ -36,8 +36,9 @@ export default [...compat.extends('eslint:recommended'), {
36
36
  allow: ['warn', 'error', 'info'],
37
37
  }],
38
38
 
39
- 'no-unused-vars': ['error', {
39
+ 'no-unused-vars': ['warn', {
40
40
  argsIgnorePattern: '^_',
41
+ caughtErrors: 'none'
41
42
  }],
42
43
 
43
44
  'no-empty': ['error', {
package/index.js CHANGED
@@ -6,4 +6,68 @@
6
6
  // See license in LICENSE.
7
7
  //
8
8
 
9
- export { default } from './lib/index.js';
9
+ import { plugins } from 'isvalid';
10
+
11
+ import argumentsParser from './lib/index.js';
12
+ import autocomplete from './lib/autocomplete/index.js';
13
+
14
+ plugins.use('argumentsParser.dummies', () => ({
15
+ phase: 'pre',
16
+ supportsType: () => true,
17
+ validatorsForType: () => ({
18
+ description: ['string'],
19
+ defaultDescription: ['string'],
20
+ secret: ['boolean'],
21
+ short: ['string']
22
+ }),
23
+ validate: (data) => data,
24
+ formalize: (schema) => schema
25
+ }));
26
+
27
+ plugins.use('argumentsParser.hints', () => ({
28
+ phase: 'pre',
29
+ supportsType: () => true,
30
+ validatorsForType: () => ({
31
+ hints: ['array', 'string']
32
+ }),
33
+ validate: (data) => data,
34
+ formalize: (data, _, schema) => {
35
+
36
+ if (!Array.isArray(data)) data = [data];
37
+
38
+ if (schema.errors.hints) {
39
+ data.push(schema.errors.hints);
40
+ delete schema.errors.hints;
41
+ }
42
+
43
+ if (data.some((data) => typeof data !== 'string')) throw new Error('Must be a string.');
44
+
45
+ return data;
46
+
47
+ }
48
+ }));
49
+
50
+ plugins.use('argumentsParser.autocomplete', () => ({
51
+ phase: 'pre',
52
+ supportsType: () => true,
53
+ validatorsForType: () => ({
54
+ autocomplete: ['array', 'string']
55
+ }),
56
+ validate: (data) => data,
57
+ formalize: (data, _, schema) => {
58
+
59
+ if (!Array.isArray(data)) data = [data];
60
+
61
+ if (schema.errors.autocomplete) {
62
+ data.push(schema.errors.autocomplete);
63
+ delete schema.errors.autocomplete;
64
+ }
65
+
66
+ if (data.some((data) => typeof data !== 'string')) throw new Error('Must be a string.');
67
+
68
+ return data;
69
+
70
+ }
71
+ }));
72
+
73
+ export default autocomplete(process.env.AUTOCOMPLETE) || argumentsParser;
@@ -0,0 +1,64 @@
1
+ //
2
+ // files.js
3
+ // @trenskow/arguments-parser
4
+ //
5
+ // Created by Kristian Trenskow on 2026/08/02
6
+ // See license in LICENSE.
7
+ //
8
+
9
+ import {
10
+ join,
11
+ resolve
12
+ } from 'path';
13
+
14
+ import {
15
+ stat,
16
+ readdir
17
+ } from 'fs/promises';
18
+
19
+ import parallel from '@trenskow/parallel';
20
+
21
+ const safeStat = async (path) => {
22
+ try {
23
+ return await stat(path);
24
+ } catch (_) {
25
+ return undefined;
26
+ }
27
+ };
28
+
29
+ export default async (current = '') => {
30
+
31
+ const stat = await safeStat(
32
+ resolve(
33
+ process.cwd(),
34
+ current));
35
+
36
+ if (stat?.isDirectory()) {
37
+
38
+ current = current.endsWith('/') ? current : `${current}/`;
39
+
40
+ return (await parallel((await readdir(
41
+ resolve(
42
+ process.cwd(),
43
+ current)))
44
+ .map(async (file) => {
45
+
46
+ if ((await safeStat(resolve(process.cwd(), current, file)))?.isDirectory()) {
47
+ return `${file}/`;
48
+ }
49
+
50
+ return file;
51
+
52
+ })))
53
+ .filter((file) => !file.startsWith('.'))
54
+ .map((file) => `${current}${file}`);
55
+
56
+ } else if (stat) {
57
+ return [];
58
+ }
59
+
60
+ return (await readdir(join(current, '../')))
61
+ .filter((file) => !file.startsWith('.'))
62
+ .map((file) => join(current, '../', file));
63
+
64
+ };
@@ -0,0 +1,15 @@
1
+ //
2
+ // index.js
3
+ // @trenskow/arguments-parser
4
+ //
5
+ // Created by Kristian Trenskow on 2026/08/02
6
+ // See license in LICENSE.
7
+ //
8
+
9
+ import files from './files.js';
10
+
11
+ export { files };
12
+
13
+ export default {
14
+ files
15
+ };
@@ -0,0 +1,281 @@
1
+ //
2
+ // index.js
3
+ // @trenskow/arguments-parser
4
+ //
5
+ // Created by Kristian Trenskow on 2026/08/02
6
+ // See license in LICENSE.
7
+ //
8
+
9
+ import {
10
+ basename
11
+ } from 'path';
12
+
13
+ import {
14
+ keyPaths,
15
+ merge
16
+ } from 'isvalid';
17
+
18
+ import caseit from '@trenskow/caseit';
19
+
20
+ import buildIns from './build-ins/index.js';
21
+
22
+ import zsh from './zsh.js';
23
+
24
+ const supported = {
25
+ zsh
26
+ };
27
+
28
+ const argumentsParser = (output) => {
29
+
30
+ output = supported[output];
31
+
32
+ if (!output) return undefined;
33
+
34
+ const argumentsParser = (
35
+ {
36
+ args = process.argv.slice(output.argsOffset || 2),
37
+ argvLevel = 0,
38
+ placeholder = '<>',
39
+ command,
40
+ onCommand,
41
+ onParameter,
42
+ options = {
43
+ schema: {},
44
+ data: {},
45
+ validationOptions: {}
46
+ },
47
+ parameters = {}
48
+ } = {}
49
+ ) => {
50
+
51
+ const base = `${process.argv.slice(1, argvLevel + 2).map(((arg) => basename(arg))).join(' ')}`;
52
+
53
+ const next = async (identifier, command) => {
54
+
55
+ return await command({
56
+ args: args.slice(1),
57
+ argumentsParser: argumentsParser({
58
+ args: args.slice(1),
59
+ argvLevel: argvLevel + 1,
60
+ onCommand,
61
+ onParameter,
62
+ placeholder,
63
+ command: command,
64
+ options,
65
+ parameters
66
+ }),
67
+ parameters
68
+ });
69
+
70
+ };
71
+
72
+ const result = {
73
+ get base() {
74
+ return base;
75
+ },
76
+ command: async (commands) => {
77
+
78
+ const tool = caseit(args[0] || '');
79
+
80
+ if (commands[tool]) {
81
+
82
+ onCommand?.(tool, args.slice(1));
83
+
84
+ return await next(
85
+ tool,
86
+ commands[tool]);
87
+
88
+ }
89
+
90
+ return output(Object.keys(commands)
91
+ .filter((command) => command.startsWith(tool))
92
+ .map((command) => ({
93
+ command,
94
+ description: commands[command].description
95
+ })));
96
+
97
+ },
98
+ parameter: async ({
99
+ identifier,
100
+ autocomplete
101
+ }, command) => {
102
+
103
+ const available = (await autocomplete?.(args[0] || '') || [])
104
+ .filter((item) => item !== args[0] && item.startsWith(args[0] || ''));
105
+
106
+ if (available.length) {
107
+ return output(available);
108
+ }
109
+
110
+ parameters[identifier] = args[0];
111
+
112
+ (onParameter || onCommand)?.(identifier, args.slice(1));
113
+
114
+ return await next(
115
+ identifier,
116
+ command);
117
+
118
+ },
119
+ options: (schema, validationOptions = {}) => {
120
+
121
+ validationOptions.command = validationOptions.command || command;
122
+
123
+ Object.assign(
124
+ options.validationOptions,
125
+ validationOptions);
126
+
127
+ options.schema = merge(
128
+ schema, {
129
+ transform: {
130
+ post: (key, validator) => {
131
+ if (key === 'enum') {
132
+ return validator.map((value) => caseit(value, 'kebab'));
133
+ }
134
+ }
135
+ }
136
+ })
137
+ .with(options.schema);
138
+
139
+ return result;
140
+
141
+ },
142
+ then: (
143
+ ...resultArguments
144
+ ) => {
145
+ return (async () => {
146
+
147
+ const {
148
+ variadic,
149
+ autocomplete = buildIns.files
150
+ } = options.validationOptions;
151
+
152
+ const allKeyPaths = keyPaths(options.schema)
153
+ .all()
154
+ .filter((keyPath) => keyPath);
155
+
156
+ let idx;
157
+ let schemas = {};
158
+ let spent = [];
159
+
160
+ const complete = async (idx, current = '') => {
161
+
162
+ let key = current;
163
+
164
+ while (key.startsWith('-')) {
165
+ key = key.slice(1);
166
+ }
167
+
168
+ key = caseit(key);
169
+
170
+ if (schemas[idx]) {
171
+
172
+ if (schemas[idx].enum) {
173
+ return Object.keys(schemas[idx].enum)
174
+ .filter((item) => caseit(key, 'kebab') !== item && item.startsWith(current))
175
+ .map((item) => caseit(item, 'kebab'));
176
+ }
177
+
178
+ if (schemas[idx].type === String && !schemas[idx].autocomplete) {
179
+ return await (buildIns.files(
180
+ current))
181
+ .filter((item) => key !== item && item.startsWith(current));
182
+ }
183
+
184
+ return (await schemas[idx].autocomplete?.(current) || [])
185
+ .filter((item) => key !== item && item.startsWith(current));
186
+
187
+ } else {
188
+
189
+ let items = [];
190
+
191
+ if (!current.startsWith('--')) {
192
+
193
+ if (!current.startsWith('-')) {
194
+ if (variadic === 'allow') {
195
+ items = items.concat(
196
+ (await autocomplete?.(current) || [])
197
+ .filter((item) => current !== item && item.startsWith(current)));
198
+ }
199
+ }
200
+
201
+ items = items.concat(
202
+ allKeyPaths
203
+ .filter((keyPath) => !spent.includes(keyPath))
204
+ .filter((keyPath) => key !== keyPath && keyPath.startsWith(key) && keyPaths(options.schema).get(keyPath).short)
205
+ .map((keyPath) => ({
206
+ command: caseit(keyPaths(options.schema).get(keyPath).short, 'kebab'),
207
+ description: keyPaths(options.schema).get(keyPath).description,
208
+ prefix: '-'
209
+ })));
210
+
211
+ }
212
+
213
+ items = items.concat(
214
+ allKeyPaths
215
+ .filter((keyPath) => !spent.includes(keyPath))
216
+ .filter((keyPath) => key !== keyPath && keyPath.startsWith(key))
217
+ .map((keyPath) => ({
218
+ command: caseit(keyPath, 'kebab'),
219
+ description: keyPaths(options.schema).get(keyPath).description,
220
+ prefix: '--'
221
+ })));
222
+
223
+ return items;
224
+
225
+ }
226
+
227
+ };
228
+
229
+ if (args.length === 0) {
230
+ return output(
231
+ await complete(-1));
232
+ }
233
+
234
+ for (idx = 0; idx < args.length; idx++) {
235
+
236
+ if (!schemas[idx]) {
237
+
238
+ if (!args[idx].startsWith('-')) {
239
+ return output(
240
+ await complete(idx, args[idx]));
241
+ }
242
+
243
+ const [keyPath, schema] = allKeyPaths
244
+ .filter((keyPath) => !spent.includes(keyPath))
245
+ .map((keyPath) => [keyPath, keyPaths(options.schema).get(keyPath)])
246
+ .filter(([keyPath, schema]) => keyPath === args[idx] || `--${caseit(keyPath, 'kebab')}` === args[idx] || (schema.short && `-${schema.short}` === args[idx]))[0] || [];
247
+
248
+ spent.push(keyPath);
249
+
250
+ if (schema && schema.type !== Boolean) {
251
+ schemas[idx + 1] = schema;
252
+ }
253
+
254
+ }
255
+
256
+ }
257
+
258
+ const current = await complete(idx - 1, args[idx - 1]);
259
+
260
+ if (current.length) {
261
+ return output(current);
262
+ }
263
+
264
+ return output(
265
+ await complete(idx));
266
+
267
+
268
+ })().then(...resultArguments);
269
+ }
270
+ };
271
+
272
+ return result;
273
+
274
+ };
275
+
276
+ return argumentsParser;
277
+
278
+ };
279
+
280
+ export default argumentsParser;
281
+ export * as buildIns from './build-ins/index.js';
@@ -0,0 +1,39 @@
1
+ //
2
+ // zsh.js
3
+ // @trenskow/arguments-parser
4
+ //
5
+ // Created by Kristian Trenskow on 2026/08/02
6
+ // See license in LICENSE.
7
+ //
8
+
9
+ import caseit from '@trenskow/caseit';
10
+
11
+ const zsh = (items) => {
12
+
13
+ if (items.length === 0) {
14
+ process.exit(0);
15
+ }
16
+
17
+ process.stdout.write('_describe \'commands\' \'(\n');
18
+
19
+ items
20
+ .forEach((command) => {
21
+
22
+ if (typeof command === 'string') {
23
+ process.stdout.write(` "${command}"\n`);
24
+ return;
25
+ }
26
+
27
+ process.stdout.write(` "${command.prefix || ''}${command.command}:${(command.description || '').replaceAll('`', '\\`')}"\n`);
28
+
29
+ });
30
+
31
+ process.stdout.write(')\'\n');
32
+
33
+ process.exit(0);
34
+
35
+ };
36
+
37
+ zsh.argsOffset = 3;
38
+
39
+ export default zsh;
package/lib/index.js CHANGED
@@ -9,40 +9,9 @@
9
9
  import { basename } from 'path';
10
10
 
11
11
  import caseit from '@trenskow/caseit';
12
- import { default as isvalid, merge, keyPaths, plugins } from 'isvalid';
12
+ import { default as isvalid, merge, keyPaths } from 'isvalid';
13
13
  import print from '@trenskow/print';
14
14
 
15
- plugins.use('argumentsParser.default', () => ({
16
- phase: 'pre',
17
- supportsType: () => true,
18
- validatorsForType: () => ({
19
- description: ['string'],
20
- defaultDescription: ['string'],
21
- secret: ['boolean'],
22
- short: ['string'],
23
- }),
24
- validate: (data) => data,
25
- formalize: (schema) => schema
26
- }));
27
-
28
- plugins.use('argumentsParser.hints', () => ({
29
- phase: 'pre',
30
- supportsType: () => true,
31
- validatorsForType: () => ({
32
- hints: ['array', 'string']
33
- }),
34
- validate: (data) => data,
35
- formalize: (data, _, schema) => {
36
- if (!Array.isArray(data)) data = [data];
37
- if (schema.errors.hints) {
38
- data.push(schema.errors.hints);
39
- delete schema.errors.hints;
40
- }
41
- if (data.some((data) => typeof data !== 'string')) throw new Error('Must be a string.');
42
- return data;
43
- }
44
- }));
45
-
46
15
  const argumentsParser = (
47
16
  {
48
17
  args = process.argv.slice(2),
@@ -65,7 +34,7 @@ const argumentsParser = (
65
34
  } = {}
66
35
  ) => {
67
36
 
68
- const [ opening, closing ] = placeholder.split('');
37
+ const [opening, closing] = placeholder.split('');
69
38
 
70
39
  const base = `${process.argv.slice(1, argvLevel + 2).map(((arg) => basename(arg))).join(' ')}`;
71
40
 
@@ -174,6 +143,7 @@ const argumentsParser = (
174
143
  print.err(`${error.stack}`);
175
144
  process.exit(1);
176
145
  }
146
+
177
147
  };
178
148
 
179
149
  const result = {
@@ -191,15 +161,7 @@ const argumentsParser = (
191
161
  print();
192
162
 
193
163
  const tools = Object.entries(commands).map(([key, command]) => ({
194
- name: `${
195
- caseit(key, 'kebab')
196
- }${
197
- Array.isArray(command.parameters) ? ` ${
198
- command.parameters
199
- .map((parameter) => `${opening}${caseit(parameter, 'kebab')}${closing}`)
200
- .join(' ')
201
- }` : ''
202
- }`,
164
+ name: `${caseit(key, 'kebab')}${Array.isArray(command.parameters) ? ` ${command.parameters.map((parameter) => `${opening}${caseit(parameter, 'kebab')}${closing}`).join(' ')}` : ''}`,
203
165
  description: commands[key].description
204
166
  }));
205
167
 
@@ -279,7 +241,7 @@ const argumentsParser = (
279
241
  command);
280
242
 
281
243
  },
282
- options: (schema, validationOptions = { }) => {
244
+ options: (schema, validationOptions = {}) => {
283
245
 
284
246
  validationOptions.command = validationOptions.command || command;
285
247
 
@@ -288,7 +250,15 @@ const argumentsParser = (
288
250
  validationOptions);
289
251
 
290
252
  options.schema = merge(
291
- schema)
253
+ schema, {
254
+ transform: {
255
+ post: (key, validator) => {
256
+ if (key === 'enum') {
257
+ return validator.map((value) => caseit(value, 'kebab'));
258
+ }
259
+ }
260
+ }
261
+ })
292
262
  .with(options.schema);
293
263
 
294
264
  return result;
@@ -365,7 +335,7 @@ const argumentsParser = (
365
335
  }
366
336
 
367
337
  if (typeof keyPathSchema.enum !== 'undefined') {
368
- description.push(`(${opening}${keyPath}${closing}: ${Object.keys(keyPathSchema.enum).map((value) => `\`${value}\``).join(', ')})`);
338
+ description.push(`(${opening}${keyPath}${closing}: ${Object.keys(keyPathSchema.enum).map((value) => `\`${caseit(value, 'kebab')}\``).join(', ')})`);
369
339
  }
370
340
 
371
341
  if (keyPathSchema.type === Array) {
@@ -418,7 +388,7 @@ const argumentsParser = (
418
388
 
419
389
  checkHelp();
420
390
 
421
- for (idx = 0 ; idx < args.length ; idx++) {
391
+ for (idx = 0; idx < args.length; idx++) {
422
392
 
423
393
  if (args[idx].slice(0, 1) !== '-') {
424
394
  rest.push(args[idx]);
@@ -514,3 +484,4 @@ const argumentsParser = (
514
484
  };
515
485
 
516
486
  export default argumentsParser;
487
+ export { buildIns as autocompleteBuildIns } from './autocomplete/index.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trenskow/arguments-parser",
3
- "version": "0.3.115",
3
+ "version": "0.3.117",
4
4
  "description": "Yet another arguments parser.",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -25,6 +25,7 @@
25
25
  "homepage": "https://github.com/trenskow/arguments-parser#readme",
26
26
  "dependencies": {
27
27
  "@trenskow/caseit": "^1.4.29",
28
+ "@trenskow/parallel": "^0.1.132",
28
29
  "@trenskow/print": "^0.1.72",
29
30
  "isvalid": "^5.0.19"
30
31
  },