71 lines
1.7 KiB
TypeScript
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()
|
|
}
|
|
}
|