一、明确核心目标

  1. 快速初始化:秒级生成项目结构
  2. 多模板支持:React/Vue/Svelte等框架预设
  3. 智能依赖管理:自动安装正确版本依赖
  4. 配置生成:零配置开箱即用,支持自定义
  5. 插件系统:可扩展工具能力(可选)

二、技术选型

# 基础技术栈
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"

六、发布与维护

  1. 发布到NPM
npm login
npm publish --access public
  1. 持续更新
  • 建立模板仓库管理机制
  • 实现脚手架自更新命令:
program
  .command('update')
  .description('更新脚手架')
  .action(() => execa('npm', ['install', '-g', 'my-cli@latest']))

七、性能优化技巧

  1. 模板预编译:将模板预先编译为二进制文件
  2. 依赖缓存:缓存常用依赖包到本地
  3. 并行操作:使用Promise.all处理异步任务
  4. 增量下载:通过Git sparse-checkout只下载必要文件

通过以上步骤,您可以逐步构建一个功能完备的现代化脚手架工具。建议先实现核心功能,再逐步迭代高级特性。注意保持代码模块化,方便后续扩展和维护。