脚本

npm 如何处理 "脚本" 字段

选择 CLI 版本:

描述

Description

package.json 文件的 "scripts" 属性支持许多内置脚本及其预设的生命周期事件以及任意脚本。这些都可以通过运行 npm run-script <stage> 或简称 npm run <stage> 来执行。具有匹配名称的前置和后置命令也将为这些运行(例如 premyscriptmyscriptpostmyscript)。来自依赖的脚本可以使用 npm explore <pkg> -- npm run <stage> 运行。

The "scripts" property of your package.json file supports a number of built-in scripts and their preset life cycle events as well as arbitrary scripts. These all can be executed by running npm run-script <stage> or npm run <stage> for short. Pre and post commands with matching names will be run for those as well (e.g. premyscript, myscript, postmyscript). Scripts from dependencies can be run with npm explore <pkg> -- npm run <stage>.

前后脚本

Pre & Post Scripts

要为 package.json"scripts" 部分中定义的任何脚本创建 "pre" 或 "post" 脚本,只需创建另一个具有匹配名称的脚本并将 "pre" 或 "post" 添加到它们的开头。

To create "pre" or "post" scripts for any scripts defined in the "scripts" section of the package.json, simply create another script with a matching name and add "pre" or "post" to the beginning of them.

{
"scripts": {
"precompress": "{{ executes BEFORE the `compress` script }}",
"compress": "{{ run command to compress files }}",
"postcompress": "{{ executes AFTER `compress` script }}"
}
}

在此示例中,npm run compress 将按照所述执行这些脚本。

In this example npm run compress would execute these scripts as described.

生命周期脚本

Life Cycle Scripts

有一些特殊的生命周期脚本只在某些情况下发生。除了 pre<event>post<event><event> 脚本之外,还会出现这些脚本。

There are some special life cycle scripts that happen only in certain situations. These scripts happen in addition to the pre<event>, post<event>, and <event> scripts.

  • prepare, prepublish, prepublishOnly, prepack, postpack, dependencies

prepare (since npm@4.0.0)

  • 在打包之前运行,即在 npm publishnpm pack 期间

    Runs BEFORE the package is packed, i.e. during npm publish and npm pack

  • 在本地 npm install 上运行,不带任何参数

    Runs on local npm install without any arguments

  • prepublish 之后但在 prepublishOnly 之前运行

    Runs AFTER prepublish, but BEFORE prepublishOnly

  • 注意:如果通过 git 安装的包包含 prepare 脚本,则在打包和安装包之前,将安装其 dependenciesdevDependencies,并运行准备脚本。

    NOTE: If a package being installed through git contains a prepare script, its dependencies and devDependencies will be installed, and the prepare script will be run, before the package is packaged and installed.

  • npm@7 起,这些脚本在后台运行。要查看输出,请运行:--foreground-scripts

    As of npm@7 these scripts run in the background. To see the output, run with: --foreground-scripts.

prepublish (DEPRECATED)

  • npm publish 期间不运行,但在 npm cinpm install 期间运行。有关更多信息,请参见下文。

    Does not run during npm publish, but does run during npm ci and npm install. See below for more info.

prepublishOnly

  • 在封装准备和封装之前运行,仅在 npm publish 上运行。

    Runs BEFORE the package is prepared and packed, ONLY on npm publish.

prepack

  • 在打包 tarball 之前运行(在“npm pack”、“npm publish”上以及安装 git 依赖时)。

    Runs BEFORE a tarball is packed (on "npm pack", "npm publish", and when installing a git dependency).

  • 注意:“npm run pack”与“npm pack”不同。“npm run pack”是任意用户定义的脚本名称,其中“npm pack”是 CLI 定义的命令。

    NOTE: "npm run pack" is NOT the same as "npm pack". "npm run pack" is an arbitrary user defined script name, where as, "npm pack" is a CLI defined command.

postpack

  • 在生成 tarball 之后但在它被移动到最终目的地之前运行(如果有的话,publish 不会在本地保存 tarball)

    Runs AFTER the tarball has been generated but before it is moved to its final destination (if at all, publish does not save the tarball locally)

