首页 文章

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

提问于
浏览
323

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

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

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

11 回答

  • 52

    这似乎也发生在npm6 - 我运行了 npm i 而没有改变任何东西,我的 package-lock.json 被修改了( requires 中所有包下的版本都改变了) .

    它似乎打算而不是打破任何东西?更多信息here

  • 2

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

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

    (向下滚动到主题中的最后一条评论)

    Update

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

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

    Update 2

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

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

  • 1

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


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


    Update 1: 下面描述的行为在npm 5.4.2中得到修复:当前预期的行为在Github issue #17979中列出 .


    Original answer: package-lock.json 的行为在npm 5.1.0中已更改,如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 .

  • 89

    使用新推出的

    npm ci
    

    npm ci承诺给大型团队带来最大利益 . 让开发人员能够“签署”程序包锁定,可以促进大型团队之间更有效的协作,并且能够准确安装锁定文件中的内容,每月可以节省数十甚至数百个开发人员时间,从而使团队更加自由花更多的时间来建造和运送惊人的东西 .

    Introducing npm ci for faster, more reliable builds

  • 3

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

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

  • 240

    你可能有类似的东西:

    "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 . 当你npm安装它抓取小部件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安装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 - C@0.0.1假设新版本没有不修改B的依赖关系 . 当然,新版本的B可能包含新版本的C和任意数量的新依赖项 . 如果这种改变是不可取的,A的作者可以指定对B@0.0.1的依赖 . 但是,如果A的作者和B的作者不是同一个人,那么A的作者就无法说当B完全没有改变时他或她不想引入新发布的C版本 .


    OP问题2:让我看看我是否理解正确 . 您所说的是锁文件指定了辅助依赖项的版本,但仍然依赖于package.json的模糊匹配来确定顶级依赖项 . 这是准确的吗?答案:否.package-lock锁定整个包树,包括package.json中描述的根包 . 如果typescript在package-lock.json中被锁定在2.4.1,它应该保持这种状态直到它被更改 . 并且让我们说明天打字稿发布版本2.4.2 . 如果我签出你的分支并运行npm install,npm将尊重lockfile并安装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

  • 0

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

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

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

  • 5

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

    新的npm ci命令仅从您的锁文件安装 . 如果你的package.json和你的锁文件不同步,那么它将报告错误 . 它的工作原理是丢弃你的node_modules并从头开始重新创建它 . 除了保证你只获得锁文件中的内容之外,当你没有以node_modules开头时,它也比npm安装快得多(2x-10x!) . 正如您可能从名称中获取的那样,我们希望它对持续集成环境来说是一个巨大的好处 . 我们还期望那些使用git标签进行 生产环境 部署的人将获得重大收益 .

  • 1

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

  • 0

    这是一个可以解释事物的场景(使用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)
    
  • 6

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

    “ci”代表“干净安装” . 它将基于package-lock.json文件而不是lenient package.json文件依赖项来安装项目依赖项 .

    它会为你的其他队友提供相同的构建,而且速度也快得多 .

相关问题