npm 脚本 script
npm 如何处理脚本字段
描述
在"scripts"
你的属性package.json
文件支持许多内置的脚本和他们的预设生命周期事件以及任意脚本。这些都可以通过运行npm run-script <stage>
或npm run <stage>
简称来执行 。前置和后 名称匹配的命令将这些以及运行(例如premyscript
, myscript
,postmyscript
)。依赖项的脚本可以使用 npm explore <pkg> -- npm run <stage>
.
前后脚本
要为"scripts"
部分中定义的任何脚本创建“pre”或“post”脚本 package.json
,只需创建另一个具有匹配名称的脚本 并将“pre”或“post”添加到它们的开头。
{
"scripts": {
"precompress": "{{ executes BEFORE the `compress` script }}",
"compress": "{{ run command to compress files }}",
"postcompress": "{{ executes AFTER `compress` script }}"
}
}
在本例中,npm run compress
将按照描述执行这些脚本。
生命周期脚本
有一些特殊的生命周期脚本仅在某些情况下发生。这些脚本发生除pre<event>
,post<event>
和 <event>
脚本。
prepare
,prepublish
,prepublishOnly
,prepack
,postpack
准备(因为npm@4.0.0
)
- 在包装包装之前的任何时间运行,即在包装期间
npm publish
和npm pack
- 在打包之前运行
- 在发布包之前运行
- 在
npm install
没有任何参数的情况下在本地运行 - 之后运行
prepublish
,但之前prepublishOnly
- 注意:如果通过 git 安装的包包含
prepare
脚本,则在打包和安装包之前dependencies
,devDependencies
将安装其和并运行准备脚本。 - 由于
npm@7
这些脚本在后台运行。要查看输出,运行带有:--foreground-scripts
。
预发布(已弃用)
- 不在期间运行
npm publish
,但在npm ci
和期间运行npm install
。请参阅下文了解更多信息。
仅预发布
- 在包准备和打包之前运行,仅在
npm publish
.
预包装
- 在打包 tarball 之前运行(在“
npm pack
”、“npm publish
”和安装 git 依赖项时)。注意:“npm run pack
”与“npm pack
”不同。"npm run pack
" 是用户定义的任意脚本名称,其中, "npm pack
" 是 CLI 定义的命令。邮包 - 在 tarball 生成之后但在将其移动到最终目的地之前运行(如果有的话,publish 不会在本地保存 tarball)
准备和预发布
弃用说明:预发布
由于npm@1.1.71
,npm CLI 已经prepublish
为npm publish
和运行了脚本npm install
,因为这是准备要使用的包的便捷方式(一些常见用例在下面的部分中描述)。在实践中,它也变得非常混乱。从 开始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 cache add
prepare
npm ci
preinstall
install
postinstall
prepublish
preprepare
prepare
postprepare
这些都在将模块实际安装到 node_modules
中后按顺序运行,中间没有发生任何内部操作
npm diff
prepare
npm install
这些也会在你运行时运行 npm install -g <pkg-name>
preinstall
install
postinstall
prepublish
preprepare
prepare
postprepare
如果binding.gyp
你的包的根目录中有一个文件并且你没有定义你自己的install
或preinstall
脚本,npm 将默认install
使用 node-gyp 编译命令通过node-gyp rebuild
这些是从脚本运行的 <pkg-name>
npm pack
prepack
prepare
postpack
npm publish
prepublishOnly
prepack
prepare
postpack
publish
postpublish
prepare
期间不会运行 --dry-run
npm rebuild
preinstall``install``postinstall``prepare``prepare
仅当当前目录是符号链接时才运行(例如,带有链接的包)
npm restart
如果restart
定义了脚本,则运行这些事件,否则 stop
,start
如果存在,则都运行,包括它们的pre
和 post
迭代)
prerestart
restart
postrestart
npm run <user defined>
pre<user-defined>
<user-defined>
post<user-defined>
npm start
prestart
start
poststart
如果server.js
包的根目录中有一个文件,那么 npm 会将start
命令默认为node server.js
. prestart
并且 poststart
在这种情况下仍会运行。
npm stop
prestop
stop
poststop
npm test
pretest
test
posttest
关于缺少npm uninstall脚本的说明
虽然 npm v6 有uninstall
生命周期脚本,但 npm v7 没有。删除包的原因多种多样,目前还没有明确的方法可以为脚本提供足够有用的上下文。
删除软件包的原因包括:
- 一个用户直接卸载了这个包
- 用户卸载了依赖包,因此正在卸载此依赖项
- 用户卸载了一个依赖包,但另一个包也依赖于这个版本
- 此版本已与另一个版本合并为副本等等。
- 由于缺乏必要的上下文,
uninstall
生命周期脚本没有实现,也不会起作用。
用户
当 npm 以 root 身份运行时,脚本总是使用工作目录所有者的有效 uid 和 gid 运行。
环境
包脚本在一个环境中运行,在该环境中提供了许多关于 npm 设置和进程当前状态的信息。
路径
如果您依赖于定义可执行脚本的模块,例如测试套件,那么这些可执行文件将被添加到PATH
用于执行脚本的 . 所以,如果你的 package.json 有这个:
{
"name" : "foo",
"dependencies" : {
"bar" : "0.1.x"
},
"scripts": {
"start" : "bar ./test"
}
}
然后你可以运行npm start
执行bar
脚本,它导出到node_modules/.bin
的目录npm install
。
package.json 变量
package.json 字段被添加到npm_package_
前缀上。因此,例如,如果您{"name":"foo", "version":"1.2.5"}
的 package.json 文件中有,那么您的包脚本会将 npm_package_name
环境变量设置为“foo”,并将其 npm_package_version
设置为“1.2.5”。您可以在代码中使用process.env.npm_package_name
和 访问这些变量,process.env.npm_package_version
对于其他字段,依此类推。
有关package-json.md
包配置的更多信息,请参见。
当前生命周期事件
最后,npm_lifecycle_event
环境变量被设置为正在执行的循环阶段。因此,您可以将单个脚本用于流程的不同部分,该脚本根据当前发生的情况进行切换。
对象按照这种格式扁平化,所以如果你 {"scripts":{"install":"foo.js"}}
在 package.json 中有,那么你会在脚本中看到:
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 以外的代码退出,则这将中止进程。
请注意,这些脚本文件不必是 Node.js 甚至 JavaScript 程序。它们只需要是某种可执行文件。
最佳实践
- 不要以非零错误代码退出,除非你是认真的。除了卸载脚本,这将导致 npm 操作失败,并可能被回滚。如果故障很小或只会阻止某些可选功能,那么最好只打印警告并成功退出。
- 尽量不要使用脚本来做 npm 可以为您做的事情。通读
package.json
以查看您可以通过简单地适当描述您的包来指定和启用的所有内容。一般来说,这将导致更健壮和一致的状态。 - 检查环境以确定放置东西的位置。例如,如果
npm_config_binroot
环境变量设置为/home/user/bin
,则不要尝试将可执行文件安装到/usr/local/bin
. 用户可能出于某种原因以这种方式进行设置。 - 不要在脚本命令前加上“sudo”。如果出于某种原因需要 root 权限,那么它会因该错误而失败,并且用户将 sudo 有问题的 npm 命令。
- 不要使用
install
. 使用.gyp
文件进行编译,以及prepublish
其他任何事情。您几乎不必显式设置预安装或安装脚本。如果您正在这样做,请考虑是否还有其他选择。install
或preinstall
脚本的唯一有效用途是编译,必须在目标架构上完成。