dependencies

  • 如果发生更改,则在修改 node_modules 目录的任何操作之后运行。

    Runs AFTER any operations that modify the node_modules directory IF changes occurred.

  • 不在全局模式下运行

    Does NOT run in global mode

准备和预发布

Prepare and Prepublish

弃用注意事项:prepublish

Deprecation Note: prepublish

npm@1.1.71 开始,npm CLI 已经为 npm publishnpm install 运行了 prepublish 脚本,因为它是一种方便的方式来准备一个包以供使用(一些常见的用例在下面的部分中描述)。在实践中,它也被证明是 非常混乱。从 npm@4.0.0 开始,引入了一个新事件 prepare,它保留了这种现有行为。添加了一个新事件 prepublishOnly 作为过渡策略,以允许用户避免现有 npm 版本的混乱行为,并且只在 npm publish 上运行(例如,最后一次运行测试以确保它们处于良好状态)。

Since npm@1.1.71, the npm CLI has run the prepublish script for both npm publish and npm install, because it's a convenient way to prepare a package for use (some common use cases are described in the section below). It has also turned out to be, in practice, very confusing. As of npm@4.0.0, a new event has been introduced, prepare, that preserves this existing behavior. A new event, prepublishOnly has been added as a transitional strategy to allow users to avoid the confusing behavior of existing npm versions and only run on npm publish (for instance, running the tests one last time to ensure they're in good shape).

有关此更改的详细说明,请参阅 https://github.com/npm/npm/issues/10074,并进一步阅读。

See https://github.com/npm/npm/issues/10074 for a much lengthier justification, with further reading, for this change.

用例

Use Cases

如果你需要在使用之前对你的包执行操作,以不依赖于目标系统的操作系统或体系结构的方式,使用 prepublish 脚本。这包括以下任务:

If you need to perform operations on your package before it is used, in a way that is not dependent on the operating system or architecture of the target system, use a prepublish script. This includes tasks such as:

  • 将 CoffeeScript 源代码编译成 JavaScript。

    Compiling CoffeeScript source code into JavaScript.

  • 创建 JavaScript 源代码的缩小版本。

    Creating minified versions of JavaScript source code.

  • 获取你的包将使用的远程资源。

    Fetching remote resources that your package will use.

prepublish 时间做这些事情的好处是它们可以在一个地方做一次,从而降低复杂性和可变性。此外,这意味着:

The advantage of doing these things at prepublish time is that they can be done once, in a single place, thus reducing complexity and variability. Additionally, this means that:

  • 你可以依赖 coffee-script 作为 devDependency,因此你的用户不需要安装它。

    You can depend on coffee-script as a devDependency, and thus your users don't need to have it installed.

  • 你不需要在你的包中包含缩小器,从而为你的用户减少大小。

    You don't need to include minifiers in your package, reducing the size for your users.

  • 你不需要依赖你的用户在目标机器上拥有 curlwget 或其他系统工具。

    You don't need to rely on your users having curl or wget or other system tools on the target machines.

依赖

Dependencies

每当 npm 命令导致 node_modules 目录发生更改时,都会运行 dependencies 脚本。它在应用更改并更新 package.jsonpackage-lock.json 文件之后运行。

The dependencies script is run any time an npm command causes changes to the node_modules directory. It is run AFTER the changes have been applied and the package.json and package-lock.json files have been updated.

生命周期操作顺序

Life Cycle Operation Order

npm cache add

  • prepare

npm ci

  • preinstall

  • install

  • postinstall

  • prepublish

  • preprepare

  • prepare

  • postprepare

这些都是在将模块实际安装到 node_modules 之后按顺序运行的,中间没有发生任何内部操作

These all run after the actual installation of modules into node_modules, in order, with no internal actions happening in between

npm diff

  • prepare

npm install

这些也会在你运行 npm install -g <pkg-name> 时运行

These also run when you run npm install -g <pkg-name>

  • preinstall

  • install

  • postinstall

  • prepublish

  • preprepare

  • prepare

  • postprepare

如果你的包的根目录中有一个 binding.gyp 文件并且你还没有定义自己的 installpreinstall 脚本,npm 将默认 install 命令通过 node-gyp rebuild 使用 node-gyp 编译

If there is a binding.gyp file in the root of your package and you haven't defined your own install or preinstall scripts, npm will default the install command to compile using node-gyp via node-gyp rebuild

