maoda-commander-tt
v0.0.28
Published
一个通用命令行工具
Readme
template-commander
一个基于 commander 的通用 CLI 模板项目,采用面向对象架构设计,支持快速扩展子命令。
特性
- 面向对象架构 — 所有命令继承
BaseCommand<TOptions>,统一生命周期管理 - Disposable 体系 — 继承自 rockbed
Disposable基类,自动管理资源释放 - 事件系统 — 基于 rockbed
Emitter提供onBeforeExecute/onAfterExecute等生命周期钩子 - 结构化错误处理 — 使用
ILvErrorOr<T>代替 throw/reject,错误码可追溯 - TypeScript 严格模式 — 全量 strict,类型安全
- 开发热调试 — 基于 tsx 直接运行 TS,支持
--inspect调试
目录结构
template-commander/
├── src/
│ ├── main.ts # 入口文件
│ ├── core/
│ │ ├── cli-app.ts # CliApp 主应用类
│ │ ├── base-command.ts # BaseCommand 抽象基类
│ │ └── command-types.ts # 命令元信息类型
│ ├── commands/
│ │ ├── hello/ # 示例命令组
│ │ ├── pippit/ # pippit 命令组
│ │ └── git/ # 新增 git 命令组
│ └── bedrock/ # 本地补充基础设施
│ ├── config/ # 本地配置读写
│ ├── pkg/ # package.json 查找
│ └── uuid/ # UUID 工具
├── package.json
└── tsconfig.json快速开始
# 安装依赖
pnpm install
# 开发运行
pnpm dev -- --help
pnpm dev -- hello Alice
pnpm dev -- hello Alice -u
pnpm dev -- git mm "update docs"
pnpm dev -- git pp -n
# 调试模式 (支持 Chrome DevTools / VS Code 断点)
pnpm dev:debug -- hello Alice
# 构建
pnpm build
# 运行编译产物
pnpm start -- --help如何扩展新命令
- 在
src/commands/下新建文件,创建继承BaseCommand<TOptions>的类:
import type { Command } from 'commander';
import { BaseCommand } from '../core/base-command.js';
import type { ICommandActionContext } from '../core/base-command.js';
import type { ICommandMeta } from '../core/command-types.js';
import type { ILvErrorOr } from 'rockbed/error';
interface IMyOptions {
verbose: boolean;
}
export class MyCommand extends BaseCommand<IMyOptions> {
protected _meta(): ICommandMeta {
return {
name: 'my-cmd',
description: '这是我的自定义命令',
aliases: ['mc'],
};
}
protected _configureArguments(cmd: Command): void {
cmd.argument('<target>', '操作目标');
}
protected _configureOptions(cmd: Command): void {
cmd.option('--verbose', '详细输出', false);
}
protected async _execute(ctx: ICommandActionContext<IMyOptions>): Promise<ILvErrorOr<void>> {
const target = ctx.args[0] as string;
if (ctx.options.verbose) {
console.log(`[verbose] target = ${target}`);
}
// 业务逻辑...
return this._makeOk();
// 或错误: return this._makeError(1001, '发生了某种错误');
}
}- 在
src/main.ts中注册:
import { MyCommand } from './commands/my-command.js';
app.registerCommand(new MyCommand());完成!新命令会自动出现在 --help 输出和 help [command] 中。
架构说明
BaseCommand
每个子命令需要实现:
| 方法 | 说明 |
|---|---|
| _meta() | 返回命令名、描述、别名 |
| _execute(ctx) | 命令核心逻辑,返回 ILvErrorOr<void> |
| _configureOptions(cmd) | (可选) 配置 commander 选项 |
| _configureArguments(cmd) | (可选) 配置 commander 参数 |
| getExample() | 根据已注册的参数和选项自动生成命令执行示例 |
生命周期事件
const cmd = new HelloCommand();
cmd.onBeforeExecute(() => console.log('即将执行'));
cmd.onAfterExecute((result) => {
if (result.ok) console.log('执行成功');
else console.log(`执行失败: [${result.code}] ${result.msg}`);
});错误处理
使用 ILvErrorOr<T> 而非 throw:
// 成功
return this._makeOk();
// 失败 (错误码 + 消息)
return this._makeError(1001, '文件不存在');
// 也可使用 rockbed 预定义错误
import { invalidArgumentError } from 'rockbed/error';
return invalidArgumentError('参数 name 不能为空');Scripts
| 脚本 | 说明 |
|---|---|
| pnpm dev | 使用 tsx 直接运行(开发用) |
| pnpm dev:debug | 启动 Node inspect 调试 |
| pnpm build | TypeScript 编译 |
| pnpm start | 运行编译后产物 |
| pnpm clean | 清理 dist 目录 |
技术栈
- TypeScript 5.x (strict mode)
- commander 13.x
- chalk 5.x
- tsx (开发热运行)
