| |
| import type { Command } from "commander"; |
| import { sanitizeForLog } from "../../../packages/terminal-core/src/ansi.js"; |
| import type { NamedCommandDescriptor } from "./command-group-descriptors.js"; |
|
|
| |
| type CommandDescriptorLike = Pick<NamedCommandDescriptor, "name" | "description" | "hidden">; |
|
|
| const SAFE_COMMAND_NAME_PATTERN = /^[A-Za-z0-9][A-Za-z0-9_-]*$/; |
|
|
| |
| export function normalizeCommandDescriptorName(name: string): string | null { |
| const normalized = name.trim(); |
| return SAFE_COMMAND_NAME_PATTERN.test(normalized) ? normalized : null; |
| } |
|
|
| function assertSafeCommandDescriptorName(name: string): string { |
| const normalized = normalizeCommandDescriptorName(name); |
| if (!normalized) { |
| throw new Error(`Invalid CLI command name: ${JSON.stringify(name.trim())}`); |
| } |
| return normalized; |
| } |
|
|
| |
| export function sanitizeCommandDescriptorDescription(description: string): string { |
| return sanitizeForLog(description).trim(); |
| } |
|
|
| |
| export function collectUniqueCommandDescriptors<TDescriptor extends CommandDescriptorLike>( |
| descriptorGroups: readonly (readonly TDescriptor[])[], |
| ): TDescriptor[] { |
| const seen = new Set<string>(); |
| const descriptors: TDescriptor[] = []; |
| for (const group of descriptorGroups) { |
| for (const descriptor of group) { |
| if (seen.has(descriptor.name)) { |
| continue; |
| } |
| seen.add(descriptor.name); |
| descriptors.push(descriptor); |
| } |
| } |
| return descriptors; |
| } |
|
|
| |
| export function addCommandDescriptorsToProgram( |
| program: Command, |
| descriptors: readonly CommandDescriptorLike[], |
| existingCommands: Set<string> = new Set(), |
| ): Set<string> { |
| for (const descriptor of descriptors) { |
| const name = assertSafeCommandDescriptorName(descriptor.name); |
| if (existingCommands.has(name)) { |
| continue; |
| } |
| program |
| .command(name, { hidden: descriptor.hidden }) |
| .description(sanitizeCommandDescriptorDescription(descriptor.description)); |
| existingCommands.add(name); |
| } |
| return existingCommands; |
| } |
|
|