这些是从 <pkg-name> 的脚本运行的

These are run from the scripts of <pkg-name>

npm pack

  • prepack

  • prepare

  • postpack

npm publish

  • prepublishOnly

  • prepack

  • prepare

  • postpack

  • publish

  • postpublish

npm rebuild

  • preinstall

  • install

  • postinstall

  • prepare

prepare 仅在当前目录是符号链接时运行(例如,带有链接包)

prepare is only run if the current directory is a symlink (e.g. with linked packages)

npm restart

如果定义了 restart 脚本,则运行这些事件,否则运行 stopstart(如果存在),包括它们的 prepost 迭代)

If there is a restart script defined, these events are run, otherwise stop and start are both run if present, including their pre and post iterations)

  • 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。在这种情况下,prestartpoststart 仍将运行。

If there is a server.js file in the root of your package, then npm will default the start command to node server.js. prestart and poststart will still run in this case.

npm stop

  • prestop

  • stop

  • poststop

npm test

  • pretest

  • test

  • posttest

npm version

  • preversion

  • version

  • postversion

关于缺少 npm uninstall 脚本的说明

A Note on a lack of npm uninstall scripts

虽然 npm v6 有 uninstall 生命周期脚本,但 npm v7 没有。删除包的原因有很多种,目前还没有明确的方法可以为脚本提供足够的上下文以使其有用。

While npm v6 had uninstall lifecycle scripts, npm v7 does not. Removal of a package can happen for a wide variety of reasons, and there's no clear way to currently give the script enough context to be useful.

删除包的原因包括:

Reasons for a package removal include:

  • 用户直接卸载了这个包

    a user directly uninstalled this package

  • 用户卸载了依赖包,因此正在卸载此依赖

    a user uninstalled a dependant package and so this dependency is being uninstalled

  • 用户卸载了一个依赖包,但另一个包也依赖于这个版本

    a user uninstalled a dependant package but another package also depends on this version

  • 此版本已作为副本与另一个版本合并

    this version has been merged as a duplicate with another version

  • 等等

    etc.

由于缺少必要的上下文,uninstall 生命周期脚本没有实现并且无法运行。

Due to the lack of necessary context, uninstall lifecycle scripts are not implemented and will not function.

用户

User

当 npm 以 root 身份运行时,脚本始终使用工作目录所有者的有效 uid 和 gid 运行。

When npm is run as root, scripts are always run with the effective uid and gid of the working directory owner.

环境

Environment

包脚本在一个环境中运行,其中提供了许多关于 npm 设置和进程当前状态的信息。

Package scripts run in an environment where many pieces of information are made available regarding the setup of npm and the current state of the process.

路径

path

如果你依赖于定义可执行脚本的模块,例如测试套件,那么这些可执行文件将被添加到 PATH 以执行脚本。所以,如果你的 package.json 有这个:

If you depend on modules that define executable scripts, like test suites, then those executables will be added to the PATH for executing the scripts. So, if your package.json has this:

{
"name": "foo",
"dependencies": {
"bar": "0.1.x"
},
"scripts": {
"start": "bar ./test"
}
}

然后你可以运行 npm start 来执行 bar 脚本,该脚本被导出到 npm install 上的 node_modules/.bin 目录中。

then you could run npm start to execute the bar script, which is exported into the node_modules/.bin directory on npm install.

package.json 变量

package.json vars

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_nameprocess.env.npm_package_version 访问这些变量,其他字段以此类推。

The package.json fields are tacked onto the npm_package_ prefix. So, for instance, if you had {"name":"foo", "version":"1.2.5"} in your package.json file, then your package scripts would have the npm_package_name environment variable set to "foo", and the npm_package_version set to "1.2.5". You can access these variables in your code with process.env.npm_package_name and process.env.npm_package_version, and so on for other fields.

有关包配置的更多信息,请参见 package.json

See package.json for more on package configs.

当前生命周期事件

current lifecycle event

最后,将 npm_lifecycle_event 环境变量设置为正在执行的循环的任何阶段。因此,你可以将一个脚本用于流程的不同部分,根据当前发生的情况进行切换。

