新闻详情

新闻详情

首页 / 资讯中心 / 详情

pnpm(pacquet)Rust 版 hoisted 布局的多级提升(Multi-Level Hoisting)实现路线解析

发布时间:2026/9/20 9:33:42来源:尧图网络
pnpm(pacquet)Rust 版 hoisted 布局的多级提升(Multi-Level Hoisting)实现路线解析
pnpmpacquetRust 版 hoisted 布局的多级提升Multi-Level Hoisting实现路线解析【免费下载链接】pnpmFast, disk space efficient package manager项目地址: https://gitcode.com/gh_mirrors/pn/pnpm导读nodeLinker: hoisted是 pnpm 提供的一种类扁平化node_modules安装布局。在 pacquet 将其 hoister 移植到 Rust 的过程中pnpm/crates/real-hoist已能正确运行yarnpkg/nm的提升算法但还缺少最后一个结构性能力per-importer按项目的提升根hoisting roots即多级输出。本文基于仓库中的实现计划文档 MULTI_LEVEL_HOISTING.md结合real-hoist、deps-restorer与config等 crate 的源码讲清楚当前单层提升的实现边界、多级提升要补齐的差距、具体实现步骤、风险与验证方式。读完你会理解为什么嵌套放置在语义上正确但布局密度有差距以及如何在 Rust 移植中复刻上游hoistTo的递归提升行为。背景从 pnpm 的 hoisted 布局到 Rust 移植在nodeLinker: hoisted模式下pnpm 把整个依赖图拍平到一个共享的node_modules目录中——所有不冲突的传递依赖都被提升到根目录只有发生版本冲突的名称才保持嵌套。这个提升算法源自 Yarn Berry 的yarnpkg/nmhoister。pacquet 用 Rust 重写了它代码位于 pnpm/crates/real-hoist/src/lib.rs其模块注释明确说明这是对yarnpkg/nm提升算法的直接移植输入侧用HoisterTree表示依赖树每个节点携带name暴露别名、ident_name真实包名、reference版本/快照键、peer_names该节点拒绝越过的 peer 名以及dependency_kindRegular/Workspace/ExternalSoftLink三类输出侧用HoisterResult表示提升后的结果图并额外记录了hoisted_dependencies被提升到祖先目录的名称映射与decoupled标记共享节点在每条路径上的单亲拷贝状态入口函数hoist(lockfile, opts)负责把 pnpm lockfile 翻译成以虚拟.为根、每个 workspace importer 为子节点的树然后调用nm_hoist。在安装管线的下游hoisted_dep_graph.rs 的lockfile_to_hoisted_dep_graph调用这个hoist拿到目录形状再用 walkerwalk_deps位于同文件的walk子模块把它展开成按绝对目录为键的依赖图——这正是 hoisted 布局与 isolated 布局的关键差异同一包可以因名称冲突占据多个目录提升决策是按目录粒度做出的而不是按 depPath 粒度。现状盘点今天已具备且绝不能回退的能力计划文档首先划出了红线多级提升不能破坏以下三项已实现行为。1. 非根 importer 无条件挂入共享树v11 parity在 hoist() 中所有非根 workspace importer 被无条件添加为虚拟.根的Workspace子节点这与 pnpm v11 的hoist()一致对应上游 issue pnpm/pnpm#12899。这一点之所以关键是因为它决定了跨项目版本去重与冲突嵌套只有把整个 workspace 视为一棵树hoister 才能做跨项目去重冲突的版本会嵌套在子树中的原有位置由 walkerwalk_deps在那里物化出来hoisted_dep_graph.rs 中build_dep_graph对每个 importer 子树递归产出目录层级。测试 multi_importer_lockfile_emits_workspace_children 验证了这一点packages/foo、packages/bar会被编码为packages%2Ffooworkspace:packages/foo这样的Workspace子节点。而 hoist_workspace_packages_false_keeps_workspace_children 进一步确认树成员资格不取决于hoist_workspace_packages开关——该开关只控制根目录是否为 workspace 包本身生成 name-link即 v11 的hoistedWorkspacePackages如果拿它去卡成员资格会静默丢掉所有仅属于 importer 的依赖。2. 提升边界hoisting borders已按一层深度生效HoistOpts中的hoisting_limits字段类型为HoistingLimits BTreeMapString, BTreeSetStringper-locator 的名称黑名单与 yarn 的hoistingLimits选项一一对应。在hoist_subtree中under_border标志和ctx.border_names检查共同实现边界语义名字在边界集合中的节点其子孙不得越过它继续提升而边界节点自身不受影响。3. workspace 包 name-link 是独立形状hoist_workspace_packages产生的 name-linkv11 的hoistedWorkspacePackages是已经实现的独立能力不依赖多级提升这项工作的落地。差距多级提升到底多做了什么既然边界已经生效那多级补的是什么关键在于边界内部的行为差异在一个被边界圈住的子树里例如hoistingLimits: workspaces下的 workspace importer上游仍然会内部提升importer 的传递依赖会被扁平化到 importer 自己的node_modules而不是停留在它们自然的树深位置。而 pacquet 目前让它们保持嵌套。换句话说上游的hoistTo会把每个标记为hoist root的节点hoistingLimits: workspaces下的 workspace、dependencies下的直接依赖都当作一次新的提升起点跑一遍定点提升从而生成一棵每个被圈住的子树都各自扁平化的多层树而 pacquet 现在只对唯一的虚拟.根执行hoist_into_root。需要强调一个关键事实嵌套放置是解析正确的。Node 的模块解析会向上层目录查找所以嵌套的依赖仍然能被正确找到。两者的差异纯粹是布局密度和去重密度而不是可解析性——这正是该差距到目前为止可以带着发布的根本原因。测试 version_conflict_keeps_loser_at_parent 展示了当前行为的一个侧面冲突失败方如b2.0.0保留在父节点c之下而c自己的冲突子d2.0.0仍被提升到c这一层——即以当前根为起点的内部提升其实已经部分存在缺的是把这种提升递归推进到每个边界内部。实现草图四条路径逐条拆解计划文档给出了清晰的实施步骤下面结合源码逐一展开。第 1 步get_hoisting_limits已就绪保持现有形状用户配置层的hoistingLimits是pnpm-workspace.yaml里的一个枚举定义在 setting_types.rs三档语义如下模式含义效果示例A → B → CA 为 workspace 包none默认尽可能提升/node_modules/B、/node_modules/Cworkspaces只提升到每个 workspace 包/packages/A/node_modules/{B,C}dependencies只提升到每个 workspace 包的直接依赖/packages/A/node_modules/B/node_modules/C该枚举在nodeLinker: isolated下无效。把用户模式翻译成 hoister 消费的 per-locator 边界映射border map的函数是 get_hoisting_limits注意实现位于deps-restorercrate计划文档中写的package-manager/src/hoisting_limits.rs是当时规划时的路径none直接返回空映射根边界累计在.下包括根 importer 自身直接依赖的别名以及每个百分号编码后的非根 importer iddependencies模式下还会为每个非根 importer 生成{encoded_id}workspace:{importer_id}的 per-importer 边界条目集合内容是该 importer 的直接依赖别名跨dependencies、devDependencies、optionalDependencies三组收集见 collect_direct_dep_names。源码注释明确写道当前 hoister 只向单一根 importer 提升所以只消费.条目dependencies模式发出的 per-importer 条目是为 parity 而产出待多级提升落地后才会真正承重。这正是多级工作的第 1 步保持这个形状不变多级落地后它自然生效。第 2 步在nm_hoist中递归执行 per-root 提升当前驱动流程nm_hoist → hoist_to的结构是把输入树convert成HoisterResult图对.根调用hoist_into_root跑定点提升递归进入每个存活的子节点把子节点当作下一级提升根继续。注意hoist_to的递归其实已经存在——它会在path_locators集合当前递归路径上的所有根 locator用于切断环的守卫下对每个留下的子节点再次hoist_into_root。真正的缺口在于递归是无差别进行的没有先判断该子节点是否是hoist root。上游hoistToyarnpkg-nm/sources/hoist.ts 中hoistTo实现及其hoistIdents/ per-root 偏好映射的逻辑是根定点结束后对每个存活的、自身是 hoist root 的子节点Workspace类型节点或名字位于带 per-locatorhoisting_limits条目的边界集合中的节点以它为根、带上它自己的 locator 边界集合再跑一遍hoist_into_root。也就是说多级实现要在hoist_to的递归入口处补上hoist root 判定Workspace类型的子节点天然是 hoist root名字落在opts.hoisting_limits.get(locator)集合中的节点也是。前者对应hoistingLimits: workspaces后者对应dependencies模式此时get_hoisting_limits产出的 per-importer 条目正好提供了每个 importer 自己的边界集合。判定通过后以该节点为根调用hoist_into_root并用其 locator 对应的边界集合替换当前上下文——上游的参考实现在hoist.ts的hoistTo。第 3 步walker 无需结构性改动walk_depshoisted_dep_graph.rs 的walk子模块已经递归进入Workspace类型的子节点并把 hoister 决定好的位置物化为目录。既然多级提升只是让 hoister 在子树内部多做扁平化walker 拿到的结果图仍是一棵可递归遍历的树所以计划文档明确The walker needs no structural change。第 4 步移植测试计划文档点名的待移植测试有两类上游hoist.test.ts中hoistingLimits的用例workspaces与dependencies两种模式——这些用例直接刻画多级输出形状CLI 已知失败用例partial_install_persists_hoisted_map对应上游 issue pnpm/pacquet#433——它同时依赖 re-hoist 合并见下文风险部分。仓库现有测试已经为多级形态埋好了观测点。例如 build_hoist_ident_map_skips_root_peer_names 的注释直接点明the shape a per-importer hoisting root would take once those land一旦 per-importer 提升根落地这就会是它呈现的形状——该测试直接驱动build_hoist_ident_map构造一个带 peer 声明的根验证根声明为 peer 的名字不会进入 ident 偏好映射这正是 per-root 提升时每个子树根都要重算的映射语义。另外 multi_round_unlocks_peer_friendly_hoist_after_blocker_moves 验证了多轮定点提升在阻塞者移走后解锁 peer 友好提升的能力这与 per-root 递归的多轮收敛是同一套机制。风险与工作顺序全局状态必须作用域化计划文档明确列出两个实施风险与源码一一对应风险一ident 偏好映射与 per-pass ident shift 目前是全局的在hoist_into_root中每个提升根都会先调用build_hoist_ident_map(root)构造 per-name 的候选 ident 排序最常用者优先然后在多轮循环里执行per-pass ident shift某个名字有多个候选 ident、且首选 ident 始终无法到达根时就丢弃首选、提升下一个候选让后续轮次可以放置次优版本对应 yarnhoistTo中的idents.shift()循环。此外HoistCtx中的used来自get_used_dependencies记录了子树里已经被提升到祖先目录解析的名称防止不同版本抢占同名槽位。问题在于这些映射目前是整个调用共享的。一旦 per-root 递归落地每个提升根都必须有自己独立的偏好映射与 ident shift 状态——上游的做法是在每次hoistTo时重新执行buildPreferenceMap(rootNode)重建这些映射。Rust 移植需要把hoist_ident_map、used等从每次hoist_to调用时构造改为每次hoist_to以新根进入时按该根的子树重新构造否则一个子树的 ident shift 会污染另一个子树的选择。风险二与 partial-installre-hoist 合并的依赖关系partial_install_persists_hoisted_map用例同时依赖 re-hoist 合并把上一次安装的 hoisted 位置合并进新一轮布局。计划文档给出的顺序建议是只有当 re-hoist 合并希望与多级提升同版本发布时才把它排在 pnpm/pacquet#433 的 partial-install 工作之后否则两条实现路径相互独立可以并行推进。验证方式与观察点要验证多级提升是否按预期落地可以从三个层面观察单元测试层面real-hoist的测试目录pnpm/crates/real-hoist/src/tests已按behavior.rs深度链平铺、别名解析、dependencies.rs版本冲突嵌套、workspace_settings_*多 importer 输出、peer 偏好映射分好了主题新增的hoistingLimits多级用例应归入workspace_settings_*系列边界映射层面deps-restorer的 hoisting_limits.rs 测试见同目录tests/验证get_hoisting_limits在workspaces/dependencies两种模式下产出的 locator 键与名称集合多级落地后这些条目从parity 形状转为承重输入CLI 层面pnpm/crates/cli/tests/suite/hoisted_node_linker.rs是 hoisted 布局的集成测试入口partial_install_persists_hoisted_map这类已知失败用例应在多级 re-hoist 合并完成后转绿。小结多级提升是 pacquet 的real-hoistcrate 对齐上游yarnpkg/nmhoister 的最后一块结构性拼图单层提升已经解决了拍平到根和边界一层深度的问题并且嵌套放置本身解析正确、可以随版本发布多级提升要做的是把内部扁平化从根递归推广到每个 hoist root让hoistingLimits: workspaces/dependencies下每个被圈住的子树也获得自己的定点提升。实现的关键不在 walker它无需改动而在 hoister 内部per-root 递归的 hoist root 判定、每个根独立的偏好映射与 ident shift 状态以及与 partial-install 的 re-hoist 合并的协调顺序。对于想在 Rust 侧理解或贡献 hoisted 布局的开发者real-hoist/src/lib.rs 的hoist_to→hoist_into_root→hoist_subtree调用链加上 deps-restorer/src/hoisting_limits.rs 的边界映射就是完整的阅读与改造地图。【免费下载链接】pnpmFast, disk space efficient package manager项目地址: https://gitcode.com/gh_mirrors/pn/pnpm创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

