脚本

npm 如何处理 "脚本" 字段

选择命令行版本:

描述

¥Description

package.json 文件的 "scripts" 属性支持许多内置脚本及其预设的生命周期事件以及任意脚本。所有这些都可以通过运行 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 <stage>. 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 上运行,不带包参数(可以像 --production--omit=dev 那样使用标志运行,但在安装特定包(例如 npm install express)时不会运行)

    ¥Runs on local npm install without package arguments (runs with flags like --production or --omit=dev, but does not run when installing specific packages like npm install express)

  • prepublishOnlyprepack 之后、postpack 之前运行

    ¥Runs AFTER prepublishOnly and prepack, but BEFORE postpack

  • 如果软件包通过 npm install <folder> 作为链接安装,则运行该软件包

    ¥Runs for a package if it's being installed as a link through npm install <folder>

  • 注意:如果通过 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.

  • 工作区中,准备脚本会在所有包中并发运行。如果你有相互依赖的包,并且其中一个包必须先于另一个包构建,请考虑使用 --foreground-scripts(可以在 .npmrc 中使用 foreground-scripts=true 设置)按顺序运行脚本,或者采用不同的构建结构。

    ¥In workspaces, prepare scripts run concurrently across all packages. If you have interdependent packages where one must build before another, consider using --foreground-scripts (which can be set in .npmrc with foreground-scripts=true) to run scripts sequentially, or structure your build differently.

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, whereas, "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

使用 prepare 脚本执行平台无关且需要在软件包使用前运行的构建任务。这包括以下任务:

¥Use a prepare script to perform build tasks that are platform-independent and need to run before your package is used. This includes tasks such as:

  • 将 TypeScript 或其他源代码编译为 JavaScript。

    ¥Compiling TypeScript or other source code into JavaScript.

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

    ¥Creating minified versions of JavaScript source code.

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

    ¥Fetching remote resources that your package will use.

prepare 脚本中运行这些构建任务可确保它们只在单个位置执行一次,从而降低复杂性和可变性。此外,这意味着:

¥Running these build tasks in the prepare script ensures they happen once, in a single place, reducing complexity and variability. Additionally, this means that:

  • 你可以将构建工具依赖为 devDependencies,这样你的用户就不需要安装这些工具。

    ¥You can depend on build tools as devDependencies, and thus your users don't need to have them 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 dependent package and so this dependency is being uninstalled

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

    ¥a user uninstalled a dependent 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.

脚本工作目录

¥Working Directory for Scripts

无论调用 npm 时当前工作目录是什么,脚本始终从包文件夹的根目录运行。这意味着你的脚本可以可靠地假定它们在包根目录中运行。

¥Scripts are always run from the root of the package folder, regardless of what the current working directory is when npm is invoked. This means your scripts can reliably assume they are running in the package root.

如果你希望脚本根据运行 npm 时所在的目录做出不同的行为,可以使用 INIT_CWD 环境变量,该变量保存了你运行 npm run 时所在的完整路径。

¥If you want your script to behave differently based on the directory you were in when you ran npm, you can use the INIT_CWD environment variable, which holds the full path you were in when you ran npm run.

旧版 npm 中的历史行为

¥Historical Behavior in Older npm Versions

对于 npm v6 及更早版本,脚本通常从包的根目录运行,但在旧版本中,在极少数情况下和存在错误的情况下,无法保证这一点。如果你的包必须支持非常旧的 npm 版本,你可能希望在脚本中添加安全措施(例如,检查 process.cwd())。

¥For npm v6 and earlier, scripts were generally run from the root of the package, but there were rare cases and bugs in older versions where this was not guaranteed. If your package must support very old npm versions, you may wish to add a safeguard in your scripts (for example, by checking process.cwd()).

更多详情,请参阅:

¥For more details, see:

用户

¥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

npm 从 package.json 设置以下环境变量:

¥npm sets the following environment variables from the package.json:

  • npm_package_name - 软件包名称

    ¥npm_package_name - The package name

  • npm_package_version - 软件包版本

    ¥npm_package_version - The package version

  • npm_package_bin_* - bin 字段中定义的每个可执行文件

    ¥npm_package_bin_* - Each executable defined in the bin field

  • npm_package_engines_* - engines 字段中定义的每个引擎

    ¥npm_package_engines_* - Each engine defined in the engines field

  • npm_package_config_* - config 字段中定义的每个配置值

    ¥npm_package_config_* - Each config value defined in the config field

  • npm_package_json - package.json 文件的完整路径

    ¥npm_package_json - The full path to the package.json file

此外,对于安装脚本(preinstallinstallpostinstall),npm 会设置以下环境变量:

¥Additionally, for install scripts (preinstall, install, postinstall), npm sets these environment variables:

  • npm_package_resolved - 软件包的已解析 URL

    ¥npm_package_resolved - The resolved URL for the package

  • npm_package_integrity - 软件包的完整性哈希值

    ¥npm_package_integrity - The integrity hash for the package

  • npm_package_optional - 如果软件包是可选依赖,请设置为 "true"

    ¥npm_package_optional - Set to "true" if the package is optional

  • npm_package_dev - 如果软件包是开发依赖,请设置为 "true"

    ¥npm_package_dev - Set to "true" if the package is a dev dependency

  • npm_package_peer - 如果软件包是同级依赖,请设置为 "true"

    ¥npm_package_peer - Set to "true" if the package is a peer dependency

  • npm_package_dev_optional - 如果软件包既是开发依赖又是可选依赖,请设置为 "true"

    ¥npm_package_dev_optional - Set to "true" if the package is both dev and optional

例如,如果你的 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 在代码中访问这些变量。

¥For example, 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.

注意:在 npm 7 及更高版本中,大多数 package.json 字段不再作为环境变量提供。需要访问其他 package.json 字段的脚本应直接读取 package.json 文件。npm_package_json 环境变量提供用于此目的的文件路径。

¥Note: In npm 7 and later, most package.json fields are no longer provided as environment variables. Scripts that need access to other package.json fields should read the package.json file directly. The npm_package_json environment variable provides the path to the file for this purpose.

有关包配置的更多信息,请参见 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": {
"prepare": "scripts/build.js",
"test": "scripts/test.js"
}
}

然后,将在生命周期的 prepare 阶段调用 scripts/build.js,如果你的脚本需要在不同的上下文中表现不同,你可以检查 npm_lifecycle_event 环境变量。

¥then scripts/build.js will be called for the prepare stage of the lifecycle, and you can check the npm_lifecycle_event environment variable if your script needs to behave differently in different contexts.

如果你想运行构建命令,可以执行此操作。这工作得很好:

¥If you want to run build commands, you can do so. This works just fine:

{
"scripts": {
"prepare": "npm run build",
"build": "tsc",
"test": "jest"
}
}

退出

¥Exiting

脚本通过将代码行作为脚本参数传递给 POSIX 系统上的 /bin/sh 或 Windows 上的 cmd.exe 来运行。你可以通过设置 script-shell 配置选项来控制使用哪个 shell。

¥Scripts are run by passing the line as a script argument to /bin/sh on POSIX systems or cmd.exe on Windows. You can control which shell is used by setting the script-shell configuration option.

如果脚本以 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

  • 不要以非零错误代码退出,除非你是认真的。如果故障很小或只会阻止某些可选功能,那么最好只打印警告并成功退出。

    ¥Don't exit with a non-zero error code unless you really mean it. 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.

也可以看看

¥See Also

npm v11.7 中文网 - 粤ICP备13048890号