UNPKG

@tanstack/solid-router

Version:

Modern and scalable routing for Solid applications

1 lines 9.12 kB
{"version":3,"file":"headContentUtils.cjs","names":["Solid","escapeHtml","useRouter","useRouterState","RouterManagedTag","useTags","router","nonce","options","ssr","routeMeta","select","state","matches","map","match","meta","filter","Boolean","Accessor","Array","createMemo","resultMeta","metaByAttribute","Record","title","routeMetasArray","i","length","metas","j","m","tag","children","json","JSON","stringify","push","attrs","type","attribute","name","property","content","reverse","links","constructed","flat","link","manifest","assets","routes","routeId","asset","preloadLinks","looseRoutesById","forEach","route","id","preloads","preload","rel","href","styles","style","headScripts","script","uniqBy","d","arr","T","fn","item","seen","Set","key","has","add"],"sources":["../../src/headContentUtils.tsx"],"sourcesContent":["import * as Solid from 'solid-js'\nimport { escapeHtml } from '@tanstack/router-core'\nimport { useRouter } from './useRouter'\nimport { useRouterState } from './useRouterState'\nimport type { RouterManagedTag } from '@tanstack/router-core'\n\n/**\n * Build the list of head/link/meta/script tags to render for active matches.\n * Used internally by `HeadContent`.\n */\nexport const useTags = () => {\n const router = useRouter()\n const nonce = router.options.ssr?.nonce\n const routeMeta = useRouterState({\n select: (state) => {\n return state.matches.map((match) => match.meta!).filter(Boolean)\n },\n })\n\n const meta: Solid.Accessor<Array<RouterManagedTag>> = Solid.createMemo(() => {\n const resultMeta: Array<RouterManagedTag> = []\n const metaByAttribute: Record<string, true> = {}\n let title: RouterManagedTag | undefined\n const routeMetasArray = routeMeta()\n for (let i = routeMetasArray.length - 1; i >= 0; i--) {\n const metas = routeMetasArray[i]!\n for (let j = metas.length - 1; j >= 0; j--) {\n const m = metas[j]\n if (!m) continue\n\n if (m.title) {\n if (!title) {\n title = {\n tag: 'title',\n children: m.title,\n }\n }\n } else if ('script:ld+json' in m) {\n // Handle JSON-LD structured data\n // Content is HTML-escaped to prevent XSS when injected via innerHTML\n try {\n const json = JSON.stringify(m['script:ld+json'])\n resultMeta.push({\n tag: 'script',\n attrs: {\n type: 'application/ld+json',\n },\n children: escapeHtml(json),\n })\n } catch {\n // Skip invalid JSON-LD objects\n }\n } else {\n const attribute = m.name ?? m.property\n if (attribute) {\n if (metaByAttribute[attribute]) {\n continue\n } else {\n metaByAttribute[attribute] = true\n }\n }\n\n resultMeta.push({\n tag: 'meta',\n attrs: {\n ...m,\n nonce,\n },\n })\n }\n }\n }\n\n if (title) {\n resultMeta.push(title)\n }\n\n if (router.options.ssr?.nonce) {\n resultMeta.push({\n tag: 'meta',\n attrs: {\n property: 'csp-nonce',\n content: router.options.ssr.nonce,\n },\n })\n }\n resultMeta.reverse()\n\n return resultMeta\n })\n\n const links = useRouterState({\n select: (state) => {\n const constructed = state.matches\n .map((match) => match.links!)\n .filter(Boolean)\n .flat(1)\n .map((link) => ({\n tag: 'link',\n attrs: {\n ...link,\n nonce,\n },\n })) satisfies Array<RouterManagedTag>\n\n const manifest = router.ssr?.manifest\n\n const assets = state.matches\n .map((match) => manifest?.routes[match.routeId]?.assets ?? [])\n .filter(Boolean)\n .flat(1)\n .filter((asset) => asset.tag === 'link')\n .map(\n (asset) =>\n ({\n tag: 'link',\n attrs: { ...asset.attrs, nonce },\n }) satisfies RouterManagedTag,\n )\n\n return [...constructed, ...assets]\n },\n })\n\n const preloadLinks = useRouterState({\n select: (state) => {\n const preloadLinks: Array<RouterManagedTag> = []\n\n state.matches\n .map((match) => router.looseRoutesById[match.routeId]!)\n .forEach((route) =>\n router.ssr?.manifest?.routes[route.id]?.preloads\n ?.filter(Boolean)\n .forEach((preload) => {\n preloadLinks.push({\n tag: 'link',\n attrs: {\n rel: 'modulepreload',\n href: preload,\n nonce,\n },\n })\n }),\n )\n\n return preloadLinks\n },\n })\n\n const styles = useRouterState({\n select: (state) =>\n (\n state.matches\n .map((match) => match.styles!)\n .flat(1)\n .filter(Boolean) as Array<RouterManagedTag>\n ).map(({ children, ...style }) => ({\n tag: 'style',\n attrs: {\n ...style,\n nonce,\n },\n children,\n })),\n })\n\n const headScripts = useRouterState({\n select: (state) =>\n (\n state.matches\n .map((match) => match.headScripts!)\n .flat(1)\n .filter(Boolean) as Array<RouterManagedTag>\n ).map(({ children, ...script }) => ({\n tag: 'script',\n attrs: {\n ...script,\n nonce,\n },\n children,\n })),\n })\n\n return () =>\n uniqBy(\n [\n ...meta(),\n ...preloadLinks(),\n ...links(),\n ...styles(),\n ...headScripts(),\n ] as Array<RouterManagedTag>,\n (d) => {\n return JSON.stringify(d)\n },\n )\n}\n\nexport function uniqBy<T>(arr: Array<T>, fn: (item: T) => string) {\n const seen = new Set<string>()\n return arr.filter((item) => {\n const key = fn(item)\n if (seen.has(key)) {\n return false\n }\n seen.add(key)\n return true\n })\n}\n"],"mappings":"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"}