everything-dev
Version:
A consolidated product package for building Module Federation apps with oRPC APIs.
1 lines • 8.42 kB
Source Map (JSON)
{"version":3,"file":"router.mjs","names":[],"sources":["../../src/ui/router.ts"],"sourcesContent":["import type { AnyRouter } from \"@tanstack/react-router\";\nimport type { HeadData, HeadLink, HeadMeta, HeadScript } from \"./types\";\n\nexport function getMetaKey(meta: HeadMeta): string {\n if (!meta) return \"null\";\n if (\"title\" in meta) return \"title\";\n if (\"charSet\" in meta) return \"charSet\";\n if (\"name\" in meta) return `name:${(meta as { name: string }).name}`;\n if (\"property\" in meta) {\n return `property:${(meta as { property: string }).property}`;\n }\n if (\"httpEquiv\" in meta) {\n return `httpEquiv:${(meta as { httpEquiv: string }).httpEquiv}`;\n }\n return JSON.stringify(meta);\n}\n\nexport function getLinkKey(link: HeadLink): string {\n const rel = (link as { rel?: string }).rel ?? \"\";\n const href = (link as { href?: string }).href ?? \"\";\n return `${rel}:${href}`;\n}\n\nexport function getScriptKey(script: HeadScript): string {\n if (!script) return \"null\";\n if (\"src\" in script && script.src) return `src:${script.src}`;\n if (\"children\" in script && script.children) {\n return `children:${typeof script.children === \"string\" ? script.children : JSON.stringify(script.children)}`;\n }\n return JSON.stringify(script);\n}\n\nexport async function collectHeadData(router: AnyRouter): Promise<HeadData> {\n await router.load();\n\n const metaMap = new Map<string, HeadMeta>();\n const linkMap = new Map<string, HeadLink>();\n const scriptMap = new Map<string, HeadScript>();\n\n for (const match of router.state.matches) {\n const route =\n (\n router as AnyRouter & {\n routesById?: Record<string, { options?: { head?: (...args: unknown[]) => unknown } }>;\n }\n ).routesById?.[(match as { routeId: string }).routeId] ??\n (match as { route?: { options?: { head?: (...args: unknown[]) => unknown } } }).route;\n const headFn = route?.options?.head;\n if (!headFn) continue;\n\n try {\n const headResult = (await headFn({\n loaderData: match.loaderData,\n matches: router.state.matches,\n match,\n params: match.params,\n })) as {\n meta?: HeadMeta[];\n links?: HeadLink[];\n scripts?: HeadScript[];\n };\n\n if (headResult?.meta) {\n for (const meta of headResult.meta) {\n metaMap.set(getMetaKey(meta), meta);\n }\n }\n if (headResult?.links) {\n for (const link of headResult.links) {\n linkMap.set(getLinkKey(link), link);\n }\n }\n if (headResult?.scripts) {\n for (const script of headResult.scripts) {\n scriptMap.set(getScriptKey(script), script);\n }\n }\n } catch (error) {\n console.warn(`[collectHeadData] head() failed for ${match.routeId}:`, error);\n }\n }\n\n return {\n meta: [...metaMap.values()],\n links: [...linkMap.values()],\n scripts: [...scriptMap.values()],\n };\n}\n\nfunction escapeHtml(s: string): string {\n return s\n .replace(/&/g, \"&\")\n .replace(/\"/g, \""\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\");\n}\n\nfunction serializeMeta(meta: HeadMeta): string {\n if (!meta) return \"\";\n if (\"charSet\" in meta) return `<meta charset=\"${escapeHtml(String(meta.charSet))}\">`;\n if (\"title\" in meta) return `<title>${escapeHtml(String(meta.title))}</title>`;\n const attrs = Object.entries(meta as Record<string, unknown>)\n .filter(([, v]) => v != null)\n .map(([k, v]) => `${k}=\"${escapeHtml(String(v))}\"`)\n .join(\" \");\n return `<meta ${attrs}>`;\n}\n\nfunction serializeLink(link: HeadLink): string {\n if (!link) return \"\";\n const attrs = Object.entries(link as Record<string, unknown>)\n .filter(([, v]) => v != null)\n .map(([k, v]) => `${k}=\"${escapeHtml(String(v))}\"`)\n .join(\" \");\n return `<link ${attrs}>`;\n}\n\nfunction serializeScript(script: HeadScript, cspNonce?: string): string {\n if (!script) return \"\";\n if (\"src\" in script && script.src) {\n const attrs: string[] = [`src=\"${escapeHtml(script.src)}\"`];\n if (script.integrity) attrs.push(`integrity=\"${escapeHtml(script.integrity)}\"`);\n if (script.crossOrigin) attrs.push(`crossorigin=\"${escapeHtml(script.crossOrigin)}\"`);\n if (script.type) attrs.push(`type=\"${escapeHtml(script.type)}\"`);\n if (cspNonce) attrs.push(`nonce=\"${cspNonce}\"`);\n return `<script ${attrs.join(\" \")}></script>`;\n }\n if (\"children\" in script) {\n const attrs: string[] = [];\n if (script.type) attrs.push(`type=\"${escapeHtml(script.type)}\"`);\n if (cspNonce) attrs.push(`nonce=\"${cspNonce}\"`);\n const content =\n typeof script.children === \"string\" ? script.children : JSON.stringify(script.children);\n return `<script${attrs.length ? ` ${attrs.join(\" \")}` : \"\"}>${content}</script>`;\n }\n return \"\";\n}\n\nexport function serializeHeadData(\n headData: HeadData,\n cspNonce?: string,\n): { metaHtml: string; linkHtml: string; scriptHtml: string } {\n return {\n metaHtml: headData.meta\n .filter(Boolean)\n .map((m) => serializeMeta(m))\n .join(\"\\n\"),\n linkHtml: headData.links\n .filter(Boolean)\n .map((l) => serializeLink(l))\n .join(\"\\n\"),\n scriptHtml: headData.scripts\n .filter(Boolean)\n .map((s) => serializeScript(s, cspNonce))\n .join(\"\\n\"),\n };\n}\n"],"mappings":"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"}