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..71496c519b 100644 --- a/app/pages/[...package].vue +++ b/app/pages/[...package].vue @@ -131,6 +131,20 @@ const latestVersion = computed(() => { return pkg.value.versions[latestTag] ?? null }) +const deprecationNotice = computed(() => { + if (!displayVersion.value?.deprecated) return null + + const isLatestDeprecated = !!latestVersion.value?.deprecated + + // If latest is deprecated, show "package deprecated" + if (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 +412,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 } /**