87 lines
2.5 KiB
TypeScript
87 lines
2.5 KiB
TypeScript
/**
|
|
* CLI Argument extraction and validation.
|
|
*
|
|
* Converts `-${key}` or `--${key}` to `key` in the args object.
|
|
*/
|
|
import { z } from "zod";
|
|
|
|
/**
|
|
* Converts the CLI args to a key-value object and return the options object along with the other arguments still in the array.\
|
|
* eg: `xo-cli mnemonic create page pencil stock planet limb cluster assault speak off joke private pioneer -v -o mnemonic.txt` will return:
|
|
* {
|
|
* args: ["mnemonic", "create", "page", "pencil", "stock", "planet", "limb", "cluster", "assault", "speak", "off", "joke", "private", "pioneer"],
|
|
* options: {
|
|
* output: "mnemonic.txt",
|
|
* verbose: "true",
|
|
* },
|
|
* }
|
|
*
|
|
* @param args - The CLI args to convert.
|
|
* @returns The key-value object.
|
|
*/
|
|
export function convertArgsToObject(args: string[]): {
|
|
args: string[];
|
|
options: Record<string, string>;
|
|
} {
|
|
// Map of single-character short flags to their canonical long names
|
|
const shortToFull: Record<string, string> = {
|
|
m: "mnemonicFile",
|
|
o: "output",
|
|
v: "verbose",
|
|
h: "help",
|
|
};
|
|
|
|
// Flags that are always boolean and never consume the next argument as a value.
|
|
// Uses the canonical (expanded) names so the check works after short-form resolution.
|
|
const booleanFlags = new Set<string>([
|
|
"verbose",
|
|
"help",
|
|
"autoInputs",
|
|
"sign",
|
|
"broadcast",
|
|
"install",
|
|
]);
|
|
|
|
const positionalArgs: string[] = [];
|
|
const optionsObject: Record<string, string> = {};
|
|
|
|
for (let i = 0; i < args.length; i++) {
|
|
const arg = args[i];
|
|
|
|
// Collect non-option arguments as positional args
|
|
if (!arg || !arg.startsWith("-")) {
|
|
if (arg) positionalArgs.push(arg);
|
|
continue;
|
|
}
|
|
|
|
// Format the option key:
|
|
// - Remove the leading `-`s
|
|
// - Convert kebab-case to camelCase
|
|
// - Expand known short forms to their full names
|
|
let key = arg
|
|
.replace(/^-+/, "")
|
|
.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
|
|
key = shortToFull[key] ?? key;
|
|
|
|
// Known boolean flags never take a value
|
|
if (booleanFlags.has(key)) {
|
|
optionsObject[key] = "true";
|
|
continue;
|
|
}
|
|
|
|
const nextArg = args[i + 1];
|
|
|
|
// If there's no next arg or it starts with `-`, treat this as a boolean flag
|
|
if (!nextArg || nextArg.startsWith("-")) {
|
|
optionsObject[key] = "true";
|
|
continue;
|
|
}
|
|
|
|
// Consume the next arg as the value and skip it in the next iteration
|
|
optionsObject[key] = nextArg;
|
|
i++;
|
|
}
|
|
|
|
return { args: positionalArgs, options: optionsObject };
|
|
}
|