脚本
npm 如何处理 "scripts" 字段描述
你的 package.json
文件的 "scripts"
属性支持许多内置脚本及其预设的生命周期事件以及任意脚本。 这些都可以通过运行 npm run-script <stage>
或简称 npm run <stage>
来执行。 具有匹配名称的前置和后置命令也将为这些运行(例如 premyscript
、myscript
、postmyscript
)。 来自依赖的脚本可以使用 npm explore <pkg> -- npm run <stage>
运行。
前后脚本
要为 package.json
的 "scripts"
部分中定义的任何脚本创建 "pre" 或 "post" 脚本,只需创建另一个具有匹配名称的脚本并将 "pre" 或 "post" 添加到它们的开头。
{"scripts": {"precompress": "{{ executes BEFORE the `compress` script }}","compress": "{{ run command to compress files }}","postcompress": "{{ executes AFTER `compress` script }}"}}
生命周期脚本
有一些特殊的生命周期脚本只在某些情况下发生。 这些脚本发生在 "pre" 和 "post" 脚本之外。
prepare
、prepublish
、prepublishOnly
、prepack
、postpack
prepare(自 npm@4.0.0
)
- 在打包之前运行
- 在包发布之前运行
- 在本地
npm install
上运行,不带任何参数 - 在
prepublish
之后但在prepublishOnly
之前运行 - 注意: 如果通过 git 安装的包包含
prepare
脚本,则在打包和安装包之前,将安装其dependencies
和devDependencies
,并运行准备脚本。
prepublish(弃用)
- 同
prepare
prepublishOnly
- 在封装准备和封装之前运行,仅在
npm publish
上运行。
prepack
- 在打包 tarball 之前运行(在 "
npm pack
"、"npm publish
" 和安装 git 依赖时)。 - 注意: "
npm run pack
" 与 "npm pack
" 不同。 "npm run pack
" 是任意用户定义的脚本名称,其中 "npm pack
" 是 CLI 定义的命令。
postpack
- 在 tarball 生成并移动到其最终目的地之后运行。
准备和预发布
弃用说明:预发布
从 npm@1.1.71
开始,npm CLI 已经为 npm publish
和 npm install
运行了 prepublish
脚本,因为它是一种方便的方式来准备一个包以供使用(一些常见的用例在下面的部分中描述)。 在实践中,它也被证明是 非常混乱。 从 npm@4.0.0
开始,引入了一个新事件 prepare
,它保留了这种现有行为。 添加了一个新事件 prepublishOnly
作为过渡策略,以允许用户避免现有 npm 版本的混乱行为,并且只在 npm publish
上运行(例如,最后一次运行测试以确保它们处于良好状态)。
有关此更改的详细说明,请参阅 https://github.com/npm/npm/issues/10074,并进一步阅读。
用例
如果你需要在使用之前对你的包执行操作,以不依赖于目标系统的操作系统或体系结构的方式,使用 prepublish
脚本。 这包括以下任务:
- 将 CoffeeScript 源代码编译成 JavaScript。
- 创建 JavaScript 源代码的缩小版本。
- 获取你的包将使用的远程资源。
在 prepublish
时间做这些事情的好处是它们可以在一个地方做一次,从而降低复杂性和可变性。 此外,这意味着:
- 你可以依赖
coffee-script
作为devDependency
,因此你的用户不需要安装它。 - 你不需要在你的包中包含缩小器,从而为你的用户减少大小。
- 你不需要依赖你的用户在目标机器上拥有
curl
或wget
或其他系统工具。
生命周期操作顺序
npm publish
prepublishOnly
prepare
prepublish
publish
postpublish
npm pack
prepack
postpack
npm install
preinstall
install
postinstall
也触发
prepublish
(在本地时)prepare
(在本地时)
npm start
npm run start
有一个 npm start
简写。
prestart
start
poststart
默认值
npm 将根据包内容默认一些脚本值。
"start": "node server.js"
:如果你的包根目录中有
server.js
文件,那么 npm 将默认start
命令为node server.js
。"install": "node-gyp rebuild"
:如果你的包根目录中有一个
binding.gyp
文件,并且你还没有定义自己的install
或preinstall
脚本,npm 将默认使用 node-gyp 编译install
命令。
用户
如果 npm 以 root 权限调用,那么它会将 uid 更改为 user
配置指定的用户账户或 uid,默认为 nobody
。 设置 unsafe-perm
标志以使用 root 权限运行脚本。
环境
包脚本在一个环境中运行,其中提供了许多关于 npm 设置和进程当前状态的信息。
路径
如果你依赖于定义可执行脚本的模块,例如测试套件,那么这些可执行文件将被添加到 PATH
以执行脚本。 所以,如果你的 package.json 有这个:
{"name" : "foo","dependencies" : {"bar" : "0.1.x"},"scripts": {"start" : "bar ./test"}}
然后你可以运行 npm start
来执行 bar
脚本,该脚本被导出到 npm install
上的 node_modules/.bin
目录中。
package.json 变量
package.json 字段附加在 npm_package_
前缀上。 因此,例如,如果你的 package.json 文件中有 {"name":"foo", "version":"1.2.5"}
,那么你的包脚本会将 npm_package_name
环境变量设置为 "foo",并将 npm_package_version
设置为 "1.2.5"。 你可以在代码中使用 process.env.npm_package_name
和 process.env.npm_package_version
访问这些变量,其他字段以此类推。
配置
配置参数以 npm_config_
前缀放入环境中。 例如,你可以通过检查 npm_config_root
环境变量来查看有效的 root
配置。
特殊:package.json "配置" 对象
如果有 <name>[@<version>]:<key>
的配置参数,则 package.json "配置" 键在环境中被覆盖。 例如,如果 package.json 有这个:
{"name" : "foo","config" : {"port" : "8080"},"scripts" : {"start" : "node server.js"}}
server.js 是这样的:
http.createServer(...).listen(process.env.npm_package_config_port)
然后用户可以通过执行以下操作来改变行为:
npm config set foo:port 80
当前生命周期事件
最后,将 npm_lifecycle_event
环境变量设置为正在执行的循环的任何阶段。 因此,你可以将一个脚本用于流程的不同部分,根据当前发生的情况进行切换。
对象按照这种格式展平,所以如果你的 package.json 中有 {"scripts":{"install":"foo.js"}}
,那么你会在脚本中看到:
process.env.npm_package_scripts_install === "foo.js"
示例
例如,如果你的 package.json 包含以下内容:
{"scripts" : {"install" : "scripts/install.js","postinstall" : "scripts/install.js","uninstall" : "scripts/uninstall.js"}}
那么 scripts/install.js
将在生命周期的安装和安装后阶段被调用,而 scripts/uninstall.js
将在包被卸载时被调用。 由于 scripts/install.js
运行在两个不同的阶段,在这种情况下查看 npm_lifecycle_event
环境变量是明智的。
如果你想运行一个 make 命令,你可以这样做。 这工作得很好:
{"scripts" : {"preinstall" : "./configure","install" : "make && make install","test" : "make test"}}
退出
通过将该行作为脚本参数传递给 sh
来运行脚本。
如果脚本以 0 以外的代码退出,那么这将中止该过程。
请注意,这些脚本文件不必是 nodejs 甚至 javascript 程序。 它们必须是某种可执行文件。
钩子脚本
如果要在所有包的特定生命周期事件中运行特定脚本,则可以使用钩子脚本。
将一个可执行文件放在 node_modules/.hooks/{eventname}
中,当所有包在该根目录中安装的任何包的包生命周期中经历该点时,它将为所有包运行。
钩子脚本的运行方式与 package.json 脚本完全相同。 也就是说,它们位于一个单独的子进程中,具有上述环境。
最佳实践
- 不要以非零错误代码退出,除非你是认真的。 除了卸载脚本,这将导致 npm 操作失败,并可能被回滚。 如果故障很小或只会阻止某些可选功能,那么最好只打印警告并成功退出。
- 尽量不要使用脚本来做 npm 可以为你做的事情。 通读
package.json
以查看你可以通过适当地描述你的包来指定和启用的所有内容。 一般来说,这将导致更健壮和一致的状态。 - 检查环境以确定将东西放在哪里。 例如,如果
npm_config_binroot
环境变量设置为/home/user/bin
,则不要尝试将可执行文件安装到/usr/local/bin
中。 用户可能出于某种原因这样设置它。 - 不要在脚本命令前加上 "sudo"。 如果出于某种原因需要 root 权限,那么它将因该错误而失败,并且用户将 sudo 有问题的 npm 命令。
- 不要使用
install
。 使用.gyp
文件进行编译,使用prepublish
进行其他任何操作。 你几乎不必显式设置预安装或安装脚本。 如果你这样做,请考虑是否有其他选择。install
或preinstall
脚本的唯一有效用途是编译必须在目标架构上完成。