From 598b5e690c0ec9768203fe442c01dbc7b6100eff Mon Sep 17 00:00:00 2001
From: Tunglies <77394545+Tunglies@users.noreply.github.com>
Date: Sat, 29 Aug 2026 11:52:08 +0800
Subject: [PATCH] feat(updater): sanitize release notes and standardize semver
---
package.json | 3 +
pnpm-lock.yaml | 224 ++++++++++++++++++
src/components/setting/mods/update-viewer.tsx | 51 +++-
src/services/update.ts | 67 +-----
4 files changed, 281 insertions(+), 64 deletions(-)
diff --git a/package.json b/package.json
index 5733db8ec..95ef1d8a8 100644
--- a/package.json
+++ b/package.json
@@ -59,6 +59,7 @@
"@tauri-apps/plugin-updater": "2.10.1",
"ahooks": "^3.9.7",
"cidr-block": "^2.3.0",
+ "compare-versions": "^6.1.1",
"dayjs": "1.11.21",
"foxact": "^0.3.4",
"foxts": "^5.4.2",
@@ -78,6 +79,8 @@
"react-markdown": "10.1.0",
"react-router": "^8.0.0",
"rehype-raw": "^7.0.0",
+ "rehype-sanitize": "^6.0.0",
+ "remark-gfm": "^4.0.1",
"swr": "^2.4.2",
"tauri-plugin-mihomo-api": "github:clash-verge-rev/tauri-plugin-mihomo#refactor-use-reqwest-ipc",
"types-pac": "^1.0.3",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 90da62b2e..1cca367d1 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -163,6 +163,9 @@ importers:
cidr-block:
specifier: ^2.3.0
version: 2.3.0
+ compare-versions:
+ specifier: ^6.1.1
+ version: 6.1.1
dayjs:
specifier: 1.11.21
version: 1.11.21
@@ -220,6 +223,12 @@ importers:
rehype-raw:
specifier: ^7.0.0
version: 7.0.0
+ rehype-sanitize:
+ specifier: ^6.0.0
+ version: 6.0.0
+ remark-gfm:
+ specifier: ^4.0.1
+ version: 4.0.1
swr:
specifier: ^2.4.2
version: 2.5.1(react@19.2.8)
@@ -2569,6 +2578,10 @@ packages:
resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
engines: {node: '>=10'}
+ escape-string-regexp@5.0.0:
+ resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==}
+ engines: {node: '>=12'}
+
eslint-import-context@0.1.9:
resolution: {integrity: sha512-K9Hb+yRaGAGUbwjhFNHvSmmkZs9+zbuoe3kFQ4V1wYjrepUFYM2dZAfNtjbbj3qsPfUfsA68Bx/ICWQMi+C8Eg==}
engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0}
@@ -2903,6 +2916,9 @@ packages:
hast-util-raw@9.1.0:
resolution: {integrity: sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw==}
+ hast-util-sanitize@5.0.2:
+ resolution: {integrity: sha512-3yTWghByc50aGS7JlGhk61SPenfE/p1oaFeNwkOOyrscaOkMGrcW9+Cy/QAIOBpZxP1yqDIzFMR0+Np0i0+usg==}
+
hast-util-to-jsx-runtime@2.3.6:
resolution: {integrity: sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==}
@@ -3199,6 +3215,9 @@ packages:
magic-string@1.2.3:
resolution: {integrity: sha512-Bpb0W2TbLKOZ7vJnOUnVRGq3WL2p+ISV29M6hYPL1AFCpyKZpdr5ytiXoTSSxRVhg8YW7f65+6gbG8WG6PCa/g==}
+ markdown-table@3.0.4:
+ resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==}
+
marked@14.0.0:
resolution: {integrity: sha512-uIj4+faQ+MgHgwUW1l2PsPglZLOLOT1uErt06dAPtx2kjteLAkbsd/0FiYg/MGS+i7ZKLb7w2WClxHkzOOuryQ==}
engines: {node: '>= 18'}
@@ -3208,9 +3227,30 @@ packages:
resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
engines: {node: '>= 0.4'}
+ mdast-util-find-and-replace@3.0.2:
+ resolution: {integrity: sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==}
+
mdast-util-from-markdown@2.0.3:
resolution: {integrity: sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q==}
+ mdast-util-gfm-autolink-literal@2.0.1:
+ resolution: {integrity: sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==}
+
+ mdast-util-gfm-footnote@2.1.0:
+ resolution: {integrity: sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==}
+
+ mdast-util-gfm-strikethrough@2.0.0:
+ resolution: {integrity: sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==}
+
+ mdast-util-gfm-table@2.0.0:
+ resolution: {integrity: sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==}
+
+ mdast-util-gfm-task-list-item@2.0.0:
+ resolution: {integrity: sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==}
+
+ mdast-util-gfm@3.1.0:
+ resolution: {integrity: sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==}
+
mdast-util-mdx-expression@2.0.1:
resolution: {integrity: sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==}
@@ -3247,6 +3287,27 @@ packages:
micromark-core-commonmark@2.0.3:
resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==}
+ micromark-extension-gfm-autolink-literal@2.1.0:
+ resolution: {integrity: sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==}
+
+ micromark-extension-gfm-footnote@2.1.0:
+ resolution: {integrity: sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==}
+
+ micromark-extension-gfm-strikethrough@2.1.0:
+ resolution: {integrity: sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==}
+
+ micromark-extension-gfm-table@2.1.1:
+ resolution: {integrity: sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==}
+
+ micromark-extension-gfm-tagfilter@2.0.0:
+ resolution: {integrity: sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==}
+
+ micromark-extension-gfm-task-list-item@2.1.0:
+ resolution: {integrity: sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==}
+
+ micromark-extension-gfm@3.0.0:
+ resolution: {integrity: sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==}
+
micromark-factory-destination@2.0.1:
resolution: {integrity: sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==}
@@ -3595,12 +3656,21 @@ packages:
rehype-raw@7.0.0:
resolution: {integrity: sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==}
+ rehype-sanitize@6.0.0:
+ resolution: {integrity: sha512-CsnhKNsyI8Tub6L4sm5ZFsme4puGfc6pYylvXo1AeqaGbjOYyzNv3qZPwvs0oMJ39eryyeOdmxwUIo94IpEhqg==}
+
+ remark-gfm@4.0.1:
+ resolution: {integrity: sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==}
+
remark-parse@11.0.0:
resolution: {integrity: sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==}
remark-rehype@11.1.2:
resolution: {integrity: sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==}
+ remark-stringify@11.0.0:
+ resolution: {integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==}
+
resize-observer-polyfill@1.5.1:
resolution: {integrity: sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==}
@@ -6290,6 +6360,8 @@ snapshots:
escape-string-regexp@4.0.0: {}
+ escape-string-regexp@5.0.0: {}
+
eslint-import-context@0.1.9(unrs-resolver@1.12.2):
dependencies:
get-tsconfig: 4.14.3
@@ -6729,6 +6801,12 @@ snapshots:
web-namespaces: 2.0.1
zwitch: 2.0.4
+ hast-util-sanitize@5.0.2:
+ dependencies:
+ '@types/hast': 3.0.5
+ '@ungap/structured-clone': 1.3.4
+ unist-util-position: 5.0.0
+
hast-util-to-jsx-runtime@2.3.6:
dependencies:
'@types/estree': 1.0.9
@@ -7011,10 +7089,19 @@ snapshots:
dependencies:
'@jridgewell/sourcemap-codec': 1.5.5
+ markdown-table@3.0.4: {}
+
marked@14.0.0: {}
math-intrinsics@1.1.0: {}
+ mdast-util-find-and-replace@3.0.2:
+ dependencies:
+ '@types/mdast': 4.0.4
+ escape-string-regexp: 5.0.0
+ unist-util-is: 6.0.1
+ unist-util-visit-parents: 6.0.2
+
mdast-util-from-markdown@2.0.3:
dependencies:
'@types/mdast': 4.0.4
@@ -7032,6 +7119,63 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ mdast-util-gfm-autolink-literal@2.0.1:
+ dependencies:
+ '@types/mdast': 4.0.4
+ ccount: 2.0.1
+ devlop: 1.1.0
+ mdast-util-find-and-replace: 3.0.2
+ micromark-util-character: 2.1.1
+
+ mdast-util-gfm-footnote@2.1.0:
+ dependencies:
+ '@types/mdast': 4.0.4
+ devlop: 1.1.0
+ mdast-util-from-markdown: 2.0.3
+ mdast-util-to-markdown: 2.1.2
+ micromark-util-normalize-identifier: 2.0.1
+ transitivePeerDependencies:
+ - supports-color
+
+ mdast-util-gfm-strikethrough@2.0.0:
+ dependencies:
+ '@types/mdast': 4.0.4
+ mdast-util-from-markdown: 2.0.3
+ mdast-util-to-markdown: 2.1.2
+ transitivePeerDependencies:
+ - supports-color
+
+ mdast-util-gfm-table@2.0.0:
+ dependencies:
+ '@types/mdast': 4.0.4
+ devlop: 1.1.0
+ markdown-table: 3.0.4
+ mdast-util-from-markdown: 2.0.3
+ mdast-util-to-markdown: 2.1.2
+ transitivePeerDependencies:
+ - supports-color
+
+ mdast-util-gfm-task-list-item@2.0.0:
+ dependencies:
+ '@types/mdast': 4.0.4
+ devlop: 1.1.0
+ mdast-util-from-markdown: 2.0.3
+ mdast-util-to-markdown: 2.1.2
+ transitivePeerDependencies:
+ - supports-color
+
+ mdast-util-gfm@3.1.0:
+ dependencies:
+ mdast-util-from-markdown: 2.0.3
+ mdast-util-gfm-autolink-literal: 2.0.1
+ mdast-util-gfm-footnote: 2.1.0
+ mdast-util-gfm-strikethrough: 2.0.0
+ mdast-util-gfm-table: 2.0.0
+ mdast-util-gfm-task-list-item: 2.0.0
+ mdast-util-to-markdown: 2.1.2
+ transitivePeerDependencies:
+ - supports-color
+
mdast-util-mdx-expression@2.0.1:
dependencies:
'@types/estree-jsx': 1.0.5
@@ -7138,6 +7282,64 @@ snapshots:
micromark-util-symbol: 2.0.1
micromark-util-types: 2.0.2
+ micromark-extension-gfm-autolink-literal@2.1.0:
+ dependencies:
+ micromark-util-character: 2.1.1
+ micromark-util-sanitize-uri: 2.0.1
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-extension-gfm-footnote@2.1.0:
+ dependencies:
+ devlop: 1.1.0
+ micromark-core-commonmark: 2.0.3
+ micromark-factory-space: 2.0.1
+ micromark-util-character: 2.1.1
+ micromark-util-normalize-identifier: 2.0.1
+ micromark-util-sanitize-uri: 2.0.1
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-extension-gfm-strikethrough@2.1.0:
+ dependencies:
+ devlop: 1.1.0
+ micromark-util-chunked: 2.0.1
+ micromark-util-classify-character: 2.0.1
+ micromark-util-resolve-all: 2.0.1
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-extension-gfm-table@2.1.1:
+ dependencies:
+ devlop: 1.1.0
+ micromark-factory-space: 2.0.1
+ micromark-util-character: 2.1.1
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-extension-gfm-tagfilter@2.0.0:
+ dependencies:
+ micromark-util-types: 2.0.2
+
+ micromark-extension-gfm-task-list-item@2.1.0:
+ dependencies:
+ devlop: 1.1.0
+ micromark-factory-space: 2.0.1
+ micromark-util-character: 2.1.1
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-extension-gfm@3.0.0:
+ dependencies:
+ micromark-extension-gfm-autolink-literal: 2.1.0
+ micromark-extension-gfm-footnote: 2.1.0
+ micromark-extension-gfm-strikethrough: 2.1.0
+ micromark-extension-gfm-table: 2.1.1
+ micromark-extension-gfm-tagfilter: 2.0.0
+ micromark-extension-gfm-task-list-item: 2.1.0
+ micromark-util-combine-extensions: 2.0.1
+ micromark-util-types: 2.0.2
+
micromark-factory-destination@2.0.1:
dependencies:
micromark-util-character: 2.1.1
@@ -7569,6 +7771,22 @@ snapshots:
hast-util-raw: 9.1.0
vfile: 6.0.3
+ rehype-sanitize@6.0.0:
+ dependencies:
+ '@types/hast': 3.0.5
+ hast-util-sanitize: 5.0.2
+
+ remark-gfm@4.0.1:
+ dependencies:
+ '@types/mdast': 4.0.4
+ mdast-util-gfm: 3.1.0
+ micromark-extension-gfm: 3.0.0
+ remark-parse: 11.0.0
+ remark-stringify: 11.0.0
+ unified: 11.0.5
+ transitivePeerDependencies:
+ - supports-color
+
remark-parse@11.0.0:
dependencies:
'@types/mdast': 4.0.4
@@ -7586,6 +7804,12 @@ snapshots:
unified: 11.0.5
vfile: 6.0.3
+ remark-stringify@11.0.0:
+ dependencies:
+ '@types/mdast': 4.0.4
+ mdast-util-to-markdown: 2.1.2
+ unified: 11.0.5
+
resize-observer-polyfill@1.5.1: {}
resolve-from@4.0.0: {}
diff --git a/src/components/setting/mods/update-viewer.tsx b/src/components/setting/mods/update-viewer.tsx
index 569f53d6f..e80ac9d93 100644
--- a/src/components/setting/mods/update-viewer.tsx
+++ b/src/components/setting/mods/update-viewer.tsx
@@ -58,16 +58,50 @@ const GITHUB_ALERT_PATTERN =
/^\[!(NOTE|TIP|IMPORTANT|WARNING|CAUTION)\][\t ]*\n?/i
const GITHUB_ALERT_CLASS_PATTERN =
/markdown-alert-(note|tip|important|warning|caution)/
+const UPDATE_MARKDOWN_ID_PREFIX = 'update-markdown-'
const shouldShowReleaseNotes = (language: string) => language === 'zh'
const LazyReactMarkdown = lazy(async () => {
- const [{ default: ReactMarkdown }, { default: rehypeRaw }] =
- await Promise.all([import('react-markdown'), import('rehype-raw')])
+ const [
+ { default: ReactMarkdown },
+ { default: rehypeRaw },
+ { default: rehypeSanitize, defaultSchema },
+ { default: remarkGfm },
+ ] = await Promise.all([
+ import('react-markdown'),
+ import('rehype-raw'),
+ import('rehype-sanitize'),
+ import('remark-gfm'),
+ ])
+
+ const sanitizeSchema = {
+ ...defaultSchema,
+ clobberPrefix: UPDATE_MARKDOWN_ID_PREFIX,
+ attributes: {
+ ...defaultSchema.attributes,
+ blockquote: [
+ ...(defaultSchema.attributes?.blockquote ?? []),
+ [
+ 'className',
+ 'markdown-alert',
+ ...Object.keys(GITHUB_ALERTS).map((type) => `markdown-alert-${type}`),
+ ],
+ ],
+ p: [
+ ...(defaultSchema.attributes?.p ?? []),
+ ['className', 'markdown-alert-title'],
+ ],
+ },
+ }
return {
default: (props: ReactMarkdownOptions) => (
-
+
),
}
})
@@ -425,18 +459,27 @@ export function UpdateViewer({ ref }: { ref?: Ref }) {
remarkPlugins={[remarkGitHubAlertsPlugin]}
components={{
a: ({ href, children, ...props }) => {
+ const isFragment = href?.startsWith('#') ?? false
+ const renderedHref = isFragment
+ ? `#${UPDATE_MARKDOWN_ID_PREFIX}${href?.slice(1)}`
+ : href
+ ? '#'
+ : undefined
+
return (
{
+ if (isFragment) return
event.preventDefault()
if (!href) return
openUrlWithNotice(href)
}}
onAuxClick={(event) => {
+ if (isFragment) return
event.preventDefault()
if (event.button === 1 && href) {
openUrlWithNotice(href)
diff --git a/src/services/update.ts b/src/services/update.ts
index a35856c86..dccf1acf2 100644
--- a/src/services/update.ts
+++ b/src/services/update.ts
@@ -3,14 +3,10 @@ import {
type CheckOptions,
type Update,
} from '@tauri-apps/plugin-updater'
+import { compareVersions as compareSemver } from 'compare-versions'
import { version as appVersion } from '@root/package.json'
-type VersionParts = {
- main: number[]
- pre: (number | string)[]
-}
-
const SEMVER_FULL_REGEX =
/^\d+(?:\.\d+){1,2}(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/
const SEMVER_SEARCH_REGEX =
@@ -36,62 +32,13 @@ const extractSemver = (input: string | null | undefined): string | null => {
return normalizeVersion(match[0])
}
-const splitVersion = (version: string | null): VersionParts | null => {
- if (!version) return null
- const [mainPart, preRelease] = version.split('-')
- const main = mainPart
- .split('.')
- .map((part) => Number.parseInt(part, 10))
- .map((num) => (Number.isNaN(num) ? 0 : num))
-
- const pre =
- preRelease?.split('.').map((token) => {
- const numeric = Number.parseInt(token, 10)
- return Number.isNaN(numeric) ? token : numeric
- }) ?? []
-
- return { main, pre }
-}
-
-const compareVersionParts = (a: VersionParts, b: VersionParts): number => {
- const length = Math.max(a.main.length, b.main.length)
- for (let i = 0; i < length; i += 1) {
- const diff = (a.main[i] ?? 0) - (b.main[i] ?? 0)
- if (diff !== 0) return diff > 0 ? 1 : -1
- }
-
- if (a.pre.length === 0 && b.pre.length === 0) return 0
- if (a.pre.length === 0) return 1
- if (b.pre.length === 0) return -1
-
- const preLen = Math.max(a.pre.length, b.pre.length)
- for (let i = 0; i < preLen; i += 1) {
- const aToken = a.pre[i]
- const bToken = b.pre[i]
- if (aToken === undefined) return -1
- if (bToken === undefined) return 1
-
- if (typeof aToken === 'number' && typeof bToken === 'number') {
- if (aToken > bToken) return 1
- if (aToken < bToken) return -1
- continue
- }
-
- if (typeof aToken === 'number') return -1
- if (typeof bToken === 'number') return 1
-
- if (aToken > bToken) return 1
- if (aToken < bToken) return -1
- }
-
- return 0
-}
-
const compareVersions = (a: string | null, b: string | null): number | null => {
- const partsA = splitVersion(a)
- const partsB = splitVersion(b)
- if (!partsA || !partsB) return null
- return compareVersionParts(partsA, partsB)
+ if (!a || !b) return null
+ try {
+ return compareSemver(a, b)
+ } catch {
+ return null
+ }
}
const resolveRemoteVersion = (update: Update): string | null => {