getopt-like CLI Options in node.js
getopt-like CLI Options in node.js
Parsing CLI options with getopt-ish configuration
#!/usr/bin/env node
function argvAddValue(ret, id, val) {
if (ret[id] === undefined) {
ret[id] = val;
return;
}
if (!Array.isArray(ret[id])) ret[id] = [ ret[id] ];
ret[id].push(val);
}
function argvParser(options, argv) {
if (!argv) argv = process.argv;
const { length } = argv;
const sym = Symbol.for('input');
const ret = {};
for(let idx = 2, needValue = false; idx < length; idx++){
const arg = argv[idx];
if(arg[0] !== '\-') {
if (ret[sym] === undefined) ret[sym] = [];
if (!needValue) ret[sym].push(arg);
continue;
}
for(const key of options){
let token = key;
needValue = Boolean(token.match(/(:$)/));
if (needValue) token = token.replace(/:$/, '');
const argIsLongOpt = Boolean(arg.match(/^\-\-/));
const baseToken = `(^` + token.replace(/\-/g, '\\-');
const regexp = argIsLongOpt
? new RegExp(baseToken + '(?:=|\$))')
: new RegExp(baseToken + ')');
const hasMatch = arg.match(regexp);
if (!hasMatch) continue;
// take long options name or first char of the token
const id = token.match(/(?:^|\|)\-\-(.*?)(?:=|$)/)?.[1] ?? arg[1];
if (!needValue) {
argvAddValue(ret, id, true);
break;
}
// no longer necessary, revert now
needValue = false;
if (argIsLongOpt) {
const afterEq = arg.match(/=(.*$)/);
if (afterEq) {
const value = afterEq[1];
argvAddValue(ret, id, value);
break;
}
}
if (!argIsLongOpt && arg.length > 2) {
const value = arg.replace(hasMatch[1], '');
argvAddValue(ret, id, value);
break;
}
const value = argv[++idx];
argvAddValue(ret, id, value);
}
}
return ret;
}
/* ***************************** EXAMPLE ************************************ */
const parsed = argvParser([
'-c', // short opt switch
'-b:', // short opt with value
'--long', // long opt switch
'--long-arg:', // long opt with option
'-v|--verbose', // short and long opt switch
'-e|--example:', // short and long opt with value
]);
console.log(parsed);
console.table(parsed);
console.table(parsed[Symbol.for('input')]);
/*******************************************************************************
* INPUT:
* ./argvParser.js \
* file1 \
* -v \
* -bIPv4 \
* --long \
* --long-arg arg1 \
* --long-arg=arg2 \
* -e example \
* file2
*
* OUTPUT:
* {
* verbose: true,
* b: 'IPv4',
* long: true,
* 'long-arg': [ 'arg1', 'arg2' ],
* example: 'example',
* [Symbol(input)]: [ 'file1', 'file2' ]
* }
* ┌──────────┬────────┬────────┬───────────┐
* │ (index) │ 0 │ 1 │ Values │
* ├──────────┼────────┼────────┼───────────┤
* │ verbose │ │ │ true │
* │ b │ │ │ 'IPv4' │
* │ long │ │ │ true │
* │ long-arg │ 'arg1' │ 'arg2' │ │
* │ example │ │ │ 'example' │
* └──────────┴────────┴────────┴───────────┘
* ┌─────────┬─────────┐
* │ (index) │ Values │
* ├─────────┼─────────┤
* │ 0 │ 'file1' │
* │ 1 │ 'file2' │
* └─────────┴─────────┘
* */