import { raw } from "ilha"; import type { ResolvableHead as Head } from "unhead/types"; import { sanitizeMdxHtmlString } from "../../core/sanitize-mdx-html"; import type { MdxContent } from "./types"; import { filePathToRoutePath, mdxIslandLoaders, mdxIslandSequences, mdxPages } from "./routes"; let prerenderedMdxHtml: string | undefined; function toHtml(content: MdxContent) { return typeof content === "string" ? content : content.value; } function decodeBase64(value: string) { const binary = atob(value); const bytes = Uint8Array.from(binary, (char) => char.charCodeAt(0)); return new TextDecoder().decode(bytes); } type PrerenderDataPayload = { mdxHtml?: string; mdxHtmlBase64?: string; mdxPath?: string; }; export function setPrerenderedMdxHtml(html: string | undefined) { prerenderedMdxHtml = html; } export function getPrerenderedMdxHtml() { return prerenderedMdxHtml; } export function getClientPrerenderedMdxHtml(path: string) { if (typeof document === "undefined") return undefined; const data = document.getElementById("prerender-data")?.textContent; if (!data) return undefined; try { const parsed = JSON.parse(data) as PrerenderDataPayload; const normalizedPath = path.replace(/\/$/, "") || "/"; if (parsed.mdxPath !== normalizedPath) return undefined; if (typeof parsed.mdxHtmlBase64 === "string") return decodeBase64(parsed.mdxHtmlBase64); return typeof parsed.mdxHtml === "string" ? parsed.mdxHtml : undefined; } catch { return undefined; } } function normalizePath(url: string) { return new URL(url, "http://localhost").pathname.replace(/\/$/, "") || "/"; } async function loadMdxModule(url: string) { const pathname = normalizePath(url); const entry = [...mdxPages.entries()].find(([path]) => path === pathname); if (!entry) return undefined; const [path, loader] = entry; const filePath = Object.keys(mdxIslandSequences).find((key) => filePathToRoutePath(key) === path); return { pathname, filePath, mod: await loader() }; } const ISLAND_SLOT_RE = /]*\bdata-ilha-slot=)(?=[^>]*\bdata-ilha-props=)(?![^>]*\bdata-imprensa-mdx-island=)/g; function isMountable(value: unknown): boolean { return ( !!value && (typeof value === "object" || typeof value === "function") && typeof (value as { mount?: unknown }).mount === "function" ); } const islandKeyCache = new Map(); /** * The scanned sequence lists every capitalized component tag, but only islands * emit `data-ilha-slot` divs — auto-bind components (e.g. areia's) render plain * markup. Tagging is positional against slots, so non-island entries must be * dropped or every slot after one would get the wrong key. */ async function islandOnlySequence(filePath: string, sequence: string[]) { const loaders = mdxIslandLoaders[filePath] ?? {}; const flags = await Promise.all( sequence.map(async (key) => { if (key.startsWith("imprensa:")) return true; const cached = islandKeyCache.get(key); if (cached !== undefined) return cached; const loader = loaders[key]; let island = false; if (loader) { try { island = isMountable(await loader()); } catch { island = false; } } islandKeyCache.set(key, island); return island; }), ); return sequence.filter((_, index) => flags[index]); } async function tagMdxIslandSlots(html: string, filePath: string | undefined) { if (!filePath) return html; const sequence = await islandOnlySequence(filePath, mdxIslandSequences[filePath] ?? []); if (!sequence.length) return html; // The sequence is matched positionally against island slots in source order; // when it runs out (extra slots from live island demos or static components) // the remaining slots are left untouched rather than mis-tagged. let index = 0; return html.replace(ISLAND_SLOT_RE, (match) => { const key = sequence[index++]; return key ? `${match} data-imprensa-mdx-island=${JSON.stringify(key)}` : match; }); } export async function renderMdxContent(url: string) { const loaded = await loadMdxModule(url); if (!loaded) return undefined; return sanitizeMdxHtmlString( await tagMdxIslandSlots(toHtml(loaded.mod.default({})), loaded.filePath), ); } export async function getMdxHead(url: string): Promise { return (await loadMdxModule(url))?.mod.head; } export async function renderMdx(url: string) { const loaded = await loadMdxModule(url); if (!loaded) return undefined; return { html: sanitizeMdxHtmlString( await tagMdxIslandSlots(toHtml(loaded.mod.default({})), loaded.filePath), ), path: loaded.pathname, }; } /** Client-only: load doc HTML. */ export async function loadMdxHtml(path: string) { const cached = getPrerenderedMdxHtml() ?? getClientPrerenderedMdxHtml(path); const content = cached ?? (await renderMdxContent(path)); if (!content) return null; const html = typeof content === "string" ? content : toHtml(content); return raw(sanitizeMdxHtmlString(html)); } export function getMdxContent(path: string) { const cached = getPrerenderedMdxHtml() ?? getClientPrerenderedMdxHtml(path); return cached ? raw(sanitizeMdxHtmlString(cached)) : undefined; } export function getMdxContentNeedsAsyncPaint(_path: string): boolean { return false; }