为什么“ npm install”会重写package-lock.json?


612

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

例如,锁定文件已将打字稿指定为版本2.1.6。然后,在npm install命令之后,版本更改为2.4.1。这似乎破坏了锁定文件的全部目的。

我想念什么?如何让npm真正尊重我的锁定文件?


4
这无法回答您的问题,因此希望可以发表评论,但请查看Yarn。切换花费了我们不到一个小时的时间。
KayakinKoder

4
相同的问题,但使用yarn github.com/yarnpkg/yarn/issues/570(非常有启发性)
Yves M.

2
我有相同的问题。我package-lock.json跑步时恢复活力npm install。这闻起来像一个npm错误。您是否使用自己的注册表?
HaNdTriX


@YvesM。--no-save防止更改锁文件,但不会影响OP提到的愚蠢的第一级依赖关系升级。
罗斯·艾伦

Answers:


422

更新3:正如其他答案所指出的那样,npm cinpm 5.7.0中引入了该命令,作为在CI上下文中实现快速且可复制的构建的其他方法。有关更多信息,请参见文档npm博客


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


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


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

也就是说,只要在中找到依赖项的更新版本,package.json就可以覆盖。如果你想有效地定位您的依赖关系,您现在必须在没有前缀指定版本,例如,你需要将它们写成的,而不是或。然后,和的组合将生成可复制的构建。需要明确的是:仅凭它不再锁定根级别依赖项!package-lock.jsonpackage.json1.2.0~1.2.0^1.2.0package.jsonpackage-lock.jsonpackage-lock.json

不管这个设计决定是好的还是不可行的,在GitHub上的问题#17979引起了持续的讨论。(在我看来,这是一个有问题的决定;至少这个名称lock不再适用。)

还有一点注意事项:对于不支持不可变软件包的注册表也有一个限制,例如当您直接从GitHub而不是npmjs.org提取软件包时。有关更多说明,请参见此软件包锁文档


43
什么是黑客npm update的呢?:○我有同样的感觉,npm install更新DEPS,但我并不想相信它..但好像它是可悲的是真实的。不管怎么样还是有选项可以使用npm shrinkwrap到锁DEPS,但绝对名包锁是不正确因为它不会冻结,也不会锁定依赖项
。– Jurosh

266
真是一团糟!世界上最大的软件包管理器,但没有有关其工作方式的文档。每个人都在猜测应该做什么,这变成了一场意见大战。讨论是件好事,但应该在发布之前进行。在某个时候,有人需要进行最后的呼叫,然后才能实施,记录和发布。PHP是由委员会和临时组织共同设计的,看看结果如何。我不希望看到同样的事情发生在这个至关重要且广泛使用的工具上。
Landon Poch

85
那么,使用package-lock有什么意义呢?我以为它会在不同的工作区中创建相同的环境,但事实证明它无能为力
laltin

17
“然后,package.json和package-lock.json的组合将产生可复制的构建。” “ package-lock.json”在这里起什么作用?如果不使用任何版本前缀,仅“ package.json”就不能生成可复制的版本吗?
贾尼斯·埃默里斯(JānisElmeris)

12
@JānisElmeris我认为package.json无法锁定深层依赖...
Juan Mendes

165

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

新的npm ci命令仅从锁定文件安装。如果您的package.json和锁定文件不同步,则它将报告错误。

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

除了保证您只获得锁定文件中的内容外,如果不使用node_modules进行启动,它也比npm install快得多(2x-10x!)。

您可能会从名称中得知,我们希望它对持续集成环境大有裨益。我们还期望从git标签进行生产部署的人们会看到很大的收获。


133
如果存在锁定文件,则这应该是默认行为。
空性

13
因此,他们改变了npm i的工作方式,只是几个月后才将其恢复为npm ci?
Scott Flack

1
我还是很困惑。在该项目中运行命令之前,文档说明“确保具有程序包锁和最新的安装:npm installnpm ci。不会npm install覆盖package-lock.json文件吗?
adiga

