@@ -26,7 +26,8 @@ function parse (args, opts) {
26
26
'set-placeholder-key' : false ,
27
27
'halt-at-non-option' : false ,
28
28
'strip-aliased' : false ,
29
- 'strip-dashed' : false
29
+ 'strip-dashed' : false ,
30
+ 'collect-unknown-options' : false
30
31
} , opts . configuration )
31
32
var defaults = opts . default || { }
32
33
var configObjects = opts . configObjects || [ ]
@@ -142,8 +143,10 @@ function parse (args, opts) {
142
143
var next
143
144
var value
144
145
146
+ if ( configuration [ 'collect-unknown-options' ] && isUnknownOption ( arg ) ) {
147
+ argv . _ . push ( arg )
145
148
// -- separated by =
146
- if ( arg . match ( / ^ - - .+ = / ) || (
149
+ } else if ( arg . match ( / ^ - - .+ = / ) || (
147
150
! configuration [ 'short-option-groups' ] && arg . match ( / ^ - .+ = / )
148
151
) ) {
149
152
// Using [\s\S] instead of . because js doesn't support the
@@ -757,6 +760,74 @@ function parse (args, opts) {
757
760
return isSet
758
761
}
759
762
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 - Z a - 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
+
760
831
// make a best effor to pick a default value
761
832
// for an option based on name and type.
762
833
function defaultValue ( key ) {
0 commit comments