UNPKG

@tanstack/solid-router

Version:

Modern and scalable routing for Solid applications

1 lines 12.2 kB
{"version":3,"file":"headContentUtils.cjs","names":[],"sources":["../../src/headContentUtils.tsx"],"sourcesContent":["import * as Solid from 'solid-js'\nimport {\n _getAssetMatches,\n appendUniqueUserTags,\n escapeHtml,\n getAssetCrossOrigin,\n getScriptPreloadAttrs,\n resolveManifestCssLink,\n} from '@tanstack/router-core'\nimport { isServer } from '@tanstack/router-core/isServer'\nimport { useRouter } from './useRouter'\nimport type { HeadTag } from '@solidjs/web'\nimport type {\n AssetCrossOriginConfig,\n RouterManagedTag,\n} from '@tanstack/router-core'\n\n/**\n * Build the head/link/meta/script tags from the renderable presented prefix.\n * Used internally by `HeadContent`.\n */\nexport const useTags = (assetCrossOrigin?: AssetCrossOriginConfig) => {\n const router = useRouter()\n const nonce = router.options.ssr?.nonce\n const activeMatches = Solid.createMemo(() =>\n _getAssetMatches(router.stores.matches.get()),\n )\n const routeMeta = Solid.createMemo(() =>\n activeMatches()\n .map((match) => match.meta)\n .filter((meta) => meta !== undefined),\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) {\n continue\n }\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 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 }\n metaByAttribute[attribute] = true\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 (nonce) {\n resultMeta.push({\n tag: 'meta',\n attrs: {\n property: 'csp-nonce',\n content: nonce,\n },\n })\n }\n resultMeta.reverse()\n return resultMeta\n })\n\n const links = Solid.createMemo(() => {\n return activeMatches()\n .flatMap((match) => match.links ?? [])\n .filter((link) => link !== undefined)\n .map((link) => ({\n tag: 'link',\n attrs: {\n ...link,\n nonce,\n },\n })) satisfies Array<RouterManagedTag>\n })\n\n const retainedManifestCssTags = new Map<string, RouterManagedTag>()\n const manifestCssTags = Solid.createMemo(() => {\n const manifest = router.ssr?.manifest\n const tags: Array<RouterManagedTag> = []\n\n if (!manifest) {\n return tags\n }\n\n for (const match of activeMatches()) {\n for (const link of manifest.routes[match.routeId]?.css ?? []) {\n const resolvedLink = resolveManifestCssLink(link)\n const tag: RouterManagedTag = {\n tag: 'link',\n attrs: {\n rel: 'stylesheet',\n ...resolvedLink,\n crossOrigin:\n getAssetCrossOrigin(assetCrossOrigin, 'stylesheet') ??\n resolvedLink.crossOrigin,\n nonce,\n },\n }\n const key = JSON.stringify(tag)\n if (!retainedManifestCssTags.has(key)) {\n retainedManifestCssTags.set(key, tag)\n }\n }\n }\n\n // Lazy modules are cached and do not reinsert their CSS when revisited.\n tags.push(...retainedManifestCssTags.values())\n\n if (manifest.inlineStyle) {\n tags.push({\n tag: 'style',\n attrs: {\n ...manifest.inlineStyle.attrs,\n nonce,\n },\n children: manifest.inlineStyle.children,\n inlineCss: true,\n })\n }\n\n return tags\n })\n\n const preloadLinks = Solid.createMemo(() => {\n const manifest = router.ssr?.manifest\n const tags: Array<RouterManagedTag> = []\n for (const match of activeMatches()) {\n for (const preload of manifest?.routes[match.routeId]?.preloads ?? []) {\n if (!preload) {\n continue\n }\n tags.push({\n tag: 'link',\n attrs: {\n ...getScriptPreloadAttrs(manifest, preload, assetCrossOrigin),\n nonce,\n },\n })\n }\n }\n return tags\n })\n\n const styles = Solid.createMemo(() => {\n return activeMatches()\n .flatMap((match) => match.styles ?? [])\n .filter((style) => style !== undefined)\n .map(({ children, ...attrs }) => ({\n tag: 'style',\n attrs: {\n ...attrs,\n nonce,\n },\n children: children as string | undefined,\n })) satisfies Array<RouterManagedTag>\n })\n\n const headScripts = Solid.createMemo(() => {\n return activeMatches()\n .flatMap((match) => match.headScripts ?? [])\n .filter((script) => script !== undefined)\n .map(({ children, ...attrs }) => ({\n tag: 'script',\n attrs: {\n ...attrs,\n nonce,\n },\n children: children as string | undefined,\n })) satisfies Array<RouterManagedTag>\n })\n\n return Solid.createMemo(() => {\n const next: Array<RouterManagedTag> = []\n appendUniqueUserTags(next, meta())\n appendUniqueUserTags(next, links())\n next.push(...manifestCssTags())\n next.push(...preloadLinks())\n appendUniqueUserTags(next, styles())\n appendUniqueUserTags(next, headScripts())\n return next\n })\n}\n\nconst INLINE_CSS_HYDRATION_ATTR = 'data-tsr-inline-css'\n\n/**\n * Convert the router-managed tags into head registry descriptors for\n * `useHead`. Pure data mapping — it is evaluated inside the registry's own\n * flush boundary (server) / effect (client), so it must not allocate\n * reactive owners.\n */\nexport function toHeadTags(tags: Array<RouterManagedTag>): Array<HeadTag> {\n return tags.map(toHeadTag)\n}\n\nfunction toHeadTag(t: RouterManagedTag): HeadTag {\n const props: Record<string, any> = { ...t.attrs }\n let children: string | undefined = t.children\n\n if (\n t.tag === 'style' &&\n t.inlineCss &&\n (process.env.TSS_INLINE_CSS_ENABLED === 'true' ||\n (process.env.TSS_INLINE_CSS_ENABLED === undefined && isServer))\n ) {\n // Mark the inline-CSS style so the client can find it again: the\n // serialized manifest omits the CSS text, so on the client the tag\n // arrives with `children === undefined` and the text is recovered from\n // the server-rendered element.\n props[INLINE_CSS_HYDRATION_ATTR] = ''\n }\n\n if (\n t.tag === 'style' &&\n t.inlineCss &&\n children === undefined &&\n typeof document !== 'undefined'\n ) {\n children =\n document.querySelector<HTMLStyleElement>(\n `style[${INLINE_CSS_HYDRATION_ATTR}]`,\n )?.textContent ?? ''\n }\n\n if (children !== undefined) {\n props.children = children\n }\n\n const headTag: HeadTag = { tag: t.tag, props }\n\n // Inline scripts and styles have no natural registry identity: the\n // registry assigns them per-runtime unique ids, which can never match\n // between server and client, so hydration would append a client copy\n // next to the server-rendered one. A stable content-derived key gives\n // both runtimes the same identity to reconcile on.\n if (t.tag === 'style' || (t.tag === 'script' && props.src === undefined)) {\n headTag.key = `tsr-${hashString(t.tag + JSON.stringify(props))}`\n }\n\n return headTag\n}\n\nfunction hashString(s: string): string {\n let h = 5381\n for (let i = 0; i < s.length; i++) {\n h = ((h << 5) + h + s.charCodeAt(i)) | 0\n }\n return (h >>> 0).toString(36)\n}\n"],"mappings":"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"}