npm 支持众多 CLI 命令,除了 install 最常用的就是 npm run command ,npm run 是 npm run-script 的简写,其执行的命令定义来自于 npm scripts

什么是 npm scripts

npm package 通过 package.json 的 main 字段描述 package 入口,在 package 开发过程中经常有需要和 CLI 交互的工作,比如执行 eslint、运行单元测试、生成测试报告、开启 debug 模式等

npm 允许通过 package.json 的 scripts 字段描述这些 CLI 命令,然后通过 npm run command 运行。egg.js 使用脚手架初始化代码会内置如下 scripts

  1. "scripts": {
  2. "start": "egg-scripts start --daemon --title=egg-server-demo",
  3. "stop": "egg-scripts stop --title=egg-server-demo",
  4. "dev": "egg-bin dev",
  5. "debug": "egg-bin debug",
  6. "test": "npm run lint -- --fix && npm run test-local",
  7. "test-local": "egg-bin test",
  8. "cov": "egg-bin cov",
  9. "lint": "eslint .",
  10. "ci": "npm run lint && npm run cov",
  11. "autod": "autod"
  12. },

如果想开启 egg.js 本地联调,在命令行执行 npm run dev 即可,执行单元测试可以通过 npm run test 实现,这样方便 package 使用者处理各种不同任务

工作原理

在运行 npm run command 时 npm 会在当前路径的 package.json>scripts 下搜索 command,匹配到后新建一个 shell,在 shell 内执行 command 配置的命令内容,windows 操作系统会使用 cmd.exe

这样 npm scripts 可以不拘泥于 node 或 js脚本,任何 shell 可执行的内容都可以自定义在 npm scripts 中,如果机器中安装了 python也可以被 scripts 内容定义为 python PATH.py

如果希望运行的脚本接收参数,格式和 shell 要求一致,使用 --key=value 格式

  1. "start": "egg-scripts start --daemon --title=egg-server-demo"

环境变量

npm scripts 执行前会设置一些可能用到的环境变量,通过 npm run env 命令可以查看这些环境变量,比较常用的有

package.json 信息

package.json 中的所有字段都会用 npm_package_字段名 格式设置到环境变量,可以通过 npm_package_version 访问到 package 的 version 信息,如果 scripts 使用 node 脚本
package.json

  1. "scripts": {
  2. "test": "node test.js"
  3. },

test.js

  1. const assert = require('assert');
  2. const version = process.env.npm_package_version;
  3. assert(version, '1.0.0');

在 CLI 执行 npm run test 即可执行

npm config 信息

类似的 npm 相关的所有配置也会被设置到 npm_config_ 开头的环境变量里

PATH 添加 node_modules/.bin

npm scripts 执行前会把当前目录的 node_modules/.bin 目录加入到环境变量 PATH 中,执行完成后恢复,这样该目录下的所有脚本都可以不写路径直接使用

为什么要刻意添加 node_modules/.bin 目录?

很多工具型 package 写出来就是为了服务于其它 package 的开发,这些 package 会通过 .bin 目录暴露出其可执行文件,比如自动化测试工具 jest,如果没有把 .bin 目录加入到环境变量,其它 package 在 npm scripts 中使用 jest 需要

  1. "scripts": {
  2. "test": "jest"
  3. }

.bin 目录加入到 PATH 中后可以写成

  1. "scripts": {
  2. "test": "jest"
  3. }

package 生成 .bin 目录方式 https://docs.npmjs.com/files/package.json#bin jest package.json > https://github.com/facebook/jest/blob/master/packages/jest/package.json

  1. {
  2. "name": "jest",
  3. "version": "26.2.2",
  4. "main": "build/jest.js",
  5. "dependencies": {
  6. "@jest/core": "^26.2.2",
  7. "import-local": "^3.0.2",
  8. "jest-cli": "^26.2.2"
  9. },
  10. "bin": "./bin/jest.js"
  11. }

pre、post 钩子

当使用命令 npm run xxx 时,npm 会尝试执行 package.json scripts 中配置的 xxx 脚本命令,但 npm 同样会尝试在 package.json scripts 中查找是否配置了 prexxx,postxxx 脚本命令。如果都配置了,npm 会按照以下顺序执行脚本

  • npm run prexxx
  • npm run xxx
  • npm run postxxx

npm 提供一个 npm_lifecycle_event 变量,返回当前正在运行的脚本名称,可以利用这个变量,使用同一个脚本为不同的启动方式做差异化的处理

简写

npm run 是 npm run-script 简写,有几个命令因为过于常用,npm 提供了进一步简写

  • npm start -> npm run start
  • npm stop -> npm run stop
  • npm test -> npm run test
  • npm restart -> npm run stop && npm run restart && npm run start

了解了 npm scripts 后就可以写自己的命令行工具了