commander.js教程笔记

并非严格的教程博文,属于是个人在学习过程中的笔记心得汇总。
内容相对官方文档更通俗一些,入门新人,如果看不懂官方文档,可以大致看一看这个。有错的话请指正,标准的还得看官方文档。

1.commander.js概述

commander.js是一个工具,用来构建node的命令行程序,使得能够使用自定义指令在全局命令行运行node脚本

本来我们只能在脚本所在文件的根目录里通过node xxx.js运行脚本,通过commander构建命令行程序后,就能在任意一个目录里,比如桌面,比如用户目录,直接输入自定义的那个指令,就能直接运行脚本,更加简便。

另外还有一种简便的方式,就是打包成exe文件,直接双击就能运行,但是缺点在于适应性,就是如果想要在win、linux等多个平台使用,就得分别打包,而使用命令行形式,可以轻松跨平台使用

2.小案例演示

光说未必能想象出来,还得实际场景看一看,我这就写一个小小案例演示一下。
1.文件目录

├─bin
├─node_modules
│  └─commander
│      ├─lib
│      └─typings
└─src

除此之外还有package.json文件,上面图没有显示

2.文件内容

//index.js
console.log('演示案例');
#!/usr/bin/env node
//my-cli.js
const program=require('commander')
program
    .version('1.0.0')
    .parse(process.argv)
require('../src/index.js')
{
  "name": "cac",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "dependencies": {
    "commander": "^9.4.0"
  },
  "bin": {
    "my-cli":"./bin/my-cli.js"
  }
}

3.全局运行脚本
在这里插入图片描述

没有用node做前缀运行,没有在根目录,直接在全局cmd就直接运行了脚本,输出了结果,这就是commander的作用。

另外这个命令还能配置很多参数功能,如查看版本等,都可以自定义配置

4.实现方法
①功能写在主文件index.js里
②然后通过bin文件夹下的my-cli引入运行
③最后在package.json里配置bin字段,设置自定义命令文件路径
④在根目录使用yarn link/npm link,将软件包链接到全局

3.安装

npm安装:

npm install commander

yarn安装:

yarn add commander

4.声明program变量

commander提供了一个全局对象program

const program= require('commander');

如果要是用多种方式使用commander,可以通过Command类来创建

const {Command}=require('commander')
const program=new Command()

5.选项

