脚本
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
prepublishOnlyprepareprepublishpublishpostpublish
npm pack
prepackpostpack
npm install
preinstallinstallpostinstall
也触发
prepublish(在本地时)prepare(在本地时)
npm start
npm run start 有一个 npm start 简写。
prestartstartpoststart
默认值
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脚本的唯一有效用途是编译必须在目标架构上完成。