Skip to content

Commit 7909cc4

Browse files
hendereabcoe
authored andcommittedSep 6, 2019
feat: add configuration option to "collect-unknown-options" (#181)
1 parent d3d9027 commit 7909cc4

File tree

3 files changed

+314
-55
lines changed

3 files changed

+314
-55
lines changed
 

‎README.md

+22
Original file line numberDiff line numberDiff line change
@@ -386,6 +386,28 @@ node example.js --test-field 1
386386
{ _: [], testField: 1 }
387387
```
388388
389+
### collect unknown options
390+
391+
* default: `false`
392+
* key: `collect-unknown-options`
393+
394+
Should unknown options be collected into `_`? An unknown option is one that is not
395+
configured in `opts`.
396+
397+
_If disabled_
398+
399+
```sh
400+
node example.js --unknown-option --known-option 2
401+
{ _: [], unknownOption: true, knownOption: 2 }
402+
```
403+
404+
_If enabled_
405+
406+
```sh
407+
node example.js --unknown-option --known-option 2
408+
{ _: ['--unknown-option'], knownOption: 2 }
409+
```
410+
389411
## Special Thanks
390412
391413
The yargs project evolves from optimist and minimist. It owes its

‎index.js

+73-2
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ function parse (args, opts) {
2626
'set-placeholder-key': false,
2727
'halt-at-non-option': false,
2828
'strip-aliased': false,
29-
'strip-dashed': false
29+
'strip-dashed': false,
30+
'collect-unknown-options': false
3031
}, opts.configuration)
3132
var defaults = opts.default || {}
3233
var configObjects = opts.configObjects || []
@@ -142,8 +143,10 @@ function parse (args, opts) {
142143
var next
143144
var value
144145

146+
if (configuration['collect-unknown-options'] && isUnknownOption(arg)) {
147+
argv._.push(arg)
145148
// -- separated by =
146-
if (arg.match(/^--.+=/) || (
149+
} else if (arg.match(/^--.+=/) || (
147150
!configuration['short-option-groups'] && arg.match(/^-.+=/)
148151
)) {
149152
// Using [\s\S] instead of . because js doesn't support the
@@ -757,6 +760,74 @@ function parse (args, opts) {
757760
return isSet
758761
}
759762

763+
function hasAnyFlag (key) {
764+
var isSet = false
765+
// XXX Switch to [].concat(...Object.values(flags)) once node.js 6 is dropped
766+
var toCheck = [].concat(...Object.keys(flags).map(k => flags[k]))
767+
768+
toCheck.forEach(function (flag) {
769+
if (flag[key]) isSet = flag[key]
770+
})
771+
772+
return isSet
773+
}
774+
775+
function hasFlagsMatching (arg, ...patterns) {
776+
var hasFlag = false
777+
var toCheck = [].concat(...patterns)
778+
toCheck.forEach(function (pattern) {
779+
var match = arg.match(pattern)
780+
if (match && hasAnyFlag(match[1])) {
781+
hasFlag = true
782+
}
783+
})
784+
return hasFlag
785+
}
786+
787+
// based on a simplified version of the short flag group parsing logic
788+
function hasAllShortFlags (arg) {
789+
// if this is a negative number, or doesn't start with a single hyphen, it's not a short flag group
790+
if (arg.match(negative) || !arg.match(/^-[^-]+/)) { return false }
791+
var hasAllFlags = true
792+
var letters = arg.slice(1).split('')
793+
var next
794+
for (var j = 0; j < letters.length; j++) {
795+
next = arg.slice(j + 2)
796+
797+
if (!hasAnyFlag(letters[j])) {
798+
hasAllFlags = false
799+
break
800+
}
801+
802+
if ((letters[j + 1] && letters[j + 1] === '=') ||
803+
next === '-' ||
804+
(/[A-Za-z]/.test(letters[j]) && /^-?\d+(\.\d*)?(e-?\d+)?$/.test(next)) ||
805+
(letters[j + 1] && letters[j + 1].match(/\W/))) {
806+
break
807+
}
808+
}
809+
return hasAllFlags
810+
}
811+
812+
function isUnknownOption (arg) {
813+
// ignore negative numbers
814+
if (arg.match(negative)) { return false }
815+
// if this is a short option group and all of them are configured, it isn't unknown
816+
if (hasAllShortFlags(arg)) { return false }
817+
// e.g. '--count=2'
818+
const flagWithEquals = /^-+([^=]+?)=[\s\S]*$/
819+
// e.g. '-a' or '--arg'
820+
const normalFlag = /^-+([^=]+?)$/
821+
// e.g. '-a-'
822+
const flagEndingInHyphen = /^-+([^=]+?)-$/
823+
// e.g. '-abc123'
824+
const flagEndingInDigits = /^-+([^=]+?)\d+$/
825+
// e.g. '-a/usr/local'
826+
const flagEndingInNonWordCharacters = /^-+([^=]+?)\W+.*$/
827+
// check the different types of flag styles, including negatedBoolean, a pattern defined near the start of the parse method
828+
return !hasFlagsMatching(arg, flagWithEquals, negatedBoolean, normalFlag, flagEndingInHyphen, flagEndingInDigits, flagEndingInNonWordCharacters)
829+
}
830+
760831
// make a best effor to pick a default value
761832
// for an option based on name and type.
762833
function defaultValue (key) {

0 commit comments

Comments
 (0)
Please sign in to comment.