UNPKG

18.4 kBMarkdownView Raw
1A light, featureful and explicit option parsing library for node.js.
2
3[Why another one? See below](#why). tl;dr: The others I've tried are one of
4too loosey goosey (not explicit), too big/too many deps, or ill specified.
5YMMV.
6
7
8# Install
9
10 npm install dashdash
11
12
13# Active Versions
14
15- [2.x](https://github.com/trentm/node-dashdash/tree/master) - Version 2.x
16 dropped support for node versions earlier than node 10.x. While I don't
17 expect to actively break functionality, new changes are no longer tested
18 on earlier versions.
19- [1.x](https://github.com/trentm/node-dashdash/tree/1.x) - This version
20 supports back to node version 0.10.x.
21
22
23# Usage
24
25```javascript
26var dashdash = require('dashdash');
27
28// Specify the options. Minimally `name` (or `names`) and `type`
29// must be given for each.
30var options = [
31 {
32 // `names` or a single `name`. First element is the `opts.KEY`.
33 names: ['help', 'h'],
34 // See "Option specs" below for types.
35 type: 'bool',
36 help: 'Print this help and exit.'
37 }
38];
39
40// Shortcut form. As called it infers `process.argv`. See below for
41// the longer form to use methods like `.help()` on the Parser object.
42var opts = dashdash.parse({options: options});
43
44console.log("opts:", opts);
45console.log("args:", opts._args);
46```
47
48
49# Longer Example
50
51A more realistic [starter script "foo.js"](./examples/foo.js) is as follows.
52This also shows using `parser.help()` for formatted option help.
53
54```javascript
55var dashdash = require('dashdash');
56
57var options = [
58 {
59 name: 'version',
60 type: 'bool',
61 help: 'Print tool version and exit.'
62 },
63 {
64 names: ['help', 'h'],
65 type: 'bool',
66 help: 'Print this help and exit.'
67 },
68 {
69 names: ['verbose', 'v'],
70 type: 'arrayOfBool',
71 help: 'Verbose output. Use multiple times for more verbose.'
72 },
73 {
74 names: ['file', 'f'],
75 type: 'string',
76 help: 'File to process',
77 helpArg: 'FILE'
78 }
79];
80
81var parser = dashdash.createParser({options: options});
82try {
83 var opts = parser.parse(process.argv);
84} catch (e) {
85 console.error('foo: error: %s', e.message);
86 process.exit(1);
87}
88
89console.log("# opts:", opts);
90console.log("# args:", opts._args);
91
92// Use `parser.help()` for formatted options help.
93if (opts.help) {
94 var help = parser.help({includeEnv: true}).trimRight();
95 console.log('usage: node foo.js [OPTIONS]\n'
96 + 'options:\n'
97 + help);
98 process.exit(0);
99}
100
101// ...
102```
103
104
105Some example output from this script (foo.js):
106
107```
108$ node foo.js -h
109# opts: { help: true,
110 _order: [ { name: 'help', value: true, from: 'argv' } ],
111 _args: [] }
112# args: []
113usage: node foo.js [OPTIONS]
114options:
115 --version Print tool version and exit.
116 -h, --help Print this help and exit.
117 -v, --verbose Verbose output. Use multiple times for more verbose.
118 -f FILE, --file=FILE File to process
119
120$ node foo.js -v
121# opts: { verbose: [ true ],
122 _order: [ { name: 'verbose', value: true, from: 'argv' } ],
123 _args: [] }
124# args: []
125
126$ node foo.js --version arg1
127# opts: { version: true,
128 _order: [ { name: 'version', value: true, from: 'argv' } ],
129 _args: [ 'arg1' ] }
130# args: [ 'arg1' ]
131
132$ node foo.js -f bar.txt
133# opts: { file: 'bar.txt',
134 _order: [ { name: 'file', value: 'bar.txt', from: 'argv' } ],
135 _args: [] }
136# args: []
137
138$ node foo.js -vvv --file=blah
139# opts: { verbose: [ true, true, true ],
140 file: 'blah',
141 _order:
142 [ { name: 'verbose', value: true, from: 'argv' },
143 { name: 'verbose', value: true, from: 'argv' },
144 { name: 'verbose', value: true, from: 'argv' },
145 { name: 'file', value: 'blah', from: 'argv' } ],
146 _args: [] }
147# args: []
148```
149
150
151See the ["examples"](examples/) dir for a number of starter examples using
152some of dashdash's features.
153
154
155# Environment variable integration
156
157If you want to allow environment variables to specify options to your tool,
158dashdash makes this easy. We can change the 'verbose' option in the example
159above to include an 'env' field:
160
161```javascript
162 {
163 names: ['verbose', 'v'],
164 type: 'arrayOfBool',
165 env: 'FOO_VERBOSE', // <--- add this line
166 help: 'Verbose output. Use multiple times for more verbose.'
167 },
168```
169
170then the **"FOO_VERBOSE" environment variable** can be used to set this
171option:
172
173```shell
174$ FOO_VERBOSE=1 node foo.js
175# opts: { verbose: [ true ],
176 _order: [ { name: 'verbose', value: true, from: 'env' } ],
177 _args: [] }
178# args: []
179```
180
181Boolean options will interpret the empty string as unset, '0' as false
182and anything else as true.
183
184```shell
185$ FOO_VERBOSE= node examples/foo.js # not set
186# opts: { _order: [], _args: [] }
187# args: []
188
189$ FOO_VERBOSE=0 node examples/foo.js # '0' is false
190# opts: { verbose: [ false ],
191 _order: [ { key: 'verbose', value: false, from: 'env' } ],
192 _args: [] }
193# args: []
194
195$ FOO_VERBOSE=1 node examples/foo.js # true
196# opts: { verbose: [ true ],
197 _order: [ { key: 'verbose', value: true, from: 'env' } ],
198 _args: [] }
199# args: []
200
201$ FOO_VERBOSE=boogabooga node examples/foo.js # true
202# opts: { verbose: [ true ],
203 _order: [ { key: 'verbose', value: true, from: 'env' } ],
204 _args: [] }
205# args: []
206```
207
208Non-booleans can be used as well. Strings:
209
210```shell
211$ FOO_FILE=data.txt node examples/foo.js
212# opts: { file: 'data.txt',
213 _order: [ { key: 'file', value: 'data.txt', from: 'env' } ],
214 _args: [] }
215# args: []
216```
217
218Numbers:
219
220```shell
221$ FOO_TIMEOUT=5000 node examples/foo.js
222# opts: { timeout: 5000,
223 _order: [ { key: 'timeout', value: 5000, from: 'env' } ],
224 _args: [] }
225# args: []
226
227$ FOO_TIMEOUT=blarg node examples/foo.js
228foo: error: arg for "FOO_TIMEOUT" is not a positive integer: "blarg"
229```
230
231With the `includeEnv: true` config to `parser.help()` the environment
232variable can also be included in **help output**:
233
234 usage: node foo.js [OPTIONS]
235 options:
236 --version Print tool version and exit.
237 -h, --help Print this help and exit.
238 -v, --verbose Verbose output. Use multiple times for more verbose.
239 Environment: FOO_VERBOSE=1
240 -f FILE, --file=FILE File to process
241
242
243# Bash completion
244
245Dashdash provides a simple way to create a Bash completion file that you
246can place in your "bash_completion.d" directory -- sometimes that is
247"/usr/local/etc/bash_completion.d/"). Features:
248
249- Support for short and long opts
250- Support for knowing which options take arguments
251- Support for subcommands (e.g. 'git log <TAB>' to show just options for the
252 log subcommand). See
253 [node-cmdln](https://github.com/trentm/node-cmdln#bash-completion) for
254 how to integrate that.
255- Does the right thing with "--" to stop options.
256- Custom optarg and arg types for custom completions.
257
258Dashdash will return bash completion file content given a parser instance:
259
260 var parser = dashdash.createParser({options: options});
261 console.log( parser.bashCompletion({name: 'mycli'}) );
262
263or directly from a `options` array of options specs:
264
265 var code = dashdash.bashCompletionFromOptions({
266 name: 'mycli',
267 options: OPTIONS
268 });
269
270Write that content to "/usr/local/etc/bash_completion.d/mycli" and you will
271have Bash completions for `mycli`. Alternatively you can write it to
272any file (e.g. "~/.bashrc") and source it.
273
274You could add a `--completion` hidden option to your tool that emits the
275completion content and document for your users to call that to install
276Bash completions.
277
278See [examples/ddcompletion.js](examples/ddcompletion.js) for a complete
279example, including how one can define bash functions for completion of custom
280option types. Also see [node-cmdln](https://github.com/trentm/node-cmdln) for
281how it uses this for Bash completion for full multi-subcommand tools.
282
283- TODO: document specExtra
284- TODO: document includeHidden
285- TODO: document custom types, `function complete\_FOO` guide, completionType
286- TODO: document argtypes
287
288
289# Parser config
290
291Parser construction (i.e. `dashdash.createParser(CONFIG)`) takes the
292following fields:
293
294- `options` (Array of option specs). Required. See the
295 [Option specs](#option-specs) section below.
296
297- `interspersed` (Boolean). Optional. Default is true. If true this allows
298 interspersed arguments and options. I.e.:
299
300 node ./tool.js -v arg1 arg2 -h # '-h' is after interspersed args
301
302 Set it to false to have '-h' **not** get parsed as an option in the above
303 example.
304
305- `allowUnknown` (Boolean). Optional. Default is false. If false, this causes
306 unknown arguments to throw an error. I.e.:
307
308 node ./tool.js -v arg1 --afe8asefksjefhas
309
310 Set it to true to treat the unknown option as a positional
311 argument.
312
313 **Caveat**: When a shortopt group, such as `-xaz` contains a mix of
314 known and unknown options, the *entire* group is passed through
315 unmolested as a positional argument.
316
317 Consider if you have a known short option `-a`, and parse the
318 following command line:
319
320 node ./tool.js -xaz
321
322 where `-x` and `-z` are unknown. There are multiple ways to
323 interpret this:
324
325 1. `-x` takes a value: `{x: 'az'}`
326 2. `-x` and `-z` are both booleans: `{x:true,a:true,z:true}`
327
328 Since dashdash does not know what `-x` and `-z` are, it can't know
329 if you'd prefer to receive `{a:true,_args:['-x','-z']}` or
330 `{x:'az'}`, or `{_args:['-xaz']}`. Leaving the positional arg unprocessed
331 is the easiest mistake for the user to recover from.
332
333
334# Option specs
335
336Example using all fields (required fields are noted):
337
338```javascript
339{
340 names: ['file', 'f'], // Required (one of `names` or `name`).
341 type: 'string', // Required.
342 completionType: 'filename',
343 env: 'MYTOOL_FILE',
344 help: 'Config file to load before running "mytool"',
345 helpArg: 'PATH',
346 helpWrap: false,
347 default: path.resolve(process.env.HOME, '.mytoolrc')
348}
349```
350
351Each option spec in the `options` array must/can have the following fields:
352
353- `name` (String) or `names` (Array). Required. These give the option name
354 and aliases. The first name (if more than one given) is the key for the
355 parsed `opts` object.
356
357- `type` (String). Required. One of:
358
359 - bool
360 - string
361 - number
362 - integer
363 - positiveInteger
364 - date (epoch seconds, e.g. 1396031701, or ISO 8601 format
365 `YYYY-MM-DD[THH:MM:SS[.sss][Z]]`, e.g. "2014-03-28T18:35:01.489Z")
366 - arrayOfBool
367 - arrayOfString
368 - arrayOfNumber
369 - arrayOfInteger
370 - arrayOfPositiveInteger
371 - arrayOfDate
372
373 FWIW, these names attempt to match with asserts on
374 [assert-plus](https://github.com/mcavage/node-assert-plus).
375 You can add your own custom option types with `dashdash.addOptionType`.
376 See below.
377
378- `completionType` (String). Optional. This is used for [Bash
379 completion](#bash-completion) for an option argument. If not specified,
380 then the value of `type` is used. Any string may be specified, but only the
381 following values have meaning:
382
383 - `none`: Provide no completions.
384 - `file`: Bash's default completion (i.e. `complete -o default`), which
385 includes filenames.
386 - *Any string FOO for which a `function complete_FOO` Bash function is
387 defined.* This is for custom completions for a given tool. Typically
388 these custom functions are provided in the `specExtra` argument to
389 `dashdash.bashCompletionFromOptions()`. See
390 ["examples/ddcompletion.js"](examples/ddcompletion.js) for an example.
391
392- `env` (String or Array of String). Optional. An environment variable name
393 (or names) that can be used as a fallback for this option. For example,
394 given a "foo.js" like this:
395
396 var options = [{names: ['dry-run', 'n'], env: 'FOO_DRY_RUN'}];
397 var opts = dashdash.parse({options: options});
398
399 Both `node foo.js --dry-run` and `FOO_DRY_RUN=1 node foo.js` would result
400 in `opts.dry_run = true`.
401
402 An environment variable is only used as a fallback, i.e. it is ignored if
403 the associated option is given in `argv`.
404
405- `help` (String). Optional. Used for `parser.help()` output.
406
407- `helpArg` (String). Optional. Used in help output as the placeholder for
408 the option argument, e.g. the "PATH" in:
409
410 ...
411 -f PATH, --file=PATH File to process
412 ...
413
414- `helpWrap` (Boolean). Optional, default true. Set this to `false` to have
415 that option's `help` *not* be text wrapped in `<parser>.help()` output.
416
417- `default`. Optional. A default value used for this option, if the
418 option isn't specified in argv.
419
420- `hidden` (Boolean). Optional, default false. If true, help output will not
421 include this option. See also the `includeHidden` option to
422 `bashCompletionFromOptions()` for [Bash completion](#bash-completion).
423
424
425# Option group headings
426
427You can add headings between option specs in the `options` array. To do so,
428simply add an object with only a `group` property -- the string to print as
429the heading for the subsequent options in the array. For example:
430
431```javascript
432var options = [
433 {
434 group: 'Armament Options'
435 },
436 {
437 names: [ 'weapon', 'w' ],
438 type: 'string'
439 },
440 {
441 group: 'General Options'
442 },
443 {
444 names: [ 'help', 'h' ],
445 type: 'bool'
446 }
447];
448...
449```
450
451Note: You can use an empty string, `{group: ''}`, to get a blank line in help
452output between groups of options.
453
454
455# Help config
456
457The `parser.help(...)` function is configurable as follows:
458
459 Options:
460 Armament Options:
461 ^^ -w WEAPON, --weapon=WEAPON Weapon with which to crush. One of: |
462 / sword, spear, maul |
463 / General Options: |
464 / -h, --help Print this help and exit. |
465 / ^^^^ ^ |
466 \ `-- indent `-- helpCol maxCol ---'
467 `-- headingIndent
468
469- `indent` (Number or String). Default 4. Set to a number (for that many
470 spaces) or a string for the literal indent.
471- `headingIndent` (Number or String). Default half length of `indent`. Set to
472 a number (for that many spaces) or a string for the literal indent. This
473 indent applies to group heading lines, between normal option lines.
474- `nameSort` (String). Default is 'length'. By default the names are
475 sorted to put the short opts first (i.e. '-h, --help' preferred
476 to '--help, -h'). Set to 'none' to not do this sorting.
477- `maxCol` (Number). Default 80. Note that reflow is just done on whitespace
478 so a long token in the option help can overflow maxCol.
479- `helpCol` (Number). If not set a reasonable value will be determined
480 between `minHelpCol` and `maxHelpCol`.
481- `minHelpCol` (Number). Default 20.
482- `maxHelpCol` (Number). Default 40.
483- `helpWrap` (Boolean). Default true. Set to `false` to have option `help`
484 strings *not* be textwrapped to the helpCol..maxCol range.
485- `includeEnv` (Boolean). Default false. If the option has associated
486 environment variables (via the `env` option spec attribute), then
487 append mentioned of those envvars to the help string.
488- `includeDefault` (Boolean). Default false. If the option has a default value
489 (via the `default` option spec attribute, or a default on the option's type),
490 then a "Default: VALUE" string will be appended to the help string.
491
492
493# Custom option types
494
495Dashdash includes a good starter set of option types that it will parse for
496you. However, you can add your own via:
497
498 var dashdash = require('dashdash');
499 dashdash.addOptionType({
500 name: '...',
501 takesArg: true,
502 helpArg: '...',
503 parseArg: function (option, optstr, arg) {
504 ...
505 },
506 array: false, // optional
507 arrayFlatten: false, // optional
508 default: ..., // optional
509 completionType: ... // optional
510 });
511
512For example, a simple option type that accepts 'yes', 'y', 'no' or 'n' as
513a boolean argument would look like:
514
515 var dashdash = require('dashdash');
516
517 function parseYesNo(option, optstr, arg) {
518 var argLower = arg.toLowerCase()
519 if (~['yes', 'y'].indexOf(argLower)) {
520 return true;
521 } else if (~['no', 'n'].indexOf(argLower)) {
522 return false;
523 } else {
524 throw new Error(format(
525 'arg for "%s" is not "yes" or "no": "%s"',
526 optstr, arg));
527 }
528 }
529
530 dashdash.addOptionType({
531 name: 'yesno'
532 takesArg: true,
533 helpArg: '<yes|no>',
534 parseArg: parseYesNo
535 });
536
537 var options = {
538 {names: ['answer', 'a'], type: 'yesno'}
539 };
540 var opts = dashdash.parse({options: options});
541
542See "examples/custom-option-\*.js" for other examples.
543See the `addOptionType` block comment in "lib/dashdash.js" for more details.
544Please let me know [with an
545issue](https://github.com/trentm/node-dashdash/issues/new) if you write a
546generally useful one.
547
548
549
550# Why
551
552Why another node.js option parsing lib?
553
554- `nopt` really is just for "tools like npm". Implicit opts (e.g. '--no-foo'
555 works for every '--foo'). Can't disable abbreviated opts. Can't do multiple
556 usages of same opt, e.g. '-vvv' (I think). Can't do grouped short opts.
557
558- `optimist` has surprise interpretation of options (at least to me).
559 Implicit opts mean ambiguities and poor error handling for fat-fingering.
560 `process.exit` calls makes it hard to use as a library.
561
562- `optparse` Incomplete docs. Is this an attempted clone of Python's `optparse`.
563 Not clear. Some divergence. `parser.on("name", ...)` API is weird.
564
565- `argparse` Dep on underscore. No thanks just for option processing.
566 `find lib | wc -l` -> `26`. Overkill.
567 Argparse is a bit different anyway. Not sure I want that.
568
569- `posix-getopt` No type validation. Though that isn't a killer. AFAIK can't
570 have a long opt without a short alias. I.e. no `getopt_long` semantics.
571 Also, no whizbang features like generated help output.
572
573- ["commander.js"](https://github.com/visionmedia/commander.js): I wrote
574 [a critique](http://trentm.com/2014/01/a-critique-of-commander-for-nodejs.html)
575 a while back. It seems fine, but last I checked had
576 [an outstanding bug](https://github.com/visionmedia/commander.js/pull/121)
577 that would prevent me from using it.
578
579
580# License
581
582MIT. See LICENSE.txt.