NPM package-locks
说明
从概念上讲,npm-install
的 ‘input’ 是 package.json,而其“输出”是一个完整的 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 使用包 lock.json 或者,如果存在的话, NPM-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 重新计算依赖项版本 。
软件包锁的存在会更改安装行为,从而:
-
复制由包锁描述的模块树。这意味着使用“已解决”中引用的特定文件(如果有)来重现文件中描述的结构,如果没有,则使用“版本”降至正常的包解析度。
-
遍历树,以常规方式安装所有丢失的依赖项。
如果 preshrinkwrap,shrinkwrap 或者 postshrinkwrap 是在 scripts 财产 package.json,他们将在顺序执行。preshrinkwrap 并 shrinkwrap 在收缩包装之前 postshrinkwrap 执行,然后再执行。这些脚本同时针对 package-lock.json 和 运行 npm-shrinkwrap.json。例如,对生成的文件运行一些后处理:
"scripts": {
"postshrinkwrap": "json -I -e \"this.myMetadata = $MY_APP_METADATA\""
}
使用锁定的包
使用锁定的软件包与使用没有软件包锁的任何软件包没有什么不同:任何更新 node_modules 和 / 或 package.json 依赖的命令都会自动同步现有的锁文件。
这包括 npm install,npm rm,npm update 等为了防止这种更新情况发生,你可以使用 –no-save 选项,防止一起保存,或 –no-shrinkwrap 允许 package.json 同时使被更新 package-lock.json 或 npm-shrinkwrap.json 完整。
强烈建议您将生成的程序包锁定提交给源代码控制:这将允许您团队中的其他任何人,
您的部署,您的 CI / 持续集成以及 npm install 在您的程序包源中运行的其他任何人都获得与您完全相同的依赖树正在发展。
此外,这些更改的差异是人类可读的,并且会通知您 npm 对您的所做的任何更改 node_modules,
因此您可以注意到是否有任何传递性依赖项被更新,提升等。
解决锁文件冲突
有时,两个单独的 npm 安装会创建软件包锁,这些软件包锁会导致源控制系统中的合并冲突。
从开始 npm@5.7.0,这些冲突可以通过手动修复任何 package.json 冲突然后 npm install [--package-lock-only]
重新运行来解决。npm 将自动为您解决任何冲突,并在合理的树中编写合并的软件包锁,其中包括来自两个分支的所有依赖项。
如果 --package-lock-only
提供,它将在不修改 local 的情况下执行此操作 node_modules/
。
为了使此过程在 git 上无缝运行,请考虑安装 npm-merge-driver
,它将教 git 如何在没有任何用户交互的情况下自行完成此操作。
简而言之:$ npx npm-merge-driver install -g
可以使您做到这一点,甚至可以使用 npm@5.7.0
npm 5
的预发行版,尽管噪音更大。
请注意,如果 package.json
自身发生冲突,则 npm install
即使使用合并驱动程序,也必须手动解决并手动运行 。