mirror of
https://github.com/Instadapp/Swap-Aggregator-Subgraph.git
synced 2024-07-29 21:57:12 +00:00
265 lines
8.8 KiB
JavaScript
265 lines
8.8 KiB
JavaScript
/**
|
|
* @fileoverview Command line options utility.
|
|
* @license Apache-2.0
|
|
*/
|
|
|
|
const path = require("path");
|
|
const colorsUtil = require("./colors");
|
|
|
|
// type | meaning
|
|
// -----|---------------
|
|
// b | boolean
|
|
// i | integer
|
|
// f | float
|
|
// s | string
|
|
// I | integer array
|
|
// F | float array
|
|
// S | string array
|
|
|
|
/** Parses the specified command line arguments according to the given configuration. */
|
|
function parse(argv, config, propagateDefaults = true) {
|
|
var options = {};
|
|
var unknown = [];
|
|
var args = [];
|
|
var trailing = [];
|
|
|
|
// make an alias map and initialize defaults
|
|
var aliases = {};
|
|
Object.keys(config).forEach(key => {
|
|
if (key.startsWith(" ")) return;
|
|
var option = config[key];
|
|
if (option.alias != null) {
|
|
if (typeof option.alias === "string") aliases[option.alias] = key;
|
|
else if (Array.isArray(option.alias)) option.alias.forEach(alias => aliases[alias] = key);
|
|
}
|
|
if (propagateDefaults && option.default != null) options[key] = option.default;
|
|
});
|
|
|
|
// iterate over argv
|
|
for (var i = 0, k = (argv = argv.slice()).length; i < k; ++i) {
|
|
let arg = argv[i];
|
|
if (arg == "--") { ++i; break; }
|
|
let match = /^(?:(-\w)(?:=(.*))?|(--\w{2,})(?:=(.*))?)$/.exec(arg), option, key;
|
|
if (match) {
|
|
if (config[arg]) option = config[key = arg]; // exact
|
|
else if (match[1] != null) { // alias
|
|
option = config[key = aliases[match[1].substring(1)]];
|
|
if (option && match[2] != null) argv[i--] = match[2];
|
|
} else if (match[3] != null) { // full
|
|
option = config[key = match[3].substring(2)];
|
|
if (option && match[4] != null) argv[i--] = match[4];
|
|
}
|
|
} else {
|
|
if (arg.charCodeAt(0) == 45) option = config[key = arg]; // exact
|
|
else { args.push(arg); continue; } // argument
|
|
}
|
|
if (option) {
|
|
if (option.value) {
|
|
// alias setting fixed values
|
|
Object.keys(option.value).forEach(k => options[k] = option.value[k]);
|
|
} else if (option.type == null || option.type === "b") {
|
|
// boolean flag not taking a value
|
|
options[key] = true;
|
|
} else {
|
|
if (i + 1 < argv.length && argv[i + 1].charCodeAt(0) != 45) {
|
|
// non-boolean with given value
|
|
switch (option.type) {
|
|
case "i": options[key] = parseInt(argv[++i], 10); break;
|
|
case "I": options[key] = (options[key] || []).concat(parseInt(argv[++i], 10)); break;
|
|
case "f": options[key] = parseFloat(argv[++i]); break;
|
|
case "F": options[key] = (options[key] || []).concat(parseFloat(argv[++i])); break;
|
|
case "s": options[key] = String(argv[++i]); break;
|
|
case "S": options[key] = (options[key] || []).concat(argv[++i].split(",")); break;
|
|
default: unknown.push(arg); --i;
|
|
}
|
|
} else {
|
|
// non-boolean with omitted value
|
|
switch (option.type) {
|
|
case "i":
|
|
case "f": options[key] = option.default || 0; break;
|
|
case "s": options[key] = option.default || ""; break;
|
|
case "I":
|
|
case "F":
|
|
case "S": options[key] = option.default || []; break;
|
|
default: unknown.push(arg);
|
|
}
|
|
}
|
|
}
|
|
} else unknown.push(arg);
|
|
}
|
|
while (i < k) trailing.push(argv[i++]); // trailing
|
|
if (propagateDefaults) addDefaults(config, options);
|
|
|
|
return { options, unknown, arguments: args, trailing };
|
|
}
|
|
|
|
exports.parse = parse;
|
|
|
|
/** Generates the help text for the specified configuration. */
|
|
function help(config, options) {
|
|
if (!options) options = {};
|
|
var indent = options.indent || 2;
|
|
var padding = options.padding || 24;
|
|
var eol = options.eol || "\n";
|
|
var sbCategories = {};
|
|
var sbOther = [];
|
|
Object.keys(config).forEach(key => {
|
|
var option = config[key];
|
|
if (option.description == null) return;
|
|
var text = "";
|
|
while (text.length < indent) text += " ";
|
|
text += "--" + key;
|
|
if (option.alias) text += ", -" + option.alias;
|
|
while (text.length < padding) text += " ";
|
|
var sb;
|
|
if (!options.noCategories && option.category) {
|
|
if (!(sb = sbCategories[option.category])) {
|
|
sbCategories[option.category] = sb = [];
|
|
}
|
|
} else {
|
|
sb = sbOther;
|
|
}
|
|
if (Array.isArray(option.description)) {
|
|
sb.push(text + option.description[0] + option.description.slice(1).map(line => {
|
|
for (let i = 0; i < padding; ++i) line = " " + line;
|
|
return eol + line;
|
|
}).join(""));
|
|
} else sb.push(text + option.description);
|
|
});
|
|
var sb = [];
|
|
var hasCategories = false;
|
|
Object.keys(sbCategories).forEach(category => {
|
|
hasCategories = true;
|
|
sb.push(eol + " " + colorsUtil.gray(category) + eol);
|
|
sb.push(sbCategories[category].join(eol));
|
|
});
|
|
if (hasCategories) {
|
|
sb.push(eol + " " + colorsUtil.gray("Other") + eol);
|
|
}
|
|
sb.push(sbOther.join(eol));
|
|
return sb.join(eol);
|
|
}
|
|
|
|
exports.help = help;
|
|
|
|
/** Sanitizes an option value to be a valid value of the option's type. */
|
|
function sanitizeValue(value, type) {
|
|
if (value != null) {
|
|
switch (type) {
|
|
case undefined:
|
|
case "b": return Boolean(value);
|
|
case "i": return Math.trunc(value) || 0;
|
|
case "f": return Number(value) || 0;
|
|
case "s": {
|
|
if (value === true) return "";
|
|
return String(value);
|
|
}
|
|
case "I": {
|
|
if (!Array.isArray(value)) value = [ value ];
|
|
return value.map(v => Math.trunc(v) || 0);
|
|
}
|
|
case "F": {
|
|
if (!Array.isArray(value)) value = [ value ];
|
|
return value.map(v => Number(v) || 0);
|
|
}
|
|
case "S": {
|
|
if (!Array.isArray(value)) value = [ value ];
|
|
return value.map(String);
|
|
}
|
|
}
|
|
}
|
|
return undefined;
|
|
}
|
|
|
|
/** Merges two sets of options into one, preferring the current over the parent set. */
|
|
function merge(config, currentOptions, parentOptions, parentBaseDir) {
|
|
const mergedOptions = {};
|
|
for (const [key, { type, mutuallyExclusive, isPath, useNodeResolution, cliOnly }] of Object.entries(config)) {
|
|
let currentValue = sanitizeValue(currentOptions[key], type);
|
|
let parentValue = sanitizeValue(parentOptions[key], type);
|
|
if (currentValue == null) {
|
|
if (parentValue != null) {
|
|
// only parent value present
|
|
if (cliOnly) continue;
|
|
if (Array.isArray(parentValue)) {
|
|
let exclude;
|
|
if (isPath) {
|
|
parentValue = parentValue.map(value => resolvePath(value, parentBaseDir, useNodeResolution));
|
|
}
|
|
if (mutuallyExclusive != null && (exclude = currentOptions[mutuallyExclusive])) {
|
|
mergedOptions[key] = parentValue.filter(value => !exclude.includes(value));
|
|
} else {
|
|
mergedOptions[key] = parentValue.slice();
|
|
}
|
|
} else {
|
|
if (isPath) {
|
|
parentValue = resolvePath(parentValue, parentBaseDir, useNodeResolution);
|
|
}
|
|
mergedOptions[key] = parentValue;
|
|
}
|
|
}
|
|
} else if (parentValue == null) {
|
|
// only current value present
|
|
if (Array.isArray(currentValue)) {
|
|
mergedOptions[key] = currentValue.slice();
|
|
} else {
|
|
mergedOptions[key] = currentValue;
|
|
}
|
|
} else {
|
|
// both current and parent values present
|
|
if (Array.isArray(currentValue)) {
|
|
if (cliOnly) {
|
|
mergedOptions[key] = currentValue.slice();
|
|
continue;
|
|
}
|
|
let exclude;
|
|
if (isPath) {
|
|
parentValue = parentValue.map(value => resolvePath(value, parentBaseDir, useNodeResolution));
|
|
}
|
|
if (mutuallyExclusive != null && (exclude = currentOptions[mutuallyExclusive])) {
|
|
mergedOptions[key] = [
|
|
...currentValue,
|
|
...parentValue.filter(value => !currentValue.includes(value) && !exclude.includes(value))
|
|
];
|
|
} else {
|
|
mergedOptions[key] = [
|
|
...currentValue,
|
|
...parentValue.filter(value => !currentValue.includes(value)) // dedup
|
|
];
|
|
}
|
|
} else {
|
|
mergedOptions[key] = currentValue;
|
|
}
|
|
}
|
|
}
|
|
return mergedOptions;
|
|
}
|
|
|
|
exports.merge = merge;
|
|
|
|
const dynrequire = typeof __webpack_require__ === "function"
|
|
? __non_webpack_require__
|
|
: require;
|
|
|
|
/** Resolves a single possibly relative path. Keeps absolute paths, otherwise prepends baseDir. */
|
|
function resolvePath(p, baseDir, useNodeResolution = false) {
|
|
if (path.isAbsolute(p)) return p;
|
|
if (useNodeResolution && !p.startsWith(".")) {
|
|
return dynrequire.resolve(p, { paths: [ baseDir ] });
|
|
}
|
|
return path.join(baseDir, p);
|
|
}
|
|
|
|
exports.resolvePath = resolvePath;
|
|
|
|
/** Populates default values on a parsed options result. */
|
|
function addDefaults(config, options) {
|
|
for (const [key, { default: defaultValue }] of Object.entries(config)) {
|
|
if (options[key] == null && defaultValue != null) {
|
|
options[key] = defaultValue;
|
|
}
|
|
}
|
|
}
|
|
|
|
exports.addDefaults = addDefaults;
|