- 包管理器
- 开发工具
- CLI
【免费下载链接】pnpm
Fast, disk space efficient package manager
导读
本文围绕 pnpm 对peerDependencies字段校验规则的变更(对应.changeset/peer-scheme-specifiers.md),系统讲解 pnpm 与 pacquet(pnpm 的 Rust 实现,本仓库 pnpm/crates 即其源码)如何从"一刀切拒绝"演进为"按 scheme 解析匹配":peer 声明现在可以写成work:5.x.x、npm:bar@^5、file:../pkg或 git/URL 等带 scheme 的说明符,匹配时按说明符携带的 semver 范围校验,安装时仍由原始说明符选择包。读完你将掌握:哪些 peer 声明格式合法、每种格式如何提取匹配范围、裸name@version为何仍被拒绝,以及这条规则在源码中的完整实现链路。
一、变更背景:peerDependencies 曾经"拒绝一切非常规说明符"
在peerDependencies中,声明格式长期被严格限制为:
- 合法的 semver 范围,如
^5.0.0、>=1.0.0 <2.0.0; workspace:/catalog:引用。
一旦出现其他写法,pnpm 会以错误码ERR_PNPM_INVALID_PEER_DEPENDENCY_SPECIFICATION中止安装,报错信息形如:
The peerDependencies field named '{dep_name}' of package '{project_id}' has an invalid value: '{specifier}'该错误在 Rust 实现中的定义位于 resolve_dependency_tree.rs,其 help 文本明确给出了合法值范围:
The values in peerDependencies should be a valid semver range, a
workspace:/catalog:spec, or a dependency specifier such as a named-registry (<registry>:<version>),npm:,file:, or git/URL spec
也就是说,pnpm 此前虽然已经认识到这些带 scheme 的说明符"应该"合法,但校验逻辑并不接受它们,导致work:5.x.x、npm:bar@^5这类写法直接报错(对应 issue #13095)。本次变更(minor 级)正是补齐了这一缺口:允许 scheme-carrying 说明符进入peerDependencies。
二、变更内容:现在接受哪些 peer 说明符
依据 changeset,peerDependencies现在接受携带 scheme 的依赖说明符,包括以下四类:
| 类别 | 写法示例 | 说明 |
|---|---|---|
| named-registry 说明符 | work:5.x.x、gh:@scope/pkg@^1.0.0 | <registry>:<version>形式,指向配置的命名 registry 别名 |
npm:别名 | npm:bar@^5 | 通过 npm registry 安装别名为其他名字的包 |
file:说明符 | file:../vendor/real-peer | 指向本地目录或文件 |
| git / URL 说明符 | github:user/repo、https://... | 指向 git 仓库或 tarball URL |
其中"named-registry"别名机制在本仓库有完整支撑:用户可通过pnpm-workspace.yaml声明 registry 别名(见 settings.rs),内置别名在 defaults.rs 定义;<alias>:@scope/pkg这类带 scope 的说明符由 named-registry resolver 专门拆分处理(见 resolver_setup.rs)。
三、匹配规则:从说明符中提取 semver 范围
允许写法只是第一步,关键是"如何匹配"——peer 依赖最终要与实际安装的版本做兼容性校验。核心规则(对应 TypeScript 包@pnpm/deps.peer-range,Rust 镜像实现于 peer_range.rs):
| 声明写法 | 提取出的匹配范围 | 说明 |
|---|---|---|
work:5.x.x | 5.x.x | named-registry 说明符取其版本体 |
npm:bar@^5 | ^5 | 取@之后的版本部分 |
| 带 scheme 但无版本 | * | 如file:、git/URL 说明符没有版本信息 |
workspace:1.2.3 | 1.2.3 | 剥掉前缀后剩余部分是范围则直接用 |
workspace:^/workspace:~/workspace: | * | 无版本可提取,视为任意版本 |
| 纯 semver 范围 | 原样返回 | ^5.0.0仍是^5.0.0 |
catalog:引用 | 原样返回 | 由 catalog 解析处理 |
work:^1 \|\| work:^2 | ^1 \|\| ^2 | 多个消费者范围合并后的\|\|联合,逐一提取版本体 |
该规则由 get_peer_version_range 实现,分三步:
- 若含
||,先按联合拆分、对每个子项递归处理再重新 join; - 若是合法 peer 范围(semver /
workspace:/catalog:),交给desugar_workspace_range处理workspace:前缀; - 否则寻找
:(要求位置不在首位),取其后的版本体;若版本体本身是合法范围直接使用,若形如bar@^5则取最后一个@之后的部分;仍解析不出则返回*。
设计意图很清晰:匹配范围只决定"这个版本是否满足 peer 要求",而安装时选哪个包由原始说明符决定。例如file:../vendor/real-peer匹配范围是*(任何版本都满足),但实际安装的永远是../vendor/real-peer这个本地目录。
四、仍被拒绝:裸name@version
规则收紧的另一面是:裸的name@version值(如foo@1.0.0)几乎总是笔误,仍然被拒绝。这类写法没有 scheme 前缀,又不是合法 semver 范围,校验时会被判为非法。
拒绝逻辑见 is_acceptable_peer_spec:一个 peer 值只有在"是合法范围(semver/workspace:/catalog:)"或"包含:"时才被接受。foo@1.0.0两者都不满足,于是报ERR_PNPM_INVALID_PEER_DEPENDENCY_SPECIFICATION。
校验的入口在 importer.rs 的validate_peer_dependencies:它遍历 manifest 中所有DependencyGroup::Peer条目,对每个(dep_name, specifier)调用is_acceptable_peer_spec,一旦发现非法值立即返回InvalidPeerDependencySpecification错误,其中project_id取 manifest 的name字段、缺失时回退到目录路径。注释点明了这样做的原因:拒绝裸name@version是为了避免这类笔误被当作"项目相对路径依赖"(name/version会被解析成目录)而悄悄解析成功。
五、peer 自动安装:scheme 如何参与选择与去重
peer 说明符的 scheme 不仅在校验时起作用,还贯穿"缺失 peer 自动安装"流程:
在 missing_peers.rs,当多个消费者对同一 peer 声明了不同范围时,
classify_missing_peer合并这些范围;注释明确说明:hoisting 一个缺失的必需 peer 时需要保留原始说明符的 scheme(如work:5.x.x),因为要按该说明符去拉取包,而范围合并只负责产生一个可比较的版本区间。在 hoist_peers.rs,
preferred_version_specifier选择去重目标版本时调用get_peer_version_range把 scheme 说明符还原成可比范围:如果某 importer 已解析出一个满足该范围的版本,就直接复用("dedupe onto a preferred version");若存在候选版本但都不满足范围,则回退为用范围本身从 registry 解析,而不是硬装一个 peer 明确拒绝的版本。对于没有版本体的说明符(如catalog:、dist-tag),提取结果不是 semver,则保持"去重到最高版本"的旧行为。
六、测试与验证
本仓库围绕该特性提供了三层测试,可作为行为规范:
Rust 单元测试:peer_range/tests.rs 中
is_acceptable_peer_spec_accepts_scheme_carrying_specifiers验证 scheme 说明符被接受、get_peer_version_range_reduces_a_union_of_scheme_specifiers验证||联合提取;importer_wanted_specs.rs 的accepts_scheme_carrying_peer_specifiers验证整条 importer 解析链路放行这些说明符。CLI 集成测试:cli/tests/suite/peers.rs 覆盖
ERR_PNPM_INVALID_PEER_DEPENDENCY_SPECIFICATION错误码的端到端表现。TypeScript 侧镜像实现:
@pnpm/deps.peer-range对应的校验逻辑在 validatePeerDependencies.ts,配套测试见 validatePeerDependencies.ts。Rust 侧注释明确要求与 TS 侧"保持同步"(keep the two in sync)。
七、实战配置示例
在package.json中声明带 scheme 的 peer 依赖:
{ "name": "my-plugin", "peerDependencies": { "workbox": "work:5.x.x", "bar": "npm:bar@^5", "real-peer": "file:../vendor/real-peer" } }解析行为对应如下:
- 安装时
validate_peer_dependencies先放行全部三项(都含:); - 解析/校验时分别按
5.x.x、^5、*匹配消费者实际安装的版本; - 若某 peer 缺失需要自动安装,则用
work:5.x.x、npm:bar@^5、file:../vendor/real-peer原样作为拉取说明符; - 若有人误写成
"workbox": "workbox@5.0.0",安装立即以ERR_PNPM_INVALID_PEER_DEPENDENCY_SPECIFICATION失败并提示正确写法。
八、影响范围与注意事项
- 本次变更在 changeset 中以 minor 级发布,同时作用于
pnpm(TypeScript 实现)与pacquet(Rust 实现),并同步 bump 了@pnpm/deps.peer-range(minor)、@pnpm/installing.deps-resolver与@pnpm/deps.inspection.peers-checker(patch)。 - 兼容性:原合法的 semver、
workspace:、catalog:行为完全不变;npm:别名、named-registry、file:/git/URL 由"报错"变为"可解析",属向后兼容的放宽;裸name@version的拒绝行为保持不变,不会静默放行。 - 使用前提:named-registry 说明符依赖项目已配置对应的 registry 别名(内置别名见 defaults.rs,用户别名经
pnpm-workspace.yaml声明,参见 settings.rs);file:/git 说明符需要对应目录或仓库可达。 - 匹配语义提醒:
file:/git/URL 说明符的匹配范围是*,意味着 peer 兼容性检查不会因版本号产生警告,但这不代表"任意版本都合理"——peer 的实际内容兼容性仍由声明方自行保证,pnpm 只负责安装时按原始说明符取包。
总结
peer-scheme-specifiers这项变更让 pnpm 的peerDependencies从"严格 semver 白名单"演进为"scheme 感知的说明符解析":named-registry、npm:别名、file:/git/URL 说明符各按其携带的版本体参与匹配(无版本则匹配*),原始说明符始终负责选择安装目标,裸name@version笔误则继续被ERR_PNPM_INVALID_PEER_DEPENDENCY_SPECIFICATION拦截。核心判定与范围提取集中在 peer_range.rs,校验入口在 importer.rs,自动安装的 scheme 保留与去重逻辑分别在 missing_peers.rs 与 hoist_peers.rs,三层测试用例可作为行为规范的最终依据。
- 包管理器
- 开发工具
- CLI
【免费下载链接】pnpm
Fast, disk space efficient package manager
相关推荐
Nativefier URL重写规则文档:规则说明与示例
Nativefier URL重写规则文档:规则说明与示例 一、URL重写功能概述 URL重写(URL Rewrite)是Nativefier中用于修改网页请求U
桌面应用CLI从WaveNet到DiffSinger:awesome-speech-recognition-speech-synthesis-papers语音合成技术深度解析
从WaveNet到DiffSinger:awesome speech recognition speech synthesis papers语音合成技术深度解析
KubeEdge 依赖解析:containerd/platforms 平台说明符语法、规范化与匹配机制全解
KubeEdge 依赖解析:containerd/platforms 平台说明符语法、规范化与匹配机制全解 导读 容器平台(Container Platform
云原生边缘计算物联网容器编排边缘网关
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考