从0到1自定义脚手架

924 阅读3分钟

一、搭建一个简单的脚手架

  1. 环境准备:

    • 安装node环境

    • 初始化项目 npm init -y

    • 安装 commander:npm install commander

  2. index.js

    #!/usr/bin/env node
    
    const { program } = require('commander');
    
    program.version(require('./package.json').version)
    
    program.parse(process.argv) // 默认参数是 process.argv,所以参数可以不传
    
    

    package.json

    {
      "name": "my_cli",
      "version": "1.1.2",
      "description": "",
      "main": "index.js",
      "bin": {
        "my-cli": "index.js"
      },
      "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1"
      },
      "keywords": [],
      "author": "",
      "license": "ISC",
      "dependencies": {
        "commander": "^9.2.0"
      }
    }
    
  3. 执行 npm link

    • 将项目自定义的 bin 命令添加到全局变量中

至此就可以在终端中使用自定义的 bin 命令了,这里是 my-cli

  • my-cli --help :查看帮助
  • my-cli --version 查看版本

二、搭建一个功能完善的脚手架

2.1、前置知识

2.1.1、commander 命令

先来熟悉要用到的api

#!/usr/bin/env node
// index.js
const { program } = require('commander');

program
  // 定义cli命令
  .command('create')
  // 定义create命令的参数
  .argument('<project-name>') 
  // 添加命令的描述
  .description('clone a repository into a newly created directory')
  // create命令执行的逻辑
  .action((projectName) => {
    console.log('project-name: ', projectName)
  })

program.parse()

更多关于 commander 的用法请参考官方文档

命令行输入: my-cli --help 结果如下:

截屏2022-04-21 10.32.58.png

命令行输入: my-cli create vue-app 结果如下:

截屏2022-04-21 10.38.17.png

2.1.2、download-git-repo 下载模版

  • 安装依赖 npm install download-git-repo

因为 download-git-repo 不支持promise,所以这里使用 promisifydownload-git-repo 进行了promise封装,具体 promisify 更多使用细节可以参考官方文档

#!/usr/bin/env node
// index.js
const { program } = require('commander');
const { promisify } = require('util')
const download = promisify(require('download-git-repo'))

program
  // 定义cli命令
  .command('create')
  // 定义create命令的参数
  .argument('<project-name>')
  // 添加命令的描述
  .description('clone a repository into a newly created directory')
  // create命令执行的逻辑
  .action(async (projectName) => {
    // 1. 下载模版
    console.log('init project')
    // 远程模版地址,默认master分支,这里下载的是main分支
    const repoUrl = 'direct:https://github.com/Micah-Yu/vue-template.git#main'
    await download(repoUrl, projectName, { clone: true })
  })

program.parse()

命令行执行 my-cli create vue-app

可以看到多可一个文件夹,内容就是我们远程仓库的模版

更多关于 download-git-repo 的用法参考官方文档

Error: 'git clone' failed with status 128 git 报错这个是文件夹存在了

2.1.3、项目依赖的安装及运行

项目的依赖的安装及运行需要用用node中的 child_process 中的 spawn开一个子进程,用于执行终端命令

封装如下:

// terminal.js
/*
* 执行终端命令相关的代码
* */
const { spawn } = require('child_process')

const spawnCommand = (...args) => {
  return new Promise((resolve, reject) => {
    const childProcess = spawn(...args)
    // 将子进程所有的标准输出都传给父进程,显示在控制台上
    childProcess.stdout.pipe(process.stdout)
    // 将子进程所有的错误都传给父进程,显示在控制台上
    childProcess.stderr.pipe(process.stderr)
    childProcess.on('close', () => {
      resolve()
    })
  })
}

module.exports = {
  spawnCommand
}

更多关于 spawn 的使用细节参考官方文档

#!/usr/bin/env node
// index.js
const { program } = require('commander');
const { promisify } = require('util')
const {spawnCommand} = require("./lib/utils/terminal.js");
const download = promisify(require('download-git-repo'))

program
  // 定义cli命令
  .command('create')
  // 定义create命令的参数
  .argument('<project-name>')
  // 添加命令的描述
  .description('clone a repository into a newly created directory')
  // create命令执行的逻辑
  .action(async (projectName) => {
    // 1. 下载模版
    console.log('init project')
    // 远程模版地址,默认master分支,这里下载的是main分支
    const repoUrl = 'direct:https://github.com/Micah-Yu/vue-template.git#main'
    await download(repoUrl, projectName, { clone: true })

    // 2. 安装依赖
    console.log('installing dependents...')
  	// 这里做一个npm命令不同系统的兼容
    const command = process.platform === 'win32' ? 'npm.cmd' : 'npm'
    await spawnCommand(command, ['install'], { cwd: `./${projectName}` })

    // 3. 启动项目
    console.log('start project')
    await spawnCommand(command, ['run', 'serve'], { cwd: `./${projectName}` })
  })

program.parse()

终端执行 my-cli create vue-app

至此完成一个简单的自定义脚手架

其他类似 vue-cli 提示选择的功能,使用的是 inquirer 这个库实现的,官方文档

源码地址