欢迎大家关注公众号『
JavaScript与编程艺术』高质量文章优先公众号发布。
首先提个问题,如果你能自信回答出来就无需阅读本文了:
某包存在 package-lock.json,当项目 npm install 该依赖,请问会按照其 package-lock.json 指定的版本安装依赖吗?
例如包
A依赖C@1.0.0而且锁了版本,当C升级到1.0.1,项目P再次安装相同版本的A,此时C仍然会是1.0.0吗?
答案
视情况而定。
- 首次安装:A,C 将会是更新后 1.0.1。即使项目 P 和包 A 都有锁版本
- 二次安装:P 如果存在 lockfile,则 C 会保留 1.0.0。否则会更新到 1.0.1
本文将介绍为什么 A 已经锁定了版本为何仍然会更新。
为什么依赖的 package-lock.json 不生效
那是不是发布产物没有 lock file 导致的,我们尝试通过 package.json files 指定发布产物必须增加 package-lock.json 和 yarn.lock。
// package.json
{
"files": [ "package-lock.json", "yarn.lock", "dist" ]
}
但出现了新问题:package-lock.json 仍然会没有打包到产物中(小技巧可以通过 npm publish --dry-run 查看产物而无需真实发布),为什么呢?
The difference is that
package-lock.jsoncannot be published, and it will be ignored if found in any place other than the root project.
这是从官方文档引用的原文,但说的不是很清楚,stackoverflow 上也有一些讨论。
翻阅了诸多文档后,在这篇文章中找到了解释:
What's the issue in publishing package-lock.json
lockfile 不应该被发布的原因是因为这不会有任何作用。npm 会忽略任何不在项目根目录下的 package-lock.json 文件,因为锁定依赖项除了在项目本身之外的任何地方都没有意义。
没有任何出错的危险,只是发布它是毫无意义的。
可以得出两点:
- npm install 只会寻找根目录的
package-lock.json。故即使我们发布产物包含了package-lock.json也没用。 - npm install 寻找依赖的
package-lock.json没有意义,现在的模式没有任何风险。
我们需要辩证的看,第一点没问题是 npm 的机制,第二点并非没有意义而且现在是存在风险的。
设想一个这样的 case。
我开发了一个包 A@1.0.0, 依赖了 B@2.0.0, 它依赖了 C@^1.0.0. 在发布的那一刻代码依赖是
A -> B@2.0.0 -> C@1.0.0。我发布之前本地测试通过了,但是此后 C 发布了 1.0.2, 却引起了破坏性的变更,即使A已经锁定了版本,但是安装过程变成了A@1.0.0 -> B@2.0.0 ->C@1.0.1,而我只测试了C@1.0.0但是却被其他项目安装的时候自动更新成C@1.0.1,自然别人安装同样版本的A@1.0.0就挂了。
这其实是有现实案例的,即使包 A 锁定了版本也会出现 C 被自动更新,案例见 react-i18next 从 14.1.2 升级到 14.1.3,我的项目跑不起来了 -_-||。
怎么锁定依赖的依赖
可以让 A 安装严格相等版本。
.npmrc 增加以下配置,安装无需加任何 flag,只需 npm install xx
save=true
save-exact=true
安装时会默认添加 --save-exact 或 -E 进一步锁定版本。因为通过 lockfile 锁定的版本,在被其他项目 install 仍然会更新依赖。
- "react-i18next": "^14.1.1",
+ "react-i18next": "14.1.2"
其他方案:采用 yarn#resolutions 或 npm#overrides。相对比较麻烦得逐个包设置。
问题回顾
本文提出了一个“反常识”的现象,锁版本对于库或包来说“不起作用”。因为被安装的时候 npm 只会寻找自己根目录的 package-lock.json。
该问题在首次安装时候会出现,其实影响还好,因为首次安装一定会测试到位。但是有个不小的困扰我们debug的问题是同一个包不同时间段被安装可能出现问题,即使这个包作者已经锁了版本,如果我们理解了第一点就能快速做出 fix 措施。