From 8b23b9ad6b66653451c0a20eedcc798ea39eb182 Mon Sep 17 00:00:00 2001 From: Luke Karrys Date: Tue, 30 May 2023 14:44:25 -0700 Subject: [PATCH] feat: add fallback option to use function when looking up types --- lib/nopt-lib.js | 37 ++++++++++++++-------- package.json | 6 ++-- test/dynamic-types.js | 72 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 100 insertions(+), 15 deletions(-) create mode 100644 test/dynamic-types.js diff --git a/lib/nopt-lib.js b/lib/nopt-lib.js index f4c2240..85f1521 100644 --- a/lib/nopt-lib.js +++ b/lib/nopt-lib.js @@ -2,7 +2,22 @@ var abbrev = require('abbrev') const debug = require('./debug') const defaultTypeDefs = require('./type-defs') -function nopt (args, { types, shorthands, typeDefs, invalidHandler, typeDefault }) { +const hasOwn = (o, k) => Object.prototype.hasOwnProperty.call(o, k) + +const getType = (k, { types, dynamicTypes }) => { + let hasType = hasOwn(types, k) + let type = types[k] + if (!hasType && typeof dynamicTypes === 'function') { + const matchedType = dynamicTypes(k) + if (matchedType !== undefined) { + type = matchedType + hasType = true + } + } + return [hasType, type] +} + +function nopt (args, { types, dynamicTypes, shorthands, typeDefs, invalidHandler, typeDefault }) { debug(types, shorthands, args, typeDefs) var data = {} @@ -12,10 +27,10 @@ function nopt (args, { types, shorthands, typeDefs, invalidHandler, typeDefault original: args.slice(0), } - parse(args, data, argv.remain, { typeDefs, types, shorthands }) + parse(args, data, argv.remain, { typeDefs, types, dynamicTypes, shorthands }) // now data is full - clean(data, { types, typeDefs, invalidHandler, typeDefault }) + clean(data, { types, dynamicTypes, typeDefs, invalidHandler, typeDefault }) data.argv = argv Object.defineProperty(data.argv, 'toString', { @@ -28,7 +43,7 @@ function nopt (args, { types, shorthands, typeDefs, invalidHandler, typeDefault return data } -function clean (data, { types, typeDefs, invalidHandler, typeDefault }) { +function clean (data, { types, dynamicTypes, typeDefs, invalidHandler, typeDefault }) { const StringType = typeDefs.String.type const NumberType = typeDefs.Number.type const ArrayType = typeDefs.Array.type @@ -48,7 +63,7 @@ function clean (data, { types, typeDefs, invalidHandler, typeDefault }) { } var val = data[k] var isArray = Array.isArray(val) - let rawType = types[k] + let [hasType, rawType] = getType(k, { types, dynamicTypes }) var type = rawType if (!isArray) { val = [val] @@ -86,7 +101,7 @@ function clean (data, { types, typeDefs, invalidHandler, typeDefault }) { } } - if (!Object.prototype.hasOwnProperty.call(types, k)) { + if (!hasType) { if (!hasTypeDefault) { return v } @@ -205,7 +220,7 @@ function validate (data, k, val, type, { typeDefs }) { return ok } -function parse (args, data, remain, { typeDefs, types, shorthands }) { +function parse (args, data, remain, { typeDefs, types, dynamicTypes, shorthands }) { const StringType = typeDefs.String.type const NumberType = typeDefs.String.type const ArrayType = typeDefs.Array.type @@ -214,6 +229,7 @@ function parse (args, data, remain, { typeDefs, types, shorthands }) { debug('parse', args, data, remain) var abbrevs = abbrev(Object.keys(types)) + debug('abbrevs=%j', abbrevs) var shortAbbr = abbrev(Object.keys(shorthands)) for (var i = 0; i < args.length; i++) { @@ -260,7 +276,7 @@ function parse (args, data, remain, { typeDefs, types, shorthands }) { arg = abbrevs[arg] } - var argType = types[arg] + var [hasType, argType] = getType(arg, { types, dynamicTypes }) var isTypeArray = Array.isArray(argType) if (isTypeArray && argType.length === 1) { isTypeArray = false @@ -271,10 +287,7 @@ function parse (args, data, remain, { typeDefs, types, shorthands }) { isTypeArray && argType.indexOf(ArrayType) !== -1 // allow unknown things to be arrays if specified multiple times. - if ( - !Object.prototype.hasOwnProperty.call(types, arg) && - Object.prototype.hasOwnProperty.call(data, arg) - ) { + if (!hasType && hasOwn(data, arg)) { if (!Array.isArray(data[arg])) { data[arg] = [data[arg]] } diff --git a/package.json b/package.json index 06080b1..99cc9f2 100644 --- a/package.json +++ b/package.json @@ -30,9 +30,9 @@ "tap": "^16.3.0" }, "tap": { - "lines": 91, - "branches": 87, - "statements": 91, + "statements": 94, + "branches": 88, + "lines": 94, "nyc-arg": [ "--exclude", "tap-snapshots/**" diff --git a/test/dynamic-types.js b/test/dynamic-types.js new file mode 100644 index 0000000..237e303 --- /dev/null +++ b/test/dynamic-types.js @@ -0,0 +1,72 @@ +const t = require('tap') +const nopt = require('../lib/nopt-lib.js') + +t.test('fallback types', (t) => { + const n = (dynamicTypes) => { + const args = [ + '--hello', '100', + '--goodbye', '50', + '--hat=blue', + '--mult', '200', + '--mult', '300', + '--multeq=111', + '--multeq=999', + ] + const res = nopt.nopt(args, { + types: { hello: nopt.typeDefs.Number.type }, + dynamicTypes, + typeDefs: nopt.typeDefs, + shorthands: {}, + }) + delete res.argv.cooked + delete res.argv.original + return res + } + + t.strictSame(n(), { + hello: 100, + goodbye: true, + hat: 'blue', + mult: [ + true, + true, + ], + multeq: [ + '111', + '999', + ], + argv: { + remain: [ + '50', + '200', + '300', + ], + }, + }, 'parse args with no fallback') + + t.strictSame(n((k) => { + if (k.startsWith('goo')) { + return nopt.typeDefs.Number.type + } + if (k === 'mult') { + return [nopt.typeDefs.Number.type, nopt.typeDefs.Array.type] + } + }), { + hello: 100, + goodbye: 50, + hat: 'blue', + mult: [ + 200, + 300, + ], + multeq: [ + '111', + '999', + ], + argv: { + remain: [], + }, + }, 'parse args with no fallback') + + t.end() +})