From b848f56abb1fba82a4cab60e8d289d5c73f71f49 Mon Sep 17 00:00:00 2001 From: Kieron Lanning Date: Fri, 9 Oct 2026 14:16:47 +0100 Subject: [PATCH] docs: moved content around --- package.json | 2 +- src/src/lib/releases/normalize.ts | 20 +++++++++++ src/src/pages/projects/[project].astro | 49 ++++++++++++++------------ src/src/pages/releases/index.astro | 5 ++- src/tests/dist/built-output.test.ts | 22 ++++++++++++ src/tests/unit/releases.test.ts | 26 ++++++++++++++ 6 files changed, 99 insertions(+), 25 deletions(-) diff --git a/package.json b/package.json index 13d7e5e..6aa2578 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "purview-dev", - "version": "0.5.1", + "version": "0.5.2", "private": true, "description": "Purview-Dev public website and unified documentation portal (workspace root).", "license": "MIT", diff --git a/src/src/lib/releases/normalize.ts b/src/src/lib/releases/normalize.ts index 1cce871..321cd70 100644 --- a/src/src/lib/releases/normalize.ts +++ b/src/src/lib/releases/normalize.ts @@ -60,6 +60,26 @@ export function selectVersions(versions: Iterable): VersionSelection { }; } +/** + * The prerelease worth surfacing alongside a stable release: the given + * prerelease when it outranks the stable, otherwise null. A prerelease that + * trails the stable (e.g. `5.0.0-prerelease.19` beside stable `5.0.2`) is + * withheld so the package table never advertises an older build as newer. + * A prerelease is always kept when there is no stable version to compare to. + */ +export function effectivePrerelease( + stable: string | null, + prerelease: string | null, +): string | null { + if (prerelease === null) { + return null; + } + if (stable === null) { + return prerelease; + } + return compareNuGetVersions(prerelease, stable) > 0 ? prerelease : null; +} + /** Compare two GitHub releases by version tag, descending. */ export function sortGitHubReleasesDescending(releases: GitHubReleaseInfo[]): GitHubReleaseInfo[] { return [...releases].toSorted((a, b) => { diff --git a/src/src/pages/projects/[project].astro b/src/src/pages/projects/[project].astro index 8a32436..2871aed 100644 --- a/src/src/pages/projects/[project].astro +++ b/src/src/pages/projects/[project].astro @@ -10,6 +10,7 @@ import { buildBadge, nugetDownloadsBadge, nugetVersionBadge, actionsUrl } from ' import { installAnchorId, installKindFor, installTabs } from '~/lib/install'; import { LLMS_LINK_ATTRS } from '~/lib/llms'; import { loadProjects } from '~/lib/manifest/load'; +import { effectivePrerelease } from '~/lib/releases/normalize'; import { projectRepoEnrichment } from '~/lib/releases/repo'; import { projectReleaseSummary } from '~/lib/releases/transform'; import { getReleaseIndex } from '~/lib/releases/runtime'; @@ -132,7 +133,9 @@ const packageRows: PackageRow[] = releases.packages.map((pkg) => ({ projectUrl, packageId: pkg.packageId, latestStable: pkg.latestStable, - latestPrerelease: pkg.latestPrerelease, + // Only surface a prerelease that outranks the stable release, so a trailing + // prerelease never sits beside a newer stable version in the table. + latestPrerelease: effectivePrerelease(pkg.latestStable, pkg.latestPrerelease), totalDownloads: pkg.totalDownloads, nugetUrl: pkg.nugetUrl, deprecated: pkg.deprecated, @@ -315,28 +318,6 @@ const packageRows: PackageRow[] = releases.packages.map((pkg) => ({
- { - project.useCases.length > 0 && ( -
-

Use cases

-

- Concrete examples of what {project.name} does, and who each one is for. -

-
- {project.useCases.map((useCase) => ( - - ))} -
-
- ) - } -

The problem

{project.description}

@@ -403,6 +384,28 @@ const packageRows: PackageRow[] = releases.packages.map((pkg) => ({
+ { + project.useCases.length > 0 && ( +
+

Use cases

+

+ Concrete examples of what {project.name} does, and who each one is for. +

+
+ {project.useCases.map((useCase) => ( + + ))} +
+
+ ) + } + { project.packages.length > 0 && (
diff --git a/src/src/pages/releases/index.astro b/src/src/pages/releases/index.astro index a69f3eb..688d490 100644 --- a/src/src/pages/releases/index.astro +++ b/src/src/pages/releases/index.astro @@ -3,6 +3,7 @@ import SiteLayout from '~/layouts/SiteLayout.astro'; import ReleaseFilter from '~/components/islands/ReleaseFilter.tsx'; import PackageVersions, { type PackageRow } from '~/components/islands/PackageVersions.tsx'; import { loadProjects } from '~/lib/manifest/load'; +import { effectivePrerelease } from '~/lib/releases/normalize'; import { flattenReleases, projectReleaseSummary } from '~/lib/releases/transform'; import { getReleaseIndex } from '~/lib/releases/runtime'; import { SITE } from '~/lib/site'; @@ -30,7 +31,9 @@ const packageRows: PackageRow[] = summaries projectName: summary.projectName, packageId: pkg.packageId, latestStable: pkg.latestStable, - latestPrerelease: pkg.latestPrerelease, + // Only surface a prerelease that outranks the stable release, so a + // trailing prerelease never sits beside a newer stable version. + latestPrerelease: effectivePrerelease(pkg.latestStable, pkg.latestPrerelease), totalDownloads: pkg.totalDownloads, nugetUrl: pkg.nugetUrl, projectUrl: withBase(`/projects/${summary.projectId}/`), diff --git a/src/tests/dist/built-output.test.ts b/src/tests/dist/built-output.test.ts index e1f8e4e..63a0690 100644 --- a/src/tests/dist/built-output.test.ts +++ b/src/tests/dist/built-output.test.ts @@ -4,6 +4,9 @@ import { resolve } from 'node:path'; import { LOCAL_PATH_PATTERNS, SECRET_PATTERNS } from '../../src/lib/build-paths'; import { loadProjects } from '../../src/lib/manifest/load'; +import { compareNuGetVersions } from '../../src/lib/nuget-version'; +import { getReleaseIndex } from '../../src/lib/releases/runtime'; +import { projectReleaseSummary } from '../../src/lib/releases/transform'; const DIST = resolve('dist'); @@ -177,6 +180,25 @@ describe('built use cases', () => { }); }); +describe('built package versions', () => { + test('a package prerelease that trails its stable release is withheld', () => { + const telemetry = loadProjects().find((project) => project.id === 'telemetry-sourcegenerator'); + expect(telemetry).toBeDefined(); + const index = getReleaseIndex(); + const summary = projectReleaseSummary(telemetry!, index.data, index.source); + const pkg = summary.packages.find( + (entry) => entry.packageId === 'Purview.Telemetry.SourceGenerator', + ); + expect(pkg?.latestStable).not.toBeNull(); + expect(pkg?.latestPrerelease).not.toBeNull(); + // The package is a trailing prerelease (e.g. 5.0.0-prerelease.19 beside + // stable 5.0.2), so the table must not advertise it as a current version. + expect(compareNuGetVersions(pkg!.latestPrerelease!, pkg!.latestStable!)).toBeLessThan(0); + const content = requireBuilt('projects/telemetry-sourcegenerator/index.html'); + expect(content).not.toContain(pkg!.latestPrerelease!); + }); +}); + describe('built llms links', () => { // The LLM text bundles are plain files rather than site pages, so every link // to one must opt into the external-link treatment. diff --git a/src/tests/unit/releases.test.ts b/src/tests/unit/releases.test.ts index ac7d874..540d811 100644 --- a/src/tests/unit/releases.test.ts +++ b/src/tests/unit/releases.test.ts @@ -16,6 +16,7 @@ import { parseNuGetSearchEntry, } from '../../src/lib/releases/github'; import { + effectivePrerelease, isPrereleaseRelease, isPrereleaseVersion, isValidVersion, @@ -266,6 +267,31 @@ describe('version selection (NuGet precedence)', () => { }); }); +describe('effective prerelease (trailing prereleases are withheld)', () => { + test('withholds a prerelease that trails the stable release', () => { + // The Telemetry SourceGenerator case: stable 5.0.2 beside 5.0.0-prerelease.19. + expect(effectivePrerelease('5.0.2', '5.0.0-prerelease.19')).toBeNull(); + }); + + test('keeps a prerelease that outranks the stable release', () => { + expect(effectivePrerelease('1.1.1', '2.0.0-prerelease.38')).toBe('2.0.0-prerelease.38'); + }); + + test('keeps a prerelease when there is no stable release', () => { + expect(effectivePrerelease(null, '2.0.0-prerelease.32')).toBe('2.0.0-prerelease.32'); + }); + + test('returns null when there is no prerelease', () => { + expect(effectivePrerelease('1.1.1', null)).toBeNull(); + expect(effectivePrerelease(null, null)).toBeNull(); + }); + + test('compares four-component versions by their build revision', () => { + expect(effectivePrerelease('13.5.3.6', '13.3.0-prerelease.10')).toBeNull(); + expect(effectivePrerelease('13.5.3', '13.5.3.1-prerelease.1')).toBe('13.5.3.1-prerelease.1'); + }); +}); + describe('project/package association and release summaries', () => { test('enriches a project with repo description and tags', () => { const cache = readReleaseFixture('index');