lerna 使用 yargs 作为整个脚手架框架,下面就简单介绍下 yargs 的使用方法。
一、工程环境搭建
- 搭建 npm 环境
创建空目录,使用 npm init -f
完成项目的初始化。
- 设置
**bin**
字段
配置 package.json
文件,添加 bin
字段。
"bin": {
"cli-test": "index.js"
},
- 编写简易脚手架
编写简易脚手架,如下所示。先调用 yargs 的构造函数,并传入参数数组。解析完成之后,需调用 **argv**
才能完成整个 yargs 的初始化过程。
#! /usr/bin/env node
const yargs = require('yargs/yargs')
// hideBin 可以进行参数解析
const { hideBin } = require('yargs/helpers')
// arg为参数解析数组
const arg = hideBin(process.argv)
console.log('arg:', arg)
yargs(arg).argv
此时就会监听到传入的参数,并进行解析,默认情况下会展示 --help
和 --version
。
- 创建软连接
进入 node 所在的 bin
目录,创建软链接ln -s ~/OpenSource/cli-test/index.js cli-test
。
➜ which node
/usr/local/bin/node
➜ cd /usr/local/bin
➜ ln -s ~/OpenSource/test/cli-test/index.js cli-test
- 运行脚手架
yargs 默认情况下会支持 --help
和 --version
这两条命令。在终端输入cli-test --help
,运行结果如下:
二、开发简易 yargs 脚手架
下面模仿 lerna 来开发简易的 yargs 脚手架。
#! /usr/bin/env node
const yargs = require('yargs/yargs')
// hideBin可以进行参数解析
const { hideBin } = require('yargs/helpers')
const dedent = require('dedent')
const arg = hideBin(process.argv)
const cli = yargs(arg)
cli
.usage('Usage: cli-test [command] <options>') // 用法
// 设置最少需要的 command 数量
.demandCommand(1, "A command is required. Pass --help to see all avaliable commands and options")
.strict() // 严格模式:针对不能识别的指令,会给出错误提示
.alias("h", "help") // 添加别名
.alias("v", "version")
.wrap(cli.terminalWidth()) // 修改脚手架默认展示的宽度
.epilogue(dedent`
When a command fails, all logs are written to lerna-debug.log in the current working directory.
For more information, find our manual at https://github.com/lerna/lerna
`) // 结尾。可以在结尾处添加我们要展示的内容。
.options({ // 主要用于增加一个全局的选项,一旦添加这个选项,针对所有的command都会生效。可支持多个
debug: {
type: 'boolean',
alias: 'd',
describe: '启动 debug 模式'
}
})
.option('registry', { // 有别于options,只能一个个定义
type: "string",
alias: 'r',
describe: '定义全局仓库地址'
})
.group(['debug'], '开发选项:') // group 用于对命令分组,此处将debug命令划分到开发选项组中
.group(['registry'], '其他选项:')
.argv
在上述代码中,使用了 dedent
库,这个库的作用是去除多行字符串里的缩进(注:是缩进,不是空行!!!),这里借用了ES6的标签模板语法。
在终端输入 cli-test -h
,运行结果如下:
三、开发复杂 yargs 脚手架
3.1 创建 command
官方示例
在官方的 npm 介绍页中有创建 command 的示例,如下所示:
#!/usr/bin/env node
const yargs = require('yargs/yargs')
const { hideBin } = require('yargs/helpers')
yargs(hideBin(process.argv))
.command('serve [port]', 'start the server', (yargs) => {
return yargs
.positional('port', {
describe: 'port to bind on',
default: 5000
})
}, (argv) => {
if (argv.verbose) console.info(`start server on :${argv.port}`)
serve(argv.port)
})
.option('verbose', {
alias: 'v',
type: 'boolean',
description: 'Run with verbose logging'
})
.parse()
其中,
serve [port]
:表示创建的自定义命令,其中port
是自定义命令(command)相关的option;start the server
:表示自定义命令的描述(describe);(yargs) => {}
:builder 函数。builder 是指运行这个 commad 之前要做得准备工作,此处定义了一个只有serve
命令才用到的 option 参数port
;(argv) => {}
:handler 函数,该函数是执行自定义命令(command)的具体行为,此处启动了一个HTTP服务。
此处采用官方示例方式定义一条 command ,如 init。
.command('init [name]', 'Init a project.', (yargs) => {
// builder 函数,此处定义 option
return yargs
.option('name', {
type: 'string',
describe: 'Name of a project',
alias: 'n'
})
}, (argv) => {
// handler 函数, argv 是解析的参数
console.log('argv:', argv)
})
在终端输入,cli-test init -h
则会显示 init 的用法。
在终端输入 cli-test init -d -r npm -n project-xxxx
, 展示结果如下:
由此可以,定义脚手架的时候,别名不能重复,否则会存在覆盖问题。
lerna 用法
在 lerna 项目中,关于 command 则返回的是一个对象,如下所示:
因此,定义 command 还有另外一种方法,即传入对象方式。
3.2 recommendCommands 用法
recommendCommands()
将我们输入的命令到所有的 command 中去查找,若找到最近似的命令就会给我们做提示,如下所示:
3.3 fail 用法
fail()
用于处理解析命令失败时要展示的结果,如果想做个性化错误提示就可以通过 fail
来进行处理。
3.4 parse 用法
parse
会在最后帮我们进行参数解析,它会将传进去的 context
和 argv
进行合并,合并完后作为一个参数注入到当前的项目中。
由此可见,使用 parse
方法有个最大好处就是可以帮助我们往 argv 里面注入参数,不需要我们自己手动做太多逻辑,如判断argv是否存在等等。
完整代码
#! /usr/bin/env node
const yargs = require('yargs/yargs')
// hideBin可以进行参数解析
const { hideBin } = require('yargs/helpers')
const dedent = require('dedent')
const pkg = require("./package.json")
const arg = hideBin(process.argv)
const cli = yargs(arg)
const context = {
cliVersion: pkg.version
}
const argv = process.argv.slice(2)
cli
.usage('Usage: cli-test [command] <options>') // 用法
// 设置最少需要的 command 数量
.demandCommand(1, "A command is required. Pass --help to see all avaliable commands and options")
.strict() // 严格模式:针对不能识别的指令,会给出错误提示
.alias("h", "help") // 添加别名
.alias("v", "version")
.wrap(cli.terminalWidth()) // 修改脚手架默认展示的宽度
.epilogue(dedent`
When a command fails, all logs are written to lerna-debug.log in the current working directory.
For more information, find our manual at https://github.com/lerna/lerna
`) // 结尾。可以在结尾处添加我们要展示的内容。
.options({ // 主要用于增加一个全局的选项,一旦添加这个选项,针对所有的command都会生效。可支持多个
debug: {
type: 'boolean',
alias: 'd',
describe: '启动 debug 模式'
}
})
.option('registry', { // 有别于options,只能一个个定义
type: "string",
alias: 'r',
describe: '定义全局仓库地址'
})
.group(['debug'], '开发选项:') // group 用于对命令分组,此处将debug命令划分到开发选项组中
.group(['registry'], '其他选项:')
.command('init [name]', 'Init a project.', (yargs) => {
// builder 函数,此处定义 option
yargs
.option('name', {
type: 'string',
describe: 'Name of a project',
alias: 'n'
})
}, (argv) => {
// handler 函数, argv 是解析的参数
console.log('argv:', argv)
})
.command({
command: 'list',
aliases: ['ls','la','ll'],
describe : "List local packages",
builder: (yargs) => {},
handler: (argv) => { console.log(argv) }
})
.recommendCommands()
.fail((err, msg) => {
console.log('err:', err)
console.log('msg:', msg)
})
.parse(argv, context)