(Nodejs 开发 cli 工具) - Commander

Nodejs 的出现让前端具备了服务端的能力,还有一个特别重要的用途是用来开发很多提升效率的 CLI 工具,比如初始化项目的 脚手架,项目打包运行的工具库等。CLICommand-Line Interface 的缩写,命令行界面的意思。

基于Nodejs开发CLI工具,首先就离不开 Commander 库。它是 Nodejs 进行 CLI 开发的完整解决方案。

Commander安装


npm install commander@5.0.0

注意: 本文基于 commander@5.0.0 版本。

引入

常规方式:

const program  = require('commander');

复杂程序中,Commander 可能通过多样化的方式使用,可以通过创建本地 Commander的方式使用:

// 方式A
const Command  = require('commander');
const program = new Command();

// 方式B 
// 5.0.0 版本新增
const { createCommand } = require('commander');
const program = createCommand();

版本


const program = require('commander');
// 写死版本
program.version('0.0.1');

const program = require('commander');
// 动态读取 package 的版本
program.version(require('../package').version);

可以通过 $ comd-cli -V 显示版本号。

options

option() 方法用于 定义 Commander 的参数选项 options

基本API:

program.option('-n, -name', 'description')
  • n: 参数名称的短标示(flag),用单个字符表示。
  • name: 参数名称。
  • nname之间除了用 ,,还可以用 空格 或者 | 分割。
  • description: 参数描述。

示例:

const program = require('commander');

program
  .option('-d, --debug', 'debugging')
  .option('-s, --small', 'small size')
  .parse(process.argv);
console.log(program.opts());

//执行日志如下👇
$ comd-cli
 { debug: undefined, small: undefined}
$ comd-cli -d
 { debug: true, small: undefined}
$ comd-cli -ds
 { debug: true, small: true}
  • program.opts() 获取命令所有参数信息。
  • 不带对应标示,参数的默认值是 undefined
  • 带对应标示,参数值为boolean类型 - true
  • -d -s 可以简写为 -ds
  • $comd-cli --debug 等同于 $comd-cli -d, 一般都使用更短的标示。
  • 类似 --template-engine 这种命名的名称,会被自动转为驼峰 templateEngine

后续示例都假定通过 $comd-cli 这个 CLI 执行。

boolean 类型参数

如果希望参数不是一个 boolean 类型,而是具体的 vaue。可以通过增加 <type> 或者 [type] 实现:

const program = require('commander');

program
  .option('-d, --debug ', 'if debugging')
  .option('-s, --size <type>', 'size type')
  .option('-l, --limit [type]', 'file size limit')
  .parse(process.argv);
console.log(program.opts());

//执行日志如下👇
$ comd-cli -s
 error option '-s, --size <type>' argument missing
$ comd-cli -s small
 { debug: undefined, size: 'small', limit: undefined }
$ comd-cli -l
 { debug: undefined, size: 'small', limit: true }
$ comd-cli -l 1m
 { debug: undefined, size: 'small', limit: '1m' }
  • <> 包裹的拓展参数 type必输值
  • [] 包裹的拓展参数 type非必输,默认值为 true
  • type 是由用户自主命名。

自定义默认值

如前所述,参数的默认值都为 true。你可以在 options() 方法中添加第三个参数作为默认值

const program  = require('commander');

program
  .option('-d, --debug ', 'if debugging', '')
  .option('-s, --size <type>', 'size type', 'smal')
  .option('-l, --limit [type]', 'file size limit', '1M')
  .parse(process.argv);
console.log(program.opts());

//执行日志如下👇
$ comd-cli 
 { debug: undefined, size: 'small', limit: '1M' }
$ comd-cli -s large
 { debug: undefined, size: 'large', limit: '1M' }
  • 对于带拓展 type 的参数,如上例的 sizelimit,可设置默认值。
  • 无拓展 type 的参数,如上例的 debug,设置默认值无效。
  • 默认值可以是对象,数组等。

变更默认值

可以通过 --no-name 达到修改 name 参数的默认值的效果。

const program  = require('commander');

program
  .option('-d, --debug ', 'if debugging')
  .option('-s, --size <type>', 'size type', 'small')
  .option('--no-debug', 'change debug')
  .option('--no-size', 'Remove size')
  .parse(process.argv);
console.log(program.opts());
//执行日志如下👇
$ comd-cli 
 { debug: true, size: true}
$ comd-cli -d
 { debug: false, size: true}
$ comd-cli -s big
 { debug: false, size: 'big'}
$ comd-cli --no-debug
 { debug: false, size: true}
$ comd-cli --no-size
 { debug: false, size: undefined}
  • --no-name 参数会把 name 命名的参数的默认值变为 true,不管它是 boolean 还是 value
  • 命令带 --no-name 参数执行,boolean 类型的 name 会变成 falsevalue 值会变成undefined

