"use client"; /** * Component to render collected meta descriptors in the document head. * * Supports both sync and async meta descriptors. Async descriptors * (Promise) are resolved using React's use() hook. * * When theme is enabled in the router config, MetaTags also renders * the theme initialization script to prevent FOUC (flash of unstyled content). * * @example * ```tsx * function RootLayout() { * return ( * * * * * ... * * ); * } * ``` */ import { use } from "react"; import { useHandle } from "../browser/react/use-handle.js"; import { Meta } from "./meta.js"; import type { MetaDescriptor, MetaDescriptorBase } from "../router/types.js"; import { useThemeContext } from "../theme/theme-context.js"; import { generateThemeScript } from "../theme/theme-script.js"; import { useNonce } from "../browser/react/nonce-context.js"; // Type guards for MetaDescriptorBase variants function hasCharSet(d: MetaDescriptorBase): d is { charSet: "utf-8" } { return "charSet" in d && d.charSet === "utf-8"; } function hasTitle(d: MetaDescriptorBase): d is { title: string } { return "title" in d && typeof (d as { title?: unknown }).title === "string"; } function hasNameContent( d: MetaDescriptorBase, ): d is { name: string; content: string } { return ( "name" in d && "content" in d && typeof (d as { name?: unknown }).name === "string" && typeof (d as { content?: unknown }).content === "string" ); } function hasPropertyContent( d: MetaDescriptorBase, ): d is { property: string; content: string } { return ( "property" in d && "content" in d && typeof (d as { property?: unknown }).property === "string" && typeof (d as { content?: unknown }).content === "string" ); } function hasHttpEquivContent( d: MetaDescriptorBase, ): d is { httpEquiv: string; content: string } { return ( "httpEquiv" in d && "content" in d && typeof (d as { httpEquiv?: unknown }).httpEquiv === "string" && typeof (d as { content?: unknown }).content === "string" ); } function hasScriptLdJson( d: MetaDescriptorBase, ): d is { "script:ld+json": object } { return "script:ld+json" in d; } function hasTagName( d: MetaDescriptorBase, ): d is { tagName: "meta" | "link"; [name: string]: string } { return ( "tagName" in d && ((d as { tagName?: unknown }).tagName === "meta" || (d as { tagName?: unknown }).tagName === "link") ); } /** * Check if a value is a Promise. */ function isPromise(value: unknown): value is Promise { return value !== null && typeof value === "object" && "then" in value; } /** * Render a single meta descriptor as a React element. */ function renderMetaDescriptor( descriptor: MetaDescriptorBase, index: number, ): React.ReactNode { // charset if (hasCharSet(descriptor)) { return ; } // title if (hasTitle(descriptor)) { return {descriptor.title}; } // name + content (description, viewport, etc.) if (hasNameContent(descriptor)) { return ( ); } // property + content (Open Graph, etc.) if (hasPropertyContent(descriptor)) { return ( ); } // http-equiv + content if (hasHttpEquivContent(descriptor)) { return ( ); } // JSON-LD structured data if (hasScriptLdJson(descriptor)) { const json = JSON.stringify(descriptor["script:ld+json"]); return (