🛠 用 Node.js 和 commander 快速搭建一个 CLI 工具骨架(gix 实战)

🛠 用 Node.js 和 commander 快速搭建一个 CLI 工具骨架(gix 实战)

在上一篇中,我们聊了聊为什么我要开发一个 Git CLI 工具 ------ gix,解决我日常开发中繁琐重复的 Git 操作。

这篇文章,我们正式开工,从 0 到 1 搭建一个 CLI 工具的基础骨架,包含命令解析、参数传递、交互式输入等核心功能。


🧰 技术选型

工具 用途
Node.js 编写 CLI 工具的基础平台
TypeScript 类型安全、增强开发体验
commander 命令行参数解析
prompts 交互式输入(比 inquirer 更轻量)
chalk 控制终端输出样式
execa 执行 Git 命令

📁 项目初始化

我们用 pnpm 创建项目:

csharp 复制代码
pnpm init

安装依赖:

sql 复制代码
pnpm add commander prompts chalk execa
pnpm add -D typescript tsx @types/node

创建 tsconfig.json:

json 复制代码
{
  "compilerOptions": {
    "target": "ES2020",
    "module": "ESNext",
    "moduleResolution": "Node",
    "esModuleInterop": true,
    "resolveJsonModule": true,
    "outDir": "dist",
    "strict": true
  },
  "include": ["src"]
}

🧱 文件结构

bash 复制代码
/packages/cli
  ├── src
  │   ├── index.ts         # 入口文件
  │   ├── commands
  │   │   ├── squash.ts    # squash 命令逻辑
  │   │   └── merge.ts     # merge 命令逻辑
  │   └── utils
  │       └── git.ts       # Git 命令封装
  ├── package.json
  └── tsconfig.json

🚪 Step 1:配置命令行入口

在 src/index.ts 中配置 CLI 主入口:

javascript 复制代码
#!/usr/bin/env node

import { Command } from 'commander'
import { runSquash } from './commands/squash'
import { runMerge } from './commands/merge'

const program = new Command()

program.name('gix').description('Git 扩展工具集').version('0.0.1')

program
  .command('squash')
  .description('合并最近的多次提交')
  .option('-n, --count <number>', '合并次数', '2')
  .option('--all', '合并所有提交')
  .action(runSquash)

program
  .command('merge')
  .description('交互式合并两个 commit')
  .option('-f, --from <hash>', '起始 commit')
  .option('-t, --to <hash>', '结束 commit')
  .option('-m, --message <msg>', '合并后的 commit 信息')
  .action(runMerge)

program.parse()

在 package.json 中加入:

json 复制代码
"bin": {
  "gix": "dist/index.js"
}

然后加上执行权限:

bash 复制代码
chmod +x dist/index.js

✨ Step 2:实现 squash 命令逻辑

src/commands/squash.ts

typescript 复制代码
import prompts from 'prompts'
import { execGit } from '../utils/git'
import chalk from 'chalk'

export async function runSquash(opts: { count: string; all?: boolean }) {
  const count = opts.all ? 'HEAD~$(git rev-list --count HEAD)' : `HEAD~${opts.count}`

  const { message } = await prompts({
    type: 'text',
    name: 'message',
    message: '请输入新的 commit 信息:'
  })

  if (!message) {
    console.log(chalk.red('❌ commit 信息不能为空'))
    return
  }

  await execGit(`reset --soft ${count}`)
  await execGit(`commit -m "${message}" --no-verify`)

  console.log(chalk.green('🎉 squash 成功完成!'))
}

🔧 Step 3:封装 Git 命令工具函数

src/utils/git.ts

javascript 复制代码
import { execa } from 'execa'

export async function execGit(cmd: string) {
  const [cmdName, ...args] = cmd.split(' ')
  await execa(cmdName, args, { stdio: 'inherit' })
}

✅ 试运行

bash 复制代码
pnpm build
pnpm link
gix squash -n 2

输出:

sql 复制代码
? 请输入新的 commit 信息:feat: 优化用户界面逻辑
🎉 squash 成功完成!

💡 小结

至此,我们完成了:

  • 初始化 CLI 工具项目结构

  • 配置 commander 命令解析

  • 支持交互式输入(prompts)

  • 执行 Git 命令(execa)

gix 的核心命令正在一步步搭建中 🚀


🔮 下一篇预告

下一篇我会分享如何构建更复杂的交互命令(如 gix merge),支持选择 commit、填写信息、自动推送,并通过 prompts 提升使用体验。

欢迎点赞 + 收藏 + 评论支持我继续更系列文章!

相关推荐
亦世凡华、18 分钟前
Rollup入门与进阶:为现代Web应用构建超小的打包文件
前端·经验分享·rollup·配置项目·前端分享
嘵奇29 分钟前
Spring Boot中HTTP连接池的配置与优化实践
spring boot·后端·http
Bl_a_ck44 分钟前
【React】Craco 简介
开发语言·前端·react.js·typescript·前端框架
子燕若水1 小时前
Flask 调试的时候进入main函数两次
后端·python·flask
程序员爱钓鱼1 小时前
跳转语句:break、continue、goto -《Go语言实战指南》
开发语言·后端·golang·go1.19
x66ccff1 小时前
【github】主页显示star和fork
github
augenstern4162 小时前
webpack重构优化
前端·webpack·重构
Persistence___2 小时前
SpringBoot中的拦截器
java·spring boot·后端
海拥✘2 小时前
CodeBuddy终极测评:中国版Cursor的开发革命(含安装指南+HTML游戏实战)
前端·游戏·html
嘵奇2 小时前
Spring Boot 跨域问题全解:原理、解决方案与最佳实践
java·spring boot·后端