Skip to content

Commit 0093784

Browse files
committed
Move home-page package flags to load() so SSR emits only valid icon links
1 parent 06034bb commit 0093784

2 files changed

Lines changed: 57 additions & 21 deletions

File tree

src/routes/+page.svelte

Lines changed: 12 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,15 @@
44
import { goto } from '$app/navigation';
55
import Icon from '$lib/components/common/Icon.svelte';
66
import Tooltip, { tooltip } from '$lib/components/common/Tooltip.svelte';
7-
import { packages, packageOrder, nav, type PackageId } from '$lib/config/packages';
7+
import { packages, packageOrder, nav } from '$lib/config/packages';
88
import type { SearchResult } from '$lib/utils/search';
99
import { createDebouncedSearch } from '$lib/stores/searchHook.svelte';
1010
import { searchTarget } from '$lib/stores/searchNavigation';
11-
import { getPackageManifest, packageHasRoadmap, versionHasExamples } from '$lib/api/versions';
1211
import { SearchResult as SearchResultComponent } from '$lib/components/search';
12+
import type { PageData } from './$types';
13+
14+
let { data }: { data: PageData } = $props();
15+
let packageFlags = $derived(data.packageFlags);
1316
1417
function navigateWithTransition(path: string) {
1518
const doc = document as Document & {
@@ -35,23 +38,9 @@
3538
}
3639
}
3740
38-
// Track which packages have roadmaps / examples (latest version)
39-
let roadmapFlags = $state<Record<string, boolean>>({});
40-
let examplesFlags = $state<Record<string, boolean>>({});
41-
4241
onMount(() => {
4342
window.addEventListener('keydown', handleGlobalKeydown);
4443
45-
// Load manifests to derive roadmap and examples availability
46-
for (const pkgId of packageOrder) {
47-
getPackageManifest(pkgId, fetch)
48-
.then((manifest) => {
49-
roadmapFlags[pkgId] = packageHasRoadmap(manifest);
50-
examplesFlags[pkgId] = versionHasExamples(manifest.latestTag, manifest);
51-
})
52-
.catch(() => {});
53-
}
54-
5544
return () => {
5645
window.removeEventListener('keydown', handleGlobalKeydown);
5746
};
@@ -171,9 +160,11 @@
171160
<div class="card-info-row">
172161
<span>{pkg.shortName}</span>
173162
<div class="header-actions">
174-
<a href="{base}/{pkg.api}" class="icon-btn" use:tooltip={'API'}>
175-
<Icon name="braces" size={14} />
176-
</a>
163+
{#if packageFlags[pkgId].hasApi}
164+
<a href="{base}/{pkg.api}" class="icon-btn" use:tooltip={'API'}>
165+
<Icon name="braces" size={14} />
166+
</a>
167+
{/if}
177168
<a href="{base}/{pkg.docs}" class="icon-btn" use:tooltip={'Docs'}>
178169
<Icon name="book" size={14} />
179170
</a>
@@ -182,12 +173,12 @@
182173
<Icon name="package" size={14} />
183174
</a>
184175
{/if}
185-
{#if examplesFlags[pkgId]}
176+
{#if packageFlags[pkgId].hasExamples}
186177
<a href="{base}/{pkgId}/examples" class="icon-btn" use:tooltip={'Examples'}>
187178
<Icon name="play" size={14} />
188179
</a>
189180
{/if}
190-
{#if roadmapFlags[pkgId]}
181+
{#if packageFlags[pkgId].hasRoadmap}
191182
<a href="{base}/{pkgId}/roadmap" class="icon-btn" use:tooltip={'Roadmap'}>
192183
<Icon name="roadmap" size={14} />
193184
</a>

src/routes/+page.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import type { PageLoad } from './$types';
2+
import { packageOrder, type PackageId } from '$lib/config/packages';
3+
import {
4+
getPackageManifest,
5+
packageHasRoadmap,
6+
versionHasApi,
7+
versionHasExamples
8+
} from '$lib/api/versions';
9+
10+
export interface PackageFlags {
11+
hasApi: boolean;
12+
hasExamples: boolean;
13+
hasRoadmap: boolean;
14+
}
15+
16+
/**
17+
* Load each package's manifest at prerender time so the home page can
18+
* server-render the API / Examples / Roadmap icon links based on real
19+
* availability flags. Doing this client-side in onMount would emit the
20+
* icons only after hydration — and the adapter-static crawler would never
21+
* see the links, leaving the unversioned redirect routes unprerendered.
22+
*/
23+
export const load: PageLoad = async ({ fetch }) => {
24+
const entries = await Promise.all(
25+
packageOrder.map(async (pkgId) => {
26+
try {
27+
const manifest = await getPackageManifest(pkgId, fetch);
28+
const flags: PackageFlags = {
29+
hasApi: versionHasApi(manifest.latestTag, manifest),
30+
hasExamples: versionHasExamples(manifest.latestTag, manifest),
31+
hasRoadmap: packageHasRoadmap(manifest)
32+
};
33+
return [pkgId, flags] as const;
34+
} catch {
35+
// Manifest unavailable — hide every conditional link for this package.
36+
const flags: PackageFlags = { hasApi: false, hasExamples: false, hasRoadmap: false };
37+
return [pkgId, flags] as const;
38+
}
39+
})
40+
);
41+
42+
const packageFlags = Object.fromEntries(entries) as Record<PackageId, PackageFlags>;
43+
44+
return { packageFlags };
45+
};

0 commit comments

Comments
 (0)