From 720d2acf1819f47d56b75ad29a68f072cf7001d1 Mon Sep 17 00:00:00 2001 From: lihbr Date: Mon, 26 Jan 2026 15:28:07 +0900 Subject: [PATCH 1/2] feat: display deprecation notice for deprecated versions and packages --- app/components/PackageVersions.vue | 89 +++++++++++++++++++++++++----- app/composables/useNpmRegistry.ts | 16 +++--- app/pages/[...package].vue | 33 +++++++++++ shared/types/npm-registry.ts | 1 + 4 files changed, 117 insertions(+), 22 deletions(-) diff --git a/app/components/PackageVersions.vue b/app/components/PackageVersions.vue index 2f3210e1e5..0b32f4ea2b 100644 --- a/app/components/PackageVersions.vue +++ b/app/components/PackageVersions.vue @@ -26,6 +26,7 @@ interface VersionDisplay { time?: string tags?: string[] hasProvenance: boolean + deprecated?: string } // Check if a version has provenance/attestations @@ -86,16 +87,32 @@ const allTagRows = computed(() => { time: props.time[version], tags, hasProvenance: hasProvenance(versionData), + deprecated: versionData?.deprecated, } as VersionDisplay, })) .sort((a, b) => compareVersions(b.primaryVersion.version, a.primaryVersion.version)) }) -// Visible tag rows (limited to MAX_VISIBLE_TAGS) -const visibleTagRows = computed(() => allTagRows.value.slice(0, MAX_VISIBLE_TAGS)) +// Check if the whole package is deprecated (latest version is deprecated) +const isPackageDeprecated = computed(() => { + const latestVersion = props.distTags.latest + if (!latestVersion) return false + return !!props.versions[latestVersion]?.deprecated +}) + +// Visible tag rows: limited to MAX_VISIBLE_TAGS +// If package is NOT deprecated, filter out deprecated tags from visible list +const visibleTagRows = computed(() => { + const rows = isPackageDeprecated.value + ? allTagRows.value + : allTagRows.value.filter(row => !row.primaryVersion.deprecated) + return rows.slice(0, MAX_VISIBLE_TAGS) +}) -// Hidden tag rows (overflow beyond MAX_VISIBLE_TAGS) - shown in "Other versions" -const hiddenTagRows = computed(() => allTagRows.value.slice(MAX_VISIBLE_TAGS)) +// Hidden tag rows (all other tags) - shown in "Other versions" +const hiddenTagRows = computed(() => + allTagRows.value.filter(row => !visibleTagRows.value.includes(row)), +) // Client-side state for expansion and loaded versions const expandedTags = ref>(new Set()) @@ -166,6 +183,7 @@ function processLoadedVersions(allVersions: PackageVersionInfo[]) { time: v.time, tags: versionToTags.value.get(v.version), hasProvenance: v.hasProvenance, + deprecated: v.deprecated, })) tagVersions.value.set(row.tag, channelVersions) @@ -190,6 +208,7 @@ function processLoadedVersions(allVersions: PackageVersionInfo[]) { time: v.time, tags: versionToTags.value.get(v.version), hasProvenance: v.hasProvenance, + deprecated: v.deprecated, }) } @@ -306,8 +325,17 @@ function getTagVersions(tag: string): VersionDisplay[] {
{{ row.primaryVersion.version }} @@ -350,8 +378,13 @@ function getTagVersions(tag: string): VersionDisplay[] {
{{ v.version }} @@ -422,8 +455,17 @@ function getTagVersions(tag: string): VersionDisplay[] {
{{ row.primaryVersion.version }} @@ -467,7 +509,10 @@ function getTagVersions(tag: string): VersionDisplay[] { class="w-3 h-3 transition-transform duration-200 text-fg-subtle" :class="group.expanded ? 'i-carbon-chevron-down' : 'i-carbon-chevron-right'" /> - + {{ group.versions[0]?.version }}
@@ -492,8 +537,17 @@ function getTagVersions(tag: string): VersionDisplay[] { {{ group.versions[0].version }} @@ -515,8 +569,13 @@ function getTagVersions(tag: string): VersionDisplay[] {
{{ v.version }} diff --git a/app/composables/useNpmRegistry.ts b/app/composables/useNpmRegistry.ts index 7542b934a6..9900ce9acb 100644 --- a/app/composables/useNpmRegistry.ts +++ b/app/composables/useNpmRegistry.ts @@ -379,16 +379,18 @@ export async function fetchAllPackageVersions(packageName: string): Promise { const encodedName = encodePackageName(packageName) - const data = await $fetch<{ versions: Record; time: Record }>( - `${NPM_REGISTRY}/${encodedName}`, - ) - - return Object.keys(data.versions) - .filter(v => data.time[v]) - .map(version => ({ + const data = await $fetch<{ + versions: Record + time: Record + }>(`${NPM_REGISTRY}/${encodedName}`) + + return Object.entries(data.versions) + .filter(([v]) => data.time[v]) + .map(([version, versionData]) => ({ version, time: data.time[version], hasProvenance: false, // Would need to check dist.attestations for each version + deprecated: versionData.deprecated, })) .sort((a, b) => compareVersions(b.version, a.version)) })() diff --git a/app/pages/[...package].vue b/app/pages/[...package].vue index 6434097a43..00d24718d5 100644 --- a/app/pages/[...package].vue +++ b/app/pages/[...package].vue @@ -131,6 +131,22 @@ const latestVersion = computed(() => { return pkg.value.versions[latestTag] ?? null }) +const deprecationNotice = computed(() => { + if (!displayVersion.value?.deprecated) return null + + const isLatestDeprecated = !!latestVersion.value?.deprecated + const isViewingLatest = + !requestedVersion.value || displayVersion.value?.version === latestVersion.value?.version + + // Show "package deprecated" if viewing latest OR if the whole package (latest) is deprecated + if (isViewingLatest || isLatestDeprecated) { + return { type: 'package' as const, message: displayVersion.value.deprecated } + } + + // Otherwise show "version deprecated" + return { type: 'version' as const, message: displayVersion.value.deprecated } +}) + const hasDependencies = computed(() => { if (!displayVersion.value) return false const deps = displayVersion.value.dependencies @@ -398,6 +414,23 @@ defineOgImageComponent('Package', {
+
+

+ {{ + deprecationNotice.type === 'package' + ? 'This package has been deprecated.' + : 'This version has been deprecated.' + }} +

+

+ +

+

No reason provided

+
+
diff --git a/shared/types/npm-registry.ts b/shared/types/npm-registry.ts index 0676740d99..5dadc33633 100644 --- a/shared/types/npm-registry.ts +++ b/shared/types/npm-registry.ts @@ -48,6 +48,7 @@ export interface PackageVersionInfo { version: string time?: string hasProvenance: boolean + deprecated?: string } /** From 8fd210061a4b21c8ead149d3e25a6493d4d39ea8 Mon Sep 17 00:00:00 2001 From: lihbr Date: Mon, 26 Jan 2026 15:41:25 +0900 Subject: [PATCH 2/2] deprecation notice logic --- app/pages/[...package].vue | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/app/pages/[...package].vue b/app/pages/[...package].vue index 00d24718d5..71496c519b 100644 --- a/app/pages/[...package].vue +++ b/app/pages/[...package].vue @@ -135,11 +135,9 @@ const deprecationNotice = computed(() => { if (!displayVersion.value?.deprecated) return null const isLatestDeprecated = !!latestVersion.value?.deprecated - const isViewingLatest = - !requestedVersion.value || displayVersion.value?.version === latestVersion.value?.version - // Show "package deprecated" if viewing latest OR if the whole package (latest) is deprecated - if (isViewingLatest || isLatestDeprecated) { + // If latest is deprecated, show "package deprecated" + if (isLatestDeprecated) { return { type: 'package' as const, message: displayVersion.value.deprecated } }