/** * 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; } { // Map of single-character short flags to their canonical long names const shortToFull: Record = { 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([ "verbose", "help", "autoInputs", "sign", "broadcast", "install", ]); const positionalArgs: string[] = []; const optionsObject: Record = {}; 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 }; }