Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 10 additions & 7 deletions app/components/DepsStats/DependencyStatsPanel.vue
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,16 @@ const { data: installSize, execute: fetchInstallSize } = useLazyFetch<InstallSiz
},
)

watch(
resolvedVersion,
version => {
if (version) fetchInstallSize()
},
{ immediate: true },
)
// Client-only: install size walks the whole dependency tree, so never block SSR on it.
if (import.meta.client) {
watch(
resolvedVersion,
version => {
if (version) fetchInstallSize()
},
{ immediate: true },
)
}

const { diff: sizeDiff } = useInstallSizeDiff(
() => props.packageName,
Expand Down
11 changes: 11 additions & 0 deletions app/composables/useDependencyAnalysis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@
* Shared composable for dependency analysis data (vulnerabilities, deprecated packages).
* Fetches once and caches the result so multiple components can use it.
* Before: useVulnerabilityTree - but now we use this for both vulnerabilities and deprecated packages.
*
* Client-only and lazy: resolving the whole transitive dependency graph (one packument download
* per dependency) plus OSV queries is far too slow to block the package page's server render. The
* data only drives the vulnerability count and the vulnerability/deprecated trees, none of which
* need to be in the server HTML.
*
* `useFetch` dedupes by key (the request URL), so all consumers share the same request/result.
*/
export function useDependencyAnalysis(
packageName: MaybeRefOrGetter<string>,
Expand All @@ -10,5 +17,9 @@ export function useDependencyAnalysis(
return useFetch(
() =>
`/api/registry/vulnerabilities/${encodePackageName(toValue(packageName))}/v/${toValue(version)}`,
{
server: false,
lazy: true,
},
)
}
60 changes: 34 additions & 26 deletions app/pages/package/[[org]]/[name].vue
Original file line number Diff line number Diff line change
Expand Up @@ -128,16 +128,19 @@ const {
},
)

// Trigger fetch only when we have the real resolved version
watch(
[resolvedVersion, resolvedStatus],
([version, status]) => {
if (version && status === 'success') {
fetchInstallSize()
}
},
{ immediate: true },
)
// Trigger fetch only when we have the real resolved version.
// Client-only: install size walks the whole dependency tree, so never block SSR on it.
if (import.meta.client) {
watch(
[resolvedVersion, resolvedStatus],
([version, status]) => {
if (version && status === 'success') {
fetchInstallSize()
}
},
{ immediate: true },
)
}

const { data: skillsData } = useLazyFetch<SkillsListResponse>(
() => {
Expand Down Expand Up @@ -672,23 +675,28 @@ const showSkeleton = shallowRef(false)
<span v-else>-</span>
</span>

<!-- Total install size in parens -->
<template v-if="displayVersion?.dist?.unpackedSize !== installSize?.totalSize">
<span class="ms-1">
<span
v-if="installSizeStatus === 'pending'"
class="inline-flex items-center gap-1 text-fg-subtle"
>
(<span class="i-svg-spinners:ring-resize w-3 h-3" aria-hidden="true" />)
</span>
<span v-else-if="installSize?.totalSize" dir="ltr">
<span class="text-fg-subtle">(</span
>{{ bytesFormatter.format(installSize.totalSize)
}}<span class="text-fg-subtle">)</span>
<!-- Total install size in parens.
Client-only: the lazy install-size fetch can resolve after the server HTML is
rendered but before the payload is serialized, so the value would be hydrated
even though the server rendered something else (a spinner or '-'). -->
<ClientOnly>
<template v-if="displayVersion?.dist?.unpackedSize !== installSize?.totalSize">
<span class="ms-1">
<span
v-if="installSizeStatus === 'pending'"
class="inline-flex items-center gap-1 text-fg-subtle"
>
(<span class="i-svg-spinners:ring-resize w-3 h-3" aria-hidden="true" />)
</span>
<span v-else-if="installSize?.totalSize" dir="ltr">
<span class="text-fg-subtle">(</span
>{{ bytesFormatter.format(installSize.totalSize)
}}<span class="text-fg-subtle">)</span>
</span>
<span v-else class="text-fg-subtle">(-)</span>
</span>
<span v-else class="text-fg-subtle">(-)</span>
</span>
</template>
</template>
</ClientOnly>
</dd>
</div>

Expand Down
Loading