ChatGPT解决这个技术问题 Extra ChatGPT

为什么“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 真正尊重我的锁定文件?

同样的问题,但使用纱线 github.com/yarnpkg/yarn/issues/570(非常有指导意义)
我有同样的问题。当我运行 npm install 时,我的 package-lock.json 会重新生成。这闻起来像一个 npm 错误。您使用自己的注册表吗?
@YvesM。 --no-save 防止更改锁定文件,但它不会影响 OP 提到的愚蠢的一级依赖项升级。
这似乎也发生在 npm6 上 - 我运行 npm i 时没有更改任何内容,并且我的 package-lock.json 已修改(requires 中所有包下的版本都已更改)。它似乎是有意而不是破坏任何东西?更多信息here

B
Ben Scheirman

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

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

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

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

这意味着只要在 package.json 中为依赖项找到较新版本,package.json 就可以覆盖 package-lock.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 了解更多说明。


那么 npm update 是为了什么? :o 我有同样的感觉,npm install 更新了 deps,但我不想相信它.. 但似乎这是可悲的事实.. 无论如何仍然可以选择使用 npm shrinkwrap 来锁定 deps,但绝对名称 package-lock 不正确,因为它不会冻结,也不会锁定依赖项..
真是一团糟!世界上最大的包管理器,但它没有关于它应该如何工作的文档。每个人都在猜测它应该做什么,它变成了一场意见战。讨论是好的,但应该在发布到野外之前进行。在某些时候,需要有人做出最终决定,然后才能实施、记录和发布。 PHP 是由委员会和 ad-hoc 共同设计的,看看结果如何。我不想看到同样的事情发生在一个如此重要且被广泛使用的工具上。
那么,使用 package-lock 有什么意义呢?我认为它会在不同的工作空间中创建相同的环境,但事实证明它什么也没做
“然后 package.json 和 package-lock.json 的组合将产生可重复的构建。” “package-lock.json”在这里有什么作用?如果不使用版本前缀,单独的“package.json”是否已经产生了可重复的构建?
@JānisElmeris 我认为 package.json 不能锁定深度依赖...
C
Community

我发现会有一个带有新命令 npm ci 的新版本 npm 5.7.1,它只能从 package-lock.json 安装

新的 npm ci 命令仅从您的锁定文件安装。如果你的 package.json 和你的 lock-file 不同步,那么它会报错。它通过丢弃你的 node_modules 并从头开始重新创建它来工作。除了保证您只会获得锁定文件中的内容之外,当您不从 node_modules 开始时,它也比 npm install 快得多(2x-10x!)。正如您可能从名称中看到的那样,我们希望它对持续集成环境来说是一个巨大的福音。我们还希望通过 git 标签进行生产部署的人们会看到重大收获。


