Skip to content

Commit 7f5b507

Browse files
authored
fix: 100 test coverage (#126)
1 parent 008c672 commit 7f5b507

File tree

9 files changed

+420
-165
lines changed

9 files changed

+420
-165
lines changed

lib/debug.js

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
/* istanbul ignore next */
22
module.exports = process.env.DEBUG_NOPT || process.env.NOPT_DEBUG
3-
? function () {
4-
console.error.apply(console, arguments)
5-
}
6-
: function () {}
3+
? (...a) => console.error(...a)
4+
: () => {}

lib/nopt-lib.js

Lines changed: 112 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
var abbrev = require('abbrev')
1+
const abbrev = require('abbrev')
22
const debug = require('./debug')
33
const defaultTypeDefs = require('./type-defs')
44

@@ -17,11 +17,22 @@ const getType = (k, { types, dynamicTypes }) => {
1717
return [hasType, type]
1818
}
1919

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+
} = {}) {
2132
debug(types, shorthands, args, typeDefs)
2233

23-
var data = {}
24-
var argv = {
34+
const data = {}
35+
const argv = {
2536
remain: [],
2637
cooked: args,
2738
original: args.slice(0),
@@ -43,35 +54,48 @@ function nopt (args, { types, dynamicTypes, shorthands, typeDefs, invalidHandler
4354
return data
4455
}
4556

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
5269

5370
const hasTypeDefault = typeof typeDefault !== 'undefined'
5471
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+
}
5679
}
5780

58-
var remove = {}
81+
const remove = {}
5982

60-
Object.keys(data).forEach(function (k) {
83+
Object.keys(data).forEach((k) => {
6184
if (k === 'argv') {
6285
return
6386
}
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)
6690
let [hasType, rawType] = getType(k, { types, dynamicTypes })
67-
var type = rawType
91+
let type = rawType
6892
if (!isArray) {
6993
val = [val]
7094
}
7195
if (!type) {
7296
type = typeDefault
7397
}
74-
if (type === ArrayType) {
98+
if (isTypeDef(type, ArrayType)) {
7599
type = typeDefault.concat(ArrayType)
76100
}
77101
if (!Array.isArray(type)) {
@@ -80,22 +104,22 @@ function clean (data, { types, dynamicTypes, typeDefs, invalidHandler, typeDefau
80104

81105
debug('val=%j', val)
82106
debug('types=', type)
83-
val = val.map(function (v) {
107+
val = val.map((v) => {
84108
// if it's an unknown value, then parse false/true/null/numbers/dates
85109
if (typeof v === 'string') {
86110
debug('string %j', v)
87111
v = v.trim()
88112
if ((v === 'null' && ~type.indexOf(null))
89113
|| (v === 'true' &&
90-
(~type.indexOf(true) || ~type.indexOf(BooleanType)))
114+
(~type.indexOf(true) || hasTypeDef(type, BooleanType)))
91115
|| (v === 'false' &&
92-
(~type.indexOf(false) || ~type.indexOf(BooleanType)))) {
116+
(~type.indexOf(false) || hasTypeDef(type, BooleanType)))) {
93117
v = JSON.parse(v)
94118
debug('jsonable %j', v)
95-
} else if (~type.indexOf(NumberType) && !isNaN(v)) {
119+
} else if (hasTypeDef(type, NumberType) && !isNaN(v)) {
96120
debug('convert to number', v)
97121
v = +v
98-
} else if (~type.indexOf(DateType) && !isNaN(Date.parse(v))) {
122+
} else if (hasTypeDef(type, DateType) && !isNaN(Date.parse(v))) {
99123
debug('convert to date', v)
100124
v = new Date(v)
101125
}
@@ -114,11 +138,11 @@ function clean (data, { types, dynamicTypes, typeDefs, invalidHandler, typeDefau
114138

115139
// allow `--no-blah` to set 'blah' to null if null is allowed
116140
if (v === false && ~type.indexOf(null) &&
117-
!(~type.indexOf(false) || ~type.indexOf(BooleanType))) {
141+
!(~type.indexOf(false) || hasTypeDef(type, BooleanType))) {
118142
v = null
119143
}
120144

121-
var d = {}
145+
const d = {}
122146
d[k] = v
123147
debug('prevalidated val', d, v, rawType)
124148
if (!validate(d, k, v, rawType, { typeDefs })) {
@@ -131,13 +155,11 @@ function clean (data, { types, dynamicTypes, typeDefs, invalidHandler, typeDefau
131155
}
132156
debug('validated v', d, v, rawType)
133157
return d[k]
134-
}).filter(function (v) {
135-
return v !== remove
136-
})
158+
}).filter((v) => v !== remove)
137159

138160
// if we allow Array specifically, then an empty array is how we
139161
// express 'no value here', not null. Allow it.
140-
if (!val.length && type.indexOf(ArrayType) === -1) {
162+
if (!val.length && doesNotHaveTypeDef(type, ArrayType)) {
141163
debug('VAL HAS NO LENGTH, DELETE IT', val, k, type.indexOf(ArrayType))
142164
delete data[k]
143165
} else if (isArray) {
@@ -151,12 +173,12 @@ function clean (data, { types, dynamicTypes, typeDefs, invalidHandler, typeDefau
151173
})
152174
}
153175

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
156178
// arrays are lists of types.
157179
if (Array.isArray(type)) {
158180
for (let i = 0, l = type.length; i < l; i++) {
159-
if (type[i] === ArrayType) {
181+
if (isTypeDef(type[i], ArrayType)) {
160182
continue
161183
}
162184
if (validate(data, k, val, type[i], { typeDefs })) {
@@ -168,7 +190,7 @@ function validate (data, k, val, type, { typeDefs }) {
168190
}
169191

170192
// an array of anything?
171-
if (type === ArrayType) {
193+
if (isTypeDef(type, ArrayType)) {
172194
return true
173195
}
174196

@@ -193,17 +215,17 @@ function validate (data, k, val, type, { typeDefs }) {
193215
}
194216

195217
// 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)
198220
for (let i = 0, l = types.length; i < l; i++) {
199221
debug('test type %j %j %j', k, val, types[i])
200-
var t = typeDefs[types[i]]
222+
const t = typeDefs[types[i]]
201223
if (t && (
202224
(type && type.name && t.type && t.type.name) ?
203225
(type.name === t.type.name) :
204226
(type === t.type)
205227
)) {
206-
var d = {}
228+
const d = {}
207229
ok = t.validate(d, k, val) !== false
208230
val = d[k]
209231
if (ok) {
@@ -220,20 +242,25 @@ function validate (data, k, val, type, { typeDefs }) {
220242
return ok
221243
}
222244

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
228255

229256
debug('parse', args, data, remain)
230257

231-
var abbrevs = abbrev(Object.keys(types))
258+
const abbrevs = abbrev(Object.keys(types))
232259
debug('abbrevs=%j', abbrevs)
233-
var shortAbbr = abbrev(Object.keys(shorthands))
260+
const shortAbbr = abbrev(Object.keys(shorthands))
234261

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]
237264
debug('arg', arg)
238265

239266
if (arg.match(/^-{2,}$/)) {
@@ -243,30 +270,29 @@ function parse (args, data, remain, { typeDefs, types, dynamicTypes, shorthands
243270
args[i] = '--'
244271
break
245272
}
246-
var hadEq = false
273+
let hadEq = false
247274
if (arg.charAt(0) === '-' && arg.length > 1) {
248-
var at = arg.indexOf('=')
275+
const at = arg.indexOf('=')
249276
if (at > -1) {
250277
hadEq = true
251-
var v = arg.slice(at + 1)
278+
const v = arg.slice(at + 1)
252279
arg = arg.slice(0, at)
253280
args.splice(i, 1, arg, v)
254281
}
255282

256283
// see if it's a shorthand
257284
// 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 })
259286
debug('arg=%j shRes=%j', arg, shRes)
260287
if (shRes) {
261-
debug(arg, shRes)
262288
args.splice.apply(args, [i, 1].concat(shRes))
263289
if (arg !== shRes[0]) {
264290
i--
265291
continue
266292
}
267293
}
268294
arg = arg.replace(/^-+/, '')
269-
var no = null
295+
let no = null
270296
while (arg.toLowerCase().indexOf('no-') === 0) {
271297
no = !no
272298
arg = arg.slice(3)
@@ -276,15 +302,15 @@ function parse (args, data, remain, { typeDefs, types, dynamicTypes, shorthands
276302
arg = abbrevs[arg]
277303
}
278304

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)
281307
if (isTypeArray && argType.length === 1) {
282308
isTypeArray = false
283309
argType = argType[0]
284310
}
285311

286-
var isArray = argType === ArrayType ||
287-
isTypeArray && argType.indexOf(ArrayType) !== -1
312+
let isArray = isTypeDef(argType, ArrayType) ||
313+
isTypeArray && hasTypeDef(argType, ArrayType)
288314

289315
// allow unknown things to be arrays if specified multiple times.
290316
if (!hasType && hasOwn(data, arg)) {
@@ -294,12 +320,12 @@ function parse (args, data, remain, { typeDefs, types, dynamicTypes, shorthands
294320
isArray = true
295321
}
296322

297-
var val
298-
var la = args[i + 1]
323+
let val
324+
let la = args[i + 1]
299325

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) ||
303329
(typeof argType === 'undefined' && !hadEq) ||
304330
(la === 'false' &&
305331
(argType === null ||
@@ -330,11 +356,11 @@ function parse (args, data, remain, { typeDefs, types, dynamicTypes, shorthands
330356
i++
331357
} else if (!la.match(/^-{2,}[^-]/) &&
332358
!isNaN(la) &&
333-
~argType.indexOf(NumberType)) {
359+
hasTypeDef(argType, NumberType)) {
334360
// number
335361
val = +la
336362
i++
337-
} else if (!la.match(/^-[^-]/) && ~argType.indexOf(StringType)) {
363+
} else if (!la.match(/^-[^-]/) && hasTypeDef(argType, StringType)) {
338364
// string
339365
val = la
340366
i++
@@ -350,7 +376,7 @@ function parse (args, data, remain, { typeDefs, types, dynamicTypes, shorthands
350376
continue
351377
}
352378

353-
if (argType === StringType) {
379+
if (isTypeDef(argType, StringType)) {
354380
if (la === undefined) {
355381
la = ''
356382
} else if (la.match(/^-{1,2}[^-]+/)) {
@@ -378,7 +404,26 @@ function parse (args, data, remain, { typeDefs, types, dynamicTypes, shorthands
378404
}
379405
}
380406

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+
382427
// handle single-char shorthands glommed together, like
383428
// npm ls -glp, but only if there is one dash, and only if
384429
// all of the chars are single-char shorthands, and it's
@@ -401,28 +446,9 @@ function resolveShort (arg, shortAbbr, abbrevs, { shorthands }) {
401446
}
402447

403448
// 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), [])
426452
}
427453

428454
// if it's an arg abbrev, and not a literal shorthand, then prefer the arg

0 commit comments

Comments
 (0)