本文将手把手带你构建一个支持远程模板下载、自定义项目名称,并完成模块化拆分的 CLI 脚手架工具,适用于初创项目、团队内部工具或者开源项目快速初始化。
🧩 为什么要自己造一个 CLI 脚手架?
在日常开发中,我们常用脚手架如 create-react-app
、vue-cli
来初始化项目,它们帮我们完成繁琐的初始化配置。如果你:
- 想统一团队的项目初始化结构;
- 想搭建自己的模板体系;
- 想通过命令行一键拉取模板代码;
那自定义脚手架就是你的不二选择!
📦 项目结构设计(模块化)
我们先设计一套清晰的目录结构,便于后续拓展和维护:
my-cli/
├── bin/
│ └── my-cli.js # CLI 命令入口
├── lib/
│ ├── init.js # 初始化流程调度
│ ├── download.js # 模板下载逻辑
│ ├── log.js # 统一日志封装
│ └── repo.js # 仓库地址统一管理
├── package.json
🛠️ 核心依赖库说明
库名 | 用途描述 |
---|---|
commander |
CLI 命令行参数解析 |
download-git-repo |
支持 GitHub/Gitee 等仓库模板下载 |
ora |
终端加载动画 |
chalk |
控制台美化输出 |
inquirer (可选) |
增强交互体验,如模板选择 |
🧱 一步步构建 CLI 工具
Step 1️⃣:初始化并安装依赖
mkdir my-cli && cd my-cli
npm init -y
npm install commander download-git-repo ora chalk
Step 2️⃣:编写命令入口 bin/my-cli.js
#!/usr/bin/env node
const { program } = require('commander');
const init = require('../lib/init');
program
.version('1.0.0')
.command('init <projectName>')
.description('初始化一个项目模板')
.action((projectName) => {
init(projectName);
});
program.parse(process.argv);
别忘了给该文件执行权限:
chmod +x bin/my-cli.js
并在package.json
添加:
"bin": {
"my-cli": "./bin/my-cli.js"
}
Step 3️⃣:编写模板下载逻辑 lib/download.js
const download = require('download-git-repo');
const ora = require('ora');
const { error, success } = require('./log');
function downloadTemplate(repo, dest) {
const spinner = ora('📦 正在下载模板...').start();
return new Promise((resolve, reject) => {
download(repo, dest, { clone: false }, (err) => {
if (err) {
spinner.fail('❌ 下载失败');
error(err);
reject(err);
} else {
spinner.succeed('✅ 下载成功');
resolve();
}
});
});
}
module.exports = downloadTemplate;
Step 4️⃣:统一日志输出 lib/log.js
const chalk = require('chalk');
exports.success = (msg) => console.log(chalk.green(msg));
exports.error = (msg) => console.log(chalk.red(msg));
exports.info = (msg) => console.log(chalk.blue(msg));
Step 5️⃣:管理模板仓库地址 lib/repo.js
module.exports = {
vue: 'github:yourname/vue-template',
react: 'github:yourname/react-template',
default: 'github:yourname/base-template'
};
你可以拓展为支持
inquirer
交互选择模板。
Step 6️⃣:主流程调度 lib/init.js
const path = require('path');
const fs = require('fs');
const downloadTemplate = require('./download');
const repo = require('./repo');
const { error, success } = require('./log');
async function init(projectName) {
const targetDir = path.resolve(process.cwd(), projectName);
if (fs.existsSync(targetDir)) {
error(`目录 ${projectName} 已存在`);
process.exit(1);
}
const templateRepo = repo.default;
try {
await downloadTemplate(templateRepo, targetDir);
success(`🎉 项目 ${projectName} 初始化完成`);
console.log(`👉 你可以执行:\n cd ${projectName}\n npm install\n npm start`);
} catch (err) {
error('初始化失败');
}
}
module.exports = init;
🚀 如何运行和测试
npm link # 将 CLI 注册为全局命令
my-cli init my-app
你的模板就会从远程仓库拉取并生成到 my-app
目录!
🌱 后续拓展方向
目标 | 技术点 |
---|---|
多模板选择 | 使用 inquirer 提供列表选择 |
模板渲染变量 | 加入 ejs 渲染 package.json 等文件 |
模板配置项 | 提供 .myclirc 管理默认仓库 |
支持 GitLab / Gitee | 使用 clone: true 下载私有仓库 |