选项定义

  • 使用.option()方法定义
  • 每个选项可以定义两个名字,一个短选项(-开头接单个字符),一个长选项(--开头接多个字符),逗号隔开(也可以用空格或者|
program
    .option('-a,--anli','这里是描述语句')

选项获取

  • 解析后的选项可以使用.opts()方法获取(选项同时也会传递给命令处理函数–不懂,待补充
  • 如果长选项的命名使用了多单词,比如max-length,就会自动被转化为驼峰命名法maxLength
  • 没有在命令行使用选项则选项值为undefined
program
    .option('-a,--anli','演示案例选项')
    .option('-b,--tuo-feng','转化为驼峰命名')
    .parse()
console.log('已经被解析的选项和选项值',program.opts());
console.log('可以直接通过选项名获取选项值:tuoFeng,',program.opts().anli,program.opts().tuoFeng);
PS F:\desk\cac> my-cli
已经被解析的选项和选项值 {}
可以直接通过选项名获取选项值:tuoFeng, undefined
PS F:\desk\cac> my-cli -a -b
已经被解析的选项和选项值 { anli: true, tuoFeng: true }
可以直接通过选项名获取选项值:tuoFeng, true

常用选项类型

  • 有两种,一类是boolean选项,无需配置参数,在命令行中不指定选项被定义为undefined,指定则为true
  • 另一类是带参数选项,在名字后面用尖括号声明,如--canshu <value>,在命令行不指定选项名被定义为undefined,指定选项但没有赋参数则会报错error: option '-a,--anli <value>' argument missing
  • 多个布尔值选项连写可以合并,如-a -b -c可以合并为为 -abc,也可以和一个带参数选项连写,带参数的放在最后如-abcp test
  • 带参数类型后如果没写参数,它会读取后面的选项作为参数,如 -a -b,最后a的参数值为‘-b’
program
    .option('-a,--anli <value>','带参数选项')
    .option('-b,--boolean','布尔值选项')
    .parse()
console.log('已经被解析的选项和选项值',program.opts());
PS F:\desk\cac> my-cli
已经被解析的选项和选项值 {}
PS F:\desk\cac> my-cli -a
error: option '-a,--anli <value>' argument missing
PS F:\desk\cac> my-cli -b
已经被解析的选项和选项值 { boolean: true }
PS F:\desk\cac> my-cli -b -a test
已经被解析的选项和选项值 { boolean: true, anli: 'test' }
PS F:\desk\cac> my-cli -a --boolean
已经被解析的选项和选项值 { anli: '--boolean' }

选项默认值

  • 带参数选项可以设置默认值,当在命令行没有指定选项时,自动赋默认值。
  • 指定选项但没有写参数,则还是会报错
  • 指定选项且写了参数,赋写的新参数
program
    .option('-a,--anli <value>','描述','moren')
    .parse()
console.log('已经被解析的选项和选项值',program.opts());
PS F:\desk\cac> my-cli
已经被解析的选项和选项值 { anli: 'moren' }
PS F:\desk\cac> my-cli -a
error: option '-a,--anli <value>' argument missing
PS F:\desk\cac> my-cli -a qer
已经被解析的选项和选项值 { anli: 'qer' }
PS F:\desk\cac>

取反选项
是一个以no-开头的布尔值型长选项,作用是在命令行使用时,把选项值赋false
有如下几种情况

  • 只使用原选项,赋值为true
  • 只使用取反选项,赋值为false
  • 先使用原选项,后使用取反选项,赋值为false
  • 先使用取反选项,后使用原选项,赋值为true
  • 总的来说就是赋值可覆盖,以后者为准
program
    .option('-c,--cac','描述')
    .option('--no-cac','取反选项')
    .parse()
console.log('已经被解析的选项和选项值',program.opts());
PS F:\desk\cac> my-cli
已经被解析的选项和选项值 {}
PS F:\desk\cac> my-cli -c
已经被解析的选项和选项值 { cac: true }
PS F:\desk\cac> my-cli --no-cac
已经被解析的选项和选项值 { cac: false }
PS F:\desk\cac> my-cli -c --no-cac
已经被解析的选项和选项值 { cac: false }
PS F:\desk\cac> my-cli --no-cac -c
已经被解析的选项和选项值 { cac: true }

可选参数选项

  • 参数可选,在使用选项但没给参数的情况下会变成布尔值选项
  • 且不会像带参数选项那样把后面的选项命令当做参数,会忽略破折号开头的输入参数,如果想让破折号开头的作为参数,可以使用=,如-i=-5
  • 参数使用方括号声明--icon [value]
program
    .option('-c,--cac [value]','描述')
    .parse()
console.log('已经被解析的选项和选项值',program.opts());
PS F:\desk\cac> my-cli
已经被解析的选项和选项值 {}
PS F:\desk\cac> my-cli -c
已经被解析的选项和选项值 { cac: true }
PS F:\desk\cac> my-cli -c ahh
已经被解析的选项和选项值 { cac: 'ahh' }
PS F:\desk\cac> my-cli -c -666
error: unknown option '-666'
PS F:\desk\cac> my-cli -c=-666
已经被解析的选项和选项值 { cac: '=-666' }

必填选项

  • 必填选项要么设置了默认值,要么在命令行必须输入选项,必须保证选项在解析时有赋值
  • 使用.requiredOption()设置
PS F:\desk\cac> my-cli
error: required option '-c,--cac <value>' not specified

变长参数选项

可以在命令行输入多个参数,解析后会以数组形式存储,在下一个选项之前的输入都会被当做变长参数

program
    .option('-c,--cac <value...>','描述')
    .parse()
console.log('已经被解析的选项和选项值',program.opts());
PS F:\desk\cac> my-cli -c a b c 1 2 3
已经被解析的选项和选项值 { cac: [ 'a', 'b', 'c', '1', '2', '3' ] }

版本选项

  • .version(),设置版本,自己只用填写版本号,它是有默认选项的,为-V(大写),--version
  • 使用选项会输出当前版本号
PS F:\desk\cac> my-cli -V
1.0.0

构造选项

  • 上面的选项配置都是通过.option()添加,但是还用一种方式,采用new Option()方式构建
  • 优点在于能够做更加详细的配置,用得不多,这里不多说,需要的时候再查
program
    .addOption(new Option('-e,--erhen'.hideHelp()))

自定义选项处理
自定义一个函数,放置在选项中,当传入参数时会自动处理参数并返回结果
函数有两个参数,分别为新参数和老参数

function add(value,previous){
    return [value,previous]
}
program
    .version('1.0.0')
    .option('-c,--cac <value...>','描述',add)
    .parse()
console.log('已经被解析的选项和选项值',program.opts());
PS F:\desk\cac> my-cli -c 3
已经被解析的选项和选项值 { cac: [ '3', undefined ] }
PS F:\desk\cac> my-cli -c 2 -c 3
已经被解析的选项和选项值 { cac: [ '3', [ '2', undefined ] ] }

6.命令

配置方法有两种方式

一种使用.command()配置,一种使用.addCommand()配置

实现方式也有两种,

一种为命令行绑定处理函数,在内部写一个函数来处理数据,另一种是把命令写成可执行文件,实现方法写在了后面,点击跳转

使用.command()进行配置,直接添加,直接执行

const {Command}=require('commander')
const program=new Command()
program
    .command('add <one> [two]')
    .description('描述')
    .action((one,two)=>{
        console.log('one:',one,'two:',two);
    })
    .parse()
PS F:\desk\cac> my-cli 2 3
one: 2 two: 3

当然,如果为program配置了多个命令,就要附上命令名了

const {Command}=require('commander')
const program=new Command()
program
    .command('add <one> [two]')
    .description('描述')
    .action((one,two)=>{
        console.log('one:',one,'two:',two);
    })
program
    .command('dd <on> [tw]')
    .description('描述')
    .action((on,tw)=>{
        console.log('on:',on,'tw:',tw);
    })
program
    .parse()
PS F:\desk\cac> my-cli dd 2 3
on: 2 tw: 3

使用.addCommand()进行配置,采用了函数方式,执行也是函数名加子命令,相当于打了一个包。

const {Command}=require('commander')
function addcomm(){
    const addcom=new Command('addcom')
    addcom
        .command('asd')
        .action(()=>{
            console.log('asdd');
        })
    addcom
        .command('asf')
        .action(()=>{
            console.log('asdf');
        })
    return addcom
}

program.addCommand(addcomm())
    .parse(process.argv)
PS F:\desk\cac> my-cli addcom asf
asdf

命令参数
上面命令的参数是直接在command里指定的,但是还有另一种方法,使用.argument()添加参数,command只写名称。

注意:一个argument里只能写一个参数,要想写多个参数得用arguments

使用argument

const {Command}=require('commander')
const program=new Command()
program
    .command('add')
    .argument('<one>','参数描述')
    .argument('<two>','参数描述')
    .action((one,two)=>{
        console.log('one:',one,'two:',two);
    })
program
    .parse()

使用arguments

.arguments("<one>","[two]")

和选项一样,也能用可变参数,用法一样

.argument('<dirs...>')

同样的,也有自定义参数处理,老一套

.argument('<first>', 'integer argument', myParseInt)

处理函数

  • 处理函数就是上面提到过的.action(),通过命令传递参数后,使用函数做相应的处理。
  • 除了命令传递的参数外,处理函数还自带了两个参数,一个是options解析的选项,一个是command命令本身
  • 可以跳过参数声明直接使用command,使用关键字this操作,注意不能在箭头函数用
.action((one,two,options,command)=>{
    console.log('one:',one,'two:',two);
})
.action(function(){
    console.log(this.opts());
})

独立可执行命令

> - 如果command带有**参数描述**,就代表可以使用独立的可执行文件作为处理函数 > - commander就会在脚本根目录搜索`command-subcommand`类型文件,就比如,我的指令是`my-cli`,命令的名字是`add`,他就会去找`my-cli-add`文件去处理参数
program
    .command('add [name]','xxx')

看下面的报错,我执行了指令和命令,带上了参数,他就会去根目录找相应文件,但是这时候没有这个文件,就报错了
后面建立了该文件,就成功执行,输出了‘233’

PS F:\desk\cac> my-cli add 233
node:internal/modules/cjs/loader:936
  throw err;
  ^

Error: Cannot find module 'F:\desk\cac\my-cli-add'
    at Function.Module._resolveFilename (node:internal/modules/cjs/loader:933:15)
    at Function.Module._load (node:internal/modules/cjs/loader:778:27)
    at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:77:12)
    at node:internal/main/run_main_module:17:47 {
  code: 'MODULE_NOT_FOUND',
  requireStack: []
}
PS F:\desk\cac> my-cli add 233

生命周期钩子
和vue之类的一样,commander也有生命周期,设置回调函数就可以在对应的时机执行

钩子触发时机参数
preAction/postAction本命令的处理函数执行前后thisCommand,actionCommand
preSubcommand直接子命令解析之前thisCommand,subcommand
.hook('preAction',(thisCommand, actionCommand)=>{
    console.log('生命周期函数用法');
})

补充

.parse()
作用就是解析,参数就是要解析的字符串,一般使用时参数就是用process.argv,就是用户输入参数

(这些都是比较常用的部分,有一些功能可以去官方文档看,看不懂可以留言,我要是看得懂就在这补充上去)

  • 31
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值