package-locks

npm 锁文件的解释

描述

从概念上讲,"input" 到 npm install 是一个 package.json,而它的 "output" 是一个完整的 node_modules 树: 你声明的依赖的表示。 在理想世界中,npm 会像纯函数一样工作: 相同的 package.json 应该在任何时候产生完全相同的 node_modules 树。 在某些情况下,这确实是真的。 但在许多其他情况下,npm 无法做到这一点。 这有多种原因:

  • 可能使用了不同版本的 npm(或其他包管理器)来安装包,每个包使用的安装算法略有不同。

  • 自上次安装包以来,可能已经发布了直接 semver-range 包的新版本,因此将使用更新的版本。

  • 你的一个依赖的依赖可能已经发布了一个新版本,即使你使用固定的依赖说明符(1.2.3 而不是 ^1.2.3)也会更新

  • 你安装的注册表不再可用,或者允许版本变更(与主 npm 注册表不同),并且现在在相同版本号下存在不同版本的包。

例如,考虑包 A:

{
"name": "A",
"version": "0.1.0",
"dependencies": {
"B": "<0.1.0"
}
}

包 B:

{
"name": "B",
"version": "0.0.1",
"dependencies": {
"C": "<0.1.0"
}
}

和包 C:

{
"name": "C",
"version": "0.0.1"
}

如果这些是注册表中唯一可用的 A、B 和 C 版本,则将安装普通 npm install A

A@0.1.0
`-- B@0.0.1
`-- C@0.0.1

但是,如果 B@0.0.2 已发布,则会安装新的 npm install A

A@0.1.0
`-- B@0.0.2
`-- C@0.0.1

假设新版本没有修改 B 的依赖。 当然,新版本的 B 可以包含新版本的 C 和任意数量的新依赖。 如果不需要这样的更改,A 的作者可以指定对 B@0.0.1 的依赖。 但是,如果 A 的作者和 B 的作者不是同一个人,那么 A 的作者就没有办法在 B 完全没有变化的情况下说他或她不想拉入新发布的 C 版本。

为防止此潜在问题,npm 使用 package-lock.jsonnpm-shrinkwrap.json(如果存在)。 这些文件称为包锁或锁文件。

每当你运行 npm install 时,npm 都会生成或更新你的包锁,它看起来像这样:

{
"name": "A",
"version": "0.1.0",
...metadata fields...
"dependencies": {
"B": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/B/-/B-0.0.1.tgz",
"integrity": "sha512-DeAdb33F+"
"dependencies": {
"C": {
"version": "git://github.com/org/C.git#5c380ae319fc4efe9e7f2d9c78b0faa588fd99b4"
}
}
}
}
}

这个文件描述了一个精确的,更重要的是可重现的 node_modules 树。 一旦它存在,任何未来的安装都将基于这个文件,而不是重新计算 package.json 的依赖版本。

包锁的存在会改变安装行为,例如:

  1. 包锁描述的模块树再现。 这意味着复制文件中描述的结构,如果可用,则使用 "resolved" 中引用的特定文件,如果没有,则使用 "version" 回退到正常的包解析。

  2. 遍历树并以通常的方式安装任何缺少的依赖。

如果 preshrinkwrapshrinkwrappostshrinkwrappackage.jsonscripts 属性中,它们将按顺序执行。 preshrinkwrapshrinkwrap 在收缩封装之前执行,postshrinkwrap 在之后执行。 这些脚本适用于 package-lock.jsonnpm-shrinkwrap.json。 例如在生成的文件上运行一些后处理:

"scripts": {
"postshrinkwrap": "json -I -e \"this.myMetadata = $MY_APP_METADATA\""
}

使用锁定的包

使用锁定的包与使用任何没有包锁的包没有什么不同: 任何更新 node_modules 和/或 package.json 的依赖的命令都会自动同步现有的锁定文件。 这包括 npm installnpm rmnpm update 等。为防止发生此更新,你可以使用 --no-save 选项完全阻止保存,或使用 --no-shrinkwrap 允许更新 package.json,同时保持 package-lock.jsonnpm-shrinkwrap.json 不变。

强烈建议你将生成的包锁提交给源代码管理: 这将允许你团队中的任何其他人、你的部署、你的 CI/持续集成以及在你的包源中运行 npm install 的任何其他人获得与你正在开发的完全相同的依赖树。 此外,这些更改的差异是人类可读的,并且会通知你 npm 对你的 node_modules 所做的任何更改,因此你可以注意到是否有任何传递依赖被更新、提升等。

解决锁文件冲突

有时,两个单独的 npm install 会创建包锁,从而导致源代码控制系统中的合并冲突。 从 npm@5.7.0 开始,可以通过手动修复任何 package.json 冲突,然后再次运行 npm install [--package-lock-only] 来解决这些冲突。 npm 将自动为你解决任何冲突,并编写一个合并的包锁,其中包括一个合理树中两个分支的所有依赖。 如果提供了 --package-lock-only,它会在不修改本地 node_modules/ 的情况下执行此操作。

为了让这个过程在 git 上无缝,可以考虑安装 npm-merge-driver,它会教 git 如何在没有任何用户交互的情况下自己做这件事。 简而言之: $ npx npm-merge-driver install -g 将允许你执行此操作,甚至可以与 npm 5 的 npm@5.7.0 之前版本一起使用,尽管噪音更大一些。 请注意,如果 package.json 本身发生冲突,你必须手动解决并手动运行 npm install,即使使用合并驱动程序也是如此。

也可以看看

npm 中文网 - 粤ICP备13048890号