@boneskull/bargs 2.0.0 → 3.0.1
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 +306 -298
- package/dist/bargs.cjs +465 -135
- package/dist/bargs.cjs.map +1 -1
- package/dist/bargs.d.cts +36 -17
- package/dist/bargs.d.cts.map +1 -1
- package/dist/bargs.d.ts +36 -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 +21 -3
- package/dist/help.d.cts.map +1 -1
- package/dist/help.d.ts +21 -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 +18 -79
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +18 -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 +85 -113
- package/dist/opt.d.cts.map +1 -1
- package/dist/opt.d.ts +85 -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/theme.cjs.map +1 -1
- package/dist/theme.d.cts +17 -1
- package/dist/theme.d.cts.map +1 -1
- package/dist/theme.d.ts +17 -1
- package/dist/theme.d.ts.map +1 -1
- package/dist/theme.js.map +1 -1
- package/dist/types.cjs +1 -3
- package/dist/types.cjs.map +1 -1
- package/dist/types.d.cts +128 -234
- package/dist/types.d.cts.map +1 -1
- package/dist/types.d.ts +128 -234
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +1 -3
- package/dist/types.js.map +1 -1
- package/package.json +6 -3
- 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,255 @@ 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)
|
|
269
221
|
|
|
270
|
-
|
|
222
|
+
> Or `.defaultCommand(parser, handler)`
|
|
271
223
|
|
|
272
|
-
|
|
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)`) |
|
|
224
|
+
Set the command that runs when no command is specified.
|
|
284
225
|
|
|
285
226
|
```typescript
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
});
|
|
227
|
+
// Reference an existing command by name
|
|
228
|
+
.defaultCommand('list')
|
|
229
|
+
|
|
230
|
+
// Or define an inline default
|
|
231
|
+
.defaultCommand(
|
|
232
|
+
pos.positionals(pos.string({ name: 'file' })),
|
|
233
|
+
({ positionals }) => console.log(positionals[0]),
|
|
234
|
+
)
|
|
295
235
|
```
|
|
296
236
|
|
|
297
|
-
###
|
|
237
|
+
### .parse(args?) / .parseAsync(args?)
|
|
298
238
|
|
|
299
|
-
|
|
239
|
+
Parse arguments and execute handlers.
|
|
300
240
|
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
| `theme` | `ThemeInput` | `--help` Color theme (see [Theming](#theming)) |
|
|
241
|
+
- **`.parse()`** - Synchronous. Throws if any transform or handler returns a Promise.
|
|
242
|
+
- **`.parseAsync()`** - Asynchronous. Supports async transforms and handlers.
|
|
304
243
|
|
|
305
244
|
```typescript
|
|
306
|
-
|
|
245
|
+
// Async (supports async transforms/handlers)
|
|
246
|
+
const result = await bargs.create('my-cli').globals(...).parseAsync();
|
|
247
|
+
console.log(result.values, result.positionals, result.command);
|
|
248
|
+
|
|
249
|
+
// Sync (no async transforms/handlers)
|
|
250
|
+
const result = bargs.create('my-cli').globals(...).parse();
|
|
307
251
|
```
|
|
308
252
|
|
|
309
253
|
## Option Helpers
|
|
310
254
|
|
|
311
255
|
```typescript
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
256
|
+
import { opt } from '@boneskull/bargs';
|
|
257
|
+
|
|
258
|
+
opt.string({ default: 'value' }); // --name value
|
|
259
|
+
opt.number({ default: 42 }); // --count 42
|
|
260
|
+
opt.boolean({ aliases: ['v'] }); // --verbose, -v
|
|
261
|
+
opt.enum(['a', 'b', 'c']); // --level a
|
|
262
|
+
opt.array('string'); // --file x --file y
|
|
263
|
+
opt.count(); // -vvv → 3
|
|
318
264
|
```
|
|
319
265
|
|
|
320
266
|
### Option Properties
|
|
321
267
|
|
|
322
|
-
All option helpers accept these properties:
|
|
323
|
-
|
|
324
268
|
| Property | Type | Description |
|
|
325
269
|
| ------------- | ---------- | ------------------------------------------------ |
|
|
326
270
|
| `aliases` | `string[]` | Short flags (e.g., `['v']` for `-v`) |
|
|
@@ -330,27 +274,27 @@ All option helpers accept these properties:
|
|
|
330
274
|
| `hidden` | `boolean` | Hide from `--help` output |
|
|
331
275
|
| `required` | `boolean` | Mark as required (makes the option non-nullable) |
|
|
332
276
|
|
|
333
|
-
|
|
277
|
+
### `opt.options(schema)`
|
|
278
|
+
|
|
279
|
+
Create a parser from an options schema:
|
|
334
280
|
|
|
335
281
|
```typescript
|
|
336
|
-
|
|
337
|
-
aliases: ['
|
|
338
|
-
default: '
|
|
339
|
-
description: 'Output file path',
|
|
340
|
-
group: 'Output Options',
|
|
282
|
+
const parser = opt.options({
|
|
283
|
+
verbose: opt.boolean({ aliases: ['v'] }),
|
|
284
|
+
output: opt.string({ default: 'out.txt' }),
|
|
341
285
|
});
|
|
342
|
-
|
|
343
|
-
// Hidden options won't appear in help
|
|
344
|
-
bargs.boolean({ hidden: true });
|
|
286
|
+
// Type: Parser<{ verbose: boolean | undefined, output: string }, []>
|
|
345
287
|
```
|
|
346
288
|
|
|
347
289
|
## Positional Helpers
|
|
348
290
|
|
|
349
291
|
```typescript
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
292
|
+
import { pos } from '@boneskull/bargs';
|
|
293
|
+
|
|
294
|
+
pos.string({ required: true }); // <file>
|
|
295
|
+
pos.number({ default: 8080 }); // [port]
|
|
296
|
+
pos.enum(['dev', 'prod']); // [env]
|
|
297
|
+
pos.variadic('string'); // [files...]
|
|
354
298
|
```
|
|
355
299
|
|
|
356
300
|
### Positional Properties
|
|
@@ -362,41 +306,116 @@ bargs.variadic('string'); // [files...]
|
|
|
362
306
|
| `name` | `string` | Display name in help (defaults to `arg0`, `arg1`, ...) |
|
|
363
307
|
| `required` | `boolean` | Mark as required (shown as `<name>` vs `[name]`) |
|
|
364
308
|
|
|
365
|
-
|
|
309
|
+
### `pos.positionals(...defs)`
|
|
310
|
+
|
|
311
|
+
Create a parser from positional definitions:
|
|
366
312
|
|
|
367
313
|
```typescript
|
|
368
|
-
|
|
369
|
-
name: '
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
}
|
|
314
|
+
const parser = pos.positionals(
|
|
315
|
+
pos.string({ name: 'source', required: true }),
|
|
316
|
+
pos.string({ name: 'dest', required: true }),
|
|
317
|
+
);
|
|
318
|
+
// Type: Parser<{}, [string, string]>
|
|
373
319
|
```
|
|
374
320
|
|
|
375
|
-
|
|
321
|
+
Use `variadic` for rest arguments (must be last):
|
|
376
322
|
|
|
377
323
|
```typescript
|
|
378
|
-
const
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
324
|
+
const parser = pos.positionals(pos.variadic('string', { name: 'files' }));
|
|
325
|
+
// Type: Parser<{}, [string[]]>
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
## Merging Parsers
|
|
329
|
+
|
|
330
|
+
Use `merge()` to combine multiple parsers into one:
|
|
331
|
+
|
|
332
|
+
```typescript
|
|
333
|
+
import { merge, opt, pos } from '@boneskull/bargs';
|
|
385
334
|
|
|
386
|
-
const
|
|
387
|
-
|
|
335
|
+
const combined = merge(
|
|
336
|
+
opt.options({
|
|
337
|
+
priority: opt.enum(['low', 'medium', 'high'], { default: 'medium' }),
|
|
338
|
+
}),
|
|
339
|
+
pos.positionals(pos.string({ name: 'task', required: true })),
|
|
340
|
+
);
|
|
341
|
+
// Type: Parser<{ priority: 'low' | 'medium' | 'high' }, [string]>
|
|
388
342
|
```
|
|
389
343
|
|
|
390
|
-
|
|
344
|
+
You can merge as many parsers as needed—options are merged (later overrides earlier), and positionals are concatenated.
|
|
345
|
+
|
|
346
|
+
Alternatively, parsers can be merged by calling one with the other:
|
|
391
347
|
|
|
392
348
|
```typescript
|
|
393
|
-
const
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
349
|
+
const options = opt.options({ priority: opt.enum(['low', 'medium', 'high']) });
|
|
350
|
+
const positionals = pos.positionals(
|
|
351
|
+
pos.string({ name: 'task', required: true }),
|
|
352
|
+
);
|
|
353
|
+
|
|
354
|
+
// These are equivalent:
|
|
355
|
+
const combined1 = positionals(options);
|
|
356
|
+
const combined2 = options(positionals);
|
|
357
|
+
```
|
|
397
358
|
|
|
398
|
-
|
|
399
|
-
|
|
359
|
+
Use whichever style you find more readable.
|
|
360
|
+
|
|
361
|
+
## Transforms
|
|
362
|
+
|
|
363
|
+
Use `map()` to transform parsed values before they reach your handler:
|
|
364
|
+
|
|
365
|
+
```typescript
|
|
366
|
+
import { bargs, map, opt } from '@boneskull/bargs';
|
|
367
|
+
|
|
368
|
+
const globals = map(
|
|
369
|
+
opt.options({
|
|
370
|
+
config: opt.string(),
|
|
371
|
+
verbose: opt.boolean({ default: false }),
|
|
372
|
+
}),
|
|
373
|
+
({ values, positionals }) => ({
|
|
374
|
+
positionals,
|
|
375
|
+
values: {
|
|
376
|
+
...values,
|
|
377
|
+
// Add computed properties
|
|
378
|
+
timestamp: new Date().toISOString(),
|
|
379
|
+
configLoaded: !!values.config,
|
|
380
|
+
},
|
|
381
|
+
}),
|
|
382
|
+
);
|
|
383
|
+
|
|
384
|
+
await bargs
|
|
385
|
+
.create('my-cli')
|
|
386
|
+
.globals(globals)
|
|
387
|
+
.command(
|
|
388
|
+
'info',
|
|
389
|
+
opt.options({}),
|
|
390
|
+
({ values }) => {
|
|
391
|
+
// values.timestamp and values.configLoaded are available
|
|
392
|
+
console.log(values.timestamp);
|
|
393
|
+
},
|
|
394
|
+
'Show info',
|
|
395
|
+
)
|
|
396
|
+
.parseAsync();
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
Transforms are fully type-safe—the return type becomes the type available in handlers.
|
|
400
|
+
|
|
401
|
+
### Async Transforms
|
|
402
|
+
|
|
403
|
+
Transforms can be async:
|
|
404
|
+
|
|
405
|
+
```typescript
|
|
406
|
+
const globals = map(
|
|
407
|
+
opt.options({ url: opt.string({ required: true }) }),
|
|
408
|
+
async ({ values, positionals }) => {
|
|
409
|
+
const response = await fetch(values.url);
|
|
410
|
+
return {
|
|
411
|
+
positionals,
|
|
412
|
+
values: {
|
|
413
|
+
...values,
|
|
414
|
+
data: await response.json(),
|
|
415
|
+
},
|
|
416
|
+
};
|
|
417
|
+
},
|
|
418
|
+
);
|
|
400
419
|
```
|
|
401
420
|
|
|
402
421
|
## Epilog
|
|
@@ -405,16 +424,12 @@ By default, **bargs** displays your package's homepage and repository URLs (from
|
|
|
405
424
|
|
|
406
425
|
```typescript
|
|
407
426
|
// Custom epilog
|
|
408
|
-
bargs({
|
|
409
|
-
name: 'my-cli',
|
|
427
|
+
bargs.create('my-cli', {
|
|
410
428
|
epilog: 'For more info, visit https://example.com',
|
|
411
429
|
});
|
|
412
430
|
|
|
413
431
|
// Disable epilog entirely
|
|
414
|
-
bargs({
|
|
415
|
-
name: 'my-cli',
|
|
416
|
-
epilog: false,
|
|
417
|
-
});
|
|
432
|
+
bargs.create('my-cli', { epilog: false });
|
|
418
433
|
```
|
|
419
434
|
|
|
420
435
|
## Theming
|
|
@@ -423,38 +438,23 @@ Customize help output colors with built-in themes or your own:
|
|
|
423
438
|
|
|
424
439
|
```typescript
|
|
425
440
|
// 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
|
-
);
|
|
441
|
+
bargs.create('my-cli', { theme: 'ocean' });
|
|
433
442
|
|
|
434
443
|
// Disable colors entirely
|
|
435
|
-
bargs(
|
|
444
|
+
bargs.create('my-cli', { theme: 'mono' });
|
|
436
445
|
```
|
|
437
446
|
|
|
438
|
-
The `ansi` export provides common ANSI escape codes for styled terminal output:
|
|
447
|
+
The `ansi` export provides common ANSI escape codes for styled terminal output:
|
|
439
448
|
|
|
440
449
|
```typescript
|
|
441
450
|
import { ansi } from '@boneskull/bargs';
|
|
442
451
|
|
|
443
|
-
bargs(
|
|
452
|
+
bargs.create('my-cli', {
|
|
444
453
|
theme: {
|
|
445
454
|
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
455
|
flag: ansi.brightCyan,
|
|
452
456
|
positional: ansi.magenta,
|
|
453
|
-
|
|
454
|
-
sectionHeader: ansi.brightMagenta,
|
|
455
|
-
type: ansi.magenta,
|
|
456
|
-
url: ansi.cyan,
|
|
457
|
-
usage: ansi.cyan,
|
|
457
|
+
// ...
|
|
458
458
|
},
|
|
459
459
|
});
|
|
460
460
|
```
|
|
@@ -495,14 +495,16 @@ import {
|
|
|
495
495
|
} from '@boneskull/bargs';
|
|
496
496
|
|
|
497
497
|
try {
|
|
498
|
-
bargs(
|
|
498
|
+
await bargs.create('my-cli').parseAsync();
|
|
499
499
|
} catch (error) {
|
|
500
500
|
if (error instanceof ValidationError) {
|
|
501
501
|
// Config validation failed (e.g., invalid schema)
|
|
502
|
+
// i.e., "you screwed up"
|
|
502
503
|
console.error(`Config error at "${error.path}": ${error.message}`);
|
|
503
504
|
} else if (error instanceof HelpError) {
|
|
504
|
-
//
|
|
505
|
-
|
|
505
|
+
// Likely invalid options, command or positionals;
|
|
506
|
+
// re-throw to trigger help display
|
|
507
|
+
throw error;
|
|
506
508
|
} else if (error instanceof BargsError) {
|
|
507
509
|
// General bargs error
|
|
508
510
|
console.error(error.message);
|
|
@@ -512,11 +514,12 @@ try {
|
|
|
512
514
|
|
|
513
515
|
### Programmatic Help
|
|
514
516
|
|
|
515
|
-
Generate help text
|
|
517
|
+
Generate help text programmatically:
|
|
516
518
|
|
|
517
519
|
```typescript
|
|
518
520
|
import { generateHelp, generateCommandHelp } from '@boneskull/bargs';
|
|
519
521
|
|
|
522
|
+
// These require the internal config structure—see source for details
|
|
520
523
|
const helpText = generateHelp(config);
|
|
521
524
|
const commandHelp = generateCommandHelp(config, 'migrate');
|
|
522
525
|
```
|
|
@@ -558,11 +561,16 @@ console.log(styler.flag('--verbose'));
|
|
|
558
561
|
|
|
559
562
|
// Strip ANSI codes for plain text output
|
|
560
563
|
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
564
|
```
|
|
565
565
|
|
|
566
|
+
### Low-Level Utilities
|
|
567
|
+
|
|
568
|
+
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)`.
|
|
569
|
+
|
|
570
|
+
## Dependencies
|
|
571
|
+
|
|
572
|
+
**bargs** has zero (0) dependencies. Only Node.js v22+.
|
|
573
|
+
|
|
566
574
|
## Motivation
|
|
567
575
|
|
|
568
576
|
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.
|