Files
verdaccio-cache-manager/backend/semver.js
T
yinjun.chen1 115c5ccc9a fix(clean): 物理 tgz 按文件匹配删除 + semver 完整语义排序
- 新增 tgz.js:统一列出包目录物理 .tgz 文件,支持新版 {version}.tgz 精确匹配
  与旧版 {pkgname}-{version}.tgz 后缀匹配,删除按真实文件 size 统计
- semver.js 重写:完整 npm 语义比较(prerelease 逐标识比较、数字<非数字、
  公共前缀更长者更大),修复原实现剥离 prerelease 导致 beta 挤掉正式版的隐患
- clean.js / scanner.js / server.js 接入统一口径
- 新增 test/semver.test.js 与 test/tgz.test.js 回归测试(node --test)
2026-09-21 09:49:38 +08:00

57 lines
2.0 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* semver 完整比较(与 npm semver 语义一致,无第三方依赖)
* - 支持 v 前缀、build 元数据(+xxx,比较时忽略)
* - prerelease 规则:正式版 > 任何带 prerelease 的版本;
* 两个 prerelease 逐标识比较:纯数字按数值、混合按 ASCII 字典序、数字 < 非数字
* - 用途:清理时「保留最新 N 个版本」的排序基准,保证不会把正式版删掉却留下 beta
*/
export default function compare(a, b) {
const pa = parse(a)
const pb = parse(b)
if (pa.major !== pb.major) return pa.major - pb.major
if (pa.minor !== pb.minor) return pa.minor - pb.minor
if (pa.patch !== pb.patch) return pa.patch - pb.patch
return comparePrerelease(pa.prerelease, pb.prerelease)
}
function parse(v) {
const s = String(v).replace(/^v/, '')
const [core, pre] = s.split('-')
const [majorRaw, minorRaw, patchRaw] = core.split('.')
return {
major: toInt(majorRaw),
minor: toInt(minorRaw),
patch: toInt(patchRaw),
// build 元数据(+metadata)在比较中忽略;prerelease 与 build 之间以第一个 + 分隔
prerelease: pre ? pre.split('+')[0].split('.') : null,
}
}
function toInt(s) {
const n = parseInt(s, 10)
return Number.isNaN(n) ? 0 : n
}
/**
* prerelease 标识数组比较(null 表示无 prerelease = 正式版)
* 结果语义与 compare 一致:负数 a<b / 0 / 正数 a>b
*/
function comparePrerelease(a, b) {
if (a === b) return 0
if (a === null) return 1 // 正式版 > prerelease
if (b === null) return -1
const len = Math.min(a.length, b.length)
for (let i = 0; i < len; i++) {
const ai = a[i]
const bi = b[i]
if (ai === bi) continue
const an = /^\d+$/.test(ai)
const bn = /^\d+$/.test(bi)
if (an && bn) return Number(ai) - Number(bi)
if (an) return -1 // 数字标识 < 非数字标识
if (bn) return 1
return ai < bi ? -1 : 1 // 非数字按 ASCII 字典序
}
// 公共前缀相同:标识更多者更大(1.0.0-rc < 1.0.0-rc.1)
return a.length - b.length
}