pnpm 冻结锁文件安装清理不可达快照修复--frozen-lockfile下幽灵依赖与生命周期脚本重复执行【免费下载链接】pnpmFast, disk space efficient package manager项目地址: https://gitcode.com/gh_mirrors/pn/pnpm导读本指南聚焦 pnpm 在 Rust 重写实现pnpm/crates与pnpm11等目录中的一个具体修复当项目使用pnpm install --frozen-lockfile或--frozen-lockfile同族标志安装依赖时若pnpm-lock.yaml中已没有任何项目再依赖某个包pnpm 现在会将该包从node_modules中删除。此前这个不可达锁文件快照会在每次运行时被重新安装导致其生命周期脚本install/postinstall反复执行同时verifyDepsBeforeRun也会在每次pnpm run/pnpm exec之前额外触发安装。读完本文你将理解该修复的来龙去脉、它在当前仓库中的实现位置、相关的配置参数verifyDepsBeforeRun以及可复现的验证方法。变更背景问题 #14891 的修复该变更为 pnpm 仓库中的一次补丁changeset对应上游 issue pnpm/pnpm#14891其核心结论是pnpm install --frozen-lockfile现在会在没有任何项目再依赖某个包时将该包从node_modules中移除。此前这种包会在每次运行时被重新安装从而每次都重新执行其生命周期脚本。verifyDepsBeforeRun此前也会在每次pnpm run和pnpm exec之前执行安装。changeset 文件 .changeset/fix-unreachable-lockfile-snapshots.md 以pacquet: patch形式声明这是一个针对 pacquetpnpm 的 Rust 实现代号的 patch 级修复即行为修正而非新功能。在继续阅读源码细节之前先明确三个关键术语不可达锁文件快照unreachable lockfile snapshotpnpm-lock.yaml中记录的某个包快照在当前所有项目workspace 中的 importer的依赖声明中已不再被引用。它仍然残留在锁文件里可能是历史安装留下的旧实现会在安装时把它重新物化到node_modules。冻结锁文件安装--frozen-lockfile以及--frozen-lockfile同族的--frozen-lockfile/--lockfile-only等要求完全按照锁文件安装不进行任何版本解析变更CI 环境中常用--frozen-lockfile保证可复现构建。生命周期脚本包在安装时执行的preinstall/install/postinstall脚本反复执行它们既浪费时间也可能带来副作用例如下载二进制、修改全局状态。修复前的症状每次安装都重新执行生命周期脚本在修复之前当pnpm-lock.yaml中存在某个快照、但当前没有任何项目依赖它时pnpm install --frozen-lockfile的行为如下解析锁文件发现不可达快照由于冻结模式不修改锁文件旧实现把该快照视为仍需安装的依赖把它重新物化到node_modules物化过程中执行该包的生命周期脚本由于每次运行都重复上述流程每次pnpm install --frozen-lockfile都会重复执行该包的生命周期脚本。这带来两类实际问题重复副作用例如二进制下载、缓存预热、环境探测类postinstall脚本被反复执行构建不确定性CI 中出现每次安装都做同样的事的冗余工作且与冻结锁文件所承诺的确定性语义相悖。修复后pnpm 会在本次安装中识别出该包已不再被任何项目依赖直接将其从node_modules中移除不再安装、不再执行脚本。修复后的行为移除不可达包保留锁文件不变修复后的--frozen-lockfile语义可以归纳为移除动作不再被任何项目依赖的包会被从node_modules中移除而不是重新安装锁文件不变冻结模式下依然不修改pnpm-lock.yaml不可达快照的清理是node_modules层面的动作生命周期脚本不再重复因为包不再被安装其install/postinstall等脚本自然不再执行verifyDepsBeforeRun不再重复触发安装pnpm run/pnpm exec前的依赖一致性检查见下文不会再因为这类幽灵快照而判定不同步、进而触发额外安装。与pnpm prune的关系需要注意的是该修复解决的是冻结安装过程中的清理问题与显式的pnpm prune按锁文件修剪node_modules不同本修复发生在常规install --frozen-lockfile流程内属于安装器自身的一致性维护而不是一个独立的清理命令。从仓库中可以看到安装阶段有一系列相关行为如 merged-branch-lockfiles-drop-removed-deps.md 中合并分支后的锁文件会丢弃已移除依赖的 changeset说明 pnpm 团队在持续打磨锁文件与node_modules之间的一致性这一主题。仓库中的实现证据从 changeset 到源码Rust 实现pacquet中的对应代码当前仓库的 Rust 实现中与verifyDepsBeforeRun直接对应的源码位于pnpm/crates/cli/src/cli_args/verify_deps.rs实现了verify_deps_before_run门控。注释明确写到The verify-deps-before-run gate: beforepnpm run/pnpm execexecute anything, verify thatnode_modulesis in sync with the lockfile and apply the configured action — spawn an install, prompt for one, error out, or warn. pnpms counterpart isrunDepsStatusCheckinexec/commands.即 pnpm 的 TS 实现对应exec/commands中的runDepsStatusCheckpnpm/crates/package-manager/src/optimistic_repeat_install/deps_status.rs实现了check_deps_status_before_run这一预运行依赖状态检查它复用了安装快速路径optimistic repeat install的新鲜度检查但做了差异化处理pnpm/crates/cli/tests/suite/verify_deps_before_run.rs端到端测试覆盖默认install动作会在脚本执行前先安装、生产模式安装install --prod被正确复现等场景。deps_status.rs中RunDepsStatus枚举的三种结果值得展开pub enum RunDepsStatus { UpToDate, // 一切同步脚本直接执行 SkippedPnp, // node-linkerpnp 下无法检查警告后直接运行 Outdated { issue: String, install_args: VecString }, // 检测到漂移 }当Outdated时install_args_from_state会根据上次安装记录的依赖组--prod/--dev/--no-optional重建pnpm install参数保证补装的安装与项目上次的安装方式一致——这正是修复后不会因为不可达快照而重复安装的关键门控判定同步后直接放行脚本执行。冻结锁文件在 CLI 层的接线--frozen-lockfile标志的解析与分发位于pnpm/crates/cli/src/cli_args/install.rs安装命令参数入口pnpm/crates/cli/src/cli_args/install/arguments.rs具体参数解析pnpm/crates/cli/tests/suite/ci_frozen_lockfile.rs面向 CI 冻结锁文件场景的端到端测试。这些文件共同保证了--frozen-lockfile在解析、分发、以及 CI 场景下的行为一致性。安装快速路径OptimisticRepeatInstallCheckverifyDepsBeforeRun之所以能快速判定同步是因为它复用了安装器的乐观重复安装检查optimistic repeat installpnpm/crates/package-manager/src/optimistic_repeat_install.rs 定义了OptimisticRepeatInstallCheck与check_optimistic_repeat_install当一切未变化时安装直接输出 Already up to datedeps_status.rs中的check_deps_status_before_run是它的预运行孪生它无论optimisticRepeatInstall配置如何都会运行、从不把本地 file 依赖视为过期、忽略dev/optional/production漂移脚本始终以默认组执行但会比对配置依赖并以面向用户的措辞报告漂移。从源码结构可以推断修复后不可达锁文件快照不会再让上述检查判定为 Outdated因而不会再在每次pnpm run/pnpm exec前触发额外安装这与 changeset 的表述完全一致。配置项verifyDepsBeforeRun该变更同时把verifyDepsBeforeRun的行为纳入修复范围。该配置控制pnpm run/pnpm exec之前是否检查node_modules与锁文件的同步状态以及不同步时如何处置。可用取值与行为如下依据 pnpm/crates/cli/src/cli_args/verify_deps.rs 的match分支取值行为备注install默认自动派生一次pnpm install然后再执行脚本派生安装带--verify-deps-before-run-install --use-stderr并沿用--reporter设置失败时透传子进程退出码prompt交互式询问用户是否执行安装非交互环境stdin 非 TTY下报ERR_PNPM_VERIFY_DEPS_BEFORE_RUN/CannotPrompt错误用户中断Esc / Ctrl-C时以退出码 1 退出error直接报错提示运行pnpm install错误码ERR_PNPM_VERIFY_DEPS_BEFORE_RUN帮助信息为 Run pnpm installwarn打印告警后继续执行脚本告警文本形如 Your node_modules are out of sync with your lockfile. ...true/false布尔值true只做检查不采取动作false直接跳过检查与node-linkerpnp组合时检查被跳过并输出 verify-deps-before-run does not work with node-linkerpnp 告警如何配置在pnpm-workspace.yaml中设置# pnpm-workspace.yaml verifyDepsBeforeRun: install # 或 prompt / error / warn / true / false或在命令行直接传递pnpm config set verify-deps-before-run install pnpm run --verify-deps-before-runprompt build端到端测试如何验证该行为仓库中提供了直接可读的测试佐证pnpm/crates/cli/tests/suite/verify_deps_before_run.rs文件头注释说明其镜像了pnpm11/pnpm/test/verifyDepsBeforeRun/下的 TypeScript 场景。其中default_install_action_installs_before_running_the_script验证默认install动作下新项目的首次run会先派生安装、再执行脚本断言 marker 文件存在且node_modules已生成install_action_reruns_a_production_only_install验证派生安装会复现上次记录的依赖组如install --prod从而保证生产模式安装下pnpm run依然可用对应的 TypeScript 侧场景位于pnpm11/pnpm/test/verifyDepsBeforeRun/目录。这些测试从行为契约层面锁定了本 changeset 的修复语义预运行检查必须与锁文件/node_modules的真实状态一致且不能因为不可达快照而误判。复现与验证指南验证不可达快照不再重复安装准备一个项目安装一个包例如is-number生成pnpm-lock.yaml从package.json移除该依赖但保留锁文件中对应快照不动例如用pnpm install生成锁文件后手动编辑package.json并在冻结模式下安装运行pnpm install --frozen-lockfile观察行为修复前该包的快照仍会被重新物化install/postinstall脚本每次都会执行修复后该包被从node_modules移除锁文件不变且不再执行其生命周期脚本。验证verifyDepsBeforeRun# 在锁文件与 node_modules 不同步时运行脚本 pnpm run --verify-deps-before-runwarn build # 预期打印告警后仍执行脚本 pnpm run --verify-deps-before-runerror build # 预期报 ERR_PNPM_VERIFY_DEPS_BEFORE_RUN pnpm run --verify-deps-before-runinstall build # 预期先派生安装再执行脚本注意非交互终端下使用prompt会失败无法弹确认框应改用warn/error/install。相关 changeset 与后续演进本修复并非孤立变更仓库.changeset/目录中的一系列相关条目共同勾勒出锁文件 /node_modules一致性的演进脉络drop-removed-dependencies-without-resolving.md不解析直接丢弃已移除依赖merged-branch-lockfiles-drop-removed-deps.md合并分支后的锁文件会丢弃已移除依赖unused-patch-after-removal.md依赖被移除后清理不再使用的 patchfrozen-lockfile-package-manager-repair.md 与 frozen-lockfile-package-manager-deps.md冻结锁文件场景下包管理器自身依赖的修复。小结本次 patch 修复回答了一个长期困扰 CI 用户的问题--frozen-lockfile本应保证只按锁文件安装却因为不可达快照的存在每次运行都重复安装一个已无引用的包并反复执行其生命周期脚本。修复后 pnpm 会在安装阶段直接清理这类包同时让verifyDepsBeforeRun不再因此误判同步状态、在每次pnpm run/pnpm exec前重复安装。其实现证据分布在 pnpm/crates/cli/src/cli_args/verify_deps.rs、pnpm/crates/package-manager/src/optimistic_repeat_install/deps_status.rs 以及对应的端到端测试 pnpm/crates/cli/tests/suite/verify_deps_before_run.rs 中读者可沿着这些路径进一步阅读源码细节。【免费下载链接】pnpmFast, disk space efficient package manager项目地址: https://gitcode.com/gh_mirrors/pn/pnpm创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考