pnpm11 版本策略引擎 @pnpm/config.version-policy 完全指南:解析、匹配与发布年龄策略实现

pnpm11 版本策略引擎 @pnpm/config.version-policy 完全指南:解析、匹配与发布年龄策略实现 pnpm11 版本策略引擎 pnpm/config.version-policy 完全指南解析、匹配与发布年龄策略实现【免费下载链接】pnpmFast, disk space efficient package manager项目地址: https://gitcode.com/gh_mirrors/pn/pnpm本文以pnpm11/config/version-policy/模块为核心讲解 pnpm11 如何将minimumReleaseAgeExclude、trustPolicyExclude等用户配置解析为可执行的包版本策略Package Version Policy从name1.2.3 || 1.2.4这类规则串的语法解析到基于pnpm/config.matcher的包名通配匹配再到minimumReleaseAge截止时间的推导与排除规则合并。读完本文你将掌握该模块的公开 API、规则语法、错误码约定以及它在解析器resolver中的实际调用方式并能直接在自己的 pnpm 配置或二次开发中正确使用版本策略规则。一、模块定位一个纯函数式的版本策略解析与求值单元pnpm/config.version-policy是 pnpm11 配置体系中的一个独立子包位于仓库 pnpm11/config/version-policy其package.json中的描述非常精确Parses and evaluates package version policy specs and produces package-version matchers也就是说它的职责是两件事解析Parse把用户书写的版本策略规则字符串例如axios1.12.2 || 1.12.3解析成结构化的规则求值Evaluate根据某个包名返回该包是否被策略命中命中时若规则指定了精确版本则返回命中的版本列表。模块不依赖任何文件系统或网络状态核心逻辑全部集中在 src/index.ts约 178 行依赖面极小依赖作用pnpm/config.matcher提供createMatcher实现包名的通配符匹配pnpm/error提供PnpmError用于抛出带错误码的解析错误pnpm/types提供PackageVersionPolicy类型定义semver校验精确版本号、对版本列表做排序模块的engines要求 Node.js22.13采用 ESM 输出type: modulemain指向lib/index.js。安装方式即 README 中给出的pnpm add pnpm/config.version-policy二、核心类型PackageVersionPolicy在继续之前先看模块所服务的类型契约。该类型定义在 pnpm11/core/types/src/config.tsexport type PackageVersionPolicy (pkgName: string) boolean | string[]这是一个谓词函数类型接收包名返回boolean | string[]返回true该包被策略整体命中策略未限定具体版本或规则中出现了不带版本号的裸包名模式返回string[]该包被命中且返回所有被策略精确列出的版本号返回false该包未被任何规则命中。这个函数类型贯穿 pnpm11 的解析与安装链路在 resolveDependencies.ts 等处被作为publishedByExclude等字段的载体传递。三、规则语法如何书写一条版本策略模块通过parseVersionPolicyRulesrc/index.ts#L141-L166解析单条规则。一条规则的完整形态是packageName[exactVersion[ || exactVersion...]]3.1 包名部分裸包名axios、lodash命中该包的所有版本作用域包babel/core。解析器对作用域包做了特殊处理——的查找从索引 1 开始isScoped ? pattern.indexOf(, 1) : pattern.indexOf()避免把作用域标志scope误当作版本分隔符通配符模式is-*、ax*。包名部分会被交给pnpm/config.matcher的createMatcher编译成正则匹配器。通配符模式的解析实现在 matcher/src/index.ts使用escapeStringRegexp转义后把*转换为通配语义因此is-*能同时命中is-odd、is-even。3.2 版本部分可选版本之间用||连接构成精确版本联合exact version union每个版本必须是通过semver.valid()校验的精确版本src/index.ts#L168-L178不允许使用^、~、等范围语法版本两侧的空格会被忽略pkg1.0.0||1.0.1 || 1.0.2与pkg1.0.0 || 1.0.1 || 1.0.2等价。3.3 两条硬性校验解析失败时会抛出带错误码的PnpmError错误码触发条件示例INVALID_VERSION_UNION版本部分不是合法的精确版本联合lodash^4.17.0、react18.0.0NAME_PATTERN_IN_VERSION_UNION包名含*通配符同时又写了版本联合is-*1.0.0这两条规则在测试 test/index.ts#L39-L43 中有完整覆盖^4.17.0、~4.17.0、18.0.0均会触发Invalid versions union而is-*1.0.0会触发Name patterns are not allowed with version unions。设计意图很明确通配包名 精确版本组合语义过于模糊哪个版本匹配哪个包因此直接拒绝。四、策略工厂createPackageVersionPolicy4.1 输入形态与规则编译createPackageVersionPolicysrc/index.ts#L10-L18接受string[] | string两种形态。注释明确说明当配置以 YAML 标量书写或来自PNPM_CONFIG_*环境变量时会以单个字符串到达这里因此模块对两种形态都做了兼容typeof patterns string ? [patterns] : patterns。每条规则被编译成内部的VersionPolicyRuleinterface VersionPolicyRule { nameMatcher: Matcher // 来自 pnpm/config.matcher 的包名匹配器 exactVersions: string[] // 精确版本列表空数组表示命中所有版本 }最终返回绑定好规则列表的evaluateVersionPolicy函数src/index.ts#L107-L129其求值逻辑为依次遍历规则用nameMatcher(pkgName)判断包名是否命中若命中且exactVersions为空 → 立即返回true整体命中短路若命中且带有精确版本 → 把版本收集进去重后的数组遍历完毕若有收集到版本则返回版本数组否则返回false。注意收集版本时会用Set去重!seen.has(version)因此重复规则不会产生重复版本。4.2 行为速查表综合 test/index.ts 的用例规则组合的求值行为如下规则询问包结果说明axios1.12.2axios[1.12.2]命中并返回精确版本axios1.12.2is-oddfalse未命中is-*is-odd/is-even/lodashtrue/true/false通配符命中babel/corebabel/coretrue裸作用域包名命中全部版本babel/core7.20.0babel/core[7.20.0]作用域包 精确版本axios1.12.0 \|\| 1.12.1axios[1.12.0, 1.12.1]版本联合pkg1.0.0\|\|1.0.1 \|\| 1.0.2pkg[1.0.0,1.0.1,1.0.2]空格容忍form-data4.0.6,form-data2.5.6form-data[4.0.6,2.5.6]多条规则合并版本form-data4.0.6,form-data4.0.6form-data[4.0.6]版本去重axios1.12.2,axiosaxiostrue裸包名规则吸收版本规则axios,axios1.12.2axiostrue顺序无关裸包名优先整体命中axios1.12.2,ax*axiostrue通配符吸收版本规则ax*,axios1.12.2axiostrue同上其中裸包名/通配符吸收版本规则的行为非常关键只要某条规则对该包返回整体命中true求值立即短路返回true后续带版本的规则不会再参与。这保证了排除语义的一致——minimumReleaseAgeExclude: [axios, axios1.12.2]等价于排除axios的全部版本。4.3 单字符串输入由于 YAML 标量与环境变量可能以单字符串到达模块特意测试了createPackageVersionPolicy(axios1.12.2)这种形态test/index.ts#L85-L88行为与数组形态完全一致。五、面向配置键的错误包装createPackageVersionPolicyOrThrow直接使用createPackageVersionPolicy时解析错误抛出的是INVALID_VERSION_UNION、NAME_PATTERN_IN_VERSION_UNION这类面向内部解析器的错误码用户很难看出问题出在哪个配置项上。因此模块提供了createPackageVersionPolicyOrThrow(patterns, key)src/index.ts#L25-L35它内部捕获解析错误重写为INVALID_KEY形式的PnpmError——把驼峰配置键转换成全大写下划线minimumReleaseAgeExclude→MINIMUM_RELEASE_AGE_EXCLUDE并带上用户可读的消息Invalid value in minimumReleaseAgeExclude: 原始错误消息最终错误码形如ERR_PNPM_INVALID_MINIMUM_RELEASE_AGE_EXCLUDE。测试 test/index.ts#L91-L109 验证了createPackageVersionPolicyOrThrow([lodash^4.17.0], minimumReleaseAgeExclude)抛出ERR_PNPM_INVALID_MINIMUM_RELEASE_AGE_EXCLUDEcreatePackageVersionPolicyOrThrow([is-*1.0.0], trustPolicyExclude)抛出ERR_PNPM_INVALID_TRUST_POLICY_EXCLUDE合法输入如[axios1.12.2]则正常返回可用的策略函数。这一设计让配置错误信息直指用户书写的配置键是配置体验层面的关键封装。六、发布年龄策略推导getPublishedByPolicygetPublishedByPolicysrc/index.ts#L53-L62把两个配置项翻译成解析器需要的策略对象interface PublishedByPolicy { publishedBy?: Date // 发布截止时间 publishedByExclude?: PackageVersionPolicy // 排除策略 }6.1 截止时间推导minimumReleaseAge以分钟为单位截止时间 Date.now() - minimumReleaseAge * 60 * 1000例如minimumReleaseAge: 144024 小时会得到当前时间往前推 1440 分钟的Date对象。测试 test/index.ts#L118-L129 用before/after两次取时间戳的方式验证了该推导落在预期的 ± 时钟漂移窗口内。6.2 统一的错误码minimumReleaseAgeExclude若给出非法模式如pnpm^9.0.0会经由createPackageVersionPolicyOrThrow(..., minimumReleaseAgeExclude)抛出ERR_PNPM_INVALID_MINIMUM_RELEASE_AGE_EXCLUDEtest/index.ts#L140-L145。注释特别强调把推导集中在此处是为了让所有调用方在同一时刻计算截止时间并对非法排除模式使用同一个错误码避免各调用点行为漂移。6.3 未配置时的返回值未设置任何配置时getPublishedByPolicy({})返回{ publishedBy: undefined, publishedByExclude: undefined }test/index.ts#L111-L116下游解析器据此跳过发布年龄检查。七、规格合并与展开mergePackageVersionSpecs与expandPackageVersionSpecs7.1mergePackageVersionSpecs同包规格合并该函数src/index.ts#L72-L90把一组版本策略规格例如minimumReleaseAgeExclude的多条记录合并为每个包一条规范条目规则如下裸包名/通配符吸收若某个包出现了不带版本的规则existing null则该包最终输出为裸包名吸收所有版本规格反之若先出现裸包名后续带版本条目也会被吸收版本去重同一包的多个精确版本合并进同一个Set重复版本自动消除semver 排序最终输出namev1 || v2时按Array.from(versions).sort(semver.compare)排序稳定顺序包按首次出现的顺序输出。测试用例test/index.ts#L147-L191覆盖了输入输出[axios0.21.2,axios0.18.1,axios0.21.1][axios0.18.1 \|\| 0.21.1 \|\| 0.21.2][axios0.18.1 \|\| 0.21.1,axios0.21.2][axios0.18.1 \|\| 0.21.1 \|\| 0.21.2][axios0.18.1,axios0.18.1][axios0.18.1][lodash4.17.21,axios0.18.1][lodash4.17.21,axios0.18.1]保持首次出现顺序[scope/pkg1.0.0,scope/pkg2.0.0][scope/pkg1.0.0 \|\| 2.0.0]作用域包[axios1.0.0,axios][axios]裸包名吸收[is-*,is-odd1.0.0][is-*,is-odd1.0.0]注意通配符与具体包互不吸收因为包名不同注意最后一行is-*与is-odd1.0.0的包名不同一个是模式、一个是具体名因此不会被合并——合并只发生在解析后包名相同的条目之间。7.2expandPackageVersionSpecs规格展开expandPackageVersionSpecssrc/index.ts#L92-L105把规格展开成包级或包版本级的集合无版本 → 加入packageName有版本 → 每个版本生成一个packageNameversion条目。例如[axios1.0.0 || 1.1.0, lodash]展开为Set { axios1.0.0, axios1.1.0, lodash }。这种一一对应的扁平结构便于在需要逐条比对的场景如校验、报告中直接使用。八、在 pnpm11 配置体系中的实际落点版本策略模块不是孤立存在它在 pnpm11 的配置读取与依赖解析链路中有清晰的落点8.1 配置字段pnpm11/config/reader/src/Config.ts 定义了相关配置字段minimumReleaseAge?: number // 发布年龄下限分钟 minimumReleaseAgeExclude?: string[] // 排除的包/版本 minimumReleaseAgeExcludePrune?: boolean minimumReleaseAgeIgnoreMissingTime?: boolean minimumReleaseAgeStrict?: boolean trustPolicy?: TrustPolicy trustPolicyExclude?: string[] // 信任策略排除 trustPolicyExcludePrune?: boolean trustPolicyIgnoreAfter?: number其中minimumReleaseAgeExclude、trustPolicyExclude等字符串数组就是喂给createPackageVersionPolicyOrThrow的原始输入。8.2 严格模式的默认开关pnpm11/config/reader/src/index.ts#L750-L762 展示了一个重要行为当用户显式设置minimumReleaseAge时默认把minimumReleaseAgeStrict置为true。注释解释了原因内置默认值故意是非严格的向后兼容但如果用户显式设置了该值却仍是非严格模式就会在范围内没有满足发布年龄的成熟版本时静默回退到安装不成熟版本让配置看起来毫无效果。该逻辑必须在环境变量解析之后执行以保证pnpm_config_minimum_release_age也能触发严格模式。8.3 解析器中的消费方式在 resolveDependencies.ts 中maximumPublishedBy?: Date与publishedByExclude?: PackageVersionPolicy作为解析选项被传入而注释明确说明minimumReleaseAge的检查发生在npm-resolver内部违反时解析器会把违规记录推入resolutionPolicyViolations累积数组供安装命令通过handleResolutionPolicyViolations回调提示/中止响应并在安装收尾时由 auto-persist 路径把违规落盘到工作区 manifestsrc/index.ts#L127-L139 与之配套的RESOLUTION_POLICY_VIOLATIONS_UNHANDLED错误码提示也印证了这一链路。换句话说version-policy 模块产出的谓词函数是发布年龄/信任策略在实际版本选择中的最终裁决器。8.4 安全策略相关配置不随项目走值得注意的是index.ts#L1291-L1304 将minimumReleaseAge、minimumReleaseAgeExclude、trustPolicy、trustPolicyExclude等列入SELF_UPDATE_SKIPPED_SETTINGS这些策略决定本机信任哪些包会跟随用户离开仓库因此只从内置默认值、全局配置 YAML、环境变量和 CLI 参数读取不从项目本地配置读取防止仓库内的恶意配置影响自更新。九、小结从配置字符串到最终裁决的完整链路把整个模块串起来一条版本策略的完整生命周期是用户配置pnpm-workspace.yaml / .npmrc / 环境变量 / CLI │ ▼ minimumReleaseAgeExclude: [axios1.12.0 || 1.12.1, is-*] │ ▼ createPackageVersionPolicyOrThrow(patterns, minimumReleaseAgeExclude) ├─ parseVersionPolicyRule拆包名 / 版本联合校验 semver 精确版本 ├─ createMatcher包名通配符编译pnpm/config.matcher └─ 出错 → ERR_PNPM_INVALID_MINIMUM_RELEASE_AGE_EXCLUDE │ ▼ PackageVersionPolicy (pkgName) boolean | string[] │ ▼ getPublishedByPolicy() 推导 publishedBy 截止时间 publishedByExclude 策略 │ ▼ deps-resolver / npm-resolver按 publishedBy 过滤版本、按排除策略豁免 违规写入 resolutionPolicyViolations → handleResolutionPolicyViolations 响应pnpm/config.version-policy用约 180 行代码把配置字符串 → 结构化规则 → 包名匹配 → 精确版本裁决这条链路封装成了可组合、可测试的纯函数集合并贯穿了配置读取、严格模式开关、依赖解析违规收集等 pnpm11 的多个关键环节。理解这个模块也就理解了 pnpm11 中minimumReleaseAge、trustPolicyExclude等安全策略从配置到行为的全部实现路径。参考与延伸阅读模块源码pnpm11/config/version-policy/src/index.ts模块测试含全部行为用例pnpm11/config/version-policy/test/index.ts包名匹配器实现pnpm11/config/matcher/src/index.ts策略类型定义pnpm11/core/types/src/config.ts配置字段定义pnpm11/config/reader/src/Config.ts严格模式默认开关pnpm11/config/reader/src/index.ts解析器消费与违规收集pnpm11/installing/deps-resolver/src/resolveDependencies.ts、pnpm11/installing/deps-resolver/src/index.ts【免费下载链接】pnpmFast, disk space efficient package manager项目地址: https://gitcode.com/gh_mirrors/pn/pnpm创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考