本文是 Node.js 使用浅谈的三件套之一工具篇,主要讲解了 NPM 命令行接口(CLI)工具发布的教程,适合阅读的对象包括:
不熟悉 NPM 工具 和 Node 脚本
不知道如何制作通用的 CLI 工具
只会简单的发布和使用 NPM 包
温馨提示:这是一篇 2 年前写的相对老旧知识体系的旧文章,只用于新人扫盲。完整的源代码仓库地址 ziyi2/npm-cli-package。
服务篇可查看 基于 Express 应用框架的技术方案选型浅谈。
Node 脚本
在项目中我们经常会用到一些 Node 脚本命令来简化开发的工作量,例如 Element 组件库的 NPM 脚本命令 build:file (使用脚本生成 Webpack 构建的入口源文件)。
简单的 Node 脚本命令可以解决当务之急,但是如果实现的功能相对复杂,并且存在多人复用的情况(例如绑定脚手架或多人拷贝复用),可能会产生以下问题:
维护性差 (历史功能 /依赖 / 版本等维护)
污染项目文件夹
增加开发沟通成本
温馨提示:作为面试官往往能看到简历中写着开发或维护了 xxx 组件,此时会简单追问该组件如何做到多人复用,会有一些面试者回答手动拷贝…
为了解决 Node 脚本命令产生的上述问题,可以采用 NPM 发包的形式实现通用的 CLI 工具包。
NPM 包管理器
NPM 包管理器允许用户将自己编写的通用包或 CLI 工具上传到 NPM 服务器,其他开发人员只需要下载并安装该 CLI 工具到本地即可使用。
需要注意的是 NPM 包的安装可以分为本地和全局两种方式。采用本地安装可以将发布的 CLI 工具加入到项目的开发态依赖列表。采用全局方式进行安装会将 NPM CLI 工具的命令链接到全局执行环境。
温馨提示:采用本地安装更多的可能是在项目中配合 NPM 脚本使用,并且可固化工具的功能版本。采用全局安装更多的是进行类似初始化的功能,例如 vue-cli、create-react-app、express-generator 等,此时 Node 脚本不会植入到项目中,而是存放在操作系统的用户文件夹中。
大家可能常见的 CLI 工具如下:
npm/cli
babel/cli
webpack/webpack-cli
vuejs/vue-cli
commitizen/cz-cli
接下来将详细讲解如何构建、发布、安装和使用一个简单的 CLI 工具。
CLI 工具
构建
首先新建 NPM CLI 工具的项目文件,执行 npm init 命令创建 package.json 描述文件,需要输入项目名称、版本、描述、项目入口文件(发布 CLI 工具并不需要 main 字段信息,该信息主要用于发布库包)、作者信息等:
AppledeMacBook-Pro:npm-cli-package ziyi2$ npm init
This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sensible defaults.
See npm help json
for definitive documentation on these fields
and exactly what they do.
Use npm install <pkg>
afterwards to install a package and
save it as a dependency in the package.json file.
Press ^C at any time to quit.
package name: (npm-cli-package)
version: (1.0.0)
description: NPM CLI 工具的发布和使用
entry point: (index.js)
test command:
git repository: (https://github.com/ziyi2/npm-cli-package.git)
keywords: npm cli
author: ziyi2
license: (ISC)
About to write to /Users/ziyi2/Git/npm-cli-package/package.json:
{
“name”: “npm-cli-package”,
“version”: “1.0.0”,
“description”: “NPM CLI 工具的发布和使用”,
“main”: “index.js”,
“scripts”: {
“test”: “echo “Error: no test specified” && exit 1”
},
“repository”: {
“type”: “git”,
“url”: “git+https://github.com/ziyi2/npm-cli-package.git”
},
“keywords”: [
“npm”,
“cli”
],
“author”: “ziyi2”,
“license”: “ISC”,
“bugs”: {
“url”: “https://github.com/ziyi2/npm-cli-package/issues”
},
“homepage”: “https://github.com/ziyi2/npm-cli-package#readme”
}
Is this OK? (yes)
复制代码
温馨提示: NPM 本身就是一个 CLI 工具。
输入信息后生成的 package.json 描述文件如下:
{
“name”: “npm-cli-package”,
“version”: “1.0.0”,
“description”: “NPM CLI 工具的发布和使用”,
“main”: “index.js”,
“scripts”: {
“test”: “echo “Error: no test specified” && exit 1”
},
“repository”: {
“type”: “git”,
“url”: “git+https://github.com/ziyi2/npm-cli-package.git”
},
“keywords”: [
“npm”,
“cli”
],
“author”: “ziyi2”,
“license”: “ISC”,
“bugs”: {
“url”: “https://github.com/ziyi2/npm-cli-package/issues”
},
“homepage”: “https://github.com/ziyi2/npm-cli-package#readme”
}
复制代码其次需要配置 PATH 路径的可执行文件,在 package.json 里配置 bin 属性,该属性对应的是可执行文件的路径。例如将 bin 对应的可执行文件路径配置为当前项目下的 src/index.js:
“bin”: {
// npm-cli-package 是一个可执行的命令,该命令指向了 src/index.js 脚本
// 这里暂时还不清楚该脚本的环境解释器
“npm-cli-package”: “src/index.js”
},
复制代码
温馨提示:当执行 npm link 或者安装 CLI 工具时,NPM 会为 bin 配置的文件创建一个软链接。 对于 Windows 系统,全局安装默认会在 C:\Users{username}\AppData\Roaming\npm 目录下,局部安装则会在项目的 ./node_modules/.bin 目录下。
配置 bin 属性的模块路径后,可以开始设计可执行文件。为了使入口文件使用 Node 作为解释程序,需要在文件头部写入 #! /usr/bin/env node,目的是使用 env 来寻找操作系统中的 Node 启动路径,并将 Node 作为可执行文件的环境解释器。例如在 src/index.js 入口文件写入一个打印信息的 Node 脚本:
#! /usr/bin/env node
console.info(‘npm-cli-package:’, ‘1.0.0’)
复制代码
温馨提示:在 env 中包含了一些环境变量,包括我们安装的一些软件执行路径等,因此可以使用 env 来找到不同操作系统上的 Node 执行路径,从而让文件可被正常的解释和执行。
软链接
CLI 工具设计完成后,接下来是测试它能否被正常使用,此时可以通过 npm link 命令将其链接到全局执行环境,从而在系统的任意路径下可以使用该 CLI 工具。执行 npm link:
// Windows
PS C:\Users\ziyi2\Desktop\npm-cli-package> npm link
up to date in 0.428s
C:\Users\ziyi2\AppData\Roaming\npm\npm-cli-package -> C:\Users\ziyi2\AppData\Roaming\npm\node_modules\npm-cli-package\src\index.js
C:\Users\ziyi2\AppData\Roaming\npm\node_modules\npm-cli-package -> C:\Users\ziyi2\Desktop\npm-cli-package
// MAC OS X
AppledeMacBook-Pro:npm-cli-package ziyi2$ npm link
npm notice created a lockfile as package-lock.json. You should commit this file.
up to date in 4.113s
found 0 vulnerabilities
/usr/local/bin/npm-cli-package -> /usr/local/lib/node_modules/npm-cli-package/src/index.js
/usr/local/lib/node_modules/npm-cli-package -> /Users/ziyi2/Git/npm-cli-package
复制代码当执行 npm link 后,可以看到在 Mac 下该命令主要做了两件事:
为可执行文件 src/index.js 创建一个软链接,将其链接到 /usr/local/bin/( Windows 下是 C:\Users{username}\AppData\Roaming\npm<package> )
为当前项目创建一个软链接,将其链接到 /usr/local/lib/node_modules/( Windows 下是C:\Users{username}\AppData\Roaming\npm\node_modules<package> )
因此在全局环境执行 bin 配置的命令时,会启用 Node 去执行对应的可执行文件。
温馨提示:如果 bin 不配置执行的命令名称,默认将使用pageage.json 中的 字段作为命令。
此时在任意位置执行 npm-cli-package 命令:
AppledeMacBook-Pro:npm-cli-package ziyi2$ npm-cli-package
npm-cli-package: 1.0.0
复制代码可以发现在当前项目外的任意路径都可以使用该命令成功打印信息,说明 Node 解释器和软链接都设置成功。
温馨提示:Windows 系统可以在用户目录 C:\Users{username}\AppData\Roaming\npm\node_modules 下查看 npm-cli-package 包的软链接,并且可以在 C:\Users{username}\AppData\Roaming\npm 中找到 npm-cli-package (Shell)和 npm-cli-package.cmd (Cmd)两个可执行文件。
发布
通过 npm link 以及命令行的使用测试,发现工具的设计没有任何问题,此时想将该其分享给他人使用,此时可以利用 NPM 包管理器的发布机制。在发布工具之前,需要在 NPM 官网 注册账号。注册成功后,在命令终端中使用 npm login 链接你注册的账号( npm login 会将账号登录的证书信息保存在本地电脑,从而不需要再次登录账号),同时会在 NPM 的网站中生成你当前登录的 token 信息,登录后可以通过 npm whoami 命令查看当前登录账号名。
温馨提示:登录的时候不要使用 NPM 淘宝镜像地址,需要使用 NPM 官方地址,可以通过 npm config set registry https://registry.npmjs.org/ 命令设置成 NPM 官方的包发布地址。
AppledeMacBook-Pro:~ ziyi2$ npm login
Username: ziyi222
Password:
Email: (this IS public) 673191402@qq.com
Logged in as ziyi222 on https://registry.npmjs.org/.
复制代码npm login 后会在 NPM 官网产生 token 信息,接下来使用 npm publish 命令发布 CLI 工具:
AppledeMacBook-Pro:npm-cli-package ziyi2$ npm-cli-package
npm-cli-package: 1.0.0
AppledeMacBook-Pro:npm-cli-package ziyi2$ npm publish
npm notice
npm notice 📦 npm-cli-package@1.0.0
npm notice === Tarball Contents ===
npm notice 595B package.json
npm notice 12.1kB README.md
npm notice 64B src/index.js
npm notice === Tarball Details ===
npm notice name: npm-cli-package
npm notice version: 1.0.0
npm notice package size: 5.1 kB
npm notice unpacked size: 12.8 kB
npm notice shasum: 624e3e45667da53d474418907d3250336b56208b
npm notice integrity: sha512-b77RbfmmF+Gjf[…]MSbp9PL4UUE9w==
npm notice total files: 3
npm notice
- npm-cli-package@1.0.0
复制代码此时查看 NPM 官网中的个人账号信息,可以发现发布了该工具的 1.0.0 版本。如果需要发布 Scope 包,需要在 NPM 官网中创建一个组织(例如这里将 ziyi222 的账号作为组织,然后将账号 ziyi222 重命名成 ziyi222222,并重新登录账号):
AppledeMacBook-Pro:npm-cli-package ziyi2$ npm login
Username: ziyi222222
Password:
Email: (this IS public) 673191402@qq.com
Logged in as ziyi222222 on https://registry.npmjs.org/.
复制代码重新修改 package.json 配置文件:
{
“name”: “@ziyi222/npm-cli-package”,
// …
“publishConfig”: {
“access”: “public”
}
}
复制代码使用 npm publish 发布:
AppledeMacBook-Pro:npm-cli-package ziyi2$ npm publish
npm notice
npm notice 📦 @ziyi222/npm-cli-package@1.0.1
npm notice === Tarball Contents ===
npm notice 653B package.json
npm notice 12.1kB README.md
npm notice === Tarball Details ===
npm notice name: @ziyi222/npm-cli-package
npm notice version: 1.0.1
npm notice package size: 5.1 kB
npm notice unpacked size: 12.8 kB
npm notice shasum: fac560a42c43d276c9fee17c21887bedaf34bad1
npm notice integrity: sha512-j/QScEgX+glfS[…]OxhhZcIavUhfg==
npm notice total files: 2
npm notice
- @ziyi222/npm-cli-package@1.0.1
复制代码安装和使用
开发者通过 npm install 命令对工具进行全局安装:
AppledeMacBook-Pro:npm-cli-package ziyi2$ npm install npm-cli-package -g
/usr/local/bin/npm-cli-package -> /usr/local/lib/node_modules/npm-cli-package/src/index.js - npm-cli-package@1.0.0
updated 1 package in 12.977s
复制代码由安装打印信息可以发现,最终 CLI 工具脚本链接指向了 /usr/local/bin/npm-cli-package 。安装成功之后,可以在项目中使用 CLI 指定的命令了。
最后
这只是一个简单的教程示例,真正设计的 CLI 工具可能需要考虑以下一些功能:
帮助信息:用于打印支持的命令、选项参数等
版本信息:用于告知使用者当前的 CLI 版本
环境检测:用于检测当前支持的解释器(Node)版本等
交互面板:提供当前命令的可选项
信息打印:提供各种语义颜色的打印信息
…
因此你可能需要一些额外功能的依赖库,例如:
commander.js - node.js command-line interfaces made easy
chalk - Terminal string styling done right
Inquirer - A collection of common interactive command line user interfaces
node-semver - The semver parser for node (the one npm uses)
…
设计 CLI 工具的真正强大之处在于可使用 Node 支持的一切能力(例如常用的文件系统、http 服务等),从而使得开发者有各种可发挥的空间:
例如一键生成脚手架项目
例如一键生成 / 更改源码文件
例如配套各种提供开放 API 的管理平台(例如 Mock、I18N、主题包管理等)实现本地项目和平台之间的信息同步能力
例如一键生成 Webpack / Babel / Git Hook 等配置文件信息
…
除此之外,如果设计的 CLI 工具对于开发者的使用体验不够友好,也可以设计一个类似于 vue ui的图形化界面工具来增强用户的开发体验(例如之前配合同事设计了一个 lego ui工具来一键生成项目的经典布局页面)。