@@ -16,12 +16,11 @@ const colorsUtil = require("./colors");
16
16
// S | string array
17
17
18
18
/** Parses the specified command line arguments according to the given configuration. */
19
- function parse ( argv , config ) {
19
+ function parse ( argv , config , propagateDefaults = true ) {
20
20
var options = { } ;
21
21
var unknown = [ ] ;
22
22
var args = [ ] ;
23
23
var trailing = [ ] ;
24
- var provided = new Set ( ) ;
25
24
26
25
// make an alias map and initialize defaults
27
26
var aliases = { } ;
@@ -54,13 +53,15 @@ function parse(argv, config) {
54
53
else { args . push ( arg ) ; continue ; } // argument
55
54
}
56
55
if ( option ) {
57
- if ( option . type == null || option . type === "b" ) {
58
- options [ key ] = true ; // flag
59
- provided . add ( key ) ;
56
+ if ( option . value ) {
57
+ // alias setting fixed values
58
+ Object . keys ( option . value ) . forEach ( k => options [ k ] = option . value [ k ] ) ;
59
+ } else if ( option . type == null || option . type === "b" ) {
60
+ // boolean flag not taking a value
61
+ options [ key ] = true ;
60
62
} else {
61
- // the argument was provided
62
- if ( i + 1 < argv . length && argv [ i + 1 ] . charCodeAt ( 0 ) != 45 ) { // present
63
- provided . add ( key ) ;
63
+ if ( i + 1 < argv . length && argv [ i + 1 ] . charCodeAt ( 0 ) != 45 ) {
64
+ // non-boolean with given value
64
65
switch ( option . type ) {
65
66
case "i" : options [ key ] = parseInt ( argv [ ++ i ] , 10 ) ; break ;
66
67
case "I" : options [ key ] = ( options [ key ] || [ ] ) . concat ( parseInt ( argv [ ++ i ] , 10 ) ) ; break ;
@@ -70,7 +71,8 @@ function parse(argv, config) {
70
71
case "S" : options [ key ] = ( options [ key ] || [ ] ) . concat ( argv [ ++ i ] . split ( "," ) ) ; break ;
71
72
default : unknown . push ( arg ) ; -- i ;
72
73
}
73
- } else { // omitted
74
+ } else {
75
+ // non-boolean with omitted value
74
76
switch ( option . type ) {
75
77
case "i" :
76
78
case "f" : options [ key ] = option . default || 0 ; break ;
@@ -82,12 +84,12 @@ function parse(argv, config) {
82
84
}
83
85
}
84
86
}
85
- if ( option . value ) Object . keys ( option . value ) . forEach ( k => options [ k ] = option . value [ k ] ) ;
86
87
} else unknown . push ( arg ) ;
87
88
}
88
89
while ( i < k ) trailing . push ( argv [ i ++ ] ) ; // trailing
90
+ if ( propagateDefaults ) addDefaults ( config , options ) ;
89
91
90
- return { options, unknown, arguments : args , trailing, provided } ;
92
+ return { options, unknown, arguments : args , trailing } ;
91
93
}
92
94
93
95
exports . parse = parse ;
@@ -138,3 +140,93 @@ function help(config, options) {
138
140
}
139
141
140
142
exports . help = help ;
143
+
144
+ /** Sanitizes an option value to be a valid value of the option's type. */
145
+ function sanitizeValue ( value , type ) {
146
+ if ( value != null ) {
147
+ switch ( type ) {
148
+ case undefined :
149
+ case "b" : return Boolean ( value ) ;
150
+ case "i" : return Math . trunc ( value ) || 0 ;
151
+ case "f" : return Number ( value ) || 0 ;
152
+ case "s" : return String ( value ) ;
153
+ case "I" : {
154
+ if ( ! Array . isArray ( value ) ) value = [ value ] ;
155
+ return value . map ( v => Math . trunc ( v ) || 0 ) ;
156
+ }
157
+ case "F" : {
158
+ if ( ! Array . isArray ( value ) ) value = [ value ] ;
159
+ return value . map ( v => Number ( v ) || 0 ) ;
160
+ }
161
+ case "S" : {
162
+ if ( ! Array . isArray ( value ) ) value = [ value ] ;
163
+ return value . map ( String ) ;
164
+ }
165
+ }
166
+ }
167
+ return undefined ;
168
+ }
169
+
170
+ /** Merges two sets of options into one, preferring the current over the parent set. */
171
+ function merge ( config , currentOptions , parentOptions ) {
172
+ const mergedOptions = { } ;
173
+ for ( const [ key , { type, mutuallyExclusive } ] of Object . entries ( config ) ) {
174
+ let currentValue = sanitizeValue ( currentOptions [ key ] , type ) ;
175
+ let parentValue = sanitizeValue ( parentOptions [ key ] , type ) ;
176
+ if ( currentValue == null ) {
177
+ if ( parentValue != null ) {
178
+ // only parent value present
179
+ if ( Array . isArray ( parentValue ) ) {
180
+ let exclude ;
181
+ if ( mutuallyExclusive != null && ( exclude = currentOptions [ mutuallyExclusive ] ) ) {
182
+ mergedOptions [ key ] = parentValue . filter ( value => ! exclude . includes ( value ) ) ;
183
+ } else {
184
+ mergedOptions [ key ] = parentValue . slice ( ) ;
185
+ }
186
+ } else {
187
+ mergedOptions [ key ] = parentValue ;
188
+ }
189
+ }
190
+ } else if ( parentValue == null ) {
191
+ // only current value present
192
+ if ( Array . isArray ( currentValue ) ) {
193
+ mergedOptions [ key ] = currentValue . slice ( ) ;
194
+ } else {
195
+ mergedOptions [ key ] = currentValue ;
196
+ }
197
+ } else {
198
+ // both current and parent values present
199
+ if ( Array . isArray ( currentValue ) ) {
200
+ let exclude ;
201
+ if ( mutuallyExclusive != null && ( exclude = currentOptions [ mutuallyExclusive ] ) ) {
202
+ mergedOptions [ key ] = [
203
+ ...currentValue ,
204
+ ...parentValue . filter ( value => ! currentValue . includes ( value ) && ! exclude . includes ( value ) )
205
+ ] ;
206
+ } else {
207
+ mergedOptions [ key ] = [
208
+ ...currentValue ,
209
+ ...parentValue . filter ( value => ! currentValue . includes ( value ) ) // dedup
210
+ ] ;
211
+ }
212
+ } else {
213
+ mergedOptions [ key ] = currentValue ;
214
+ }
215
+ }
216
+ }
217
+ return mergedOptions ;
218
+ }
219
+
220
+ exports . merge = merge ;
221
+
222
+ /** Populates default values on a parsed options result. */
223
+ function addDefaults ( config , options ) {
224
+ for ( const [ key , { default : defaultValue } ] of Object . entries ( config ) ) {
225
+ if ( options [ key ] == null && defaultValue != null ) {
226
+ options [ key ] = defaultValue ;
227
+ }
228
+ }
229
+ return options ;
230
+ }
231
+
232
+ exports . addDefaults = addDefaults ;
0 commit comments