Nodejs
的出现让前端具备了服务端的能力,还有一个特别重要的用途是用来开发很多提升效率的 CLI
工具,比如初始化项目的 脚手架
,项目打包运行的工具库等。CLI
是 Command-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
: 参数名称。n
和name
之间除了用,
,还可以用空格
或者|
分割。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
的参数,如上例的size
和limit
,可设置默认值。 - 无拓展
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
会变成false
,value
值会变成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
) 的名字搜索入口脚本文件目录下的可执行文件来执行子命令。- 不需要
description
和action
。usage
和alise
同上。
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);