Files
sharper.ly/apps/web/app/routes/_events-opportunities/root-seo.ts
T
2026-09-11 02:46:56 +02:00

72 lines
2.5 KiB
TypeScript

/* ============================================================================
ROOT LOADER DATA, READ FROM `meta`
----------------------------------------------------------------------------
`buildMeta` needs the locale, the pathname and the site origin, and all
three are resolved once per request by the root loader. A `meta` function
reaches them through its `matches`.
React Router 8 names that property `loaderData` on every match (see
`CreateMetaArgs` in react-router/lib/types/route-module-annotations.d.ts and
the `Meta` component that builds the array). Version 7 called it `data`, and
plenty of examples still do, so both are read here.
Defensive rather than indexed: `matches[0]` has no data at all while the
root loader is in flight or on an error render, and a `meta` that throws
takes the whole document down with it.
========================================================================= */
import { DEFAULT_LOCALE, isLocale, localizedPath, type Locale } from "~/i18n";
export interface RootSeo {
readonly locale: Locale;
/** Pathname INCLUDING the locale prefix. */
readonly pathname: string;
/** Site origin without a trailing slash. */
readonly siteUrl: string;
}
/** Matches the default in ~/lib/env.server, for the case root data is absent. */
const FALLBACK_SITE_URL = "https://sharper.ly";
function readMatchData(match: unknown): Record<string, unknown> | undefined {
if (!match || typeof match !== "object") return undefined;
const { id, loaderData, data } = match as {
id?: unknown;
loaderData?: unknown;
data?: unknown;
};
if (id !== "root") return undefined;
const payload = loaderData ?? data;
return payload && typeof payload === "object"
? (payload as Record<string, unknown>)
: undefined;
}
/** The root loader's `{ locale, pathname, siteUrl }`, with safe fallbacks. */
export function readRootSeo(matches: readonly unknown[]): RootSeo {
for (const match of matches) {
const record = readMatchData(match);
if (!record) continue;
const locale = isLocale(record.locale) ? record.locale : DEFAULT_LOCALE;
return {
locale,
pathname:
typeof record.pathname === "string"
? record.pathname
: localizedPath("/", locale),
siteUrl:
typeof record.siteUrl === "string" ? record.siteUrl : FALLBACK_SITE_URL,
};
}
return {
locale: DEFAULT_LOCALE,
pathname: localizedPath("/", DEFAULT_LOCALE),
siteUrl: FALLBACK_SITE_URL,
};
}