@clo4/cli 0.0.0-stage → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"testing.js","names":[],"sources":["../src/util.ts","../node_modules/.pnpm/fastest-levenshtein@1.0.16/node_modules/fastest-levenshtein/mod.js","../src/help.ts","../src/parse.ts","../src/testing.ts"],"sourcesContent":["import type { NonEmptyArray } from \"./types.js\";\n\nexport function isArray<T>(o: T | readonly T[]): o is readonly T[] {\n return Array.isArray(o);\n}\n\n/** Convert the value, if any, to an array with 0 or more elements */\nexport function makeArray<T>(thing: T | readonly T[] | undefined): T[] {\n if (thing === undefined) return [];\n if (isArray(thing)) {\n return thing.slice();\n }\n return [thing];\n}\n\n/**\n * Like {@link makeArray}, but without copying when the input is already an\n * array. Only safe when the caller treats the result as read-only — use it on\n * the hot path to avoid slicing read-only spec metadata.\n */\nexport function asArray<T>(thing: T | readonly T[] | undefined): readonly T[] {\n if (thing === undefined) return [];\n if (isArray(thing)) return thing;\n return [thing];\n}\n\n/** Convert the value to an array with at least one element */\nexport function makeArray1<T>(thing: T | NonEmptyArray<T>): [T, ...T[]] {\n if (isArray(thing)) {\n // We know that this array has at least one element in it\n return thing.slice() as [T, ...T[]];\n }\n return [thing];\n}\n\n/** Set many keys in a Map to the same value */\nexport function setEach<K, V>(map: Map<K, V>, keys: K | readonly K[], value: V): void {\n if (isArray(keys)) {\n for (const key of keys) {\n map.set(key, value);\n }\n } else {\n map.set(keys, value);\n }\n}\n\nexport function assert(expr: unknown, msg = \"\"): asserts expr {\n if (!expr) {\n throw new Error(msg);\n }\n}\n\nexport function error(...strings: unknown[]): void {\n console.error(\"%cError:\", \"color: red\", ...strings);\n}\n\n/** Print a message to stdout, trimming trailing whitespace. */\nexport function print(text: string): void {\n console.log(text.trimEnd());\n}\n\n/** Print an error message to stderr with the shared `Error:` prefix. */\nexport function printActionError(message: string): void {\n console.error(\"Error:\", message);\n}\n","\"use strict\";\nexports.__esModule = true;\nexports.distance = exports.closest = void 0;\nvar peq = new Uint32Array(0x10000);\nvar myers_32 = function (a, b) {\n var n = a.length;\n var m = b.length;\n var lst = 1 << (n - 1);\n var pv = -1;\n var mv = 0;\n var sc = n;\n var i = n;\n while (i--) {\n peq[a.charCodeAt(i)] |= 1 << i;\n }\n for (i = 0; i < m; i++) {\n var eq = peq[b.charCodeAt(i)];\n var xv = eq | mv;\n eq |= ((eq & pv) + pv) ^ pv;\n mv |= ~(eq | pv);\n pv &= eq;\n if (mv & lst) {\n sc++;\n }\n if (pv & lst) {\n sc--;\n }\n mv = (mv << 1) | 1;\n pv = (pv << 1) | ~(xv | mv);\n mv &= xv;\n }\n i = n;\n while (i--) {\n peq[a.charCodeAt(i)] = 0;\n }\n return sc;\n};\nvar myers_x = function (b, a) {\n var n = a.length;\n var m = b.length;\n var mhc = [];\n var phc = [];\n var hsize = Math.ceil(n / 32);\n var vsize = Math.ceil(m / 32);\n for (var i = 0; i < hsize; i++) {\n phc[i] = -1;\n mhc[i] = 0;\n }\n var j = 0;\n for (; j < vsize - 1; j++) {\n var mv_1 = 0;\n var pv_1 = -1;\n var start_1 = j * 32;\n var vlen_1 = Math.min(32, m) + start_1;\n for (var k = start_1; k < vlen_1; k++) {\n peq[b.charCodeAt(k)] |= 1 << k;\n }\n for (var i = 0; i < n; i++) {\n var eq = peq[a.charCodeAt(i)];\n var pb = (phc[(i / 32) | 0] >>> i) & 1;\n var mb = (mhc[(i / 32) | 0] >>> i) & 1;\n var xv = eq | mv_1;\n var xh = ((((eq | mb) & pv_1) + pv_1) ^ pv_1) | eq | mb;\n var ph = mv_1 | ~(xh | pv_1);\n var mh = pv_1 & xh;\n if ((ph >>> 31) ^ pb) {\n phc[(i / 32) | 0] ^= 1 << i;\n }\n if ((mh >>> 31) ^ mb) {\n mhc[(i / 32) | 0] ^= 1 << i;\n }\n ph = (ph << 1) | pb;\n mh = (mh << 1) | mb;\n pv_1 = mh | ~(xv | ph);\n mv_1 = ph & xv;\n }\n for (var k = start_1; k < vlen_1; k++) {\n peq[b.charCodeAt(k)] = 0;\n }\n }\n var mv = 0;\n var pv = -1;\n var start = j * 32;\n var vlen = Math.min(32, m - start) + start;\n for (var k = start; k < vlen; k++) {\n peq[b.charCodeAt(k)] |= 1 << k;\n }\n var score = m;\n for (var i = 0; i < n; i++) {\n var eq = peq[a.charCodeAt(i)];\n var pb = (phc[(i / 32) | 0] >>> i) & 1;\n var mb = (mhc[(i / 32) | 0] >>> i) & 1;\n var xv = eq | mv;\n var xh = ((((eq | mb) & pv) + pv) ^ pv) | eq | mb;\n var ph = mv | ~(xh | pv);\n var mh = pv & xh;\n score += (ph >>> (m - 1)) & 1;\n score -= (mh >>> (m - 1)) & 1;\n if ((ph >>> 31) ^ pb) {\n phc[(i / 32) | 0] ^= 1 << i;\n }\n if ((mh >>> 31) ^ mb) {\n mhc[(i / 32) | 0] ^= 1 << i;\n }\n ph = (ph << 1) | pb;\n mh = (mh << 1) | mb;\n pv = mh | ~(xv | ph);\n mv = ph & xv;\n }\n for (var k = start; k < vlen; k++) {\n peq[b.charCodeAt(k)] = 0;\n }\n return score;\n};\nvar distance = function (a, b) {\n if (a.length < b.length) {\n var tmp = b;\n b = a;\n a = tmp;\n }\n if (b.length === 0) {\n return a.length;\n }\n if (a.length <= 32) {\n return myers_32(a, b);\n }\n return myers_x(a, b);\n};\nexports.distance = distance;\nvar closest = function (str, arr) {\n var min_distance = Infinity;\n var min_index = 0;\n for (var i = 0; i < arr.length; i++) {\n var dist = distance(str, arr[i]);\n if (dist < min_distance) {\n min_distance = dist;\n min_index = i;\n }\n }\n return arr[min_index];\n};\nexports.closest = closest;\n","import { closest } from \"fastest-levenshtein\";\n\nimport type { Action, Arg, Option, NonEmptyArray, SingleOrArray, Subcommand } from \"./types.js\";\nimport { makeArray, makeArray1, print, printActionError } from \"./util.js\";\n\nfunction getNamed<T extends { name: SingleOrArray<string> }>(\n named: readonly T[],\n name: string,\n): T | null {\n return named.find((obj) => makeArray(obj.name).includes(name)) ?? null;\n}\n\nfunction getParserDirective<Key extends keyof NonNullable<Subcommand[\"parserDirectives\"]>>(\n path: readonly Subcommand[],\n key: Key,\n): NonNullable<Subcommand[\"parserDirectives\"]>[Key] | undefined {\n for (let index = path.length - 1; index >= 0; index -= 1) {\n const command = path[index];\n if (command?.parserDirectives && key in command.parserDirectives) {\n return command.parserDirectives[key];\n }\n }\n return undefined;\n}\n\nfunction dedent(str: string) {\n if (str[0] !== \"\\n\") return str;\n const lines = str.split(\"\\n\").slice(1, -1);\n const margin = lines[0].length - lines[0].trimStart().length;\n return lines.map((line) => line.slice(margin)).join(\"\\n\");\n}\n\nexport interface HelpConfig {\n /** Include the command description? Default: true. */\n description?: boolean;\n\n /** Include the usage summary? Default: true. */\n usage?: boolean;\n\n /** Ask if the user intended the input to be one of these choices. */\n didYouMean?: {\n input: string;\n choices: readonly string[];\n };\n}\n\n/** Get the help message used by the built-in help option and command. */\nexport function getHelp(path: NonEmptyArray<Subcommand>, config: HelpConfig = {}): string {\n const { description = true, usage = true, didYouMean } = config;\n const sections = getSections(path);\n if (didYouMean) {\n sections.didYouMean = closest(didYouMean.input, didYouMean.choices as string[]);\n }\n if (!description) {\n sections.description = undefined;\n }\n if (!usage) {\n sections.usage = undefined;\n }\n return formatHelpSections(sections);\n}\n\n/**\n * Action that prints usage for commands where a subcommand must be provided.\n */\nexport const usage: Action = ({ path, args: [command], help }) => {\n if (command === undefined) {\n print(help());\n return 0;\n }\n\n if (command === \"\") {\n printActionError(\n `Expected a command, but the value was empty\\n\\n${help({ description: false })}`,\n );\n return 1;\n }\n\n const subcommands = path\n .at(-1)\n ?.subcommands?.filter((cmd) => !cmd.hidden)\n .flatMap((cmd) => cmd.name);\n\n if (subcommands && subcommands.length > 0) {\n const matchPrefix = getParserDirective(path, \"subcommandsMatchUniquePrefix\");\n if (matchPrefix) {\n const possible = subcommands.filter((name) => name.startsWith(command));\n if (possible.length > 0) {\n printActionError(\n `Ambiguous command '${command}', could be: ${possible.join(\", \")}\\n\\n${help({\n description: false,\n })}`,\n );\n return 1;\n }\n }\n\n printActionError(\n `Unknown command '${command}'\\n\\n${help({\n description: false,\n didYouMean: {\n input: command,\n choices: subcommands,\n },\n })}`,\n );\n return 1;\n }\n\n printActionError(`Unknown command '${command}'\\n\\n${help({ description: false })}`);\n\n return 1;\n};\n\n/** A subcommand that prints help for its parent or a sibling command. */\nexport const helpCommand: Subcommand = {\n name: \"help\",\n description: \"Print a help message\",\n args: {\n name: \"command\",\n isOptional: true,\n },\n action({ path, args: [commandName], help }) {\n const helpRoot = path.slice(0, -1) as unknown as NonEmptyArray<Subcommand>;\n\n if (!commandName) {\n print(help({ path: helpRoot }));\n return 0;\n }\n\n const parent = path[path.length - 2];\n\n if (!parent.subcommands) {\n printActionError(\n `No subcommands, try using 'help' with nothing after it\\n\\n${help({\n description: false,\n path: helpRoot,\n })}`,\n );\n return 1;\n }\n\n const command = getNamed(parent.subcommands, commandName);\n if (!command) {\n const subcommands = parent.subcommands\n .filter((cmd) => !cmd.hidden)\n .flatMap((cmd) => cmd.name);\n\n if (subcommands.length > 0) {\n printActionError(\n `There is no subcommand named '${commandName}'\\n\\n${help({\n path: helpRoot,\n didYouMean: {\n input: commandName,\n choices: subcommands,\n },\n })}`,\n );\n return 1;\n }\n\n printActionError(\n `There is no subcommand named '${commandName}'\\n\\n${help({\n description: false,\n path: helpRoot,\n })}`,\n );\n return 1;\n }\n\n print(help({ path: [...helpRoot, command] }));\n },\n};\n\n/** A persistent help option that prints help for the selected command. */\nexport const help: Option = {\n name: [\"-h\", \"--help\"],\n description: \"Print a help message\",\n isPersistent: true,\n action({ path }) {\n print(formatHelpSections(getSections(path)));\n return 0;\n },\n};\n\ntype HelpSections = {\n description?: string;\n usage?: string;\n didYouMean?: string;\n options: [formattedName: string, description: string][];\n persistentOptions: [formattedName: string, description: string][];\n subcommands: [formattedName: string, description: string][];\n};\n\nfunction getSubcommandPathString(commands: NonEmptyArray<Subcommand>): string {\n return commands.map((command) => getLongestString(makeArray1(command.name))).join(\" \");\n}\n\nfunction nonNullable<T>(value: T): value is NonNullable<T> {\n return value !== null && value !== undefined;\n}\n\nfunction getSections(commands: NonEmptyArray<Subcommand>): HelpSections {\n const command = commands[commands.length - 1];\n\n const description = command.description;\n const defaultDesc = \"No description\";\n\n const subcommands: HelpSections[\"subcommands\"] = command.subcommands\n ? command.subcommands\n .filter((subcommand) => !subcommand.hidden)\n .map((subcommand) => [\n makeArray1(subcommand.name)\n .sort((a, b) => a.length - b.length)\n .join(\", \"),\n subcommand.description ?? defaultDesc,\n ])\n : [];\n\n let persistentOptionObjects: Option[] = [];\n let optionObjects: Option[] = [];\n\n let persistentOptions: HelpSections[\"persistentOptions\"] = [];\n let options: HelpSections[\"options\"] = [];\n\n if (commands.length === 1 && subcommands.length === 0) {\n optionObjects = command.options ? command.options.filter((option) => !option.hidden) : [];\n options = optionObjects.map((option) => [\n optionToString(option),\n option.description ?? defaultDesc,\n ]);\n } else {\n persistentOptionObjects = commands\n .map((command) => command.options)\n .filter(nonNullable)\n .flat()\n .filter((option) => option.isPersistent && !option.hidden);\n\n persistentOptions = persistentOptionObjects.map((option) => [\n optionToString(option),\n option.description ?? defaultDesc,\n ]);\n\n optionObjects = (command.options ?? []).filter(\n (option) => !option.hidden && !option.isPersistent,\n );\n\n options = optionObjects.map((option) => [\n optionToString(option),\n option.description ?? defaultDesc,\n ]);\n }\n\n const usage = (() => {\n const parts = [getSubcommandPathString(commands)];\n const required = [\n ...optionObjects.filter((option) => option.isRequired),\n ...persistentOptionObjects.filter((option) => option.isRequired),\n ];\n for (const option of required) {\n parts.push(optionToString(option));\n }\n if (options.length || persistentOptions.length) {\n parts.push(\"[flags]\");\n }\n if (command.requiresSubcommand) {\n parts.push(\"<command>\");\n } else {\n const args = makeArray(command.args);\n if (args.length > 0) {\n parts.push(summarizeArguments(args));\n }\n }\n return parts.join(\" \");\n })();\n\n return {\n usage,\n description,\n persistentOptions,\n options,\n subcommands,\n };\n}\n\nexport function formatHelpSections(sections: HelpSections): string {\n const parts = [];\n\n const indent = \" \";\n\n if (sections.didYouMean) {\n parts.push(` Did you mean '${sections.didYouMean}'?`);\n }\n\n if (sections.description) {\n parts.push(dedent(sections.description));\n }\n\n if (sections.usage) {\n parts.push(`Usage:\\n${indent}${sections.usage}`);\n }\n\n const longestOptionName = Math.max(\n 0,\n ...sections.options.map(([name]) => name.length),\n ...sections.persistentOptions.map(([name]) => name.length),\n );\n const minSpacingBetweenNameAndDesc = 2;\n\n const formatOptions = (options: [string, string][]) => {\n const lines = [];\n const newline = indent + \" \".repeat(longestOptionName + minSpacingBetweenNameAndDesc);\n for (const [name, desc] of options) {\n const spaces = \" \".repeat(longestOptionName - name.length + minSpacingBetweenNameAndDesc);\n const [head, ...tail] = desc.split(\"\\n\");\n const description = [head, ...tail.map((line) => newline + line)].join(\"\\n\");\n lines.push(`${indent}${name}${spaces}${description}`);\n }\n return lines;\n };\n\n if (sections.subcommands.length > 0) {\n const lines = [\"Commands:\"];\n const longestName = Math.max(...sections.subcommands.map(([name]) => name.length));\n const newline = indent + \" \".repeat(longestName + minSpacingBetweenNameAndDesc);\n for (const [name, desc] of sections.subcommands) {\n const spaces = \" \".repeat(longestName - name.length + minSpacingBetweenNameAndDesc);\n const [head, ...tail] = desc.split(\"\\n\\n\")[0].split(\"\\n\");\n const description = [head, ...tail.map((line) => newline + line)].join(\"\\n\");\n lines.push(`${indent}${name}${spaces}${description}`);\n }\n parts.push(lines.join(\"\\n\"));\n }\n\n if (sections.options.length > 0) {\n const lines = [\"Flags:\", ...formatOptions(sections.options)];\n parts.push(lines.join(\"\\n\"));\n }\n\n if (sections.persistentOptions.length > 0) {\n const lines = [\"Global flags:\", ...formatOptions(sections.persistentOptions)];\n parts.push(lines.join(\"\\n\"));\n }\n return parts.join(\"\\n\\n\") + \"\\n\";\n}\n\nexport function getLongestString(strings: NonEmptyArray<string>): string {\n let longest = strings[0];\n for (let i = 1; i < strings.length; i++) {\n const string = strings[i];\n if (string.length > longest.length) {\n longest = string;\n }\n }\n return longest;\n}\n\nexport function optionToString(option: Option): string {\n const name = makeArray1(option.name)\n .sort((a, b) => a.length - b.length)\n .join(\", \");\n\n const optionArgs = makeArray(option.args);\n\n if (optionArgs.length === 0) {\n return name;\n }\n\n if (option.requiresSeparator) {\n const separator = option.requiresSeparator === true ? \"=\" : option.requiresSeparator;\n if (optionArgs[0].isOptional) {\n return `${name}[${separator}${optionArgs[0].name || \"argument\"}]`;\n }\n return `${name}${separator}<${optionArgs[0].name || \"argument\"}>`;\n }\n return `${name} ${summarizeArguments(optionArgs)}`;\n}\n\nexport function summarizeArguments(args: Arg[]) {\n return args.map((arg) => argToString(arg)).join(\" \");\n}\n\nexport function argToString(arg: Arg): string {\n const name = arg.name || \"argument\";\n let lhs = \"<\";\n let rhs = \">\";\n let suffix = \"\";\n if (arg.isOptional) {\n lhs = \"[\";\n rhs = \"]\";\n }\n if (arg.isVariadic) {\n suffix = \"...\";\n }\n return `${lhs}${name}${rhs}${suffix}`;\n}\n","import type { Action, CommandPath, NonEmptyArray, Option, Spec, Subcommand } from \"./types.js\";\nimport { analyze } from \"./analyze.js\";\nimport type { BaseToken, TokenOption, TokenSubcommand } from \"./analyze.js\";\nimport { assert, isArray, setEach } from \"./util.js\";\nimport {\n InvalidOptionArg,\n MissingRequiredOption,\n ParseError,\n TooFewArguments,\n TooFewOptionArguments,\n TooManyArguments,\n UnknownOption,\n} from \"./errors.js\";\nimport type { ErrorContext } from \"./errors.js\";\nimport { usage as builtinUsageAction } from \"./help.js\";\n\nexport interface ParseResult {\n args: string[];\n argSeparatorIndex: number;\n options: Map<string, string[]>;\n path: CommandPath;\n actions: Action[];\n optionActions: Action[];\n}\n\nconst enum State {\n ParseArgs,\n ParseOptionArgs,\n ParseOptionArgRequireSeparator,\n}\n\n/**\n * Returns the minimum number of arguments that satisfies the args.\n *\n * Accepts the raw spec value (a single arg, an array, or `undefined`) so the\n * hot path doesn't have to copy it into an array first.\n */\nexport function getMinArgs(\n args: { isOptional?: boolean } | readonly { isOptional?: boolean }[] | undefined,\n): number {\n if (args === undefined) return 0;\n if (!isArray(args)) return args.isOptional ? 0 : 1;\n let count = 0;\n for (let i = 0; i < args.length; i++) {\n if (args[i].isOptional) {\n break;\n }\n count++;\n }\n return count;\n}\n\n/** Returns the maximum number of arguments that satisfies the args. */\nexport function getMaxArgs(\n args: { isVariadic?: boolean } | readonly { isVariadic?: boolean }[] | undefined,\n): number {\n if (args === undefined) return 0;\n if (!isArray(args)) return args.isVariadic ? Infinity : 1;\n // Loop over the array backwards because variadic arguments are\n // more likely to be at the end of the array. They don't _need_\n // to be, but it's more idiomatic.\n for (let i = 0; i < args.length; i++) {\n if (args[args.length - 1 - i].isVariadic) {\n return Infinity;\n }\n }\n // None of the arguments are variadic, max is the number of args\n return args.length;\n}\n\n/** Check if a map has a key or any key from an array */\nfunction has<K>(map: Map<K, unknown>, key: K | NonEmptyArray<K>): boolean {\n if (isArray(key)) {\n return map.has(key[0]);\n }\n return map.has(key);\n}\n\n/** Get a value from a map by a key or any key from an array */\nfunction get<K, V>(map: Map<K, V>, key: K | NonEmptyArray<K>): V | undefined {\n if (isArray(key)) {\n return map.get(key[0]);\n }\n return map.get(key);\n}\n\n// TODO: Replace all instances of ParseError with a bespoke error type\n\n/**\n * Parse the input tokens using the given CLI spec.\n *\n * This returns a `ParseResult`, which contains the array of\n * subcommands used, the provided options, and the arguments.\n *\n * If parsing fails, this function throws.\n *\n * This function is exposed to make it easier to build a custom runner.\n */\nexport function parse(input: readonly string[], spec: Spec): ParseResult {\n const path: [Spec, ...Subcommand[]] = [spec];\n const actions: Action[] = [];\n const optionActions: Action[] = [];\n\n if (spec.action) {\n actions.push(spec.action);\n } else if (spec.requiresSubcommand) {\n actions.push(builtinUsageAction);\n }\n\n let commandArgsMin: number = getMinArgs(spec.args);\n let commandArgsMax: number = getMaxArgs(spec.args);\n\n const foundArgs: string[] = [];\n const foundOptions: Map<string, string[]> = new Map();\n\n // These have to be type assertions otherwise TS will incorrectly\n // infer that the variables are never reassigned\n let state = State.ParseArgs as State;\n let optionArgs = null as string[] | null;\n let optionArgsMin = 0;\n let optionArgsMax = 0;\n let requiredSeparator = false as string | boolean;\n let argSeparatorIndex = -1;\n\n // Each item in this set must be provided\n const dependsOnOptions = [] as Option[];\n // Each item in this set cannot be provided\n const exclusiveOnOptions = [] as Option[];\n\n const parseOption = (token: TokenOption<Option>) => {\n const option = token.option;\n\n // Don't allow repeating non-repeatable options\n if (!option.isRepeatable && has(foundOptions, option.name)) {\n throw new ParseError(ctx(), \"Repeated option\");\n }\n\n if (option.exclusiveOn) {\n exclusiveOnOptions.push(option);\n }\n\n if (option.dependsOn) {\n dependsOnOptions.push(option);\n }\n\n if (option.action) {\n optionActions.push(option.action);\n }\n\n // Repeatable options are treated differently so we\n // *have* to branch on this.\n if (option.isRepeatable) {\n const maxRepeat = option.isRepeatable === true ? Infinity : option.isRepeatable;\n let arr = get(foundOptions, option.name);\n if (!arr) {\n arr = [];\n setEach(foundOptions, option.name, arr);\n }\n if (arr.length >= maxRepeat) {\n throw new ParseError(ctx(), \"Too many repetitions\");\n }\n arr.push(\"\");\n return;\n }\n\n optionArgsMin = getMinArgs(option.args);\n optionArgsMax = getMaxArgs(option.args);\n\n optionArgs = [];\n setEach(foundOptions, option.name, optionArgs);\n\n if (optionArgsMax === 0) {\n optionArgs = null;\n state = State.ParseArgs;\n return;\n }\n\n if (option.requiresSeparator) {\n state = State.ParseOptionArgRequireSeparator;\n requiredSeparator = option.requiresSeparator;\n } else {\n state = State.ParseOptionArgs;\n }\n return;\n };\n\n const parseArg = (token: BaseToken) => {\n // If the final command requires a subcommand, providing too\n // many arguments is allowed because the user has made a typo\n // (they were intending to use a subcommand)\n if (!path[path.length - 1].requiresSubcommand && foundArgs.length >= commandArgsMax) {\n throw new TooManyArguments(token, ctx());\n }\n foundArgs.push(token.literal);\n };\n\n const parseArgSeparator = () => {\n if (foundArgs.length >= commandArgsMax) {\n throw new ParseError(\n ctx(),\n \"Unexpected argument '--', did you mean to use an option instead?\",\n );\n }\n argSeparatorIndex = foundArgs.length;\n state = State.ParseArgs;\n };\n\n const parseSubcommand = (token: TokenSubcommand<Subcommand>) => {\n path.push(token.subcommand);\n commandArgsMin = getMinArgs(token.subcommand.args);\n commandArgsMax = getMaxArgs(token.subcommand.args);\n if (token.subcommand.action) {\n actions.push(token.subcommand.action);\n } else if (token.subcommand.requiresSubcommand) {\n actions.push(builtinUsageAction);\n }\n };\n\n const ctx = () => ({ path }) as ErrorContext;\n\n const { finalState, tokens } = analyze<Subcommand, Option>(input, spec);\n\n for (const token of tokens) {\n switch (state) {\n case State.ParseArgs: {\n switch (token.kind) {\n case \"subcommand\": {\n parseSubcommand(token);\n break;\n }\n case \"arg\": {\n parseArg(token);\n break;\n }\n case \"option\": {\n parseOption(token);\n break;\n }\n case \"unknown-option\": {\n throw new UnknownOption(token, ctx());\n }\n case \"option-arg\": {\n throw new InvalidOptionArg(token, ctx());\n }\n case \"arg-separator\": {\n parseArgSeparator();\n break;\n }\n default: {\n throw new ParseError(ctx(), \"Unreachable\");\n }\n }\n break;\n }\n case State.ParseOptionArgs: {\n assert(\n Array.isArray(optionArgs),\n \"Invalid state, must have an array to store option arguments\",\n );\n switch (token.kind) {\n case \"option-arg\":\n case \"arg\": {\n if (optionArgs.length < optionArgsMax) {\n optionArgs.push(token.literal);\n } else {\n optionArgs = null;\n state = State.ParseArgs;\n parseArg(token);\n }\n break;\n }\n case \"option\": {\n if (optionArgs.length < optionArgsMin) {\n throw new TooFewOptionArguments(optionArgsMin, optionArgsMax, ctx());\n }\n parseOption(token);\n break;\n }\n case \"unknown-option\": {\n if (optionArgs.length < optionArgsMax) {\n optionArgs.push(token.literal);\n } else {\n throw new UnknownOption(token, ctx());\n }\n break;\n }\n case \"arg-separator\": {\n if (optionArgs.length < optionArgsMin) {\n throw new TooFewOptionArguments(optionArgsMin, optionArgsMax, ctx());\n }\n parseArgSeparator();\n break;\n }\n case \"subcommand\": {\n throw new ParseError(ctx(), \"Unexpected token\");\n }\n // The analyzer can't output a subcommand here, and if it does\n // then that's an error anyway\n default: {\n throw new ParseError(ctx(), \"Unreachable\");\n }\n }\n break;\n }\n case State.ParseOptionArgRequireSeparator: {\n assert(\n Array.isArray(optionArgs),\n \"Invalid state, must have an array to store option arguments\",\n );\n assert(requiredSeparator, \"Invalid state, a separator must be set\");\n switch (token.kind) {\n case \"option-arg\": {\n if (typeof requiredSeparator === \"string\" && token.separator !== requiredSeparator) {\n throw new ParseError(\n ctx(),\n `Incorrect separator, use '${requiredSeparator}' instead of '${token.separator}'`,\n );\n }\n // If this is true, we know thanks to the test suite that\n // the option takes a minimum of one argument\n optionArgs.push(token.literal);\n state = State.ParseArgs;\n break;\n }\n case \"arg\": {\n if (optionArgsMin > 0) {\n throw new TooFewOptionArguments(optionArgsMin, optionArgsMax, ctx());\n }\n optionArgs = null;\n state = State.ParseArgs;\n parseArg(token);\n break;\n }\n case \"option\": {\n if (optionArgsMin > 0) {\n throw new TooFewOptionArguments(optionArgsMin, optionArgsMax, ctx());\n }\n parseOption(token);\n break;\n }\n case \"unknown-option\": {\n throw new UnknownOption(token, ctx());\n }\n case \"arg-separator\": {\n throw new ParseError(ctx(), \"Unexpected token\");\n }\n case \"subcommand\": {\n if (optionArgsMin > 0) {\n throw new TooFewOptionArguments(optionArgsMin, optionArgsMax, ctx());\n }\n parseSubcommand(token);\n state = State.ParseArgs;\n break;\n }\n default: {\n throw new ParseError(ctx(), \"Unreachable\");\n }\n }\n break;\n }\n }\n }\n\n const hasOptionAction = optionActions.length > 0;\n\n if (!hasOptionAction && foundArgs.length < commandArgsMin) {\n throw new TooFewArguments(commandArgsMin, commandArgsMax, ctx());\n }\n if (optionArgs && optionArgs.length < optionArgsMin) {\n throw new TooFewOptionArguments(optionArgsMin, optionArgsMax, ctx());\n }\n\n // Options in the `{depends,exclusive}On` arrays definitely have the\n // corresponding array, so it's safe to assert non-null in this situation.\n for (const option of dependsOnOptions) {\n for (const name of option.dependsOn!) {\n if (!foundOptions.has(name)) {\n throw new ParseError(ctx(), `${option.name} requires ${name}, add it to fix this error`);\n }\n }\n }\n for (const option of exclusiveOnOptions) {\n for (const name of option.exclusiveOn!) {\n if (foundOptions.has(name)) {\n throw new ParseError(ctx(), `${option.name} can't be used together with ${name}`);\n }\n }\n }\n if (!hasOptionAction) {\n for (const name of finalState.localRequiredOptions.keys()) {\n if (!foundOptions.has(name)) {\n throw new MissingRequiredOption(name, ctx());\n }\n }\n for (const name of finalState.persistentRequiredOptions.keys()) {\n if (!foundOptions.has(name)) {\n throw new MissingRequiredOption(name, ctx());\n }\n }\n }\n\n return {\n path,\n actions,\n optionActions,\n argSeparatorIndex,\n args: foundArgs,\n options: foundOptions,\n };\n}\n","/**\n * Supplementary module to make testing your CLI easier.\n *\n * @module\n */\n\nimport type { Arg, Option, SingleOrArray, Subcommand } from \"./types.js\";\nimport { assert, isArray, makeArray } from \"./util.js\";\nimport { getMaxArgs, getMinArgs } from \"./parse.js\";\n\nconst repr = JSON.stringify;\n\n// TODO: one decorators are stable (ts 4.9?), use decorators this file\n\n/**\n * Run a function once for each `args` property in the spec (recursive)\n *\n * This will convert single args to an array with one item.\n */\nfunction forEachArgArray(\n spec: Subcommand,\n fn: (args: Arg[], path: Subcommand[]) => void,\n path: Subcommand[] = [],\n): void {\n path = path.concat(spec);\n fn(makeArray(spec.args), path);\n for (const option of makeArray(spec.options)) {\n fn(makeArray(option.args), path);\n }\n for (const subcommand of makeArray(spec.subcommands)) {\n forEachArgArray(subcommand, fn, path);\n }\n}\n\n/** Run a function once for each `options` property in the spec (recursive) */\nfunction forEachOptionArray(\n spec: Subcommand,\n fn: (options: Option[], path: Subcommand[]) => void,\n path: Subcommand[] = [],\n): void {\n path = path.concat(spec);\n fn(makeArray(spec.options), path);\n for (const subcommand of makeArray(spec.subcommands)) {\n forEachOptionArray(subcommand, fn, path);\n }\n}\n\n/**\n * Run a function once for each `subcommands` property in the spec (recursive)\n *\n * This will also run the function for the root spec itself.\n */\nfunction forEachSubcommand(\n spec: Subcommand,\n fn: (spec: Subcommand, path: Subcommand[]) => void,\n path: Subcommand[] = [],\n): void {\n path = path.concat(spec);\n fn(spec, path);\n for (const subcommand of makeArray(spec.subcommands)) {\n forEachSubcommand(subcommand, fn, path);\n }\n}\n\nfunction joinNames(names: SingleOrArray<string>) {\n return makeArray(names)\n .sort((a, b) => a.length - b.length)\n .join(\"|\");\n}\n\nfunction namedArrayToString(...parts: { name: SingleOrArray<string> }[]) {\n const names = parts.map((named) => joinNames(named.name));\n return `\\`${names.join(\" \")}\\``;\n}\n\n/** Asserts that required arguments do not come after an optional argument */\nexport function assertRequiredArgumentsDoNotFollowOptionalArguments(spec: Subcommand): void {\n forEachArgArray(spec, (args, path) => {\n let remainingMustBeOptional = false;\n let firstOptionalArgIndex: number | null = null;\n for (const [index, arg] of args.entries()) {\n if (remainingMustBeOptional) {\n assert(\n arg.isOptional,\n `In ${namedArrayToString(...path)}, argument ${arg.name || index} is required, but it must be optional since the argument at index ${firstOptionalArgIndex} is optional`,\n );\n } else if (arg.isOptional) {\n remainingMustBeOptional = true;\n firstOptionalArgIndex ??= index;\n }\n }\n });\n}\n\n/** Asserts that options with `isRepeatable: true` don't have arguments */\nexport function assertRepeatableOptionsHaveNoArguments(spec: Subcommand): void {\n forEachOptionArray(spec, (options, path) => {\n for (const [index, option] of options.entries()) {\n assert(\n !(option.args && option.isRepeatable),\n `The option ${namedArrayToString(...path, option)} (index ${index}) has arguments and is repeatable. Repeatable options cannot have arguments, but you can use a variadic argument instead.`,\n );\n }\n });\n}\n\n/** Asserts that `isRepeatable` is only assigned a positive integer */\nexport function assertRepeatableOptionsArePositiveIntegers(spec: Subcommand): void {\n forEachOptionArray(spec, (options, path) => {\n for (const [index, option] of options.entries()) {\n if (typeof option.isRepeatable === \"number\") {\n assert(\n Number.isSafeInteger(option.isRepeatable),\n `The option ${namedArrayToString(...path, option)} (index ${index}) has its \\`isRepeatable\\` value set to a number that is not an integer`,\n );\n assert(\n !(option.isRepeatable < 1),\n `The option ${namedArrayToString(...path, option)} (index ${index}) has its \\`isRepeatable\\` value set to a number below 1, which means it can't be used at all`,\n );\n assert(\n option.isRepeatable !== 1,\n `The option ${namedArrayToString(...path, option)} (index ${index}) has its \\`isRepeatable\\` value set to 1, which is no different that omitting the property entirely`,\n );\n }\n }\n });\n}\n\n/** Asserts that options have unique names in their scope */\nexport function assertOptionsHaveLocallyUniqueNames(spec: Subcommand): void {\n forEachSubcommand(spec, (spec, path) => {\n const optionNames = new Set<string>();\n for (const [index, option] of makeArray(spec.options).entries()) {\n for (const name of makeArray(option.name)) {\n assert(\n !optionNames.has(name),\n `Option ${namedArrayToString(...path, option)} (index ${index}) has a non-unique name, '${name}'. Option names must be unique.`,\n );\n optionNames.add(name);\n }\n }\n });\n}\n\n/** Asserts that options don't shadow a persistent option */\nexport function assertOptionsDoNotShadowPersistentOptions(spec: Subcommand): void {\n forEachOptionArray(spec, (options, path) => {\n const persistentOptionNames = new Set(\n path\n .slice(0, -1)\n .flatMap((subcommand) =>\n subcommand.options\n ? subcommand.options\n .filter((option) => option.isPersistent)\n .flatMap((option) => option.name)\n : [],\n ),\n );\n for (const [index, option] of makeArray(options).entries()) {\n for (const name of makeArray(option.name)) {\n assert(\n !persistentOptionNames.has(name),\n `The option ${namedArrayToString(...path, option)} (index ${index}) shadows the name of a persistent option, ${repr(name)}. Option names can't shadow persistent options.`,\n );\n }\n }\n });\n}\n\n/** Asserts that subcommands don't share a name with a sibling subcommand */\nexport function assertSubcommandsHaveLocallyUniqueNames(spec: Subcommand): void {\n forEachSubcommand(spec, (spec, path) => {\n const names = new Set<string>();\n for (const [index, subcommand] of makeArray(spec.subcommands).entries()) {\n for (const name of makeArray(subcommand.name)) {\n assert(\n !names.has(name),\n `The subcommand ${namedArrayToString(...path, subcommand)} (index ${index}) has a non-unique name, ${repr(name)}. Subcommand names must be unique among the subcommands in the same array.`,\n );\n names.add(name);\n }\n }\n });\n}\n\n/**\n * Asserts that option names starting with a single dash aren't longer\n * than two characters, unless parserDirectives.flagsArePosixNoncompliant is\n * set on an ancestor command.\n */\nexport function assertLongOptionNamesDoNotStartWithSingleDash(spec: Subcommand): void {\n forEachOptionArray(spec, (options, path) => {\n // TODO: check if this is correct\n const skip =\n path\n .map((command) => command.parserDirectives?.flagsArePosixNoncompliant)\n .filter((value) => value !== undefined)\n .at(-1) || false;\n\n if (skip) return;\n\n for (const [index, option] of makeArray(options).entries()) {\n for (const name of makeArray(option.name)) {\n assert(\n !(\n (name.startsWith(\"-\") || name.startsWith(\"+\")) &&\n !name.startsWith(\"--\") &&\n name.length > 2\n ),\n `Option ${namedArrayToString(...path, option)} (index ${index}) has a name that the parser is unable to parse, as it starts with a single '${name[0]}'. Tokens starting with that character are interpreted as short options, so passing '${name}' will be interpreted as '${Array.from(name.slice(1), (letter) => name[0] + letter).join(\" \")}'. \\`parserDirectives.flagsArePosixNoncompliant\\` must be \\`true\\` on an ancestor command.`,\n );\n }\n }\n });\n}\n\n/**\n * Asserts that all option names start with dashes\n */\nexport function assertOptionNamesStartWithDashes(spec: Subcommand): void {\n forEachOptionArray(spec, (options, path) => {\n // TODO: check if this is correct\n const skip =\n path\n .map((command) => command.parserDirectives?.flagsArePosixNoncompliant)\n .filter((value) => value !== undefined)\n .at(-1) || false;\n\n if (skip) return;\n\n for (const [index, option] of makeArray(options).entries()) {\n for (const name of makeArray(option.name)) {\n assert(\n name.startsWith(\"-\") || name.startsWith(\"+\"),\n `Option ${namedArrayToString(...path, option)} (index ${index}) doesn't start with a dash or plus, the parser will be unable to find matching options without \\`parserDirectives.flagsAreNonPosixCompliant\\` being set to 'true' on an ancestor command`,\n );\n }\n }\n });\n}\n\n/**\n * Asserts that nothing is named exactly '--'\n */\nexport function assertNothingIsNamedDashDash(spec: Subcommand): void {\n forEachOptionArray(spec, (options, path) => {\n for (const [index, option] of makeArray(options).entries()) {\n for (const name of makeArray(option.name)) {\n assert(\n name !== \"--\",\n `Option ${namedArrayToString(...path, option)} (index ${index}) is named '--', which is a special instruction to the parser to treat all following tokens as arguments. This will never be matched`,\n );\n }\n }\n });\n forEachSubcommand(spec, (subcommands, path) => {\n for (const [index, subcommand] of makeArray(subcommands).entries()) {\n for (const name of makeArray(subcommand.name)) {\n assert(\n name !== \"--\",\n `Subcommand ${namedArrayToString(...path, subcommand)} (index ${index}) is named '--', which is a special instruction to the parser to treat all following tokens as arguments. This will never be matched`,\n );\n }\n }\n });\n}\n\n/**\n * Asserts that no option arg separator is an empty string\n */\nexport function assertOptionArgSeparatorsHaveCharacters(spec: Subcommand): void {\n // FIXME: `subcommands` isn't an array\n forEachSubcommand(spec, (subcommands, path) => {\n for (const [index, subcommand] of makeArray(subcommands).entries()) {\n const separators = makeArray(subcommand.parserDirectives?.optionArgSeparators);\n for (const [sepIndex, separator] of separators.entries()) {\n assert(\n separator !== \"\",\n `Subcommand ${namedArrayToString(...path, subcommand)} (index ${index}) has an empty string for an option arg separator at index ${sepIndex}. If you wanted to disable option arg separators, use an empty array.`,\n );\n }\n }\n });\n}\n\n/**\n * Asserts that options named \"+\" or \"-\" takes one arg\n */\nexport function assertPlusMinusOptionsTakeOneArg(spec: Subcommand): void {\n forEachOptionArray(spec, (options, path) => {\n // TODO: check if this is correct\n const skip =\n path\n .map((command) => command.parserDirectives?.flagsArePosixNoncompliant)\n .filter((value) => value !== undefined)\n .at(-1) || false;\n\n if (skip) return;\n\n for (const [index, option] of makeArray(options).entries()) {\n for (const name of makeArray(option.name)) {\n if (name !== \"-\" && name !== \"+\") {\n continue;\n }\n assert(\n option.args && !Array.isArray(option.args),\n `Option ${namedArrayToString(...path, option)} (index ${index}) must take exactly one argument`,\n );\n }\n }\n });\n}\n\n/**\n * Asserts that names do not have leading or trailing whitespace\n */\nexport function assertNamesHaveNoExtraWhitespace(spec: Subcommand): void {\n forEachOptionArray(spec, (options, path) => {\n for (const [index, option] of makeArray(options).entries()) {\n for (const name of makeArray(option.name)) {\n assert(\n name === name.trim(),\n `Option ${namedArrayToString(...path, option)} (index ${index}) has a name with extra whitespace`,\n );\n }\n }\n });\n // FIXME: `subcommands` isn't an array\n forEachSubcommand(spec, (subcommands, path) => {\n for (const [index, subcommand] of makeArray(subcommands).entries()) {\n for (const name of makeArray(subcommand.name)) {\n assert(\n name === name.trim(),\n `Subcommand ${namedArrayToString(...path, subcommand)} (index ${index}) has a name with extra whitespace`,\n );\n }\n }\n });\n forEachArgArray(spec, (args, path) => {\n for (const [index, arg] of makeArray(args).entries()) {\n for (const name of makeArray(arg.name)) {\n assert(\n name === name.trim(),\n `Arg in ${namedArrayToString(...path)} (at index ${index}) has a name with extra whitespace`,\n );\n }\n }\n });\n}\n\n/**\n * Asserts that everything has a description\n */\nexport function assertEverythingHasDescription(spec: Subcommand): void {\n assert(spec.description, `Spec has no description`);\n forEachOptionArray(spec, (options, path) => {\n for (const [index, option] of makeArray(options).entries()) {\n assert(\n option.description,\n `Option ${namedArrayToString(...path, option)} (index ${index}) has no description`,\n );\n }\n });\n // FIXME: `subcommands` isn't an array\n forEachSubcommand(spec, (subcommands, path) => {\n for (const [index, subcommand] of makeArray(subcommands).entries()) {\n assert(\n subcommand.description,\n `Subcommand ${namedArrayToString(...path, subcommand)} (index ${index}) has no description`,\n );\n }\n });\n}\n\n/**\n * Asserts that descriptions have line length < 69\n */\nexport function assertDescriptionLineLengthUnder69(spec: Subcommand): void {\n if (spec.description) {\n assert(\n spec.description.split(\"\\n\").every((line) => line.length <= 68),\n `Spec has a description line over 68 characters`,\n );\n }\n forEachOptionArray(spec, (options, path) => {\n for (const [index, option] of makeArray(options).entries()) {\n if (option.description) {\n assert(\n option.description.split(\"\\n\").every((line) => line.length <= 68),\n `Option ${namedArrayToString(...path, option)} (index ${index}) has a description line over 68 characters`,\n );\n }\n }\n });\n // FIXME: `subcommands` isn't an array\n forEachSubcommand(spec, (subcommands, path) => {\n for (const [index, subcommand] of makeArray(subcommands).entries()) {\n if (subcommand.description) {\n assert(\n subcommand.description.split(\"\\n\").every((line) => line.length <= 68),\n `Subcommand ${namedArrayToString(...path, subcommand)} (index ${index}) has a description line over 68 characters`,\n );\n }\n }\n });\n}\n\nexport function assertRequiresSeparatorTakesOneArg(spec: Subcommand): void {\n forEachOptionArray(spec, (options, path) => {\n for (const [index, option] of makeArray(options).entries()) {\n if (option.requiresSeparator) {\n const args = makeArray(option.args);\n assert(\n getMinArgs(args) <= 1,\n `Option ${namedArrayToString(...path, option)} (index ${index}) takes a minimum of ${getMinArgs(args)} arg(s), but because of requiresSeparator, it must instead be 0 or 1`,\n );\n assert(\n getMaxArgs(args) === 1,\n `Option ${namedArrayToString(...path, option)} (index ${index}) takes a maximum of ${getMaxArgs(args)} arg(s), but because of requiresSeparator, it's only able to take 1`,\n );\n }\n }\n });\n}\n\nexport function assertCommonOptionsArePersistent(spec: Subcommand): void {\n const isSameName = (a: string | readonly string[], b: string | readonly string[]) => {\n if (isArray(a) && isArray(b)) {\n return a.every((item) => b.includes(item)) && b.every((item) => a.includes(item));\n }\n // This might compare a string to an array, but if that happens the correct\n // result is `false` anyway, so this behaves correctly\n return a === b;\n };\n const failures: [string | readonly string[], string][] = [];\n forEachSubcommand(spec, (subcommand, path) => {\n if (!subcommand.subcommands) {\n return;\n }\n for (const option of makeArray(subcommand.options)) {\n const allChildSubcommands: Subcommand[] = [];\n forEachSubcommand(subcommand, (cmd) => allChildSubcommands.push(cmd));\n\n const allShareOption = allChildSubcommands.every((cmd) =>\n makeArray(cmd.options).some((o) => isSameName(option.name, o.name)),\n );\n\n if (allShareOption && !failures.some(([name]) => isSameName(name, option.name))) {\n failures.push([option.name, namedArrayToString(...path, option)]);\n }\n }\n });\n assert(\n failures.length === 0,\n `The following options were also defined by all subcommands. Instead, define the option once, and use \\`isPersistent: true\\` to persist it across all subcommands.\n${failures.map(([, line]) => \" * \" + line).join(\"\\n\")}\n`,\n );\n}\n\n/**\n * Asserts that references to other options by name actually refer to options\n * that exist in scope\n */\nexport function assertOptionNameReferencesExist(spec: Subcommand): void {\n forEachOptionArray(spec, (options, path) => {\n const optionNames = new Set([\n ...path\n .slice(0, -1)\n .flatMap((subcommand) =>\n subcommand.options\n ? subcommand.options\n .filter((option) => option.isPersistent)\n .flatMap((option) => option.name)\n : [],\n ),\n ...options.flatMap((option) => option.name),\n ]);\n for (const [index, option] of makeArray(options).entries()) {\n for (const name of makeArray(option.dependsOn)) {\n assert(\n optionNames.has(name),\n `The option ${namedArrayToString(...path, option)} (index ${index}) depends on an option named ${repr(name)}, which doesn't exist in its scope`,\n );\n }\n for (const name of makeArray(option.exclusiveOn)) {\n assert(\n optionNames.has(name),\n `The option ${namedArrayToString(...path, option)} (index ${index}) is exclusive on an option named ${repr(name)}, which doesn't exist in its scope`,\n );\n }\n }\n });\n}\n\n/**\n * Asserts that commands that will match subcommands by prefix do not take arguments\n */\nexport function assertPrefixMatchCommandsHaveNoArguments(spec: Subcommand): void {\n forEachSubcommand(spec, (subcommand, path) => {\n // Mirror the parser's default: prefix matching is OFF unless a command on\n // the path turns it on. `analyze.ts` initializes the flag to `false` and\n // only ever sets it `true`, so the ambiguity this guards against can only\n // arise when some ancestor (or the command itself) opts in.\n const matchesPrefix = path.some(\n (cmd) => cmd.parserDirectives?.subcommandsMatchUniquePrefix === true,\n );\n\n if (!matchesPrefix) return;\n\n assert(\n !(subcommand.subcommands && subcommand.args),\n `The command ${namedArrayToString(\n ...path,\n )} has at least one argument and subcommand, but matches subcommands based on unique prefixes. To fix this, use \\`parserDirectives: { subcommandsMatchUniquePrefix: false }\\``,\n );\n });\n}\n\nexport type TestNamingStyle = \"sentence\" | \"function\";\n\n/** Options that can be provided to `test` */\nexport interface TestOptions {\n /**\n * Allows options to shadow persistent options.\n *\n * This behavior may be desirable if you want to override an option on a\n * particular subcommand, but not for others.\n *\n * Be aware that this is not idiomatic.\n */\n allowShadowingPersistentOptions?: boolean;\n\n /**\n * Allows subcommands and options to not have a description\n */\n allowNoDescription?: boolean;\n\n /**\n * Allows descriptions to have lines over 68 characters long\n */\n allowLongDescriptionLines?: boolean;\n\n /**\n * Allow commands to have `parserDirectives.subcommandsMatchUniquePrefix` with args\n *\n * This is not allowed by default because it's probably a mistake. It makes\n * arguments ambiguous, users may not know if their invocation will run a\n * subcommand or the command.\n */\n allowMatchingSubcommandPrefixAndArgs?: boolean;\n\n /**\n * Change the naming style of the tests.\n *\n * - `sentence`: Use proper sentence casing, punctuation, etc\n * - `function`: Use the name of the assert function that will be executed\n */\n namingStyle?: TestNamingStyle;\n}\n\nfunction getName(namingStyle: TestNamingStyle, names: { [K in TestNamingStyle]: string }) {\n return names[namingStyle];\n}\n\nexport interface TestContextLike {\n step(name: string, fn: () => unknown | Promise<unknown>): unknown | Promise<unknown>;\n}\n\nasync function runStep(\n context: TestContextLike | undefined,\n name: string,\n fn: () => unknown | Promise<unknown>,\n): Promise<void> {\n if (context) {\n await context.step(name, fn);\n return;\n }\n await fn();\n}\n\n/**\n * Validate your CLI in your test suite\n *\n * This enforces certain constraints that the type system cannot. The parser\n * may ignore these issues, producing correct output that _appears_ to be\n * wrong.\n */\nexport function test(\n spec: Subcommand,\n options: TestOptions = {},\n): (t?: TestContextLike) => Promise<void> {\n const {\n allowShadowingPersistentOptions = false,\n allowNoDescription = false,\n allowLongDescriptionLines = false,\n allowMatchingSubcommandPrefixAndArgs = false,\n namingStyle = \"sentence\",\n } = options;\n return async (t) => {\n await runStep(\n t,\n getName(namingStyle, {\n sentence: \"Required arguments don't follow optional arguments\",\n function: \"assertRequiredArgumentsDoNotFollowOptionalArguments\",\n }),\n () => {\n assertRequiredArgumentsDoNotFollowOptionalArguments(spec);\n },\n );\n await runStep(\n t,\n getName(namingStyle, {\n sentence: \"Repeatable options don't have arguments\",\n function: \"assertRepeatableOptionsHaveNoArguments\",\n }),\n () => {\n assertRepeatableOptionsHaveNoArguments(spec);\n },\n );\n await runStep(\n t,\n getName(namingStyle, {\n sentence: \"Repeatable options that are numbers are integers greater than 1\",\n function: \"assertRepeatableOptionsArePositiveIntegers\",\n }),\n () => {\n assertRepeatableOptionsArePositiveIntegers(spec);\n },\n );\n await runStep(\n t,\n getName(namingStyle, {\n sentence: \"Long option names don't start with a single dash\",\n function: \"assertLongOptionNamesDoNotStartWithSingleDash\",\n }),\n () => {\n assertLongOptionNamesDoNotStartWithSingleDash(spec);\n },\n );\n await runStep(\n t,\n getName(namingStyle, {\n sentence: \"Option names all start with dashes\",\n function: \"assertOptionNamesStartWithDashes\",\n }),\n () => {\n assertOptionNamesStartWithDashes(spec);\n },\n );\n await runStep(\n t,\n getName(namingStyle, {\n sentence: \"Options have locally unique names\",\n function: \"assertOptionsHaveLocallyUniqueNames\",\n }),\n () => {\n assertOptionsHaveLocallyUniqueNames(spec);\n },\n );\n if (!allowShadowingPersistentOptions) {\n await runStep(\n t,\n getName(namingStyle, {\n sentence: \"Options don't shadow persistent options\",\n function: \"assertOptionsDoNotShadowPersistentOptions\",\n }),\n () => {\n assertOptionsDoNotShadowPersistentOptions(spec);\n },\n );\n }\n await runStep(\n t,\n getName(namingStyle, {\n sentence: \"Subcommands have locally unique names\",\n function: \"assertSubcommandsHaveLocallyUniqueNames\",\n }),\n () => {\n assertSubcommandsHaveLocallyUniqueNames(spec);\n },\n );\n await runStep(\n t,\n getName(namingStyle, {\n sentence: \"Nothing is named '--'\",\n function: \"assertNothingIsNamedDashDash\",\n }),\n () => {\n assertNothingIsNamedDashDash(spec);\n },\n );\n await runStep(\n t,\n getName(namingStyle, {\n sentence: \"Option arg separators are not an empty string\",\n function: \"assertOptionArgSeparatorsHaveCharacters\",\n }),\n () => {\n assertOptionArgSeparatorsHaveCharacters(spec);\n },\n );\n await runStep(\n t,\n getName(namingStyle, {\n sentence: \"Options named '-' or '+' take exactly one argument\",\n function: \"assertPlusMinusOptionsTakeOneArg\",\n }),\n () => {\n assertPlusMinusOptionsTakeOneArg(spec);\n },\n );\n await runStep(\n t,\n getName(namingStyle, {\n sentence: \"Names have no extra leading or trailing whitespace\",\n function: \"assertNamesHaveNoExtraWhitespace\",\n }),\n () => {\n assertNamesHaveNoExtraWhitespace(spec);\n },\n );\n if (!allowNoDescription) {\n await runStep(\n t,\n getName(namingStyle, {\n sentence: \"Everything has a description\",\n function: \"assertEverythingHasDescription\",\n }),\n () => {\n assertEverythingHasDescription(spec);\n },\n );\n }\n if (!allowLongDescriptionLines) {\n await runStep(\n t,\n getName(namingStyle, {\n sentence: \"Each line in descriptions is less than or equal to 68 characters\",\n function: \"assertDescriptionLineLengthUnder69\",\n }),\n () => {\n assertDescriptionLineLengthUnder69(spec);\n },\n );\n }\n await runStep(\n t,\n getName(namingStyle, {\n sentence: \"Options with `requiresSeparator` take exactly one argument\",\n function: \"assertRequiresSeparatorTakesOneArg\",\n }),\n () => {\n assertRequiresSeparatorTakesOneArg(spec);\n },\n );\n await runStep(\n t,\n getName(namingStyle, {\n sentence: \"Common options are not defined on every subcommand\",\n function: \"assertCommonOptionsArePersistent\",\n }),\n () => {\n assertCommonOptionsArePersistent(spec);\n },\n );\n await runStep(\n t,\n getName(namingStyle, {\n sentence: \"References to other options by name are valid\",\n function: \"assertOptionNameReferencesExist\",\n }),\n () => {\n assertOptionNameReferencesExist(spec);\n },\n );\n if (!allowMatchingSubcommandPrefixAndArgs) {\n await runStep(\n t,\n getName(namingStyle, {\n sentence:\n \"Commands with `subcommandsMatchUniquePrefix` don't have arguments and subcommands\",\n function: \"assertPrefixMatchCommandsHaveNoArguments\",\n }),\n () => {\n assertPrefixMatchCommandsHaveNoArguments(spec);\n },\n );\n }\n };\n}\n\nexport const testSpec = test;\n"],"ignoreList":[1],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,65 @@
1
1
  {
2
2
  "name": "@clo4/cli",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "1.0.0",
4
+ "description": "clo4's CLI framework: fully declarative, fast",
5
+ "keywords": [
6
+ "arg-parser",
7
+ "args",
8
+ "argument-parser",
9
+ "cli",
10
+ "command-line",
11
+ "help",
12
+ "options",
13
+ "shell-completion",
14
+ "subcommands",
15
+ "typescript"
16
+ ],
17
+ "license": "MIT",
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "git+https://github.com/clo4/cli.git"
21
+ },
22
+ "files": [
23
+ "dist"
24
+ ],
25
+ "type": "module",
26
+ "exports": {
27
+ ".": {
28
+ "types": "./dist/index.d.ts",
29
+ "import": "./dist/index.js"
30
+ },
31
+ "./testing": {
32
+ "types": "./dist/testing.d.ts",
33
+ "import": "./dist/testing.js"
34
+ }
35
+ },
36
+ "publishConfig": {
37
+ "access": "public"
38
+ },
39
+ "scripts": {
40
+ "build": "tsdown",
41
+ "build:bench": "tsdown --config tsdown.bench.config.ts",
42
+ "bench": "pnpm build && pnpm build:bench && node bench/dist/run.js",
43
+ "check": "tsc -p tsconfig.json --noEmit && tsc -p tsconfig.test.json --noEmit && tsc -p tsconfig.bench.json --noEmit",
44
+ "test": "vitest run",
45
+ "lint": "oxlint",
46
+ "lint:fix": "oxlint --fix",
47
+ "fmt": "oxfmt",
48
+ "fmt:check": "oxfmt --check",
49
+ "verify": "pnpm fmt:check && pnpm lint && pnpm check && pnpm build && pnpm test"
50
+ },
51
+ "devDependencies": {
52
+ "@types/node": "^26.6.6",
53
+ "cac": "^7.1.0",
54
+ "commander": "^15.0.0",
55
+ "fastest-levenshtein": "^1.0.16",
56
+ "oxfmt": "^0.75.0",
57
+ "oxlint": "^1.90.0",
58
+ "rolldown": "^1.2.16",
59
+ "tsdown": "^0.23.4",
60
+ "typescript": "^7.0.2",
61
+ "vitest": "^5.0.5",
62
+ "yargs": "^18.2.0"
63
+ },
64
+ "packageManager": "pnpm@12.16.1"
65
+ }