diff --git a/.eslintrc b/.eslintrc index 7074004..bd1a5e0 100644 --- a/.eslintrc +++ b/.eslintrc @@ -5,36 +5,25 @@ "rules": { "array-element-newline": 0, - "camelcase": 1, - "comma-dangle": 1, "complexity": 0, - "curly": 1, - "dot-notation": 1, - "func-style": 1, + "func-style": [2, "declaration"], "max-lines-per-function": 0, "max-nested-callbacks": 1, + "max-statements-per-line": 1, "max-statements": 0, "multiline-comment-style": 0, - "no-array-constructor": 1, "no-continue": 1, - "no-div-regex": 1, - "no-extra-parens": 1, - "no-mixed-operators": 1, "no-param-reassign": 1, - "no-plusplus": 1, - "no-proto": 1, - "no-redeclare": 1, "no-restricted-syntax": 1, - "no-shadow": 1, - "no-unused-vars": 1, - "no-use-before-define": 1, - "object-curly-newline": 1, - "operator-linebreak": 1, - "quote-props": 1, - "quotes": 1, - "semi-style": 1, - "semi": 1, - "strict": 1, - "wrap-regex": 1, + "object-curly-newline": 0, }, + + "overrides": [ + { + "files": "test/**", + "rules": { + "camelcase": 0, + }, + }, + ] } diff --git a/example/parse.js b/example/parse.js index abff3e8..9e982e1 100644 --- a/example/parse.js +++ b/example/parse.js @@ -1,2 +1,4 @@ +'use strict'; + var argv = require('../')(process.argv.slice(2)); console.dir(argv); diff --git a/index.js b/index.js index 5ce6216..ad21027 100644 --- a/index.js +++ b/index.js @@ -1,12 +1,57 @@ +'use strict'; + +function isNumber(x) { + if (typeof x === 'number') { return true; } + if ((/^0x[0-9a-f]+$/i).test(x)) { return true; } + return (/^[-+]?(?:\d+(?:\.\d*)?|\.\d+)(e[-+]?\d+)?$/).test(x); +} + +function setKey(obj, keys, value) { + var o = obj; + var key; + for (var i = 0; i < keys.length - 1; i++) { + key = keys[i]; + if (key === '__proto__') { return; } + if (o[key] === undefined) { o[key] = {}; } + if (o[key] === Object.prototype || o[key] === Number.prototype + || o[key] === String.prototype) { o[key] = {}; } + if (o[key] === Array.prototype) { o[key] = []; } + o = o[key]; + } + + key = keys[keys.length - 1]; + if (key === '__proto__') { return; } + if (o === Object.prototype || o === Number.prototype + || o === String.prototype) { o = {}; } + if (o === Array.prototype) { o = []; } + if (o[key] === undefined || typeof o[key] === 'boolean') { + o[key] = value; + } else if (Array.isArray(o[key])) { + o[key].push(value); + } else { + o[key] = [o[key], value]; + } +} + +function hasKey(obj, keys) { + var o = obj; + keys.slice(0, -1).forEach(function (key) { + o = o[key] || {}; + }); + + var key = keys[keys.length - 1]; + return key in o; +} + module.exports = function (args, opts) { - if (!opts) opts = {}; + if (!opts) { opts = {}; } var flags = { bools: {}, strings: {} }; - if (typeof opts['boolean'] === 'boolean' && opts['boolean']) { + if (typeof opts.boolean === 'boolean' && opts.boolean) { flags.allBools = true; } else { - [].concat(opts['boolean']).filter(Boolean).forEach(function (key) { + [].concat(opts.boolean).filter(Boolean).forEach(function (key) { flags.bools[key] = true; }); } @@ -28,9 +73,20 @@ module.exports = function (args, opts) { } }); - var defaults = opts['default'] || {}; + var defaults = opts.default || {}; var argv = { _: [] }; + + function setArg(key, val) { + var value = !flags.strings[key] && isNumber(val) + ? Number(val) : val; + setKey(argv, key.split('.'), value); + + (aliases[key] || []).forEach(function (x) { + setKey(argv, x.split('.'), value); + }); + } + Object.keys(flags.bools).forEach(function (key) { setArg(key, defaults[key] === undefined ? false : defaults[key]); }); @@ -42,58 +98,49 @@ module.exports = function (args, opts) { args = args.slice(0, args.indexOf('--')); } - function setArg(key, val) { - var value = !flags.strings[key] && isNumber(val) - ? Number(val) : val - ; - setKey(argv, key.split('.'), value); - - (aliases[key] || []).forEach(function (x) { - setKey(argv, x.split('.'), value); - }); - } - for (var i = 0; i < args.length; i++) { var arg = args[i]; + var key; + var next; - if (/^--.+=/.test(arg)) { + if ((/^--.+=/).test(arg)) { // Using [\s\S] instead of . because js doesn't support the // 'dotall' regex modifier. See: // http://stackoverflow.com/a/1068308/13216 var m = arg.match(/^--([^=]+)=([\s\S]*)$/); setArg(m[1], m[2]); - } else if (/^--no-.+/.test(arg)) { - var key = arg.match(/^--no-(.+)/)[1]; + } else if ((/^--no-.+/).test(arg)) { + key = arg.match(/^--no-(.+)/)[1]; setArg(key, false); - } else if (/^--.+/.test(arg)) { - var key = arg.match(/^--(.+)/)[1]; - var next = args[i + 1]; - if (next !== undefined && !/^-/.test(next) + } else if ((/^--.+/).test(arg)) { + key = arg.match(/^--(.+)/)[1]; + next = args[i + 1]; + if (next !== undefined && !(/^-/).test(next) && !flags.bools[key] && !flags.allBools && (aliases[key] ? !flags.bools[aliases[key]] : true)) { setArg(key, next); - i++; - } else if (/^(true|false)$/.test(next)) { + i += 1; + } else if ((/^(true|false)$/).test(next)) { setArg(key, next === 'true'); - i++; + i += 1; } else { setArg(key, flags.strings[key] ? '' : true); } - } else if (/^-[^-]+/.test(arg)) { + } else if ((/^-[^-]+/).test(arg)) { var letters = arg.slice(1, -1).split(''); var broken = false; for (var j = 0; j < letters.length; j++) { - var next = arg.slice(j + 2); + next = arg.slice(j + 2); if (next === '-') { - setArg(letters[j], next) + setArg(letters[j], next); continue; } - if (/[A-Za-z]/.test(letters[j]) - && /-?\d+(\.\d*)?(e-?\d+)?$/.test(next)) { + if ((/[A-Za-z]/).test(letters[j]) + && (/-?\d+(\.\d*)?(e-?\d+)?$/).test(next)) { setArg(letters[j], next); broken = true; break; @@ -108,88 +155,43 @@ module.exports = function (args, opts) { } } - var key = arg.slice(-1)[0]; + key = arg.slice(-1)[0]; if (!broken && key !== '-') { - if (args[i + 1] && !/^(-|--)[^-]/.test(args[i + 1]) + if (args[i + 1] && !(/^(-|--)[^-]/).test(args[i + 1]) && !flags.bools[key] && (aliases[key] ? !flags.bools[aliases[key]] : true)) { setArg(key, args[i + 1]); - i++; - } else if (args[i + 1] && /true|false/.test(args[i + 1])) { + i += 1; + } else if (args[i + 1] && (/true|false/).test(args[i + 1])) { setArg(key, args[i + 1] === 'true'); - i++; + i += 1; } else { setArg(key, flags.strings[key] ? '' : true); } } } else { - argv._.push(flags.strings['_'] || !isNumber(arg) ? arg : Number(arg)); + argv._.push(flags.strings._ || !isNumber(arg) ? arg : Number(arg)); } } - Object.keys(defaults).forEach(function (key) { - if (!hasKey(argv, key.split('.'))) { - setKey(argv, key.split('.'), defaults[key]); + Object.keys(defaults).forEach(function (k) { + if (!hasKey(argv, k.split('.'))) { + setKey(argv, k.split('.'), defaults[k]); - (aliases[key] || []).forEach(function (x) { - setKey(argv, x.split('.'), defaults[key]); + (aliases[k] || []).forEach(function (x) { + setKey(argv, x.split('.'), defaults[k]); }); } }); if (opts['--']) { - argv['--'] = new Array(); - notFlags.forEach(function (key) { - argv['--'].push(key); - }); + argv['--'] = notFlags.slice(); } else { - notFlags.forEach(function (key) { - argv._.push(key); + notFlags.forEach(function (k) { + argv._.push(k); }); } return argv; }; -function hasKey(obj, keys) { - var o = obj; - keys.slice(0, -1).forEach(function (key) { - o = (o[key] || {}); - }); - - var key = keys[keys.length - 1]; - return key in o; -} - -function setKey(obj, keys, value) { - var o = obj; - for (var i = 0; i < keys.length - 1; i++) { - var key = keys[i]; - if (key === '__proto__') return; - if (o[key] === undefined) o[key] = {}; - if (o[key] === Object.prototype || o[key] === Number.prototype - || o[key] === String.prototype) o[key] = {}; - if (o[key] === Array.prototype) o[key] = []; - o = o[key]; - } - - var key = keys[keys.length - 1]; - if (key === '__proto__') return; - if (o === Object.prototype || o === Number.prototype - || o === String.prototype) o = {}; - if (o === Array.prototype) o = []; - if (o[key] === undefined || typeof o[key] === 'boolean') { - o[key] = value; - } else if (Array.isArray(o[key])) { - o[key].push(value); - } else { - o[key] = [o[key], value]; - } -} - -function isNumber(x) { - if (typeof x === 'number') return true; - if (/^0x[0-9a-f]+$/i.test(x)) return true; - return /^[-+]?(?:\d+(?:\.\d*)?|\.\d+)(e[-+]?\d+)?$/.test(x); -} - diff --git a/test/all_bool.js b/test/all_bool.js index 3df5709..bc47041 100644 --- a/test/all_bool.js +++ b/test/all_bool.js @@ -1,14 +1,16 @@ +'use strict'; + var parse = require('../'); var test = require('tape'); test('flag boolean true (default all --args to boolean)', function (t) { var argv = parse(['moo', '--honk', 'cow'], { - boolean: true + boolean: true, }); t.deepEqual(argv, { honk: true, - _: ['moo', 'cow'] + _: ['moo', 'cow'], }); t.deepEqual(typeof argv.honk, 'boolean'); @@ -17,14 +19,14 @@ test('flag boolean true (default all --args to boolean)', function (t) { test('flag boolean true only affects double hyphen arguments without equals signs', function (t) { var argv = parse(['moo', '--honk', 'cow', '-p', '55', '--tacos=good'], { - boolean: true + boolean: true, }); t.deepEqual(argv, { _: ['moo', 'cow'], honk: true, p: 55, - tacos: 'good' + tacos: 'good', }); t.deepEqual(typeof argv.honk, 'boolean'); diff --git a/test/bool.js b/test/bool.js index c5f15e3..42f4c2c 100644 --- a/test/bool.js +++ b/test/bool.js @@ -1,16 +1,18 @@ +'use strict'; + var parse = require('../'); var test = require('tape'); test('flag boolean default false', function (t) { var argv = parse(['moo'], { boolean: ['t', 'verbose'], - default: { verbose: false, t: false } + default: { verbose: false, t: false }, }); t.deepEqual(argv, { verbose: false, t: false, - _: ['moo'] + _: ['moo'], }); t.deepEqual(typeof argv.verbose, 'boolean'); @@ -21,14 +23,14 @@ test('flag boolean default false', function (t) { test('boolean groups', function (t) { var argv = parse(['-x', '-z', 'one', 'two', 'three'], { - boolean: ['x', 'y', 'z'] + boolean: ['x', 'y', 'z'], }); t.deepEqual(argv, { x: true, y: false, z: true, - _: ['one', 'two', 'three'] + _: ['one', 'two', 'three'], }); t.deepEqual(typeof argv.x, 'boolean'); @@ -39,21 +41,19 @@ test('boolean groups', function (t) { test('boolean and alias with chainable api', function (t) { var aliased = ['-h', 'derp']; var regular = ['--herp', 'derp']; - var opts = { - herp: { alias: 'h', boolean: true } - }; + var aliasedArgv = parse(aliased, { boolean: 'herp', - alias: { h: 'herp' } + alias: { h: 'herp' }, }); var propertyArgv = parse(regular, { boolean: 'herp', - alias: { h: 'herp' } + alias: { h: 'herp' }, }); var expected = { herp: true, h: true, - '_': ['derp'] + _: ['derp'], }; t.same(aliasedArgv, expected); @@ -65,15 +65,15 @@ test('boolean and alias with options hash', function (t) { var aliased = ['-h', 'derp']; var regular = ['--herp', 'derp']; var opts = { - alias: { 'h': 'herp' }, - boolean: 'herp' + alias: { h: 'herp' }, + boolean: 'herp', }; var aliasedArgv = parse(aliased, opts); var propertyArgv = parse(regular, opts); var expected = { herp: true, h: true, - '_': ['derp'] + _: ['derp'], }; t.same(aliasedArgv, expected); t.same(propertyArgv, expected); @@ -85,14 +85,14 @@ test('boolean and alias using explicit true', function (t) { var regular = ['--herp', 'true']; var opts = { alias: { h: 'herp' }, - boolean: 'h' + boolean: 'h', }; var aliasedArgv = parse(aliased, opts); var propertyArgv = parse(regular, opts); var expected = { herp: true, h: true, - '_': [] + _: [], }; t.same(aliasedArgv, expected); @@ -103,14 +103,14 @@ test('boolean and alias using explicit true', function (t) { // regression, see https://github.com/substack/node-optimist/issues/71 test('boolean and --x=true', function (t) { var parsed = parse(['--boool', '--other=true'], { - boolean: 'boool' + boolean: 'boool', }); t.same(parsed.boool, true); t.same(parsed.other, 'true'); parsed = parse(['--boool', '--other=false'], { - boolean: 'boool' + boolean: 'boool', }); t.same(parsed.boool, true); diff --git a/test/dash.js b/test/dash.js index e61a079..7c897d4 100644 --- a/test/dash.js +++ b/test/dash.js @@ -1,3 +1,5 @@ +'use strict'; + var parse = require('../'); var test = require('tape'); diff --git a/test/default_bool.js b/test/default_bool.js index d25dae7..ce93557 100644 --- a/test/default_bool.js +++ b/test/default_bool.js @@ -1,10 +1,12 @@ +'use strict'; + var test = require('tape'); var parse = require('../'); test('boolean default true', function (t) { var argv = parse([], { boolean: 'sometrue', - default: { sometrue: true } + default: { sometrue: true }, }); t.equal(argv.sometrue, true); t.end(); @@ -13,7 +15,7 @@ test('boolean default true', function (t) { test('boolean default false', function (t) { var argv = parse([], { boolean: 'somefalse', - default: { somefalse: false } + default: { somefalse: false }, }); t.equal(argv.somefalse, false); t.end(); diff --git a/test/dotted.js b/test/dotted.js index ed9e9d2..126ff03 100644 --- a/test/dotted.js +++ b/test/dotted.js @@ -1,3 +1,5 @@ +'use strict'; + var parse = require('../'); var test = require('tape'); diff --git a/test/long.js b/test/long.js index 99fe947..9fef51f 100644 --- a/test/long.js +++ b/test/long.js @@ -1,3 +1,5 @@ +'use strict'; + var test = require('tape'); var parse = require('../'); diff --git a/test/num.js b/test/num.js index 2075efe..074393e 100644 --- a/test/num.js +++ b/test/num.js @@ -1,3 +1,5 @@ +'use strict'; + var parse = require('../'); var test = require('tape'); @@ -8,7 +10,7 @@ test('nums', function (t) { '-z', '1e7', '-w', '10f', '--hex', '0xdeadbeef', - '789' + '789', ]); t.deepEqual(argv, { x: 1234, @@ -16,7 +18,7 @@ test('nums', function (t) { z: 1e7, w: '10f', hex: 0xdeadbeef, - _: [789] + _: [789], }); t.deepEqual(typeof argv.x, 'number'); t.deepEqual(typeof argv.y, 'number'); diff --git a/test/parse.js b/test/parse.js index 3eee946..f88c816 100644 --- a/test/parse.js +++ b/test/parse.js @@ -1,3 +1,5 @@ +'use strict'; + var parse = require('../'); var test = require('tape'); @@ -22,7 +24,7 @@ test('comprehensive', function (t) { '-h', 'awesome', '--multi=quux', '--key', 'value', '-b', '--bool', '--no-meep', '--multi=baz', - '--', '--not-a-flag', 'eek' + '--', '--not-a-flag', 'eek', ]), { c: true, @@ -36,7 +38,7 @@ test('comprehensive', function (t) { multi: ['quux', 'baz'], meep: false, name: 'meowmers', - _: ['bare', '--not-a-flag', 'eek'] + _: ['bare', '--not-a-flag', 'eek'], } ); t.end(); @@ -52,13 +54,13 @@ test('flag boolean', function (t) { test('flag boolean value', function (t) { var argv = parse(['--verbose', 'false', 'moo', '-t', 'true'], { boolean: ['t', 'verbose'], - default: { verbose: true } + default: { verbose: true }, }); t.deepEqual(argv, { verbose: false, t: true, - _: ['moo'] + _: ['moo'], }); t.deepEqual(typeof argv.verbose, 'boolean'); @@ -67,15 +69,15 @@ test('flag boolean value', function (t) { }); test('newlines in params', function (t) { - var args = parse(['-s', "X\nX"]) - t.deepEqual(args, { _: [], s: "X\nX" }); + var args = parse(['-s', 'X\nX']); + t.deepEqual(args, { _: [], s: 'X\nX' }); // reproduce in bash: // VALUE="new // line" // node program.js --s="$VALUE" - args = parse(["--s=X\nX"]) - t.deepEqual(args, { _: [], s: "X\nX" }); + args = parse(['--s=X\nX']); + t.deepEqual(args, { _: [], s: 'X\nX' }); t.end(); }); @@ -110,7 +112,7 @@ test('empty strings', function (t) { t.equal(typeof str, 'string'); var letters = parse(['-art'], { - string: ['a', 't'] + string: ['a', 't'], }); t.equal(letters.a, ''); @@ -123,7 +125,7 @@ test('empty strings', function (t) { test('string and alias', function (t) { var x = parse(['--str', '000123'], { string: 's', - alias: { s: 'str' } + alias: { s: 'str' }, }); t.equal(x.str, '000123'); @@ -133,7 +135,7 @@ test('string and alias', function (t) { var y = parse(['-s', '000123'], { string: 'str', - alias: { str: 's' } + alias: { str: 's' }, }); t.equal(y.str, '000123'); @@ -157,7 +159,7 @@ test('slashBreak', function (t) { test('alias', function (t) { var argv = parse(['-f', '11', '--zoom', '55'], { - alias: { z: 'zoom' } + alias: { z: 'zoom' }, }); t.equal(argv.zoom, 55); t.equal(argv.z, argv.zoom); @@ -167,7 +169,7 @@ test('alias', function (t) { test('multiAlias', function (t) { var argv = parse(['-f', '11', '--zoom', '55'], { - alias: { z: ['zm', 'zoom'] } + alias: { z: ['zm', 'zoom'] }, }); t.equal(argv.zoom, 55); t.equal(argv.z, argv.zoom); @@ -180,7 +182,7 @@ test('nested dotted objects', function (t) { var argv = parse([ '--foo.bar', '3', '--foo.baz', '4', '--foo.quux.quibble', '5', '--foo.quux.o_O', - '--beep.boop' + '--beep.boop', ]); t.same(argv.foo, { @@ -188,8 +190,8 @@ test('nested dotted objects', function (t) { baz: 4, quux: { quibble: 5, - o_O: true - } + o_O: true, + }, }); t.same(argv.beep, { boop: true }); t.end(); diff --git a/test/parse_modified.js b/test/parse_modified.js index 3bcad6a..9a3d298 100644 --- a/test/parse_modified.js +++ b/test/parse_modified.js @@ -1,3 +1,5 @@ +'use strict'; + var parse = require('../'); var test = require('tape'); diff --git a/test/proto.js b/test/proto.js index a97e133..589efd7 100644 --- a/test/proto.js +++ b/test/proto.js @@ -1,6 +1,10 @@ +'use strict'; + var parse = require('../'); var test = require('tape'); +/* eslint no-proto: 0 */ + test('proto pollution', function (t) { var argv = parse(['--__proto__.x', '123']); t.equal({}.x, undefined); diff --git a/test/short.js b/test/short.js index d65dc08..4a7b843 100644 --- a/test/short.js +++ b/test/short.js @@ -1,3 +1,5 @@ +'use strict'; + var parse = require('../'); var test = require('tape'); @@ -49,7 +51,7 @@ test('mixed short bool and capture', function (t) { parse(['-h', 'localhost', '-fp', '555', 'script.js']), { f: true, p: 555, h: 'localhost', - _: ['script.js'] + _: ['script.js'], } ); t.end(); @@ -60,7 +62,7 @@ test('short and long', function (t) { parse(['-h', 'localhost', '-fp', '555', 'script.js']), { f: true, p: 555, h: 'localhost', - _: ['script.js'] + _: ['script.js'], } ); t.end(); diff --git a/test/whitespace.js b/test/whitespace.js index e79c482..4fdaf1d 100644 --- a/test/whitespace.js +++ b/test/whitespace.js @@ -1,3 +1,5 @@ +'use strict'; + var parse = require('../'); var test = require('tape');