yargs 3.28.0 → 3.32.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/CHANGELOG.md CHANGED
@@ -1,5 +1,34 @@
1
1
  ## Change Log
2
2
 
3
+ ### v3.32.0 (2016/1/14 10:13 +07:00)
4
+
5
+ - [#344](https://github.com/bcoe/yargs/pull/344) yargs now has a code of conduct and contributor guidelines (@bcoe)
6
+ - [#341](https://github.com/bcoe/yargs/issues/341) Fix edge-case with camel-case arguments (@davibe)
7
+ - [#331](https://github.com/bcoe/yargs/pull/331) Handle parsing a raw argument string (@kellyselden)
8
+ - [#325](https://github.com/bcoe/yargs/pull/325) Tweaks to make tests pass again on Windows (@isaacs)
9
+ - [#321](https://github.com/bcoe/yargs/pull/321) Custom config parsing function (@bcoe)
10
+
11
+ ### v3.31.0 (2015/12/03 10:15 +07:00)
12
+
13
+ - [#239](https://github.com/bcoe/yargs/pull/239) Pass argv to commands (@bcoe)
14
+ - [#308](https://github.com/bcoe/yargs/pull/308) Yargs now handles environment variables (@nexdrew)
15
+ - [#302](https://github.com/bcoe/yargs/pull/302) Add Indonesian translation (@rilut)
16
+ - [#300](https://github.com/bcoe/yargs/pull/300) Add Turkish translation (@feyzo)
17
+ - [#298](https://github.com/bcoe/yargs/pull/298) Add Norwegian Bokmål translation (@sindresorhus)
18
+ - [#297](https://github.com/bcoe/yargs/pull/297) Fix for layout of cjk characters (@disjukr)
19
+ - [#296](https://github.com/bcoe/yargs/pull/296) Add Korean translation (@disjukr)
20
+
21
+ ### v3.30.0 (2015/11/13 16:29 +07:00)
22
+
23
+ - [#293](https://github.com/bcoe/yargs/pull/293) Polish language support (@kamilogorek)
24
+ - [#291](https://github.com/bcoe/yargs/pull/291) fix edge-cases with `.alias()` (@bcoe)
25
+ - [#289](https://github.com/bcoe/yargs/pull/289) group options in custom groups (@bcoe)
26
+
27
+ ### v3.29.0 (2015/10/16 21:51 +07:00)
28
+
29
+ - [#282](https://github.com/bcoe/yargs/pull/282) completions now accept promises (@LinusU)
30
+ - [#281](https://github.com/bcoe/yargs/pull/281) fix parsing issues with dot notation (@bcoe)
31
+
3
32
  ### v3.28.0 (2015/10/16 1:55 +07:00)
4
33
 
5
34
  - [#277](https://github.com/bcoe/yargs/pull/277) adds support for ansi escape codes (@bcoe)
package/README.md CHANGED
@@ -463,8 +463,14 @@ Hidden commands don't show up in the help output and aren't available for
463
463
  completion.
464
464
 
465
465
  Optionally, you can provide a handler `fn` which will be executed when
466
- a given command is provided. The handler will be executed with an instance
467
- of `yargs`, which can be used to compose nested commands.
466
+ a given command is provided. The handler will be called with `yargs` and
467
+ `argv` as arguments.
468
+
469
+ `yargs` is a blank instance of yargs, which can be used to compose a nested
470
+ hierarchy of options handlers.
471
+
472
+ `argv` represents the arguments parsed prior to the
473
+ command being executed (those described in the outer yargs instance).
468
474
 
469
475
  Here's an example of top-level and nested commands in action:
470
476
 
@@ -472,7 +478,7 @@ Here's an example of top-level and nested commands in action:
472
478
  var argv = require('yargs')
473
479
  .usage('npm <command>')
474
480
  .command('install', 'tis a mighty fine package to install')
475
- .command('publish', 'shiver me timbers, should you be sharing all that', function (yargs) {
481
+ .command('publish', 'shiver me timbers, should you be sharing all that', function (yargs, argv) {
476
482
  argv = yargs.option('f', {
477
483
  alias: 'force',
478
484
  description: 'yar, it usually be a bad idea'
@@ -514,7 +520,7 @@ var argv = require('yargs')
514
520
  .argv;
515
521
  ```
516
522
 
517
- But wait, there's more! You can provide asynchronous completions.
523
+ You can also provide asynchronous completions.
518
524
 
519
525
  ```js
520
526
  var argv = require('yargs')
@@ -529,15 +535,42 @@ var argv = require('yargs')
529
535
  .argv;
530
536
  ```
531
537
 
532
- <a name="config"></a>.config(key, [description])
538
+ But wait, there's more! You can return an asynchronous promise.
539
+
540
+ ```js
541
+ var argv = require('yargs')
542
+ .completion('completion', function(current, argv, done) {
543
+ return new Promise(function (resolve, reject) {
544
+ setTimeout(function () {
545
+ resolve(['apple', 'banana'])
546
+ }, 10)
547
+ })
548
+ })
549
+ .argv;
550
+ ```
551
+
552
+ <a name="config"></a>.config(key, [description], [parseFn])
533
553
  ------------
534
554
 
535
555
  Tells the parser that if the option specified by `key` is passed in, it
536
556
  should be interpreted as a path to a JSON config file. The file is loaded
537
- and parsed, and its properties are set as arguments. If present, the
538
- `description` parameter customizes the description of the config (`key`) option
557
+ and parsed, and its properties are set as arguments.
558
+
559
+ An optional `description` can be provided to customize the config (`key`) option
539
560
  in the usage string.
540
561
 
562
+ An optional `parseFn` can be used to provide a custom parser. The parsing
563
+ function must be synchronous, and should return an object containing
564
+ key value pairs or an error.
565
+
566
+ ```js
567
+ var argv = require('yargs')
568
+ .config('settings', function (configPath) {
569
+ return JSON.parse(fs.readFileSync(configPath, 'utf-8'))
570
+ })
571
+ .argv
572
+ ```
573
+
541
574
  <a name="count"></a>.count(key)
542
575
  ------------
543
576
 
@@ -600,6 +633,63 @@ Optionally `.describe()` can take an object that maps keys to descriptions.
600
633
 
601
634
  Should yargs attempt to detect the os' locale? Defaults to `true`.
602
635
 
636
+ .env([prefix])
637
+ --------------
638
+
639
+ Tell yargs to parse environment variables matching the given prefix and apply
640
+ them to argv as though they were command line arguments.
641
+
642
+ If this method is called with no argument or with an empty string or with `true`,
643
+ then all env vars will be applied to argv.
644
+
645
+ Program arguments are defined in this order of precedence:
646
+
647
+ 1. Command line args
648
+ 2. Config file
649
+ 3. Env var
650
+ 4. Configured defaults
651
+
652
+ ```js
653
+ var argv = require('yargs')
654
+ .env('MY_PROGRAM')
655
+ .option('f', {
656
+ alias: 'fruit-thing',
657
+ default: 'apple'
658
+ })
659
+ .argv
660
+ console.log(argv)
661
+ ```
662
+
663
+ ```
664
+ $ node fruity.js
665
+ { _: [],
666
+ f: 'apple',
667
+ 'fruit-thing': 'apple',
668
+ fruitThing: 'apple',
669
+ '$0': 'fruity.js' }
670
+ ```
671
+
672
+ ```
673
+ $ MY_PROGRAM_FRUIT_THING=banana node fruity.js
674
+ { _: [],
675
+ fruitThing: 'banana',
676
+ f: 'banana',
677
+ 'fruit-thing': 'banana',
678
+ '$0': 'fruity.js' }
679
+ ```
680
+
681
+ ```
682
+ $ MY_PROGRAM_FRUIT_THING=banana node fruity.js -f cat
683
+ { _: [],
684
+ f: 'cat',
685
+ 'fruit-thing': 'cat',
686
+ fruitThing: 'cat',
687
+ '$0': 'fruity.js' }
688
+ ```
689
+
690
+ Env var parsing is disabled by default, but you can also explicitly disable it
691
+ by calling `.env(false)`, e.g. if you need to undo previous configuration.
692
+
603
693
  .epilog(str)
604
694
  ------------
605
695
  .epilogue(str)
@@ -635,6 +725,27 @@ Method to execute when a failure occurs, rather than printing the failure messag
635
725
 
636
726
  `fn` is called with the failure message that would have been printed.
637
727
 
728
+ <a name="group"></a>.group(key(s), groupName)
729
+ --------------------
730
+
731
+ Given a key, or an array of keys, places options under an alternative heading
732
+ when displaying usage instructions, e.g.,
733
+
734
+ ```js
735
+ var yargs = require('yargs')(['--help'])
736
+ .help('help')
737
+ .group('batman', 'Heroes:')
738
+ .describe('batman', "world's greatest detective")
739
+ .wrap(null)
740
+ .argv
741
+ ```
742
+ ***
743
+ Heroes:
744
+ --batman world's greatest detective
745
+
746
+ Options:
747
+ --help Show help [boolean]
748
+
638
749
  .help([option, [description]])
639
750
  ------------------------------
640
751
 
@@ -719,16 +830,23 @@ Locales currently supported:
719
830
  * **en:** American English.
720
831
  * **es:** Spanish.
721
832
  * **fr:** French.
833
+ * **id:** Indonesian.
722
834
  * **ja:** Japanese.
835
+ * **ko:** Korean.
836
+ * **nb:** Norwegian Bokmål.
837
+ * **pirate:** American Pirate.
723
838
  * **pt:** Portuguese.
839
+ * **pt_BR:** Brazilian Portuguese.
840
+ * **tr:** Turkish.
724
841
  * **zh:** Chinese.
725
- * **pirate:** American Pirate.
726
842
 
727
843
  To submit a new translation for yargs:
728
844
 
729
845
  1. use `./locales/en.json` as a starting point.
730
846
  2. submit a pull request with the new locale file.
731
847
 
848
+ *The [Microsoft Terminology Search](http://www.microsoft.com/Language/en-US/Search.aspx) can be useful for finding the correct terminology in your locale.*
849
+
732
850
  <a name="nargs"></a>.nargs(key, count)
733
851
  -----------
734
852
 
@@ -807,11 +925,13 @@ Valid `opt` keys include:
807
925
  - `boolean`: boolean, interpret option as a boolean flag, see [`boolean()`](#boolean)
808
926
  - `choices`: value or array of values, limit valid option arguments to a predefined set, see [`choices()`](#choices)
809
927
  - `config`: boolean, interpret option as a path to a JSON config file, see [`config()`](#config)
928
+ - `configParser`: function, provide a custom config parsing function, see [`config()`](#config)
810
929
  - `count`: boolean, interpret option as a count of boolean flags, see [`count()`](#count)
811
930
  - `default`: value, set a default value for the option, see [`default()`](#default)
812
931
  - `defaultDescription`: string, use this description for the default value in help content, see [`default()`](#default)
813
932
  - `demand`/`require`/`required`: boolean or string, demand the option be given, with optional error message, see [`demand()`](#demand)
814
933
  - `desc`/`describe`/`description`: string, the option description for help content, see [`describe()`](#describe)
934
+ - `group`: string, when displaying usage instructions place the option under an alternative group heading, see [`group()`](#group)
815
935
  - `nargs`: number, specify how many arguments should be consumed for the option, see [`nargs()`](#nargs)
816
936
  - `requiresArg`: boolean, require the option be specified with a value, see [`requiresArg()`](#requiresArg)
817
937
  - `string`: boolean, interpret option as a string, see [`string()`](#string)
@@ -826,6 +946,8 @@ Valid `opt` keys include:
826
946
 
827
947
  Parse `args` instead of `process.argv`. Returns the `argv` object.
828
948
 
949
+ `args` may either be a pre-processed argv array, or a raw argument string.
950
+
829
951
  .require(key, [msg | boolean])
830
952
  ------------------------------
831
953
  .required(key, [msg | boolean])
@@ -1089,7 +1211,7 @@ Short numeric `-n5` style arguments work too:
1089
1211
  installation
1090
1212
  ============
1091
1213
 
1092
- With [npm](http://github.com/isaacs/npm), just do:
1214
+ With [npm](https://github.com/npm/npm), just do:
1093
1215
 
1094
1216
  npm install yargs
1095
1217
 
@@ -1115,7 +1237,7 @@ This module is loosely inspired by Perl's
1115
1237
  [gemnasium-image]: https://img.shields.io/gemnasium/bcoe/yargs.svg
1116
1238
  [coveralls-url]: https://coveralls.io/github/bcoe/yargs
1117
1239
  [coveralls-image]: https://img.shields.io/coveralls/bcoe/yargs.svg
1118
- [npm-url]: https://npmjs.org/package/yargs
1240
+ [npm-url]: https://www.npmjs.com/package/yargs
1119
1241
  [npm-image]: https://img.shields.io/npm/v/yargs.svg
1120
1242
  [windows-url]: https://ci.appveyor.com/project/bcoe/yargs
1121
1243
  [windows-image]: https://img.shields.io/appveyor/ci/bcoe/yargs/master.svg?label=Windows%20Tests
package/index.js CHANGED
@@ -2,6 +2,7 @@ var assert = require('assert')
2
2
  var Completion = require('./lib/completion')
3
3
  var Parser = require('./lib/parser')
4
4
  var path = require('path')
5
+ var tokenizeArgString = require('./lib/tokenize-arg-string')
5
6
  var Usage = require('./lib/usage')
6
7
  var Validation = require('./lib/validation')
7
8
  var Y18n = require('y18n')
@@ -58,7 +59,8 @@ function Argv (processArgs, cwd) {
58
59
  requiresArg: [],
59
60
  count: [],
60
61
  normalize: [],
61
- config: []
62
+ config: {},
63
+ envPrefix: undefined
62
64
  }
63
65
 
64
66
  usage = Usage(self, y18n) // handle usage output.
@@ -66,6 +68,7 @@ function Argv (processArgs, cwd) {
66
68
  completion = Completion(self, usage)
67
69
 
68
70
  demanded = {}
71
+ groups = {}
69
72
 
70
73
  exitProcess = true
71
74
  strict = false
@@ -115,9 +118,15 @@ function Argv (processArgs, cwd) {
115
118
  return self
116
119
  }
117
120
 
118
- self.config = function (key, msg) {
121
+ self.config = function (key, msg, parseFn) {
122
+ if (typeof msg === 'function') {
123
+ parseFn = msg
124
+ msg = null
125
+ }
119
126
  self.describe(key, msg || usage.deferY18nLookup('Path to JSON config file'))
120
- options.config.push.apply(options.config, [].concat(key))
127
+ ;(Array.isArray(key) ? key : [key]).forEach(function (k) {
128
+ options.config[k] = parseFn || true
129
+ })
121
130
  return self
122
131
  }
123
132
 
@@ -166,7 +175,25 @@ function Argv (processArgs, cwd) {
166
175
  self.alias(key, x[key])
167
176
  })
168
177
  } else {
169
- options.alias[x] = (options.alias[x] || []).concat(y)
178
+ // perhaps 'x' is already an alias in another list?
179
+ // if so we should append to x's list.
180
+ var aliases = null
181
+ Object.keys(options.alias).forEach(function (key) {
182
+ if (~options.alias[key].indexOf(x)) aliases = options.alias[key]
183
+ })
184
+
185
+ if (aliases) { // x was an alias itself.
186
+ aliases.push(y)
187
+ } else { // x is a new alias key.
188
+ options.alias[x] = (options.alias[x] || []).concat(y)
189
+ }
190
+
191
+ // wait! perhaps we've created two lists of aliases
192
+ // that reference each other?
193
+ if (options.alias[y]) {
194
+ Array.prototype.push.apply((options.alias[x] || aliases), options.alias[y])
195
+ delete options.alias[y]
196
+ }
170
197
  }
171
198
  return self
172
199
  }
@@ -275,13 +302,15 @@ function Argv (processArgs, cwd) {
275
302
  if (demand) {
276
303
  self.demand(key, demand)
277
304
  } if ('config' in opt) {
278
- self.config(key)
305
+ self.config(key, opt.configParser)
279
306
  } if ('default' in opt) {
280
307
  self.default(key, opt.default)
281
308
  } if ('nargs' in opt) {
282
309
  self.nargs(key, opt.nargs)
283
310
  } if ('choices' in opt) {
284
311
  self.choices(key, opt.choices)
312
+ } if ('group' in opt) {
313
+ self.group(key, opt.group)
285
314
  } if (opt.boolean || opt.type === 'boolean') {
286
315
  self.boolean(key)
287
316
  if (opt.alias) self.boolean(opt.alias)
@@ -313,6 +342,27 @@ function Argv (processArgs, cwd) {
313
342
  return options
314
343
  }
315
344
 
345
+ var groups = {}
346
+ self.group = function (opts, groupName) {
347
+ var seen = {}
348
+ groups[groupName] = (groups[groupName] || []).concat(opts).filter(function (key) {
349
+ if (seen[key]) return false
350
+ return (seen[key] = true)
351
+ })
352
+ return self
353
+ }
354
+ self.getGroups = function () {
355
+ return groups
356
+ }
357
+
358
+ // as long as options.envPrefix is not undefined,
359
+ // parser will apply env vars matching prefix to argv
360
+ self.env = function (prefix) {
361
+ if (prefix === false) options.envPrefix = undefined
362
+ else options.envPrefix = prefix || ''
363
+ return self
364
+ }
365
+
316
366
  self.wrap = function (cols) {
317
367
  usage.wrap(cols)
318
368
  return self
@@ -457,6 +507,8 @@ function Argv (processArgs, cwd) {
457
507
  })
458
508
 
459
509
  function parseArgs (args) {
510
+ args = normalizeArgs(args)
511
+
460
512
  var parsed = Parser(args, options, y18n)
461
513
  var argv = parsed.argv
462
514
  var aliases = parsed.aliases
@@ -481,7 +533,7 @@ function Argv (processArgs, cwd) {
481
533
  var handlerKeys = Object.keys(self.getCommandHandlers())
482
534
  for (var i = 0, command; (command = handlerKeys[i]) !== undefined; i++) {
483
535
  if (~argv._.indexOf(command)) {
484
- self.getCommandHandlers()[command](self.reset())
536
+ runCommand(command, self, argv)
485
537
  return self.argv
486
538
  }
487
539
  }
@@ -563,13 +615,28 @@ function Argv (processArgs, cwd) {
563
615
  }
564
616
  }
565
617
 
618
+ function runCommand (command, yargs, argv) {
619
+ setPlaceholderKeys(argv)
620
+ yargs.getCommandHandlers()[command](yargs.reset(), argv)
621
+ }
622
+
566
623
  function setPlaceholderKeys (argv) {
567
624
  Object.keys(options.key).forEach(function (key) {
625
+ // don't set placeholder keys for dot
626
+ // notation options 'foo.bar'.
627
+ if (~key.indexOf('.')) return
568
628
  if (typeof argv[key] === 'undefined') argv[key] = undefined
569
629
  })
570
630
  }
571
631
 
572
- sigletonify(self)
632
+ function normalizeArgs (args) {
633
+ if (typeof args === 'string') {
634
+ return tokenizeArgString(args)
635
+ }
636
+ return args
637
+ }
638
+
639
+ singletonify(self)
573
640
  return self
574
641
  }
575
642
 
@@ -587,7 +654,7 @@ function rebase (base, dir) {
587
654
  require('yargs').argv
588
655
  to get a parsed version of process.argv.
589
656
  */
590
- function sigletonify (inst) {
657
+ function singletonify (inst) {
591
658
  Object.keys(inst).forEach(function (key) {
592
659
  if (key === 'argv') {
593
660
  Argv.__defineGetter__(key, inst.__lookupGetter__(key))
package/lib/completion.js CHANGED
@@ -19,8 +19,19 @@ module.exports = function (yargs, usage) {
19
19
  // to completion().
20
20
  if (completionFunction) {
21
21
  if (completionFunction.length < 3) {
22
+ var result = completionFunction(current, argv)
23
+
24
+ // promise based completion function.
25
+ if (typeof result.then === 'function') {
26
+ return result.then(function (list) {
27
+ process.nextTick(function () { done(list) })
28
+ }).catch(function (err) {
29
+ process.nextTick(function () { throw err })
30
+ })
31
+ }
32
+
22
33
  // synchronous completion function.
23
- return done(completionFunction(current, argv))
34
+ return done(result)
24
35
  } else {
25
36
  // asynchronous completion function
26
37
  return completionFunction(current, argv, function (completions) {
package/lib/parser.js CHANGED
@@ -34,8 +34,8 @@ module.exports = function (args, opts, y18n) {
34
34
  flags.normalize[key] = true
35
35
  })
36
36
 
37
- ;[].concat(opts.config).filter(Boolean).forEach(function (key) {
38
- flags.configs[key] = true
37
+ Object.keys(opts.config).forEach(function (k) {
38
+ flags.configs[k] = opts.config[k]
39
39
  })
40
40
 
41
41
  var aliases = {}
@@ -43,6 +43,7 @@ module.exports = function (args, opts, y18n) {
43
43
 
44
44
  extendAliases(opts.key)
45
45
  extendAliases(opts.alias)
46
+ extendAliases(opts.default)
46
47
 
47
48
  var defaults = opts['default'] || {}
48
49
  Object.keys(defaults).forEach(function (key) {
@@ -220,7 +221,14 @@ module.exports = function (args, opts, y18n) {
220
221
  }
221
222
  }
222
223
 
224
+ // order of precedence:
225
+ // 1. command line arg
226
+ // 2. value from config file
227
+ // 3. value from env var
228
+ // 4. configured default value
229
+ applyEnvVars(opts, argv, true) // special case: check env vars that point to config file
223
230
  setConfig(argv)
231
+ applyEnvVars(opts, argv, false)
224
232
  applyDefaultsAndAliases(argv, aliases, defaults)
225
233
 
226
234
  Object.keys(flags.counts).forEach(function (key) {
@@ -281,6 +289,15 @@ module.exports = function (args, opts, y18n) {
281
289
  var splitKey = key.split('.')
282
290
  setKey(argv, splitKey, value)
283
291
 
292
+ // alias references an inner-value within
293
+ // a dot-notation object. see #279.
294
+ if (~key.indexOf('.') && aliases[key]) {
295
+ aliases[key].forEach(function (x) {
296
+ x = x.split('.')
297
+ setKey(argv, x, value)
298
+ })
299
+ }
300
+
284
301
  ;(aliases[splitKey[0]] || []).forEach(function (x) {
285
302
  x = x.split('.')
286
303
 
@@ -325,7 +342,22 @@ module.exports = function (args, opts, y18n) {
325
342
  var configPath = argv[configKey] || configLookup[configKey]
326
343
  if (configPath) {
327
344
  try {
328
- var config = require(path.resolve(process.cwd(), configPath))
345
+ var config = null
346
+ var resolvedConfigPath = path.resolve(process.cwd(), configPath)
347
+
348
+ if (typeof flags.configs[configKey] === 'function') {
349
+ try {
350
+ config = flags.configs[configKey](resolvedConfigPath)
351
+ } catch (e) {
352
+ config = e
353
+ }
354
+ if (config instanceof Error) {
355
+ error = config
356
+ return
357
+ }
358
+ } else {
359
+ config = require(resolvedConfigPath)
360
+ }
329
361
 
330
362
  Object.keys(config).forEach(function (key) {
331
363
  // setting arguments via CLI takes precedence over
@@ -342,12 +374,27 @@ module.exports = function (args, opts, y18n) {
342
374
  })
343
375
  }
344
376
 
377
+ function applyEnvVars (opts, argv, configOnly) {
378
+ if (typeof opts.envPrefix === 'undefined') return
379
+
380
+ var prefix = typeof opts.envPrefix === 'string' ? opts.envPrefix : ''
381
+ Object.keys(process.env).forEach(function (envVar) {
382
+ if (prefix === '' || envVar.lastIndexOf(prefix, 0) === 0) {
383
+ var key = camelCase(envVar.substring(prefix.length))
384
+ if (((configOnly && flags.configs[key]) || !configOnly) && (!(key in argv) || flags.defaulted[key])) {
385
+ setArg(key, process.env[envVar])
386
+ }
387
+ }
388
+ })
389
+ }
390
+
345
391
  function applyDefaultsAndAliases (obj, aliases, defaults) {
346
392
  Object.keys(defaults).forEach(function (key) {
347
393
  if (!hasKey(obj, key.split('.'))) {
348
394
  setKey(obj, key.split('.'), defaults[key])
349
395
 
350
396
  ;(aliases[key] || []).forEach(function (x) {
397
+ if (hasKey(obj, x.split('.'))) return
351
398
  setKey(obj, x.split('.'), defaults[key])
352
399
  })
353
400
  }
@@ -361,7 +408,9 @@ module.exports = function (args, opts, y18n) {
361
408
  })
362
409
 
363
410
  var key = keys[keys.length - 1]
364
- return key in o
411
+
412
+ if (typeof o !== 'object') return false
413
+ else return key in o
365
414
  }
366
415
 
367
416
  function setKey (obj, keys, value) {
@@ -388,6 +437,11 @@ module.exports = function (args, opts, y18n) {
388
437
  // extend the aliases list with inferred aliases.
389
438
  function extendAliases (obj) {
390
439
  Object.keys(obj || {}).forEach(function (key) {
440
+ // short-circuit if we've already added a key
441
+ // to the aliases array, for example it might
442
+ // exist in both 'opts.default' and 'opts.key'.
443
+ if (aliases[key]) return
444
+
391
445
  aliases[key] = [].concat(opts.alias[key] || [])
392
446
  // For "--option-name", also set argv.optionName
393
447
  aliases[key].concat(key).forEach(function (x) {
@@ -0,0 +1,32 @@
1
+ // take an un-split argv string and tokenize it.
2
+ module.exports = function (argString) {
3
+ var i = 0
4
+ var c = null
5
+ var opening = null
6
+ var args = []
7
+
8
+ for (var ii = 0; ii < argString.length; ii++) {
9
+ c = argString.charAt(ii)
10
+
11
+ // split on spaces unless we're in quotes.
12
+ if (c === ' ' && !opening) {
13
+ i++
14
+ continue
15
+ }
16
+
17
+ // don't split the string if we're in matching
18
+ // opening or closing single and double quotes.
19
+ if (c === opening) {
20
+ opening = null
21
+ continue
22
+ } else if ((c === "'" || c === '"') && !opening) {
23
+ opening = c
24
+ continue
25
+ }
26
+
27
+ if (!args[i]) args[i] = ''
28
+ args[i] += c
29
+ }
30
+
31
+ return args
32
+ }
package/lib/usage.js CHANGED
@@ -2,6 +2,7 @@
2
2
  // failures, etc. keeps logging in one place.
3
3
  var cliui = require('cliui')
4
4
  var decamelize = require('decamelize')
5
+ var stringWidth = require('string-width')
5
6
  var wsize = require('window-size')
6
7
 
7
8
  module.exports = function (yargs, y18n) {
@@ -101,10 +102,12 @@ module.exports = function (yargs, y18n) {
101
102
  return deferY18nLookupPrefix + str
102
103
  }
103
104
 
105
+ var defaultGroup = 'Options:'
104
106
  self.help = function () {
105
107
  normalizeAliases()
106
108
 
107
109
  var demanded = yargs.getDemanded()
110
+ var groups = yargs.getGroups()
108
111
  var options = yargs.getOptions()
109
112
  var keys = Object.keys(
110
113
  Object.keys(descriptions)
@@ -141,7 +144,8 @@ module.exports = function (yargs, y18n) {
141
144
  ui.div()
142
145
  }
143
146
 
144
- // the options table.
147
+ // perform some cleanup on the keys array, making it
148
+ // only include top-level keys not their aliases.
145
149
  var aliasKeys = (Object.keys(options.alias) || [])
146
150
  .concat(Object.keys(yargs.parsed.newAliases) || [])
147
151
 
@@ -151,20 +155,39 @@ module.exports = function (yargs, y18n) {
151
155
  })
152
156
  })
153
157
 
154
- var switches = keys.reduce(function (acc, key) {
155
- acc[key] = [ key ].concat(options.alias[key] || [])
156
- .map(function (sw) {
157
- return (sw.length > 1 ? '--' : '-') + sw
158
+ // populate 'Options:' group with any keys that have not
159
+ // explicitly had a group set.
160
+ if (!groups[defaultGroup]) groups[defaultGroup] = []
161
+ addUngroupedKeys(keys, options.alias, groups)
162
+
163
+ // display 'Options:' table along with any custom tables:
164
+ Object.keys(groups).forEach(function (groupName) {
165
+ if (!groups[groupName].length) return
166
+
167
+ ui.div(__(groupName))
168
+
169
+ // if we've grouped the key 'f', but 'f' aliases 'foobar',
170
+ // normalizedKeys should contain only 'foobar'.
171
+ var normalizedKeys = groups[groupName].map(function (key) {
172
+ if (~aliasKeys.indexOf(key)) return key
173
+ for (var i = 0, aliasKey; (aliasKey = aliasKeys[i]) !== undefined; i++) {
174
+ if (~(options.alias[aliasKey] || []).indexOf(key)) return aliasKey
175
+ }
176
+ return key
158
177
  })
159
- .join(', ')
160
178
 
161
- return acc
162
- }, {})
179
+ // actually generate the switches string --foo, -f, --bar.
180
+ var switches = normalizedKeys.reduce(function (acc, key) {
181
+ acc[key] = [ key ].concat(options.alias[key] || [])
182
+ .map(function (sw) {
183
+ return (sw.length > 1 ? '--' : '-') + sw
184
+ })
185
+ .join(', ')
163
186
 
164
- if (keys.length) {
165
- ui.div(__('Options:'))
187
+ return acc
188
+ }, {})
166
189
 
167
- keys.forEach(function (key) {
190
+ normalizedKeys.forEach(function (key) {
168
191
  var kswitch = switches[key]
169
192
  var desc = descriptions[key] || ''
170
193
  var type = null
@@ -195,7 +218,7 @@ module.exports = function (yargs, y18n) {
195
218
  })
196
219
 
197
220
  ui.div()
198
- }
221
+ })
199
222
 
200
223
  // describe some common use-cases for your application.
201
224
  if (examples.length) {
@@ -238,7 +261,7 @@ module.exports = function (yargs, y18n) {
238
261
  }
239
262
 
240
263
  table.forEach(function (v) {
241
- width = Math.max(v[0].length, width)
264
+ width = Math.max(stringWidth(v[0]), width)
242
265
  })
243
266
 
244
267
  // if we've enabled 'wrap' we should limit
@@ -260,7 +283,6 @@ module.exports = function (yargs, y18n) {
260
283
  if (descriptions[alias]) self.describe(key, descriptions[alias])
261
284
  // copy demanded.
262
285
  if (demanded[alias]) yargs.demand(key, demanded[alias].msg)
263
-
264
286
  // type messages.
265
287
  if (~options.boolean.indexOf(alias)) yargs.boolean(key)
266
288
  if (~options.count.indexOf(alias)) yargs.count(key)
@@ -271,6 +293,26 @@ module.exports = function (yargs, y18n) {
271
293
  })
272
294
  }
273
295
 
296
+ // given a set of keys, place any keys that are
297
+ // ungrouped under the 'Options:' grouping.
298
+ function addUngroupedKeys (keys, aliases, groups) {
299
+ var groupedKeys = []
300
+ var toCheck = null
301
+ Object.keys(groups).forEach(function (group) {
302
+ groupedKeys = groupedKeys.concat(groups[group])
303
+ })
304
+
305
+ keys.forEach(function (key) {
306
+ toCheck = [key].concat(aliases[key])
307
+ if (!toCheck.some(function (k) {
308
+ return groupedKeys.indexOf(k) !== -1
309
+ })) {
310
+ groups[defaultGroup].push(key)
311
+ }
312
+ })
313
+ return groupedKeys
314
+ }
315
+
274
316
  self.showHelp = function (level) {
275
317
  level = level || 'error'
276
318
  console[level](self.help())
@@ -0,0 +1,37 @@
1
+
2
+ {
3
+ "Commands:": "Perintah:",
4
+ "Options:": "Pilihan:",
5
+ "Examples:": "Contoh:",
6
+ "boolean": "boolean",
7
+ "count": "jumlah",
8
+ "string": "string",
9
+ "array": "larik",
10
+ "required": "diperlukan",
11
+ "default:": "bawaan:",
12
+ "choices:": "pilihan:",
13
+ "generated-value": "nilai-yang-dihasilkan",
14
+ "Not enough non-option arguments: got %s, need at least %s": "Argumen wajib kurang: hanya %s, minimal %s",
15
+ "Too many non-option arguments: got %s, maximum of %s": "Terlalu banyak argumen wajib: ada %s, maksimal %s",
16
+ "Missing argument value: %s": {
17
+ "one": "Kurang argumen: %s",
18
+ "other": "Kurang argumen: %s"
19
+ },
20
+ "Missing required argument: %s": {
21
+ "one": "Kurang argumen wajib: %s",
22
+ "other": "Kurang argumen wajib: %s"
23
+ },
24
+ "Unknown argument: %s": {
25
+ "one": "Argumen tak diketahui: %s",
26
+ "other": "Argumen tak diketahui: %s"
27
+ },
28
+ "Invalid values:": "Nilai-nilai tidak valid:",
29
+ "Argument: %s, Given: %s, Choices: %s": "Argumen: %s, Diberikan: %s, Pilihan: %s",
30
+ "Argument check failed: %s": "Pemeriksaan argument gagal: %s",
31
+ "Implications failed:": "Implikasi gagal:",
32
+ "Not enough arguments following: %s": "Kurang argumen untuk: %s",
33
+ "Invalid JSON config file: %s": "Berkas konfigurasi JSON tidak valid: %s",
34
+ "Path to JSON config file": "Alamat berkas konfigurasi JSON",
35
+ "Show help": "Lihat bantuan",
36
+ "Show version number": "Lihat nomor versi"
37
+ }
@@ -0,0 +1,36 @@
1
+ {
2
+ "Commands:": "명령:",
3
+ "Options:": "옵션:",
4
+ "Examples:": "예시:",
5
+ "boolean": "여부",
6
+ "count": "개수",
7
+ "string": "문자열",
8
+ "array": "배열",
9
+ "required": "필수",
10
+ "default:": "기본:",
11
+ "choices:": "선택:",
12
+ "generated-value": "생성된 값",
13
+ "Not enough non-option arguments: got %s, need at least %s": "옵션이 아닌 인자가 충분치 않습니다: %s개를 받았지만, 적어도 %s개는 필요합니다",
14
+ "Too many non-option arguments: got %s, maximum of %s": "옵션이 아닌 인자가 너무 많습니다: %s개를 받았지만, %s개 이하여야 합니다",
15
+ "Missing argument value: %s": {
16
+ "one": "인자값을 받지 못했습니다: %s",
17
+ "other": "인자값들을 받지 못했습니다: %s"
18
+ },
19
+ "Missing required argument: %s": {
20
+ "one": "필수 인자를 받지 못했습니다: %s",
21
+ "other": "필수 인자들을 받지 못했습니다: %s"
22
+ },
23
+ "Unknown argument: %s": {
24
+ "one": "알 수 없는 인자입니다: %s",
25
+ "other": "알 수 없는 인자들입니다: %s"
26
+ },
27
+ "Invalid values:": "잘못된 값입니다:",
28
+ "Argument: %s, Given: %s, Choices: %s": "인자: %s, 입력받은 값: %s, 선택지: %s",
29
+ "Argument check failed: %s": "유효하지 않은 인자입니다: %s",
30
+ "Implications failed:": "옵션의 조합이 잘못되었습니다:",
31
+ "Not enough arguments following: %s": "인자가 충분하게 주어지지 않았습니다: %s",
32
+ "Invalid JSON config file: %s": "유효하지 않은 JSON 설정파일입니다: %s",
33
+ "Path to JSON config file": "JSON 설정파일 경로",
34
+ "Show help": "도움말을 보여줍니다",
35
+ "Show version number": "버전 넘버를 보여줍니다"
36
+ }
@@ -0,0 +1,36 @@
1
+ {
2
+ "Commands:": "Kommandoer:",
3
+ "Options:": "Alternativer:",
4
+ "Examples:": "Eksempler:",
5
+ "boolean": "boolsk",
6
+ "count": "antall",
7
+ "string": "streng",
8
+ "array": "matrise",
9
+ "required": "obligatorisk",
10
+ "default:": "standard:",
11
+ "choices:": "valg:",
12
+ "generated-value": "generert-verdi",
13
+ "Not enough non-option arguments: got %s, need at least %s": "Ikke nok ikke-alternativ argumenter: fikk %s, trenger minst %s",
14
+ "Too many non-option arguments: got %s, maximum of %s": "For mange ikke-alternativ argumenter: fikk %s, maksimum %s",
15
+ "Missing argument value: %s": {
16
+ "one": "Mangler argument verdi: %s",
17
+ "other": "Mangler argument verdier: %s"
18
+ },
19
+ "Missing required argument: %s": {
20
+ "one": "Mangler obligatorisk argument: %s",
21
+ "other": "Mangler obligatoriske argumenter: %s"
22
+ },
23
+ "Unknown argument: %s": {
24
+ "one": "Ukjent argument: %s",
25
+ "other": "Ukjente argumenter: %s"
26
+ },
27
+ "Invalid values:": "Ugyldige verdier:",
28
+ "Argument: %s, Given: %s, Choices: %s": "Argument: %s, Gitt: %s, Valg: %s",
29
+ "Argument check failed: %s": "Argument sjekk mislyktes: %s",
30
+ "Implications failed:": "Konsekvensene mislyktes:",
31
+ "Not enough arguments following: %s": "Ikke nok følgende argumenter: %s",
32
+ "Invalid JSON config file: %s": "Ugyldig JSON konfigurasjonsfil: %s",
33
+ "Path to JSON config file": "Bane til JSON konfigurasjonsfil",
34
+ "Show help": "Vis hjelp",
35
+ "Show version number": "Vis versjonsnummer"
36
+ }
@@ -0,0 +1,36 @@
1
+ {
2
+ "Commands:": "Polecenia:",
3
+ "Options:": "Opcje:",
4
+ "Examples:": "Przykłady:",
5
+ "boolean": "boolean",
6
+ "count": "ilość",
7
+ "string": "ciąg znaków",
8
+ "array": "tablica",
9
+ "required": "wymagany",
10
+ "default:": "domyślny:",
11
+ "choices:": "dostępne:",
12
+ "generated-value": "wygenerowana-wartość",
13
+ "Not enough non-option arguments: got %s, need at least %s": "Niewystarczająca ilość argumentów: otrzymano %s, wymagane co najmniej %s",
14
+ "Too many non-option arguments: got %s, maximum of %s": "Zbyt duża ilość argumentów: otrzymano %s, wymagane co najwyżej %s",
15
+ "Missing argument value: %s": {
16
+ "one": "Brak wartości dla argumentu: %s",
17
+ "other": "Brak wartości dla argumentów: %s"
18
+ },
19
+ "Missing required argument: %s": {
20
+ "one": "Brak wymaganego argumentu: %s",
21
+ "other": "Brak wymaganych argumentów: %s"
22
+ },
23
+ "Unknown argument: %s": {
24
+ "one": "Nieznany argument: %s",
25
+ "other": "Nieznane argumenty: %s"
26
+ },
27
+ "Invalid values:": "Nieprawidłowe wartości:",
28
+ "Argument: %s, Given: %s, Choices: %s": "Argument: %s, Otrzymano: %s, Dostępne: %s",
29
+ "Argument check failed: %s": "Weryfikacja argumentów nie powiodła się: %s",
30
+ "Implications failed:": "Założenia nie zostały spełnione:",
31
+ "Not enough arguments following: %s": "Niewystarczająca ilość argumentów następujących po: %s",
32
+ "Invalid JSON config file: %s": "Nieprawidłowy plik konfiguracyjny JSON: %s",
33
+ "Path to JSON config file": "Ścieżka do pliku konfiguracyjnego JSON",
34
+ "Show help": "Pokaż pomoc",
35
+ "Show version number": "Pokaż numer wersji"
36
+ }
@@ -0,0 +1,36 @@
1
+ {
2
+ "Commands:": "Comandos:",
3
+ "Options:": "Opções:",
4
+ "Examples:": "Exemplos:",
5
+ "boolean": "boolean",
6
+ "count": "contagem",
7
+ "string": "string",
8
+ "array": "array",
9
+ "required": "obrigatório",
10
+ "default:": "padrão:",
11
+ "choices:": "opções:",
12
+ "generated-value": "valor-gerado",
13
+ "Not enough non-option arguments: got %s, need at least %s": "Argumentos insuficientes: Argumento %s, necessário pelo menos %s",
14
+ "Too many non-option arguments: got %s, maximum of %s": "Excesso de argumentos: recebido %s, máximo de %s",
15
+ "Missing argument value: %s": {
16
+ "one": "Falta valor de argumento: %s",
17
+ "other": "Falta valores de argumento: %s"
18
+ },
19
+ "Missing required argument: %s": {
20
+ "one": "Falta argumento obrigatório: %s",
21
+ "other": "Faltando argumentos obrigatórios: %s"
22
+ },
23
+ "Unknown argument: %s": {
24
+ "one": "Argumento desconhecido: %s",
25
+ "other": "Argumentos desconhecidos: %s"
26
+ },
27
+ "Invalid values:": "Valores inválidos:",
28
+ "Argument: %s, Given: %s, Choices: %s": "Argumento: %s, Dado: %s, Opções: %s",
29
+ "Argument check failed: %s": "Verificação de argumento falhou: %s",
30
+ "Implications failed:": "Implicações falharam:",
31
+ "Not enough arguments following: %s": "Argumentos insuficientes a seguir: %s",
32
+ "Invalid JSON config file: %s": "Arquivo JSON de configuração inválido: %s",
33
+ "Path to JSON config file": "Caminho para o arquivo JSON de configuração",
34
+ "Show help": "Exibe ajuda",
35
+ "Show version number": "Exibe a versão"
36
+ }
@@ -0,0 +1,36 @@
1
+ {
2
+ "Commands:": "Komutlar:",
3
+ "Options:": "Seçenekler:",
4
+ "Examples:": "Örnekler:",
5
+ "boolean": "boolean",
6
+ "count": "sayı",
7
+ "string": "string",
8
+ "array": "array",
9
+ "required": "zorunlu",
10
+ "default:": "varsayılan:",
11
+ "choices:": "seçimler:",
12
+ "generated-value": "oluşturulan-değer",
13
+ "Not enough non-option arguments: got %s, need at least %s": "Seçenek dışı argümanlar yetersiz: %s bulundu, %s gerekli",
14
+ "Too many non-option arguments: got %s, maximum of %s": "Seçenek dışı argümanlar gereğinden fazla: %s bulundu, azami %s",
15
+ "Missing argument value: %s": {
16
+ "one": "Eksik argüman değeri: %s",
17
+ "other": "Eksik argüman değerleri: %s"
18
+ },
19
+ "Missing required argument: %s": {
20
+ "one": "Eksik zorunlu argüman: %s",
21
+ "other": "Eksik zorunlu argümanlar: %s"
22
+ },
23
+ "Unknown argument: %s": {
24
+ "one": "Bilinmeyen argüman: %s",
25
+ "other": "Bilinmeyen argümanlar: %s"
26
+ },
27
+ "Invalid values:": "Geçersiz değerler:",
28
+ "Argument: %s, Given: %s, Choices: %s": "Argüman: %s, Verilen: %s, Seçimler: %s",
29
+ "Argument check failed: %s": "Argüman kontrolü başarısız oldu: %s",
30
+ "Implications failed:": "Sonuçlar başarısız oldu:",
31
+ "Not enough arguments following: %s": "%s için yeterli argüman bulunamadı",
32
+ "Invalid JSON config file: %s": "Geçersiz JSON yapılandırma dosyası: %s",
33
+ "Path to JSON config file": "JSON yapılandırma dosya konumu",
34
+ "Show help": "Yardım detaylarını göster",
35
+ "Show version number": "Versiyon detaylarını göster"
36
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yargs",
3
- "version": "3.28.0",
3
+ "version": "3.32.0",
4
4
  "description": "Light-weight option parsing with an argv hash. No optstrings attached.",
5
5
  "main": "./index.js",
6
6
  "files": [
@@ -11,26 +11,29 @@
11
11
  "LICENSE"
12
12
  ],
13
13
  "dependencies": {
14
- "camelcase": "^1.2.1",
15
- "cliui": "^3.0.2",
16
- "decamelize": "^1.0.0",
14
+ "camelcase": "^2.0.1",
15
+ "cliui": "^3.0.3",
16
+ "decamelize": "^1.1.1",
17
17
  "os-locale": "^1.4.0",
18
- "window-size": "^0.1.2",
18
+ "string-width": "^1.0.1",
19
+ "window-size": "^0.1.4",
19
20
  "y18n": "^3.2.0"
20
21
  },
21
22
  "devDependencies": {
22
- "chai": "^3.3.0",
23
+ "chai": "^3.4.1",
23
24
  "chalk": "^1.1.1",
24
25
  "coveralls": "^2.11.4",
26
+ "es6-promise": "^3.0.2",
25
27
  "hashish": "0.0.4",
26
- "mocha": "^2.3.3",
27
- "nyc": "^3.2.2",
28
- "standard": "^5.3.1",
28
+ "mocha": "^2.3.4",
29
+ "nyc": "^5.2.0",
30
+ "standard": "^5.4.1",
29
31
  "which": "^1.1.2",
30
32
  "win-spawn": "^2.0.0"
31
33
  },
32
34
  "scripts": {
33
- "test": "standard && nyc ./node_modules/.bin/_mocha --timeout=4000 --check-leaks",
35
+ "pretest": "standard",
36
+ "test": "nyc --cache mocha --timeout=4000 --check-leaks",
34
37
  "coverage": "nyc report --reporter=text-lcov | coveralls"
35
38
  },
36
39
  "repository": {
@@ -94,6 +97,6 @@
94
97
  ],
95
98
  "license": "MIT",
96
99
  "engine": {
97
- "node": ">=0.4"
100
+ "node": ">=0.10"
98
101
  }
99
102
  }