UNPKG

@tanstack/solid-router

Version:

Modern and scalable routing for Solid applications

1 lines 13.5 kB
{"version":3,"file":"Asset.cjs","names":["<Title attrs={attrs} children={children} />","<HeadElement tag=\"meta\" attrs={attrs} />","tag=\"meta\"","<HeadElement tag=\"link\" attrs={attrs} />","tag=\"link\"","<InlineCssStyle attrs={attrs}>{children}</InlineCssStyle>","<HeadElement tag=\"style\" attrs={attrs} children={children} />","tag=\"style\"","<Script attrs={attrs} children={children} />","<HeadElement\n tag=\"style\"\n attrs={{ ...attrs, [INLINE_CSS_HYDRATION_ATTR]: '' }}\n children={html}\n />","children={html}","ref={(e) => (el = e)}","<title ref={(e) => (el = e)} {...props.attrs}>\n {props.children as string}\n </title>","<script {...attrs} />","innerHTML={children}","<script {...attrs} innerHTML={children} />"],"sources":["../../src/Asset.tsx"],"sourcesContent":["import { isServer } from '@tanstack/router-core/isServer'\nimport { createEffect, onCleanup, onSettled } from 'solid-js'\nimport { useRouter } from './useRouter'\nimport type { RouterManagedTag } from '@tanstack/router-core'\nimport type { JSX } from '@solidjs/web'\n\nconst INLINE_CSS_HYDRATION_ATTR = 'data-tsr-inline-css'\n\nexport function Asset(asset: RouterManagedTag): JSX.Element | null {\n const { tag, attrs, children } = asset\n\n switch (tag) {\n case 'title':\n return <Title attrs={attrs} children={children} />\n case 'meta':\n return <HeadElement tag=\"meta\" attrs={attrs} />\n case 'link':\n return <HeadElement tag=\"link\" attrs={attrs} />\n case 'style':\n if (\n asset.inlineCss &&\n (process.env.TSS_INLINE_CSS_ENABLED === 'true' ||\n (process.env.TSS_INLINE_CSS_ENABLED === undefined && isServer))\n ) {\n return <InlineCssStyle attrs={attrs}>{children}</InlineCssStyle>\n }\n\n return <HeadElement tag=\"style\" attrs={attrs} children={children} />\n case 'script':\n return <Script attrs={attrs} children={children} />\n default:\n return null\n }\n}\n\n// On the client, relocate a rendered head element into document.head so head\n// tags end up in the right place even when <HeadContent /> is rendered in\n// <body>. The *same* node that Solid rendered/hydrated is moved — never\n// recreated — so the server-rendered node stays claimed (its hydration id is\n// preserved) and stylesheets/scripts are not refetched or re-executed.\n//\n// When <HeadContent /> is placed in <head> (the SSR/hydration case), the node\n// is already in document.head, so this is a no-op and the element is left\n// exactly where Solid hydrated it.\nfunction useRelocateToHead(getEl: () => Node | undefined) {\n onSettled(() => {\n const el = getEl()\n if (el && el.parentNode !== document.head) {\n document.head.appendChild(el)\n }\n })\n\n onCleanup(() => {\n const el = getEl()\n if (el?.parentNode) {\n el.parentNode.removeChild(el)\n }\n })\n}\n\nfunction InlineCssStyle({\n attrs,\n children,\n}: {\n attrs?: Record<string, any>\n children?: RouterManagedTag['children']\n}) {\n const isInlineCssPlaceholder = children === undefined\n const html = isInlineCssPlaceholder\n ? typeof document === 'undefined'\n ? ''\n : (document.querySelector<HTMLStyleElement>(\n `style[${INLINE_CSS_HYDRATION_ATTR}]`,\n )?.textContent ?? '')\n : (children ?? '')\n\n return (\n <HeadElement\n tag=\"style\"\n attrs={{ ...attrs, [INLINE_CSS_HYDRATION_ATTR]: '' }}\n children={html}\n />\n )\n}\n\ninterface ScriptAttrs {\n [key: string]: string | boolean | undefined\n src?: string\n}\n\nfunction HeadElement(props: {\n tag: 'meta' | 'link' | 'style'\n attrs?: Record<string, any>\n children?: unknown\n}): JSX.Element | null {\n // Each branch must live in its own `case` block so the Solid compiler emits\n // exactly ONE getNextElement() per invocation. Sequential `if ... return`\n // statements compile to multiple getNextElement() calls that all execute,\n // desyncing the hydration key counter (causing \"expected <style>\" mismatches\n // and unclaimed nodes).\n //\n // We capture the element via the JSX return value (a real DOM node in Solid)\n // rather than a `ref` attribute: a `ref` compiles to a _$ref() call that\n // interferes with attribute spreading on hydration-claimed nodes (wiping the\n // SSR attributes).\n let element: Element\n switch (props.tag) {\n case 'style': {\n const attrs = {\n ...props.attrs,\n innerHTML:\n typeof props.children === 'string' ? props.children : undefined,\n }\n element = (<style {...attrs} />) as unknown as Element\n break\n }\n case 'meta':\n element = (<meta {...props.attrs} />) as unknown as Element\n break\n default:\n element = (<link {...props.attrs} />) as unknown as Element\n }\n\n // Move the rendered/hydrated element into <head> when <HeadContent /> is\n // placed in <body>. No-op when already in <head>. Called unconditionally so\n // server and client register the same primitives (see Title).\n useRelocateToHead(() => element)\n\n return element as unknown as JSX.Element\n}\n\nfunction Title(props: {\n attrs?: Record<string, any>\n children?: unknown\n}): JSX.Element | null {\n let el: HTMLTitleElement | undefined\n\n // IMPORTANT: call these hooks UNCONDITIONALLY (do not guard with isServer).\n // Solid's hydration relies on the component body registering the same\n // reactive primitives in the same order on the server and the client. An\n // `if (!isServer)` guard would skip them on the server but run them on the\n // client, desyncing the hydration owner tree and causing the server <title>\n // to be left unclaimed (and a duplicate appended). These hooks are no-ops\n // during SSR anyway.\n\n // Move the rendered/hydrated <title> into <head> when <HeadContent /> is\n // placed in <body>. No-op when already in <head>.\n useRelocateToHead(() => el)\n\n // Keep document.title in sync during client-side navigation.\n createEffect(\n () => props.children,\n (titleText) => {\n document.title = typeof titleText === 'string' ? titleText : ''\n },\n )\n\n return (\n <title ref={(e) => (el = e)} {...props.attrs}>\n {props.children as string}\n </title>\n )\n}\n\nfunction Script(props: {\n attrs?: Record<string, any>\n children?: unknown\n}): JSX.Element | null {\n const router = useRouter()\n const attrs = props.attrs\n const children = props.children\n\n const dataScript =\n typeof attrs?.type === 'string' &&\n attrs.type !== '' &&\n attrs.type !== 'text/javascript' &&\n attrs.type !== 'module'\n\n // --- Server rendering ---\n if (isServer ?? router.isServer) {\n if (attrs?.src) {\n return <script {...attrs} />\n }\n\n if (typeof children === 'string') {\n return <script {...attrs} innerHTML={children} />\n }\n\n return null\n }\n\n // --- Client rendering ---\n\n // Data scripts (e.g. application/ld+json) are rendered in the tree;\n // they don't need to execute.\n if (dataScript && typeof children === 'string') {\n return <script {...attrs} innerHTML={children} />\n }\n\n // For executable scripts, use imperative DOM injection so the browser\n // actually executes them during client-side navigation. The injection is\n // idempotent (it checks for an already-present matching script), so the\n // server-rendered script node is reused rather than duplicated.\n createEffect(\n () => ({ attrs, children, dataScript }) as const,\n ({ attrs, children, dataScript }) => {\n if (dataScript) return\n\n let script: HTMLScriptElement | undefined\n\n if (attrs?.src) {\n const normSrc = (() => {\n try {\n const base = document.baseURI || window.location.href\n return new URL(attrs.src, base).href\n } catch {\n return attrs.src\n }\n })()\n const existingScript = Array.from(\n document.querySelectorAll('script[src]'),\n ).find((el) => (el as HTMLScriptElement).src === normSrc)\n\n if (existingScript) {\n return\n }\n\n script = document.createElement('script')\n\n for (const [key, value] of Object.entries(attrs)) {\n if (value !== undefined && value !== false) {\n script.setAttribute(\n key,\n typeof value === 'boolean' ? '' : String(value),\n )\n }\n }\n\n document.head.appendChild(script)\n } else if (typeof children === 'string') {\n const typeAttr =\n typeof attrs?.type === 'string' ? attrs.type : 'text/javascript'\n const nonceAttr =\n typeof attrs?.nonce === 'string' ? attrs.nonce : undefined\n const existingScript = Array.from(\n document.querySelectorAll('script:not([src])'),\n ).find((el) => {\n if (!(el instanceof HTMLScriptElement)) return false\n const sType = el.getAttribute('type') ?? 'text/javascript'\n const sNonce = el.getAttribute('nonce') ?? undefined\n return (\n el.textContent === children &&\n sType === typeAttr &&\n sNonce === nonceAttr\n )\n })\n\n if (existingScript) {\n return\n }\n\n script = document.createElement('script')\n script.textContent = children\n\n if (attrs) {\n for (const [key, value] of Object.entries(attrs)) {\n if (value !== undefined && value !== false) {\n script.setAttribute(\n key,\n typeof value === 'boolean' ? '' : String(value),\n )\n }\n }\n }\n\n document.head.appendChild(script)\n }\n\n return () => {\n if (script?.parentNode) {\n script.parentNode.removeChild(script)\n }\n }\n },\n )\n\n return null\n}\n"],"mappings":"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"}