mime-todo-cli/src/cli/CLI.ts
2026-03-15 05:11:59 +01:00

71 lines
1.7 KiB
TypeScript

// CLI dispatcher — recursively walks CLICommand tree and wires up yargs
import yargs, { type Argv } from "yargs"
import { hideBin } from "yargs/helpers"
import { CLICommand } from "./CLICommand.js"
interface CLIOpts {
prog: string
description: string
}
export class CLI {
private prog: string
private description: string
constructor(opts: CLIOpts) {
this.prog = opts.prog
this.description = opts.description
}
private registerCommand(y: Argv, CommandClass: new () => CLICommand): void {
const cmd = new CommandClass()
const subcommands = (CommandClass as any)._subcommands as (new () => CLICommand)[] | undefined
if (subcommands?.length) {
// Branch command with subcommands
y.command(
cmd.name,
cmd.help,
(sub) => {
for (const Sub of subcommands) {
this.registerCommand(sub, Sub)
}
return sub.demandCommand(1, `Please specify a ${cmd.name} subcommand`)
},
() => {}
)
} else {
// Leaf command
y.command(
cmd.name,
cmd.help,
(sub) => cmd.addArguments(sub),
async (args) => {
const code = await cmd.execute(args)
process.exit(code)
}
)
}
}
bootstrap(commands: (new () => CLICommand)[]): void {
const y = yargs(hideBin(process.argv))
.scriptName(this.prog)
.usage(`${this.description}\n\n$0 <command> [options]`)
.option("verbose", {
alias: "v",
type: "boolean",
description: "Enable verbose output",
default: false,
})
.demandCommand(1, "Please specify a command")
.strict()
.help()
for (const Cmd of commands) {
this.registerCommand(y, Cmd)
}
y.parse()
}
}