diff --git a/bin/nopt.js b/bin/nopt.js index 3232d4c..bb04291 100755 --- a/bin/nopt.js +++ b/bin/nopt.js @@ -1,54 +1,56 @@ #!/usr/bin/env node -var nopt = require("../lib/nopt") - , path = require("path") - , types = { num: Number - , bool: Boolean - , help: Boolean - , list: Array - , "num-list": [Number, Array] - , "str-list": [String, Array] - , "bool-list": [Boolean, Array] - , str: String - , clear: Boolean - , config: Boolean - , length: Number - , file: path - } - , shorthands = { s: [ "--str", "astring" ] - , b: [ "--bool" ] - , nb: [ "--no-bool" ] - , tft: [ "--bool-list", "--no-bool-list", "--bool-list", "true" ] - , "?": ["--help"] - , h: ["--help"] - , H: ["--help"] - , n: [ "--num", "125" ] - , c: ["--config"] - , l: ["--length"] - , f: ["--file"] - } - , parsed = nopt( types - , shorthands - , process.argv - , 2 ) +var nopt = require('../lib/nopt') +var path = require('path') +var types = { num: Number, + bool: Boolean, + help: Boolean, + list: Array, + 'num-list': [Number, Array], + 'str-list': [String, Array], + 'bool-list': [Boolean, Array], + str: String, + clear: Boolean, + config: Boolean, + length: Number, + file: path, +} +var shorthands = { s: ['--str', 'astring'], + b: ['--bool'], + nb: ['--no-bool'], + tft: ['--bool-list', '--no-bool-list', '--bool-list', 'true'], + '?': ['--help'], + h: ['--help'], + H: ['--help'], + n: ['--num', '125'], + c: ['--config'], + l: ['--length'], + f: ['--file'], +} +var parsed = nopt(types + , shorthands + , process.argv + , 2) -console.log("parsed", parsed) +console.log('parsed', parsed) if (parsed.help) { - console.log("") - console.log("nopt cli tester") - console.log("") - console.log("types") + console.log('') + console.log('nopt cli tester') + console.log('') + console.log('types') console.log(Object.keys(types).map(function M (t) { var type = types[t] if (Array.isArray(type)) { - return [t, type.map(function (type) { return type.name })] + return [t, type.map(function (mappedType) { + return mappedType.name + })] } return [t, type && type.name] }).reduce(function (s, i) { s[i[0]] = i[1] return s }, {})) - console.log("") - console.log("shorthands") + console.log('') + console.log('shorthands') console.log(shorthands) } diff --git a/docs/examples/my-program.js b/docs/examples/my-program.js index 142447e..c9b32af 100755 --- a/docs/examples/my-program.js +++ b/docs/examples/my-program.js @@ -1,30 +1,30 @@ #!/usr/bin/env node -//process.env.DEBUG_NOPT = 1 +// process.env.DEBUG_NOPT = 1 // my-program.js -var nopt = require("../lib/nopt") - , Stream = require("stream").Stream - , path = require("path") - , knownOpts = { "foo" : [String, null] - , "bar" : [Stream, Number] - , "baz" : path - , "bloo" : [ "big", "medium", "small" ] - , "flag" : Boolean - , "pick" : Boolean - } - , shortHands = { "foofoo" : ["--foo", "Mr. Foo"] - , "b7" : ["--bar", "7"] - , "m" : ["--bloo", "medium"] - , "p" : ["--pick"] - , "f" : ["--flag", "true"] - , "g" : ["--flag"] - , "s" : "--flag" - } - // everything is optional. - // knownOpts and shorthands default to {} - // arg list defaults to process.argv - // slice defaults to 2 - , parsed = nopt(knownOpts, shortHands, process.argv, 2) +var nopt = require('../lib/nopt') +var Stream = require('stream').Stream +var path = require('path') +var knownOpts = { foo: [String, null], + bar: [Stream, Number], + baz: path, + bloo: ['big', 'medium', 'small'], + flag: Boolean, + pick: Boolean, +} +var shortHands = { foofoo: ['--foo', 'Mr. Foo'], + b7: ['--bar', '7'], + m: ['--bloo', 'medium'], + p: ['--pick'], + f: ['--flag', 'true'], + g: ['--flag'], + s: '--flag', +} +// everything is optional. +// knownOpts and shorthands default to {} +// arg list defaults to process.argv +// slice defaults to 2 +var parsed = nopt(knownOpts, shortHands, process.argv, 2) -console.log("parsed =\n"+ require("util").inspect(parsed)) +console.log('parsed =\n' + require('util').inspect(parsed)) diff --git a/lib/nopt.js b/lib/nopt.js index 8abac92..5829c2f 100644 --- a/lib/nopt.js +++ b/lib/nopt.js @@ -1,130 +1,147 @@ // info about each config option. var debug = process.env.DEBUG_NOPT || process.env.NOPT_DEBUG - ? function () { console.error.apply(console, arguments) } + ? function () { + console.error.apply(console, arguments) + } : function () {} -var url = require("url") - , path = require("path") - , Stream = require("stream").Stream - , abbrev = require("abbrev") - , os = require("os") +var url = require('url') +var path = require('path') +var Stream = require('stream').Stream +var abbrev = require('abbrev') +var os = require('os') module.exports = exports = nopt exports.clean = clean exports.typeDefs = - { String : { type: String, validate: validateString } - , Boolean : { type: Boolean, validate: validateBoolean } - , url : { type: url, validate: validateUrl } - , Number : { type: Number, validate: validateNumber } - , path : { type: path, validate: validatePath } - , Stream : { type: Stream, validate: validateStream } - , Date : { type: Date, validate: validateDate } + { String: { type: String, validate: validateString }, + Boolean: { type: Boolean, validate: validateBoolean }, + url: { type: url, validate: validateUrl }, + Number: { type: Number, validate: validateNumber }, + path: { type: path, validate: validatePath }, + Stream: { type: Stream, validate: validateStream }, + Date: { type: Date, validate: validateDate }, } function nopt (types, shorthands, args, slice) { args = args || process.argv types = types || {} shorthands = shorthands || {} - if (typeof slice !== "number") slice = 2 + if (typeof slice !== 'number') { + slice = 2 + } debug(types, shorthands, args, slice) args = args.slice(slice) var data = {} - , key - , argv = { - remain: [], - cooked: args, - original: args.slice(0) - } + var argv = { + remain: [], + cooked: args, + original: args.slice(0), + } parse(args, data, argv.remain, types, shorthands) // now data is full clean(data, types, exports.typeDefs) data.argv = argv Object.defineProperty(data.argv, 'toString', { value: function () { - return this.original.map(JSON.stringify).join(" ") - }, enumerable: false }) + return this.original.map(JSON.stringify).join(' ') + }, + enumerable: false }) return data } function clean (data, types, typeDefs) { typeDefs = typeDefs || exports.typeDefs var remove = {} - , typeDefault = [false, true, null, String, Array] + var typeDefault = [false, true, null, String, Array] Object.keys(data).forEach(function (k) { - if (k === "argv") return + if (k === 'argv') { + return + } var val = data[k] - , isArray = Array.isArray(val) - , type = types[k] - if (!isArray) val = [val] - if (!type) type = typeDefault - if (type === Array) type = typeDefault.concat(Array) - if (!Array.isArray(type)) type = [type] - - debug("val=%j", val) - debug("types=", type) - val = val.map(function (val) { + var isArray = Array.isArray(val) + var type = types[k] + if (!isArray) { + val = [val] + } + if (!type) { + type = typeDefault + } + if (type === Array) { + type = typeDefault.concat(Array) + } + if (!Array.isArray(type)) { + type = [type] + } + + debug('val=%j', val) + debug('types=', type) + val = val.map(function (v) { // if it's an unknown value, then parse false/true/null/numbers/dates - if (typeof val === "string") { - debug("string %j", val) - val = val.trim() - if ((val === "null" && ~type.indexOf(null)) - || (val === "true" && + if (typeof v === 'string') { + debug('string %j', v) + v = v.trim() + if ((v === 'null' && ~type.indexOf(null)) + || (v === 'true' && (~type.indexOf(true) || ~type.indexOf(Boolean))) - || (val === "false" && + || (v === 'false' && (~type.indexOf(false) || ~type.indexOf(Boolean)))) { - val = JSON.parse(val) - debug("jsonable %j", val) - } else if (~type.indexOf(Number) && !isNaN(val)) { - debug("convert to number", val) - val = +val - } else if (~type.indexOf(Date) && !isNaN(Date.parse(val))) { - debug("convert to date", val) - val = new Date(val) + v = JSON.parse(v) + debug('jsonable %j', v) + } else if (~type.indexOf(Number) && !isNaN(v)) { + debug('convert to number', v) + v = +v + } else if (~type.indexOf(Date) && !isNaN(Date.parse(v))) { + debug('convert to date', v) + v = new Date(v) } } - if (!types.hasOwnProperty(k)) { - return val + if (!Object.prototype.hasOwnProperty.call(types, k)) { + return v } // allow `--no-blah` to set 'blah' to null if null is allowed - if (val === false && ~type.indexOf(null) && + if (v === false && ~type.indexOf(null) && !(~type.indexOf(false) || ~type.indexOf(Boolean))) { - val = null + v = null } var d = {} - d[k] = val - debug("prevalidated val", d, val, types[k]) - if (!validate(d, k, val, types[k], typeDefs)) { + d[k] = v + debug('prevalidated val', d, v, types[k]) + if (!validate(d, k, v, types[k], typeDefs)) { if (exports.invalidHandler) { - exports.invalidHandler(k, val, types[k], data) + exports.invalidHandler(k, v, types[k], data) } else if (exports.invalidHandler !== false) { - debug("invalid: "+k+"="+val, types[k]) + debug('invalid: ' + k + '=' + v, types[k]) } return remove } - debug("validated val", d, val, types[k]) + debug('validated v', d, v, types[k]) return d[k] - }).filter(function (val) { return val !== remove }) + }).filter(function (v) { + return v !== remove + }) // if we allow Array specifically, then an empty array is how we // express 'no value here', not null. Allow it. if (!val.length && type.indexOf(Array) === -1) { debug('VAL HAS NO LENGTH, DELETE IT', val, k, type.indexOf(Array)) delete data[k] - } - else if (isArray) { + } else if (isArray) { debug(isArray, data[k], val) data[k] = val - } else data[k] = val[0] + } else { + data[k] = val[0] + } - debug("k=%s val=%j", k, val, data[k]) + debug('k=%s val=%j', k, val, data[k]) }) } @@ -133,14 +150,18 @@ function validateString (data, k, val) { } function validatePath (data, k, val) { - if (val === true) return false - if (val === null) return true + if (val === true) { + return false + } + if (val === null) { + return true + } val = String(val) - var isWin = process.platform === 'win32' - , homePattern = isWin ? /^~(\/|\\)/ : /^~\// - , home = os.homedir() + var isWin = process.platform === 'win32' + var homePattern = isWin ? /^~(\/|\\)/ : /^~\// + var home = os.homedir() if (home && val.match(homePattern)) { data[k] = path.resolve(home, val.slice(2)) @@ -151,63 +172,92 @@ function validatePath (data, k, val) { } function validateNumber (data, k, val) { - debug("validate Number %j %j %j", k, val, isNaN(val)) - if (isNaN(val)) return false + debug('validate Number %j %j %j', k, val, isNaN(val)) + if (isNaN(val)) { + return false + } data[k] = +val } function validateDate (data, k, val) { var s = Date.parse(val) - debug("validate Date %j %j %j", k, val, s) - if (isNaN(s)) return false + debug('validate Date %j %j %j', k, val, s) + if (isNaN(s)) { + return false + } data[k] = new Date(val) } function validateBoolean (data, k, val) { - if (val instanceof Boolean) val = val.valueOf() - else if (typeof val === "string") { - if (!isNaN(val)) val = !!(+val) - else if (val === "null" || val === "false") val = false - else val = true - } else val = !!val + if (val instanceof Boolean) { + val = val.valueOf() + } else if (typeof val === 'string') { + if (!isNaN(val)) { + val = !!(+val) + } else if (val === 'null' || val === 'false') { + val = false + } else { + val = true + } + } else { + val = !!val + } data[k] = val } function validateUrl (data, k, val) { + // Changing this would be a breaking change in the npm cli + /* eslint-disable-next-line node/no-deprecated-api */ val = url.parse(String(val)) - if (!val.host) return false + if (!val.host) { + return false + } data[k] = val.href } function validateStream (data, k, val) { - if (!(val instanceof Stream)) return false + if (!(val instanceof Stream)) { + return false + } data[k] = val } function validate (data, k, val, type, typeDefs) { // arrays are lists of types. if (Array.isArray(type)) { - for (var i = 0, l = type.length; i < l; i ++) { - if (type[i] === Array) continue - if (validate(data, k, val, type[i], typeDefs)) return true + for (let i = 0, l = type.length; i < l; i++) { + if (type[i] === Array) { + continue + } + if (validate(data, k, val, type[i], typeDefs)) { + return true + } } delete data[k] return false } // an array of anything? - if (type === Array) return true + if (type === Array) { + return true + } + // Original comment: // NaN is poisonous. Means that something is not allowed. + // New comment: Changing this to an isNaN check breaks a lot of tests. + // Something is being assumed here that is not actually what happens in + // practice. Fixing it is outside the scope of getting linting to pass in + // this repo. Leaving as-is for now. + /* eslint-disable-next-line no-self-compare */ if (type !== type) { - debug("Poison NaN", k, val, type) + debug('Poison NaN', k, val, type) delete data[k] return false } // explicit list of values if (val === type) { - debug("Explicitly allowed %j", val) + debug('Explicitly allowed %j', val) // if (isArray) (data[k] = data[k] || []).push(val) // else data[k] = val data[k] = val @@ -216,14 +266,17 @@ function validate (data, k, val, type, typeDefs) { // now go through the list of typeDefs, validate against each one. var ok = false - , types = Object.keys(typeDefs) - for (var i = 0, l = types.length; i < l; i ++) { - debug("test type %j %j %j", k, val, types[i]) + var types = Object.keys(typeDefs) + for (let i = 0, l = types.length; i < l; i++) { + debug('test type %j %j %j', k, val, types[i]) var t = typeDefs[types[i]] - if (t && - ((type && type.name && t.type && t.type.name) ? (type.name === t.type.name) : (type === t.type))) { + if (t && ( + (type && type.name && t.type && t.type.name) ? + (type.name === t.type.name) : + (type === t.type) + )) { var d = {} - ok = false !== t.validate(d, k, val) + ok = t.validate(d, k, val) !== false val = d[k] if (ok) { // if (isArray) (data[k] = data[k] || []).push(val) @@ -233,32 +286,33 @@ function validate (data, k, val, type, typeDefs) { } } } - debug("OK? %j (%j %j %j)", ok, k, val, types[i]) + debug('OK? %j (%j %j %j)', ok, k, val, types[types.length - 1]) - if (!ok) delete data[k] + if (!ok) { + delete data[k] + } return ok } function parse (args, data, remain, types, shorthands) { - debug("parse", args, data, remain) + debug('parse', args, data, remain) - var key = null - , abbrevs = abbrev(Object.keys(types)) - , shortAbbr = abbrev(Object.keys(shorthands)) + var abbrevs = abbrev(Object.keys(types)) + var shortAbbr = abbrev(Object.keys(shorthands)) - for (var i = 0; i < args.length; i ++) { + for (var i = 0; i < args.length; i++) { var arg = args[i] - debug("arg", arg) + debug('arg', arg) if (arg.match(/^-{2,}$/)) { // done with keys. // the rest are args. remain.push.apply(remain, args.slice(i + 1)) - args[i] = "--" + args[i] = '--' break } var hadEq = false - if (arg.charAt(0) === "-" && arg.length > 1) { + if (arg.charAt(0) === '-' && arg.length > 1) { var at = arg.indexOf('=') if (at > -1) { hadEq = true @@ -270,23 +324,25 @@ function parse (args, data, remain, types, shorthands) { // see if it's a shorthand // if so, splice and back up to re-parse it. var shRes = resolveShort(arg, shorthands, shortAbbr, abbrevs) - debug("arg=%j shRes=%j", arg, shRes) + debug('arg=%j shRes=%j', arg, shRes) if (shRes) { debug(arg, shRes) args.splice.apply(args, [i, 1].concat(shRes)) if (arg !== shRes[0]) { - i -- + i-- continue } } - arg = arg.replace(/^-+/, "") + arg = arg.replace(/^-+/, '') var no = null - while (arg.toLowerCase().indexOf("no-") === 0) { + while (arg.toLowerCase().indexOf('no-') === 0) { no = !no arg = arg.slice(3) } - if (abbrevs[arg]) arg = abbrevs[arg] + if (abbrevs[arg]) { + arg = abbrevs[arg] + } var argType = types[arg] var isTypeArray = Array.isArray(argType) @@ -299,20 +355,24 @@ function parse (args, data, remain, types, shorthands) { isTypeArray && argType.indexOf(Array) !== -1 // allow unknown things to be arrays if specified multiple times. - if (!types.hasOwnProperty(arg) && data.hasOwnProperty(arg)) { - if (!Array.isArray(data[arg])) + if ( + !Object.prototype.hasOwnProperty.call(types, arg) && + Object.prototype.hasOwnProperty.call(data, arg) + ) { + if (!Array.isArray(data[arg])) { data[arg] = [data[arg]] + } isArray = true } var val - , la = args[i + 1] + var la = args[i + 1] var isBool = typeof no === 'boolean' || argType === Boolean || isTypeArray && argType.indexOf(Boolean) !== -1 || (typeof argType === 'undefined' && !hadEq) || - (la === "false" && + (la === 'false' && (argType === null || isTypeArray && ~argType.indexOf(null))) @@ -320,11 +380,13 @@ function parse (args, data, remain, types, shorthands) { // just set and move along val = !no // however, also support --bool true or --bool false - if (la === "true" || la === "false") { + if (la === 'true' || la === 'false') { val = JSON.parse(la) la = null - if (no) val = !val - i ++ + if (no) { + val = !val + } + i++ } // also support "foo":[Boolean, "bar"] and "--foo bar" @@ -332,49 +394,55 @@ function parse (args, data, remain, types, shorthands) { if (~argType.indexOf(la)) { // an explicit type val = la - i ++ - } else if ( la === "null" && ~argType.indexOf(null) ) { + i++ + } else if (la === 'null' && ~argType.indexOf(null)) { // null allowed val = null - i ++ - } else if ( !la.match(/^-{2,}[^-]/) && + i++ + } else if (!la.match(/^-{2,}[^-]/) && !isNaN(la) && - ~argType.indexOf(Number) ) { + ~argType.indexOf(Number)) { // number val = +la - i ++ - } else if ( !la.match(/^-[^-]/) && ~argType.indexOf(String) ) { + i++ + } else if (!la.match(/^-[^-]/) && ~argType.indexOf(String)) { // string val = la - i ++ + i++ } } - if (isArray) (data[arg] = data[arg] || []).push(val) - else data[arg] = val + if (isArray) { + (data[arg] = data[arg] || []).push(val) + } else { + data[arg] = val + } continue } if (argType === String) { if (la === undefined) { - la = "" + la = '' } else if (la.match(/^-{1,2}[^-]+/)) { - la = "" - i -- + la = '' + i-- } } if (la && la.match(/^-{2,}$/)) { la = undefined - i -- + i-- } val = la === undefined ? true : la - if (isArray) (data[arg] = data[arg] || []).push(val) - else data[arg] = val + if (isArray) { + (data[arg] = data[arg] || []).push(val) + } else { + data[arg] = val + } - i ++ + i++ continue } remain.push(arg) @@ -389,14 +457,16 @@ function resolveShort (arg, shorthands, shortAbbr, abbrevs) { arg = arg.replace(/^-+/, '') // if it's an exact known option, then don't go any further - if (abbrevs[arg] === arg) + if (abbrevs[arg] === arg) { return null + } // if it's an exact known shortopt, same deal if (shorthands[arg]) { // make it an array, if it's a list of words - if (shorthands[arg] && !Array.isArray(shorthands[arg])) + if (shorthands[arg] && !Array.isArray(shorthands[arg])) { shorthands[arg] = shorthands[arg].split(/\s+/) + } return shorthands[arg] } @@ -406,7 +476,7 @@ function resolveShort (arg, shorthands, shortAbbr, abbrevs) { if (!singles) { singles = Object.keys(shorthands).filter(function (s) { return s.length === 1 - }).reduce(function (l,r) { + }).reduce(function (l, r) { l[r] = true return l }, {}) @@ -414,28 +484,32 @@ function resolveShort (arg, shorthands, shortAbbr, abbrevs) { debug('shorthand singles', singles) } - var chrs = arg.split("").filter(function (c) { + var chrs = arg.split('').filter(function (c) { return singles[c] }) - if (chrs.join("") === arg) return chrs.map(function (c) { - return shorthands[c] - }).reduce(function (l, r) { - return l.concat(r) - }, []) - + if (chrs.join('') === arg) { + return chrs.map(function (c) { + return shorthands[c] + }).reduce(function (l, r) { + return l.concat(r) + }, []) + } // if it's an arg abbrev, and not a literal shorthand, then prefer the arg - if (abbrevs[arg] && !shorthands[arg]) + if (abbrevs[arg] && !shorthands[arg]) { return null + } // if it's an abbr for a shorthand, then use that - if (shortAbbr[arg]) + if (shortAbbr[arg]) { arg = shortAbbr[arg] + } // make it an array, if it's a list of words - if (shorthands[arg] && !Array.isArray(shorthands[arg])) + if (shorthands[arg] && !Array.isArray(shorthands[arg])) { shorthands[arg] = shorthands[arg].split(/\s+/) + } return shorthands[arg] } diff --git a/package.json b/package.json index af67853..8dbea69 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ "tap": "^16.3.0" }, "tap": { - "lines": 89, + "lines": 87, "functions": 91, "branches": 81, "statements": 87 diff --git a/test/basic.js b/test/basic.js index c030e64..2de08de 100644 --- a/test/basic.js +++ b/test/basic.js @@ -1,315 +1,314 @@ -var nopt = require("../") - , test = require('tap').test - , isWin = process.platform === 'win32' +var nopt = require('../') +var test = require('tap').test +var isWin = process.platform === 'win32' -test("empty array is fine if type includes Array", function (t) { +test('empty array is fine if type includes Array', function (t) { var typeDefs = { - arr: [Array, String] + arr: [Array, String], } var data = { - arr: [] + arr: [], } nopt.clean(data, typeDefs) t.same(data.arr, []) t.end() }) -test("passing a string results in a string", function (t) { - var parsed = nopt({ key: String }, {}, ["--key", "myvalue"], 0) - t.same(parsed.key, "myvalue") +test('passing a string results in a string', function (t) { + var parsed = nopt({ key: String }, {}, ['--key', 'myvalue'], 0) + t.same(parsed.key, 'myvalue') t.end() }) // https://github.com/npm/nopt/issues/31 -test("Empty String results in empty string, not true", function (t) { - var parsed = nopt({ empty: String }, {}, ["--empty"], 0) - t.same(parsed.empty, "") +test('Empty String results in empty string, not true', function (t) { + var parsed = nopt({ empty: String }, {}, ['--empty'], 0) + t.same(parsed.empty, '') t.end() }) // https://github.com/npm/nopt/issues/65 -test("Empty String should not swallow next flag", function (t) { - var parsed = nopt({ empty: String, foo: String }, {}, ["--empty", "--foo"], 0) - t.same(parsed.empty, "") - t.same(parsed.foo, "") +test('Empty String should not swallow next flag', function (t) { + var parsed = nopt({ empty: String, foo: String }, {}, ['--empty', '--foo'], 0) + t.same(parsed.empty, '') + t.same(parsed.foo, '') t.end() }) // https://github.com/npm/nopt/issues/66 -test("Empty String should not be true when type is single item Array", function (t) { - var parsed = nopt({ 'foo': [String] }, {}, ["--foo"], 0) - t.same(parsed.foo, "") +test('Empty String should not be true when type is single item Array', function (t) { + var parsed = nopt({ foo: [String] }, {}, ['--foo'], 0) + t.same(parsed.foo, '') t.end() }) -test("~ path is resolved to " + (isWin ? '%USERPROFILE%' : '$HOME'), function (t) { - var path = require("path") - , the +test('~ path is resolved to ' + (isWin ? '%USERPROFILE%' : '$HOME'), function (t) { + var path = require('path') + var the if (isWin) { the = { key: 'USERPROFILE', dir: 'C:\\temp', - val: '~\\val' + val: '~\\val', } } else { the = { key: 'HOME', dir: '/tmp', - val: '~/val' + val: '~/val', } } - if (!process.env[the.key]) process.env[the.key] = v.dir - var parsed = nopt({key: path}, {}, ["--key=" + the.val], 0) - t.same(parsed.key, path.resolve(process.env[the.key], "val")) + if (!process.env[the.key]) { + process.env[the.key] = the.dir + } + var parsed = nopt({ key: path }, {}, ['--key=' + the.val], 0) + t.same(parsed.key, path.resolve(process.env[the.key], 'val')) t.end() }) // https://github.com/npm/nopt/issues/24 -test("Unknown options are not parsed as numbers", function (t) { - var parsed = nopt({"parse-me": Number}, null, ['--leave-as-is=1.20', '--parse-me=1.20'], 0) - t.equal(parsed['leave-as-is'], '1.20') - t.equal(parsed['parse-me'], 1.2) - t.end() -}); +test('Unknown options are not parsed as numbers', function (t) { + var parsed = nopt({ 'parse-me': Number }, null, ['--leave-as-is=1.20', '--parse-me=1.20'], 0) + t.equal(parsed['leave-as-is'], '1.20') + t.equal(parsed['parse-me'], 1.2) + t.end() +}) // https://github.com/npm/nopt/issues/48 -test("Check types based on name of type", function (t) { - var parsed = nopt({"parse-me": {name: "Number"}}, null, ['--parse-me=1.20'], 0) +test('Check types based on name of type', function (t) { + var parsed = nopt({ 'parse-me': { name: 'Number' } }, null, ['--parse-me=1.20'], 0) t.equal(parsed['parse-me'], 1.2) t.end() }) - -test("Missing types are not parsed", function (t) { - var parsed = nopt({"parse-me": {}}, null, ['--parse-me=1.20'], 0) - //should only contain argv +test('Missing types are not parsed', function (t) { + var parsed = nopt({ 'parse-me': {} }, null, ['--parse-me=1.20'], 0) + // should only contain argv t.equal(Object.keys(parsed).length, 1) t.end() }) -test("Types passed without a name are not parsed", function (t) { - var parsed = nopt({"parse-me": {}}, {}, ['--parse-me=1.20'], 0) - //should only contain argv +test('Types passed without a name are not parsed', function (t) { + var parsed = nopt({ 'parse-me': {} }, {}, ['--parse-me=1.20'], 0) + // should only contain argv t.equal(Object.keys(parsed).length, 1) t.end() }) -test("other tests", function (t) { - - var util = require("util") - , Stream = require("stream") - , path = require("path") - , url = require("url") +test('other tests', function (t) { + var Stream = require('stream') + var path = require('path') + var url = require('url') - , shorthands = - { s : ["--loglevel", "silent"] - , d : ["--loglevel", "info"] - , dd : ["--loglevel", "verbose"] - , ddd : ["--loglevel", "silly"] - , noreg : ["--no-registry"] - , reg : ["--registry"] - , "no-reg" : ["--no-registry"] - , silent : ["--loglevel", "silent"] - , verbose : ["--loglevel", "verbose"] - , h : ["--usage"] - , H : ["--usage"] - , "?" : ["--usage"] - , help : ["--usage"] - , v : ["--version"] - , f : ["--force"] - , desc : ["--description"] - , "no-desc" : ["--no-description"] - , "local" : ["--no-global"] - , l : ["--long"] - , p : ["--parseable"] - , porcelain : ["--parseable"] - , g : ["--global"] + var shorthands = + { s: ['--loglevel', 'silent'], + d: ['--loglevel', 'info'], + dd: ['--loglevel', 'verbose'], + ddd: ['--loglevel', 'silly'], + noreg: ['--no-registry'], + reg: ['--registry'], + 'no-reg': ['--no-registry'], + silent: ['--loglevel', 'silent'], + verbose: ['--loglevel', 'verbose'], + h: ['--usage'], + H: ['--usage'], + '?': ['--usage'], + help: ['--usage'], + v: ['--version'], + f: ['--force'], + desc: ['--description'], + 'no-desc': ['--no-description'], + local: ['--no-global'], + l: ['--long'], + p: ['--parseable'], + porcelain: ['--parseable'], + g: ['--global'], } - , types = - { aoa: Array - , nullstream: [null, Stream] - , date: Date - , str: String - , browser : String - , cache : path - , color : ["always", Boolean] - , depth : Number - , description : Boolean - , dev : Boolean - , editor : path - , force : Boolean - , global : Boolean - , globalconfig : path - , group : [String, Number] - , gzipbin : String - , logfd : [Number, Stream] - , loglevel : ["silent","win","error","warn","info","verbose","silly"] - , long : Boolean - , "node-version" : [false, String] - , npaturl : url - , npat : Boolean - , "onload-script" : [false, String] - , outfd : [Number, Stream] - , parseable : Boolean - , pre: Boolean - , prefix: path - , proxy : url - , "rebuild-bundle" : Boolean - , registry : url - , searchopts : String - , searchexclude: [null, String] - , shell : path - , t: [Array, String] - , tag : String - , tar : String - , tmp : path - , "unsafe-perm" : Boolean - , usage : Boolean - , user : String - , username : String - , userconfig : path - , version : Boolean - , viewer: path - , _exit : Boolean - , path: path + var types = + { aoa: Array, + nullstream: [null, Stream], + date: Date, + str: String, + browser: String, + cache: path, + color: ['always', Boolean], + depth: Number, + description: Boolean, + dev: Boolean, + editor: path, + force: Boolean, + global: Boolean, + globalconfig: path, + group: [String, Number], + gzipbin: String, + logfd: [Number, Stream], + loglevel: ['silent', 'win', 'error', 'warn', 'info', 'verbose', 'silly'], + long: Boolean, + 'node-version': [false, String], + npaturl: url, + npat: Boolean, + 'onload-script': [false, String], + outfd: [Number, Stream], + parseable: Boolean, + pre: Boolean, + prefix: path, + proxy: url, + 'rebuild-bundle': Boolean, + registry: url, + searchopts: String, + searchexclude: [null, String], + shell: path, + t: [Array, String], + tag: String, + tar: String, + tmp: path, + 'unsafe-perm': Boolean, + usage: Boolean, + user: String, + username: String, + userconfig: path, + version: Boolean, + viewer: path, + _exit: Boolean, + path: path, } - ; [["-v", {version:true}, []] - ,["---v", {version:true}, []] - ,["ls -s --no-reg connect -d", - {loglevel:"info",registry:null},["ls","connect"]] - ,["ls ---s foo",{loglevel:"silent"},["ls","foo"]] - ,["ls --registry blargle", {}, ["ls"]] - ,["--no-registry", {registry:null}, []] - ,["--no-color true", {color:false}, []] - ,["--no-color false", {color:true}, []] - ,["--no-color", {color:false}, []] - ,["--color false", {color:false}, []] - ,["--color --logfd 7", {logfd:7,color:true}, []] - ,["--color=true", {color:true}, []] - ,["--logfd=10", {logfd:10}, []] - ,["--tmp=/tmp -tar=gtar", {tmp: isWin ? "C:\\tmp" : "/tmp",tar:"gtar"},[]] - ,["--tmp=tmp -tar=gtar", - {tmp:path.resolve(process.cwd(), "tmp"),tar:"gtar"},[]] - ,["--logfd x", {}, []] - ,["a -true -- -no-false", {true:true},["a","-no-false"]] - ,["a -no-false", {false:false},["a"]] - ,["a -no-no-true", {true:true}, ["a"]] - ,["a -no-no-no-false", {false:false}, ["a"]] - ,["---NO-no-No-no-no-no-nO-no-no"+ - "-No-no-no-no-no-no-no-no-no"+ - "-no-no-no-no-NO-NO-no-no-no-no-no-no"+ - "-no-body-can-do-the-boogaloo-like-I-do" - ,{"body-can-do-the-boogaloo-like-I-do":false}, []] - ,["we are -no-strangers-to-love "+ - "--you-know=the-rules --and=so-do-i "+ - "---im-thinking-of=a-full-commitment "+ - "--no-you-would-get-this-from-any-other-guy "+ - "--no-gonna-give-you-up "+ - "-no-gonna-let-you-down=true "+ - "--no-no-gonna-run-around false "+ - "--desert-you=false "+ - "--make-you-cry false "+ - "--no-tell-a-lie "+ - "--no-no-and-hurt-you false" - ,{"strangers-to-love":false - ,"you-know":"the-rules" - ,"and":"so-do-i" - ,"you-would-get-this-from-any-other-guy":false - ,"gonna-give-you-up":false - ,"gonna-let-you-down":false - ,"gonna-run-around":false - ,"desert-you":false - ,"make-you-cry":false - ,"tell-a-lie":false - ,"and-hurt-you":false - },["we", "are"]] - ,["-t one -t two -t three" - ,{t: ["one", "two", "three"]} - ,[]] - ,["-t one -t null -t three four five null" - ,{t: ["one", "null", "three"]} - ,["four", "five", "null"]] - ,["-t foo" - ,{t:["foo"]} - ,[]] - ,["--no-t" - ,{t:["false"]} - ,[]] - ,["-no-no-t" - ,{t:["true"]} - ,[]] - ,["-aoa one -aoa null -aoa 100" - ,{aoa:["one", null, '100']} - ,[]] - ,["-str 100" - ,{str:"100"} - ,[]] - ,["--color always" - ,{color:"always"} - ,[]] - ,["--no-nullstream" - ,{nullstream:null} - ,[]] - ,["--nullstream false" - ,{nullstream:null} - ,[]] - ,["--notadate=2011-01-25" - ,{notadate: "2011-01-25"} - ,[]] - ,["--date 2011-01-25" - ,{date: new Date("2011-01-25")} - ,[]] - ,["-cl 1" - ,{config: true, length: 1} - ,[] - ,{config: Boolean, length: Number, clear: Boolean} - ,{c: "--config", l: "--length"}] - ,["--acount bla" - ,{"acount":true} - ,["bla"] - ,{account: Boolean, credentials: Boolean, options: String} - ,{a:"--account", c:"--credentials",o:"--options"}] - ,["--clear" - ,{clear:true} - ,[] - ,{clear:Boolean,con:Boolean,len:Boolean,exp:Boolean,add:Boolean,rep:Boolean} - ,{c:"--con",l:"--len",e:"--exp",a:"--add",r:"--rep"}] - ,["--file -" - ,{"file":"-"} - ,[] - ,{file:String} - ,{}] - ,["--file -" - ,{"file":true} - ,["-"] - ,{file:Boolean} - ,{}] - ,["--path" - ,{"path":null} - ,[]] - ,["--path ." - ,{"path":process.cwd()} - ,[]] - ].forEach(function (test) { - var argv = test[0].split(/\s+/) - , opts = test[1] - , rem = test[2] - , actual = nopt(test[3] || types, test[4] || shorthands, argv, 0) - , parsed = actual.argv - delete actual.argv - for (var i in opts) { - var e = JSON.stringify(opts[i]) - , a = JSON.stringify(actual[i] === undefined ? null : actual[i]) - if (e && typeof e === "object") { - t.same(e, a) - } else { - t.equal(e, a) - } + ; [['-v', { version: true }, []], + ['---v', { version: true }, []], + ['ls -s --no-reg connect -d', + { loglevel: 'info', registry: null }, ['ls', 'connect']], + ['ls ---s foo', { loglevel: 'silent' }, ['ls', 'foo']], + ['ls --registry blargle', {}, ['ls']], + ['--no-registry', { registry: null }, []], + ['--no-color true', { color: false }, []], + ['--no-color false', { color: true }, []], + ['--no-color', { color: false }, []], + ['--color false', { color: false }, []], + ['--color --logfd 7', { logfd: 7, color: true }, []], + ['--color=true', { color: true }, []], + ['--logfd=10', { logfd: 10 }, []], + ['--tmp=/tmp -tar=gtar', { tmp: isWin ? 'C:\\tmp' : '/tmp', tar: 'gtar' }, []], + ['--tmp=tmp -tar=gtar', + { tmp: path.resolve(process.cwd(), 'tmp'), tar: 'gtar' }, []], + ['--logfd x', {}, []], + ['a -true -- -no-false', { true: true }, ['a', '-no-false']], + ['a -no-false', { false: false }, ['a']], + ['a -no-no-true', { true: true }, ['a']], + ['a -no-no-no-false', { false: false }, ['a']], + ['---NO-no-No-no-no-no-nO-no-no' + + '-No-no-no-no-no-no-no-no-no' + + '-no-no-no-no-NO-NO-no-no-no-no-no-no' + + '-no-body-can-do-the-boogaloo-like-I-do', + { 'body-can-do-the-boogaloo-like-I-do': false }, []], + ['we are -no-strangers-to-love ' + + '--you-know=the-rules --and=so-do-i ' + + '---im-thinking-of=a-full-commitment ' + + '--no-you-would-get-this-from-any-other-guy ' + + '--no-gonna-give-you-up ' + + '-no-gonna-let-you-down=true ' + + '--no-no-gonna-run-around false ' + + '--desert-you=false ' + + '--make-you-cry false ' + + '--no-tell-a-lie ' + + '--no-no-and-hurt-you false', + { 'strangers-to-love': false, + 'you-know': 'the-rules', + and: 'so-do-i', + 'you-would-get-this-from-any-other-guy': false, + 'gonna-give-you-up': false, + 'gonna-let-you-down': false, + 'gonna-run-around': false, + 'desert-you': false, + 'make-you-cry': false, + 'tell-a-lie': false, + 'and-hurt-you': false, + }, ['we', 'are']], + ['-t one -t two -t three', + { t: ['one', 'two', 'three'] }, + []], + ['-t one -t null -t three four five null', + { t: ['one', 'null', 'three'] }, + ['four', 'five', 'null']], + ['-t foo', + { t: ['foo'] }, + []], + ['--no-t', + { t: ['false'] }, + []], + ['-no-no-t', + { t: ['true'] }, + []], + ['-aoa one -aoa null -aoa 100', + { aoa: ['one', null, '100'] }, + []], + ['-str 100', + { str: '100' }, + []], + ['--color always', + { color: 'always' }, + []], + ['--no-nullstream', + { nullstream: null }, + []], + ['--nullstream false', + { nullstream: null }, + []], + ['--notadate=2011-01-25', + { notadate: '2011-01-25' }, + []], + ['--date 2011-01-25', + { date: new Date('2011-01-25') }, + []], + ['-cl 1', + { config: true, length: 1 }, + [], + { config: Boolean, length: Number, clear: Boolean }, + { c: '--config', l: '--length' }], + ['--acount bla', + { acount: true }, + ['bla'], + { account: Boolean, credentials: Boolean, options: String }, + { a: '--account', c: '--credentials', o: '--options' }], + ['--clear', + { clear: true }, + [], + { clear: Boolean, con: Boolean, len: Boolean, exp: Boolean, add: Boolean, rep: Boolean }, + { c: '--con', l: '--len', e: '--exp', a: '--add', r: '--rep' }], + ['--file -', + { file: '-' }, + [], + { file: String }, + {}], + ['--file -', + { file: true }, + ['-'], + { file: Boolean }, + {}], + ['--path', + { path: null }, + []], + ['--path .', + { path: process.cwd() }, + []], + ].forEach(function (params) { + var argv = params[0].split(/\s+/) + var opts = params[1] + var rem = params[2] + var actual = nopt(params[3] || types, params[4] || shorthands, argv, 0) + var parsed = actual.argv + delete actual.argv + for (var i in opts) { + var e = JSON.stringify(opts[i]) + var a = JSON.stringify(actual[i] === undefined ? null : actual[i]) + if (e && typeof e === 'object') { + t.same(e, a) + } else { + t.equal(e, a) } - t.same(rem, parsed.remain) - }) + } + t.same(rem, parsed.remain) + }) t.end() })