lerna 使用 yargs 作为整个脚手架框架,下面就简单介绍下 yargs 的使用方法。

一、工程环境搭建

  1. 搭建 npm 环境

创建空目录,使用 npm init -f 完成项目的初始化。

  1. 设置 **bin** 字段

配置 package.json 文件,添加 bin 字段。

  1. "bin": {
  2. "cli-test": "index.js"
  3. },
  1. 编写简易脚手架

编写简易脚手架,如下所示。先调用 yargs 的构造函数,并传入参数数组。解析完成之后,需调用 **argv** 才能完成整个 yargs 的初始化过程。

  1. #! /usr/bin/env node
  2. const yargs = require('yargs/yargs')
  3. // hideBin 可以进行参数解析
  4. const { hideBin } = require('yargs/helpers')
  5. // arg为参数解析数组
  6. const arg = hideBin(process.argv)
  7. console.log('arg:', arg)
  8. yargs(arg).argv

此时就会监听到传入的参数,并进行解析,默认情况下会展示 --help--version

  1. 创建软连接

进入 node 所在的 bin 目录,创建软链接ln -s ~/OpenSource/cli-test/index.js cli-test

  1. which node
  2. /usr/local/bin/node
  3. cd /usr/local/bin
  4. ln -s ~/OpenSource/test/cli-test/index.js cli-test
  1. 运行脚手架

yargs 默认情况下会支持 --help--version 这两条命令。在终端输入cli-test --help ,运行结果如下:
image.png

二、开发简易 yargs 脚手架

下面模仿 lerna 来开发简易的 yargs 脚手架。

  1. #! /usr/bin/env node
  2. const yargs = require('yargs/yargs')
  3. // hideBin可以进行参数解析
  4. const { hideBin } = require('yargs/helpers')
  5. const dedent = require('dedent')
  6. const arg = hideBin(process.argv)
  7. const cli = yargs(arg)
  8. cli
  9. .usage('Usage: cli-test [command] <options>') // 用法
  10. // 设置最少需要的 command 数量
  11. .demandCommand(1, "A command is required. Pass --help to see all avaliable commands and options")
  12. .strict() // 严格模式:针对不能识别的指令,会给出错误提示
  13. .alias("h", "help") // 添加别名
  14. .alias("v", "version")
  15. .wrap(cli.terminalWidth()) // 修改脚手架默认展示的宽度
  16. .epilogue(dedent`
  17. When a command fails, all logs are written to lerna-debug.log in the current working directory.
  18. For more information, find our manual at https://github.com/lerna/lerna
  19. `) // 结尾。可以在结尾处添加我们要展示的内容。
  20. .options({ // 主要用于增加一个全局的选项,一旦添加这个选项,针对所有的command都会生效。可支持多个
  21. debug: {
  22. type: 'boolean',
  23. alias: 'd',
  24. describe: '启动 debug 模式'
  25. }
  26. })
  27. .option('registry', { // 有别于options,只能一个个定义
  28. type: "string",
  29. alias: 'r',
  30. describe: '定义全局仓库地址'
  31. })
  32. .group(['debug'], '开发选项:') // group 用于对命令分组,此处将debug命令划分到开发选项组中
  33. .group(['registry'], '其他选项:')
  34. .argv

在上述代码中,使用了 dedent 库,这个库的作用是去除多行字符串里的缩进(注:是缩进,不是空行!!!),这里借用了ES6的标签模板语法

在终端输入 cli-test -h,运行结果如下:
image.png

三、开发复杂 yargs 脚手架

3.1 创建 command

官方示例

在官方的 npm 介绍页中有创建 command 的示例,如下所示:

  1. #!/usr/bin/env node
  2. const yargs = require('yargs/yargs')
  3. const { hideBin } = require('yargs/helpers')
  4. yargs(hideBin(process.argv))
  5. .command('serve [port]', 'start the server', (yargs) => {
  6. return yargs
  7. .positional('port', {
  8. describe: 'port to bind on',
  9. default: 5000
  10. })
  11. }, (argv) => {
  12. if (argv.verbose) console.info(`start server on :${argv.port}`)
  13. serve(argv.port)
  14. })
  15. .option('verbose', {
  16. alias: 'v',
  17. type: 'boolean',
  18. description: 'Run with verbose logging'
  19. })
  20. .parse()

