From f7e468f28fe8548eb238e70aa882d195ca1e9014 Mon Sep 17 00:00:00 2001 From: CPTProgrammer <46586216+CPTProgrammer@users.noreply.github.com> Date: Fri, 10 Jul 2026 14:58:51 +0800 Subject: [PATCH] Add SemVer.compare static method for version comparison --- src/lib/utils/templates/semver.test.ts | 43 ++++++++++++++++++++++++++ src/lib/utils/templates/semver.ts | 20 ++++++++++++ 2 files changed, 63 insertions(+) diff --git a/src/lib/utils/templates/semver.test.ts b/src/lib/utils/templates/semver.test.ts index 4e1d669..a6548af 100644 --- a/src/lib/utils/templates/semver.test.ts +++ b/src/lib/utils/templates/semver.test.ts @@ -48,6 +48,49 @@ describe("SemVer", () => { }); }); + describe("compare", () => { + test("相等", () => { + expect(SemVer.compare("1.0.0", "1.0.0")).toBe(0); + expect(SemVer.compare("1.0", "1.0")).toBe(0); + }); + + test("major 不同", () => { + expect(SemVer.compare("1.0.0", "2.0.0")).toBeLessThan(0); + expect(SemVer.compare("2.0.0", "1.0.0")).toBeGreaterThan(0); + }); + + test("minor 不同", () => { + expect(SemVer.compare("1.0.0", "1.1.0")).toBeLessThan(0); + expect(SemVer.compare("1.1.0", "1.0.0")).toBeGreaterThan(0); + }); + + test("patch 不同", () => { + expect(SemVer.compare("1.0.1", "1.0.2")).toBeLessThan(0); + expect(SemVer.compare("1.0.2", "1.0.1")).toBeGreaterThan(0); + }); + + test("缺 patch 视为 0", () => { + expect(SemVer.compare("1.0", "1.0.0")).toBe(0); + expect(SemVer.compare("1.0.1", "1.0")).toBeGreaterThan(0); + }); + + test("pre-release < 正式版", () => { + expect(SemVer.compare("1.0.0-alpha", "1.0.0")).toBeLessThan(0); + expect(SemVer.compare("1.0.0", "1.0.0-beta")).toBeGreaterThan(0); + }); + + test("可作排序用", () => { + const input = ["1.10.0", "1.2.0", "1.2.1", "1.2.0-beta"]; + const sorted = input.toSorted(SemVer.compare); + expect(sorted).toEqual(["1.2.0-beta", "1.2.0", "1.2.1", "1.10.0"]); + }); + + test(">=26 版本", () => { + expect(SemVer.compare("26.1", "26.2")).toBeLessThan(0); + expect(SemVer.compare("27.0", "26.99")).toBeGreaterThan(0); + }); + }); + describe("format", () => { test("两段", () => { expect(new SemVer(1, 2).format()).toBe("1.2"); diff --git a/src/lib/utils/templates/semver.ts b/src/lib/utils/templates/semver.ts index b34eb8e..a6f3619 100644 --- a/src/lib/utils/templates/semver.ts +++ b/src/lib/utils/templates/semver.ts @@ -46,4 +46,24 @@ export class SemVer implements TemplateValue { match[4] ?? null, ); }; + + /** + * 比较两个版本号字符串,返回负数/0/正数。 + * 可直接作为 Array.toSorted 的比较函数。 + */ + static compare(a: string, b: string): number { + const va = SemVer.parse(a); + const vb = SemVer.parse(b); + if (va.major !== vb.major) return va.major - vb.major; + if (va.minor !== vb.minor) return va.minor - vb.minor; + const pa = va.patch ?? 0; + const pb = vb.patch ?? 0; + if (pa !== pb) return pa - pb; + if (va.preRelease === null && vb.preRelease !== null) return 1; + if (va.preRelease !== null && vb.preRelease === null) return -1; + if (va.preRelease !== null && vb.preRelease !== null) { + return va.preRelease.localeCompare(vb.preRelease); + } + return 0; + } }