Skip to content

Commit 1339305

Browse files
fix: use semver to compare versions (npmx-dev#143)
1 parent 58db4f3 commit 1339305

4 files changed

Lines changed: 10 additions & 70 deletions

File tree

app/components/PackageVersions.vue

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
<script setup lang="ts">
22
import type { PackumentVersion, PackageVersionInfo } from '#shared/types'
33
import type { RouteLocationRaw } from 'vue-router'
4+
import { compare } from 'semver'
45
import {
56
buildVersionToTagsMap,
6-
compareVersions,
77
filterExcludedTags,
88
getPrereleaseChannel,
99
parseVersion,
@@ -90,7 +90,7 @@ const allTagRows = computed(() => {
9090
deprecated: versionData?.deprecated,
9191
} as VersionDisplay,
9292
}))
93-
.sort((a, b) => compareVersions(b.primaryVersion.version, a.primaryVersion.version))
93+
.sort((a, b) => compare(b.primaryVersion.version, a.primaryVersion.version))
9494
})
9595
9696
// Check if the whole package is deprecated (latest version is deprecated)
@@ -177,7 +177,7 @@ function processLoadedVersions(allVersions: PackageVersionInfo[]) {
177177
const vChannel = getPrereleaseChannel(v.version)
178178
return vParsed.major === tagParsed.major && vChannel === tagChannel
179179
})
180-
.sort((a, b) => compareVersions(b.version, a.version))
180+
.sort((a, b) => compare(b.version, a.version))
181181
.map(v => ({
182182
version: v.version,
183183
time: v.time,
@@ -214,7 +214,7 @@ function processLoadedVersions(allVersions: PackageVersionInfo[]) {
214214
215215
// Sort within each major
216216
for (const versions of byMajor.values()) {
217-
versions.sort((a, b) => compareVersions(b.version, a.version))
217+
versions.sort((a, b) => compare(b.version, a.version))
218218
}
219219
220220
// Build major groups sorted by major descending

app/composables/useNpmRegistry.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ import type {
99
PackageVersionInfo,
1010
} from '#shared/types'
1111
import type { ReleaseType } from 'semver'
12-
import { maxSatisfying, prerelease, major, minor, diff, gt } from 'semver'
12+
import { maxSatisfying, prerelease, major, minor, diff, gt, compare } from 'semver'
13+
import { isExactVersion } from '~/utils/versions'
14+
import { extractInstallScriptsInfo } from '~/utils/install-scripts'
1315

1416
const NPM_REGISTRY = 'https://registry.npmjs.org'
1517
const NPM_API = 'https://api.npmjs.org'
@@ -432,7 +434,7 @@ export async function fetchAllPackageVersions(packageName: string): Promise<Pack
432434
hasProvenance: false, // Would need to check dist.attestations for each version
433435
deprecated: versionData.deprecated,
434436
}))
435-
.sort((a, b) => compareVersions(b.version, a.version))
437+
.sort((a, b) => compare(b.version, a.version))
436438
})()
437439

438440
allVersionsCache.set(packageName, promise)

app/utils/versions.ts

Lines changed: 2 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { valid } from 'semver'
1+
import { compare, valid } from 'semver'
22

33
/**
44
* Utilities for handling npm package versions and dist-tags
@@ -39,29 +39,6 @@ export function parseVersion(version: string): ParsedVersion {
3939
}
4040
}
4141

42-
/**
43-
* Compare two semver versions for sorting
44-
* Returns positive if a > b, negative if a < b, 0 if equal
45-
* @param a - First version string
46-
* @param b - Second version string
47-
* @returns Comparison result for sorting
48-
*/
49-
export function compareVersions(a: string, b: string): number {
50-
const va = parseVersion(a)
51-
const vb = parseVersion(b)
52-
53-
if (va.major !== vb.major) return va.major - vb.major
54-
if (va.minor !== vb.minor) return va.minor - vb.minor
55-
if (va.patch !== vb.patch) return va.patch - vb.patch
56-
57-
// Stable versions (no prerelease) are greater than prereleases
58-
if (va.prerelease && vb.prerelease) return va.prerelease.localeCompare(vb.prerelease)
59-
if (va.prerelease) return -1
60-
if (vb.prerelease) return 1
61-
62-
return 0
63-
}
64-
6542
/**
6643
* Extract the prerelease channel from a version string
6744
* @param version - The version string (e.g., "1.0.0-beta.1")
@@ -145,7 +122,7 @@ export function buildTaggedVersionRows(distTags: Record<string, string>): Tagged
145122
tags,
146123
version,
147124
}))
148-
.sort((a, b) => compareVersions(b.version, a.version))
125+
.sort((a, b) => compare(b.version, a.version))
149126
}
150127

151128
/**

test/unit/versions.spec.ts

Lines changed: 0 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import { describe, expect, it } from 'vitest'
22
import {
33
buildTaggedVersionRows,
44
buildVersionToTagsMap,
5-
compareVersions,
65
filterExcludedTags,
76
getPrereleaseChannel,
87
parseVersion,
@@ -58,44 +57,6 @@ describe('parseVersion', () => {
5857
})
5958
})
6059

61-
describe('compareVersions', () => {
62-
it('compares major versions', () => {
63-
expect(compareVersions('2.0.0', '1.0.0')).toBeGreaterThan(0)
64-
expect(compareVersions('1.0.0', '2.0.0')).toBeLessThan(0)
65-
})
66-
67-
it('compares minor versions', () => {
68-
expect(compareVersions('1.2.0', '1.1.0')).toBeGreaterThan(0)
69-
expect(compareVersions('1.1.0', '1.2.0')).toBeLessThan(0)
70-
})
71-
72-
it('compares patch versions', () => {
73-
expect(compareVersions('1.0.2', '1.0.1')).toBeGreaterThan(0)
74-
expect(compareVersions('1.0.1', '1.0.2')).toBeLessThan(0)
75-
})
76-
77-
it('ranks stable above prerelease', () => {
78-
expect(compareVersions('1.0.0', '1.0.0-beta.1')).toBeGreaterThan(0)
79-
expect(compareVersions('1.0.0-beta.1', '1.0.0')).toBeLessThan(0)
80-
})
81-
82-
it('compares prereleases alphabetically', () => {
83-
expect(compareVersions('1.0.0-beta.1', '1.0.0-alpha.1')).toBeGreaterThan(0)
84-
expect(compareVersions('1.0.0-alpha.1', '1.0.0-beta.1')).toBeLessThan(0)
85-
})
86-
87-
it('returns 0 for equal versions', () => {
88-
expect(compareVersions('1.0.0', '1.0.0')).toBe(0)
89-
expect(compareVersions('1.0.0-beta.1', '1.0.0-beta.1')).toBe(0)
90-
})
91-
92-
it('sorts Nuxt versions correctly', () => {
93-
const versions = ['3.21.0', '4.0.0-alpha.4', '4.0.0-rc.0', '4.3.0']
94-
const sorted = [...versions].sort((a, b) => compareVersions(b, a))
95-
expect(sorted).toEqual(['4.3.0', '4.0.0-rc.0', '4.0.0-alpha.4', '3.21.0'])
96-
})
97-
})
98-
9960
describe('getPrereleaseChannel', () => {
10061
it('returns empty string for stable versions', () => {
10162
expect(getPrereleaseChannel('1.0.0')).toBe('')

0 commit comments

Comments
 (0)