npm-scripts

Copy and tran from npm-scriptsopen in new window

描述

npm支持package.json文件里的scripts属性的以下脚本:

  • prepublish: 在包(package)打包发布之前运行,也在本地不带任何参数执行npm install时运行。(见下方)
  • prepare: 在包打包发布之前运行,也在本地不带任何参数执行npm install时运行,以及在安装git依赖时运行(见下方)。prepare会在prepublish之后运行,但是在prepublishOnly之前运行。
  • prepublishOnly: 在包打包发布之前运行,仅在npm publish时。(见下方)
  • prepack: 在原始码(tarball)被打包(在npm packnpm publish以及安装git依赖时)之前运行。
  • postpack: 在原始码(tarball)被生成和移动到最终的目的地之后运行。
  • publishpostpublish: 在包被发布之后运行。
  • preinstall: 在包安装依赖之前运行。
  • installpostinstall: 在包安装依赖之后运行。
  • preuninstalluninstall: 在包卸载依赖之前运行。
  • postuninstall: 在包安装依赖之后运行。
  • preversion: 升级包的版本之前运行。
  • version: 升级包的版本之后运行,但是在提交之前。
  • postversion: 在升级包的版本之后运行,但是在提交之后。
  • pretesttestposttest: 分别在npm test之前、之时、之后运行。
  • prestopstoppoststop: 分别在npm stop之前、之时、之后运行。
  • prestartstartpoststart: 分别在npm start之前、之时、之后运行。
  • prerestartrestartpostrestart: 分别在npm restart之前、之时、之后运行。注意,若是未提供restart脚本,npm restart将运行stop脚本和start脚本。
  • preshrinkwrapshrinkwrappostshrinkwrap: 分别在npm shrinkwrap之前、之时、之后运行。

此外,可以通过运行npm run-script <stage>来执行任意脚本。匹配名称的prepost命令也会运行(比如,premyscriptmyscriptpostmyscript)。依赖里的脚本可以通过npm explore <pkg> -- npm run <stage>来运行。

prepublish 和 prepare

废弃说明

自从npm@1.1.71以来,npm的 CLI 会同时为npm publishnpm install运行prepublish脚本,因为这是一个便利的方式以在使用包之前做一些准备工作(一些常用的使用示例将在之后的部分介绍)。但在实践中,这会特别令人困惑。在npm@4.0.0,一个新的事件prepare被引入(来替代prepublish),且之前已存在的行为(即prepublish脚本)也被保留了。一个仅在npm publish时运行(比如,最后一次运行测试以确保其处于良好的状态)的新事件prepublishOnly作为过渡性策略被添加,以允许用户避开已存在的npm版本的困惑行为。

参见https://github.com/npm/npm/issues/10074open in new window可了解关于这个改变的更深入原因。

使用示例

若是你需要在包被使用之前执行一些不依赖于操作系统或者目标系统体系结构的操作,则可以使用prepublish脚本。比如如下一些任务:

  • 将 CoffeeScript 源码编译为 JavaScript。
  • 创建 JavaScript 源码的压缩版。
  • 抓取你的包将使用到的远程资源。

prepublish阶段做这些事情的优势是可以在单一地方一次完成,以减少复杂性和可变性。此外,这意味着:

  • 你可以将coffee-script作为开发依赖devDependency,因此你的用户不需要已经安装coffee-script
  • 你不需要在你的包里包含压缩器minifier
  • 你不需要依赖你的用户在目标机器上有curlwget或其他系统工具。

默认值

npm会基于包的内容设置一些脚本的默认值。

  • start: node server.js。若是在包的根目录下有server.js文件,则npm默认会将start命令设置为node server.js
  • install: node-gyp rebuild。若是在包的根目录下有binding.gyp文件,且你没有定义自己的installpreinstall脚本,则npm默认会将install命令设置为node-gyp rebuild

用户

若是npmroot权限调用,则它会将uid改为用户账号或者用户配置里指定的uid,默认为nobody。设置unsafe-perm标志以使用root权限运行脚本。

环境

包的脚本运行的环境里,有大量关于npm设置和进程的当前状态的信息可以使用。

path

若你依赖了定义了可执行脚本的模块,比如测试套件,则这些可执行的脚本会被加入到PATH以执行这些脚本。因此,若是你的package.json是这样:

{
    "name" : "foo",
    "dependencies": {
        "bar" : "0.1.x"
    },
    "scripts": {
        "start" : "bar ./test"
    }
}
1
2
3
4
5
6
7
8
9

则你可以运行npm start去执行bar脚本,bar脚本在执行npm install时就输出到了node_modules/.bin目录里。

package.json 变量

package.json文件里的各个属性都以npm_package_前缀的显示存在在环境里。因此,若是你package.json文件里有{"name": "foo", "version": "1.2.5"},则你的包的脚本里的npm_package_name环境变量的值则为foonpm_package_version环境变量的值则为1.2.5。你可以在代码里使用process.env.npm_package_nameprocess.env.npm_package_version访问这些变量,package.json文件里的其他属性也是这样。

