diff --git a/lib/nopt-lib.js b/lib/nopt-lib.js index 89d269f..f6f55da 100644 --- a/lib/nopt-lib.js +++ b/lib/nopt-lib.js @@ -1,8 +1,9 @@ var abbrev = require('abbrev') const debug = require('./debug') const defaultTypeDefs = require('./type-defs') +const _typeDefault = require('./type-default') -function nopt (args, { types, shorthands, typeDefs, invalidHandler }) { +function nopt (args, { types, shorthands, typeDefs, invalidHandler, typeDefault = _typeDefault }) { debug(types, shorthands, args, typeDefs) var data = {} @@ -15,7 +16,7 @@ function nopt (args, { types, shorthands, typeDefs, invalidHandler }) { parse(args, data, argv.remain, { typeDefs, types, shorthands }) // now data is full - clean(data, { types, typeDefs, invalidHandler }) + clean(data, { types, typeDefs, invalidHandler, typeDefault }) data.argv = argv Object.defineProperty(data.argv, 'toString', { @@ -28,15 +29,13 @@ function nopt (args, { types, shorthands, typeDefs, invalidHandler }) { return data } -function clean (data, { types, typeDefs, invalidHandler }) { - const StringType = typeDefs.String.type +function clean (data, { types, typeDefs, invalidHandler, typeDefault = _typeDefault }) { const NumberType = typeDefs.Number.type const ArrayType = typeDefs.Array.type const BooleanType = typeDefs.Boolean.type const DateType = typeDefs.Date.type var remove = {} - var typeDefault = [false, true, null, StringType, ArrayType] Object.keys(data).forEach(function (k) { if (k === 'argv') { @@ -44,7 +43,8 @@ function clean (data, { types, typeDefs, invalidHandler }) { } var val = data[k] var isArray = Array.isArray(val) - var type = types[k] + let rawType = types[k] + var type = rawType if (!isArray) { val = [val] } @@ -82,7 +82,14 @@ function clean (data, { types, typeDefs, invalidHandler }) { } if (!Object.prototype.hasOwnProperty.call(types, k)) { - return v + if (typeDefault === _typeDefault) { + return v + } + // if the default type has been passed in then we want to validate the + // unknown data key instead of bailing out earlier. we also set the raw + // type which is passed to the invalid handler so that it can be + // determined if during validation if it is unknown vs invalid + rawType = typeDefault } // allow `--no-blah` to set 'blah' to null if null is allowed @@ -93,16 +100,16 @@ function clean (data, { types, typeDefs, invalidHandler }) { var d = {} d[k] = v - debug('prevalidated val', d, v, types[k]) - if (!validate(d, k, v, types[k], { typeDefs })) { + debug('prevalidated val', d, v, rawType) + if (!validate(d, k, v, rawType, { typeDefs })) { if (invalidHandler) { - invalidHandler(k, v, types[k], data) + invalidHandler(k, v, rawType, data) } else if (invalidHandler !== false) { - debug('invalid: ' + k + '=' + v, types[k]) + debug('invalid: ' + k + '=' + v, rawType) } return remove } - debug('validated v', d, v, types[k]) + debug('validated v', d, v, rawType) return d[k] }).filter(function (v) { return v !== remove @@ -425,4 +432,5 @@ module.exports = { validate, resolveShort, typeDefs: defaultTypeDefs, + typeDefault: _typeDefault, } diff --git a/lib/nopt.js b/lib/nopt.js index 70fd809..50e2936 100644 --- a/lib/nopt.js +++ b/lib/nopt.js @@ -1,5 +1,4 @@ const lib = require('./nopt-lib') -const defaultTypeDefs = require('./type-defs') // This is the version of nopt's API that requires setting typeDefs and invalidHandler // on the required `nopt` object since it is a singleton. To not do a breaking change @@ -9,7 +8,8 @@ const defaultTypeDefs = require('./type-defs') module.exports = exports = nopt exports.clean = clean -exports.typeDefs = defaultTypeDefs +exports.typeDefs = lib.typeDefs +exports.typeDefault = lib.typeDefault exports.lib = lib function nopt (types = {}, shorthands = {}, args = process.argv, slice = 2) { @@ -17,6 +17,7 @@ function nopt (types = {}, shorthands = {}, args = process.argv, slice = 2) { types, shorthands: shorthands || {}, typeDefs: exports.typeDefs, + typeDefault: exports.typeDefault, invalidHandler: exports.invalidHandler, }) } @@ -25,6 +26,7 @@ function clean (data, types, typeDefs = exports.typeDefs) { return lib.clean(data, { types, typeDefs, + typeDefault: exports.typeDefault, invalidHandler: exports.invalidHandler, }) } diff --git a/lib/type-default.js b/lib/type-default.js new file mode 100644 index 0000000..b6a2ae6 --- /dev/null +++ b/lib/type-default.js @@ -0,0 +1,3 @@ +const defs = require('./type-defs') + +module.exports = [false, true, null, defs.String.type, defs.Array.type] diff --git a/package.json b/package.json index fd0c0ff..2f30b28 100644 --- a/package.json +++ b/package.json @@ -30,9 +30,9 @@ "tap": "^16.3.0" }, "tap": { - "lines": 91, - "branches": 87, - "statements": 91, + "lines": 92, + "branches": 86, + "statements": 92, "nyc-arg": [ "--exclude", "tap-snapshots/**" diff --git a/test/type-default.js b/test/type-default.js new file mode 100644 index 0000000..dd5ea73 --- /dev/null +++ b/test/type-default.js @@ -0,0 +1,48 @@ +const t = require('tap') +const nopt = require('../lib/nopt-lib.js') + +t.test('use other type default', (t) => { + const NotAllowed = Symbol('NotAllowed') + const Invalid = Symbol('Invalid') + + const clean = (data, opts) => { + const invalids = [] + nopt.clean(data, { + types: { + str: nopt.typeDefs.String.type, + invalid: Invalid, + }, + typeDefs: { + ...nopt.typeDefs, + NotAllowed: { type: NotAllowed, validate: () => false }, + Invalid: { type: Invalid, validate: () => false }, + }, + invalidHandler: (k, v, type) => invalids.push([k, v, type]), + ...opts, + }) + return { + keys: Object.keys(data), + invalids, + } + } + + t.strictSame(clean({ + str: 'aaa', + invalid: 'bad', + unknown: 'huh?', + }), { + keys: ['str', 'unknown'], + invalids: [['invalid', 'bad', Invalid]], + }, 'invalid data is removed with clean') + + t.strictSame(clean({ + str: 'aaa', + invalid: 'bad', + unknown: 'huh?', + }, { typeDefault: NotAllowed }), { + keys: ['str'], + invalids: [['invalid', 'bad', Invalid], ['unknown', 'huh?', NotAllowed]], + }, 'invalid and unknown data is removed with a custom typeDefault') + + t.end() +})