yargs 13.3.2 → 14.2.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/CHANGELOG.md CHANGED
@@ -2,6 +2,71 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4
4
 
5
+ ### [14.2.1](https://github.com/yargs/yargs/compare/v14.2.0...v14.2.1) (2019-10-30)
6
+
7
+
8
+ ### Bug Fixes
9
+
10
+ * stop-parse was not being respected by commands ([#1459](https://github.com/yargs/yargs/issues/1459)) ([e78e76e](https://github.com/yargs/yargs/commit/e78e76e3ac0551d4f30c71a05ddb21582960fcef))
11
+
12
+ ## [14.2.0](https://github.com/yargs/yargs/compare/v14.1.0...v14.2.0) (2019-10-07)
13
+
14
+
15
+ ### Bug Fixes
16
+
17
+ * async middleware was called twice ([#1422](https://github.com/yargs/yargs/issues/1422)) ([9a42b63](https://github.com/yargs/yargs/commit/9a42b63))
18
+ * fix promise check to accept any spec conform object ([#1424](https://github.com/yargs/yargs/issues/1424)) ([0be43d2](https://github.com/yargs/yargs/commit/0be43d2))
19
+ * groups were not being maintained for nested commands ([#1430](https://github.com/yargs/yargs/issues/1430)) ([d38650e](https://github.com/yargs/yargs/commit/d38650e))
20
+ * **docs:** broken markdown link ([#1426](https://github.com/yargs/yargs/issues/1426)) ([236e24e](https://github.com/yargs/yargs/commit/236e24e))
21
+ * support merging deeply nested configuration ([#1423](https://github.com/yargs/yargs/issues/1423)) ([bae66fe](https://github.com/yargs/yargs/commit/bae66fe))
22
+
23
+
24
+ ### Features
25
+
26
+ * **deps:** introduce yargs-parser with support for unknown-options-as-args ([#1440](https://github.com/yargs/yargs/issues/1440)) ([4d21520](https://github.com/yargs/yargs/commit/4d21520))
27
+
28
+ ## [14.1.0](https://github.com/yargs/yargs/compare/v14.0.0...v14.1.0) (2019-09-06)
29
+
30
+
31
+ ### Bug Fixes
32
+
33
+ * **docs:** fix incorrect parserConfiguration documentation ([2a99124](https://github.com/yargs/yargs/commit/2a99124))
34
+ * detect zsh when zsh isnt run as a login prompt ([#1395](https://github.com/yargs/yargs/issues/1395)) ([8792d13](https://github.com/yargs/yargs/commit/8792d13))
35
+ * populate correct value on yargs.parsed and stop warning on access ([#1412](https://github.com/yargs/yargs/issues/1412)) ([bb0eb52](https://github.com/yargs/yargs/commit/bb0eb52))
36
+ * showCompletionScript was logging script twice ([#1388](https://github.com/yargs/yargs/issues/1388)) ([07c8537](https://github.com/yargs/yargs/commit/07c8537))
37
+ * strict() should not ignore hyphenated arguments ([#1414](https://github.com/yargs/yargs/issues/1414)) ([b774b5e](https://github.com/yargs/yargs/commit/b774b5e))
38
+ * **docs:** formalize existing callback argument to showHelp ([#1386](https://github.com/yargs/yargs/issues/1386)) ([d217764](https://github.com/yargs/yargs/commit/d217764))
39
+
40
+
41
+ ### Features
42
+
43
+ * make it possible to merge configurations when extending other config. ([#1411](https://github.com/yargs/yargs/issues/1411)) ([5d7ad98](https://github.com/yargs/yargs/commit/5d7ad98))
44
+
45
+ ## [14.0.0](https://github.com/yargs/yargs/compare/v13.3.0...v14.0.0) (2019-07-30)
46
+
47
+
48
+ ### ⚠ BREAKING CHANGES
49
+
50
+ * we now only officially support yargs.$0 parameter and discourage direct access to yargs.parsed
51
+ * previously to this fix methods like `yargs.getOptions()` contained the state of the last command to execute.
52
+ * do not allow additional positionals in strict mode
53
+
54
+ ### Bug Fixes
55
+
56
+ * calling parse multiple times now appropriately maintains state ([#1137](https://github.com/yargs/yargs/issues/1137)) ([#1369](https://github.com/yargs/yargs/issues/1369)) ([026b151](https://github.com/yargs/yargs/commit/026b151))
57
+ * prefer user supplied script name in usage ([#1383](https://github.com/yargs/yargs/issues/1383)) ([28c74b9](https://github.com/yargs/yargs/commit/28c74b9))
58
+ * **deps:** use decamelize from npm instead of vendored copy ([#1377](https://github.com/yargs/yargs/issues/1377)) ([015eeb9](https://github.com/yargs/yargs/commit/015eeb9))
59
+ * **examples:** fix usage-options.js to reflect current API ([#1375](https://github.com/yargs/yargs/issues/1375)) ([6e5b76b](https://github.com/yargs/yargs/commit/6e5b76b))
60
+ * do not allow additional positionals in strict mode ([35d777c](https://github.com/yargs/yargs/commit/35d777c))
61
+ * properties accessed on singleton now reflect current state of instance ([#1366](https://github.com/yargs/yargs/issues/1366)) ([409d35b](https://github.com/yargs/yargs/commit/409d35b))
62
+ * tolerate null prototype for config objects with `extends` ([#1376](https://github.com/yargs/yargs/issues/1376)) ([3d26d11](https://github.com/yargs/yargs/commit/3d26d11)), closes [#1372](https://github.com/yargs/yargs/issues/1372)
63
+ * yargs.parsed now populated before returning, when yargs.parse() called with no args (#1382) ([e3981fd](https://github.com/yargs/yargs/commit/e3981fd)), closes [#1382](https://github.com/yargs/yargs/issues/1382)
64
+
65
+ ### Features
66
+
67
+ * adds support for multiple epilog messages ([#1384](https://github.com/yargs/yargs/issues/1384)) ([07a5554](https://github.com/yargs/yargs/commit/07a5554))
68
+ * allow completionCommand to be set via showCompletionScript ([#1385](https://github.com/yargs/yargs/issues/1385)) ([5562853](https://github.com/yargs/yargs/commit/5562853))
69
+
5
70
  ## [13.3.0](https://www.github.com/yargs/yargs/compare/v13.2.4...v13.3.0) (2019-06-10)
6
71
 
7
72
 
package/README.md CHANGED
@@ -5,6 +5,7 @@
5
5
  <p align="center">
6
6
  <b >Yargs be a node.js library fer hearties tryin' ter parse optstrings</b>
7
7
  </p>
8
+
8
9
  <br>
9
10
 
10
11
  [![Build Status][travis-image]][travis-url]
@@ -15,7 +16,7 @@
15
16
  [![Slack][slack-image]][slack-url]
16
17
 
17
18
  ## Description :
18
- Yargs helps you build interactive command line tools, by parsing arguments and generating an elegant user interface.
19
+ Yargs helps you build interactive command line tools, by parsing arguments and generating an elegant user interface.
19
20
 
20
21
  It gives you:
21
22
 
@@ -79,13 +80,24 @@ require('yargs') // eslint-disable-line
79
80
  })
80
81
  .option('verbose', {
81
82
  alias: 'v',
82
- default: false
83
+ type: 'boolean',
84
+ description: 'Run with verbose logging'
83
85
  })
84
86
  .argv
85
87
  ```
86
88
 
87
89
  Run the example above with `--help` to see the help for the application.
88
90
 
91
+ ## TypeScript
92
+
93
+ yargs has type definitions at [@types/yargs][type-definitions].
94
+
95
+ ```
96
+ npm i @types/yargs --save-dev
97
+ ```
98
+
99
+ See usage examples in [docs](/docs/typescript.md).
100
+
89
101
  ## Community :
90
102
 
91
103
  Having problems? want to contribute? join our [community slack](http://devtoolscommunity.herokuapp.com).
@@ -102,6 +114,7 @@ Having problems? want to contribute? join our [community slack](http://devtoolsc
102
114
  * [Numbers](/docs/tricks.md#numbers)
103
115
  * [Arrays](/docs/tricks.md#arrays)
104
116
  * [Objects](/docs/tricks.md#objects)
117
+ * [Quotes](/docs/tricks.md#quotes)
105
118
  * [Advanced Topics](/docs/advanced.md)
106
119
  * [Composing Your App Using Commands](/docs/advanced.md#commands)
107
120
  * [Building Configurable CLI Apps](/docs/advanced.md#configuration)
@@ -120,3 +133,4 @@ Having problems? want to contribute? join our [community slack](http://devtoolsc
120
133
  [conventional-commits-url]: https://conventionalcommits.org/
121
134
  [slack-image]: http://devtoolscommunity.herokuapp.com/badge.svg
122
135
  [slack-url]: http://devtoolscommunity.herokuapp.com
136
+ [type-definitions]: https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/yargs
package/index.js CHANGED
@@ -25,8 +25,15 @@ function singletonify (inst) {
25
25
  Object.keys(inst).forEach((key) => {
26
26
  if (key === 'argv') {
27
27
  Argv.__defineGetter__(key, inst.__lookupGetter__(key))
28
+ } else if (typeof inst[key] === 'function') {
29
+ Argv[key] = inst[key].bind(inst)
28
30
  } else {
29
- Argv[key] = typeof inst[key] === 'function' ? inst[key].bind(inst) : inst[key]
31
+ Argv.__defineGetter__('$0', () => {
32
+ return inst.$0
33
+ })
34
+ Argv.__defineGetter__('parsed', () => {
35
+ return inst.parsed
36
+ })
30
37
  }
31
38
  })
32
39
  }
@@ -16,10 +16,24 @@ function getPathToDefaultConfig (cwd, pathToExtend) {
16
16
  return path.resolve(cwd, pathToExtend)
17
17
  }
18
18
 
19
- function applyExtends (config, cwd) {
19
+ function mergeDeep (config1, config2) {
20
+ const target = {}
21
+ const isObject = obj => obj && typeof obj === 'object' && !Array.isArray(obj)
22
+ Object.assign(target, config1)
23
+ for (let key of Object.keys(config2)) {
24
+ if (isObject(config2[key]) && isObject(target[key])) {
25
+ target[key] = mergeDeep(config1[key], config2[key])
26
+ } else {
27
+ target[key] = config2[key]
28
+ }
29
+ }
30
+ return target
31
+ }
32
+
33
+ function applyExtends (config, cwd, mergeExtends) {
20
34
  let defaultConfig = {}
21
35
 
22
- if (config.hasOwnProperty('extends')) {
36
+ if (Object.prototype.hasOwnProperty.call(config, 'extends')) {
23
37
  if (typeof config.extends !== 'string') return defaultConfig
24
38
  const isPath = /\.json|\..*rc$/.test(config.extends)
25
39
  let pathToDefault = null
@@ -42,12 +56,12 @@ function applyExtends (config, cwd) {
42
56
 
43
57
  defaultConfig = isPath ? JSON.parse(fs.readFileSync(pathToDefault, 'utf8')) : require(config.extends)
44
58
  delete config.extends
45
- defaultConfig = applyExtends(defaultConfig, path.dirname(pathToDefault))
59
+ defaultConfig = applyExtends(defaultConfig, path.dirname(pathToDefault), mergeExtends)
46
60
  }
47
61
 
48
62
  previouslyVisitedConfigs = []
49
63
 
50
- return Object.assign({}, defaultConfig, config)
64
+ return mergeExtends ? mergeDeep(defaultConfig, config) : Object.assign({}, defaultConfig, config)
51
65
  }
52
66
 
53
67
  module.exports = applyExtends
package/lib/command.js CHANGED
@@ -174,7 +174,7 @@ module.exports = function command (yargs, usage, validation, globalMiddleware) {
174
174
  let numFiles = currentContext.files.length
175
175
  const parentCommands = currentContext.commands.slice()
176
176
 
177
- // what does yargs look like after the buidler is run?
177
+ // what does yargs look like after the builder is run?
178
178
  let innerArgv = parsed.argv
179
179
  let innerYargs = null
180
180
  let positionalMap = {}
@@ -186,24 +186,17 @@ module.exports = function command (yargs, usage, validation, globalMiddleware) {
186
186
  // a function can be provided, which builds
187
187
  // up a yargs chain and possibly returns it.
188
188
  innerYargs = commandHandler.builder(yargs.reset(parsed.aliases))
189
- // if the builder function did not yet parse argv with reset yargs
190
- // and did not explicitly set a usage() string, then apply the
191
- // original command string as usage() for consistent behavior with
192
- // options object below.
193
- if (yargs.parsed === false) {
194
- if (shouldUpdateUsage(yargs)) {
195
- yargs.getUsageInstance().usage(
196
- usageFromParentCommandsCommandHandler(parentCommands, commandHandler),
197
- commandHandler.description
198
- )
199
- }
200
- innerArgv = innerYargs ? innerYargs._parseArgs(null, null, true, commandIndex) : yargs._parseArgs(null, null, true, commandIndex)
201
- } else {
202
- innerArgv = yargs.parsed.argv
189
+ if (!innerYargs || (typeof innerYargs._parseArgs !== 'function')) {
190
+ innerYargs = yargs
203
191
  }
204
-
205
- if (innerYargs && yargs.parsed === false) aliases = innerYargs.parsed.aliases
206
- else aliases = yargs.parsed.aliases
192
+ if (shouldUpdateUsage(innerYargs)) {
193
+ innerYargs.getUsageInstance().usage(
194
+ usageFromParentCommandsCommandHandler(parentCommands, commandHandler),
195
+ commandHandler.description
196
+ )
197
+ }
198
+ innerArgv = innerYargs._parseArgs(null, null, true, commandIndex)
199
+ aliases = innerYargs.parsed.aliases
207
200
  } else if (typeof commandHandler.builder === 'object') {
208
201
  // as a short hand, an object can instead be provided, specifying
209
202
  // the options that a command takes.
@@ -234,17 +227,28 @@ module.exports = function command (yargs, usage, validation, globalMiddleware) {
234
227
 
235
228
  if (commandHandler.handler && !yargs._hasOutput()) {
236
229
  yargs._setHasOutput()
230
+ // to simplify the parsing of positionals in commands,
231
+ // we temporarily populate '--' rather than _, with arguments
232
+ const populateDoubleDash = !!yargs.getOptions().configuration['populate--']
233
+ if (!populateDoubleDash) yargs._copyDoubleDash(innerArgv)
237
234
 
238
235
  innerArgv = applyMiddleware(innerArgv, yargs, middlewares, false)
239
-
240
- const handlerResult = isPromise(innerArgv)
241
- ? innerArgv.then(argv => commandHandler.handler(argv))
242
- : commandHandler.handler(innerArgv)
236
+ let handlerResult
237
+ if (isPromise(innerArgv)) {
238
+ handlerResult = innerArgv.then(argv => commandHandler.handler(argv))
239
+ } else {
240
+ handlerResult = commandHandler.handler(innerArgv)
241
+ }
243
242
 
244
243
  if (isPromise(handlerResult)) {
245
- handlerResult.catch(error =>
246
- yargs.getUsageInstance().fail(null, error)
247
- )
244
+ yargs.getUsageInstance().cacheHelpMessage()
245
+ handlerResult.catch(error => {
246
+ try {
247
+ yargs.getUsageInstance().fail(null, error)
248
+ } catch (err) {
249
+ // fail's throwing would cause an unhandled rejection.
250
+ }
251
+ })
248
252
  }
249
253
  }
250
254
 
@@ -348,7 +352,12 @@ module.exports = function command (yargs, usage, validation, globalMiddleware) {
348
352
  // short-circuit parse.
349
353
  if (!unparsed.length) return
350
354
 
351
- const parsed = Parser.detailed(unparsed, options)
355
+ const config = Object.assign({}, options.configuration, {
356
+ 'populate--': true
357
+ })
358
+ const parsed = Parser.detailed(unparsed, Object.assign({}, options, {
359
+ configuration: config
360
+ }))
352
361
 
353
362
  if (parsed.error) {
354
363
  yargs.getUsageInstance().fail(parsed.error.message, parsed.error)
@@ -419,18 +428,19 @@ module.exports = function command (yargs, usage, validation, globalMiddleware) {
419
428
  // the state of commands such that
420
429
  // we can apply .parse() multiple times
421
430
  // with the same yargs instance.
422
- let frozen
431
+ let frozens = []
423
432
  self.freeze = () => {
424
- frozen = {}
433
+ let frozen = {}
434
+ frozens.push(frozen)
425
435
  frozen.handlers = handlers
426
436
  frozen.aliasMap = aliasMap
427
437
  frozen.defaultCommand = defaultCommand
428
438
  }
429
439
  self.unfreeze = () => {
440
+ let frozen = frozens.pop()
430
441
  handlers = frozen.handlers
431
442
  aliasMap = frozen.aliasMap
432
443
  defaultCommand = frozen.defaultCommand
433
- frozen = undefined
434
444
  }
435
445
 
436
446
  return self
package/lib/completion.js CHANGED
@@ -8,7 +8,8 @@ module.exports = function completion (yargs, usage, command) {
8
8
  completionKey: 'get-yargs-completions'
9
9
  }
10
10
 
11
- const zshShell = process.env.SHELL && process.env.SHELL.indexOf('zsh') !== -1
11
+ const zshShell = (process.env.SHELL && process.env.SHELL.indexOf('zsh') !== -1) ||
12
+ (process.env.ZSH_NAME && process.env.ZSH_NAME.indexOf('zsh') !== -1)
12
13
  // get a list of completion commands.
13
14
  // 'args' is the array of strings from the line to be completed
14
15
  self.getCompletion = function getCompletion (args, done) {
package/lib/is-promise.js CHANGED
@@ -1,3 +1,3 @@
1
1
  module.exports = function isPromise (maybePromise) {
2
- return maybePromise instanceof Promise
2
+ return !!maybePromise && !!maybePromise.then && (typeof maybePromise.then === 'function')
3
3
  }
package/lib/middleware.js CHANGED
@@ -40,8 +40,7 @@ function applyMiddleware (argv, yargs, middlewares, beforeValidation) {
40
40
  const beforeValidationError = new Error('middleware cannot return a promise when applyBeforeValidation is true')
41
41
  return middlewares
42
42
  .reduce((accumulation, middleware) => {
43
- if (middleware.applyBeforeValidation !== beforeValidation &&
44
- !isPromise(accumulation)) {
43
+ if (middleware.applyBeforeValidation !== beforeValidation) {
45
44
  return accumulation
46
45
  }
47
46
 
package/lib/usage.js CHANGED
@@ -1,7 +1,7 @@
1
1
  'use strict'
2
2
  // this file handles outputting usage instructions,
3
3
  // failures, etc. keeps logging in one place.
4
- const decamelize = require('./decamelize')
4
+ const decamelize = require('decamelize')
5
5
  const stringWidth = require('string-width')
6
6
  const objFilter = require('./obj-filter')
7
7
  const path = require('path')
@@ -122,9 +122,9 @@ module.exports = function usage (yargs, y18n) {
122
122
  }
123
123
  self.getDescriptions = () => descriptions
124
124
 
125
- let epilog
125
+ let epilogs = []
126
126
  self.epilog = (msg) => {
127
- epilog = msg
127
+ epilogs.push(msg)
128
128
  }
129
129
 
130
130
  let wrapSet = false
@@ -148,10 +148,11 @@ module.exports = function usage (yargs, y18n) {
148
148
 
149
149
  const defaultGroup = 'Options:'
150
150
  self.help = function help () {
151
+ if (cachedHelpMessage) return cachedHelpMessage
151
152
  normalizeAliases()
152
153
 
153
154
  // handle old demanded API
154
- const base$0 = path.basename(yargs.$0)
155
+ const base$0 = yargs.customScriptName ? yargs.$0 : path.basename(yargs.$0)
155
156
  const demandedOptions = yargs.getDemandedOptions()
156
157
  const demandedCommands = yargs.getDemandedCommands()
157
158
  const groups = yargs.getGroups()
@@ -345,8 +346,8 @@ module.exports = function usage (yargs, y18n) {
345
346
  }
346
347
 
347
348
  // the usage string.
348
- if (epilog) {
349
- const e = epilog.replace(/\$0/g, base$0)
349
+ if (epilogs.length > 0) {
350
+ const e = epilogs.map(epilog => epilog.replace(/\$0/g, base$0)).join('\n')
350
351
  ui.div(`${e}\n`)
351
352
  }
352
353
 
@@ -403,6 +404,13 @@ module.exports = function usage (yargs, y18n) {
403
404
  })
404
405
  }
405
406
 
407
+ // if yargs is executing an async handler, we take a snapshot of the
408
+ // help message to display on failure:
409
+ let cachedHelpMessage
410
+ self.cacheHelpMessage = function () {
411
+ cachedHelpMessage = this.help()
412
+ }
413
+
406
414
  // given a set of keys, place any keys that are
407
415
  // ungrouped under the 'Options:' grouping.
408
416
  function addUngroupedKeys (keys, aliases, groups) {
@@ -505,35 +513,36 @@ module.exports = function usage (yargs, y18n) {
505
513
  failureOutput = false
506
514
  usages = []
507
515
  usageDisabled = false
508
- epilog = undefined
516
+ epilogs = []
509
517
  examples = []
510
518
  commands = []
511
519
  descriptions = objFilter(descriptions, (k, v) => !localLookup[k])
512
520
  return self
513
521
  }
514
522
 
515
- let frozen
523
+ let frozens = []
516
524
  self.freeze = function freeze () {
517
- frozen = {}
525
+ let frozen = {}
526
+ frozens.push(frozen)
518
527
  frozen.failMessage = failMessage
519
528
  frozen.failureOutput = failureOutput
520
529
  frozen.usages = usages
521
530
  frozen.usageDisabled = usageDisabled
522
- frozen.epilog = epilog
531
+ frozen.epilogs = epilogs
523
532
  frozen.examples = examples
524
533
  frozen.commands = commands
525
534
  frozen.descriptions = descriptions
526
535
  }
527
536
  self.unfreeze = function unfreeze () {
537
+ let frozen = frozens.pop()
528
538
  failMessage = frozen.failMessage
529
539
  failureOutput = frozen.failureOutput
530
540
  usages = frozen.usages
531
541
  usageDisabled = frozen.usageDisabled
532
- epilog = frozen.epilog
542
+ epilogs = frozen.epilogs
533
543
  examples = frozen.examples
534
544
  commands = frozen.commands
535
545
  descriptions = frozen.descriptions
536
- frozen = undefined
537
546
  }
538
547
 
539
548
  return self
package/lib/validation.js CHANGED
@@ -96,13 +96,13 @@ module.exports = function validation (yargs, usage, y18n) {
96
96
  if (specialKeys.indexOf(key) === -1 &&
97
97
  !positionalMap.hasOwnProperty(key) &&
98
98
  !yargs._getParseContext().hasOwnProperty(key) &&
99
- !aliases.hasOwnProperty(key)
99
+ !self.isValidAndSomeAliasIsNotNew(key, aliases)
100
100
  ) {
101
101
  unknown.push(key)
102
102
  }
103
103
  })
104
104
 
105
- if (commandKeys.length > 0) {
105
+ if ((currentContext.commands.length > 0) || (commandKeys.length > 0)) {
106
106
  argv._.slice(currentContext.commands.length).forEach((key) => {
107
107
  if (commandKeys.indexOf(key) === -1) {
108
108
  unknown.push(key)
@@ -120,6 +120,21 @@ module.exports = function validation (yargs, usage, y18n) {
120
120
  }
121
121
  }
122
122
 
123
+ // check for a key that is not an alias, or for which every alias is new,
124
+ // implying that it was invented by the parser, e.g., during camelization
125
+ self.isValidAndSomeAliasIsNotNew = function isValidAndSomeAliasIsNotNew (key, aliases) {
126
+ if (!aliases.hasOwnProperty(key)) {
127
+ return false
128
+ }
129
+ const newAliases = yargs.parsed.newAliases
130
+ for (let a of [key, ...aliases[key]]) {
131
+ if (!newAliases.hasOwnProperty(a) || !newAliases[key]) {
132
+ return true
133
+ }
134
+ }
135
+ return false
136
+ }
137
+
123
138
  // validate arguments limited to enumerated choices
124
139
  self.limitedChoices = function limitedChoices (argv) {
125
140
  const options = yargs.getOptions()
@@ -209,43 +224,36 @@ module.exports = function validation (yargs, usage, y18n) {
209
224
  return implied
210
225
  }
211
226
 
227
+ function keyExists (argv, val) {
228
+ // convert string '1' to number 1
229
+ let num = Number(val)
230
+ val = isNaN(num) ? val : num
231
+
232
+ if (typeof val === 'number') {
233
+ // check length of argv._
234
+ val = argv._.length >= val
235
+ } else if (val.match(/^--no-.+/)) {
236
+ // check if key/value doesn't exist
237
+ val = val.match(/^--no-(.+)/)[1]
238
+ val = !argv[val]
239
+ } else {
240
+ // check if key/value exists
241
+ val = argv[val]
242
+ }
243
+ return val
244
+ }
245
+
212
246
  self.implications = function implications (argv) {
213
247
  const implyFail = []
214
248
 
215
249
  Object.keys(implied).forEach((key) => {
216
250
  const origKey = key
217
251
  ;(implied[key] || []).forEach((value) => {
218
- let num
219
252
  let key = origKey
220
253
  const origValue = value
254
+ key = keyExists(argv, key)
255
+ value = keyExists(argv, value)
221
256
 
222
- // convert string '1' to number 1
223
- num = Number(key)
224
- key = isNaN(num) ? key : num
225
-
226
- if (typeof key === 'number') {
227
- // check length of argv._
228
- key = argv._.length >= key
229
- } else if (key.match(/^--no-.+/)) {
230
- // check if key doesn't exist
231
- key = key.match(/^--no-(.+)/)[1]
232
- key = !argv[key]
233
- } else {
234
- // check if key exists
235
- key = argv[key]
236
- }
237
-
238
- num = Number(value)
239
- value = isNaN(num) ? value : num
240
-
241
- if (typeof value === 'number') {
242
- value = argv._.length >= value
243
- } else if (value.match(/^--no-.+/)) {
244
- value = value.match(/^--no-(.+)/)[1]
245
- value = !argv[value]
246
- } else {
247
- value = argv[value]
248
- }
249
257
  if (key && !value) {
250
258
  implyFail.push(` ${origKey} -> ${origValue}`)
251
259
  }
@@ -323,18 +331,19 @@ module.exports = function validation (yargs, usage, y18n) {
323
331
  return self
324
332
  }
325
333
 
326
- let frozen
334
+ let frozens = []
327
335
  self.freeze = function freeze () {
328
- frozen = {}
336
+ let frozen = {}
337
+ frozens.push(frozen)
329
338
  frozen.implied = implied
330
339
  frozen.checks = checks
331
340
  frozen.conflicting = conflicting
332
341
  }
333
342
  self.unfreeze = function unfreeze () {
343
+ let frozen = frozens.pop()
334
344
  implied = frozen.implied
335
345
  checks = frozen.checks
336
346
  conflicting = frozen.conflicting
337
- frozen = undefined
338
347
  }
339
348
 
340
349
  return self
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yargs",
3
- "version": "13.3.2",
3
+ "version": "14.2.1",
4
4
  "description": "yargs the modern, pirate-themed, successor to optimist.",
5
5
  "main": "./index.js",
6
6
  "contributors": [
@@ -20,6 +20,7 @@
20
20
  ],
21
21
  "dependencies": {
22
22
  "cliui": "^5.0.0",
23
+ "decamelize": "^1.2.0",
23
24
  "find-up": "^3.0.0",
24
25
  "get-caller-file": "^2.0.1",
25
26
  "require-directory": "^2.1.1",
@@ -28,7 +29,7 @@
28
29
  "string-width": "^3.0.0",
29
30
  "which-module": "^2.0.0",
30
31
  "y18n": "^4.0.0",
31
- "yargs-parser": "^13.1.2"
32
+ "yargs-parser": "^15.0.0"
32
33
  },
33
34
  "devDependencies": {
34
35
  "chai": "^4.2.0",
@@ -42,7 +43,7 @@
42
43
  "nyc": "^14.1.0",
43
44
  "rimraf": "^2.6.3",
44
45
  "standard": "^12.0.1",
45
- "standard-version": "^6.0.1",
46
+ "standard-version": "^7.0.0",
46
47
  "which": "^1.3.1",
47
48
  "yargs-test-extends": "^1.0.1"
48
49
  },
package/yargs.js CHANGED
@@ -37,7 +37,8 @@ function Yargs (processArgs, cwd, parentRequire) {
37
37
 
38
38
  if (!cwd) cwd = process.cwd()
39
39
 
40
- self.scriptName = function scriptName (scriptName) {
40
+ self.scriptName = function (scriptName) {
41
+ self.customScriptName = true
41
42
  self.$0 = scriptName
42
43
  return self
43
44
  }
@@ -93,14 +94,17 @@ function Yargs (processArgs, cwd, parentRequire) {
93
94
  })
94
95
  })
95
96
 
96
- // preserve all groups not set to local.
97
- preservedGroups = Object.keys(groups).reduce((acc, groupName) => {
98
- const keys = groups[groupName].filter(key => !(key in localLookup))
99
- if (keys.length > 0) {
100
- acc[groupName] = keys
101
- }
102
- return acc
103
- }, {})
97
+ // add all groups not set to local to preserved groups
98
+ Object.assign(
99
+ preservedGroups,
100
+ Object.keys(groups).reduce((acc, groupName) => {
101
+ const keys = groups[groupName].filter(key => !(key in localLookup))
102
+ if (keys.length > 0) {
103
+ acc[groupName] = keys
104
+ }
105
+ return acc
106
+ }, {})
107
+ )
104
108
  // groups can now be reset
105
109
  groups = {}
106
110
 
@@ -144,9 +148,10 @@ function Yargs (processArgs, cwd, parentRequire) {
144
148
  self.resetOptions()
145
149
 
146
150
  // temporary hack: allow "freezing" of reset-able state for parse(msg, cb)
147
- let frozen
151
+ let frozens = []
148
152
  function freeze () {
149
- frozen = {}
153
+ let frozen = {}
154
+ frozens.push(frozen)
150
155
  frozen.options = options
151
156
  frozen.configObjects = options.configObjects.slice(0)
152
157
  frozen.exitProcess = exitProcess
@@ -160,8 +165,11 @@ function Yargs (processArgs, cwd, parentRequire) {
160
165
  frozen.exitError = exitError
161
166
  frozen.hasOutput = hasOutput
162
167
  frozen.parsed = self.parsed
168
+ frozen.parseFn = parseFn
169
+ frozen.parseContext = parseContext
163
170
  }
164
171
  function unfreeze () {
172
+ let frozen = frozens.pop()
165
173
  options = frozen.options
166
174
  options.configObjects = frozen.configObjects
167
175
  exitProcess = frozen.exitProcess
@@ -175,9 +183,8 @@ function Yargs (processArgs, cwd, parentRequire) {
175
183
  command.unfreeze()
176
184
  strict = frozen.strict
177
185
  completionCommand = frozen.completionCommand
178
- parseFn = null
179
- parseContext = null
180
- frozen = undefined
186
+ parseFn = frozen.parseFn
187
+ parseContext = frozen.parseContext
181
188
  }
182
189
 
183
190
  self.boolean = function (keys) {
@@ -231,7 +238,6 @@ function Yargs (processArgs, cwd, parentRequire) {
231
238
  function populateParserHintArray (type, keys, value) {
232
239
  keys = [].concat(keys)
233
240
  keys.forEach((key) => {
234
- key = sanitizeKey(key)
235
241
  options[type].push(key)
236
242
  })
237
243
  }
@@ -287,8 +293,8 @@ function Yargs (processArgs, cwd, parentRequire) {
287
293
 
288
294
  function populateParserHintObject (builder, isArray, type, key, value) {
289
295
  if (Array.isArray(key)) {
290
- const temp = Object.create(null)
291
296
  // an array of keys with one value ['x', 'y', 'z'], function parse () {}
297
+ const temp = {}
292
298
  key.forEach((k) => {
293
299
  temp[k] = value
294
300
  })
@@ -299,7 +305,6 @@ function Yargs (processArgs, cwd, parentRequire) {
299
305
  builder(k, key[k])
300
306
  })
301
307
  } else {
302
- key = sanitizeKey(key)
303
308
  // a single key value pair 'x', parse() {}
304
309
  if (isArray) {
305
310
  options[type][key] = (options[type][key] || []).concat(value)
@@ -309,13 +314,6 @@ function Yargs (processArgs, cwd, parentRequire) {
309
314
  }
310
315
  }
311
316
 
312
- // TODO(bcoe): in future major versions move more objects towards
313
- // Object.create(null):
314
- function sanitizeKey (key) {
315
- if (key === '__proto__') return '___proto___'
316
- return key
317
- }
318
-
319
317
  function deleteFromParserHintObject (optionKey) {
320
318
  // delete from all parsing hints:
321
319
  // boolean, array, key, alias, etc.
@@ -335,7 +333,7 @@ function Yargs (processArgs, cwd, parentRequire) {
335
333
  argsert('[object|string] [string|function] [function]', [key, msg, parseFn], arguments.length)
336
334
  // allow a config object to be provided directly.
337
335
  if (typeof key === 'object') {
338
- key = applyExtends(key, cwd)
336
+ key = applyExtends(key, cwd, self.getParserConfiguration()['deep-merge-config'])
339
337
  options.configObjects = (options.configObjects || []).concat(key)
340
338
  return self
341
339
  }
@@ -509,7 +507,7 @@ function Yargs (processArgs, cwd, parentRequire) {
509
507
 
510
508
  // If an object exists in the key, add it to options.configObjects
511
509
  if (obj[key] && typeof obj[key] === 'object') {
512
- conf = applyExtends(obj[key], rootPath || cwd)
510
+ conf = applyExtends(obj[key], rootPath || cwd, self.getParserConfiguration()['deep-merge-config'])
513
511
  options.configObjects = (options.configObjects || []).concat(conf)
514
512
  }
515
513
 
@@ -547,8 +545,14 @@ function Yargs (processArgs, cwd, parentRequire) {
547
545
  let parseContext = null
548
546
  self.parse = function parse (args, shortCircuit, _parseFn) {
549
547
  argsert('[string|array] [function|boolean|object] [function]', [args, shortCircuit, _parseFn], arguments.length)
548
+ freeze()
550
549
  if (typeof args === 'undefined') {
551
- return self._parseArgs(processArgs)
550
+ const argv = self._parseArgs(processArgs)
551
+ const tmpParsed = self.parsed
552
+ unfreeze()
553
+ // TODO: remove this compatibility hack when we release yargs@15.x:
554
+ self.parsed = tmpParsed
555
+ return argv
552
556
  }
553
557
 
554
558
  // a context object can optionally be provided, this allows
@@ -569,7 +573,6 @@ function Yargs (processArgs, cwd, parentRequire) {
569
573
  // skipping validation, etc.
570
574
  if (!shortCircuit) processArgs = args
571
575
 
572
- freeze()
573
576
  if (parseFn) exitProcess = false
574
577
 
575
578
  const parsed = self._parseArgs(args, shortCircuit)
@@ -910,7 +913,7 @@ function Yargs (processArgs, cwd, parentRequire) {
910
913
  }
911
914
 
912
915
  // register the completion command.
913
- completionCommand = cmd || 'completion'
916
+ completionCommand = cmd || completionCommand || 'completion'
914
917
  if (!desc && desc !== false) {
915
918
  desc = 'generate completion script'
916
919
  }
@@ -922,10 +925,10 @@ function Yargs (processArgs, cwd, parentRequire) {
922
925
  return self
923
926
  }
924
927
 
925
- self.showCompletionScript = function ($0) {
926
- argsert('[string]', [$0], arguments.length)
928
+ self.showCompletionScript = function ($0, cmd) {
929
+ argsert('[string] [string]', [$0, cmd], arguments.length)
927
930
  $0 = $0 || self.$0
928
- _logger.log(completion.generateCompletionScript($0, completionCommand))
931
+ _logger.log(completion.generateCompletionScript($0, cmd || completionCommand || 'completion'))
929
932
  return self
930
933
  }
931
934
 
@@ -1022,21 +1025,27 @@ function Yargs (processArgs, cwd, parentRequire) {
1022
1025
  enumerable: true
1023
1026
  })
1024
1027
 
1025
- self._parseArgs = function parseArgs (args, shortCircuit, _skipValidation, commandIndex) {
1026
- let skipValidation = !!_skipValidation
1028
+ self._parseArgs = function parseArgs (args, shortCircuit, _calledFromCommand, commandIndex) {
1029
+ let skipValidation = !!_calledFromCommand
1027
1030
  args = args || processArgs
1028
1031
 
1029
1032
  options.__ = y18n.__
1030
1033
  options.configuration = self.getParserConfiguration()
1031
-
1032
1034
  // Deprecated
1033
1035
  let pkgConfig = pkgUp()['yargs']
1034
1036
  if (pkgConfig) {
1035
- console.warn('Configuring yargs through package.json is deprecated and will be removed in the next major release, please use the JS API instead.')
1037
+ console.warn('Configuring yargs through package.json is deprecated and will be removed in a future major release, please use the JS API instead.')
1036
1038
  options.configuration = Object.assign({}, pkgConfig, options.configuration)
1037
1039
  }
1038
1040
 
1039
- const parsed = Parser.detailed(args, options)
1041
+ const populateDoubleDash = !!options.configuration['populate--']
1042
+ const config = Object.assign({}, options.configuration, {
1043
+ 'populate--': true
1044
+ })
1045
+ const parsed = Parser.detailed(args, Object.assign({}, options, {
1046
+ configuration: config
1047
+ }))
1048
+
1040
1049
  let argv = parsed.argv
1041
1050
  if (parseContext) argv = Object.assign({}, argv, parseContext)
1042
1051
  const aliases = parsed.aliases
@@ -1051,7 +1060,7 @@ function Yargs (processArgs, cwd, parentRequire) {
1051
1060
  // are two passes through the parser. If completion
1052
1061
  // is being performed short-circuit on the first pass.
1053
1062
  if (shortCircuit) {
1054
- return argv
1063
+ return (populateDoubleDash || _calledFromCommand) ? argv : self._copyDoubleDash(argv)
1055
1064
  }
1056
1065
 
1057
1066
  // if there's a handler associated with a
@@ -1084,7 +1093,8 @@ function Yargs (processArgs, cwd, parentRequire) {
1084
1093
  // commands are executed using a recursive algorithm that executes
1085
1094
  // the deepest command first; we keep track of the position in the
1086
1095
  // argv._ array that is currently being executed.
1087
- return command.runCommand(cmd, self, parsed, i + 1)
1096
+ const innerArgv = command.runCommand(cmd, self, parsed, i + 1)
1097
+ return populateDoubleDash ? innerArgv : self._copyDoubleDash(innerArgv)
1088
1098
  } else if (!firstUnknownCommand && cmd !== completionCommand) {
1089
1099
  firstUnknownCommand = cmd
1090
1100
  break
@@ -1093,7 +1103,8 @@ function Yargs (processArgs, cwd, parentRequire) {
1093
1103
 
1094
1104
  // run the default command, if defined
1095
1105
  if (command.hasDefaultCommand() && !skipDefaultCommand) {
1096
- return command.runCommand(null, self, parsed)
1106
+ const innerArgv = command.runCommand(null, self, parsed)
1107
+ return populateDoubleDash ? innerArgv : self._copyDoubleDash(innerArgv)
1097
1108
  }
1098
1109
 
1099
1110
  // recommend a command if recommendCommands() has
@@ -1110,7 +1121,8 @@ function Yargs (processArgs, cwd, parentRequire) {
1110
1121
  self.exit(0)
1111
1122
  }
1112
1123
  } else if (command.hasDefaultCommand() && !skipDefaultCommand) {
1113
- return command.runCommand(null, self, parsed)
1124
+ const innerArgv = command.runCommand(null, self, parsed)
1125
+ return populateDoubleDash ? innerArgv : self._copyDoubleDash(innerArgv)
1114
1126
  }
1115
1127
 
1116
1128
  // we must run completions first, a user might
@@ -1128,7 +1140,7 @@ function Yargs (processArgs, cwd, parentRequire) {
1128
1140
 
1129
1141
  self.exit(0)
1130
1142
  })
1131
- return argv
1143
+ return (populateDoubleDash || _calledFromCommand) ? argv : self._copyDoubleDash(argv)
1132
1144
  }
1133
1145
 
1134
1146
  // Handle 'help' and 'version' options
@@ -1172,6 +1184,16 @@ function Yargs (processArgs, cwd, parentRequire) {
1172
1184
  else throw err
1173
1185
  }
1174
1186
 
1187
+ return (populateDoubleDash || _calledFromCommand) ? argv : self._copyDoubleDash(argv)
1188
+ }
1189
+
1190
+ // to simplify the parsing of positionals in commands,
1191
+ // we temporarily populate '--' rather than _, with arguments
1192
+ // after the '--' directive. After the parse, we copy these back.
1193
+ self._copyDoubleDash = function (argv) {
1194
+ if (!argv._) return argv
1195
+ argv._.push.apply(argv._, argv['--'])
1196
+ delete argv['--']
1175
1197
  return argv
1176
1198
  }
1177
1199
 
package/lib/decamelize.js DELETED
@@ -1,32 +0,0 @@
1
- /*
2
- MIT License
3
-
4
- Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)
5
-
6
- Permission is hereby granted, free of charge, to any person obtaining a copy of
7
- this software and associated documentation files (the "Software"), to deal in
8
- the Software without restriction, including without limitation the rights to
9
- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
10
- the Software, and to permit persons to whom the Software is furnished to do so,
11
- subject to the following conditions:
12
-
13
- The above copyright notice and this permission notice shall be included in all
14
- copies or substantial portions of the Software.
15
-
16
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
18
- FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
19
- COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
20
- IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21
- CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
- */
23
- 'use strict'
24
-
25
- module.exports = (text, separator) => {
26
- separator = typeof separator === 'undefined' ? '_' : separator
27
-
28
- return text
29
- .replace(/([a-z\d])([A-Z])/g, `$1${separator}$2`)
30
- .replace(/([A-Z]+)([A-Z][a-z\d]+)/g, `$1${separator}$2`)
31
- .toLowerCase()
32
- }