123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150 |
- /*
- * Intro
- *
- * Very simple implementation of an option parser. It supports long and short
- * options, bundled options (-avd) and options with values. It receives an
- * argument object (argObj) describing the options, their type (not to be
- * confused with their value type, i.e Integer, String, etc), a default value
- * (if any), and, possibly, functions for value validation and convertion. It
- * returns an object contanining key-value pairs of the defined long options. If
- * a option of type 'value' is not specified, and if there is no default value,
- * it will be set no null.
- *
- * This 'getOpt' also forces the definition of long option names, this is in
- * attempt to make the returned option object more readable. But nothing is
- * stopping the user from defining single letter long option names (i.e '--l').
- *
- * Brief example
- *
- * Example of an argObj:
- *
- * {
- * 'has-value':
- * {
- * type: 'value',
- * short: 'h',
- * convert: v => parseInt(v),
- * validate: v => v > 1
- * }
- * 'verbose': {
- * type: 'flag',
- * short: 'v'
- * }
- * }
- *
- * If a command with the options of '--has-value 5 --verbose' or '-h 5 -v' was
- * supplied, it would generate and the options object: { 'has-value': 5,
- * verbose: true }.
- *
- * Argument Object
- *
- * type
- *
- * There are two types of arguments supported: arguments with user defined
- * values ('type: value') and arguments that act as switches ('type: flag') and
- * carry no user specified value. They have two strategies for parsing: type
- * 'value' consumes the next immediate argument; and type 'flag' doesn't consume
- * anything on the argument parsing loop.
- *
- * short
- *
- * A short version of the option, meant to be used with a single dash ('-')
- * character. Short options can be bundled together ('-abc' or '-p3000'), but
- * only if they are all of type 'flag', or if the there is a single 'value' type
- * option on the end ('-abp3000').
- *
- * default
- *
- * Default value for option in case none is specified.
- *
- * convert
- *
- * A optional function to perform any required operations on the extracted
- * value. Its executed before the 'validate' function.
- *
- * validate
- *
- * An optional function that tests the value of value. Its exectued after the
- * 'convert' function and must return a falsy or truthy value.
- */
- "use strict";
- export function getOpt(argObj) {
- let argv = process.argv.slice(2)
- let opts = {};
- let notOpts = [];
-
- let arg;
- while ((arg = argv.shift())) {
- // Skip non-option args
- if (!arg.startsWith('-')) {
- notOpts.push(arg);
- continue;
- }
-
- // End of options found
- if (arg == '--') break;
- // Remove dashes from option
- let argOpt = arg.slice(1);
- if (argOpt[0] == '-')
- argOpt = argKey.slice(1);
- try {
- // Split possible bundled options
- let argKey;
- let found = false;
- for (let i = 0; i < argOpt.length; i++) {
- const c = argOpt[i];
- for (const k of Object.keys(argObj)) {
- if (argObj[k].short == c) {
- if (!argKey) argKey = k;
- else argv.unshift(c);
- found = true;
- }
- }
- if (!found) {
- argv.unshift(argOpt.slice(i, argObj.length));
- break;
- }
- found = false;
- }
-
- let type = argObj[argKey].type;
-
- switch (type) {
- case 'value':
- let val = argv.shift();
-
- const validateFunc = argObj[argKey].validate;
- const convertFunc = argObj[argKey].convert;
- if (convertFunc) val = convertFunc(val)
- if (validateFunc) {
- if (!validateFunc(val)) throw '';
- }
- opts[argKey] = val;
- break;
- case 'flag':
- opts[argOpt] = true;
- argv.shift();
- break;
- default:
- die(`Invalid string '${type}' for getOpt`);
- }
- } catch (e) {
- die(`Invalid argument '${arg}'`);
- }
- }
- if (notOpts.length >= 1)
- process.argv = process.argv.slice(0, 2).concat(notOpts);
- Object.keys(argObj).forEach(k => {
- if (!opts[k]) {
- if (argObj[k].default)
- opts[k] = argObj[k].default;
- else opts[k] = null;
- }
- });
- return opts;
- }
|