Umama-at-Bluchip/Quick-UI
0
1const flagSymbol = Symbol('arg flag');2 3class ArgError extends Error {4 constructor(msg, code) {5 super(msg);6 this.name = 'ArgError';7 this.code = code;8 9 Object.setPrototypeOf(this, ArgError.prototype);10 }11}12 13function arg(14 opts,15 {16 argv = process.argv.slice(2),17 permissive = false,18 stopAtPositional = false19 } = {}20) {21 if (!opts) {22 throw new ArgError(23 'argument specification object is required',24 'ARG_CONFIG_NO_SPEC'25 );26 }27 28 const result = { _: [] };29 30 const aliases = {};31 const handlers = {};32 33 for (const key of Object.keys(opts)) {34 if (!key) {35 throw new ArgError(36 'argument key cannot be an empty string',37 'ARG_CONFIG_EMPTY_KEY'38 );39 }40 41 if (key[0] !== '-') {42 throw new ArgError(43 `argument key must start with '-' but found: '${key}'`,44 'ARG_CONFIG_NONOPT_KEY'45 );46 }47 48 if (key.length === 1) {49 throw new ArgError(50 `argument key must have a name; singular '-' keys are not allowed: ${key}`,51 'ARG_CONFIG_NONAME_KEY'52 );53 }54 55 if (typeof opts[key] === 'string') {56 aliases[key] = opts[key];57 continue;58 }59 60 let type = opts[key];61 let isFlag = false;62 63 if (64 Array.isArray(type) &&65 type.length === 1 &&66 typeof type[0] === 'function'67 ) {68 const [fn] = type;69 type = (value, name, prev = []) => {70 prev.push(fn(value, name, prev[prev.length - 1]));71 return prev;72 };73 isFlag = fn === Boolean || fn[flagSymbol] === true;74 } else if (typeof type === 'function') {75 isFlag = type === Boolean || type[flagSymbol] === true;76 } else {77 throw new ArgError(78 `type missing or not a function or valid array type: ${key}`,79 'ARG_CONFIG_VAD_TYPE'80 );81 }82 83 if (key[1] !== '-' && key.length > 2) {84 throw new ArgError(85 `short argument keys (with a single hyphen) must have only one character: ${key}`,86 'ARG_CONFIG_SHORTOPT_TOOLONG'87 );88 }89 90 handlers[key] = [type, isFlag];91 }92 93 for (let i = 0, len = argv.length; i < len; i++) {94 const wholeArg = argv[i];95 96 if (stopAtPositional && result._.length > 0) {97 result._ = result._.concat(argv.slice(i));98 break;99 }100 101 if (wholeArg === '--') {102 result._ = result._.concat(argv.slice(i + 1));103 break;104 }105 106 if (wholeArg.length > 1 && wholeArg[0] === '-') {107 /* eslint-disable operator-linebreak */108 const separatedArguments =109 wholeArg[1] === '-' || wholeArg.length === 2110 ? [wholeArg]111 : wholeArg112 .slice(1)113 .split('')114 .map((a) => `-${a}`);115 /* eslint-enable operator-linebreak */116 117 for (let j = 0; j < separatedArguments.length; j++) {118 const arg = separatedArguments[j];119 const [originalArgName, argStr] =120 arg[1] === '-' ? arg.split(/=(.*)/, 2) : [arg, undefined];121 122 let argName = originalArgName;123 while (argName in aliases) {124 argName = aliases[argName];125 }126 127 if (!(argName in handlers)) {128 if (permissive) {129 result._.push(arg);130 continue;131 } else {132 throw new ArgError(133 `unknown or unexpected option: ${originalArgName}`,134 'ARG_UNKNOWN_OPTION'135 );136 }137 }138 139 const [type, isFlag] = handlers[argName];140 141 if (!isFlag && j + 1 < separatedArguments.length) {142 throw new ArgError(143 `option requires argument (but was followed by another short argument): ${originalArgName}`,144 'ARG_MISSING_REQUIRED_SHORTARG'145 );146 }147 148 if (isFlag) {149 result[argName] = type(true, argName, result[argName]);150 } else if (argStr === undefined) {151 if (152 argv.length < i + 2 ||153 (argv[i + 1].length > 1 &&154 argv[i + 1][0] === '-' &&155 !(156 argv[i + 1].match(/^-?\d*(\.(?=\d))?\d*$/) &&157 (type === Number ||158 // eslint-disable-next-line no-undef159 (typeof BigInt !== 'undefined' && type === BigInt))160 ))161 ) {162 const extended =163 originalArgName === argName ? '' : ` (alias for ${argName})`;164 throw new ArgError(165 `option requires argument: ${originalArgName}${extended}`,166 'ARG_MISSING_REQUIRED_LONGARG'167 );168 }169 170 result[argName] = type(argv[i + 1], argName, result[argName]);171 ++i;172 } else {173 result[argName] = type(argStr, argName, result[argName]);174 }175 }176 } else {177 result._.push(wholeArg);178 }179 }180 181 return result;182}183 184arg.flag = (fn) => {185 fn[flagSymbol] = true;186 return fn;187};188 189// Utility types190arg.COUNT = arg.flag((v, name, existingCount) => (existingCount || 0) + 1);191 192// Expose error class193arg.ArgError = ArgError;194 195module.exports = arg;196 