diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..a858fe0 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +github: lukeed \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..96fa516 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,20 @@ +name: CI + +on: [push, pull_request] + +jobs: + test: + name: Node.js v6 + runs-on: ubuntu-latest + timeout-minutes: 3 + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-node@v2 + with: + node-version: 6 + + - name: Install + run: npm install + + - name: Tests + run: npm test \ No newline at end of file diff --git a/.gitignore b/.gitignore index a734e5b..fa297e8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,7 @@ node_modules .DS_Store +*-lock.* *.lock *.log + +/lib diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 7503aad..0000000 --- a/.travis.yml +++ /dev/null @@ -1,4 +0,0 @@ -language: node_js -node_js: - - 6 - - 4 diff --git a/bench/index.js b/bench/index.js index 6cef9ad..110d355 100644 --- a/bench/index.js +++ b/bench/index.js @@ -1,18 +1,34 @@ -const nopt = require('nopt'); +const { Suite } = require('benchmark'); const previous = require('mri'); + +console.log('Load Times:'); + +console.time('nopt'); +const nopt = require('nopt'); +console.timeEnd('nopt'); + +console.time('yargs-parser'); const yargs = require('yargs-parser'); -const { Suite } = require('benchmark'); +console.timeEnd('yargs-parser'); + +console.time('minimist'); const minimist = require('minimist'); +console.timeEnd('minimist'); + +console.time('mri'); const mri = require('../lib'); +console.timeEnd('mri'); + +console.log('\nBenchmark:'); const bench = new Suite(); const args = ['-b', '--bool', '--no-meep', '--multi=baz']; bench - .add('minimist ', () => minimist(args)) - .add('mri (prev) ', () => previous(args)) - .add('mri ', () => mri(args)) - .add('nopt ', () => nopt(args)) - .add('yargs-parser', () => yargs(args)) + .add('minimist ', () => minimist(args)) + .add('mri (1.1.1) ', () => previous(args)) + .add('mri ', () => mri(args)) + .add('nopt ', () => nopt(args)) + .add('yargs-parser ', () => yargs(args)) .on('cycle', e => console.log(String(e.target))) .run(); diff --git a/bench/package.json b/bench/package.json index db3996b..f567e3f 100644 --- a/bench/package.json +++ b/bench/package.json @@ -1,9 +1,9 @@ { "devDependencies": { - "benchmark": "^2.1.4", - "minimist": "^1.2.0", - "mri": "^0.1.0", - "nopt": "^4.0.1", - "yargs-parser": "^11.1.1" + "benchmark": "2.1.4", + "minimist": "1.2.5", + "mri": "1.1.1", + "nopt": "5.0.0", + "yargs-parser": "20.2.9" } } diff --git a/index.d.ts b/index.d.ts new file mode 100644 index 0000000..a46e43d --- /dev/null +++ b/index.d.ts @@ -0,0 +1,21 @@ +type Dict = Record; +type Arrayable = T | T[]; +type Default = Dict; + +declare function mri(args?: string[], options?: mri.Options): mri.Argv; + +declare namespace mri { + export interface Options { + boolean?: Arrayable; + string?: Arrayable; + alias?: Dict>; + default?: Dict; + unknown?(flag: string): void; + } + + export type Argv = T & { + _: string[]; + } +} + +export = mri; diff --git a/package.json b/package.json index 8114446..5a224b2 100644 --- a/package.json +++ b/package.json @@ -1,23 +1,28 @@ { "name": "mri", - "version": "1.1.3", + "version": "1.2.0", "description": "Quickly scan for CLI flags and arguments", "repository": "lukeed/mri", + "module": "lib/index.mjs", "main": "lib/index.js", + "types": "index.d.ts", "license": "MIT", "files": [ + "*.d.ts", "lib" ], "author": { "name": "Luke Edwards", "email": "luke.edwards05@gmail.com", - "url": "lukeed.com" + "url": "https://lukeed.com" }, "engines": { "node": ">=4" }, "scripts": { + "build": "bundt", "bench": "node bench", + "pretest": "npm run build", "test": "tape test/*.js | tap-spec" }, "keywords": [ @@ -31,7 +36,8 @@ "args" ], "devDependencies": { - "tap-spec": "^4.1.1", - "tape": "^4.6.3" + "bundt": "1.0.2", + "tap-spec": "4.1.2", + "tape": "4.13.3" } } diff --git a/readme.md b/readme.md index d9cccb6..bff4a04 100644 --- a/readme.md +++ b/readme.md @@ -1,4 +1,4 @@ -# mri [![Build Status](https://travis-ci.org/lukeed/mri.svg?branch=master)](https://travis-ci.org/lukeed/mri) +# mri [![CI](https://github.com/lukeed/mri/workflows/CI/badge.svg?branch=master&event=push)](https://github.com/lukeed/mri/actions) [![licenses](https://licenses.dev/b/npm/mri)](https://licenses.dev/npm/mri) > Quickly scan for CLI flags and arguments @@ -145,13 +145,20 @@ Once an unknown flag is encountered, parsing will terminate, regardless of your ## Benchmarks -``` -# Node v10.13.0 +> Running Node.js v10.13.0 -minimist x 324,469 ops/sec ±1.20% (96 runs sampled) -mri x 1,611,167 ops/sec ±0.22% (96 runs sampled) -nopt x 920,029 ops/sec ±1.13% (97 runs sampled) -yargs-parser x 39,542 ops/sec ±1.14% (95 runs sampled) +``` +Load Times: + nopt 3.179ms + yargs-parser 2.137ms + minimist 0.746ms + mri 0.517ms + +Benchmark: + minimist x 328,747 ops/sec ±1.09% (89 runs sampled) + mri x 1,622,801 ops/sec ±0.94% (92 runs sampled) + nopt x 888,223 ops/sec ±0.22% (92 runs sampled) + yargs-parser x 30,538 ops/sec ±0.81% (91 runs sampled) ``` ## License diff --git a/lib/index.js b/src/index.js similarity index 81% rename from lib/index.js rename to src/index.js index b4d8a0a..1a233e5 100644 --- a/lib/index.js +++ b/src/index.js @@ -5,14 +5,14 @@ function toArr(any) { function toVal(out, key, val, opts) { var x, old=out[key], nxt=( !!~opts.string.indexOf(key) ? (val == null || val === true ? '' : String(val)) - : !!~opts.boolean.indexOf(key) ? (val === 'false' ? false : val === 'true' || (out._.push((x = +val,x * 0 === 0) ? x : val),!!val)) : typeof val === 'boolean' ? val + : !!~opts.boolean.indexOf(key) ? (val === 'false' ? false : val === 'true' || (out._.push((x = +val,x * 0 === 0) ? x : val),!!val)) : (x = +val,x * 0 === 0) ? x : val ); out[key] = old == null ? nxt : (Array.isArray(old) ? old.concat(nxt) : [old, nxt]); } -module.exports = function (args, opts) { +export default function (args, opts) { args = args || []; opts = opts || {}; @@ -36,18 +36,26 @@ module.exports = function (args, opts) { } } - opts.boolean.forEach(key => { - opts.boolean = opts.boolean.concat(opts.alias[key] = opts.alias[key] || []); - }); + for (i=opts.boolean.length; i-- > 0;) { + arr = opts.alias[opts.boolean[i]] || []; + for (j=arr.length; j-- > 0;) opts.boolean.push(arr[j]); + } - opts.string.forEach(key => { - opts.string = opts.string.concat(opts.alias[key] = opts.alias[key] || []); - }); + for (i=opts.string.length; i-- > 0;) { + arr = opts.alias[opts.string[i]] || []; + for (j=arr.length; j-- > 0;) opts.string.push(arr[j]); + } if (defaults) { for (k in opts.default) { - opts.alias[k] = opts.alias[k] || []; - (opts[typeof opts.default[k]] || []).push(k); + name = typeof opts.default[k]; + arr = opts.alias[k] = opts.alias[k] || []; + if (opts[name] !== void 0) { + opts[name].push(k); + for (i=0; i < arr.length; i++) { + opts[name].push(arr[i]); + } + } } } diff --git a/test/index.js b/test/index.js index 88b3738..37abaa5 100644 --- a/test/index.js +++ b/test/index.js @@ -93,6 +93,54 @@ test('flag default and alias', t => { t.end(); }); +test('flag default string w/ alias', t => { + t.deepEqual( + fn(['--arg', '01'], { + alias: { a: ['arg'] }, + default: { arg: '' }, + }), + { _: [], arg: '01', a: '01' } + ); + + t.deepEqual( + fn(['-a', '01'], { + alias: { a: ['arg'] }, + default: { arg: '' }, + }), + { _: [], arg: '01', a: '01' } + ); + + // --- + + t.deepEqual( + fn(['-a', '01'], { + alias: { arg: ['a'] }, + default: { a: '' }, + }), + { _: [], arg: '01', a: '01' } + ); + + t.deepEqual( + fn(['--arg', '01'], { + alias: { arg: ['a'] }, + default: { a: '' }, + }), + { _: [], arg: '01', a: '01' } + ); + + // --- + + t.deepEqual( + fn(['-a', '01'], { + alias: { arg: ['a'] }, + default: { arg: '' }, + }), + { _: [], arg: '01', a: '01' } + ); + + t.end(); +}); + // test('newlines in params' , t => { // var args = fn(['-s', "X\nX"]) // t.deepEqual(args, { _ : [], s : "X\nX" }); @@ -231,3 +279,52 @@ test('flag default null value', t => { t.same(argv, { foo:true, bar:null, _:[] }); t.end(); }); + +test('flag boolean with default', t => { + const foo = fn(['-t'], { default: { t:true }}); + t.same(foo, { t:true, _:[] }); + t.is(typeof foo.t, 'boolean'); + + const bar = fn(['-t'], { default: { t:false }}); + t.same(bar, { t:true, _:[] }); + t.is(typeof bar.t, 'boolean'); + + const baz = fn(['--no-two'], { default: { two:true }}); + t.same(baz, { two:false, _:[] }); + t.is(typeof baz.two, 'boolean'); + t.end(); +}); + +test('flag boolean with default & alias', t => { + const alias = { t: ['tt'], two:['toot'] }; + + const foo = fn(['-t'], { alias, default: { t:true }}); + t.same(foo, { t:true, tt:true, _:[] }); + t.is(typeof foo.t, 'boolean'); + + const bar = fn(['-t'], { alias, default: { t:false }}); + t.same(bar, { t:true, tt:true, _:[] }); + t.is(typeof bar.t, 'boolean'); + + const baz = fn(['--no-two'], { alias, default: { two:true }}); + t.same(baz, { two:false, toot:false, _:[] }); + t.is(typeof baz.two, 'boolean'); + + t.end(); +}); + +test('flag boolean with default string & alias', t => { + const foo = fn(['-t'], { default: { t:'hi' }}); + t.same(foo, { t:'', _:[] }); + t.is(typeof foo.t, 'string'); + + const bar = fn(['-t'], { alias:{ t:'tt' }, default: { t:'boo' }}); + t.same(bar, { t:'', tt:'', _:[] }); + t.is(typeof bar.t, 'string'); + + // --no-* overrides + const baz = fn(['--no-two'], { default: { two:'hi' }}); + t.same(baz, { two:false, _:[] }); + t.is(typeof baz.two, 'boolean'); + t.end(); +});