前言
最近封装了一个移动端上的模板,再想能不能像vue-cli一样,对项目模块的一些初始化操作、git代码提交的规范。经查询得知commanderJs主要用于node命令行的解决方法,对文档常用的属性方法进行的总结跟笔记。
正文
1.创建目录
mkdir module-cli
cd module-cli
mkdir example
npm init
npm install commander
2.api学习笔记
官方文档:点击访问
2.1选项
为简化使用,Commander 提供了一个全局对象program。
如果程序较为复杂,用户需要以多种方式来使用Commander,创建本地 Command 对象。
我们先来认识Commander的基础配置option。
创建一个option.js,执行node .\option.js -d --small -p test
const { program } = require('commander');
program
.option('-d, --debug', 'output extra debugging')
.option('-s, --small', 'small pizza size')
.option('-p, --pizza-type <type>', 'flavour of pizza');
program.parse(process.argv);
const options = program.opts();
if (options.debug) console.log(options);
console.log('pizza details:');
if (options.small) console.log('- small pizza size');
if (options.pizzaType) console.log(`- ${options.pizzaType}`);
输出结果为:
{ debug: true, small: true, pizzaType: 'test' }
pizza details:
- small pizza size
- test
首先我们来看option这个函数入参的定义:
- 简写:例如
-d、-s、-p,并且会做贪婪匹配,例如-dsp会同时匹配-d -s -p。
全称:在简写的基础上可以更语义化命名,需使用--开头,如同简写一起时,需与简写空格分开。
默认为布尔值,如命名开头为no,例 --no-debug,则默认值为true,调用时--no-debug则为false。
声明结尾增加,如果在命令行中不指定具体的选项及参数,则会被定义为undefined,例--pizza-type ,命令行不指定选项或者不增加入参,则为undefined
如声明结尾<number...>,在输入下一个选项前(-或--开头),用户输入的指令均会被视作变长参数,例.option('-n, --number <numbers...>', 'specify numbers'),调用命令时-n 1 2 3,则该值为[ '1', '2', '3' ] - 选项描述,说明该选项的具体用户,一般会在命令输出帮助时候(-h,-help)时,有所帮助
- 可接受一个正则、函数、或者默认值的入参
- 如第三参数为正则、函数,则第四位入参为默认值
以上基本满足比较简单的应用了,commander还提供复杂场景下的选项配置,例
- requiredOption函数,通过
.requiredOption()方法可以设置选项为必填。必填选项要么设有默认值,要么必须在命令行中输入,对应的属性字段在解析时必定会有赋值。该方法其余参数与.option()一致。 - 直接构造
Option对象,对选项进行更详尽的配置,通过new Option('-t, --timeout ', 'timeout in seconds')可配置选项模式,选项描述。后可通过链式调用
default:(value: unknown, description?: string): this, 命令行未输入指令时给予默认值,入参为默认值及描述,描述会在help命令中输出
preset:(arg: unknown): this,同上入参(预设值)。区别在于命令行要输入指令时才有效,如果指定不指定 [type]类型,则该指令值始终为预设值。
conflicts:(names: string | string[]): this,接受一个字符或者字符数组的冲突指令名称入参,执行该指令时,如果命令行有冲突指令名称入参,则会抛出一个error的错误。
implies:(optionValues: OptionValues): this,接受一个Record<string, any>的入参,为其他指令设置默认值。例如指令-a依赖-b指令的值,然而再命令行中并无输入-b,这时候就可以利用implies来给-b设置默认参
env:(name: string): this,接受一个字符串入参,作为环境变量的key,再PORT=9001 node options-env.js命令执行时,会去取node前PORT=9001中的值。
argParser:<T>(fn: (value: string, previous: T) => T): this,入参为一个函数,函数内接受当前value值,previous则为default方法内的默认值。
makeOptionMandatory:(mandatory?: boolean): this,入参为布尔值,用来表示该选项是否必须有值。
hideHelp:(hide?: boolean): this,入参为布尔值,用来表示该选项是否屏蔽help的输出。
choices:(values: readonly string[]): this,接收一个字符串数组,用来指定该指令可接受哪一些值,非数组内的值会抛出错误,如不指定<type>类型则默认为布尔值,会一直报错。
name:无入参,函数返回当前指令的name,长名称。
attributeName:无入参,函数返回当前指令的驼峰式name,可用于对象,长名称。例如--disable-server则返回disableServer。
isBoolean:无入参,函数返回当前是布尔的值。
2.2 命令
通过.command()或.addCommand()可以配置命令,一种是命令配置函数来进行处理,另一种:是运行一个可执行文件。
.command有两个参数,第一个参数必填 空格分隔。第一个字符串为命令名称,紧随其后的为参数名定义,<>代表参数必选的,[]代表参数可选的,<key...>[key...]...代表变长参数,将后续参数汇总成一个,只能是最后一个参数。
.addCommand有两个参数,第一个参数为Command对象,通过new Command生成,第二个参数则为可选项,对象为hidden再help隐藏,isDefault默认命令,noHelp再help隐藏
2.2.1 命令参数
上诉我们已经知道配置一个命令了,现在我们设置命令的参数,一共有四种方式。
.command方法入参时候,再命令名后空格设置,例.command('init <a>[b][c...]'),对于执行可执行文件时,只能通过这种方法来设置参数。.argument方法接收两个入参,第一个为参数名称,同<>[]...规则,第二个则为参数描述。.arguments方法接受一个入参,为参数名称字符串列表,通过空格分隔,同<>[]...规则,无参数描述。addArgument方法接收一个Argument对象入参,通过new commander.Argument('<drink-size>', 'drink cup size')来传递,具体Argument的方法可参考Options对象的链式调用。
default:(value: unknown, description?: string): this, 命令行未输入指令时给予默认值,入参为默认值及描述,描述会在help命令中输出
argParser:<T>(fn: (value: string, previous: T) => T): this,入参为一个函数,函数内接受当前value值,previous则为default方法内的默认值。
choices:(values: readonly string[]): this,接收一个字符串数组,用来指定该指令可接受哪一些值,非数组内的值会抛出错误,如不指定<type>类型则默认为布尔值,会一直报错。
argRequired(): this,使参数必传。
argOptional(): this,使参数可选。
2.2.2 独立可执行(子)命令
当我们想配置一个外部可执行文件时,我们可以再.command方法增加描述,则会被视为执行外部可执行。默认是需要 当前文件名 +子命令name,例如执行的文件是node index.js,子命令name为install,则会去寻找当前目录的index-install.js
1. executableFile作为.command的第三个入参,入参为ExecutableCommandOptions类型对象,可配置executableFile属性来匹配执行子命令的可执行文件地址,isDefault属性配置是否为默认子命令,hidden noHelp用来隐藏输出help信息。
2.2.3 命令的生命周期
可以在命令中设置钩子,做一些前置、后置的处理。
钩子函数支持async,相应的,需要使用.parseAsync代替.parse。一个事件上可以添加多个钩子
支持的事件有:
| 事件名称 | 触发时机 | 参数列表 |
|---|---|---|
preAction, postAction | 本命令或其子命令的处理函数执行前/后 | (thisCommand, actionCommand) |
preSubcommand | 在其直接子命令解析之前调用 | (thisCommand, subcommand) |
preAction、postAction对应的回调参数 thisCommand为当前挂载钩子的命令,actionCommand则是当前执行的命令。
preSubcommand对应的回调参数thisCommand为当前文件默认主命令,subcommand则为当前执行的子命令。
2.2.4 其他补充
alias(alias: string): this:为命令设置别名,再自动生成帮助中只会显示第一个设置的别名name(str: string): this:设置当前命令名称
2.3 自定义帮助
由于官方文档描述的很详细了,此处不做阐述。
2.4 零碎知识
.parse()和.parseasync()是解析参数、命令必须调用的方法。
第一个参数是要解析的字符串数组,也可以省略参数而使用process.argv。 如果参数遵循与 node 不同的约定,可以在第二个参数中传递from选项:
node:默认值,argv[0]是应用,argv[1]是要跑的脚本,后续为用户参数;electron:argv[1]根据 electron 应用是否打包而变化;user:来自用户的所有参数。
例如:
program.parse(process.argv); // 指明,按 node 约定
program.parse(); // 默认,自动识别 electron
program.parse(['-f', 'filename'], { from: 'user' });
- 重写退出,再调用
program.parse函数中包一层try...catch...,可以再catch中捕获CommanderError错误信息,主要是再命令exit之前拦截,再exit之前打印的错误信息不会重写。 - 自定义事件监听,再
program.on可以增加对应option:key或者command:key对应的回调事件,回调事件中使用this来获取program相关信息;例:program.on(option:verbose, function () { process.env.VERBOSE = this.opts().verbose; });
2.5 调试
-
运行ts文件,需使用
ts-node,执行命令为node -r ts-node/register pm.ts。