一、明确核心目标
- 快速初始化:秒级生成项目结构
- 多模板支持:React/Vue/Svelte等框架预设
- 智能依赖管理:自动安装正确版本依赖
- 配置生成:零配置开箱即用,支持自定义
- 插件系统:可扩展工具能力(可选)
二、技术选型
# 基础技术栈
Node.js (>=16.x) + TypeScript(推荐)
Commander.js # 命令行交互
Inquirer.js # 用户问答
Chalk # 终端美化
Ora # 加载动画
Fs-extra # 文件操作
Download-git-repo # 模板下载
三、开发步骤
1. 初始化脚手架项目
mkdir my-cli && cd my-cli
npm init -y
npm install commander inquirer chalk ora fs-extra download-git-repo
2. 创建可执行文件
// bin/cli.js
#!/usr/bin/env node
const { program } = require('commander')
program
.version('1.0.0')
.command('create <project-name>')
.description('创建新项目')
.action(async (name) => {
console.log(`创建项目: ${name}`)
// 后续添加交互逻辑
})
program.parse()
3. 实现模板下载
// utils/download.js
const { promisify } = require('util')
const download = promisify(require('download-git-repo'))
const downloadTemplate = async (repo, dest) => {
const loading = ora('下载模板...').start()
try {
await download(repo, dest, { clone: true })
loading.succeed('模板下载完成')
} catch (err) {
loading.fail('下载失败: ' + err.message)
process.exit(1)
}
}
4. 交互式配置生成
// prompts/config.js
const inquirer = require('inquirer')
const getProjectConfig = async () => {
return inquirer.prompt([
{
type: 'list',
name: 'framework',
message: '选择框架:',
choices: ['React', 'Vue', 'Svelte']
},
{
type: 'checkbox',
name: 'features',
message: '选择功能:',
choices: ['TypeScript', 'ESLint', 'Unit Test']
}
])
}
5. 动态文件处理
// generators/configGenerator.js
const fs = require('fs-extra')
const handlebars = require('handlebars')
const generateConfig = async (projectPath, answers) => {
// 读取模板文件
const pkgPath = `${projectPath}/package.json`
const pkgContent = await fs.readFile(pkgPath, 'utf-8')
// 使用Handlebars模板引擎
const template = handlebars.compile(pkgContent)
const result = template({
name: answers.projectName,
features: answers.features.includes('TypeScript')
? { typescript: true }
: {}
})
// 写入修改后的文件
await fs.outputFile(pkgPath, result)
}
6. 依赖安装
// utils/installDeps.js
const { execa } = require('execa')
const installDependencies = async (projectPath) => {
const loading = ora('安装依赖...').start()
try {
process.chdir(projectPath)
await execa('npm', ['install'])
loading.succeed('依赖安装完成')
} catch (err) {
loading.fail('安装失败: ' + err.message)
}
}
四、高级功能实现
1. 插件系统设计
// core/pluginAPI.js
class PluginAPI {
constructor(context) {
this.context = context
this.plugins = []
}
registerPlugin(plugin) {
this.plugins.push(plugin)
plugin.apply(this.context)
}
}
// 使用示例
api.registerPlugin({
apply: (ctx) => {
ctx.addPreset('vue-advanced', require('./presets/vue-advanced'))
}
})
2. 智能版本管理
// utils/versionManager.js
const getLatestVersion = async (pkg) => {
const { stdout } = await execa('npm', ['view', pkg, 'version'])
return stdout.trim()
}
const resolveDeps = async (features) => {
const deps = {
react: await getLatestVersion('react'),
vue: await getLatestVersion('vue@next')
}
// 返回版本映射
}
五、质量保障
1. 单元测试
// tests/create.test.js
test('生成React项目', async () => {
await runCliCommand('create test-react --template react')
expect(fs.existsSync('test-react/src/App.jsx')).toBeTruthy()
})
2. E2E测试
# 使用Cypress测试完整流程
cypress run --spec "cypress/e2e/create-project.cy.js"
六、发布与维护
- 发布到NPM:
npm login
npm publish --access public
- 持续更新:
- 建立模板仓库管理机制
- 实现脚手架自更新命令:
program
.command('update')
.description('更新脚手架')
.action(() => execa('npm', ['install', '-g', 'my-cli@latest']))
七、性能优化技巧
- 模板预编译:将模板预先编译为二进制文件
- 依赖缓存:缓存常用依赖包到本地
- 并行操作:使用Promise.all处理异步任务
- 增量下载:通过Git sparse-checkout只下载必要文件
通过以上步骤,您可以逐步构建一个功能完备的现代化脚手架工具。建议先实现核心功能,再逐步迭代高级特性。注意保持代码模块化,方便后续扩展和维护。