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

Why does "npm install" rewrite package-lock.json?

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

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

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

你可能有这样的东西:

"typescript":"~2.1.6"

在你的 package.json which npm updates to the latest minor version,在你的情况下是 2.4.1

Edit: Question from OP

But that doesn't explain why "npm install" would change the lock file. Isn't the lock file meant to create a reproducible build? If so, regardless of the semver value, it should still use the same 2.1.6 version.

Answer:

This is intended to lock down your full dependency tree. Let's say typescript v2.4.1 requires widget ~v1.0.0. When you npm install it grabs widget v1.0.0. Later on your fellow developer (or CI build) does an npm install and gets typescript v2.4.1 but widget has been updated to widget v1.0.1. Now your node module are out of sync. This is what package-lock.json prevents.

Or more generally:

As an example, consider

package A:

{ "name": "A", "version": "0.1.0", "dependencies": { "B": "<0.1.0" } }

package B:

{ "name": "B", "version": "0.0.1", "dependencies": { "C": "<0.1.0" } }

and package C:

{ "name": "C", "version": "0.0.1" }

If these are the only versions of A, B, and C available in the registry, then a normal npm install A will install:

A@0.1.0 -- B@0.0.1 -- C@0.0.1

However, if B@0.0.2 is published, then a fresh npm install A will install:

A@0.1.0 -- B@0.0.2 -- C@0.0.1 assuming the new version did not modify B's dependencies. Of course, the new version of B could include a new version of C and any number of new dependencies. If such changes are undesirable, the author of A could specify a dependency on B@0.0.1. However, if A's author and B's author are not the same person, there's no way for A's author to say that he or she does not want to pull in newly published versions of C when B hasn't changed at all.


OP Question 2: So let me see if I understand correctly. What you're saying is that the lock file specifies the versions of the secondary dependencies, but still relies on the fuzzy matching of package.json to determine the top-level dependencies. Is that accurate?

Answer: No. package-lock locks the entire package tree, including the root packages described in package.json. If typescript is locked at 2.4.1 in your package-lock.json, it should remain that way until it is changed. And lets say tomorrow typescript releases version 2.4.2. If I checkout your branch and run npm install, npm will respect the lockfile and install 2.4.1.

有关 package-lock.json 的更多信息:

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

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

Describe a single representation of a dependency tree such that teammates, deployments, and continuous integration are guaranteed to install exactly the same dependencies.

Provide a facility for users to "time-travel" to previous states of node_modules without having to commit the directory itself.

To facilitate greater visibility of tree changes through readable source control diffs.

And optimize the installation process by allowing npm to skip repeated metadata resolutions for previously-installed packages.

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

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


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


更新 1: 下面描述的行为在 npm 5.4.2 中得到了修复:GitHub issue #17979.

中概述了当前预期的行为

原答案: package-lock.json 的行为在 npm 5.1.0 as discussed in issue #16866 中发生了变化。从 5.1.0 版开始,您观察到的行为显然是 npm 有意为之。

这意味着只要在 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 v5.4.2 中修复

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

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

更新

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

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

更新 2

在这里查看我的回答:

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

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

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

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

编辑:"lock" 这个名字很棘手,它的 NPM 试图赶上 Yarn。它根本不是锁定的文件。 package.json是用户固定文件,一旦"installed"会生成node_modules个文件夹树,然后在package-lock.json中写入该树。所以你看,它是另一种方式 - 依赖版本将一如既往地从 package.json 中提取,并且 package-lock.json 应该被称为 package-tree.json

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


一个简单的答案:package.json 像往常一样有你的依赖关系,而 package-lock.json 是 "an exact, and more importantly reproducible node_modules tree"(取自 npm docs itself)。

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

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

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

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

The new npm ci command installs from your lock-file ONLY. If your package.json and your lock-file are out of sync then it will report an error.

It works by throwing away your node_modules and recreating it from scratch.

Beyond guaranteeing you that you'll only get what is in your lock-file it's also much faster (2x-10x!) than npm install when you don't start with a node_modules.

As you may take from the name, we expect it to be a big boon to continuous integration environments. We also expect that folks who do production deploys from git tags will see major gains.

使用新引入的

npm ci

npm ci promises the most benefit to large teams. Giving developers the ability to “sign off” on a package lock promotes more efficient collaboration across large teams, and the ability to install exactly what is in a lockfile has the potential to save tens if not hundreds of developer hours a month, freeing teams up to spend more time building and shipping amazing things.

Introducing npm ci for faster, more reliable builds

简答:

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

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

您在 package.json 中声明依赖关系,例如:

"depA": "^1.0.0"

然后你这样做,npm install 这将生成一个包-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 ci 命令代替 npm install

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

它将根据 package-lock.json 文件而不是宽松的 package.json 文件 dependencies.[=13= 安装项目 dependencies ]

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

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

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

npm ci

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

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

The main difference is, npm install does install the packages taking packge.json as a reference. Where in the case of npm ci, it does install the packages taking package-lock.json as a reference, making sure every time the exact package is installed.

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

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

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