自定义option处理

option() 方法的第三个参数是一个函数(回调函数)的时候,该函数会自动对参数值做处理。

const program  = require('commander');

const func = (dummyValue, previous) => {
  console.log(`dummyValue: ${dummyValue} ; previous: ${previous}`);
  return dummyValue.split(',')
}

program
  .option('-d, --debug ', 'if debugging')
  .option('-s, --size [type]', 'size type',func,['1'])
  .parse(process.argv);
console.log(program.opts());

//执行日志如下👇
$ comd-cli -s 2,3,4
  dummyValue: 2,3,4 ; previous: 1
  { debug: undefined, size: [ '2', '3', '4' ] }
  • 这个时候,默认值可做为 option() 方法的第四个参数。
  • 回调函数参数:dummyValue 用户指定的值; previous: 当前值,上述示例是默认值。
  • 回调函数返回值作为 option 最新值。

必需参数

如果一个命令行必须有某个参数才可以执行,可以通过 requiredOption() 方法处理。

const { program } = require('commander');

program
  .option('-d, --debug ', 'if debugging')
  .requiredOption('-s, --size <type>', 'size type');
  .parse(process.argv);

//执行日志如下👇
$ comd-cli
  error: required option '-s, --size <type>' not specified 

参数传递

你可以使用 -- 来表示选项的结束,剩余的参数将被使用而不会被解释。这对于将选项传递给另一条命令特别有用。

const program  = require('commander');
console.log(process.argv);
program
  .option('-d, --debug ', 'if debugging')
  .option('-s, --size [type]', 'size type','small')
  .parse(process.argv);
console.log(program.opts());
console.log(program.args);

//执行日志如下👇
$ comd-cli -d -- -s big git react 
 { debug: true, size: 'small' }
 [ '-s', 'big', 'git', 'react' ]
  • 注意,-- 后面的 -s big 便不会被 option 处理。
  • 一般,我们通过 parse(process.argv) 方法把其他没有被 options 处理的参数变为 program.args 对象使用。

Commands

Commander 提供了两种用于添加命令的方式,分别对应一下两个 API:

  • command(): 通过用户自己定义命令名称,和一些命令参数来添加一个命令。
  • addCommand(): 用于添加一个已经初始化好的子命令。复杂场景下,往往会定义一个方法来生成命令,就需要使用该方式。

其实,用 addCommand() 添加的子命令,也是通过 command() 方式初始化的。

addCommand() 方式是在 5.0.0 版本才引入的。

命令的执行也有两种方式:

  • 给命令添加一个 action 事件处理程序。需要5.0.0及以上版本
  • 通过一个单独的可执行文件。

下面,我们通过以下三种情况来了解:

  • command()+ action handler
  • command()+ 可执行文件
  • addCommand() 使用示例

command()+ action handler

一定注意,5.0.0及以上版本才支持的方式:

program
  .command('clone <required> [optional]')
  .alise('c')
  .useage('<command> <fileName>')
  .desciption('clone command descption')
  .action((required, optional) => {
    console.log('command called')
  })

//执行日志如下👇
$ comd-cli clone fileName
  command called
  • clone: 命令名称。
  • required: 必须参数。
  • optional: 其他可选参数。
  • alise('c'): 给命令一个简短的别名 c
  • usage(): 命令的使用格式。使用 --help 会显示。
  • desciption(): 设置命令描述信息。
  • action(handler): 命令执行程序,handler 是一个回调函数,可以获取到命令参数。

command()+ 可执行文件

这种方式是早期版本便一直支持的方式:

// 假设该入口脚本文件目录为:example/comdCli.js
program
  .command('clone <required> [optional]','clone command descption')
  .alise('c')

//执行日志如下👇
$ comdCli clone fileA
  Error: 'comdCli-clone' does not exist
  • 命令描述作为 command() 方法的第二个参数。这个时候就会通过可执行文件执行命令。
  • Commander 会自动使用 program-subCommand(上例中是 comdCli-clone ) 的名字搜索入口脚本文件目录下的可执行文件来执行子命令。
  • 不需要 descriptionactionusagealise同上。

addCommand() 使用示例

addCommand() 只是 5.0.0 版本增加的一个用于增加一个已经初始化好的子命令到 program 的方法:

const Command  = require('commander');
const program = new Command();
program
  .command('tea')
  .action(() => {
    console.log('brew tea');
  });
function makeHeatCommand() {
  const heat = new Command();
  heat
    .command('jug')
    .action(() => {
      console.log('heat jug');
    });
  return heat;
}
// 看这里
program.addCommand(makeHeatCommand());
program.parse(process.argv);

参考资料

gitHub-commander