pnpm 修补包失效(Unused Patch)检测修复解析:删除依赖后不再静默更新锁文件
发布时间:2026/9/20 16:56:51 作者:尧图编辑部 阅读量:1,286
检测修复解析:删除依赖后不再静默更新锁文件)
pnpm 修补包失效Unused Patch检测修复解析删除依赖后不再静默更新锁文件【免费下载链接】pnpmFast, disk space efficient package manager项目地址: https://gitcode.com/gh_mirrors/pn/pnpm导读本文讲解 pnpm 在安装过程中针对「修补patch配置的包不再存在」这一场景的最新行为变更当一次安装把某个被修补包的最后依赖者移除后pnpm 不再就地改写锁文件并静默成功而是回退到完整解析通过ERR_PNPM_UNUSED_PATCH错误明确报告未使用的补丁在开启allowUnusedPatches的情况下同一场景会以警告形式告知补丁未被使用。读者读完后将理解该修复的触发路径移除依赖、放宽ignoredOptionalDependencies、添加移除 override、allowUnusedPatches的语义以及底层快速更新fast update机制为何必须把这类场景让给完整解析。变更背景快路径修补与“静默成功”问题pnpm 的patchedDependencies机制允许用户对特定依赖应用补丁补丁文件会被记录在锁文件中。为了性能pnpm 提供了一条“快速更新修补依赖”fast update patched dependencies路径当patchedDependencies配置发生变化时它并不重新解析整个依赖图而是直接把锁文件中的包 key含(patch_hash...)段和指向它们的引用改写到新配置对应的 key。从源码注释可以确认这一设计的前提pnpm11/installing/deps-installer/src/install/tryFastUpdatePatchedDependencies.ts解析器从不读取补丁内容它只是把补丁文件的哈希追加到已解析的包 id 上所以无论走哪条路包与版本的集合都相同。受影响的只是包的 key 和指向它们的引用移动了这属于对已加载锁文件的重写而不是重新解析。问题出在一个特殊子场景如果一次安装移除了某个被修补包的最后一个依赖者例如从package.json移除依赖、放宽ignoredOptionalDependencies、或添加移除 override那么这个包会被剪枝掉补丁也随之失去应用对象。在本次修复之前快速更新路径会直接重写锁文件并静默成功导致锁文件中保留着一个永远用不上的补丁配置——这正是本次变更修复的核心缺陷。本次变更内容记录在仓库的变更集文件 .changeset/unused-patch-after-removal.md 中同时作用于pnpm/installing.deps-installer、pacquet与pnpm三个包均为 patch 级变更。修复后的行为报错还是警告取决于allowUnusedPatches默认行为allowUnusedPatches: falseERR_PNPM_UNUSED_PATCH报错修复后以下任意一种操作导致被修补包被剪枝时安装不再就地更新锁文件并静默成功从依赖中移除最后一个引用被修补包的项目放宽ignoredOptionalDependencies使某个被修补的可选依赖被忽略添加一个移除此包的 override如victim: -。这类安装会回退到完整解析full resolution由解析器抛出ERR_PNPM_UNUSED_PATCH错误明确告知用户存在未使用的补丁。错误信息格式与提示语定义在 pnpm11/patching/config/src/verifyPatches.tsTypeScript 实现与 pnpm/crates/patching/src/verify.rsRust 实现中The following patches were not used: 补丁key1, 补丁key2 提示Either remove them from patchedDependencies or update them to match packages in your dependencies.Rust 侧通过#[diagnostic(code(ERR_PNPM_UNUSED_PATCH))]声明了错误码消息与提示语与 TypeScript 实现完全一致。开启allowUnusedPatches警告而非报错当在pnpm-workspace.yaml中配置allowUnusedPatches: true时同一个安装保持锁文件就地更新即继续走快速更新路径但补丁未被使用的事实不再沉默安装会输出一条警告内容同样为 “The following patches were not used: …”。配置项语义pnpm/crates/config/src/settings.rsallowUnusedPatches来自pnpm-workspace.yaml。当为true时配置了但未匹配任何已安装依赖的补丁产生警告而不是让安装以ERR_PNPM_UNUSED_PATCH失败。默认false——未使用的补丁是错误。也就是说该开关只决定“报错”还是“警告”而本次修复保证无论开关状态如何都不会再静默成功。底层原理快速更新路径为何必须“让位”给完整解析三层守卫guard检查在 pnpm11/installing/deps-installer/src/install/tryFastUpdatePatchedDependencies.ts 的tryFastUpdatePatchedDependencies中任何一层检查失败都会返回false从而保留完整解析路径配置确实变化changedKeys锁文件记录与当前配置的 key 集合无差异时直接返回false分组可解析groupsFromHashes某个 key 的版本段既不是版本也不是 range 时返回false把ERR_PNPM_PATCH_NON_SEMVER_RANGE留给解析器每个配置的补丁都有应用对象everyConfiguredPatchIsApplied这是本次修复的核心守卫。当allowUnusedPatches为false时它检查锁文件中每个配置补丁 key 是否至少被一个已记录的包命中只要有一个未命中就返回false从而让解析器去抛出ERR_PNPM_UNUSED_PATCH。只有allowUnusedPatches: true时该守卫直接放行tryFastUpdatePatchedDependencies.ts。Rust 侧的对应实现是every_configured_patch_is_appliedpnpm/crates/package-manager/src/fast_update_patched_dependencies.rs逻辑一致allow_unused_patches为假时只要all_patch_keys(groups)中存在未被applied_patch_keys命中的 key 就返回false。为什么“重写”无法自行报告这个错误源码注释给出了关键设计约束tryFastUpdatePatchedDependencies.ts一个没有任何可应用对象的补丁就是ERR_PNPM_UNUSED_PATCH这只有解析器才能抛出——verifyPatches在解析器中运行——所以一个会产生该错误的重写必须拒绝执行让解析器去报告。任何丢弃一条边的处理程序都可能产生该错误而不仅仅是补丁配置的变化。allowUnusedPatches把该错误变成解析器发出的警告而警告同样无法由重写路径复现因此修复策略是产生错误的场景一律回退到完整解析产生警告的场景在保留快速路径的同时调用warnUnusedPatches复现警告。警告如何被复现在快速路径完成提交后安装器会调用warnUnusedPatchespnpm11/installing/deps-installer/src/install/index.ts。该函数仅当allowUnusedPatches为true时生效内部以allowUnusedPatches: true调用verifyPatches从而让“补丁未被使用”以警告级别输出tryFastUpdatePatchedDependencies.ts。测试 pnpm11/installing/deps-installer/test/install/patchedDependenciesFastUpdate.ts 验证了这一点移除依赖后requestedPackages为空证明没走解析但 reporter 收到level: warn、消息为 “The following patches were not used: is-positive1.0.0” 的日志。触发场景详解与回退矩阵场景一从依赖中移除被修补包的最后一个引用当parent是唯一引用被修补的victim的项目时移除parent依赖会让victim及其补丁失去应用对象。单元测试lockfileWithAPatchedTransitiveDependency构造了这一场景patchedDependenciesFastUpdate.ts相关断言位于 patchedDependenciesFastUpdate.ts默认配置下tryComposeFastUpdates返回false即回退。场景二移除 override 剪掉被修补包通过overrides: { victim: - }这类移除 override 也能剪掉被修补包。测试 patchedDependenciesFastUpdate.ts 断言该场景同样回退requestPackage抛错即证明最终走了解析。场景三放宽ignoredOptionalDependencies放宽ignoredOptionalDependencies会允许忽略某个被修补的可选依赖效果同样是剪枝。该场景与上述两者在变更集文档中并列列出行为一致默认报ERR_PNPM_UNUSED_PATCH开启allowUnusedPatches后警告。回退矩阵总结场景allowUnusedPatches: falseallowUnusedPatches: true移除依赖导致补丁无应用对象回退完整解析报ERR_PNPM_UNUSED_PATCH就地更新锁文件 警告 “not used”添加移除 override同上同上放宽ignoredOptionalDependencies同上同上移除一个本就未匹配任何包的补丁无需回退补丁被移除没有“未使用”问题无需回退注意最后一行如果移除的补丁本来就没匹配到任何包那么补丁配置随之消失不存在未使用补丁因此不需要allowUnusedPatches也无需回退——对应测试 patchedDependenciesFastUpdate.ts。测试验证仓库中的证据链Rust CLI 集成测试pnpm/crates/cli/tests/suite/patch/removal.rs 提供了端到端验证unused_patch_fails_with_err_pnpm_unused_patchremoval.rs配置两个补丁、其中一个无匹配包install失败stderr 包含ERR_PNPM_UNUSED_PATCH及未使用的补丁 keyis-negative1.0.0unused_patch_warns_when_allow_unused_patches_is_setremoval.rs开启allowUnusedPatches后install成功输出同时包含 “not used” 与补丁 key此外还覆盖了--config.allow-unused-patchestrueCLI 覆盖与PNPM_CONFIG_ALLOW_UNUSED_PATCHES环境变量的透传removal.rs以及过滤安装--filter下不完整选择不触发未使用补丁检查的特殊行为removal.rs。NAPI 侧测试pnpm/crates/napi/src/install/tests/reporting.rs 断言错误 reason 包含ERR_PNPM_UNUSED_PATCH且allowUnusedPatches能让安装通过。配置解析allowUnusedPatches的配置来源包括pnpm-workspace.yamlsettings.rs、workspace_yaml/settings.rs、已知设置白名单known_settings.rs、旧版pnpm字段兼容legacy_pnpm_field.rs以及 CLI 覆盖apply.rs。对应的 NAPI 类型声明见 pnpm/npm/napi/index.d.ts。修复的边界条件哪些场景仍需要完整解析即便补丁配置未变化只要任何“丢弃一条边”的处理程序可能产生未使用补丁就必须回退。快速更新路径还会在以下情况主动让位tryFastUpdatePatchedDependencies.ts被修补包被其他快照以 peer 依赖方式引用peer 后缀内嵌了被修补包的 depPath重写 peer 后缀需要同时重写其依赖者仅凭锁文件数据无法完成peer 后缀被压缩为哈希超过peersSuffixMaxLength时 pnpm 会用短哈希代替哈希形式的 peer 后缀无法被检查必须交还解析器被修补包来自命名 registry、git 或 tarball 引用这些 key 的匹配无法从 key 本身复现只要配置里有针对该包的补丁或 key 上已有补丁哈希就回退。Rust 侧对应实现为plan_rekeys与peer_suffixes_survive_rekeysfast_update_patched_dependencies.rs测试用例覆盖于 patchedDependenciesFastUpdate.ts。结语与实操建议本次变更把“移除被修补包的最后一个依赖者”从快速路径的静默成功中拉出来交还给完整解析去如实报告。对你的日常开发而言影响与建议如下默认配置下若pnpm install突然报ERR_PNPM_UNUSED_PATCH先检查是否有依赖被移除、ignoredOptionalDependencies是否放宽、或是否有移除 override 剪掉了被修补包按错误提示从patchedDependencies中移除对应补丁或更新补丁使其匹配仍存在的依赖。若希望保留补丁配置例如计划后续重新引入该依赖可在pnpm-workspace.yaml中设置allowUnusedPatches: true让安装以警告继续同时留意 “The following patches were not used” 警告。该开关不影响修复本身修复保证任何情况下都不再静默区别只在于报错还是警告。【免费下载链接】pnpmFast, disk space efficient package manager项目地址: https://gitcode.com/gh_mirrors/pn/pnpm创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考