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

时间:2017-07-10 21:38:49

标签: node.js npm npm-install package-lock.json

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

例如,锁定文件的typescript指定为版本2.1.6。然后,在npm install命令之后,版本更改为2.4.1。这似乎打败了锁定文件的整个目的。

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

12 个答案:

答案 0 :(得分:323)

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

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

更新1:下面描述的行为已在npm 5.4.2中得到修复:GitHub issue #17979中列出了当前预期的行为。

原始回答: npm 5.1.0package-lock.json的行为已在issue #16866中进行了更改。您观察到的行为显然是从版本5.1.0开始的npm。

这意味着只要找到package.json中的依赖项的较新版本,package-lock.json就可以胜过package.json。如果要有效地固定依赖关系,现在必须指定没有前缀的版本,例如,您需要将其编写为1.2.0而不是~1.2.0^1.2.0。然后package.jsonpackage-lock.json的组合将产生可重现的构建。要明确:单独package-lock.json不再锁定根级依赖项!

这个设计决定是否合适是有争议的,在issue #17979中对GitHub的混淆导致了持续的讨论。 (在我看来,这是一个值得怀疑的决定;至少名称lock不再适用。)

还有一个注意事项:对不支持不可变包的注册表也有限制,例如直接从GitHub而不是npmjs.org提取包时。有关详细说明,请参阅this documentation of package locks

答案 1 :(得分:122)

我发现npm 5.7.1的新版本将使用新命令npm ci,该命令将仅从package-lock.json安装

  

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

     

它的工作方式是丢弃你的node_modules并从头开始重新创建它。

     

除了保证你只获得锁文件中的内容之外,当你没有以node_modules开头时,它也比npm安装快得多(2x-10x!)。

     

正如您可能从名称中获取的那样,我们希望它对持续集成环境来说是一个巨大的好处。我们还期望那些使用git标签进行生产部署的人将获得重大收益。

答案 2 :(得分:71)

使用新引入的

npm ci
  

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

Introducing npm ci for faster, more reliable builds

答案 3 :(得分:27)

简短答案:

  • 当package-lock.json存在时,它会否决package.json
  • 修改package.json时,它会否决package-lock.json

以下是一个可以解释问题的场景(已通过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 :(得分:7)

使用npm ci命令而不是npm install

“ ci”代表“全新安装”。它将基于package-lock.json文件而不是宽松的package.json文件依赖项来安装项目依赖项。

它将与您的其他队友生成相同的版本,并且速度也要快得多。

答案 5 :(得分:6)

你可能有类似的东西:

"typescript":"~2.1.6"

package.json中哪个npm更新到最新的次要版本,在您的情况下为2.4.1

  

编辑:来自OP的问题

     

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

     

答案:

     

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

     

或更一般地说:

     

举个例子,考虑

     

包A:

     

{“name”:“A”,“version”:“0.1.0”,“依赖项”:{         “B”:“< 0.1.0”}}

     

包B:

     

{“name”:“B”,“version”:“0.0.1”,“依赖项”:{        “C”:“< 0.1.0”}}

     

和包C:

     

{“name”:“C”,“version”:“0.0.1”}

     

如果这些是唯一的版本   在注册表中可用的A,B和C,然后是正常的npm安装A.   将安装:

     

A@0.1.0 - B@0.0.1        - C@0.0.1

     

但是,如果发布了B@0.0.2,那么将安装新的npm安装A:

     

A@0.1.0 - B@0.0.2        - 假设新版本没有修改B的依赖关系,则为C@0.0.1。当然,B的新版本可能包括一个新版本   C版本和任意数量的新依赖项。如果有这样的变化   不可取的,A的作者可以指定对B@0.0.1的依赖。   但是,如果A的作者和B的作者不是同一个人,那就是   A的作者无法说他或她不想参与其中   当B完全没有改变时,C的新发布版本。

  

OP问题2:让我看看我是否理解正确。你是什​​么   说是锁文件指定了辅助版本   依赖,但仍依赖于package.json的模糊匹配   确定顶级依赖项。那是准确的吗?

     

答案:没有。包锁可以锁定整个包树,包括   package.json中描述的根包。如果typescript被锁定   在2.4.1的{​​{1}},它应该保持这种状态直到它为止   改变。让我们说明天package-lock.json发布版本typescript。   如果我签出你的分支并运行2.4.2,npm将尊重你   锁定文件并安装npm install

有关2.4.1的更多信息:

对于npm修改node_modules树或package.json的任何操作,都会自动生成

package-lock.json。它描述了生成的确切树,以便后续安装能够生成相同的树,无论中间依赖性更新如何。

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

  

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

     

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

     

通过可读的源代码控制差异来提高树更改的可见性。

     

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

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

答案 6 :(得分:5)

将来,您可以使用--from-lock-file(或类似)标记从package-lock.json安装 而不进行修改。

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

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

答案 7 :(得分:3)

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

答案 8 :(得分:3)

可能您应该使用类似的东西

npm ci

代替使用npm install 如果您不想更改软件包的版本。

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

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

答案 9 :(得分:1)

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

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

答案 10 :(得分:0)

编辑:名称&#34;锁定&#34;是一个棘手的,它的NPM试图赶上纱线。它不是一个锁定的文件。 package.json是一个用户修复的文件,只需安装一次&#34;将生成node_modules文件夹树,然后该树将写入package-lock.json。所以你看,它是相反的 - 依赖版本将一如既往地从package.json拉出,而package-lock.json应该被称为package-tree.json

(希望这可以让我的答案更清楚,经过这么多的投票后)

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

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

答案 11 :(得分:0)

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

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

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