如果存在锁定文件,这应该是默认行为。
所以他们改变了 npm i 的工作方式,只是在几个月后将它作为 npm ci 重新引入?
我仍然很困惑。在该项目中运行命令 npm ci 之前,文档说 “确保您有包锁和最新的安装:npm installnpm install 不会覆盖 package-lock.json 文件吗?
AFAIK:@adiga - 从版本 5.4 开始,npm更改锁定文件如果需要这样做,以满足 packages.json 中的规范。因此,如果包过去说 thatpackage: 1,而 lock 说 ..: 1.0.4,则开发人员可以编辑说 thatpackage: 2 - 这将强制 lock 文件更改,因为 1.0.4 与新指定的范围不兼容。如果不更改 packages.json,将保持锁定在确切的版本,直到删除锁定文件。 [如果没有保持锁定,并且没有更改 packages.json,请提交错误报告。]
花了我一整天的时间。我花了一整天的时间在这个基本问题上 :( :(
A
ATOMP

简短的回答:

npm install 只有在满足 package.json 的要求时才会尊重 package-lock.json。

如果它不满足这些要求,则更新包并覆盖包锁。

如果您希望安装失败而不是在发生这种情况时覆盖包锁定,请使用 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)

这确实是“锁定”文件的预期行为。显然,旧版本的 NPM 并非如此。
那么 npm 是如何跟踪 package.json 的最后更新的呢?将 package.json 和 package-lock.json 移动到另一台计算机时会发生什么?新机上的 npm 是如何知道 package.lock 是原来的还是已经更新,来决定是否需要更新 package-lock.json 呢?
@LahiruChandima 它并没有真正跟踪更新。 npm install 将使用来自 package-lock.json 的锁定版本,除非它不满足 package.json,在这种情况下它会安装 package.json 并相应地重建 package-lock.json。如果您更改了您的 package.json,使现有的包锁仍然满足更新后的 package.json,它将继续使用该 package-lock
如果您在 node_modules 中已经有一个满足 package.json 要求的模块,那么 npm install 什么都不做,不管 package-lock.json 是什么。即使有可用的更新与 package.json 中指定的 semver 匹配,我们也必须显式更新包。至少这是我多年来的经验。
@ToolmakerSteve 我也对@carlin.scott 报告的行为持怀疑态度,但我只是对其进行了测试,事实上他是正确的。如果 node_modules 内的版本满足 package.json 的范围,并且没有 package-lock.json 文件,则 npm 在运行 npm install 时不会更新模块。我想这很好,因为您可以使用 npm update(或最新的 npm-check)来更新依赖项,并且对于某人只是向 package.json 添加一个条目并且不希望不相关的包更新自己的情况,这种行为会更快到满足 sem-ver 范围的最新版本。
G
Gal Margalit

使用新推出的

npm ci

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

Introducing npm ci for faster, more reliable builds


这对我来说似乎是正确的?其他人可以确认吗?
@phouse512 这是正确的。我们几乎使用 npm ci,并且仅在更新或安装新软件包时使用 npm install
最近的评论等。这就是我要回答的问题。太糟糕了,他们无法解决可怕的混乱,但如果新的福音是“npm ci”,那很好。我可以适应。
太糟糕了,它总是会删除现有的 node_modules 目录并在本地重建,即使那是一个空但重要的符号链接。 :(
@ToolmakerSteve 不要屏住呼吸!我认为删除目录的内容比删除目录要慢得多。您必须枚举内容,然后向操作系统发出一系列删除命令,而不仅仅是一个删除命令。由于之前在 npm 上解决的性能问题以及使用 npm ci 的改进,我预计他们将非常不愿意引入任何可能降低相当不常见用例的性能的东西。您可能想查看 pnpm.js.org,尽管它使用硬链接来减少磁盘使用量。
D
Daniel Tonon

使用 npm ci 命令代替 npm install

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

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

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

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


ci 指“持续集成”,如宣布命令的文档和博客文章中所述:blog.npmjs.org/post/171556855892/…
“而且它也快得多” - 它将删除 node_modules 文件夹并从头开始重新创建它。真的快很多吗? npm install 是否也会删除 node_modules 文件夹?
我认为速度来自 npm 不需要计算要下载的包。将其想象为 npm install 在运行时必须解决所有包依赖项。 npm ci 只是“获取这些确切模块”的购物清单。
ci 实际上代表 clean install
代表“全新安装”,我刚刚阅读了构建它的人的帖子。暂时找不到链接。。
D
Daniel Tonon

看来此问题已在 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.4.2,它仍然会导致在 npm i 时修改我的 package-lock.json。例如,当我在不支持 fsevents 的机器上 npm i 时删除模块 fsevents,然后在支持的机器上再次添加一个 npm i 时重新添加模块。
然后你应该在 npm GitHub repo 中提出一个新问题来解释这一点。如果它不按他们所说的那样工作,那么他们会将其视为迫切需要修复的高优先级错误。
@hrdwdmrbl 在与 Mac OS X 贡献者合作时,我在 package-lock.json 中看到与 npm@5.5 相同的 fsevents。如果你还没有打开一个问题,我会的。
@hrdwdmrbl 在我留下评论并忘记回到 SO 更新我的评论后,我发现了这一点(以及相关问题的长线)。谢谢你让我回来。一切都很好。
T
Timothy Higinbottom

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

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

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


我对此表示怀疑。如果不同操作系统的依赖关系不同,你怎么能强制安装一些不起作用的东西?
@YevgeniyAfanasyev 而不是那个标志,它被实现为 npm ci ,它也处理你的问题。
S
Sengottaian Karthik

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

npm ci

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

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

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


M
Matt

你可能有类似的东西:

"typescript":"~2.1.6"

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

编辑:来自 OP 的问题 但这并不能解释为什么“npm install”会更改锁定文件。锁定文件不是要创建可重现的构建吗?如果是这样,无论 semver 值如何,它仍应使用相同的 2.1.6 版本。答:这是为了锁定您的完整依赖关系树。假设 typescript v2.4.1 需要小部件 ~v1.0.0。当您 npm install 时,它会抓取小部件 v1.0.0。稍后,您的开发人员(或 CI 构建)执行 npm 安装并获取 typescript v2.4.1,但小部件已更新为小部件 v1.0.1。现在你的节点模块不同步了。这就是 package-lock.json 所阻止的。或更一般地说:例如,考虑包 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 版本。

OP 问题 2:所以让我看看我是否理解正确。你说的是lock文件指定了二级依赖的版本,但还是依赖package.json的模糊匹配来确定顶级依赖。那准确吗?答:不会。package-lock 会锁定整个包树,包括 package.json 中描述的根包。如果 typescript 在您的 package-lock.json 中被锁定为 2.4.1,它应该保持这种状态,直到它被更改。让我们说明天打字稿发布版本 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


但这并不能解释为什么“npm install”会更改锁定文件。锁定文件不是要创建可重现的构建吗?如果是这样,无论 semver 值如何,它仍应使用相同的 2.1.6 版本。
这就是我要说的。我的包锁定文件显示 typescript@2.1.6 但是当我运行 npm install 时,该条目被替换为 typescript@2.4.1。
我遇到过同样的问题。在我们的 CI/CD 中,package-lock.json 被拉下,然后我们运行 npm install,但 package-lock.json 文件被修改,我们必须执行重置,然后才能拉下下一个更改。
我不明白。如果后续安装仍可能进行升级,这怎么是“锁定”文件?!
我认为他们从将这个文件作为“信息”和“锁定”的想法开始,然后决定它只是一个“信息”文件。更好的名称是“package-info.json”。我希望有一个“npm install -lock”,它将从“package-lock.json”安装并忽略“package.json”
B
Bernardo Dal Corno

编辑:“锁”这个名字是一个棘手的问题,它的 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 itself)。

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


因为如果你运行 npm install,package-lock 将会被更新。
h
hrdwdmrbl

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

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


package-lock 中的重写是有意的,问题不是由此造成的
o
obul

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

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

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


这个答案与接受的答案有何不同?
因为它的某些部分是不正确的。