likes
comments
collection
share

前端脚手架:命令行解析库commander的使用教程

作者站长头像
站长
· 阅读数 56

我报名参加金石计划1期挑战——瓜分10万奖池,这是我的第11篇文章,点击查看活动详情

利用nodejs开发一个前端脚手架,首先面临的问题是如何定义并解析命令行的命令、参数、配置等信息。业界流行的两个库是yargscommander,其中commander更为强大,本文将详细介绍commander的使用。

全局配置options

获取program对象有两种方式:

// 获取commander单例 
const { program } = commander 
// 手动实例化一个commander实例 
const program = new commander.Command();
#!/usr/bin/env node

const commander = require('commander')
const pkg = require('../package.json')

const program = new commander.Command()

program.usage('<command> [options]')
program.version(pkg.version)

program.parse()

commander会默认有两个全局配置(options) --versiong -V 和 --help -h,因此你可以执行下面语句:

test-cli -V // 1.0.0

除了默认全局配置,我们还可以增加自己定义的全局配置:

// 第三个参数是默认值
program.option('-d, --debug', '是否开启调试模式', false)
// -e 后面必须要输入值,因为<>表示必填
program.option('-e, --envName <envName>', '获取环境变量')

执行test-cli -h:

前端脚手架:命令行解析库commander的使用教程

如果获取options的值呢?

// 获取所有options的值 
program.opts()

全局配置options有四种写法,如下:

serve -p 80
serve -p80
serve --port 80
serve --port=80

我们一般采用serve -p 80serve --port=80这两种写法。

命令注册

先看一个最简单的例子:

program
  .command('clone [source] [destination]')
  .description('clone a repository into a newly created directory')
  .option('-s, --separator <char>', 'separator character', ',')
  .action((source, destination, options) => {
    console.log('source', source)
    console.log('destination', destination)
    console.log('options', options)
  })

执行:

test-cli clone index.js out.js -s /

// 打印结果:
source index.js
destination out.js
options { separator: '/' }

【注意】 <>表示必填,[]表示可选

上面的那种写法把命令的参数(source, destination)和命令写在一起,其实我们可以分开写(推荐分开写):

program
  .command('split')
  .description('分割字符串')
  // 用<>表示是一个必输的字符,[]表示可输
  .argument('<string>', 'str to split')
  .argument('<array>', 'str to split')
  .option('-s, --separator <char>', 'separator character', ',')
  .action((str, array, options) => {
    console.log('str', str)
    console.log('array', array)
    console.log('options', options)
  })

假如你的命令很多,需要对命令进行分组,这样方便管理,怎么做呢?需要使用addCommand这个api:

// 定义了一个service命令
const service = new commander.Command('service')
// 在service命令对象上注册子命令start和stop
service
  .command('start [port]')
  .description('service start at port')
  .action(port => {
    console.log('service at' + port)
  })
service
  .command('stop')
  .description('stop service')
  .action(() => {
    console.log('stop service')
  })
  
program.addCommand(service)

前端脚手架:命令行解析库commander的使用教程

如何调用其他的脚手架

在你的脚手架上调用其他的第三方脚手架,实现脚手架的串行使用,可以使你的脚手架功能大大增强。

command方法有两种使用,它用一个函数重载来实现的:

command(nameAndArgs: string, opts?: CommandOptions): ReturnType<this['createCommand']>;
  
command(nameAndArgs: string, description: string, opts?: ExecutableCommandOptions): this;

当我们在command命令中传入脚手架命令和命令描述时,它的使用就有点不一样了,看下面代码:

program.command('install [name]', 'install package')

当执行命令test-cli install时,它会报错test-cli-install不存在,也就是当你执行上面命令时会帮你拼接成一个新的命令test-cli-install,这有点类似于npm init aaabbb这个命令,这个命令执行时会报'create-aaabbb@latest' is not in this registry,因为它会在aaabbb前面加上create然后去下载create-aaabbb包。

所以,如果你想再执行test-cli install不报错,可以加上第三个参数:

program
  .command('install [name]', 'install package', {
    executableFile: 'vue'
  })

当你执行test-cli install时,它实际上是执行vue这个脚手架。

所以当你执行test-cli install create test-name,就是在执行vue create test-name

这样就会让你脚手架可以利用别人的脚手架做很多事情,这个功能非常有用。

监听

对options的监听

现在我要对--debug进行监听,如果有输入--debug就执行回调函数,这个回调函数要早于命令的回调函数

program.on('option:debug', () => { 
    process.env.LOG_LEVEL = 'verbose' 
})

对command的监听

如果用户输入一个我们没有定义的命令,那么需要给用户提供一个纠错信息,那么就可以使用监听功能。

program.on('command:*', obj => {
  console.error('未知的命令:' + obj[0])
  // 获取已经注册的命令
  const availableCommands = program.commands.map(cmd => cmd.name())
  console.log('可用命令:' + availableCommands.join(','))
})

对于上面的功能除了使用监听外,还可以使用program.arguments,这是一个兜底的命令,也就是用户输入的命令我们没有定义,那么就走到program.arguments里面的逻辑。

program
  .arguments('<cmd> [options]')
  .description('未知命令')
  .action(function (cmd) {
    console.log('arguments', cmd)
  })

除了program.arguments外,其实还有一个配置能实现兜底的功能,即isDefault: true:

// command可以传一个opts选项
program
  .command('clone [source] [destination]', {
    isDefault: true
  })
  .action((source) => {
    console.log('source')
  })

当输入test-cli aaa时,因为aaa没有注册,那么就执行clone命令。

转载自:https://juejin.cn/post/7148650138053902372
评论
请登录