1
- var abbrev = require ( 'abbrev' )
1
+ const abbrev = require ( 'abbrev' )
2
2
const debug = require ( './debug' )
3
3
const defaultTypeDefs = require ( './type-defs' )
4
4
@@ -17,11 +17,22 @@ const getType = (k, { types, dynamicTypes }) => {
17
17
return [ hasType , type ]
18
18
}
19
19
20
- function nopt ( args , { types, dynamicTypes, shorthands, typeDefs, invalidHandler, typeDefault } ) {
20
+ const isTypeDef = ( type , def ) => def && type === def
21
+ const hasTypeDef = ( type , def ) => def && type . indexOf ( def ) !== - 1
22
+ const doesNotHaveTypeDef = ( type , def ) => def && ! hasTypeDef ( type , def )
23
+
24
+ function nopt ( args , {
25
+ types,
26
+ shorthands,
27
+ typeDefs,
28
+ invalidHandler,
29
+ typeDefault,
30
+ dynamicTypes,
31
+ } = { } ) {
21
32
debug ( types , shorthands , args , typeDefs )
22
33
23
- var data = { }
24
- var argv = {
34
+ const data = { }
35
+ const argv = {
25
36
remain : [ ] ,
26
37
cooked : args ,
27
38
original : args . slice ( 0 ) ,
@@ -43,35 +54,48 @@ function nopt (args, { types, dynamicTypes, shorthands, typeDefs, invalidHandler
43
54
return data
44
55
}
45
56
46
- function clean ( data , { types, dynamicTypes, typeDefs, invalidHandler, typeDefault } ) {
47
- const StringType = typeDefs . String . type
48
- const NumberType = typeDefs . Number . type
49
- const ArrayType = typeDefs . Array . type
50
- const BooleanType = typeDefs . Boolean . type
51
- const DateType = typeDefs . Date . type
57
+ function clean ( data , {
58
+ types = { } ,
59
+ typeDefs = { } ,
60
+ dynamicTypes,
61
+ invalidHandler,
62
+ typeDefault,
63
+ } = { } ) {
64
+ const StringType = typeDefs . String ?. type
65
+ const NumberType = typeDefs . Number ?. type
66
+ const ArrayType = typeDefs . Array ?. type
67
+ const BooleanType = typeDefs . Boolean ?. type
68
+ const DateType = typeDefs . Date ?. type
52
69
53
70
const hasTypeDefault = typeof typeDefault !== 'undefined'
54
71
if ( ! hasTypeDefault ) {
55
- typeDefault = [ false , true , null , StringType , ArrayType ]
72
+ typeDefault = [ false , true , null ]
73
+ if ( StringType ) {
74
+ typeDefault . push ( StringType )
75
+ }
76
+ if ( ArrayType ) {
77
+ typeDefault . push ( ArrayType )
78
+ }
56
79
}
57
80
58
- var remove = { }
81
+ const remove = { }
59
82
60
- Object . keys ( data ) . forEach ( function ( k ) {
83
+ Object . keys ( data ) . forEach ( ( k ) => {
61
84
if ( k === 'argv' ) {
62
85
return
63
86
}
64
- var val = data [ k ]
65
- var isArray = Array . isArray ( val )
87
+ let val = data [ k ]
88
+ debug ( 'val=%j' , val )
89
+ const isArray = Array . isArray ( val )
66
90
let [ hasType , rawType ] = getType ( k , { types, dynamicTypes } )
67
- var type = rawType
91
+ let type = rawType
68
92
if ( ! isArray ) {
69
93
val = [ val ]
70
94
}
71
95
if ( ! type ) {
72
96
type = typeDefault
73
97
}
74
- if ( type === ArrayType ) {
98
+ if ( isTypeDef ( type , ArrayType ) ) {
75
99
type = typeDefault . concat ( ArrayType )
76
100
}
77
101
if ( ! Array . isArray ( type ) ) {
@@ -80,22 +104,22 @@ function clean (data, { types, dynamicTypes, typeDefs, invalidHandler, typeDefau
80
104
81
105
debug ( 'val=%j' , val )
82
106
debug ( 'types=' , type )
83
- val = val . map ( function ( v ) {
107
+ val = val . map ( ( v ) => {
84
108
// if it's an unknown value, then parse false/true/null/numbers/dates
85
109
if ( typeof v === 'string' ) {
86
110
debug ( 'string %j' , v )
87
111
v = v . trim ( )
88
112
if ( ( v === 'null' && ~ type . indexOf ( null ) )
89
113
|| ( v === 'true' &&
90
- ( ~ type . indexOf ( true ) || ~ type . indexOf ( BooleanType ) ) )
114
+ ( ~ type . indexOf ( true ) || hasTypeDef ( type , BooleanType ) ) )
91
115
|| ( v === 'false' &&
92
- ( ~ type . indexOf ( false ) || ~ type . indexOf ( BooleanType ) ) ) ) {
116
+ ( ~ type . indexOf ( false ) || hasTypeDef ( type , BooleanType ) ) ) ) {
93
117
v = JSON . parse ( v )
94
118
debug ( 'jsonable %j' , v )
95
- } else if ( ~ type . indexOf ( NumberType ) && ! isNaN ( v ) ) {
119
+ } else if ( hasTypeDef ( type , NumberType ) && ! isNaN ( v ) ) {
96
120
debug ( 'convert to number' , v )
97
121
v = + v
98
- } else if ( ~ type . indexOf ( DateType ) && ! isNaN ( Date . parse ( v ) ) ) {
122
+ } else if ( hasTypeDef ( type , DateType ) && ! isNaN ( Date . parse ( v ) ) ) {
99
123
debug ( 'convert to date' , v )
100
124
v = new Date ( v )
101
125
}
@@ -114,11 +138,11 @@ function clean (data, { types, dynamicTypes, typeDefs, invalidHandler, typeDefau
114
138
115
139
// allow `--no-blah` to set 'blah' to null if null is allowed
116
140
if ( v === false && ~ type . indexOf ( null ) &&
117
- ! ( ~ type . indexOf ( false ) || ~ type . indexOf ( BooleanType ) ) ) {
141
+ ! ( ~ type . indexOf ( false ) || hasTypeDef ( type , BooleanType ) ) ) {
118
142
v = null
119
143
}
120
144
121
- var d = { }
145
+ const d = { }
122
146
d [ k ] = v
123
147
debug ( 'prevalidated val' , d , v , rawType )
124
148
if ( ! validate ( d , k , v , rawType , { typeDefs } ) ) {
@@ -131,13 +155,11 @@ function clean (data, { types, dynamicTypes, typeDefs, invalidHandler, typeDefau
131
155
}
132
156
debug ( 'validated v' , d , v , rawType )
133
157
return d [ k ]
134
- } ) . filter ( function ( v ) {
135
- return v !== remove
136
- } )
158
+ } ) . filter ( ( v ) => v !== remove )
137
159
138
160
// if we allow Array specifically, then an empty array is how we
139
161
// express 'no value here', not null. Allow it.
140
- if ( ! val . length && type . indexOf ( ArrayType ) === - 1 ) {
162
+ if ( ! val . length && doesNotHaveTypeDef ( type , ArrayType ) ) {
141
163
debug ( 'VAL HAS NO LENGTH, DELETE IT' , val , k , type . indexOf ( ArrayType ) )
142
164
delete data [ k ]
143
165
} else if ( isArray ) {
@@ -151,12 +173,12 @@ function clean (data, { types, dynamicTypes, typeDefs, invalidHandler, typeDefau
151
173
} )
152
174
}
153
175
154
- function validate ( data , k , val , type , { typeDefs } ) {
155
- const ArrayType = typeDefs . Array . type
176
+ function validate ( data , k , val , type , { typeDefs } = { } ) {
177
+ const ArrayType = typeDefs ? .Array ? .type
156
178
// arrays are lists of types.
157
179
if ( Array . isArray ( type ) ) {
158
180
for ( let i = 0 , l = type . length ; i < l ; i ++ ) {
159
- if ( type [ i ] === ArrayType ) {
181
+ if ( isTypeDef ( type [ i ] , ArrayType ) ) {
160
182
continue
161
183
}
162
184
if ( validate ( data , k , val , type [ i ] , { typeDefs } ) ) {
@@ -168,7 +190,7 @@ function validate (data, k, val, type, { typeDefs }) {
168
190
}
169
191
170
192
// an array of anything?
171
- if ( type === ArrayType ) {
193
+ if ( isTypeDef ( type , ArrayType ) ) {
172
194
return true
173
195
}
174
196
@@ -193,17 +215,17 @@ function validate (data, k, val, type, { typeDefs }) {
193
215
}
194
216
195
217
// now go through the list of typeDefs, validate against each one.
196
- var ok = false
197
- var types = Object . keys ( typeDefs )
218
+ let ok = false
219
+ const types = Object . keys ( typeDefs )
198
220
for ( let i = 0 , l = types . length ; i < l ; i ++ ) {
199
221
debug ( 'test type %j %j %j' , k , val , types [ i ] )
200
- var t = typeDefs [ types [ i ] ]
222
+ const t = typeDefs [ types [ i ] ]
201
223
if ( t && (
202
224
( type && type . name && t . type && t . type . name ) ?
203
225
( type . name === t . type . name ) :
204
226
( type === t . type )
205
227
) ) {
206
- var d = { }
228
+ const d = { }
207
229
ok = t . validate ( d , k , val ) !== false
208
230
val = d [ k ]
209
231
if ( ok ) {
@@ -220,20 +242,25 @@ function validate (data, k, val, type, { typeDefs }) {
220
242
return ok
221
243
}
222
244
223
- function parse ( args , data , remain , { typeDefs, types, dynamicTypes, shorthands } ) {
224
- const StringType = typeDefs . String . type
225
- const NumberType = typeDefs . String . type
226
- const ArrayType = typeDefs . Array . type
227
- const BooleanType = typeDefs . Boolean . type
245
+ function parse ( args , data , remain , {
246
+ types = { } ,
247
+ typeDefs = { } ,
248
+ shorthands = { } ,
249
+ dynamicTypes,
250
+ } = { } ) {
251
+ const StringType = typeDefs . String ?. type
252
+ const NumberType = typeDefs . Number ?. type
253
+ const ArrayType = typeDefs . Array ?. type
254
+ const BooleanType = typeDefs . Boolean ?. type
228
255
229
256
debug ( 'parse' , args , data , remain )
230
257
231
- var abbrevs = abbrev ( Object . keys ( types ) )
258
+ const abbrevs = abbrev ( Object . keys ( types ) )
232
259
debug ( 'abbrevs=%j' , abbrevs )
233
- var shortAbbr = abbrev ( Object . keys ( shorthands ) )
260
+ const shortAbbr = abbrev ( Object . keys ( shorthands ) )
234
261
235
- for ( var i = 0 ; i < args . length ; i ++ ) {
236
- var arg = args [ i ]
262
+ for ( let i = 0 ; i < args . length ; i ++ ) {
263
+ let arg = args [ i ]
237
264
debug ( 'arg' , arg )
238
265
239
266
if ( arg . match ( / ^ - { 2 , } $ / ) ) {
@@ -243,30 +270,29 @@ function parse (args, data, remain, { typeDefs, types, dynamicTypes, shorthands
243
270
args [ i ] = '--'
244
271
break
245
272
}
246
- var hadEq = false
273
+ let hadEq = false
247
274
if ( arg . charAt ( 0 ) === '-' && arg . length > 1 ) {
248
- var at = arg . indexOf ( '=' )
275
+ const at = arg . indexOf ( '=' )
249
276
if ( at > - 1 ) {
250
277
hadEq = true
251
- var v = arg . slice ( at + 1 )
278
+ const v = arg . slice ( at + 1 )
252
279
arg = arg . slice ( 0 , at )
253
280
args . splice ( i , 1 , arg , v )
254
281
}
255
282
256
283
// see if it's a shorthand
257
284
// if so, splice and back up to re-parse it.
258
- var shRes = resolveShort ( arg , shortAbbr , abbrevs , { shorthands } )
285
+ const shRes = resolveShort ( arg , shortAbbr , abbrevs , { shorthands } )
259
286
debug ( 'arg=%j shRes=%j' , arg , shRes )
260
287
if ( shRes ) {
261
- debug ( arg , shRes )
262
288
args . splice . apply ( args , [ i , 1 ] . concat ( shRes ) )
263
289
if ( arg !== shRes [ 0 ] ) {
264
290
i --
265
291
continue
266
292
}
267
293
}
268
294
arg = arg . replace ( / ^ - + / , '' )
269
- var no = null
295
+ let no = null
270
296
while ( arg . toLowerCase ( ) . indexOf ( 'no-' ) === 0 ) {
271
297
no = ! no
272
298
arg = arg . slice ( 3 )
@@ -276,15 +302,15 @@ function parse (args, data, remain, { typeDefs, types, dynamicTypes, shorthands
276
302
arg = abbrevs [ arg ]
277
303
}
278
304
279
- var [ hasType , argType ] = getType ( arg , { types, dynamicTypes } )
280
- var isTypeArray = Array . isArray ( argType )
305
+ let [ hasType , argType ] = getType ( arg , { types, dynamicTypes } )
306
+ let isTypeArray = Array . isArray ( argType )
281
307
if ( isTypeArray && argType . length === 1 ) {
282
308
isTypeArray = false
283
309
argType = argType [ 0 ]
284
310
}
285
311
286
- var isArray = argType === ArrayType ||
287
- isTypeArray && argType . indexOf ( ArrayType ) !== - 1
312
+ let isArray = isTypeDef ( argType , ArrayType ) ||
313
+ isTypeArray && hasTypeDef ( argType , ArrayType )
288
314
289
315
// allow unknown things to be arrays if specified multiple times.
290
316
if ( ! hasType && hasOwn ( data , arg ) ) {
@@ -294,12 +320,12 @@ function parse (args, data, remain, { typeDefs, types, dynamicTypes, shorthands
294
320
isArray = true
295
321
}
296
322
297
- var val
298
- var la = args [ i + 1 ]
323
+ let val
324
+ let la = args [ i + 1 ]
299
325
300
- var isBool = typeof no === 'boolean' ||
301
- argType === BooleanType ||
302
- isTypeArray && argType . indexOf ( BooleanType ) !== - 1 ||
326
+ const isBool = typeof no === 'boolean' ||
327
+ isTypeDef ( argType , BooleanType ) ||
328
+ isTypeArray && hasTypeDef ( argType , BooleanType ) ||
303
329
( typeof argType === 'undefined' && ! hadEq ) ||
304
330
( la === 'false' &&
305
331
( argType === null ||
@@ -330,11 +356,11 @@ function parse (args, data, remain, { typeDefs, types, dynamicTypes, shorthands
330
356
i ++
331
357
} else if ( ! la . match ( / ^ - { 2 , } [ ^ - ] / ) &&
332
358
! isNaN ( la ) &&
333
- ~ argType . indexOf ( NumberType ) ) {
359
+ hasTypeDef ( argType , NumberType ) ) {
334
360
// number
335
361
val = + la
336
362
i ++
337
- } else if ( ! la . match ( / ^ - [ ^ - ] / ) && ~ argType . indexOf ( StringType ) ) {
363
+ } else if ( ! la . match ( / ^ - [ ^ - ] / ) && hasTypeDef ( argType , StringType ) ) {
338
364
// string
339
365
val = la
340
366
i ++
@@ -350,7 +376,7 @@ function parse (args, data, remain, { typeDefs, types, dynamicTypes, shorthands
350
376
continue
351
377
}
352
378
353
- if ( argType === StringType ) {
379
+ if ( isTypeDef ( argType , StringType ) ) {
354
380
if ( la === undefined ) {
355
381
la = ''
356
382
} else if ( la . match ( / ^ - { 1 , 2 } [ ^ - ] + / ) ) {
@@ -378,7 +404,26 @@ function parse (args, data, remain, { typeDefs, types, dynamicTypes, shorthands
378
404
}
379
405
}
380
406
381
- function resolveShort ( arg , shortAbbr , abbrevs , { shorthands } ) {
407
+ const SINGLES = Symbol ( 'singles' )
408
+ const singleCharacters = ( arg , shorthands ) => {
409
+ let singles = shorthands [ SINGLES ]
410
+ if ( ! singles ) {
411
+ singles = Object . keys ( shorthands ) . filter ( ( s ) => s . length === 1 ) . reduce ( ( l , r ) => {
412
+ l [ r ] = true
413
+ return l
414
+ } , { } )
415
+ shorthands [ SINGLES ] = singles
416
+ debug ( 'shorthand singles' , singles )
417
+ }
418
+ const chrs = arg . split ( '' ) . filter ( ( c ) => singles [ c ] )
419
+ return chrs . join ( '' ) === arg ? chrs : null
420
+ }
421
+
422
+ function resolveShort ( arg , ...rest ) {
423
+ const { types = { } , shorthands = { } } = rest . length ? rest . pop ( ) : { }
424
+ const shortAbbr = rest [ 0 ] ?? abbrev ( Object . keys ( shorthands ) )
425
+ const abbrevs = rest [ 1 ] ?? abbrev ( Object . keys ( types ) )
426
+
382
427
// handle single-char shorthands glommed together, like
383
428
// npm ls -glp, but only if there is one dash, and only if
384
429
// all of the chars are single-char shorthands, and it's
@@ -401,28 +446,9 @@ function resolveShort (arg, shortAbbr, abbrevs, { shorthands }) {
401
446
}
402
447
403
448
// first check to see if this arg is a set of single-char shorthands
404
- var singles = shorthands . ___singles
405
- if ( ! singles ) {
406
- singles = Object . keys ( shorthands ) . filter ( function ( s ) {
407
- return s . length === 1
408
- } ) . reduce ( function ( l , r ) {
409
- l [ r ] = true
410
- return l
411
- } , { } )
412
- shorthands . ___singles = singles
413
- debug ( 'shorthand singles' , singles )
414
- }
415
-
416
- var chrs = arg . split ( '' ) . filter ( function ( c ) {
417
- return singles [ c ]
418
- } )
419
-
420
- if ( chrs . join ( '' ) === arg ) {
421
- return chrs . map ( function ( c ) {
422
- return shorthands [ c ]
423
- } ) . reduce ( function ( l , r ) {
424
- return l . concat ( r )
425
- } , [ ] )
449
+ const chrs = singleCharacters ( arg , shorthands )
450
+ if ( chrs ) {
451
+ return chrs . map ( ( c ) => shorthands [ c ] ) . reduce ( ( l , r ) => l . concat ( r ) , [ ] )
426
452
}
427
453
428
454
// if it's an arg abbrev, and not a literal shorthand, then prefer the arg
0 commit comments