其中,

  • serve [port]:表示创建的自定义命令,其中 port 是自定义命令(command)相关的option;
  • start the server :表示自定义命令的描述(describe);
  • (yargs) => {}builder 函数。builder 是指运行这个 commad 之前要做得准备工作,此处定义了一个只有 serve 命令才用到的 option 参数port;
  • (argv) => {}handler 函数,该函数是执行自定义命令(command)的具体行为,此处启动了一个HTTP服务。

此处采用官方示例方式定义一条 command ,如 init。

  1. .command('init [name]', 'Init a project.', (yargs) => {
  2. // builder 函数,此处定义 option
  3. return yargs
  4. .option('name', {
  5. type: 'string',
  6. describe: 'Name of a project',
  7. alias: 'n'
  8. })
  9. }, (argv) => {
  10. // handler 函数, argv 是解析的参数
  11. console.log('argv:', argv)
  12. })

在终端输入,cli-test init -h 则会显示 init 的用法。
image.png

在终端输入 cli-test init -d -r npm -n project-xxxx, 展示结果如下:
image.png
由此可以,定义脚手架的时候,别名不能重复,否则会存在覆盖问题。

lerna 用法

在 lerna 项目中,关于 command 则返回的是一个对象,如下所示:
image.png

因此,定义 command 还有另外一种方法,即传入对象方式。
image.png

3.2 recommendCommands 用法

recommendCommands() 将我们输入的命令到所有的 command 中去查找,若找到最近似的命令就会给我们做提示,如下所示:
image.png

3.3 fail 用法

fail() 用于处理解析命令失败时要展示的结果,如果想做个性化错误提示就可以通过 fail 来进行处理。
image.png

3.4 parse 用法

parse 会在最后帮我们进行参数解析,它会将传进去的 contextargv 进行合并,合并完后作为一个参数注入到当前的项目中。
image.png
由此可见,使用 parse 方法有个最大好处就是可以帮助我们往 argv 里面注入参数,不需要我们自己手动做太多逻辑,如判断argv是否存在等等。

完整代码

  1. #! /usr/bin/env node
  2. const yargs = require('yargs/yargs')
  3. // hideBin可以进行参数解析
  4. const { hideBin } = require('yargs/helpers')
  5. const dedent = require('dedent')
  6. const pkg = require("./package.json")
  7. const arg = hideBin(process.argv)
  8. const cli = yargs(arg)
  9. const context = {
  10. cliVersion: pkg.version
  11. }
  12. const argv = process.argv.slice(2)
  13. cli
  14. .usage('Usage: cli-test [command] <options>') // 用法
  15. // 设置最少需要的 command 数量
  16. .demandCommand(1, "A command is required. Pass --help to see all avaliable commands and options")
  17. .strict() // 严格模式:针对不能识别的指令,会给出错误提示
  18. .alias("h", "help") // 添加别名
  19. .alias("v", "version")
  20. .wrap(cli.terminalWidth()) // 修改脚手架默认展示的宽度
  21. .epilogue(dedent`
  22. When a command fails, all logs are written to lerna-debug.log in the current working directory.
  23. For more information, find our manual at https://github.com/lerna/lerna
  24. `) // 结尾。可以在结尾处添加我们要展示的内容。
  25. .options({ // 主要用于增加一个全局的选项,一旦添加这个选项,针对所有的command都会生效。可支持多个
  26. debug: {
  27. type: 'boolean',
  28. alias: 'd',
  29. describe: '启动 debug 模式'
  30. }
  31. })
  32. .option('registry', { // 有别于options,只能一个个定义
  33. type: "string",
  34. alias: 'r',
  35. describe: '定义全局仓库地址'
  36. })
  37. .group(['debug'], '开发选项:') // group 用于对命令分组,此处将debug命令划分到开发选项组中
  38. .group(['registry'], '其他选项:')
  39. .command('init [name]', 'Init a project.', (yargs) => {
  40. // builder 函数,此处定义 option
  41. yargs
  42. .option('name', {
  43. type: 'string',
  44. describe: 'Name of a project',
  45. alias: 'n'
  46. })
  47. }, (argv) => {
  48. // handler 函数, argv 是解析的参数
  49. console.log('argv:', argv)
  50. })
  51. .command({
  52. command: 'list',
  53. aliases: ['ls','la','ll'],
  54. describe : "List local packages",
  55. builder: (yargs) => {},
  56. handler: (argv) => { console.log(argv) }
  57. })
  58. .recommendCommands()
  59. .fail((err, msg) => {
  60. console.log('err:', err)
  61. console.log('msg:', msg)
  62. })
  63. .parse(argv, context)