阅读 4565

使用 NPM 发布和使用 CLI 工具

本文是 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 工具如下:

接下来将详细讲解如何构建、发布、安装和使用一个简单的 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/<package>( Windows 下是 C:\Users\{username}\AppData\Roaming\npm\<package>
  • 为当前项目创建一个软链接,将其链接到 /usr/local/lib/node_modules/<package>( Windows 下是C:\Users\{username}\AppData\Roaming\npm\node_modules\<package> )

因此在全局环境执行 bin 配置的命令时,会启用 Node 去执行对应的可执行文件。

温馨提示:如果 bin 不配置执行的命令名称,默认将使用pageage.json 中的 <name> 字段作为命令。

此时在任意位置执行 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工具来一键生成项目的经典布局页面)。

招聘小广告(电线杆不孕不育系列)

Hi,大家好,我们是阿里巴巴新成立的 BU 政务钉钉事业部(杭州同学在用的健康码是我 BU 联合其他 BU 共同设计的一个项目),目前还有大量的 Web 前端职位空缺。希望想找我内推或者想了解更多招聘信息的同学可以加我微信:18768107826