为你的团队打造一个 Node CLI 工具吧

4,946 阅读3分钟

想一想你的团队会有一些重复性工作吗?思考一下,CLI 工具也许可以减少掉许多重复工作,统一合作规范,提升工作效率。

为什么是 Node

Node 拥有最为庞大的生态社区,数十万的 Package,其中有许多 Package 是专门为建强大的 CLI 工具打造的。在社区生态的帮助下,你可以更快速构建更强大的 CLI 工具。同时,在 NPM、Yarn 等 Package 管理工具的帮助下,你也可以快速的分发的你的 CLI 工具,让你的用户更简单的使用你的 CLI 工具。

快速开始

初始化项目

mkdir cli-tool && cd cli-tool
npm init --yes

创建 src/cli.js,写入如下内容:

// src/cli.js
module.exports = function cli() {
  // 打印命令参数
  console.log(process.argv);
};

创建 bin/cli.js 文件,这是 CLI 工具的入口文件,写入如下内容:

#!/usr/bin/env node
const cli = require("../src/cli");

cli();

重点:修改 package.json ,写入 bin 字段,添加 cli 命令

{
  "name": "cli-tool",
  "version": "1.0.0",
  "description": "",
  "main": "src/cli.js",
  "bin": {
    "cli": "bin/cli.js"
  },
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

在项目根目录下,运行:

npm link

在终端中输入 cli 命令,即可打印出如下类似内容:

[
  '/Users/xx/.nvm/versions/node/v12.16.2/bin/node',
  '/Users/xx/.nvm/versions/node/v12.16.2/bin/cli'
]

至此,一个简单的 Node CLI 工具就完成了。

基本原理

bin

NPM 实现了将 Package 中的可执行文件添加全局 PATH 中的能力,当在 package.json 文件中添加 bin 字段时,NPM 会将 bin 声明的文件链接(软链接)到 bin 目录中(全局可用),或者 ./node_modules/bin/(本地可用)。

如下面的 bin 创建 /usr/local/bin/myappcli.js 的软链接:

{ "bin" : { "myapp" : "./cli.js" } }

同时,我们要在 bin 声明的文件的开头添加 #!/usr/bin/env node 声明,确保系统会用 Node 执行此文件。

npm link

npm link 命令会创建当前 Package 到 Gloabl 的软链接,使当前 Package 在全局可用,类似虚拟的 npm i --global packae命令,在本地调试时十分有用。

实践

参数解析

CLI 工具的基本能力就是要解析用户输入的 CLI 命令与参数,实现相关功能。Node 的 process.argv 数组包含了 Node 进程被启动时传入的命令行参数,通过解析 process.argv 数组即可,社区中相关的 Package 也比较多,如:yargsminimist 等。

这里推荐使用 [commander](https://github.com/tj/commander.js) 构建你的 CLI 工具,commander 是一个较为完善的 Node CLI 解决方案,拥有解析输入参数、关联处理逻辑、输出命令提示等功能。只有当 commander 的功能无法满足你的需求时,才建议你尝试自己处理命令参数解析。

交互输入

在某些情况下,CLI 工具可能需要使用交互式输入,如输入密码时,这种情况不适合直接通过参数输入。简单的输入时,可以直接 Node 的 readline 模块:

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);
});

当需要比较复杂的交互时,如列表选择时,就需要使用社区中的 Package 了。这里推荐 enquirer,功能较为强大,如下:

发布

最后,你可以通过 NPM 将你的 Node CLI 工具快速分发出去:

# 登陆 npm
npm adduser

# 打 Tag
npm tag v1.0.0

# 发布
npm publish

推荐