| |
| import type { Command, Option } from "commander"; |
|
|
| const activeErrorCommandByRoot = new WeakMap<Command, Command>(); |
| const lazyCommands = new WeakSet<Command>(); |
|
|
| export function markCommanderLazyCommand(command: Command): void { |
| lazyCommands.add(command); |
| } |
|
|
| function getCommandHierarchy(command: Command): Command[] { |
| const hierarchy: Command[] = []; |
| for (let current: Command | null = command; current; current = current.parent ?? null) { |
| hierarchy.unshift(current); |
| } |
| return hierarchy; |
| } |
|
|
| function requiresFollowingValue(token: string, options: readonly Option[]): boolean { |
| if (token.includes("=")) { |
| return false; |
| } |
| const exact = options.find((option) => option.short === token || option.long === token); |
| if (exact) { |
| return exact.required; |
| } |
| if (!token.startsWith("-") || token.startsWith("--")) { |
| return false; |
| } |
| const shortGroup = token.slice(1); |
| for (let index = 0; index < shortGroup.length; index += 1) { |
| const option = options.find((candidate) => candidate.short === `-${shortGroup[index]}`); |
| if (!option) { |
| return false; |
| } |
| if (option.required || option.optional) { |
| return option.required && index === shortGroup.length - 1; |
| } |
| } |
| return false; |
| } |
|
|
| |
| export function hasCommanderOptionToken( |
| command: Command, |
| argv: readonly string[], |
| tokens: ReadonlySet<string>, |
| kind: "flag" | "value", |
| ): boolean { |
| const hierarchy = getCommandHierarchy(command); |
| const args = argv.slice(2); |
| let commandIndex = 0; |
| for (let index = 0; index < args.length; index += 1) { |
| const arg = args[index]; |
| if (arg === undefined || arg === "--") { |
| break; |
| } |
| const nextCommand = hierarchy[commandIndex + 1]; |
| if (nextCommand && (arg === nextCommand.name() || nextCommand.aliases().includes(arg))) { |
| commandIndex += 1; |
| continue; |
| } |
| |
| |
| if (requiresFollowingValue(arg, hierarchy[commandIndex]?.options ?? [])) { |
| const value = args[index + 1]; |
| if (kind === "value" && value && tokens.has(value)) { |
| return true; |
| } |
| index += 1; |
| continue; |
| } |
| if (kind === "flag" && tokens.has(arg.split("=")[0] ?? arg)) { |
| return true; |
| } |
| } |
| return false; |
| } |
|
|
| |
| export function getCommanderCommandPath(command: Command): string[] { |
| const commandPath: string[] = []; |
| for (let current: Command | null = command; current?.parent; current = current.parent) { |
| commandPath.unshift(current.name()); |
| } |
| return commandPath; |
| } |
|
|
| function getRootCommand(command: Command): Command { |
| let root = command; |
| while (root.parent) { |
| root = root.parent; |
| } |
| return root; |
| } |
|
|
| |
| export function getCommanderSubcommandFact( |
| command: Command, |
| args: readonly string[], |
| ): { kind: "defer" } | { kind: "unknown"; name: string } | undefined { |
| const helpRequested = args.includes("-h") || args.includes("--help"); |
| if (helpRequested && lazyCommands.has(command)) { |
| return { kind: "defer" }; |
| } |
| const firstArgument = command.args[0]; |
| const matchesChild = command.commands.some( |
| (child) => child.name() === firstArgument || child.aliases().includes(firstArgument ?? ""), |
| ); |
| if ( |
| command.registeredArguments.length > 0 || |
| firstArgument === undefined || |
| firstArgument.startsWith("-") || |
| matchesChild |
| ) { |
| return undefined; |
| } |
| return command.commands.length > 0 ? { kind: "unknown", name: firstArgument } : undefined; |
| } |
|
|
| |
| export function setCommanderErrorCommand(command: Command): () => void { |
| const root = getRootCommand(command); |
| const previous = activeErrorCommandByRoot.get(root); |
| activeErrorCommandByRoot.set(root, command); |
| return () => { |
| if (previous) { |
| activeErrorCommandByRoot.set(root, previous); |
| } else { |
| activeErrorCommandByRoot.delete(root); |
| } |
| }; |
| } |
|
|
| |
| export function getCommanderErrorCommandPath(program: Command): string[] | undefined { |
| const command = activeErrorCommandByRoot.get(getRootCommand(program)); |
| return command ? getCommanderCommandPath(command) : undefined; |
| } |
|
|
| |
| export function getCommanderErrorCommandNames(program: Command): string[] | undefined { |
| const command = activeErrorCommandByRoot.get(getRootCommand(program)); |
| return command && getCommanderCommandPath(command).length |
| ? command |
| .createHelp() |
| .visibleCommands(command) |
| .flatMap((child) => child.aliases().concat(child.name())) |
| : undefined; |
| } |
|
|