Java高并发抽奖系统:MySQL事务+Redis限流+加权算法实战 2026/9/20 10:24:53

Java高并发抽奖系统:MySQL事务+Redis限流+加权算法实战

简介:这是一套开箱即用的Java抽奖系统实战项目,面向Java初学者与SpringBoot开发者,解决活动运营中大转盘抽奖功能从设计到落地的全流程问题。资源包含完整前后端源码、MySQL数据库脚本、Redis缓存配置及核心抽奖算法实现,覆盖用户…

阅读更多 →
AIGC短片制作全流程:从脚本到成片的实战工作流 2026/9/20 10:24:53

AIGC短片制作全流程:从脚本到成片的实战工作流

前两天我刚刚把一段不到两百字的脚本,变成了一条50多秒的AI短片。从写脚本到出成片,总共花了大概三个小时。放在以前,这种带完整剧情、有对白、有情绪转折的短片,从写稿、找演员、约场地到正式拍摄,没有一周时间根本下…

阅读更多 →
Coroot 开源可观测平台入门指南:4 步跑通部署、服务地图、火焰图与 SLO 告警 2026/9/20 10:24:53

Coroot 开源可观测平台入门指南:4 步跑通部署、服务地图、火焰图与 SLO 告警

Coroot 开源可观测平台入门指南:4 步跑通部署、服务地图、火焰图与 SLO 告警 【免费下载链接】coroot Coroot is an open-source observability and APM tool with AI-powered Root Cause Analysis. It combines metrics, logs, traces, continuous profiling, and …

阅读更多 →
技术融合预测:从专利多特征到链接预测与价值评估 2026/9/20 10:24:53

技术融合预测:从专利多特征到链接预测与价值评估

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
CANN ops-math 数学算子解析:CumulativeLogsumexp 累积 log-sum-exp 的原理、参数与 Ascend 950 实现 2026/9/20 10:24:53

CANN ops-math 数学算子解析:CumulativeLogsumexp 累积 log-sum-exp 的原理、参数与 Ascend 950 实现

CANN ops-math 数学算子解析:CumulativeLogsumexp 累积 log-sum-exp 的原理、参数与 Ascend 950 实现 【免费下载链接】ops-math 本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。 项目地址: https://gitcode.com/cann/ops-math …

阅读更多 →
SpringBoot图书借阅系统高校实战指南 2026/9/20 10:21:52

SpringBoot图书借阅系统高校实战指南

简介:这是一套基于Spring Boot开发的图书借阅管理系统完整毕业设计项目,面向计算机专业本科生及Java初学者,解决高校或小型图书馆场景下的图书登记、用户管理、借阅归还、库存统计等核心业务需求。资源包共133个文件,涵盖22个Java…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