Lastly, the npm_lifecycle_event environment variable is set to whichever stage of the cycle is being executed. So, you could have a single script used for different parts of the process which switches based on what's currently happening.

对象按照这种格式展平,所以如果你的 package.json 中有 {"scripts":{"install":"foo.js"}},那么你会在脚本中看到:

Objects are flattened following this format, so if you had {"scripts":{"install":"foo.js"}} in your package.json, then you'd see this in the script:

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

示例

Examples

例如,如果你的 package.json 包含以下内容:

For example, if your package.json contains this:

{
"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 环境变量是明智的。

then scripts/install.js will be called for the install and post-install stages of the lifecycle, and scripts/uninstall.js will be called when the package is uninstalled. Since scripts/install.js is running for two different phases, it would be wise in this case to look at the npm_lifecycle_event environment variable.

如果你想运行一个 make 命令,你可以这样做。这工作得很好:

If you want to run a make command, you can do so. This works just fine:

{
"scripts": {
"preinstall": "./configure",
"install": "make && make install",
"test": "make test"
}
}

退出

Exiting

通过将该行作为脚本参数传递给 sh 来运行脚本。

Scripts are run by passing the line as a script argument to sh.

如果脚本以 0 以外的代码退出,那么这将中止该过程。

If the script exits with a code other than 0, then this will abort the process.

请注意,这些脚本文件不必是 Node.js 甚至 JavaScript 程序。它们必须是某种可执行文件。

Note that these script files don't have to be Node.js or even JavaScript programs. They just have to be some kind of executable file.

最佳实践

Best Practices

  • 不要以非零错误代码退出,除非你是认真的。除了卸载脚本,这将导致 npm 操作失败,并可能被回滚。如果故障很小或只会阻止某些可选功能,那么最好只打印警告并成功退出。

    Don't exit with a non-zero error code unless you really mean it. Except for uninstall scripts, this will cause the npm action to fail, and potentially be rolled back. If the failure is minor or only will prevent some optional features, then it's better to just print a warning and exit successfully.

  • 尽量不要使用脚本来做 npm 可以为你做的事情。通读 package.json 以查看你可以通过适当地描述你的包来指定和启用的所有内容。一般来说,这将导致更健壮和一致的状态。

    Try not to use scripts to do what npm can do for you. Read through package.json to see all the things that you can specify and enable by simply describing your package appropriately. In general, this will lead to a more robust and consistent state.

  • 检查环境以确定将东西放在哪里。例如,如果 npm_config_binroot 环境变量设置为 /home/user/bin,则不要尝试将可执行文件安装到 /usr/local/bin 中。用户可能出于某种原因这样设置它。

    Inspect the env to determine where to put things. For instance, if the npm_config_binroot environment variable is set to /home/user/bin, then don't try to install executables into /usr/local/bin. The user probably set it up that way for a reason.

  • 不要在脚本命令前加上 "sudo"。如果出于某种原因需要 root 权限,那么它将因该错误而失败,并且用户将 sudo 有问题的 npm 命令。

    Don't prefix your script commands with "sudo". If root permissions are required for some reason, then it'll fail with that error, and the user will sudo the npm command in question.

  • 不要使用 install。使用 .gyp 文件进行编译,使用 prepare 进行其他任何操作。你几乎不必显式设置预安装或安装脚本。如果你这样做,请考虑是否有其他选择。installpreinstall 脚本的唯一有效用途是编译必须在目标架构上完成。

    Don't use install. Use a .gyp file for compilation, and prepare for anything else. You should almost never have to explicitly set a preinstall or install script. If you are doing this, please consider if there is another option. The only valid use of install or preinstall scripts is for compilation which must be done on the target architecture.

  • 无论调用 npm 时当前工作目录是什么,脚本都从包文件夹的根目录运行。如果你希望脚本根据你所在的子目录使用不同的行为,你可以使用 INIT_CWD 环境变量,它包含你在运行 npm run 时所在的完整路径。

    Scripts are run from the root of the package folder, regardless of what the current working directory is when npm is invoked. If you want your script to use different behavior based on what subdirectory you're in, you can use the INIT_CWD environment variable, which holds the full path you were in when you ran npm run.

也可以看看

See Also

npm 中文网 - 粤ICP备13048890号