Node 开发 CLI 工具简易指南
开发 CLI 工具其实没想象中那么复杂。作为一个经常需要重复执行各种任务的开发者,我发现自己写小工具会比找现成工具更顺手。今天就来聊聊怎么快速上手开发一个实用的命令行工具。
为什么要开发 CLI 工具?
日常开发中,我们经常遇到这些场景:
- 每次部署都要执行一堆命令
- 代码检查、格式化总是忘记某些步骤
- 团队协作时,大家的操作流程不统一
- 重复性工作太多,但又不值得搞个复杂的自动化系统
这时候,一个简单的 CLI 工具就能解决大部分问题。
搭建基础框架
项目准备
先创建项目结构:
mkdir my-cli-tool
cd my-cli-tool
npm init -y
安装核心依赖:
npm install yargs
npm install -D typescript @types/node
为什么选择 yargs?它能帮我们处理命令解析、参数验证、帮助信息生成等琐事,让我们专注于业务逻辑。
配置文件
package.json
里最重要的是 bin
字段,它告诉系统这个包提供了哪些可执行命令:
{
"bin": {
"mytool": "./dist/index.js"
},
"type": "module"
}
核心实现思路
入口文件设计
主入口文件其实就是个"指挥官",负责接收命令并分发给对应的处理模块:
#!/usr/bin/env node
import yargs from 'yargs';
import { hideBin } from 'yargs/helpers';
// 导入各种命令
import deployCommand from './commands/deploy.js';
import checkCommand from './commands/check.js';
yargs(hideBin(process.argv))
.command(deployCommand)
.command(checkCommand)
.help()
.argv;
关键在于那行 shebang #!/usr/bin/env node
,它让系统知道用 Node.js 来执行这个文件。
命令模块化
每个功能做成独立的命令模块,结构清晰,维护方便:
// commands/deploy.js
export default {
command: 'deploy [env]',
describe: '部署到指定环境',
builder: (yargs) => {
return yargs
.positional('env', {
describe: '目标环境',
choices: ['dev', 'test', 'prod'],
default: 'dev'
})
.option('force', {
type: 'boolean',
describe: '强制部署'
});
},
handler: async (argv) => {
console.log(`开始部署到 ${argv.env}...`);
// 实际部署逻辑
}
};
实用技巧分享
让用户体验更友好
添加一些贴心的细节:
// 彩色输出
const colors = {
green: '\x1b[32m',
red: '\x1b[31m',
yellow: '\x1b[33m',
reset: '\x1b[0m'
};
const log = {
success: (msg) => console.log(`${colors.green}✓${colors.reset} ${msg}`),
error: (msg) => console.error(`${colors.red}✗${colors.reset} ${msg}`),
warn: (msg) => console.warn(`${colors.yellow}!${colors.reset} ${msg}`)
};
配置文件支持
大多数工具都需要配置,可以这样处理:
import { existsSync, readFileSync } from 'fs';
import { join } from 'path';
function loadConfig() {
const configPath = join(process.cwd(), '.mytool.json');
if (existsSync(configPath)) {
return JSON.parse(readFileSync(configPath, 'utf8'));
}
return {}; // 默认配置
}
进度反馈
对于耗时操作,给用户一些反馈:
// 简单的加载动画
let dots = 0;
const spinner = setInterval(() => {
process.stdout.write(`\r处理中${'.'.repeat(dots % 4)}`);
dots++;
}, 200);
// 完成后清理
clearInterval(spinner);
console.log('\n完成!');
常见需求的解决方案
交互式输入
有时需要用户输入一些信息:
npm install inquirer
import inquirer from 'inquirer';
const answers = await inquirer.prompt([
{
type: 'confirm',
name: 'continue',
message: '确定要执行这个操作吗?'
}
]);
if (!answers.continue) {
console.log('操作已取消');
process.exit(0);
}
文件操作
CLI 工具经常需要读写文件:
import { promises as fs } from 'fs';
// 检查文件是否存在
async function fileExists(path) {
try {
await fs.access(path);
return true;
} catch {
return false;
}
}
// 安全地写入文件
async function writeFileIfNotExists(path, content) {
if (await fileExists(path)) {
const answer = await inquirer.prompt({
type: 'confirm',
name: 'overwrite',
message: `${path} 已存在,是否覆盖?`
});
if (!answer.overwrite) return;
}
await fs.writeFile(path, content);
}
调试和发布
本地测试
开发期间,用 npm link
来本地测试:
npm run build
npm link
# 现在可以直接使用你的命令了
mytool deploy --env test
错误处理
别忘了处理异常情况:
process.on('uncaughtException', (error) => {
console.error('程序出现错误:', error.message);
process.exit(1);
});
// 在异步操作中
try {
await riskyOperation();
} catch (error) {
log.error(`操作失败:${error.message}`);
process.exit(1);
}
一些建议
保持简单:CLI 工具最大的优势就是简单直接。如果发现逻辑越来越复杂,可能需要重新考虑设计。
文档很重要:写个简单的 README,说明常用命令和参数。用户(包括未来的自己)会感谢你的。
版本管理:每次更新都更新版本号,方便追踪问题。
测试一下:至少在不同环境下手动测试一遍主要功能。
总结
开发 CLI 工具其实就是把日常重复的工作自动化。不需要追求完美,能解决实际问题就是好工具。
从简单开始,逐步完善。可能一开始只是几行脚本,慢慢就发展成了团队必备的生产力工具。
最重要的是,动手去做。第一个版本可能很粗糙,但至少迈出了第一步。后续的优化和功能添加都是水到渠成的事情。