1
AFAIK:@adiga-从5.4版开始,npm 必要时更改锁文件,以符合packages.json中的规范。因此,如果软件包以前说过thatpackage: 1,而lock说过..: 1.0.4,则dev可以编辑说thatpackage: 2-,这将迫使锁文件更改,因为1.0.4与新指定的范围不兼容。如果不更改packages.json,将保持锁定在确切的版本,直到删除锁定文件。[如果没有保持锁定,并且没有更改packages.json,请提交错误报告。]
ToolmakerSteve

1
@George从我已阅读的信息(对于npm的最新版本)和有限的测试中得出:两者都是。
Venryx

95

使用新推出的

npm ci

npm ci承诺将为大型团队带来最大的利益。使开发人员能够“退出”软件包锁定功能可以促进大型团队之间更有效的协作,而完全安装锁定文件中内容的功能则可以每月节省数十甚至数百小时的开发人员时间,从而释放团队花更多的时间来建造和运输令人惊奇的东西。

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


3
这对我来说似乎是正确的?有人可以确认吗?
phouse512

6
@ phouse512这是正确的。我们几乎使用npm ci,并且仅npm install在更新或安装新软件包时使用。
雅各布·席维尔

1
最近的评论等。这就是我要解决的问题。太可惜了他们无法解决可怕的问题,但是如果新福音是“ npm ci”,那就好了。我可以适应。
斯文德(Svend)'18年

不幸的是,即使它是空的但重要的符号链接,它总是会删除现有node_modules目录并在本地重建。:(
Joe Atzberger

2
@ToolmakerSteve不要屏住呼吸!我认为删除目录的内容比删除目录要慢很多。您将必须枚举内容,然后向操作系统发出一系列删除命令,而不仅仅是发出一个删除命令。鉴于以前的性能问题在npm上得到解决,并且使用方面有所改进,npm ci我希望他们在非常罕见的用例中不愿引入任何可能降低性能的内容。您可能想查看pnpm.js.org,尽管它使用硬链接来减少磁盘使用量。
卡托

64

简短答案:

  • 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)

4
这确实是“锁定”文件的预期行为。显然,较旧版本的NPM并非如此。
Blockost

1
那么npm如何跟踪对package.json的最新更新?将package.json和package-lock.json移至另一台计算机时会发生什么?新计算机中的npm如何知道package.lock是原始文件还是已更新,以决定是否需要更新package-lock.json?
Lahiru Chandima

3
@LahiruChandima它并不真正跟踪更新。除非不满意npm installpackage-lock.json否则将使用的锁定版本,package.json在这种情况下,它将安装package.json并相应地重建package-lock.json。如果你改变了你package.json以这样的方式,现有的包锁仍满足更新package.json将继续使用package-lock
艾哈迈德Abdelghany

1
如果在node_modules中已经有一个满足package.json要求的模块,则npm install不管package-lock.json如何,都不会执行任何操作。即使有可用的更新与package.json中指定的semver匹配,我们也必须显式更新软件包。至少这是我多年的经验。
carlin.scott

1
@ToolmakerSteve我也对@ carlin.scott报告的行为表示怀疑,但我只是对其进行了测试,实际上他是正确的。如果其中的版本node_modules满足in的范围package.json,并且没有package-lock.json文件,则npm在运行时将不会更新模块npm install。我猜这很好,因为您可以使用npm update(或npm-check最新)来更新依赖关系,并且对于仅向其中添加一个条目package.json,而不希望无关包将自身更新为满足sem-ver的最新情况,这种行为会更快范围。
Venryx

19

使用npm ci命令代替npm install

“ ci”代表“持续集成”。

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

它会为您的队友生成相同的版本,并且速度也更快。

您可以在此博客文章中阅读有关此内容的更多信息:https : //blog.npmjs.org/post/171556855892/introducing-npm-ci-for-faster-more-reliable


2
ci指的是“持续集成”,如文档和博客帖子中提到的宣布命令:blog.npmjs.org/post/171556855892/...
乔Atzberger

谢谢乔。我已经用正确的名称更新了答案,并链接到了博客文章。😊(对于那些阅读本文的人,以前我说过它代表“全新安装”)
Daniel Tonon

“而且速度也快得多”-它将删除node_modules文件夹并从头开始重新创建它。真的快很多吗?也npm install删除node_modules文件夹吗?
izogfif