配置

配置参数是以npm_config_为前缀放入环境里的。比如,你可以通过检查npm_config_root环境变量来查看有效的root配置。

特殊: package.json 里的 config 对象

若是存在<name>[@<version>]:<key>形式的配置参数,则在环境里,package.json文件里的config对象会被覆盖。比如,若是package.json是这样:

{
    "name": "foo",
    "config": {
        "port": "8080"
    },
    "scripts": {
        "start": "node server.js"
    }
}
1
2
3
4
5
6
7
8
9

server.js是这样的:

http.createServer(...).listen(process.env.npm_package_config_port)
1

则用户可以通过以下方式改变这个行为:

npm config set foo:port 80
1

生命周期事件

最后,npm_lifecycle_event环境变量会被设置为正在执行的周期阶段。因此,你可以将单个脚本用于进程的不同阶段,这将基于当前阶段进行切换。

对象将以这种方式打平,因此你的package.json里要是有{"scripts": {"install": "foo.js"}},则你将在脚本里得到:

process.env.npm_package_scripts_install === "foo.js"
1

示例

比如,若是你的package.json包含如下内容:

{
    "scripts": {
        "install": "scripts/install.js",
        "postinstall": "scripts/install.js",
        "uninstall": "scripts/uninstall.js"
    }
}
1
2
3
4
5
6
7

scripts/install.js将在生命周期的installpost-install阶段调用,scripts/uninstall.js将在包uninstall的时候调用。由于scripts/install.js在两个不同的阶段运行,因此可通过npm_lifecycle_event环境变量来判断处于哪个阶段。

若是你想执行make命令,你可以这样做,这同时可以工作:

{
    "scripts": {
        "preinstall" : "./configure",
        "install" : "make && make install",
        "test" : "make test"
    }
}
1
2
3
4
5
6
7

退出

脚本是将整行作为脚本参数传递给sh命令来运行的。

若是脚本以非 0 的code退出,则将停止进程。

需要注意的是,这些脚本文件不一定必须是nodejs甚至是 JavaScript 程序。它们只要是某种可执行的文件即可。

钩子脚本

若是你想要为所有的包在指定生命周期事件时运行指定的脚本,则你可以使用钩子脚本。将可执行文件放置在node_modules/.hooks/{eventname},则针对任何安装在那个根目录下的包,当这些包经历了该包生命周期里的该阶段时,这个脚本都会执行。

钩子脚本的运行与package.json里的脚本完全一样。但它们是在分离的子进程里运行,都有上述描述的环境。

最佳实践

  • 不要以非 0 的错误码退出,除非你真的了解它。除了uninstall脚本,这将引起npm行为的失败,以及存在被回滚的可能。若是这种失败是微小的或只是阻止了一些可选的特性,则更好的方式是仅仅打印一个警告,并成功退出。
  • 不要尝试使用脚本去做npm能帮你做的事情。读一下package.jsonopen in new window,看看所有通过简单、适当地描述你的包就能指定和开启的事情。通常,这样会更加方便和可靠。
  • 检查环境以确定将文件放置在何处。比如,若是npm_config_binroot环境变量设置为/home/user/bin,则不要试图往/usr/local/bin里安装可执行文件。用户如此设置可能是有理由的。
  • 不要以sudo前缀执行你的脚本命令。若是因为一些原因需要root权限,且因为没有root权限而失败,则用户将根据提示切换权限来执行npm命令。
  • 不要使用install脚本。使用一个.gyp文件来编译,其他任何情况都使用prepublish。你几乎应该从来不要明确地设置preinstallinstall脚本。若是你设置了,请考虑下是否有其他的选择。唯一合理地使用installpreinstall脚本,是为了要在目标体系结构里必须要完成某项编译的情况下。

补充知识

Reference: 阮一峰 - npm scripts 使用指南open in new window

npm run

不带参数执行npm run命令,可以查看当前项目的所有npm脚本命令:

➜  blog git:(master) npm run
Lifecycle scripts included in blog:
  start
    npm run dev

available via `npm run-script`:
  dev
    vuepress dev docs
  build
    vuepress build docs
  deploy
    sh deploy.sh
  eslint
    sh eslint.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14

npm run env

你可以运行npm run env(类 Unix 下)或npm run env-windows(Windows 下)查看所有的环境变量。

npm scripts 参数

方式一

{
    "scripts": {
        "build": "node version.js -v ${V}"
    }
}
1
2
3
4
5
# 执行此行命令,实际执行的是 node version.js -v 123456
V=123456 npm run build
1
2

详见: npm run 执行package.json中的scripts配置时如何参数传递? - 草依山的回答 - 知乎open in new window