815

我刚刚升级到npm@5。我现在有一个package-lock.json文件,其中包含package.json中的所有内容。我希望,当我运行时npm install,将从锁定文件中提取依赖版本以确定应该在我的node_modules目录中安装什么。奇怪的是,它实际上最终修改和重写了我的package-lock.json文件。

例如,锁定文件的 typescript 指定为版本2.1.6。然后,在npm install命令之后,版本更改为2.4.1。这似乎违背了锁定文件的全部目的。

我错过了什么?如何让 npm 真正尊重我的锁定文件?

4

12 回答 12

551

更新 3:正如其他答案所指出的那样,该npm ci命令是在 npm 5.7.0 中引入的,作为在 CI 上下文中实现快速和可重复构建的另一种方式。有关详细信息,请参阅文档npm 博客


更新 2:更新和澄清文档的问题是GitHub 问题 #18103


更新 1:下面描述的行为在 npm 5.4.2 中得到修复:当前预期的行为在GitHub 问题 #17979中进行了概述。


原始答案:的行为在npm 5.1.0package-lock.json中已更改,如问题 #16866中所述。您观察到的行为显然是 npm 从 5.1.0 版开始的。

这意味着package.json只要package-lock.jsonpackage.json. 如果您想有效地固定您的依赖项,您现在必须指定不带前缀的版本,例如,您需要将它们写为1.2.0而不是~1.2.0or ^1.2.0。然后 和 的组合package.jsonpackage-lock.json产生可重复的构建。需要明确的是:package-lock.json单独不再锁定根级别的依赖关系!

这个设计决定是否好是有争议的,在 GitHub 上的问题 #17979中,这种混乱导致了持续的讨论。(在我看来,这是一个值得商榷的决定;至少这个名字lock不再适用了。)

另一个注意事项:对于不支持不可变包的注册表也有限制,例如当您直接从 GitHub 而不是 npmjs.org 拉包时。有关进一步说明,请参阅此包锁文档。

于 2017-08-08T11:16:35.910 回答
219

我发现将有一个带有新命令的新版本 npm 5.7.1npm ci,它将package-lock.json仅从

新的 npm ci 命令仅从您的锁定文件安装。如果你的 package.json 和你的 lock-file 不同步,那么它会报错。

它通过丢弃你的 node_modules 并从头开始重新创建它来工作。

除了保证您只会获得锁定文件中的内容之外,当您不从 node_modules 开始时,它也比 npm install 快得多(2x-10x!)。

正如您可能从名称中看到的那样,我们希望它对持续集成环境来说是一个巨大的福音。我们还希望通过 git 标签进行生产部署的人们会看到重大收获。

于 2018-02-27T15:10:21.987 回答
127

简短的回答:

  • npm install仅当满足 package.json 的要求时才尊重 package-lock.json。
  • 如果它不满足这些要求,则更新包并覆盖包锁。
  • 如果您希望安装失败而不是在发生这种情况时覆盖 package-lock,请使用npm ci.

这是一个可以解释事情的场景(使用 NPM 6.3.0 验证)

您在 package.json 中声明一个依赖项,例如:

"depA": "^1.0.0"

然后你做,npm install这将生成一个 package-lock.json :

"depA": "1.0.0"

几天后,发布了“depA”的更新次要版本,例如“1.1.0”,然后以下情况成立:

npm ci       # respects only package-lock.json and installs 1.0.0

npm install  # also, respects the package-lock version and keeps 1.0.0 installed 
             # (i.e. when package-lock.json exists, it overrules package.json)

接下来,您手动将 package.json 更新为:

"depA": "^1.1.0"

然后重新运行:

npm ci      # will try to honor package-lock which says 1.0.0
            # but that does not satisfy package.json requirement of "^1.1.0" 
            # so it would throw an error 

npm install # installs "1.1.0" (as required by the updated package.json)
            # also rewrites package-lock.json version to "1.1.0"
            # (i.e. when package.json is modified, it overrules the package-lock.json)
于 2018-12-03T12:39:15.047 回答
113

使用新推出的

npm ci

npm ci 承诺给大型团队带来最大的好处。让开发人员能够在包锁定上“签字”促进大型团队之间更有效的协作,并且准确安装锁定文件中的内容的能力有可能每月节省数十甚至数百小时的开发时间,从而解放团队花更多的时间建造和运输令人惊奇的东西。

引入npm ci更快、更可靠的构建

于 2018-06-26T14:59:36.017 回答
25

使用npm ci命令而不是npm install.

“ci”代表“全新安装”。

它将基于 package-lock.json 文件而不是宽松的 package.json 文件依赖项安装项目依赖项。

它将为您的队友生成相同的构建,并且速度也更快。

您可以在这篇博文中了解更多信息: https ://blog.npmjs.org/post/171556855892/introducing-npm-ci-for-faster-more-reliable

于 2018-12-08T06:48:08.900 回答
10

看来此问题已在 npm v5.4.2 中修复

https://github.com/npm/npm/issues/17979

