@boneskull/bargs 3.1.0 → 3.3.0
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/README.md +132 -24
- package/dist/bargs.cjs +193 -22
- package/dist/bargs.cjs.map +1 -1
- package/dist/bargs.d.cts +51 -5
- package/dist/bargs.d.cts.map +1 -1
- package/dist/bargs.d.ts +51 -5
- package/dist/bargs.d.ts.map +1 -1
- package/dist/bargs.js +190 -21
- package/dist/bargs.js.map +1 -1
- package/dist/help.cjs +31 -2
- package/dist/help.cjs.map +1 -1
- package/dist/help.d.cts +4 -0
- package/dist/help.d.cts.map +1 -1
- package/dist/help.d.ts +4 -0
- package/dist/help.d.ts.map +1 -1
- package/dist/help.js +31 -2
- package/dist/help.js.map +1 -1
- package/dist/index.cjs +3 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -4
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +3 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -3
- package/dist/index.js.map +1 -1
- package/dist/opt.cjs +38 -0
- package/dist/opt.cjs.map +1 -1
- package/dist/opt.d.cts +26 -0
- package/dist/opt.d.cts.map +1 -1
- package/dist/opt.d.ts +26 -0
- package/dist/opt.d.ts.map +1 -1
- package/dist/opt.js +38 -0
- package/dist/opt.js.map +1 -1
- package/dist/osc.cjs +12 -0
- package/dist/osc.cjs.map +1 -1
- package/dist/osc.d.cts +6 -0
- package/dist/osc.d.cts.map +1 -1
- package/dist/osc.d.ts +6 -0
- package/dist/osc.d.ts.map +1 -1
- package/dist/osc.js +12 -0
- package/dist/osc.js.map +1 -1
- package/dist/parser.cjs +48 -1
- package/dist/parser.cjs.map +1 -1
- package/dist/parser.d.cts +2 -0
- package/dist/parser.d.cts.map +1 -1
- package/dist/parser.d.ts +2 -0
- package/dist/parser.d.ts.map +1 -1
- package/dist/parser.js +48 -1
- package/dist/parser.js.map +1 -1
- package/dist/theme.cjs +8 -0
- package/dist/theme.cjs.map +1 -1
- package/dist/theme.d.cts +6 -0
- package/dist/theme.d.cts.map +1 -1
- package/dist/theme.d.ts +6 -0
- package/dist/theme.d.ts.map +1 -1
- package/dist/theme.js +8 -0
- package/dist/theme.js.map +1 -1
- package/dist/types.d.cts +34 -1
- package/dist/types.d.cts.map +1 -1
- package/dist/types.d.ts +34 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/version.cjs +16 -0
- package/dist/version.cjs.map +1 -1
- package/dist/version.d.cts +4 -0
- package/dist/version.d.cts.map +1 -1
- package/dist/version.d.ts +4 -0
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +16 -0
- package/dist/version.js.map +1 -1
- package/package.json +9 -2
package/README.md
CHANGED
|
@@ -27,8 +27,7 @@ A CLI with an optional command and a couple options:
|
|
|
27
27
|
```typescript
|
|
28
28
|
import { bargs, opt, pos } from '@boneskull/bargs';
|
|
29
29
|
|
|
30
|
-
await bargs
|
|
31
|
-
.create('greet', { version: '1.0.0' })
|
|
30
|
+
await bargs('greet', { version: '1.0.0' })
|
|
32
31
|
.globals(
|
|
33
32
|
opt.options({
|
|
34
33
|
name: opt.string({ default: 'world' }),
|
|
@@ -112,11 +111,10 @@ const parser = pos.positionals(pos.variadic('string', { name: 'text' }))(
|
|
|
112
111
|
}),
|
|
113
112
|
);
|
|
114
113
|
|
|
115
|
-
const { values, positionals } = await bargs
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
})
|
|
114
|
+
const { values, positionals } = await bargs('echo', {
|
|
115
|
+
description: 'Echo text to stdout',
|
|
116
|
+
version: '1.0.0',
|
|
117
|
+
})
|
|
120
118
|
.globals(parser)
|
|
121
119
|
.parseAsync();
|
|
122
120
|
|
|
@@ -132,11 +130,10 @@ For a CLI with multiple subcommands:
|
|
|
132
130
|
```typescript
|
|
133
131
|
import { bargs, merge, opt, pos } from '@boneskull/bargs';
|
|
134
132
|
|
|
135
|
-
await bargs
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
})
|
|
133
|
+
await bargs('tasks', {
|
|
134
|
+
description: 'A task manager',
|
|
135
|
+
version: '1.0.0',
|
|
136
|
+
})
|
|
140
137
|
.globals(
|
|
141
138
|
opt.options({
|
|
142
139
|
verbose: opt.boolean({ aliases: ['v'], default: false }),
|
|
@@ -181,9 +178,51 @@ $ tasks list --all
|
|
|
181
178
|
All tasks
|
|
182
179
|
```
|
|
183
180
|
|
|
181
|
+
### Nested Commands (Subcommands)
|
|
182
|
+
|
|
183
|
+
Commands can be nested to arbitrary depth by passing a `CliBuilder` as the second argument to `.command()`:
|
|
184
|
+
|
|
185
|
+
```typescript
|
|
186
|
+
import { bargs, opt, pos } from '@boneskull/bargs';
|
|
187
|
+
|
|
188
|
+
// Define subcommands as a separate builder
|
|
189
|
+
const remoteCommands = bargs('remote')
|
|
190
|
+
.command(
|
|
191
|
+
'add',
|
|
192
|
+
pos.positionals(
|
|
193
|
+
pos.string({ name: 'name', required: true }),
|
|
194
|
+
pos.string({ name: 'url', required: true }),
|
|
195
|
+
),
|
|
196
|
+
({ positionals, values }) => {
|
|
197
|
+
const [name, url] = positionals;
|
|
198
|
+
// Parent globals (verbose) are available here!
|
|
199
|
+
if (values.verbose) console.log(`Adding ${name}: ${url}`);
|
|
200
|
+
},
|
|
201
|
+
'Add a remote',
|
|
202
|
+
)
|
|
203
|
+
.command('remove' /* ... */)
|
|
204
|
+
.defaultCommand('add');
|
|
205
|
+
|
|
206
|
+
// Nest under parent CLI
|
|
207
|
+
await bargs('git')
|
|
208
|
+
.globals(opt.options({ verbose: opt.boolean({ aliases: ['v'] }) }))
|
|
209
|
+
.command('remote', remoteCommands, 'Manage remotes') // ← CliBuilder
|
|
210
|
+
.command('commit', commitParser, commitHandler) // ← Regular command
|
|
211
|
+
.parseAsync();
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
```shell
|
|
215
|
+
$ git --verbose remote add origin https://github.com/...
|
|
216
|
+
Adding origin: https://github.com/...
|
|
217
|
+
|
|
218
|
+
$ git remote remove origin
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
Parent globals automatically flow to nested command handlers. You can nest as deep as you like—just nest `CliBuilder`s inside `CliBuilder`s. See `examples/nested-commands.ts` for a full example.
|
|
222
|
+
|
|
184
223
|
## API
|
|
185
224
|
|
|
186
|
-
### bargs
|
|
225
|
+
### bargs(name, options?)
|
|
187
226
|
|
|
188
227
|
Create a CLI builder.
|
|
189
228
|
|
|
@@ -199,7 +238,7 @@ Create a CLI builder.
|
|
|
199
238
|
Set global options and transforms that apply to all commands.
|
|
200
239
|
|
|
201
240
|
```typescript
|
|
202
|
-
bargs
|
|
241
|
+
bargs('my-cli').globals(opt.options({ verbose: opt.boolean() }));
|
|
203
242
|
// ...
|
|
204
243
|
```
|
|
205
244
|
|
|
@@ -219,6 +258,21 @@ Register a command. The handler receives merged global + command types.
|
|
|
219
258
|
)
|
|
220
259
|
```
|
|
221
260
|
|
|
261
|
+
### .command(name, cliBuilder, description?)
|
|
262
|
+
|
|
263
|
+
Register a nested command group. The `cliBuilder` is another `CliBuilder` whose commands become subcommands. Parent globals are passed down to nested handlers.
|
|
264
|
+
|
|
265
|
+
```typescript
|
|
266
|
+
const subCommands = bargs('sub').command('foo', ...).command('bar', ...);
|
|
267
|
+
|
|
268
|
+
bargs('main')
|
|
269
|
+
.command('nested', subCommands, 'Nested commands') // nested group
|
|
270
|
+
.parseAsync();
|
|
271
|
+
|
|
272
|
+
// $ main nested foo
|
|
273
|
+
// $ main nested bar
|
|
274
|
+
```
|
|
275
|
+
|
|
222
276
|
### .defaultCommand(name)
|
|
223
277
|
|
|
224
278
|
> Or `.defaultCommand(parser, handler)`
|
|
@@ -245,11 +299,11 @@ Parse arguments and execute handlers.
|
|
|
245
299
|
|
|
246
300
|
```typescript
|
|
247
301
|
// Async (supports async transforms/handlers)
|
|
248
|
-
const result = await bargs
|
|
302
|
+
const result = await bargs('my-cli').globals(...).parseAsync();
|
|
249
303
|
console.log(result.values, result.positionals, result.command);
|
|
250
304
|
|
|
251
305
|
// Sync (no async transforms/handlers)
|
|
252
|
-
const result = bargs
|
|
306
|
+
const result = bargs('my-cli').globals(...).parse();
|
|
253
307
|
```
|
|
254
308
|
|
|
255
309
|
## Option Helpers
|
|
@@ -277,6 +331,32 @@ opt.count(); // -vvv → 3
|
|
|
277
331
|
| `hidden` | `boolean` | Hide from `--help` output |
|
|
278
332
|
| `required` | `boolean` | Mark as required (makes the option non-nullable) |
|
|
279
333
|
|
|
334
|
+
### Boolean Negation (`--no-<flag>`)
|
|
335
|
+
|
|
336
|
+
All boolean options automatically support a negated form `--no-<flag>` to explicitly set the option to `false`:
|
|
337
|
+
|
|
338
|
+
```shell
|
|
339
|
+
$ my-cli --verbose # verbose: true
|
|
340
|
+
$ my-cli --no-verbose # verbose: false
|
|
341
|
+
$ my-cli # verbose: undefined (or default)
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
If both `--flag` and `--no-flag` are specified, bargs throws an error:
|
|
345
|
+
|
|
346
|
+
```shell
|
|
347
|
+
$ my-cli --verbose --no-verbose
|
|
348
|
+
Error: Conflicting options: --verbose and --no-verbose cannot both be specified
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
In help output, booleans with `default: true` display as `--no-<flag>` (since that's how users would turn them off):
|
|
352
|
+
|
|
353
|
+
```typescript
|
|
354
|
+
opt.options({
|
|
355
|
+
colors: opt.boolean({ default: true, description: 'Use colors' }),
|
|
356
|
+
});
|
|
357
|
+
// Help output shows: --no-colors Use colors [boolean] default: true
|
|
358
|
+
```
|
|
359
|
+
|
|
280
360
|
### `opt.options(schema)`
|
|
281
361
|
|
|
282
362
|
Create a parser from an options schema:
|
|
@@ -384,8 +464,7 @@ const globals = map(
|
|
|
384
464
|
}),
|
|
385
465
|
);
|
|
386
466
|
|
|
387
|
-
await bargs
|
|
388
|
-
.create('my-cli')
|
|
467
|
+
await bargs('my-cli')
|
|
389
468
|
.globals(globals)
|
|
390
469
|
.command(
|
|
391
470
|
'info',
|
|
@@ -421,18 +500,47 @@ const globals = map(
|
|
|
421
500
|
);
|
|
422
501
|
```
|
|
423
502
|
|
|
503
|
+
### CamelCase Option Keys
|
|
504
|
+
|
|
505
|
+
If you prefer camelCase property names instead of kebab-case, use the `camelCaseValues` transform:
|
|
506
|
+
|
|
507
|
+
```typescript
|
|
508
|
+
import { bargs, map, opt, camelCaseValues } from '@boneskull/bargs';
|
|
509
|
+
|
|
510
|
+
const { values } = await bargs('my-cli')
|
|
511
|
+
.globals(
|
|
512
|
+
map(
|
|
513
|
+
opt.options({
|
|
514
|
+
'output-dir': opt.string({ default: '/tmp' }),
|
|
515
|
+
'dry-run': opt.boolean(),
|
|
516
|
+
}),
|
|
517
|
+
camelCaseValues,
|
|
518
|
+
),
|
|
519
|
+
)
|
|
520
|
+
.parseAsync(['--output-dir', './dist', '--dry-run']);
|
|
521
|
+
|
|
522
|
+
console.log(values.outputDir); // './dist'
|
|
523
|
+
console.log(values.dryRun); // true
|
|
524
|
+
```
|
|
525
|
+
|
|
526
|
+
The `camelCaseValues` transform:
|
|
527
|
+
|
|
528
|
+
- Converts all kebab-case keys to camelCase (`output-dir` → `outputDir`)
|
|
529
|
+
- Preserves keys that are already camelCase or have no hyphens
|
|
530
|
+
- Is fully type-safe—TypeScript knows the transformed key names
|
|
531
|
+
|
|
424
532
|
## Epilog
|
|
425
533
|
|
|
426
534
|
By default, **bargs** displays your package's homepage and repository URLs (from `package.json`) at the end of help output. URLs become clickable hyperlinks in supported terminals.
|
|
427
535
|
|
|
428
536
|
```typescript
|
|
429
537
|
// Custom epilog
|
|
430
|
-
bargs
|
|
538
|
+
bargs('my-cli', {
|
|
431
539
|
epilog: 'For more info, visit https://example.com',
|
|
432
540
|
});
|
|
433
541
|
|
|
434
542
|
// Disable epilog entirely
|
|
435
|
-
bargs
|
|
543
|
+
bargs('my-cli', { epilog: false });
|
|
436
544
|
```
|
|
437
545
|
|
|
438
546
|
## Theming
|
|
@@ -441,10 +549,10 @@ Customize help output colors with built-in themes or your own:
|
|
|
441
549
|
|
|
442
550
|
```typescript
|
|
443
551
|
// Use a built-in theme: 'default', 'mono', 'ocean', 'warm'
|
|
444
|
-
bargs
|
|
552
|
+
bargs('my-cli', { theme: 'ocean' });
|
|
445
553
|
|
|
446
554
|
// Disable colors entirely
|
|
447
|
-
bargs
|
|
555
|
+
bargs('my-cli', { theme: 'mono' });
|
|
448
556
|
```
|
|
449
557
|
|
|
450
558
|
The `ansi` export provides common ANSI escape codes for styled terminal output:
|
|
@@ -452,7 +560,7 @@ The `ansi` export provides common ANSI escape codes for styled terminal output:
|
|
|
452
560
|
```typescript
|
|
453
561
|
import { ansi } from '@boneskull/bargs';
|
|
454
562
|
|
|
455
|
-
bargs
|
|
563
|
+
bargs('my-cli', {
|
|
456
564
|
theme: {
|
|
457
565
|
command: ansi.bold,
|
|
458
566
|
flag: ansi.brightCyan,
|
|
@@ -498,7 +606,7 @@ import {
|
|
|
498
606
|
} from '@boneskull/bargs';
|
|
499
607
|
|
|
500
608
|
try {
|
|
501
|
-
await bargs
|
|
609
|
+
await bargs('my-cli').parseAsync();
|
|
502
610
|
} catch (error) {
|
|
503
611
|
if (error instanceof ValidationError) {
|
|
504
612
|
// Config validation failed (e.g., invalid schema)
|
package/dist/bargs.cjs
CHANGED
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Core bargs API using parser combinator pattern.
|
|
4
4
|
*
|
|
5
|
-
* Provides `bargs
|
|
6
|
-
*
|
|
5
|
+
* Provides `bargs()` for building CLIs with a fluent API, plus combinator
|
|
6
|
+
* functions like `pipe()`, `map()`, and `handle()`.
|
|
7
7
|
*
|
|
8
8
|
* @packageDocumentation
|
|
9
9
|
*/
|
|
10
10
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
-
exports.bargs = void 0;
|
|
11
|
+
exports.bargs = exports.camelCaseValues = void 0;
|
|
12
12
|
exports.handle = handle;
|
|
13
13
|
exports.map = map;
|
|
14
14
|
exports.merge = merge;
|
|
@@ -53,27 +53,47 @@ function handle(parserOrFn, maybeFn) {
|
|
|
53
53
|
};
|
|
54
54
|
}
|
|
55
55
|
function map(parserOrFn, maybeFn) {
|
|
56
|
+
// Helper to compose transforms (chains existing + new)
|
|
57
|
+
/**
|
|
58
|
+
* @function
|
|
59
|
+
*/
|
|
60
|
+
const composeTransform = (parser, fn) => {
|
|
61
|
+
const existing = parser.__transform;
|
|
62
|
+
if (!existing) {
|
|
63
|
+
return fn;
|
|
64
|
+
}
|
|
65
|
+
// Chain: existing transform first, then new transform
|
|
66
|
+
return (r) => {
|
|
67
|
+
const r1 = existing(r);
|
|
68
|
+
if (r1 instanceof Promise) {
|
|
69
|
+
return r1.then(fn);
|
|
70
|
+
}
|
|
71
|
+
return fn(r1);
|
|
72
|
+
};
|
|
73
|
+
};
|
|
56
74
|
// Direct form: map(parser, fn) returns Parser
|
|
57
75
|
// Check for Parser first since CallableParser is also a function
|
|
58
76
|
if (isParser(parserOrFn)) {
|
|
59
77
|
const parser = parserOrFn;
|
|
60
78
|
const fn = maybeFn;
|
|
79
|
+
const composedTransform = composeTransform(parser, fn);
|
|
61
80
|
return {
|
|
62
81
|
...parser,
|
|
63
82
|
__brand: 'Parser',
|
|
64
83
|
__positionals: [],
|
|
65
|
-
__transform:
|
|
84
|
+
__transform: composedTransform,
|
|
66
85
|
__values: {},
|
|
67
86
|
};
|
|
68
87
|
}
|
|
69
88
|
// Curried form: map(fn) returns (parser) => Parser
|
|
70
89
|
const fn = parserOrFn;
|
|
71
90
|
return (parser) => {
|
|
91
|
+
const composedTransform = composeTransform(parser, fn);
|
|
72
92
|
return {
|
|
73
93
|
...parser,
|
|
74
94
|
__brand: 'Parser',
|
|
75
95
|
__positionals: [],
|
|
76
|
-
__transform:
|
|
96
|
+
__transform: composedTransform,
|
|
77
97
|
__values: {},
|
|
78
98
|
};
|
|
79
99
|
};
|
|
@@ -130,6 +150,43 @@ function merge(...parsers) {
|
|
|
130
150
|
return result;
|
|
131
151
|
}
|
|
132
152
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
153
|
+
// CAMEL CASE HELPER
|
|
154
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
155
|
+
/**
|
|
156
|
+
* Convert kebab-case string to camelCase.
|
|
157
|
+
*
|
|
158
|
+
* @function
|
|
159
|
+
*/
|
|
160
|
+
const kebabToCamel = (s) => s.replace(/-([a-zA-Z])/g, (_, c) => c.toUpperCase());
|
|
161
|
+
/**
|
|
162
|
+
* Transform for use with `map()` that converts kebab-case option keys to
|
|
163
|
+
* camelCase.
|
|
164
|
+
*
|
|
165
|
+
* @example
|
|
166
|
+
*
|
|
167
|
+
* ```typescript
|
|
168
|
+
* import { bargs, opt, map, camelCaseValues } from '@boneskull/bargs';
|
|
169
|
+
*
|
|
170
|
+
* const { values } = await bargs('my-cli')
|
|
171
|
+
* .globals(
|
|
172
|
+
* map(opt.options({ 'output-dir': opt.string() }), camelCaseValues),
|
|
173
|
+
* )
|
|
174
|
+
* .parseAsync();
|
|
175
|
+
*
|
|
176
|
+
* console.log(values.outputDir); // camelCased!
|
|
177
|
+
* ```
|
|
178
|
+
*
|
|
179
|
+
* @function
|
|
180
|
+
*/
|
|
181
|
+
const camelCaseValues = (result) => ({
|
|
182
|
+
...result,
|
|
183
|
+
values: Object.fromEntries(Object.entries(result.values).map(([k, v]) => [
|
|
184
|
+
kebabToCamel(k),
|
|
185
|
+
v,
|
|
186
|
+
])),
|
|
187
|
+
});
|
|
188
|
+
exports.camelCaseValues = camelCaseValues;
|
|
189
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
133
190
|
// CLI BUILDER
|
|
134
191
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
135
192
|
/**
|
|
@@ -138,8 +195,7 @@ function merge(...parsers) {
|
|
|
138
195
|
* @example
|
|
139
196
|
*
|
|
140
197
|
* ```typescript
|
|
141
|
-
* const cli = await bargs
|
|
142
|
-
* .create('my-app', { version: '1.0.0' })
|
|
198
|
+
* const cli = await bargs('my-app', { version: '1.0.0' })
|
|
143
199
|
* .globals(
|
|
144
200
|
* map(opt.options({ verbose: opt.boolean() }), ({ values }) => ({
|
|
145
201
|
* values: { ...values, ts: Date.now() },
|
|
@@ -153,8 +209,10 @@ function merge(...parsers) {
|
|
|
153
209
|
* )
|
|
154
210
|
* .parseAsync();
|
|
155
211
|
* ```
|
|
212
|
+
*
|
|
213
|
+
* @function
|
|
156
214
|
*/
|
|
157
|
-
const
|
|
215
|
+
const bargs = (name, options = {}) => {
|
|
158
216
|
const theme = options.theme ? (0, theme_js_1.getTheme)(options.theme) : theme_js_1.defaultTheme;
|
|
159
217
|
return createCliBuilder({
|
|
160
218
|
commands: new Map(),
|
|
@@ -163,8 +221,11 @@ const create = (name, options = {}) => {
|
|
|
163
221
|
theme,
|
|
164
222
|
});
|
|
165
223
|
};
|
|
224
|
+
exports.bargs = bargs;
|
|
166
225
|
/**
|
|
167
226
|
* Check if something is a Command (has __brand: 'Command').
|
|
227
|
+
*
|
|
228
|
+
* @function
|
|
168
229
|
*/
|
|
169
230
|
const isCommand = (x) => {
|
|
170
231
|
if (x === null || x === undefined) {
|
|
@@ -175,21 +236,39 @@ const isCommand = (x) => {
|
|
|
175
236
|
};
|
|
176
237
|
/**
|
|
177
238
|
* Create a CLI builder.
|
|
239
|
+
*
|
|
240
|
+
* @function
|
|
178
241
|
*/
|
|
179
242
|
const createCliBuilder = (state) => {
|
|
180
|
-
|
|
181
|
-
//
|
|
182
|
-
|
|
243
|
+
const builder = {
|
|
244
|
+
// Internal method for nested command support - not part of public API
|
|
245
|
+
__parseWithParentGlobals(args, parentGlobals, allowAsync) {
|
|
246
|
+
const stateWithGlobals = { ...state, parentGlobals };
|
|
247
|
+
return parseCore(stateWithGlobals, args, allowAsync);
|
|
248
|
+
},
|
|
249
|
+
// Overloaded command(): accepts (name, Command, desc?), (name, Parser, handler, desc?), or (name, CliBuilder, desc?)
|
|
250
|
+
command(name, cmdOrParserOrBuilder, handlerOrDesc, maybeDesc) {
|
|
251
|
+
// Form 3: command(name, CliBuilder, description?) - nested commands
|
|
252
|
+
if (isCliBuilder(cmdOrParserOrBuilder)) {
|
|
253
|
+
const builder = cmdOrParserOrBuilder;
|
|
254
|
+
const description = handlerOrDesc;
|
|
255
|
+
state.commands.set(name, {
|
|
256
|
+
builder: builder,
|
|
257
|
+
description,
|
|
258
|
+
type: 'nested',
|
|
259
|
+
});
|
|
260
|
+
return this;
|
|
261
|
+
}
|
|
183
262
|
let cmd;
|
|
184
263
|
let description;
|
|
185
|
-
if (isCommand(
|
|
264
|
+
if (isCommand(cmdOrParserOrBuilder)) {
|
|
186
265
|
// Form 1: command(name, Command, description?)
|
|
187
|
-
cmd =
|
|
266
|
+
cmd = cmdOrParserOrBuilder;
|
|
188
267
|
description = handlerOrDesc;
|
|
189
268
|
}
|
|
190
|
-
else if (isParser(
|
|
269
|
+
else if (isParser(cmdOrParserOrBuilder)) {
|
|
191
270
|
// Form 2: command(name, Parser, handler, description?)
|
|
192
|
-
const parser =
|
|
271
|
+
const parser = cmdOrParserOrBuilder;
|
|
193
272
|
const handler = handlerOrDesc;
|
|
194
273
|
description = maybeDesc;
|
|
195
274
|
// Create Command from Parser + handler
|
|
@@ -207,9 +286,9 @@ const createCliBuilder = (state) => {
|
|
|
207
286
|
cmd = newCmd;
|
|
208
287
|
}
|
|
209
288
|
else {
|
|
210
|
-
throw new Error('command() requires a Command or
|
|
289
|
+
throw new Error('command() requires a Command, Parser, or CliBuilder as second argument');
|
|
211
290
|
}
|
|
212
|
-
state.commands.set(name, { cmd, description });
|
|
291
|
+
state.commands.set(name, { cmd, description, type: 'command' });
|
|
213
292
|
return this;
|
|
214
293
|
},
|
|
215
294
|
// Overloaded defaultCommand(): accepts name, Command, or (Parser, handler)
|
|
@@ -228,6 +307,7 @@ const createCliBuilder = (state) => {
|
|
|
228
307
|
state.commands.set(defaultName, {
|
|
229
308
|
cmd: nameOrCmdOrParser,
|
|
230
309
|
description: undefined,
|
|
310
|
+
type: 'command',
|
|
231
311
|
});
|
|
232
312
|
}
|
|
233
313
|
else if (isParser(nameOrCmdOrParser)) {
|
|
@@ -248,6 +328,7 @@ const createCliBuilder = (state) => {
|
|
|
248
328
|
state.commands.set(defaultName, {
|
|
249
329
|
cmd: newCmd,
|
|
250
330
|
description: undefined,
|
|
331
|
+
type: 'command',
|
|
251
332
|
});
|
|
252
333
|
}
|
|
253
334
|
else {
|
|
@@ -275,9 +356,13 @@ const createCliBuilder = (state) => {
|
|
|
275
356
|
return parseCore(state, args, true);
|
|
276
357
|
},
|
|
277
358
|
};
|
|
359
|
+
// Return as public CliBuilder (hiding internal method from type)
|
|
360
|
+
return builder;
|
|
278
361
|
};
|
|
279
362
|
/**
|
|
280
363
|
* Core parse logic shared between parse() and parseAsync().
|
|
364
|
+
*
|
|
365
|
+
* @function
|
|
281
366
|
*/
|
|
282
367
|
const parseCore = (state, args, allowAsync) => {
|
|
283
368
|
const { commands, options, theme } = state;
|
|
@@ -310,12 +395,19 @@ const parseCore = (state, args, allowAsync) => {
|
|
|
310
395
|
};
|
|
311
396
|
/**
|
|
312
397
|
* Generate command-specific help.
|
|
398
|
+
*
|
|
399
|
+
* @function
|
|
313
400
|
*/
|
|
314
401
|
const generateCommandHelpNew = (state, commandName, theme) => {
|
|
315
402
|
const commandEntry = state.commands.get(commandName);
|
|
316
403
|
if (!commandEntry) {
|
|
317
404
|
return `Unknown command: ${commandName}`;
|
|
318
405
|
}
|
|
406
|
+
// Handle nested commands - show their subcommand list
|
|
407
|
+
if (commandEntry.type === 'nested') {
|
|
408
|
+
// TODO: Generate proper help for nested command groups
|
|
409
|
+
return `${commandName} is a command group. Run '${state.name} ${commandName} --help' for subcommands.`;
|
|
410
|
+
}
|
|
319
411
|
// TODO: Implement proper command help generation
|
|
320
412
|
const config = {
|
|
321
413
|
commands: {
|
|
@@ -331,6 +423,8 @@ const generateCommandHelpNew = (state, commandName, theme) => {
|
|
|
331
423
|
};
|
|
332
424
|
/**
|
|
333
425
|
* Generate help for the new CLI structure.
|
|
426
|
+
*
|
|
427
|
+
* @function
|
|
334
428
|
*/
|
|
335
429
|
const generateHelpNew = (state, theme) => {
|
|
336
430
|
// TODO: Implement proper help generation for new structure
|
|
@@ -350,6 +444,8 @@ const generateHelpNew = (state, theme) => {
|
|
|
350
444
|
/**
|
|
351
445
|
* Check if something is a Parser (has __brand: 'Parser'). Parsers can be either
|
|
352
446
|
* objects or functions (CallableParser).
|
|
447
|
+
*
|
|
448
|
+
* @function
|
|
353
449
|
*/
|
|
354
450
|
const isParser = (x) => {
|
|
355
451
|
if (x === null || x === undefined) {
|
|
@@ -359,8 +455,26 @@ const isParser = (x) => {
|
|
|
359
455
|
const obj = x;
|
|
360
456
|
return '__brand' in obj && obj.__brand === 'Parser';
|
|
361
457
|
};
|
|
458
|
+
/**
|
|
459
|
+
* Check if something is a CliBuilder (has command, globals, parse, parseAsync
|
|
460
|
+
* methods).
|
|
461
|
+
*
|
|
462
|
+
* @function
|
|
463
|
+
*/
|
|
464
|
+
const isCliBuilder = (x) => {
|
|
465
|
+
if (x === null || x === undefined || typeof x !== 'object') {
|
|
466
|
+
return false;
|
|
467
|
+
}
|
|
468
|
+
const obj = x;
|
|
469
|
+
return (typeof obj.command === 'function' &&
|
|
470
|
+
typeof obj.globals === 'function' &&
|
|
471
|
+
typeof obj.parse === 'function' &&
|
|
472
|
+
typeof obj.parseAsync === 'function');
|
|
473
|
+
};
|
|
362
474
|
/**
|
|
363
475
|
* Run a simple CLI (no commands).
|
|
476
|
+
*
|
|
477
|
+
* @function
|
|
364
478
|
*/
|
|
365
479
|
const runSimple = (state, args, allowAsync) => {
|
|
366
480
|
const { globalParser } = state;
|
|
@@ -395,8 +509,20 @@ const runSimple = (state, args, allowAsync) => {
|
|
|
395
509
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
396
510
|
// PUBLIC API
|
|
397
511
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
512
|
+
/**
|
|
513
|
+
* Delegate parsing to a nested CliBuilder, passing down parent globals.
|
|
514
|
+
*
|
|
515
|
+
* @function
|
|
516
|
+
*/
|
|
517
|
+
const delegateToNestedBuilder = (builder, remainingArgs, parentGlobals, allowAsync) => {
|
|
518
|
+
// Access the internal parse function that accepts parent globals
|
|
519
|
+
const internalBuilder = builder;
|
|
520
|
+
return internalBuilder.__parseWithParentGlobals(remainingArgs, parentGlobals, allowAsync);
|
|
521
|
+
};
|
|
398
522
|
/**
|
|
399
523
|
* Run a CLI with commands.
|
|
524
|
+
*
|
|
525
|
+
* @function
|
|
400
526
|
*/
|
|
401
527
|
const runWithCommands = (state, args, allowAsync) => {
|
|
402
528
|
const { commands, defaultCommandName, globalParser } = state;
|
|
@@ -431,6 +557,38 @@ const runWithCommands = (state, args, allowAsync) => {
|
|
|
431
557
|
if (!commandEntry) {
|
|
432
558
|
throw new errors_js_1.HelpError(`Unknown command: ${commandName}`);
|
|
433
559
|
}
|
|
560
|
+
// Handle nested commands (subcommands)
|
|
561
|
+
if (commandEntry.type === 'nested') {
|
|
562
|
+
const { builder } = commandEntry;
|
|
563
|
+
// Parse global options first (before the command name)
|
|
564
|
+
const globalOptionsSchema = globalParser?.__optionsSchema ?? {};
|
|
565
|
+
const globalParsed = (0, parser_js_1.parseSimple)({
|
|
566
|
+
args: args.slice(0, commandIndex),
|
|
567
|
+
options: globalOptionsSchema,
|
|
568
|
+
positionals: [],
|
|
569
|
+
});
|
|
570
|
+
// Apply global transforms if any
|
|
571
|
+
let globalResult = {
|
|
572
|
+
positionals: globalParsed.positionals,
|
|
573
|
+
values: globalParsed.values,
|
|
574
|
+
};
|
|
575
|
+
const globalTransform = globalParser?.__transform;
|
|
576
|
+
// Args for nested builder are ONLY those after the command name (not global options)
|
|
577
|
+
const nestedArgs = args.slice(commandIndex + 1);
|
|
578
|
+
if (globalTransform) {
|
|
579
|
+
const transformed = globalTransform(globalResult);
|
|
580
|
+
if (transformed instanceof Promise) {
|
|
581
|
+
if (!allowAsync) {
|
|
582
|
+
throw new errors_js_1.BargsError('Async global transform detected. Use parseAsync() instead of parse().');
|
|
583
|
+
}
|
|
584
|
+
return transformed.then((r) => {
|
|
585
|
+
return delegateToNestedBuilder(builder, nestedArgs, r, allowAsync);
|
|
586
|
+
});
|
|
587
|
+
}
|
|
588
|
+
globalResult = transformed;
|
|
589
|
+
}
|
|
590
|
+
return delegateToNestedBuilder(builder, nestedArgs, globalResult, allowAsync);
|
|
591
|
+
}
|
|
434
592
|
const { cmd } = commandEntry;
|
|
435
593
|
// Merge global and command options schemas
|
|
436
594
|
const globalOptionsSchema = globalParser?.__optionsSchema ?? {};
|
|
@@ -446,11 +604,16 @@ const runWithCommands = (state, args, allowAsync) => {
|
|
|
446
604
|
options: mergedOptionsSchema,
|
|
447
605
|
positionals: commandPositionalsSchema,
|
|
448
606
|
});
|
|
607
|
+
// Merge parent globals (from nested command delegation) with parsed values
|
|
608
|
+
const parentValues = state.parentGlobals?.values ?? {};
|
|
449
609
|
let result = {
|
|
450
610
|
positionals: parsed.positionals,
|
|
451
|
-
values: parsed.values,
|
|
611
|
+
values: { ...parentValues, ...parsed.values },
|
|
452
612
|
};
|
|
453
613
|
// Helper to check for async and throw if not allowed
|
|
614
|
+
/**
|
|
615
|
+
* @function
|
|
616
|
+
*/
|
|
454
617
|
const checkAsync = (value, context) => {
|
|
455
618
|
if (value instanceof Promise && !allowAsync) {
|
|
456
619
|
throw new errors_js_1.BargsError(`Async ${context} detected. Use parseAsync() instead of parse().`);
|
|
@@ -460,6 +623,9 @@ const runWithCommands = (state, args, allowAsync) => {
|
|
|
460
623
|
const globalTransform = globalParser?.__transform;
|
|
461
624
|
const commandTransform = cmd?.__transform;
|
|
462
625
|
// Apply transforms and run handler
|
|
626
|
+
/**
|
|
627
|
+
* @function
|
|
628
|
+
*/
|
|
463
629
|
const applyTransformsAndHandle = () => {
|
|
464
630
|
// Apply global transforms first
|
|
465
631
|
if (globalTransform) {
|
|
@@ -475,6 +641,9 @@ const runWithCommands = (state, args, allowAsync) => {
|
|
|
475
641
|
}
|
|
476
642
|
return continueWithCommandTransform();
|
|
477
643
|
};
|
|
644
|
+
/**
|
|
645
|
+
* @function
|
|
646
|
+
*/
|
|
478
647
|
const continueWithCommandTransform = () => {
|
|
479
648
|
// Apply command transforms
|
|
480
649
|
if (commandTransform) {
|
|
@@ -490,6 +659,9 @@ const runWithCommands = (state, args, allowAsync) => {
|
|
|
490
659
|
}
|
|
491
660
|
return runHandler();
|
|
492
661
|
};
|
|
662
|
+
/**
|
|
663
|
+
* @function
|
|
664
|
+
*/
|
|
493
665
|
const runHandler = () => {
|
|
494
666
|
const handlerResult = cmd.handler(result);
|
|
495
667
|
checkAsync(handlerResult, 'handler');
|
|
@@ -501,9 +673,8 @@ const runWithCommands = (state, args, allowAsync) => {
|
|
|
501
673
|
return applyTransformsAndHandle();
|
|
502
674
|
};
|
|
503
675
|
/**
|
|
504
|
-
*
|
|
676
|
+
* @ignore
|
|
677
|
+
* @deprecated
|
|
505
678
|
*/
|
|
506
|
-
exports.bargs =
|
|
507
|
-
create,
|
|
508
|
-
};
|
|
679
|
+
exports.bargs.create = exports.bargs;
|
|
509
680
|
//# sourceMappingURL=bargs.js.map
|