@boneskull/bargs 2.0.0 → 3.0.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 +305 -299
- package/dist/bargs.cjs +465 -135
- package/dist/bargs.cjs.map +1 -1
- package/dist/bargs.d.cts +35 -17
- package/dist/bargs.d.cts.map +1 -1
- package/dist/bargs.d.ts +35 -17
- package/dist/bargs.d.ts.map +1 -1
- package/dist/bargs.js +463 -135
- package/dist/bargs.js.map +1 -1
- package/dist/help.cjs +1 -2
- package/dist/help.cjs.map +1 -1
- package/dist/help.d.cts +20 -3
- package/dist/help.d.cts.map +1 -1
- package/dist/help.d.ts +20 -3
- package/dist/help.d.ts.map +1 -1
- package/dist/help.js +1 -2
- package/dist/help.js.map +1 -1
- package/dist/index.cjs +27 -31
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +15 -79
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +15 -79
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +20 -30
- package/dist/index.js.map +1 -1
- package/dist/opt.cjs +148 -122
- package/dist/opt.cjs.map +1 -1
- package/dist/opt.d.cts +87 -113
- package/dist/opt.d.cts.map +1 -1
- package/dist/opt.d.ts +87 -113
- package/dist/opt.d.ts.map +1 -1
- package/dist/opt.js +147 -121
- package/dist/opt.js.map +1 -1
- package/dist/parser.cjs +3 -230
- package/dist/parser.cjs.map +1 -1
- package/dist/parser.d.cts +3 -51
- package/dist/parser.d.cts.map +1 -1
- package/dist/parser.d.ts +3 -51
- package/dist/parser.d.ts.map +1 -1
- package/dist/parser.js +2 -223
- package/dist/parser.js.map +1 -1
- package/dist/types.cjs +1 -3
- package/dist/types.cjs.map +1 -1
- package/dist/types.d.cts +111 -233
- package/dist/types.d.cts.map +1 -1
- package/dist/types.d.ts +111 -233
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +1 -3
- package/dist/types.js.map +1 -1
- package/package.json +2 -2
- package/dist/validate.cjs +0 -463
- package/dist/validate.cjs.map +0 -1
- package/dist/validate.d.cts +0 -28
- package/dist/validate.d.cts.map +0 -1
- package/dist/validate.d.ts +0 -28
- package/dist/validate.d.ts.map +0 -1
- package/dist/validate.js +0 -459
- package/dist/validate.js.map +0 -1
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<a href="/"><img src="./assets/logo.png" width="512px" align="center" alt="bargs: a barg parser"/></a>
|
|
3
3
|
<h1 align="center"><span class="bargs">⁓ bargs ⁓<span></h1>
|
|
4
4
|
<p align="center">
|
|
5
|
-
<em
|
|
5
|
+
<em>"Ex argumentis, veritas"</em>
|
|
6
6
|
<br/>
|
|
7
7
|
<small>by <a href="https://github.com/boneskull" title="@boneskull on GitHub">@boneskull</a></small>
|
|
8
8
|
</p>
|
|
@@ -16,311 +16,253 @@ npm install @boneskull/bargs
|
|
|
16
16
|
|
|
17
17
|
## Why bargs?
|
|
18
18
|
|
|
19
|
-
Most argument parsers make you choose: either a simple API with weak types, or a complex and overengineered DSL. **bargs**
|
|
19
|
+
Most argument parsers make you choose: either a simple API with weak types, or a complex and overengineered DSL. **bargs** provides a combinator-style API for building type-safe CLIs—composable schema definitions with full type inference.
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
## Quick Start
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
A CLI with an optional command and a couple options:
|
|
24
24
|
|
|
25
25
|
```typescript
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
import { bargs, opt, pos } from '@boneskull/bargs';
|
|
27
|
+
|
|
28
|
+
await bargs
|
|
29
|
+
.create('greet', { version: '1.0.0' })
|
|
30
|
+
.globals(
|
|
31
|
+
opt.options({
|
|
32
|
+
name: opt.string({ default: 'world' }),
|
|
33
|
+
loud: opt.boolean({ aliases: ['l'] }),
|
|
34
|
+
}),
|
|
35
|
+
)
|
|
36
|
+
.command(
|
|
37
|
+
'say',
|
|
38
|
+
pos.positionals(pos.string({ name: 'message', required: true })),
|
|
39
|
+
({ positionals, values }) => {
|
|
40
|
+
const [message] = positionals;
|
|
41
|
+
const greeting = `${message}, ${values.name}!`;
|
|
42
|
+
console.log(values.loud ? greeting.toUpperCase() : greeting);
|
|
43
|
+
},
|
|
44
|
+
'Say a greeting',
|
|
45
|
+
)
|
|
46
|
+
.defaultCommand('say')
|
|
47
|
+
.parseAsync();
|
|
48
|
+
```
|
|
28
49
|
|
|
29
|
-
|
|
30
|
-
|
|
50
|
+
```shell
|
|
51
|
+
$ greet Hello --name Alice --loud
|
|
52
|
+
HELLO, ALICE!
|
|
31
53
|
```
|
|
32
54
|
|
|
33
|
-
|
|
55
|
+
## Usage
|
|
34
56
|
|
|
35
|
-
###
|
|
57
|
+
### Type-Safe by Construction
|
|
36
58
|
|
|
37
|
-
|
|
59
|
+
Each helper returns a fully-typed definition:
|
|
38
60
|
|
|
39
61
|
```typescript
|
|
40
|
-
|
|
41
|
-
const verboseOpt = { verbose: bargs.boolean({ aliases: ['v'] }) };
|
|
42
|
-
const outputOpt = {
|
|
43
|
-
output: bargs.string({ aliases: ['o'], default: 'stdout' }),
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
// Merge with spread
|
|
47
|
-
const result = bargs({
|
|
48
|
-
name: 'tool',
|
|
49
|
-
options: {
|
|
50
|
-
...verboseOpt,
|
|
51
|
-
...outputOpt,
|
|
52
|
-
format: bargs.enum(['json', 'text']),
|
|
53
|
-
},
|
|
54
|
-
});
|
|
55
|
-
```
|
|
62
|
+
import { opt, pos } from '@boneskull/bargs';
|
|
56
63
|
|
|
57
|
-
|
|
64
|
+
const verbose = opt.boolean({ aliases: ['v'] });
|
|
65
|
+
// Type: BooleanOption & { aliases: ['v'] }
|
|
58
66
|
|
|
59
|
-
|
|
60
|
-
//
|
|
61
|
-
const sharedOpts = bargs.options(verboseOpt, outputOpt);
|
|
67
|
+
const level = opt.enum(['low', 'medium', 'high'], { default: 'medium' });
|
|
68
|
+
// Type: EnumOption<'low' | 'medium' | 'high'> & { default: 'medium' }
|
|
62
69
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
bargs.stringPos({ name: 'input', required: true }),
|
|
66
|
-
bargs.stringPos({ name: 'output' }),
|
|
67
|
-
);
|
|
70
|
+
const file = pos.string({ name: 'file', required: true });
|
|
71
|
+
// Type: StringPositional & { name: 'file', required: true }
|
|
68
72
|
```
|
|
69
73
|
|
|
70
|
-
|
|
71
|
-
> Helper functions are provided for convenience and composability, but you can also just use raw object literals. See the ["tasks" example](./examples/tasks.ts) to see how.
|
|
72
|
-
|
|
73
|
-
### Zero (0) Dependencies
|
|
74
|
+
When you build a CLI with these, the result types flow through automatically—options with defaults or `required: true` are non-nullable.
|
|
74
75
|
|
|
75
|
-
|
|
76
|
+
### Composable
|
|
76
77
|
|
|
77
|
-
|
|
78
|
+
Options and positionals can be merged using callable parsers:
|
|
78
79
|
|
|
79
80
|
```typescript
|
|
80
|
-
import {
|
|
81
|
+
import { opt, pos } from '@boneskull/bargs';
|
|
81
82
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
loud: bargs.boolean({ aliases: ['l'] }),
|
|
87
|
-
},
|
|
83
|
+
// Create separate parsers
|
|
84
|
+
const options = opt.options({
|
|
85
|
+
verbose: opt.boolean({ aliases: ['v'] }),
|
|
86
|
+
output: opt.string({ aliases: ['o'], default: 'stdout' }),
|
|
88
87
|
});
|
|
89
88
|
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
|
|
89
|
+
const positionals = pos.positionals(
|
|
90
|
+
pos.string({ name: 'input', required: true }),
|
|
91
|
+
);
|
|
93
92
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
93
|
+
// Merge them: positionals(options) combines both
|
|
94
|
+
const parser = positionals(options);
|
|
95
|
+
// Type: Parser<{ verbose: boolean | undefined, output: string }, [string]>
|
|
97
96
|
```
|
|
98
97
|
|
|
99
|
-
|
|
98
|
+
### Simple CLI
|
|
100
99
|
|
|
101
|
-
|
|
100
|
+
For a CLI without subcommands, use `.globals()` with merged options and positionals, then handle the result yourself:
|
|
102
101
|
|
|
103
102
|
```typescript
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
})
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
Instead, use **`bargsAsync()`**:
|
|
115
|
-
|
|
116
|
-
```typescript
|
|
117
|
-
import { bargsAsync } from '@boneskull/bargs';
|
|
103
|
+
import { bargs, opt, pos } from '@boneskull/bargs';
|
|
104
|
+
|
|
105
|
+
// Merge options and positionals into one parser
|
|
106
|
+
// when a positional is variadic, it becomes an array within the result
|
|
107
|
+
const parser = pos.positionals(pos.variadic('string', { name: 'text' }))(
|
|
108
|
+
opt.options({
|
|
109
|
+
uppercase: opt.boolean({ aliases: ['u'], default: false }),
|
|
110
|
+
}),
|
|
111
|
+
);
|
|
118
112
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
113
|
+
const { values, positionals } = await bargs
|
|
114
|
+
.create('echo', {
|
|
115
|
+
description: 'Echo text to stdout',
|
|
116
|
+
version: '1.0.0',
|
|
117
|
+
})
|
|
118
|
+
.globals(parser)
|
|
119
|
+
.parseAsync();
|
|
120
|
+
|
|
121
|
+
const [words] = positionals;
|
|
122
|
+
const text = words.join(' ');
|
|
123
|
+
console.log(values.uppercase ? text.toUpperCase() : text);
|
|
128
124
|
```
|
|
129
125
|
|
|
130
|
-
|
|
126
|
+
### Command-Based CLI
|
|
131
127
|
|
|
132
|
-
|
|
128
|
+
For a CLI with multiple subcommands:
|
|
133
129
|
|
|
134
130
|
```typescript
|
|
135
|
-
bargs
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
131
|
+
import { bargs, merge, opt, pos } from '@boneskull/bargs';
|
|
132
|
+
|
|
133
|
+
await bargs
|
|
134
|
+
.create('tasks', {
|
|
135
|
+
description: 'A task manager',
|
|
136
|
+
version: '1.0.0',
|
|
137
|
+
})
|
|
138
|
+
.globals(
|
|
139
|
+
opt.options({
|
|
140
|
+
verbose: opt.boolean({ aliases: ['v'], default: false }),
|
|
144
141
|
}),
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
142
|
+
)
|
|
143
|
+
.command(
|
|
144
|
+
'add',
|
|
145
|
+
// Use merge() to combine positionals with command-specific options
|
|
146
|
+
merge(
|
|
147
|
+
opt.options({
|
|
148
|
+
priority: opt.enum(['low', 'medium', 'high'], { default: 'medium' }),
|
|
149
|
+
}),
|
|
150
|
+
pos.positionals(pos.string({ name: 'text', required: true })),
|
|
151
|
+
),
|
|
152
|
+
({ positionals, values }) => {
|
|
153
|
+
const [text] = positionals;
|
|
154
|
+
console.log(`Adding ${values.priority} priority task: ${text}`);
|
|
155
|
+
if (values.verbose) console.log('Verbose mode enabled');
|
|
156
|
+
},
|
|
157
|
+
'Add a task',
|
|
158
|
+
)
|
|
159
|
+
.command(
|
|
160
|
+
'list',
|
|
161
|
+
opt.options({
|
|
162
|
+
all: opt.boolean({ default: false }),
|
|
152
163
|
}),
|
|
153
|
-
|
|
154
|
-
|
|
164
|
+
({ values }) => {
|
|
165
|
+
console.log(values.all ? 'All tasks' : 'Pending tasks');
|
|
166
|
+
},
|
|
167
|
+
'List tasks',
|
|
168
|
+
)
|
|
169
|
+
.defaultCommand('list')
|
|
170
|
+
.parseAsync();
|
|
155
171
|
```
|
|
156
172
|
|
|
157
173
|
```shell
|
|
158
|
-
$
|
|
159
|
-
|
|
174
|
+
$ tasks add "Buy groceries" --priority high --verbose
|
|
175
|
+
Adding high priority task: Buy groceries
|
|
176
|
+
Verbose mode enabled
|
|
160
177
|
|
|
161
|
-
$
|
|
162
|
-
|
|
178
|
+
$ tasks list --all
|
|
179
|
+
All tasks
|
|
163
180
|
```
|
|
164
181
|
|
|
165
|
-
|
|
182
|
+
## API
|
|
166
183
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
```typescript
|
|
170
|
-
bargs({
|
|
171
|
-
name: 'git',
|
|
172
|
-
commands: {
|
|
173
|
-
/* ... */
|
|
174
|
-
},
|
|
175
|
-
// Run 'status' when no command given
|
|
176
|
-
defaultHandler: 'status',
|
|
177
|
-
});
|
|
184
|
+
### `bargs.create(name, options?)`
|
|
178
185
|
|
|
179
|
-
|
|
180
|
-
bargs({
|
|
181
|
-
name: 'git',
|
|
182
|
-
commands: {
|
|
183
|
-
/* ... */
|
|
184
|
-
},
|
|
185
|
-
defaultHandler: ({ values }) => {
|
|
186
|
-
console.log('Run "git --help" for usage');
|
|
187
|
-
},
|
|
188
|
-
});
|
|
189
|
-
```
|
|
186
|
+
Create a CLI builder.
|
|
190
187
|
|
|
191
|
-
|
|
188
|
+
| Option | Type | Description |
|
|
189
|
+
| ------------- | ------------------- | ------------------------------------------- |
|
|
190
|
+
| `description` | `string` | Description shown in help |
|
|
191
|
+
| `version` | `string` | Enables `--version` flag |
|
|
192
|
+
| `epilog` | `string` or `false` | Footer text in help (see [Epilog](#epilog)) |
|
|
193
|
+
| `theme` | `Theme` | Help color theme (see [Theming](#theming)) |
|
|
192
194
|
|
|
193
|
-
|
|
195
|
+
### `.globals(parser)`
|
|
194
196
|
|
|
195
|
-
|
|
196
|
-
- Adding computed/derived values
|
|
197
|
-
- Validating or normalizing inputs
|
|
198
|
-
- Async operations like file system checks
|
|
197
|
+
Set global options and transforms that apply to all commands.
|
|
199
198
|
|
|
200
199
|
```typescript
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
options: {
|
|
204
|
-
config: bargsAsync.string({ aliases: ['c'] }),
|
|
205
|
-
verbose: bargsAsync.boolean({ default: false }),
|
|
206
|
-
},
|
|
207
|
-
positionals: [bargsAsync.variadic('string', { name: 'files' })],
|
|
208
|
-
transforms: {
|
|
209
|
-
// Transform option values
|
|
210
|
-
values: (values) => {
|
|
211
|
-
// Load config file if specified
|
|
212
|
-
const fileConfig = values.config
|
|
213
|
-
? JSON.parse(readFileSync(values.config, 'utf8'))
|
|
214
|
-
: {};
|
|
215
|
-
|
|
216
|
-
return {
|
|
217
|
-
...fileConfig,
|
|
218
|
-
...values,
|
|
219
|
-
// Add computed values
|
|
220
|
-
timestamp: new Date().toISOString(),
|
|
221
|
-
};
|
|
222
|
-
},
|
|
223
|
-
// Transform positionals
|
|
224
|
-
positionals: (positionals) => {
|
|
225
|
-
const [files] = positionals;
|
|
226
|
-
// Filter to only existing files
|
|
227
|
-
return [files.filter((f) => existsSync(f))];
|
|
228
|
-
},
|
|
229
|
-
},
|
|
230
|
-
handler: ({ values, positionals }) => {
|
|
231
|
-
// values.timestamp is available here
|
|
232
|
-
// positionals contains only valid files
|
|
233
|
-
},
|
|
234
|
-
});
|
|
200
|
+
bargs.create('my-cli').globals(opt.options({ verbose: opt.boolean() }));
|
|
201
|
+
// ...
|
|
235
202
|
```
|
|
236
203
|
|
|
237
|
-
###
|
|
238
|
-
|
|
239
|
-
Transforms are akin to yargs' "middleware". They execute after parsing but before the handler (if present). Transforms should also be used in place of yargs' "coerce" option.
|
|
240
|
-
|
|
241
|
-
| Property | Type | Description |
|
|
242
|
-
| ------------- | ----------------------------------------- | --------------------------------- |
|
|
243
|
-
| `values` | `(values) => TransformedValues` | Transform parsed option values |
|
|
244
|
-
| `positionals` | `(positionals) => TransformedPositionals` | Transform parsed positional tuple |
|
|
204
|
+
### `.command(name, parser, handler, description?)`
|
|
245
205
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
### Type Inference
|
|
249
|
-
|
|
250
|
-
Transforms are fully type-safe. TypeScript infers the transformed types:
|
|
206
|
+
Register a command. The handler receives merged global + command types.
|
|
251
207
|
|
|
252
208
|
```typescript
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
options
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
doubled: (values.count ?? 0) * 2, // Add computed property
|
|
260
|
-
}),
|
|
209
|
+
.command(
|
|
210
|
+
'build',
|
|
211
|
+
opt.options({ watch: opt.boolean() }),
|
|
212
|
+
({ values }) => {
|
|
213
|
+
// values has both global options AND { watch: boolean }
|
|
214
|
+
console.log(values.verbose, values.watch);
|
|
261
215
|
},
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
},
|
|
265
|
-
});
|
|
216
|
+
'Build the project',
|
|
217
|
+
)
|
|
266
218
|
```
|
|
267
219
|
|
|
268
|
-
|
|
220
|
+
### `.defaultCommand(name)` or `.defaultCommand(parser, handler)`
|
|
269
221
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
| Property | Type | Description |
|
|
273
|
-
| ------------- | --------------------- | ------------------------------------------------------------ |
|
|
274
|
-
| `name` | `string` | CLI name (required) |
|
|
275
|
-
| `description` | `string` | Description shown in help |
|
|
276
|
-
| `version` | `string` | Enables `--version` flag |
|
|
277
|
-
| `options` | `OptionsSchema` | Named options (`--flag`) |
|
|
278
|
-
| `positionals` | `PositionalsSchema` | Positional arguments |
|
|
279
|
-
| `commands` | `Record<string, ...>` | Subcommands |
|
|
280
|
-
| `transforms` | `TransformsConfig` | Transform values/positionals (see [Transforms](#transforms)) |
|
|
281
|
-
| `handler` | `Handler` | Handler function for simple CLIs |
|
|
282
|
-
| `epilog` | `string \| false` | Footer text in help (see [Epilog](#epilog)) |
|
|
283
|
-
| `args` | `string[]` | Custom args (defaults to `process.argv.slice(2)`) |
|
|
222
|
+
Set the command that runs when no command is specified.
|
|
284
223
|
|
|
285
224
|
```typescript
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
});
|
|
225
|
+
// Reference an existing command by name
|
|
226
|
+
.defaultCommand('list')
|
|
227
|
+
|
|
228
|
+
// Or define an inline default
|
|
229
|
+
.defaultCommand(
|
|
230
|
+
pos.positionals(pos.string({ name: 'file' })),
|
|
231
|
+
({ positionals }) => console.log(positionals[0]),
|
|
232
|
+
)
|
|
295
233
|
```
|
|
296
234
|
|
|
297
|
-
###
|
|
235
|
+
### `.parse(args?)` / `.parseAsync(args?)`
|
|
298
236
|
|
|
299
|
-
|
|
237
|
+
Parse arguments and execute handlers.
|
|
300
238
|
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
| `theme` | `ThemeInput` | `--help` Color theme (see [Theming](#theming)) |
|
|
239
|
+
- **`.parse()`** - Synchronous. Throws if any transform or handler returns a Promise.
|
|
240
|
+
- **`.parseAsync()`** - Asynchronous. Supports async transforms and handlers.
|
|
304
241
|
|
|
305
242
|
```typescript
|
|
306
|
-
|
|
243
|
+
// Async (supports async transforms/handlers)
|
|
244
|
+
const result = await bargs.create('my-cli').globals(...).parseAsync();
|
|
245
|
+
console.log(result.values, result.positionals, result.command);
|
|
246
|
+
|
|
247
|
+
// Sync (no async transforms/handlers)
|
|
248
|
+
const result = bargs.create('my-cli').globals(...).parse();
|
|
307
249
|
```
|
|
308
250
|
|
|
309
251
|
## Option Helpers
|
|
310
252
|
|
|
311
253
|
```typescript
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
254
|
+
import { opt } from '@boneskull/bargs';
|
|
255
|
+
|
|
256
|
+
opt.string({ default: 'value' }); // --name value
|
|
257
|
+
opt.number({ default: 42 }); // --count 42
|
|
258
|
+
opt.boolean({ aliases: ['v'] }); // --verbose, -v
|
|
259
|
+
opt.enum(['a', 'b', 'c']); // --level a
|
|
260
|
+
opt.array('string'); // --file x --file y
|
|
261
|
+
opt.count(); // -vvv → 3
|
|
318
262
|
```
|
|
319
263
|
|
|
320
264
|
### Option Properties
|
|
321
265
|
|
|
322
|
-
All option helpers accept these properties:
|
|
323
|
-
|
|
324
266
|
| Property | Type | Description |
|
|
325
267
|
| ------------- | ---------- | ------------------------------------------------ |
|
|
326
268
|
| `aliases` | `string[]` | Short flags (e.g., `['v']` for `-v`) |
|
|
@@ -330,27 +272,27 @@ All option helpers accept these properties:
|
|
|
330
272
|
| `hidden` | `boolean` | Hide from `--help` output |
|
|
331
273
|
| `required` | `boolean` | Mark as required (makes the option non-nullable) |
|
|
332
274
|
|
|
333
|
-
|
|
275
|
+
### `opt.options(schema)`
|
|
276
|
+
|
|
277
|
+
Create a parser from an options schema:
|
|
334
278
|
|
|
335
279
|
```typescript
|
|
336
|
-
|
|
337
|
-
aliases: ['
|
|
338
|
-
default: '
|
|
339
|
-
description: 'Output file path',
|
|
340
|
-
group: 'Output Options',
|
|
280
|
+
const parser = opt.options({
|
|
281
|
+
verbose: opt.boolean({ aliases: ['v'] }),
|
|
282
|
+
output: opt.string({ default: 'out.txt' }),
|
|
341
283
|
});
|
|
342
|
-
|
|
343
|
-
// Hidden options won't appear in help
|
|
344
|
-
bargs.boolean({ hidden: true });
|
|
284
|
+
// Type: Parser<{ verbose: boolean | undefined, output: string }, []>
|
|
345
285
|
```
|
|
346
286
|
|
|
347
287
|
## Positional Helpers
|
|
348
288
|
|
|
349
289
|
```typescript
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
290
|
+
import { pos } from '@boneskull/bargs';
|
|
291
|
+
|
|
292
|
+
pos.string({ required: true }); // <file>
|
|
293
|
+
pos.number({ default: 8080 }); // [port]
|
|
294
|
+
pos.enum(['dev', 'prod']); // [env]
|
|
295
|
+
pos.variadic('string'); // [files...]
|
|
354
296
|
```
|
|
355
297
|
|
|
356
298
|
### Positional Properties
|
|
@@ -362,41 +304,116 @@ bargs.variadic('string'); // [files...]
|
|
|
362
304
|
| `name` | `string` | Display name in help (defaults to `arg0`, `arg1`, ...) |
|
|
363
305
|
| `required` | `boolean` | Mark as required (shown as `<name>` vs `[name]`) |
|
|
364
306
|
|
|
365
|
-
|
|
307
|
+
### `pos.positionals(...defs)`
|
|
308
|
+
|
|
309
|
+
Create a parser from positional definitions:
|
|
366
310
|
|
|
367
311
|
```typescript
|
|
368
|
-
|
|
369
|
-
name: '
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
}
|
|
312
|
+
const parser = pos.positionals(
|
|
313
|
+
pos.string({ name: 'source', required: true }),
|
|
314
|
+
pos.string({ name: 'dest', required: true }),
|
|
315
|
+
);
|
|
316
|
+
// Type: Parser<{}, [string, string]>
|
|
373
317
|
```
|
|
374
318
|
|
|
375
|
-
|
|
319
|
+
Use `variadic` for rest arguments (must be last):
|
|
376
320
|
|
|
377
321
|
```typescript
|
|
378
|
-
const
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
322
|
+
const parser = pos.positionals(pos.variadic('string', { name: 'files' }));
|
|
323
|
+
// Type: Parser<{}, [string[]]>
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
## Merging Parsers
|
|
327
|
+
|
|
328
|
+
Use `merge()` to combine multiple parsers into one:
|
|
329
|
+
|
|
330
|
+
```typescript
|
|
331
|
+
import { merge, opt, pos } from '@boneskull/bargs';
|
|
385
332
|
|
|
386
|
-
const
|
|
387
|
-
|
|
333
|
+
const combined = merge(
|
|
334
|
+
opt.options({
|
|
335
|
+
priority: opt.enum(['low', 'medium', 'high'], { default: 'medium' }),
|
|
336
|
+
}),
|
|
337
|
+
pos.positionals(pos.string({ name: 'task', required: true })),
|
|
338
|
+
);
|
|
339
|
+
// Type: Parser<{ priority: 'low' | 'medium' | 'high' }, [string]>
|
|
388
340
|
```
|
|
389
341
|
|
|
390
|
-
|
|
342
|
+
You can merge as many parsers as needed—options are merged (later overrides earlier), and positionals are concatenated.
|
|
343
|
+
|
|
344
|
+
Alternatively, parsers can be merged by calling one with the other:
|
|
391
345
|
|
|
392
346
|
```typescript
|
|
393
|
-
const
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
347
|
+
const options = opt.options({ priority: opt.enum(['low', 'medium', 'high']) });
|
|
348
|
+
const positionals = pos.positionals(
|
|
349
|
+
pos.string({ name: 'task', required: true }),
|
|
350
|
+
);
|
|
397
351
|
|
|
398
|
-
|
|
399
|
-
|
|
352
|
+
// These are equivalent:
|
|
353
|
+
const combined1 = positionals(options);
|
|
354
|
+
const combined2 = options(positionals);
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
Use whichever style you find more readable.
|
|
358
|
+
|
|
359
|
+
## Transforms
|
|
360
|
+
|
|
361
|
+
Use `map()` to transform parsed values before they reach your handler:
|
|
362
|
+
|
|
363
|
+
```typescript
|
|
364
|
+
import { bargs, map, opt } from '@boneskull/bargs';
|
|
365
|
+
|
|
366
|
+
const globals = map(
|
|
367
|
+
opt.options({
|
|
368
|
+
config: opt.string(),
|
|
369
|
+
verbose: opt.boolean({ default: false }),
|
|
370
|
+
}),
|
|
371
|
+
({ values, positionals }) => ({
|
|
372
|
+
positionals,
|
|
373
|
+
values: {
|
|
374
|
+
...values,
|
|
375
|
+
// Add computed properties
|
|
376
|
+
timestamp: new Date().toISOString(),
|
|
377
|
+
configLoaded: !!values.config,
|
|
378
|
+
},
|
|
379
|
+
}),
|
|
380
|
+
);
|
|
381
|
+
|
|
382
|
+
await bargs
|
|
383
|
+
.create('my-cli')
|
|
384
|
+
.globals(globals)
|
|
385
|
+
.command(
|
|
386
|
+
'info',
|
|
387
|
+
opt.options({}),
|
|
388
|
+
({ values }) => {
|
|
389
|
+
// values.timestamp and values.configLoaded are available
|
|
390
|
+
console.log(values.timestamp);
|
|
391
|
+
},
|
|
392
|
+
'Show info',
|
|
393
|
+
)
|
|
394
|
+
.parseAsync();
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
Transforms are fully type-safe—the return type becomes the type available in handlers.
|
|
398
|
+
|
|
399
|
+
### Async Transforms
|
|
400
|
+
|
|
401
|
+
Transforms can be async:
|
|
402
|
+
|
|
403
|
+
```typescript
|
|
404
|
+
const globals = map(
|
|
405
|
+
opt.options({ url: opt.string({ required: true }) }),
|
|
406
|
+
async ({ values, positionals }) => {
|
|
407
|
+
const response = await fetch(values.url);
|
|
408
|
+
return {
|
|
409
|
+
positionals,
|
|
410
|
+
values: {
|
|
411
|
+
...values,
|
|
412
|
+
data: await response.json(),
|
|
413
|
+
},
|
|
414
|
+
};
|
|
415
|
+
},
|
|
416
|
+
);
|
|
400
417
|
```
|
|
401
418
|
|
|
402
419
|
## Epilog
|
|
@@ -405,16 +422,12 @@ By default, **bargs** displays your package's homepage and repository URLs (from
|
|
|
405
422
|
|
|
406
423
|
```typescript
|
|
407
424
|
// Custom epilog
|
|
408
|
-
bargs({
|
|
409
|
-
name: 'my-cli',
|
|
425
|
+
bargs.create('my-cli', {
|
|
410
426
|
epilog: 'For more info, visit https://example.com',
|
|
411
427
|
});
|
|
412
428
|
|
|
413
429
|
// Disable epilog entirely
|
|
414
|
-
bargs({
|
|
415
|
-
name: 'my-cli',
|
|
416
|
-
epilog: false,
|
|
417
|
-
});
|
|
430
|
+
bargs.create('my-cli', { epilog: false });
|
|
418
431
|
```
|
|
419
432
|
|
|
420
433
|
## Theming
|
|
@@ -423,38 +436,23 @@ Customize help output colors with built-in themes or your own:
|
|
|
423
436
|
|
|
424
437
|
```typescript
|
|
425
438
|
// Use a built-in theme: 'default', 'mono', 'ocean', 'warm'
|
|
426
|
-
bargs(
|
|
427
|
-
{
|
|
428
|
-
name: 'my-cli',
|
|
429
|
-
options: { verbose: bargs.boolean() },
|
|
430
|
-
},
|
|
431
|
-
{ theme: 'ocean' },
|
|
432
|
-
);
|
|
439
|
+
bargs.create('my-cli', { theme: 'ocean' });
|
|
433
440
|
|
|
434
441
|
// Disable colors entirely
|
|
435
|
-
bargs(
|
|
442
|
+
bargs.create('my-cli', { theme: 'mono' });
|
|
436
443
|
```
|
|
437
444
|
|
|
438
|
-
The `ansi` export provides common ANSI escape codes for styled terminal output:
|
|
445
|
+
The `ansi` export provides common ANSI escape codes for styled terminal output:
|
|
439
446
|
|
|
440
447
|
```typescript
|
|
441
448
|
import { ansi } from '@boneskull/bargs';
|
|
442
449
|
|
|
443
|
-
bargs(
|
|
450
|
+
bargs.create('my-cli', {
|
|
444
451
|
theme: {
|
|
445
452
|
command: ansi.bold,
|
|
446
|
-
defaultText: ansi.dim,
|
|
447
|
-
defaultValue: ansi.white,
|
|
448
|
-
description: ansi.white,
|
|
449
|
-
epilog: ansi.dim,
|
|
450
|
-
example: ansi.white + ansi.dim,
|
|
451
453
|
flag: ansi.brightCyan,
|
|
452
454
|
positional: ansi.magenta,
|
|
453
|
-
|
|
454
|
-
sectionHeader: ansi.brightMagenta,
|
|
455
|
-
type: ansi.magenta,
|
|
456
|
-
url: ansi.cyan,
|
|
457
|
-
usage: ansi.cyan,
|
|
455
|
+
// ...
|
|
458
456
|
},
|
|
459
457
|
});
|
|
460
458
|
```
|
|
@@ -495,14 +493,16 @@ import {
|
|
|
495
493
|
} from '@boneskull/bargs';
|
|
496
494
|
|
|
497
495
|
try {
|
|
498
|
-
bargs(
|
|
496
|
+
await bargs.create('my-cli').parseAsync();
|
|
499
497
|
} catch (error) {
|
|
500
498
|
if (error instanceof ValidationError) {
|
|
501
499
|
// Config validation failed (e.g., invalid schema)
|
|
500
|
+
// i.e., "you screwed up"
|
|
502
501
|
console.error(`Config error at "${error.path}": ${error.message}`);
|
|
503
502
|
} else if (error instanceof HelpError) {
|
|
504
|
-
//
|
|
505
|
-
|
|
503
|
+
// Likely invalid options, command or positionals;
|
|
504
|
+
// re-throw to trigger help display
|
|
505
|
+
throw error;
|
|
506
506
|
} else if (error instanceof BargsError) {
|
|
507
507
|
// General bargs error
|
|
508
508
|
console.error(error.message);
|
|
@@ -512,11 +512,12 @@ try {
|
|
|
512
512
|
|
|
513
513
|
### Programmatic Help
|
|
514
514
|
|
|
515
|
-
Generate help text
|
|
515
|
+
Generate help text programmatically:
|
|
516
516
|
|
|
517
517
|
```typescript
|
|
518
518
|
import { generateHelp, generateCommandHelp } from '@boneskull/bargs';
|
|
519
519
|
|
|
520
|
+
// These require the internal config structure—see source for details
|
|
520
521
|
const helpText = generateHelp(config);
|
|
521
522
|
const commandHelp = generateCommandHelp(config, 'migrate');
|
|
522
523
|
```
|
|
@@ -558,11 +559,16 @@ console.log(styler.flag('--verbose'));
|
|
|
558
559
|
|
|
559
560
|
// Strip ANSI codes for plain text output
|
|
560
561
|
const plain = stripAnsi('\x1b[32m--verbose\x1b[0m'); // '--verbose'
|
|
561
|
-
|
|
562
|
-
// Override some colors in a built-in theme
|
|
563
|
-
const customTheme = { ...themes.ocean, colors: { flag: ansi.green } };
|
|
564
562
|
```
|
|
565
563
|
|
|
564
|
+
### Low-Level Utilities
|
|
565
|
+
|
|
566
|
+
The `handle(parser, fn)` function is exported for advanced use cases where you need to create a `Command` object outside the fluent builder. It's mostly superseded by `.command(name, parser, handler)`.
|
|
567
|
+
|
|
568
|
+
## Dependencies
|
|
569
|
+
|
|
570
|
+
**bargs** has zero (0) dependencies. Only Node.js v22+.
|
|
571
|
+
|
|
566
572
|
## Motivation
|
|
567
573
|
|
|
568
574
|
I've always reached for [yargs](https://github.com/yargs/yargs) in my CLI projects. However, I find myself repeatedly doing the same things; I have a sort of boilerplate in my head, ready to go (`requiresArg: true` and `nargs: 1`, amirite?). I don't want boilerplate in my head. I wanted to distill my chosen subset of yargs' behavior into a composable API. And so **bargs** was begat.
|