(向下滚动到线程中的最后一条评论)

更新

实际上已在 5.6.0 中修复。5.4.2 中有一个跨平台错误导致问题仍然存在。

https://github.com/npm/npm/issues/18712

更新 2

在这里查看我的答案: https ://stackoverflow.com/a/53680257/1611058

npm ci是您现在安装现有项目时应该使用的命令。

于 2017-09-29T21:55:03.737 回答
8

将来,您将能够使用--from-lock-file(或类似的)标志仅从安装package-lock.json而不修改它。

这对于可重复构建很重要的 CI 等环境很有用。

有关该功能的跟踪,请参阅https://github.com/npm/npm/issues/18286

于 2017-10-06T20:25:30.647 回答
6

你可能有类似的东西:

"typescript":"~2.1.6"

在您的package.jsonwhich npm 更新到最新的次要版本,在您的情况下2.4.1

编辑:来自OP的问题

但这并不能解释为什么“npm install”会更改锁定文件。锁定文件不是要创建可重现的构建吗?如果是这样,无论 semver 值如何,它仍应使用相同的 2.1.6 版本。

回答:

这旨在锁定您的完整依赖关系树。假设typescript v2.4.1需要widget ~v1.0.0。当您 npm install 时,它会抓取widget v1.0.0. 稍后,您的开发人员(或 CI 构建)执行 npm 安装并获取typescript v2.4.1widget已更新为widget v1.0.1. 现在你的节点模块不同步了。这就是package-lock.json阻止。

或更一般地说:

例如,考虑

包一:

{“名称”:“A”,“版本”:“0.1.0”,“依赖项”:{“B”:“<0.1.0”}}

包 B:

{“名称”:“B”,“版本”:“0.0.1”,“依赖项”:{“C”:“<0.1.0”}}

和包C:

{“名称”:“C”,“版本”:“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 版本。


OP 问题 2:所以让我看看我是否理解正确。你说的是lock文件指定了二级依赖的版本,但还是依赖package.json的模糊匹配来确定顶级依赖。那准确吗?

答:不会。package-lock 会锁定整个包树,包括 中描述的根包package.json。如果typescript被锁定2.4.1在您的 中package-lock.json,它应该保持这种状态,直到它被更改。让我们说明天typescript发布版本2.4.2。如果我签出您的分支并运行npm install,npm 将尊重锁定文件并安装2.4.1

更多关于package-lock.json

package-lock.json 会为 npm 修改 node_modules 树或 package.json 的任何操作自动生成。它描述了生成的确切树,以便后续安装能够生成相同的树,而不管中间依赖项更新如何。

该文件旨在提交到源存储库中,并用于各种目的:

描述依赖关系树的单一表示,以保证团队成员、部署和持续集成安装完全相同的依赖关系。

为用户提供“时间旅行”到 node_modules 先前状态的工具,而无需提交目录本身。

通过可读的源代码控制差异来促进对树更改的更大可见性。

并通过允许 npm 跳过以前安装的包的重复元数据解析来优化安装过程。

https://docs.npmjs.com/files/package-lock.json

于 2017-07-10T21:45:31.460 回答
6

可能你应该使用这样的东西

npm ci

npm install 如果您不想更改软件包的版本,而不是使用。

根据官方文档,npm install安装npm ci项目所需的依赖项。

主要区别在于,确实安装了作为参考npm install的软件包。packge.json在 的情况下npm ci,它确实安装了package-lock.json作为参考的软件包,确保每次安装准确的软件包。

于 2020-02-14T13:56:47.123 回答
2

编辑:“锁”这个名字是一个棘手的问题,它的 NPM 试图赶上 Yarn。它不是任何锁定的文件。package.json是一个用户固定的文件,一旦“安装”将生成 node_modules 文件夹树,然后该树将被写入package-lock.json. 所以你看,它反过来 - 依赖版本将像往常一样被提取package.json,并且package-lock.json应该被调用package-tree.json

(希望这让我的回答更清楚,经过这么多的反对票)


一个简单的答案:package.json像往常一样拥有你的依赖关系,而package-lock.json“一个精确的,更重要的是可重现的 node_modules 树”(取自npm docs 本身)。

至于棘手的名字,它的 NPM 试图赶上 Yarn。

于 2017-11-15T00:54:36.637 回答
1

在他们的 github 页面上有一个未解决的问题:https ://github.com/npm/npm/issues/18712

当开发人员使用不同的操作系统时,这个问题最为严重。

于 2017-11-22T00:08:24.593 回答
-2

Npm install 检测对 package.json 文件所做的任何更改以相应地反映依赖项列表。

前任。如果用户添加或删除了新的依赖项,则构建将下载或删除本地计算机中的依赖项。我们可以将其与 java 中的 .m2 存储库进行比较,其中 maven 不断跟踪 pom.xml 文件以更新依赖项。

package-lock.json 是内部进程在运行时使用的 package.json 的副本,唯一的区别是 package-lock.json 对用户是只读的。

于 2021-07-23T14:34:41.307 回答