创建类似vue-cli、create-react-app的命令行工具

130 阅读4分钟

需要用到的几个Node.js包(每个模块使用方法自行github搜索):

  1. commander

    负责将输入到终端的参数解析为选项和命令参数;生成基础的命令选项

  2. inquirer

    命令行问答模块,自定义问题并对根据用户回答做处理

  3. chalk

    修改终端输出文字的样式

  4. ora

    在终端显示loading加载动画

  5. download-git-repo

    从 GitHub、GitLab、Bitbucket 等平台下载仓库

  6. fs

    Node.js的内置读写文件目录的模块

基本流程:

生成 package.json 文件,并添加 bin 属性;

在 bin 属性值指定的目录位置处编写命令脚本;

运行 npm link 将编写的命令行工具添加到 Node.js 全局模块目录;

终端中运行命令行工具测试;

发布到 npmjs.com;

全局安装测试。

流程记录:

1. 在 /test 测试目录下运行 npm init -y 生成package.json文件;

  1. 安装依赖
npm install commander inquirer@^8.0.0 chalk@^4.0.0 ora@^5.0.0 download-git-repo

3. package.json中添加 bin 属性

{
  "name": "pro-cli",
  "version": "1.0.0",
  "bin": {
    "pro-cli": "bin/pro-cli.js"
  },
  1. 根据bin属性,创建工具目录:

上面示例 pro-cli: 'bin/pro-cli.js 表示在终端中运行 pro-cli 命令时,执行当前路径下bin目录中的 pro-cli.js 文件;所以创建 bin 目录,在其下添加pro-cli.js文件,并在该js文件中编写脚本

  1. 在 /bin/pro-cli.js 中编写脚本:
#! /usr/bin/env node

const commander = require('commander')

commander
    .version(require('../package.json').version)
    .usage('<command> [options]')
    .command('init', '创建一个vue或react基础项目')

commander.parse(process.argv)

#! /usr/bin/env node 为固定写法;

commander使用方法参考:github.com/tj/commande…

.version 定义工具版本,从package.json文件中读取version字段值

.usage 说明该命令行工具使用方式

.command 定义子命令,这里定义了一个 init 子命令,通过 pro-cli init 执行;

读过commander使用方法后知道,这里没有使用.action 定义运行子命令要执行的动作,所以commander会在bin目录下搜索 

pro-cli-init.js

这个文件名是根据 总命令-子命令 这样的规则生成,commander会自动寻找该文件。

  1. pro-cli.js 同级目录添加 pro-cli-init.js 文件。把pro-cli.js作为命令行工具入口文件,pro-cli-init.js作为运行init子命令时执行文件。如果在该文件中添加 
console.log('pro-cli init命令'); 

这样的内容,然后运行 npm link 链接到node.js全局模块目录后,在终端中运行 pro-cli init 命令,就会执行 pro-cli-init.js 文件,打印出该语句。

pro-cli-init.js文件内容:

#! /usr/bin/env node

const inquirer = require('inquirer');
const downloadGIt = require('download-git-repo');
const ora = require('ora')
const chalk = require('chalk')
const fs = require('fs')
// const rimraf = require('rimraf');


const { projectName, frameworkList, vueVersions, gitRepos } = require('./config');
const { dirIsExist } = require('./utils')


inquirer.prompt(projectName).then(async projName => {
    dirIsExist(process.cwd() + '/' + projName.projectName, projName.projectName)
    let ans = await inquirer.prompt(frameworkList)
    let selectFrameworkType = ans.frameworkType.toLowerCase()
    if (selectFrameworkType == 'vue') {
        let selectVueVersion = await inquirer.prompt(vueVersions)
        selectVueVersion = selectVueVersion.vueVersion
        let confirmGitUrl = gitRepos.find(item => item.type == selectFrameworkType && item.version == selectVueVersion.slice(0, selectVueVersion.indexOf('.')))
        fetchGit(confirmGitUrl.url, projName.projectName)
    } else {
        let confirmGitUrl = gitRepos.find(item => item.type == selectFrameworkType)
        fetchGit(confirmGitUrl.url, projName.projectName)
    }
})

function fetchGit(gitUrl, projectName) {
    // 等待动画
    const spinner = ora('fetching template...').start()
    downloadGIt(gitUrl, process.cwd() + '/' + projectName, err => {
        if (err) {
            console.log();
            console.log(chalk.red(err));
            spinner.fail('基础模板下载失败')
            process.exit(1)
        }
        spinner.succeed(chalk.green('项目创建成功'))
        console.log();
        console.log(chalk.green(`
                    ================================================

                    cd ${projectName}

                    安装依赖:npm install / yarn install

                    运行:npm start / yarn start

                    ================================================
            `));
    })
}

从当面目录下的config.js和utils.js文件中引入了方法,config.js和utils.js文件具体为:

config.js 使用inquirer模块的问题配置

utils.js 判断目录是否存在

pro-cli-init.js 文件解释:

从 config.js 中引入了 项目名、框架类型、vue版本和预设的git配置4个问题;

从 utils.js 中引入了判断目录是否存在的方法

询问项目名(作为目录名),判断是否存在;

询问项目使用的框架(vue或者react);

如果选择了vue,询问使用2.x还是3.x版本;

根据选择的框架获取预设的框架模板所在地址;

拿到git地址后通过 download-git-repo 模块进行下载,下载过程通过 ora 模块展示loading动画,如果下载模块出错就终止运行;下载成功显示提示信息。

发布到 npmjs

发布到 npmjs.com, 需要到该网站进行用户注册,然后在编写的命令行所在目录下运行 npm publish, 会提示需要先登陆,运行 npm adduser 输入注册时的用户名和密码登陆后 npm publish 发布;可能提示没有权限,大概率是因为npmjs.com 已经有同名的包了,修改一下package.json中neme值重新publish即可。发布成功后可以全局安装该包进行测试。 上面示例全局安装就是 npm i -g pro-cli,然后运行 pro-cli 会显示帮助信息。

需要注意的是,如果npm使用了taobao镜像,需要切换回原npm镜像,然后再发布。为方便切换,可安装nrm。