必知必会的Node-CLI开发基础

本文带你了解创建一个Node-CLI工具所需知识点。

一、命令行参数解析

  在NodeJS中可以通过以下代码获取命令行中传递的参数:

  process.argv.slice(2)

  但是这对于构建一个CLI工具远远不够,首先需要考虑参数输入的各种风格:

  • Unix参数风格:前面加-,不过后面跟的是单个字符,例如-abc解析为[‘a’, ‘b’, ‘c’]。
  • GNU参数风格:前面加–,例如npm中的命令,npm --save-dev webpack。
  • BSD参数风格:前面不加修饰符。

  这里可以通过正则表达式对process.argv进行加工:

/**
 * 解析Unix、BSD和GNU参数风格
 * @param {Array} argv 命令行参数数组
 * @returns
 */
function parseArgv (argv) {
  const max = argv.length
  const result = {
    _: []
  }
  for (let i = 0; i < max; i++) {
    const arg = argv[i]
    const next = argv[i + 1]
    if (/^--.+/.test(arg)) {
      // GNU风格
      const key = arg.match(/^--(.+)/)[1]
      if (next != null && !/^-.+/.test(next)) {
        result[key] = next
        i++
      } else {
        result[key] = true
      }
    } else if (/^-[^-]+/.test(arg)) {
      // Unix风格
      const items = arg.match(/^-([^-]+)/)[1].split('')
      for (let j = 0, max = items.length; j < max; j++) {
        const item = items[j]
        // 非字母不解析
        if (!/[a-zA-Z]/.test(item)) {
          continue
        }
        if (next != null && !/^-.+/.test(next) && j === max - 1) {
          result[item] = next
          i++
        } else {
          result[item] = true
        }
      }
    } else {
      // BSD风格
      result._.push(arg)
    }
  }
  return result
}

  通过以上的方法可以得到如下结果:

  node example1.js --save-dev -age 20 some
  // => 结果
  {
    _: ['some'],
    'save-dev': true,
    a: true,
    g: true,
    e: 20
  }

  上面这个示例不仅仅为了展示解析的结果,而且还强调了Unix参数风格只解析单个字母,所以这种风格的参数可能表达的意思不太明确并且数量有限,那么就需要在正确的场景中使用这种风格的参数:

  npm --save-dev webpack
  npm -D webpack

  npm中采用Unix参数风格表示简写,这就是一种很恰当的方式,那么前面示例中的-age按照语义应该改为–age更加合理一点。

二、命令行界面

  NodeJS中的readline模块提供question和prompt方法构建命令行界面,下面是一个简单的问答式的交互界面:

const readline = require('readline');
const question = ['请输入您的姓名', '请输入您的年龄']
const result = []
const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout,
  prompt: `?${question[0]} `
});
rl.prompt();

rl.on('line', (line) => {
  result.push(line.trim())
  const max = result.length
  if (max === question.length) {
    rl.close()
  }
  rl.setPrompt(`?${question[max]} `)
  rl.prompt();
}).on('close', () => {
  console.log(`谢谢参与问答 *** 姓名: ${result[0]} 年龄: ${result[1]}`);
  process.exit(0);
}); 

必知必会的Node-CLI开发基础_第1张图片
  当然交互界面的元素并不只有这一种,在使用各类CLI工具时,你应该会遇到诸如:单项选择、下载进度条…

  下面可以尝试实现一个单项选择交互界面:

const readline = require('readline')
let selected = 0
const choices = ['javascript', 'css', 'html']
let lineCount = 0
const rl = readline.createInterface(process.stdin, process.stdout)
function reader () {
  let str = ''
  for (let i = 0; i < choices.length; i++) {
    lineCount++
    str += `${selected === i ? '[X]' : '[ ]'} ${choices[i]}\r\n`
  }
  process.stdout.write(str)
}

reader()

process.stdin.on('keypress', (s, key) => {
  const name = key.name
  const max = choices.length
  if (name === 'up' && selected > 0) {
    selected--
  } else if (name === 'down' && selected < max - 1) {
    selected++
  } else if (name === 'down' && selected === max - 1) {
    selected = 0
  } else if (name === 'up' && selected === 0) {
    selected = max - 1
  } else {
    return true
  }
  // 移动光标至起始位置,确保后续输入覆盖当前内容
  readline.moveCursor(process.stdout, 0, -lineCount)
  lineCount -= choices.length
  reader()
})

rl.on('line', () => {
  console.log(`you choose ${choices[selected]}`)
  process.exit(0)
}).on('close', () => {
  rl.close()
})

必知必会的Node-CLI开发基础_第2张图片

三、定制样式

  为了有效的区别命令行界面中信息的差异性,我们可以为这里输出信息添加适当的样式。

  这里介绍一下字符串添加样式的语法:

  \x1b[背景颜色编号;字体颜色编号m

  每条样式都要以\x1b[开头:

  // \x1b[0m 清除样式
  process.stdout.write('\x1b[44;37m OK \x1b[0m just do it\n')

在这里插入图片描述

四、自定义Node命令

  接下来就是自定义Node命令,首先需要创建一个命令执行的文件:

  // hello.js 首行需要指定脚本的解释程序
  #!/usr/bin/env node
  console.log('hello')

  再利用package.json中的bin配置:

  {
    "bin": {
      "hello": "./hello.js"
    },
  }

  执行npm的link命令:

  npm link
  
  # 输入自定义命令
  hello

  # 输出 hello

五、总结

  上面介绍了开发Node-CLI时所需要的一些基本知识,但是对于用过诸如webpack-cli、vue-cli工具的你可能会发现这些优秀的CLI工具还具有:

  • git风格的子命令;
  • 自动化的帮助信息;

  那么下面这些成熟的框架会给你很大的帮助:

  • commander.js 让CLI开发更简单
  • Yargs 解析命令行参数以及生成优雅的用户界面
  • chalk.js 终端文字样式库
  • lnquirer.js 命令行交互信息收集库

  如果本文对您有帮助,欢迎关注微信公众号,为您推送更多大前端相关的内容, 欢迎留言讨论,ε=ε=ε=┏(゜ロ゜;)┛。

  您还可以在这些地方找到我:

  • 一个前端开发者的LeetCode刷题之旅
  • 掘金

你可能感兴趣的:(JavaScript)