自定义脚手架学习之commander.js

1,834 阅读4分钟

Commander.js是node.js命令行界面的完整解决方案,受 Ruby Commander启发。()

commander官方文档

inquirer.js 开箱即用的交互式命令行工具

开始

  • 新建一个node工程
  • 执行npm install commander
  • package.json中新增代码
"bin": {
    "clidemo": "./index.js"
     // 版本上线后的全局命令名称
     // 比如vue-cli是"vue": "bin/vue.js",即执行vue命令执行的文件
  },
  • 根目录下新建index.js文件,该文件中需在开始行编写#! /usr/bin/env node代码,用于识别是node命令文件

api

  • 以下api基于const program = require('commander')
  • 其中大部分api都返回当前this
  • 最后都假设执行了program.parse(process.argv);通过program.parse(arguments)方法处理参数,没有被使用的选项会存放在program.args数组中。该方法的参数是可选的,默认值为process.argv。
  • <item1> [item2] 尖括号表示item1必填 中括号表示item2选填,若括号内以...结束,则表示可传入多个参数

version

program.version('1.0.0', '-v, --version'),其中第二个参数可以省略 具体代码如下

const program = require('commander')
const requiredPackageVersion = require('./package.json').version

program
    .version(requiredPackageVersion)
    .version(process.argv)

那么我们执行node index.js -V,可以看到命令行展示了1.0.0,如果加上第二个参数'-v, --version',则应该执行node index.js -v,展示结果与之前一致。改写了--version命令的简写方式。

option

  • commander的重要命令之一
  • 作用是添加命令以及命令的执行代码
  • 查看api可以看到参数
 option(flags: string, description?: string, defaultValue?: string | boolean): this;
 option<T>(flags: string, description: string, fn: (value: string, previous: T) => T, defaultValue?: T): this;
 option(flags: string, description: string, regexp: RegExp, defaultValue?: string | boolean): this;

flags表示命令的标识(必填),description为命令描述(可在--help中显示),fn为自定义命令方法,defaultValue为默认值

program
    .option('-d, --drink [item]', 'output extra debugging', function deb (v) {
        console.log('1---', arguments);
        return '123'
    })
    .option('-s, --small', 'small pizza size')
    .option('-p, --pizza-type <type>', 'flavour of pizza');
program.parse(process.argv);

输入命令node index.js -d 1234,虽然打印了1234,但由于deb返回了123,最终我们使用的时候会使用123

command、action

  • command用于配置命令,第一个参数可以配置命令名称及命令参数,参数支持必选(尖括号表示)、可选(方括号表示)及变长参数(点号表示,如果使用,只能是最后一个参数)
  • action为执行命令的方法
program
    .command('clone <source> [destination]')
    .description('clone a repository into a newly created directory')
    .action((source, destination) => {
        console.log(source, destination);
        console.log('clone command called');
    });
program.parse(process.argv);

小demo

我们模仿vue/cli创建一个最简单的脚手架

  • 新建node项目,配置好package.json的bin,假设我们配的是clidemo
  • 安装包commander(命令行界面)、inquirer(交互式命令行工具)、download-git-repo(git本地克隆代码工具)、rimraf(文件校验工具)、文件操作工具建议使用fs-extra而非原生的fs,不过我们暂时先不用

新建index.js文件

#! /usr/bin/env node
const program = require('commander')
const { version } = require('./package.json')
const create = require('./utils/create')

program
    .version(`scaffold ${version}`)
    .usage('<command> [options]')

program
    .command('create <app-name>')
    .description('创建一个基于平台的新项目')
    .option('-t, --typeName <typeName>', '基于哪个项目')
    .action((name, options) => {
        console.log(name, options);
        create(name, options)
    })

program.parse(process.argv)

新建文件/utils/create

const inquirer = require('inquirer')
const download = require("download-git-repo");
const path = require("path");
const rimraf = require("rimraf");

async function create (projectName, options) {
    const resp = await inquirer.prompt([ // 问答环节
        {
            type: 'list',
            name: 'typeName',
            message: '你想使用什么项目?',
            choices: ['umi', 'vue3'],
            default: 1
        },
        {
            type: 'expand',
            name: 'test',
            message: '测试expand',
            choices: [{key: 'u', value: 'umi'}, {value: 'vue3', key: 'v'}],
            default: 1
        },
        {
            type: 'checkbox',
            name: 'eslint',
            message: '你想使用的代码校验方式?',
            choices: [{value: 'aaa', checked: true}, 'eslint', 'prettier', 'typescript', {value: '123aaa', checked: true}],
        },
        {
            type: 'input',
            message: '您的手机号手机号:',
            name: 'phone',
            validate: function(val) {
                if(val.match(/\d{11}/g)) { // 校验位数
                    return true;
                }
                return "请输入11位数字";
            }
        },
        {
            type: 'confirm',
            name: 'ok',
            message: '是否确认创建项目',
            default: true
        }
    ])
    console.log(resp);
    down(resp)
}

function down (option) {
    const dir = path.join(process.cwd(), "/test"); //这里可以自定义下载的地址

    rimraf.sync(dir, {});  //在下载前需要保证路径下没有同名文件

    download(
        "https://github.com:koajs/koa#master",
         // 假设安装koa,可以填自己的项目,
         // 可以根据问答内容选择git上项目的版本
         // 公司git请在https前加 direct:
        dir,
        { clone: true },
        function (err) {
            console.log(err ? "Error" : "Success", err);
        }
    );
}

module.exports = (...args) => {
    return create(...args).catch(e => {
        console.log(e);
    })
}

我们将包发布到npm上或者上传到自己的git仓库,再全局安装sudo npm i -g clidemo(也可自己建一个空项目先局部安装),再执行clidemo create app即可创建属于自己的脚手架工程

了解完基础功能后,我们就可以慢慢完善自己的cli了😎