如何构建自己的cli(模板构建)

700 阅读3分钟

仓库地址

toscode.gitee.com/rooney10/co…

背景

工作中开发一个新项目时,如果公司开发流程较为规范的话,会先进行技术选型,用vue还是react,然后选择构建工具、UI框架、状态管理工具、路由、代码规范、提交规范等等,确定好之后开始初始化项目,各种install,各种config配置。实际情况往往是从github clone一个满足使用的项目进行开发,或者copy一个之前的项目,删删改改后在之前的基础上直接进行业务开发了。如果能够拥有我们自己的脚手架,那么开发一个新项目时,直接通过简单的初始化命令,即可完成项目的搭建,让我们把更多的精力放在业务开发上。

目标

  • 运行创建命令
  • 询问用户问题
  • 支持选择模板创建
  • 支持自定义创建
  • 根据用户的选择生成项目

相关工具库

实现

创建项目

1.新建项目目录kunkun-cli

mkdir kunkun-cli
cd kunkun-cli
npm init # 生成package.json文件

2.新建程序入口文件cli.js

touch cli.js

在package.json文件中指定入口文件为cli.js

{
  "name": "trump-cli",
  "version": "1.0.0",
  "description": "",
  "main": "cli.js",
  "bin": { // 指定入口文件
    "ikun": "cli.js"
  }, 
  "scripts": {
    "test": "echo "Error: no test specified" && exit 1"
  },
  "author": "",
  "license": "ISC"
}

编辑cli.js,首行添加#! /usr/bin/env node指定node运行环境

#! /usr/bin/env node

console.log('kunkun-cli start');

3.npm link链接到全局

库包在开发或迭代后,不适合发布到线上进行调试,npm link可以帮助我们模拟包安装后的状态,它会在系统中做一个快捷方式映射,让本地的包就好像install过一样,可以直接使用

在命令行输入ikun执行一下,就可以看到打印的kunkun-cli start了

创建脚手架启动命令

实现思路:

  1. 参照vue-cli常用的命令有create、config等
  2. 如果创建目录已存在,提示是否需要覆盖
  3. 借助commander库实现

安装依赖

npm i commander --save

创建命令

  1. 打开cli.js进行编辑
#! /usr/bin/env node 

const program = require('commander')
program
  .command('create <app-name>')
  .description('create a new project')
  .option('-f, --force', 'overwrite target directory if it exist')
  .action((name, options) => {
    console.log('name:', name, 'options:', options);
  })

program
  // 配置版本号信息
  .version(`v${require('./package.json').version}`)
  .usage('<command> [option]')
  
// 解析用户执行命令传入参数
program.parse(process.argv);
  1. 命令行输入ikun,检查命令是否创建成功
  2. 可以看到Commands下面已经有create [options] ,然后执行这个命令
  3. 成功拿到命令行输入信息

执行命令

  1. 创建lib文件夹并在文件夹下创建create.js
module.exports = async function (name, options) {
  // 验证是否正常取到值
  console.log('>>> create.js', name, options)
}
  1. 在cli.js中使用create.js
// 配置create命令
program
  .command('create <app-name>')
  .description('创建新项目')
  .option('-f, --force', '强制创建,如果目录已存在则覆盖')
  .action((name, options) => {
    // 在create.js中执行创建任务
    require('./lib/create.js')(name, options)
  })
  1. 执行ikun create my-project,此时在create.js正常打印了信息
  2. 在创建目录时,需要思考一个问题:目录是否已经存在
  3. 如果存在
    • 当{force: true}时,直接移除原来的目录,直接创建
    • 当{force: false}时,询问用户是否需要覆盖
  4. 如果不存在,直接创建
  5. 使用fs的扩展工具fs-extra,它是对fs模块的扩展,支持promise
npm install fs-extra --save
  1. 完善create.js内部实现逻辑
const path = require('path')
const fs = require('fs-extra')

module.exports = async function (name, options) {
  // 执行创建命令

  // 当前命令行选择的目录
  const cwd  = process.cwd();
  // 需要创建的目录地址
  const targetAir  = path.join(cwd, name)

  // 目录是否已经存在?
  if (fs.existsSync(targetAir)) {

    // 是否为强制创建?
    if (options.force) {
      await fs.remove(targetAir)
    } else {
      // 询问用户是否确定要覆盖
    }
  }
}

询问用户问题获取创建所需信息

询问是否覆盖已存在的目录

使用inquirer询问用户,如果覆盖的话就将已存在目录移除

// 询问用户是否确定要覆盖
let { action } = await inquirer.prompt([
  {
    name: 'action',
    type: 'list',
    message: '目标目录已经存在,请选择操作:',
    choices: [
      {
        name: '覆盖',
        value: 'overwrite'
      },
      {
        name: '返回',
        value: false
      }
    ]
  }
])
if(!action) {
  return
} else if(action === 'overwrite') {
  // 移除已存在的目录
  console.log('移除中...');
  await fs.remove(targetAir)
}

如何获取模板信息

我将模板上传到了远程仓库,github提供了接口去获取用户的项目信息:api.github.com/repos/用户名/仓…,通过axios发送请求获取信息

用户选择模板

通过问题交互获取到用户想要创建的模板name为下载模板做准备

下载远程模板

安装依赖与promise化

下载远程模板需要使用download-git-repo工具包,它是不支持 promise的,所以我们这里需要使用 使用 util 模块中的promisify 方法对其进行 promise 化

const util = require('util')
const downloadGitRepo = require('download-git-repo') // 不支持 Promise

class Generator {
  constructor (name, targetDir){
    ...

    // 对 download-git-repo 进行 promise 化改造
    this.downloadGitRepo = util.promisify(downloadGitRepo);
  }
  
  ...
}

核心下载功能

const util = require('util')
const downloadGitRepo = require('download-git-repo') // 不支持 Promise

class Generator {
  constructor (name, targetDir){
    ...

    // 对 download-git-repo 进行 promise 化改造
    this.downloadGitRepo = util.promisify(downloadGitRepo);
  }
  
  ...
   // 下载远程模板
  async download() {
    // 拼接下载地址
    const requestUrl = `kunkun-cli-template/${用户选择模板name}`
    // 调用下载方法
    this.downloadGitRepo(requestUrl, path.resolve(process.cwd(), this.targetDir)),
  }
  ...
}

发布项目

  1. 注册npm账号
  2. 执行npm adduser进行登录,报错需切换至npm镜像源registry.npmjs.com
  3. 执行npm publish进行发布,报错403可能是包名重复导致

发布成功后在npm官网就能看到已发布的npm包了,全局安装进行验证

自定义创建思路

思路

  1. 获取到用户选择的配置
  2. 开始创建
  3. 根据用户选项生成生成package.json文件
  4. 初始化git
  5. 安装依赖
  6. 生成代码