From 5ff089f5c7c013f2fcd8bbc238e23340f0164286 Mon Sep 17 00:00:00 2001 From: Maggie Negm Date: Thu, 19 Dec 2024 19:18:22 -0500 Subject: [PATCH] feat: add dynamic metadata for search results --- src/app/layout.tsx | 2 +- src/app/page.tsx | 48 ++++++++++++++++++------------------------ src/utils/fetchData.ts | 44 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 65 insertions(+), 29 deletions(-) create mode 100644 src/utils/fetchData.ts diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 7fc844e7..8341074a 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -6,7 +6,7 @@ const raleway = Raleway({ subsets: ["latin"] }); export const metadata = { description: - "Check if your npm packages are eligible for Tidelift funding. 💸<", + "Check if your npm packages are eligible for Tidelift funding. 💸", title: "Tidelift Me Up", }; diff --git a/src/app/page.tsx b/src/app/page.tsx index 9092a144..d5a62517 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,41 +1,37 @@ -import { - EstimatedPackage, - PackageOwnership, - tideliftMeUp, -} from "tidelift-me-up"; - import { Footer } from "~/components/Footer"; import { MainArea } from "~/components/MainArea"; import { OptionsForm } from "~/components/OptionsForm"; import { ResultDisplay } from "~/components/ResultDisplay"; import { ScrollButton } from "~/components/ScrollButton"; +import { SearchParamsType, fetchData } from "~/utils/fetchData"; +import { metadata as defaultMetadata } from "./layout"; import styles from "./page.module.css"; export interface HomeProps { - searchParams: Record; + searchParams: SearchParamsType; } -export default async function Home({ searchParams }: HomeProps) { - const options = { - ownership: undefinedIfEmpty( - [ - searchParams["author"] === "on" && "author", - searchParams["maintainer"] === "on" && "maintainer", - searchParams["publisher"] === "on" && "publisher", - ].filter(Boolean) as PackageOwnership[], - ), - since: (searchParams["since"] || undefined) as string | undefined, - username: searchParams.username as string, - }; - let result: Error | EstimatedPackage[] | undefined; +export async function generateMetadata({ searchParams }: HomeProps) { + const { options, result } = await fetchData(searchParams); + const username = options.username || ""; + const packageCount = Array.isArray(result) ? result.length : 0; - try { - result = options.username ? await tideliftMeUp(options) : undefined; - } catch (error) { - result = error as Error; + if (!username) { + return defaultMetadata; } + return { + description: `${username} has ${packageCount} npm package${ + packageCount === 1 ? "" : "s" + } eligible for Tidelift funding. 💸`, + title: `${username} | Tidelift Me Up`, + }; +} + +export default async function Home({ searchParams }: HomeProps) { + const { options, result } = await fetchData(searchParams); + return ( <> @@ -51,7 +47,3 @@ export default async function Home({ searchParams }: HomeProps) { ); } - -function undefinedIfEmpty(items: T[]) { - return items.length === 0 ? undefined : items; -} diff --git a/src/utils/fetchData.ts b/src/utils/fetchData.ts new file mode 100644 index 00000000..d738f9af --- /dev/null +++ b/src/utils/fetchData.ts @@ -0,0 +1,44 @@ +import { + EstimatedPackage, + PackageOwnership, + tideliftMeUp, +} from "tidelift-me-up"; + +export type OptionsType = Record; +export type SearchParamsType = Record; + +export async function fetchData(searchParams: SearchParamsType) { + const options = getOptions(searchParams); + const result = await getTideliftData(options); + return { options, result }; +} + +function getOptions(searchParams: SearchParamsType) { + return { + ownership: undefinedIfEmpty( + [ + searchParams.author === "on" && "author", + searchParams.maintainer === "on" && "maintainer", + searchParams.publisher === "on" && "publisher", + ].filter(Boolean) as PackageOwnership[], + ), + since: (searchParams.since || undefined) as string | undefined, + username: searchParams.username as string, + }; +} + +async function getTideliftData(options: OptionsType) { + let result: Error | EstimatedPackage[] | undefined; + + try { + result = options.username ? await tideliftMeUp(options) : undefined; + } catch (error) { + result = error as Error; + } + + return result; +} + +function undefinedIfEmpty(items: T[]) { + return items.length === 0 ? undefined : items; +}