11import { type Metadata } from "next" ;
2+ import { cacheLife } from "next/cache" ;
23import { redirect } from "next/navigation" ;
4+ import npa from "npm-package-arg" ;
35import { type JSX , Suspense } from "react" ;
46import { type ViewType } from "react-diff-view" ;
7+ import Skeleton from "^/components/ui/Skeleton" ;
8+ import getVersionData from "^/lib/api/npm/getVersionData" ;
9+ import packument from "^/lib/api/npm/packument" ;
510import { createSimplePackageSpec } from "^/lib/createSimplePackageSpec" ;
611import { DEFAULT_DIFF_FILES_GLOB } from "^/lib/default-diff-files" ;
712import destination from "^/lib/destination" ;
813import { parseQuery , type QueryParams } from "^/lib/query" ;
914import { simplePackageSpecToString } from "^/lib/SimplePackageSpec" ;
1015import decodeParts from "^/lib/utils/decodeParts" ;
16+ import { generateComparisons } from "^/lib/utils/generateComparisons" ;
1117import specsToDiff from "^/lib/utils/specsToDiff" ;
1218import splitParts from "^/lib/utils/splitParts" ;
1319import BundlephobiaDiff from "./_page/BundlephobiaDiff" ;
20+ import ComparisonList from "./_page/catalog/ComparisonList" ;
21+ import PackageMeta from "./_page/catalog/PackageMeta" ;
1422import DiffIntro from "./_page/DiffIntro" ;
1523import NpmDiff from "./_page/NpmDiff" ;
1624import PackagephobiaDiff from "./_page/PackagephobiaDiff" ;
@@ -28,6 +36,17 @@ export async function generateMetadata({
2836 const { parts } = await params ;
2937 const specs = splitParts ( decodeParts ( parts ) ) ;
3038
39+ // Check if this is a catalog page (single package name without version)
40+ if ( specs . length === 1 ) {
41+ const parsed = npa ( specs [ 0 ] ) ;
42+ if ( parsed . rawSpec === "*" && parsed . name ) {
43+ return {
44+ title : `${ parsed . name } - Package Catalog` ,
45+ description : `Version catalog and comparison links for the npm package "${ parsed . name } "` ,
46+ } ;
47+ }
48+ }
49+
3150 const [ a , b ] = specs . map ( ( spec ) => createSimplePackageSpec ( spec ) ) ;
3251
3352 return {
@@ -36,6 +55,55 @@ export async function generateMetadata({
3655 } ;
3756}
3857
58+ async function CatalogPageInner ( { packageName } : { packageName : string } ) {
59+ "use cache" ;
60+
61+ cacheLife ( "hours" ) ;
62+
63+ // Fetch package data
64+ const [ pack , versionMap ] = await Promise . all ( [
65+ packument ( packageName ) ,
66+ getVersionData ( packageName ) ,
67+ ] ) ;
68+
69+ // Get all versions
70+ const versions = Object . keys ( versionMap ) ;
71+
72+ // Generate comparisons
73+ const comparisons = generateComparisons ( versions , versionMap ) ;
74+
75+ return (
76+ < div className = "mx-auto w-full max-w-7xl py-8" >
77+ < div className = "grid grid-cols-1 gap-8 lg:grid-cols-2" >
78+ < div className = "flex flex-col" >
79+ < PackageMeta packument = { pack } />
80+ </ div >
81+ < div className = "flex flex-col" >
82+ < ComparisonList
83+ packageName = { packageName }
84+ comparisons = { comparisons }
85+ />
86+ </ div >
87+ </ div >
88+ </ div >
89+ ) ;
90+ }
91+
92+ function CatalogPageFallback ( ) {
93+ return (
94+ < div className = "mx-auto w-full max-w-7xl py-8" >
95+ < div className = "grid grid-cols-1 gap-8 lg:grid-cols-2" >
96+ < div className = "flex flex-col" >
97+ < Skeleton className = "h-96 w-full rounded-md" />
98+ </ div >
99+ < div className = "flex flex-col" >
100+ < Skeleton className = "h-96 w-full rounded-md" />
101+ </ div >
102+ </ div >
103+ </ div >
104+ ) ;
105+ }
106+
39107const DiffPageInner = async ( {
40108 params,
41109 searchParams,
@@ -44,6 +112,20 @@ const DiffPageInner = async ({
44112 const { diffFiles, ...optionsQuery } = await searchParams ;
45113
46114 const specsOrVersions = splitParts ( decodeParts ( parts ) ) ;
115+
116+ // Check if this is a catalog page (single package name without version)
117+ if ( specsOrVersions . length === 1 ) {
118+ const parsed = npa ( specsOrVersions [ 0 ] ) ;
119+ if ( parsed . rawSpec === "*" && parsed . name ) {
120+ // This is a catalog page
121+ return (
122+ < Suspense fallback = { < CatalogPageFallback /> } >
123+ < CatalogPageInner packageName = { parsed . name } />
124+ </ Suspense >
125+ ) ;
126+ }
127+ }
128+
47129 const { redirect : redirectTarget , canonicalSpecs } =
48130 await destination ( specsOrVersions ) ;
49131
0 commit comments