我认为速度来自npm,无需计算要下载的软件包。认为它npm install必须在运行时解决所有程序包依赖项。npm ci只是“获取这些确切模块”的购物清单。
Daniel Tonon

8

将来,您将能够使用--from-lock-file(或类似的)标志仅从进行安装,而package-lock.json无需对其进行修改。

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

请参阅https://github.com/npm/npm/issues/18286以跟踪功能。


我对此表示怀疑。如果不同操作系统的依赖关系不同,如何强制安装不起作用的内容?
Yevgeniy Afanasyev '18年

4
@YevgeniyAfanasyev代替了该标志,它的实现npm ci也可以解决您的问题。
spex

8

看来此问题已在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 是现在安装现有项目时应使用的命令。


5
我正在使用5.4.2,当时仍会导致我的package-lock.json修改npm i。例如,fsevents当我npm i在一台不支持的计算机上删除该模块时fsevents,然后在一台不支持的计算机上再次添加该模块npm i
hrdwdmrbl

然后,您应该在npm GitHub存储库中提出一个新问题来对此进行解释。如果按照他们所说的那样工作不起作用,则他们将其视为迫切需要修复的高优先级错误。
丹尼尔·托农

@hrdwdmrbl我看到了同样的fsevents在我滴package-lock.jsonnpm@5.5,而在Mac OS X的贡献者合作。如果您还没有打开问题,我会的。
AL X

@hrdwdmrbl我在留下评论后忘了(以及相关问题的长篇文章),忘了回到SO来更新我的评论。感谢您的回复。一切安好。
AL X

4

您可能有类似以下内容:

"typescript":"~2.1.6"

您将package.json哪个npm更新为最新的次要版本,2.4.1

编辑:OP的问题

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

回答:

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

或更笼统地说:

例如,考虑

套餐A:

{“名称”:“ A”,“版本”:“ 0.1.0”,“依赖关系”:{“ 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的作者不是同一个人,那么当B完全没有变化时,A的作者就无法说他或她不想插入新发布的C版本。


OP问题2:所以让我看看我是否理解正确。您要说的是,锁定文件指定了二级依赖关系的版本,但是仍然依赖于package.json的模糊匹配来确定顶级依赖关系。准确吗?

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

更多内容package-lock.json

对于npm修改node_modules树或package.json的任何操作,都会自动生成package-lock.json。它描述了生成的确切树,因此无论中间依赖项更新如何,后续安装都可以生成相同的树。

该文件旨在提交到源存储库中,并具有多种用途:

描述一个依赖关系树的单一表示,这样可以确保队友,部署和持续集成安装完全相同的依赖关系。

为用户提供一种便利,使其可以“时间旅行”到node_modules的先前状态,而不必提交目录本身。

为了通过可读的源代码控制差异更好地了解树的变化。

并允许npm跳过先前安装的软件包的重复元数据解析,从而优化安装过程。

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


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

3
这就是我要说的。我的软件包锁定文件显示为typescript@2.1.6,但是当我运行npm install时,该条目将替换为typescript@2.4.1。
Viper Bailey

5
我也遇到过同样的问题。在我们的CI / CD中,将package-lock.json获取下拉列表,然后运行npm install,但是package-lock.json文件已修改,我们必须执行重置才能提取下一个更改。
BayssMekanique

15
我不明白 如果后续安装仍可能进行升级,那么这是一个“锁定”文件吗?
罗斯·艾伦

5
我认为他们首先想到的是将此文件作为“ info”和“ lock”,然后决定将其仅作为“ info”文件。更好的名称是“ package-info.json”。我希望有一个“ npm install -lock”,它将从“ package-lock.json”安装而忽略“ package.json”
Jeremy Chone

2

也许你应该使用这样的东西

npm ci

npm install 如果不想更改软件包的版本,请不要使用。

根据官方文件,都npm installnpm ci安装这些项目所需的依赖关系。

主要区别在于,npm install是否packge.json以参考安装软件包。对于npm ci,它会确实package-lock.json以参考的方式安装软件包,请确保每次都安装了确切的软件包。



0

编辑:名称“锁”是一个棘手的人,其NPM试图赶上纱线。它不是一个锁定的文件。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。


1
因为如果您运行npm install,则包锁定将被更新。
Jean-Baptiste '18
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.