Comparing version 2020.7.31 to 2020.8.5
112
index.js
@@ -1,42 +0,100 @@ | ||
const has = (a, prop) => Object.prototype.hasOwnProperty.call(a, prop) | ||
const parse_arg = (arg, opts) => { | ||
// a argument, o option, kv key-value | ||
const parse_arg = (arg) => { | ||
if (arg.includes('__proto__')) | ||
throw Error('__proto__ not allowed as an argument to prevent prototype pollution.') | ||
throw Error('__proto__ not allowed within an argument to prevent prototype pollution.') | ||
if (arg.startsWith('--')) { | ||
// single flag OR long value | ||
// single option OR key-value | ||
const flag = arg.slice(2) | ||
if (flag === '') | ||
throw Error('Option - given without key. Expected extra character, eg: -h -v ..etc') | ||
} else if (arg.startsWith('-')) { | ||
// get bool flags | ||
const flag = arg.slice(1) | ||
if (flag === '') | ||
throw Error('Option - given without key. Expected extra character, eg: -h -v ..etc') | ||
else return flag | ||
.split('') | ||
.map(o => ({ [o]: true })) | ||
.reduce((acc, next) => ({ ...acc, ...next }), {}) | ||
throw Error('-- not supported yet. Expected extra character, eg: --h --v ..etc') | ||
else if (flag.includes('=')) { | ||
const [option, value] = flag.split('=') | ||
if (option === '' || value === '') | ||
throw Error(`Option key or value has length of 0 (${arg}). Expected at least one, eg: --pet=cat`) | ||
else { | ||
return ['kv', [option, value]] | ||
} | ||
} | ||
else return ['o', [flag]] | ||
} | ||
else if (arg.startsWith('-') && arg.length > 1) { | ||
// options | ||
const options = arg.slice(1).split('') | ||
return ['o', options] | ||
} else { | ||
// unflagged option | ||
return { 'clia': arg } | ||
// argument | ||
return ['a', arg] | ||
} | ||
return 0 | ||
} | ||
const add_args = (obj, key, val) => { | ||
if (!obj.args[key]) | ||
obj.args[key] = [val] | ||
else obj.args[key].push(val) | ||
} | ||
const combine_options = (opts) => | ||
opts.reduce((acc, next) => { | ||
const parsed = parse_arg(next, acc) | ||
const [kind, parsed] = parse_arg(next) | ||
if (has(parsed, 'clia') && has(acc, 'clia')) | ||
throw Error(`unflagged option [${arg}] previously set with: [${opts}]`) | ||
if (kind === 'o') { | ||
const options = parsed.map(o => ({ [o]: true })).reduce((acc, next) => ({ ...acc, ...next }), {}) | ||
acc.opt = { ...acc.opt, ...options } | ||
return { | ||
opts: { ...acc.opts, ...options }, | ||
tag: parsed.length == 1 && parsed.find(_ => true) || acc.tag, | ||
opt: acc.opt, | ||
args: acc.args, | ||
plain: acc.plain | ||
} | ||
return { ...parsed, ...acc } | ||
}, {}) | ||
} else if (kind === 'a') { | ||
// let flags = [] | ||
// let options = [] | ||
// let rest = [] | ||
acc.opts.$$.push(parsed) | ||
module.exports = (args = []) => combine_options(args) | ||
if (acc.tag) { | ||
const prop = '$' + acc.tag | ||
if (!acc.opts[prop]) | ||
acc.opts[prop] = [parsed] | ||
else acc.opts[prop].push(parsed) | ||
} | ||
if (acc.tag) { | ||
add_args(acc, acc.tag, parsed) | ||
} else acc.plain.push(parsed) | ||
return acc | ||
} else if (kind === 'kv') { | ||
const [k, v] = parsed | ||
const obj = {} | ||
obj[`$${k}`] = v | ||
add_args(acc, k, v) | ||
return { | ||
opts: { ...acc.opts, ...obj }, | ||
tag: acc.tag, | ||
opt: acc.opt, | ||
args: acc.args, | ||
plain: acc.plain | ||
} | ||
} else throw Error('kind mismatch') | ||
}, { | ||
opts: { $$: [] }, | ||
tag: undefined, | ||
opt: {}, | ||
args: {}, | ||
plain: [] | ||
}) | ||
module.exports = (args = []) => { | ||
const wip = combine_options(args) | ||
// add arg proxy to .clia.args | ||
return wip.opts | ||
} |
{ | ||
"name": "clia", | ||
"version": "2020.7.31", | ||
"version": "2020.8.5", | ||
"description": "Command line parser and t3st example project", | ||
"main": "index.js", | ||
"scripts": { | ||
"test": "npx t3st" | ||
"test": "npx t3st", | ||
"start": "npx nodemon -x \"npx t3st\"", | ||
"lint": "npx eslint ." | ||
}, | ||
@@ -30,4 +32,4 @@ "files": [ | ||
"devDependencies": { | ||
"t3st": "^2020.7.29" | ||
"t3st": "^2020.8.6" | ||
} | ||
} | ||
} |
# clia | ||
Command line parser and t3st example project | ||
Command line arguments parser and [t3st](https://www.npmjs.com/package/t3st) example project | ||
## usage | ||
In your-node-app: | ||
```js | ||
const clia = require('clia') | ||
const opts = process.argv.slice(2) | ||
const opts = clia(process.argv.slice(2)) | ||
``` | ||
Input | ||
From the command line input flags (abcd) and unflagged (hello) | ||
```bash | ||
@@ -25,18 +27,81 @@ node your-node-app -a -ab -cd hello | ||
d: true | ||
clia: 'hello' | ||
$$: ['hello'] | ||
} | ||
``` | ||
## Errors are thrown for: | ||
## parlance - options and arguments | ||
``` | ||
cli a -b c --d --e=f | ||
a: argument (untagged) | ||
b: option (short) | ||
c: argument (tagged) | ||
d: option (long) | ||
e: option (key-value) | ||
f: argument (key-value) | ||
``` | ||
## option -> boolean flag(s) | ||
* **short** option | ||
* starts with single `-` | ||
* refers to one or more options | ||
* **long** option | ||
* starts with double `--` | ||
* refers to one option | ||
* **key-value** option | ||
* the `key` in `--key=value` | ||
### argument -> character(s) | ||
* **untagged**: | ||
* argument(s) preceding any options | ||
* **tagged** | ||
* argument(s) succeeding the last short option or long option | ||
* **key-value** argument | ||
* the `value` in `--key=value` | ||
## parsing | ||
* When a key-value option is stated more than once, the last value is used assigned | ||
## errors are thrown for: | ||
* `__proto__` to prevent prototype pollution | ||
* multiple unflagged arguments to prevent repetition/mismatch error | ||
* dangling `-` or `--` arguments | ||
* dangling `--` argument (WIP `--` will indicate all subsequent input to be treated as arguments) | ||
## Testing | ||
## testing | ||
Clone and run tests: | ||
```bash | ||
git clone https://github.com/devmachiine/clia.git | ||
cd clia | ||
npm i # optional | ||
npm i -g nodemon # optional | ||
npm test | ||
``` | ||
``` | ||
To run live _(aka hot-reload)_ tests: | ||
```bash | ||
# ctrl+c to exit. | ||
npm start | ||
``` | ||
## references | ||
[The Art of Unix Programming](http://www.catb.org/~esr/writings/taoup/html/ch10s05.html) | ||
[GNU argument syntax conventions](https://www.gnu.org/software/libc/manual/html_node/Argument-Syntax.html) | ||
[getopts](https://github.com/jorgebucaran/getopts#readme) (therefore [this IEEE doc](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap12.html#tag_12_02)) | ||
![CI](https://github.com/devmachiine/clia/workflows/CI/badge.svg) | ||
[![License](https://img.shields.io/badge/license-MIT-black)](https://img.shields.io/badge/license-MIT-black) | ||
<!-- Todo Metrics | ||
[![Snyk](https://img.shields.io/npm/t3st/two.svg)](https://npmjs.com/two) | ||
[![Coverage](https://img.shields.io/npm/t3st/four.svg)](https://npmjs.com/four) | ||
[![OtherMetric](https://img.shields.io/npm/t3st/one.svg)](https://npmjs.com/one) | ||
--> |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
7162
5
83
106