/* ============================================================================ 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 | 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) : 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, }; }