@@ -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 - 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+
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