Skip to content

Commit 8631fa2

Browse files
committed
fix: tidy up and disable extractAsyncDataHandlers
1 parent a5c1cf0 commit 8631fa2

4 files changed

Lines changed: 67 additions & 128 deletions

File tree

app/components/PackageVersions.vue

Lines changed: 53 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -16,24 +16,6 @@ interface VersionDisplay {
1616
hasProvenance: boolean
1717
}
1818
19-
/** A dist-tag row */
20-
interface TagRow {
21-
id: string
22-
tag: string
23-
primaryVersion: VersionDisplay
24-
/** Versions in this tag's channel (same major + same prerelease type) */
25-
allVersions: VersionDisplay[]
26-
loading: boolean
27-
expanded: boolean
28-
}
29-
30-
/** Unclaimed major version group */
31-
interface MajorGroup {
32-
major: number
33-
versions: VersionDisplay[]
34-
expanded: boolean
35-
}
36-
3719
// Check if a version has provenance/attestations
3820
function hasProvenance(version: PackumentVersion | undefined): boolean {
3921
if (!version?.dist) return false
@@ -77,11 +59,6 @@ function getPrereleaseChannel(version: string): string {
7759
return match ? match[1]!.toLowerCase() : ''
7860
}
7961
80-
// Cached full version list
81-
const allVersionsCache = ref<PackageVersionInfo[] | null>(null)
82-
const loadingVersions = ref(false)
83-
const hasLoadedAll = ref(false)
84-
8562
// Version to tag lookup
8663
const versionToTag = computed(() => {
8764
const map = new Map<string, string>()
@@ -94,16 +71,9 @@ const versionToTag = computed(() => {
9471
return map
9572
})
9673
97-
// Dist-tag rows (stable structure)
98-
const tagRows = ref<TagRow[]>([])
99-
100-
// Unclaimed versions section
101-
const otherVersionsExpanded = ref(false)
102-
const otherMajorGroups = ref<MajorGroup[]>([])
103-
104-
// Initialize tag rows from props
105-
watchEffect(() => {
106-
const rows: TagRow[] = Object.entries(props.distTags)
74+
// Initial tag rows derived from props (SSR-safe)
75+
const initialTagRows = computed(() => {
76+
return Object.entries(props.distTags)
10777
.map(([tag, version]) => {
10878
const versionData = props.versions[version]
10979
return {
@@ -114,17 +84,26 @@ watchEffect(() => {
11484
time: props.time[version],
11585
tag,
11686
hasProvenance: hasProvenance(versionData),
117-
},
118-
allVersions: [],
119-
loading: false,
120-
expanded: false,
87+
} as VersionDisplay,
12188
}
12289
})
12390
.sort((a, b) => compareVersions(b.primaryVersion.version, a.primaryVersion.version))
124-
125-
tagRows.value = rows
12691
})
12792
93+
// Client-side state for expansion and loaded versions
94+
const expandedTags = ref<Set<string>>(new Set())
95+
const tagVersions = ref<Map<string, VersionDisplay[]>>(new Map())
96+
const loadingTags = ref<Set<string>>(new Set())
97+
98+
const otherVersionsExpanded = ref(false)
99+
const otherMajorGroups = ref<Array<{ major: number, versions: VersionDisplay[], expanded: boolean }>>([])
100+
const otherVersionsLoading = ref(false)
101+
102+
// Cached full version list
103+
const allVersionsCache = ref<PackageVersionInfo[] | null>(null)
104+
const loadingVersions = ref(false)
105+
const hasLoadedAll = ref(false)
106+
128107
// npm registry packument type (simplified)
129108
interface NpmPackument {
130109
versions: Record<string, unknown>
@@ -162,26 +141,27 @@ async function loadAllVersions(): Promise<PackageVersionInfo[]> {
162141
.map(version => ({
163142
version,
164143
time: data.time[version],
165-
hasProvenance: false, // We don't have this info from the basic packument
144+
hasProvenance: false,
166145
}))
167146
.sort((a, b) => compareVersions(b.version, a.version))
168147
169148
allVersionsCache.value = versions
149+
hasLoadedAll.value = true
170150
return versions
171151
}
172152
finally {
173153
loadingVersions.value = false
174154
}
175155
}
176156
177-
// Process loaded versions - populate tag rows and find unclaimed versions
157+
// Process loaded versions
178158
function processLoadedVersions(allVersions: PackageVersionInfo[]) {
179159
const distTags = props.distTags
180160
181161
// For each tag, find versions in its channel (same major + same prerelease channel)
182162
const claimedVersions = new Set<string>()
183163
184-
for (const row of tagRows.value) {
164+
for (const row of initialTagRows.value) {
185165
const tagVersion = distTags[row.tag]
186166
if (!tagVersion) continue
187167
@@ -202,7 +182,7 @@ function processLoadedVersions(allVersions: PackageVersionInfo[]) {
202182
hasProvenance: v.hasProvenance,
203183
}))
204184
205-
row.allVersions = channelVersions
185+
tagVersions.value.set(row.tag, channelVersions)
206186
207187
for (const v of channelVersions) {
208188
claimedVersions.add(v.version)
@@ -239,22 +219,19 @@ function processLoadedVersions(allVersions: PackageVersionInfo[]) {
239219
versions: byMajor.get(major)!,
240220
expanded: false,
241221
}))
242-
243-
hasLoadedAll.value = true
244222
}
245223
246224
// Expand a tag row
247-
async function expandTagRow(index: number) {
248-
const row = tagRows.value[index]
249-
if (!row) return
250-
251-
if (row.expanded) {
252-
row.expanded = false
225+
async function expandTagRow(tag: string) {
226+
if (expandedTags.value.has(tag)) {
227+
expandedTags.value.delete(tag)
228+
expandedTags.value = new Set(expandedTags.value)
253229
return
254230
}
255231
256232
if (!hasLoadedAll.value) {
257-
row.loading = true
233+
loadingTags.value.add(tag)
234+
loadingTags.value = new Set(loadingTags.value)
258235
try {
259236
const allVersions = await loadAllVersions()
260237
processLoadedVersions(allVersions)
@@ -263,11 +240,13 @@ async function expandTagRow(index: number) {
263240
console.error('Failed to load versions:', error)
264241
}
265242
finally {
266-
row.loading = false
243+
loadingTags.value.delete(tag)
244+
loadingTags.value = new Set(loadingTags.value)
267245
}
268246
}
269247
270-
row.expanded = true
248+
expandedTags.value.add(tag)
249+
expandedTags.value = new Set(expandedTags.value)
271250
}
272251
273252
// Expand "Other versions" section
@@ -278,13 +257,17 @@ async function expandOtherVersions() {
278257
}
279258
280259
if (!hasLoadedAll.value) {
260+
otherVersionsLoading.value = true
281261
try {
282262
const allVersions = await loadAllVersions()
283263
processLoadedVersions(allVersions)
284264
}
285265
catch (error) {
286266
console.error('Failed to load versions:', error)
287267
}
268+
finally {
269+
otherVersionsLoading.value = false
270+
}
288271
}
289272
290273
otherVersionsExpanded.value = true
@@ -298,6 +281,11 @@ function toggleMajorGroup(index: number) {
298281
}
299282
}
300283
284+
// Get versions for a tag (from loaded data or empty)
285+
function getTagVersions(tag: string): VersionDisplay[] {
286+
return tagVersions.value.get(tag) ?? []
287+
}
288+
301289
function formatDate(dateStr: string): string {
302290
return new Date(dateStr).toLocaleDateString('en-US', {
303291
year: 'numeric',
@@ -309,7 +297,7 @@ function formatDate(dateStr: string): string {
309297

310298
<template>
311299
<section
312-
v-if="tagRows.length > 0"
300+
v-if="initialTagRows.length > 0"
313301
aria-labelledby="versions-heading"
314302
>
315303
<h2
@@ -322,27 +310,27 @@ function formatDate(dateStr: string): string {
322310
<div class="space-y-0.5">
323311
<!-- Dist-tag rows -->
324312
<div
325-
v-for="(row, index) in tagRows"
313+
v-for="row in initialTagRows"
326314
:key="row.id"
327315
>
328316
<div class="flex items-center gap-2">
329317
<!-- Expand button (only if there are more versions to show) -->
330318
<button
331-
v-if="row.allVersions.length > 1 || !hasLoadedAll"
319+
v-if="getTagVersions(row.tag).length > 1 || !hasLoadedAll"
332320
type="button"
333321
class="w-4 h-4 flex items-center justify-center text-fg-subtle hover:text-fg transition-colors"
334-
:aria-expanded="row.expanded"
335-
:aria-label="row.expanded ? `Collapse ${row.tag}` : `Expand ${row.tag}`"
336-
@click="expandTagRow(index)"
322+
:aria-expanded="expandedTags.has(row.tag)"
323+
:aria-label="expandedTags.has(row.tag) ? `Collapse ${row.tag}` : `Expand ${row.tag}`"
324+
@click="expandTagRow(row.tag)"
337325
>
338326
<span
339-
v-if="row.loading"
327+
v-if="loadingTags.has(row.tag)"
340328
class="i-carbon-rotate w-3 h-3 animate-spin"
341329
/>
342330
<span
343331
v-else
344332
class="w-3 h-3 transition-transform duration-200"
345-
:class="row.expanded ? 'i-carbon-chevron-down' : 'i-carbon-chevron-right'"
333+
:class="expandedTags.has(row.tag) ? 'i-carbon-chevron-down' : 'i-carbon-chevron-right'"
346334
/>
347335
</button>
348336
<span
@@ -383,11 +371,11 @@ function formatDate(dateStr: string): string {
383371

384372
<!-- Expanded versions -->
385373
<div
386-
v-if="row.expanded && row.allVersions.length > 1"
374+
v-if="expandedTags.has(row.tag) && getTagVersions(row.tag).length > 1"
387375
class="ml-4 pl-2 border-l border-border space-y-0.5"
388376
>
389377
<div
390-
v-for="v in row.allVersions.slice(1)"
378+
v-for="v in getTagVersions(row.tag).slice(1)"
391379
:key="v.version"
392380
class="flex items-center justify-between py-1 text-sm gap-2"
393381
>
@@ -434,7 +422,7 @@ function formatDate(dateStr: string): string {
434422
>
435423
<span class="w-4 h-4 flex items-center justify-center text-fg-subtle hover:text-fg transition-colors">
436424
<span
437-
v-if="loadingVersions && !hasLoadedAll"
425+
v-if="otherVersionsLoading"
438426
class="i-carbon-rotate w-3 h-3 animate-spin"
439427
/>
440428
<span

app/composables/useNpmRegistry.ts

Lines changed: 12 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -4,61 +4,22 @@ import type {
44
SlimPackument,
55
NpmSearchResponse,
66
NpmDownloadCount,
7-
NpmDownloadRange,
87
} from '#shared/types'
98

109
const NPM_REGISTRY = 'https://registry.npmjs.org'
1110
const NPM_API = 'https://api.npmjs.org'
1211

13-
export function useNpmRegistry() {
14-
async function fetchPackage(name: string): Promise<Packument> {
15-
const encodedName = encodePackageName(name)
16-
return await $fetch<Packument>(`${NPM_REGISTRY}/${encodedName}`)
17-
}
18-
19-
async function searchPackages(
20-
query: string,
21-
options: {
22-
size?: number
23-
from?: number
24-
quality?: number
25-
popularity?: number
26-
maintenance?: number
27-
} = {},
28-
): Promise<NpmSearchResponse> {
29-
const params = new URLSearchParams()
30-
params.set('text', query)
31-
if (options.size) params.set('size', String(options.size))
32-
if (options.from) params.set('from', String(options.from))
33-
if (options.quality !== undefined) params.set('quality', String(options.quality))
34-
if (options.popularity !== undefined) params.set('popularity', String(options.popularity))
35-
if (options.maintenance !== undefined) params.set('maintenance', String(options.maintenance))
36-
37-
return await $fetch<NpmSearchResponse>(`${NPM_REGISTRY}/-/v1/search?${params.toString()}`)
38-
}
39-
40-
async function fetchDownloads(
41-
packageName: string,
42-
period: 'last-day' | 'last-week' | 'last-month' | 'last-year' = 'last-week',
43-
): Promise<NpmDownloadCount> {
44-
const encodedName = encodePackageName(packageName)
45-
return await $fetch<NpmDownloadCount>(`${NPM_API}/downloads/point/${period}/${encodedName}`)
46-
}
47-
48-
async function fetchDownloadRange(
49-
packageName: string,
50-
period: 'last-week' | 'last-month' | 'last-year' = 'last-month',
51-
): Promise<NpmDownloadRange> {
52-
const encodedName = encodePackageName(packageName)
53-
return await $fetch<NpmDownloadRange>(`${NPM_API}/downloads/range/${period}/${encodedName}`)
54-
}
12+
async function fetchNpmPackage(name: string): Promise<Packument> {
13+
const encodedName = encodePackageName(name)
14+
return await $fetch<Packument>(`${NPM_REGISTRY}/${encodedName}`)
15+
}
5516

56-
return {
57-
fetchPackage,
58-
searchPackages,
59-
fetchDownloads,
60-
fetchDownloadRange,
61-
}
17+
async function fetchNpmDownloads(
18+
packageName: string,
19+
period: 'last-day' | 'last-week' | 'last-month' | 'last-year' = 'last-week',
20+
): Promise<NpmDownloadCount> {
21+
const encodedName = encodePackageName(packageName)
22+
return await $fetch<NpmDownloadCount>(`${NPM_API}/downloads/point/${period}/${encodedName}`)
6223
}
6324

6425
function encodePackageName(name: string): string {
@@ -134,23 +95,19 @@ function transformPackument(pkg: Packument): SlimPackument {
13495
}
13596

13697
export function usePackage(name: MaybeRefOrGetter<string>) {
137-
const registry = useNpmRegistry()
138-
13998
return useLazyAsyncData(
14099
() => `package:${toValue(name)}`,
141-
() => registry.fetchPackage(toValue(name)).then(r => transformPackument(r)),
100+
() => fetchNpmPackage(toValue(name)).then(r => transformPackument(r)),
142101
)
143102
}
144103

145104
export function usePackageDownloads(
146105
name: MaybeRefOrGetter<string>,
147106
period: MaybeRefOrGetter<'last-day' | 'last-week' | 'last-month' | 'last-year'> = 'last-week',
148107
) {
149-
const registry = useNpmRegistry()
150-
151108
return useLazyAsyncData(
152109
() => `downloads:${toValue(name)}:${toValue(period)}`,
153-
() => registry.fetchDownloads(toValue(name), toValue(period)),
110+
() => fetchNpmDownloads(toValue(name), toValue(period)),
154111
)
155112
}
156113

app/pages/package/[...name].vue

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -45,16 +45,11 @@ const { data: pkg, status, error } = usePackage(packageName)
4545
const { data: downloads } = usePackageDownloads(packageName, 'last-week')
4646
4747
// Fetch README for specific version if requested, otherwise latest
48-
const readmeUrl = computed(() => {
48+
const { data: readmeData } = useLazyFetch(() => {
4949
const base = `/api/registry/readme/${packageName.value}`
5050
const version = requestedVersion.value
5151
return version ? `${base}/v/${version}` : base
52-
})
53-
54-
const { data: readmeData } = useLazyFetch(readmeUrl, {
55-
key: () => `readme:${packageName.value}:${requestedVersion.value ?? 'latest'}`,
56-
default: () => ({ html: '' }),
57-
})
52+
}, { default: () => ({ html: '' }) })
5853
5954
// Get the version to display (requested or latest)
6055
const displayVersion = computed(() => {

nuxt.config.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,6 @@ export default defineNuxtConfig({
4949
},
5050

5151
experimental: {
52-
extractAsyncDataHandlers: true,
5352
viewTransition: true,
5453
typedPages: true,
5554
defaults: {

0 commit comments

Comments
 (0)