@tanstack/react-router 1.136.8 → 1.136.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/Asset.cjs +7 -1
- package/dist/cjs/Asset.cjs.map +1 -1
- package/dist/cjs/HeadContent.cjs +5 -20
- package/dist/cjs/HeadContent.cjs.map +1 -1
- package/dist/cjs/ScriptOnce.cjs +1 -1
- package/dist/cjs/ScriptOnce.cjs.map +1 -1
- package/dist/cjs/Scripts.cjs +7 -0
- package/dist/cjs/Scripts.cjs.map +1 -1
- package/dist/cjs/ssr/renderRouterToString.cjs +1 -1
- package/dist/cjs/ssr/renderRouterToString.cjs.map +1 -1
- package/dist/esm/Asset.js +7 -1
- package/dist/esm/Asset.js.map +1 -1
- package/dist/esm/HeadContent.js +5 -20
- package/dist/esm/HeadContent.js.map +1 -1
- package/dist/esm/ScriptOnce.js +1 -1
- package/dist/esm/ScriptOnce.js.map +1 -1
- package/dist/esm/Scripts.js +7 -0
- package/dist/esm/Scripts.js.map +1 -1
- package/dist/esm/ssr/renderRouterToString.js +1 -1
- package/dist/esm/ssr/renderRouterToString.js.map +1 -1
- package/package.json +2 -2
- package/src/Asset.tsx +7 -1
- package/src/HeadContent.tsx +5 -22
- package/src/ScriptOnce.tsx +1 -1
- package/src/Scripts.tsx +10 -0
- package/src/ssr/renderRouterToString.tsx +1 -1
package/dist/cjs/Asset.cjs
CHANGED
|
@@ -121,7 +121,13 @@ function Script({
|
|
|
121
121
|
return void 0;
|
|
122
122
|
}, [attrs, children]);
|
|
123
123
|
if (!router.isServer) {
|
|
124
|
-
return
|
|
124
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
125
|
+
"script",
|
|
126
|
+
{
|
|
127
|
+
suppressHydrationWarning: true,
|
|
128
|
+
dangerouslySetInnerHTML: { __html: "" }
|
|
129
|
+
}
|
|
130
|
+
);
|
|
125
131
|
}
|
|
126
132
|
if (attrs?.src && typeof attrs.src === "string") {
|
|
127
133
|
return /* @__PURE__ */ jsxRuntime.jsx("script", { ...attrs, suppressHydrationWarning: true });
|
package/dist/cjs/Asset.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Asset.cjs","sources":["../../src/Asset.tsx"],"sourcesContent":["import * as React from 'react'\nimport { useRouter } from './useRouter'\nimport type { RouterManagedTag } from '@tanstack/router-core'\n\ninterface ScriptAttrs {\n [key: string]: string | boolean | undefined\n src?: string\n suppressHydrationWarning?: boolean\n}\n\nexport function Asset({\n tag,\n attrs,\n children,\n nonce,\n}: RouterManagedTag & { nonce?: string }): React.ReactElement | null {\n switch (tag) {\n case 'title':\n return (\n <title {...attrs} suppressHydrationWarning>\n {children}\n </title>\n )\n case 'meta':\n return <meta {...attrs} suppressHydrationWarning />\n case 'link':\n return <link {...attrs} nonce={nonce} suppressHydrationWarning />\n case 'style':\n return (\n <style\n {...attrs}\n dangerouslySetInnerHTML={{ __html: children as string }}\n nonce={nonce}\n />\n )\n case 'script':\n return <Script attrs={attrs}>{children}</Script>\n default:\n return null\n }\n}\n\nfunction Script({\n attrs,\n children,\n}: {\n attrs?: ScriptAttrs\n children?: string\n}) {\n const router = useRouter()\n\n React.useEffect(() => {\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 const script = document.createElement('script')\n\n for (const [key, value] of Object.entries(attrs)) {\n if (\n key !== 'suppressHydrationWarning' &&\n value !== undefined &&\n value !== false\n ) {\n script.setAttribute(\n key,\n typeof value === 'boolean' ? '' : String(value),\n )\n }\n }\n\n document.head.appendChild(script)\n\n return () => {\n if (script.parentNode) {\n script.parentNode.removeChild(script)\n }\n }\n }\n\n 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 const script = document.createElement('script')\n script.textContent = children\n\n if (attrs) {\n for (const [key, value] of Object.entries(attrs)) {\n if (\n key !== 'suppressHydrationWarning' &&\n value !== undefined &&\n value !== false\n ) {\n script.setAttribute(\n key,\n typeof value === 'boolean' ? '' : String(value),\n )\n }\n }\n }\n\n document.head.appendChild(script)\n\n return () => {\n if (script.parentNode) {\n script.parentNode.removeChild(script)\n }\n }\n }\n\n return undefined\n }, [attrs, children])\n\n if (!router.isServer) {\n return
|
|
1
|
+
{"version":3,"file":"Asset.cjs","sources":["../../src/Asset.tsx"],"sourcesContent":["import * as React from 'react'\nimport { useRouter } from './useRouter'\nimport type { RouterManagedTag } from '@tanstack/router-core'\n\ninterface ScriptAttrs {\n [key: string]: string | boolean | undefined\n src?: string\n suppressHydrationWarning?: boolean\n}\n\nexport function Asset({\n tag,\n attrs,\n children,\n nonce,\n}: RouterManagedTag & { nonce?: string }): React.ReactElement | null {\n switch (tag) {\n case 'title':\n return (\n <title {...attrs} suppressHydrationWarning>\n {children}\n </title>\n )\n case 'meta':\n return <meta {...attrs} suppressHydrationWarning />\n case 'link':\n return <link {...attrs} nonce={nonce} suppressHydrationWarning />\n case 'style':\n return (\n <style\n {...attrs}\n dangerouslySetInnerHTML={{ __html: children as string }}\n nonce={nonce}\n />\n )\n case 'script':\n return <Script attrs={attrs}>{children}</Script>\n default:\n return null\n }\n}\n\nfunction Script({\n attrs,\n children,\n}: {\n attrs?: ScriptAttrs\n children?: string\n}) {\n const router = useRouter()\n\n React.useEffect(() => {\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 const script = document.createElement('script')\n\n for (const [key, value] of Object.entries(attrs)) {\n if (\n key !== 'suppressHydrationWarning' &&\n value !== undefined &&\n value !== false\n ) {\n script.setAttribute(\n key,\n typeof value === 'boolean' ? '' : String(value),\n )\n }\n }\n\n document.head.appendChild(script)\n\n return () => {\n if (script.parentNode) {\n script.parentNode.removeChild(script)\n }\n }\n }\n\n 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 const script = document.createElement('script')\n script.textContent = children\n\n if (attrs) {\n for (const [key, value] of Object.entries(attrs)) {\n if (\n key !== 'suppressHydrationWarning' &&\n value !== undefined &&\n value !== false\n ) {\n script.setAttribute(\n key,\n typeof value === 'boolean' ? '' : String(value),\n )\n }\n }\n }\n\n document.head.appendChild(script)\n\n return () => {\n if (script.parentNode) {\n script.parentNode.removeChild(script)\n }\n }\n }\n\n return undefined\n }, [attrs, children])\n\n if (!router.isServer) {\n // render an empty script on the client just to avoid hydration errors\n return (\n <script\n suppressHydrationWarning\n dangerouslySetInnerHTML={{ __html: '' }}\n ></script>\n )\n }\n\n if (attrs?.src && typeof attrs.src === 'string') {\n return <script {...attrs} suppressHydrationWarning />\n }\n\n if (typeof children === 'string') {\n return (\n <script\n {...attrs}\n dangerouslySetInnerHTML={{ __html: children }}\n suppressHydrationWarning\n />\n )\n }\n\n return null\n}\n"],"names":["jsx","useRouter","React"],"mappings":"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"}
|
package/dist/cjs/HeadContent.cjs
CHANGED
|
@@ -103,12 +103,12 @@ const useTags = () => {
|
|
|
103
103
|
},
|
|
104
104
|
structuralSharing: true
|
|
105
105
|
});
|
|
106
|
-
const
|
|
106
|
+
const preloadLinks = useRouterState.useRouterState({
|
|
107
107
|
select: (state) => {
|
|
108
|
-
const
|
|
108
|
+
const preloadLinks2 = [];
|
|
109
109
|
state.matches.map((match) => router.looseRoutesById[match.routeId]).forEach(
|
|
110
110
|
(route) => router.ssr?.manifest?.routes[route.id]?.preloads?.filter(Boolean).forEach((preload) => {
|
|
111
|
-
|
|
111
|
+
preloadLinks2.push({
|
|
112
112
|
tag: "link",
|
|
113
113
|
attrs: {
|
|
114
114
|
rel: "modulepreload",
|
|
@@ -118,7 +118,7 @@ const useTags = () => {
|
|
|
118
118
|
});
|
|
119
119
|
})
|
|
120
120
|
);
|
|
121
|
-
return
|
|
121
|
+
return preloadLinks2;
|
|
122
122
|
},
|
|
123
123
|
structuralSharing: true
|
|
124
124
|
});
|
|
@@ -142,27 +142,12 @@ const useTags = () => {
|
|
|
142
142
|
})),
|
|
143
143
|
structuralSharing: true
|
|
144
144
|
});
|
|
145
|
-
let serverHeadScript = void 0;
|
|
146
|
-
if (router.serverSsr) {
|
|
147
|
-
const bufferedScripts = router.serverSsr.takeBufferedScripts();
|
|
148
|
-
if (bufferedScripts) {
|
|
149
|
-
serverHeadScript = {
|
|
150
|
-
tag: "script",
|
|
151
|
-
attrs: {
|
|
152
|
-
nonce,
|
|
153
|
-
className: "$tsr"
|
|
154
|
-
},
|
|
155
|
-
children: bufferedScripts
|
|
156
|
-
};
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
145
|
return uniqBy(
|
|
160
146
|
[
|
|
161
147
|
...meta,
|
|
162
|
-
...
|
|
148
|
+
...preloadLinks,
|
|
163
149
|
...links,
|
|
164
150
|
...styles,
|
|
165
|
-
...serverHeadScript ? [serverHeadScript] : [],
|
|
166
151
|
...headScripts
|
|
167
152
|
],
|
|
168
153
|
(d) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"HeadContent.cjs","sources":["../../src/HeadContent.tsx"],"sourcesContent":["import * as React from 'react'\nimport { Asset } from './Asset'\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: Array<RouterManagedTag> = React.useMemo(() => {\n const resultMeta: Array<RouterManagedTag> = []\n const metaByAttribute: Record<string, true> = {}\n let title: RouterManagedTag | undefined\n for (let i = routeMeta.length - 1; i >= 0; i--) {\n const metas = routeMeta[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 {\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 (nonce) {\n resultMeta.push({\n tag: 'meta',\n attrs: {\n property: 'csp-nonce',\n content: nonce,\n },\n })\n }\n resultMeta.reverse()\n\n return resultMeta\n }, [routeMeta, nonce])\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 // These are the assets extracted from the ViteManifest\n // using the `startManifestPlugin`\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: {\n ...asset.attrs,\n suppressHydrationWarning: true,\n nonce,\n },\n }) satisfies RouterManagedTag,\n )\n\n return [...constructed, ...assets]\n },\n structuralSharing: true as any,\n })\n\n const
|
|
1
|
+
{"version":3,"file":"HeadContent.cjs","sources":["../../src/HeadContent.tsx"],"sourcesContent":["import * as React from 'react'\nimport { Asset } from './Asset'\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: Array<RouterManagedTag> = React.useMemo(() => {\n const resultMeta: Array<RouterManagedTag> = []\n const metaByAttribute: Record<string, true> = {}\n let title: RouterManagedTag | undefined\n for (let i = routeMeta.length - 1; i >= 0; i--) {\n const metas = routeMeta[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 {\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 (nonce) {\n resultMeta.push({\n tag: 'meta',\n attrs: {\n property: 'csp-nonce',\n content: nonce,\n },\n })\n }\n resultMeta.reverse()\n\n return resultMeta\n }, [routeMeta, nonce])\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 // These are the assets extracted from the ViteManifest\n // using the `startManifestPlugin`\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: {\n ...asset.attrs,\n suppressHydrationWarning: true,\n nonce,\n },\n }) satisfies RouterManagedTag,\n )\n\n return [...constructed, ...assets]\n },\n structuralSharing: true as any,\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 structuralSharing: true as any,\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, ...attrs }) => ({\n tag: 'style',\n attrs,\n children,\n nonce,\n })),\n structuralSharing: true as any,\n })\n\n const headScripts: Array<RouterManagedTag> = 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 structuralSharing: true as any,\n })\n\n return 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\n/**\n * @description The `HeadContent` component is used to render meta tags, links, and scripts for the current route.\n * It should be rendered in the `<head>` of your document.\n */\n/**\n * Render route-managed head tags (title, meta, links, styles, head scripts).\n * Place inside the document head of your app shell.\n * @link https://tanstack.com/router/latest/docs/framework/react/guide/document-head-management\n */\nexport function HeadContent() {\n const tags = useTags()\n const router = useRouter()\n const nonce = router.options.ssr?.nonce\n return tags.map((tag) => (\n <Asset {...tag} key={`tsr-meta-${JSON.stringify(tag)}`} nonce={nonce} />\n ))\n}\n\nfunction 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"],"names":["useRouter","useRouterState","React","preloadLinks","createElement","Asset"],"mappings":"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"}
|
package/dist/cjs/ScriptOnce.cjs
CHANGED
|
@@ -13,7 +13,7 @@ function ScriptOnce({ children }) {
|
|
|
13
13
|
nonce: router.options.ssr?.nonce,
|
|
14
14
|
className: "$tsr",
|
|
15
15
|
dangerouslySetInnerHTML: {
|
|
16
|
-
__html:
|
|
16
|
+
__html: children + ';typeof $_TSR !== "undefined" && $_TSR.c()'
|
|
17
17
|
}
|
|
18
18
|
}
|
|
19
19
|
);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ScriptOnce.cjs","sources":["../../src/ScriptOnce.tsx"],"sourcesContent":["import { useRouter } from './useRouter'\n\n/**\n * Server-only helper to emit a script tag exactly once during SSR.\n */\nexport function ScriptOnce({ children }: { children: string }) {\n const router = useRouter()\n if (!router.isServer) {\n return null\n }\n\n return (\n <script\n nonce={router.options.ssr?.nonce}\n className=\"$tsr\"\n dangerouslySetInnerHTML={{\n __html:
|
|
1
|
+
{"version":3,"file":"ScriptOnce.cjs","sources":["../../src/ScriptOnce.tsx"],"sourcesContent":["import { useRouter } from './useRouter'\n\n/**\n * Server-only helper to emit a script tag exactly once during SSR.\n */\nexport function ScriptOnce({ children }: { children: string }) {\n const router = useRouter()\n if (!router.isServer) {\n return null\n }\n\n return (\n <script\n nonce={router.options.ssr?.nonce}\n className=\"$tsr\"\n dangerouslySetInnerHTML={{\n __html: children + ';typeof $_TSR !== \"undefined\" && $_TSR.c()',\n }}\n />\n )\n}\n"],"names":["useRouter","jsx"],"mappings":";;;;AAKO,SAAS,WAAW,EAAE,YAAkC;AAC7D,QAAM,SAASA,UAAAA,UAAA;AACf,MAAI,CAAC,OAAO,UAAU;AACpB,WAAO;AAAA,EACT;AAEA,SACEC,2BAAAA;AAAAA,IAAC;AAAA,IAAA;AAAA,MACC,OAAO,OAAO,QAAQ,KAAK;AAAA,MAC3B,WAAU;AAAA,MACV,yBAAyB;AAAA,QACvB,QAAQ,WAAW;AAAA,MAAA;AAAA,IACrB;AAAA,EAAA;AAGN;;"}
|
package/dist/cjs/Scripts.cjs
CHANGED
|
@@ -42,7 +42,14 @@ const Scripts = () => {
|
|
|
42
42
|
}),
|
|
43
43
|
structuralSharing: true
|
|
44
44
|
});
|
|
45
|
+
let serverBufferedScript = void 0;
|
|
46
|
+
if (router.serverSsr) {
|
|
47
|
+
serverBufferedScript = router.serverSsr.takeBufferedScripts();
|
|
48
|
+
}
|
|
45
49
|
const allScripts = [...scripts, ...assetScripts];
|
|
50
|
+
if (serverBufferedScript) {
|
|
51
|
+
allScripts.unshift(serverBufferedScript);
|
|
52
|
+
}
|
|
46
53
|
return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: allScripts.map((asset, i) => /* @__PURE__ */ React.createElement(Asset.Asset, { ...asset, key: `tsr-scripts-${asset.tag}-${i}` })) });
|
|
47
54
|
};
|
|
48
55
|
exports.Scripts = Scripts;
|
package/dist/cjs/Scripts.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Scripts.cjs","sources":["../../src/Scripts.tsx"],"sourcesContent":["import { Asset } from './Asset'\nimport { useRouterState } from './useRouterState'\nimport { useRouter } from './useRouter'\nimport type { RouterManagedTag } from '@tanstack/router-core'\n\n/**\n * Render body script tags collected from route matches and SSR manifests.\n * Should be placed near the end of the document body.\n */\n/**\n * Render body script tags collected from route matches and SSR manifests.\n * Should be placed near the end of the document body.\n */\nexport const Scripts = () => {\n const router = useRouter()\n const nonce = router.options.ssr?.nonce\n const assetScripts = useRouterState({\n select: (state) => {\n const assetScripts: Array<RouterManagedTag> = []\n const manifest = router.ssr?.manifest\n\n if (!manifest) {\n return []\n }\n\n state.matches\n .map((match) => router.looseRoutesById[match.routeId]!)\n .forEach((route) =>\n manifest.routes[route.id]?.assets\n ?.filter((d) => d.tag === 'script')\n .forEach((asset) => {\n assetScripts.push({\n tag: 'script',\n attrs: { ...asset.attrs, nonce },\n children: asset.children,\n } as any)\n }),\n )\n\n return assetScripts\n },\n structuralSharing: true as any,\n })\n\n const { scripts } = useRouterState({\n select: (state) => ({\n scripts: (\n state.matches\n .map((match) => match.scripts!)\n .flat(1)\n .filter(Boolean) as Array<RouterManagedTag>\n ).map(({ children, ...script }) => ({\n tag: 'script',\n attrs: {\n ...script,\n suppressHydrationWarning: true,\n nonce,\n },\n children,\n })),\n }),\n structuralSharing: true as any,\n })\n\n const allScripts = [...scripts, ...assetScripts] as Array<RouterManagedTag>\n\n return (\n <>\n {allScripts.map((asset, i) => (\n <Asset {...asset} key={`tsr-scripts-${asset.tag}-${i}`} />\n ))}\n </>\n )\n}\n"],"names":["useRouter","useRouterState","assetScripts","Asset"],"mappings":";;;;;;;AAaO,MAAM,UAAU,MAAM;AAC3B,QAAM,SAASA,UAAAA,UAAA;AACf,QAAM,QAAQ,OAAO,QAAQ,KAAK;AAClC,QAAM,eAAeC,eAAAA,eAAe;AAAA,IAClC,QAAQ,CAAC,UAAU;AACjB,YAAMC,gBAAwC,CAAA;AAC9C,YAAM,WAAW,OAAO,KAAK;AAE7B,UAAI,CAAC,UAAU;AACb,eAAO,CAAA;AAAA,MACT;AAEA,YAAM,QACH,IAAI,CAAC,UAAU,OAAO,gBAAgB,MAAM,OAAO,CAAE,EACrD;AAAA,QAAQ,CAAC,UACR,SAAS,OAAO,MAAM,EAAE,GAAG,QACvB,OAAO,CAAC,MAAM,EAAE,QAAQ,QAAQ,EACjC,QAAQ,CAAC,UAAU;AAClBA,wBAAa,KAAK;AAAA,YAChB,KAAK;AAAA,YACL,OAAO,EAAE,GAAG,MAAM,OAAO,MAAA;AAAA,YACzB,UAAU,MAAM;AAAA,UAAA,CACV;AAAA,QACV,CAAC;AAAA,MAAA;AAGP,aAAOA;AAAAA,IACT;AAAA,IACA,mBAAmB;AAAA,EAAA,CACpB;AAED,QAAM,EAAE,QAAA,IAAYD,8BAAe;AAAA,IACjC,QAAQ,CAAC,WAAW;AAAA,MAClB,SACE,MAAM,QACH,IAAI,CAAC,UAAU,MAAM,OAAQ,EAC7B,KAAK,CAAC,EACN,OAAO,OAAO,EACjB,IAAI,CAAC,EAAE,UAAU,GAAG,cAAc;AAAA,QAClC,KAAK;AAAA,QACL,OAAO;AAAA,UACL,GAAG;AAAA,UACH,0BAA0B;AAAA,UAC1B;AAAA,QAAA;AAAA,QAEF;AAAA,MAAA,EACA;AAAA,IAAA;AAAA,IAEJ,mBAAmB;AAAA,EAAA,CACpB;AAED,QAAM,aAAa,CAAC,GAAG,SAAS,GAAG,YAAY;AAE/C,+DAEK,UAAA,WAAW,IAAI,CAAC,OAAO,0CACrBE,MAAAA,OAAA,EAAO,GAAG,OAAO,KAAK,eAAe,MAAM,GAAG,IAAI,CAAC,IAAI,CACzD,GACH;AAEJ;;"}
|
|
1
|
+
{"version":3,"file":"Scripts.cjs","sources":["../../src/Scripts.tsx"],"sourcesContent":["import { Asset } from './Asset'\nimport { useRouterState } from './useRouterState'\nimport { useRouter } from './useRouter'\nimport type { RouterManagedTag } from '@tanstack/router-core'\n\n/**\n * Render body script tags collected from route matches and SSR manifests.\n * Should be placed near the end of the document body.\n */\n/**\n * Render body script tags collected from route matches and SSR manifests.\n * Should be placed near the end of the document body.\n */\nexport const Scripts = () => {\n const router = useRouter()\n const nonce = router.options.ssr?.nonce\n const assetScripts = useRouterState({\n select: (state) => {\n const assetScripts: Array<RouterManagedTag> = []\n const manifest = router.ssr?.manifest\n\n if (!manifest) {\n return []\n }\n\n state.matches\n .map((match) => router.looseRoutesById[match.routeId]!)\n .forEach((route) =>\n manifest.routes[route.id]?.assets\n ?.filter((d) => d.tag === 'script')\n .forEach((asset) => {\n assetScripts.push({\n tag: 'script',\n attrs: { ...asset.attrs, nonce },\n children: asset.children,\n } as any)\n }),\n )\n\n return assetScripts\n },\n structuralSharing: true as any,\n })\n\n const { scripts } = useRouterState({\n select: (state) => ({\n scripts: (\n state.matches\n .map((match) => match.scripts!)\n .flat(1)\n .filter(Boolean) as Array<RouterManagedTag>\n ).map(({ children, ...script }) => ({\n tag: 'script',\n attrs: {\n ...script,\n suppressHydrationWarning: true,\n nonce,\n },\n children,\n })),\n }),\n structuralSharing: true as any,\n })\n\n let serverBufferedScript: RouterManagedTag | undefined = undefined\n\n if (router.serverSsr) {\n serverBufferedScript = router.serverSsr.takeBufferedScripts()\n }\n\n const allScripts = [...scripts, ...assetScripts] as Array<RouterManagedTag>\n\n if (serverBufferedScript) {\n allScripts.unshift(serverBufferedScript)\n }\n\n return (\n <>\n {allScripts.map((asset, i) => (\n <Asset {...asset} key={`tsr-scripts-${asset.tag}-${i}`} />\n ))}\n </>\n )\n}\n"],"names":["useRouter","useRouterState","assetScripts","Asset"],"mappings":";;;;;;;AAaO,MAAM,UAAU,MAAM;AAC3B,QAAM,SAASA,UAAAA,UAAA;AACf,QAAM,QAAQ,OAAO,QAAQ,KAAK;AAClC,QAAM,eAAeC,eAAAA,eAAe;AAAA,IAClC,QAAQ,CAAC,UAAU;AACjB,YAAMC,gBAAwC,CAAA;AAC9C,YAAM,WAAW,OAAO,KAAK;AAE7B,UAAI,CAAC,UAAU;AACb,eAAO,CAAA;AAAA,MACT;AAEA,YAAM,QACH,IAAI,CAAC,UAAU,OAAO,gBAAgB,MAAM,OAAO,CAAE,EACrD;AAAA,QAAQ,CAAC,UACR,SAAS,OAAO,MAAM,EAAE,GAAG,QACvB,OAAO,CAAC,MAAM,EAAE,QAAQ,QAAQ,EACjC,QAAQ,CAAC,UAAU;AAClBA,wBAAa,KAAK;AAAA,YAChB,KAAK;AAAA,YACL,OAAO,EAAE,GAAG,MAAM,OAAO,MAAA;AAAA,YACzB,UAAU,MAAM;AAAA,UAAA,CACV;AAAA,QACV,CAAC;AAAA,MAAA;AAGP,aAAOA;AAAAA,IACT;AAAA,IACA,mBAAmB;AAAA,EAAA,CACpB;AAED,QAAM,EAAE,QAAA,IAAYD,8BAAe;AAAA,IACjC,QAAQ,CAAC,WAAW;AAAA,MAClB,SACE,MAAM,QACH,IAAI,CAAC,UAAU,MAAM,OAAQ,EAC7B,KAAK,CAAC,EACN,OAAO,OAAO,EACjB,IAAI,CAAC,EAAE,UAAU,GAAG,cAAc;AAAA,QAClC,KAAK;AAAA,QACL,OAAO;AAAA,UACL,GAAG;AAAA,UACH,0BAA0B;AAAA,UAC1B;AAAA,QAAA;AAAA,QAEF;AAAA,MAAA,EACA;AAAA,IAAA;AAAA,IAEJ,mBAAmB;AAAA,EAAA,CACpB;AAED,MAAI,uBAAqD;AAEzD,MAAI,OAAO,WAAW;AACpB,2BAAuB,OAAO,UAAU,oBAAA;AAAA,EAC1C;AAEA,QAAM,aAAa,CAAC,GAAG,SAAS,GAAG,YAAY;AAE/C,MAAI,sBAAsB;AACxB,eAAW,QAAQ,oBAAoB;AAAA,EACzC;AAEA,+DAEK,UAAA,WAAW,IAAI,CAAC,OAAO,0CACrBE,MAAAA,OAAA,EAAO,GAAG,OAAO,KAAK,eAAe,MAAM,GAAG,IAAI,CAAC,IAAI,CACzD,GACH;AAEJ;;"}
|
|
@@ -12,7 +12,7 @@ const renderRouterToString = async ({
|
|
|
12
12
|
const injectedHtml = await Promise.all(router.serverSsr.injectedHtml).then(
|
|
13
13
|
(htmls) => htmls.join("")
|
|
14
14
|
);
|
|
15
|
-
html = html.replace(`</body>`, `${injectedHtml}</body>`);
|
|
15
|
+
html = html.replace(`</body>`, () => `${injectedHtml}</body>`);
|
|
16
16
|
return new Response(`<!DOCTYPE html>${html}`, {
|
|
17
17
|
status: router.state.statusCode,
|
|
18
18
|
headers: responseHeaders
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"renderRouterToString.cjs","sources":["../../../src/ssr/renderRouterToString.tsx"],"sourcesContent":["import ReactDOMServer from 'react-dom/server'\nimport type { ReactNode } from 'react'\nimport type { AnyRouter } from '@tanstack/router-core'\n\nexport const renderRouterToString = async ({\n router,\n responseHeaders,\n children,\n}: {\n router: AnyRouter\n responseHeaders: Headers\n children: ReactNode\n}) => {\n try {\n let html = ReactDOMServer.renderToString(children)\n router.serverSsr!.setRenderFinished()\n const injectedHtml = await Promise.all(router.serverSsr!.injectedHtml).then(\n (htmls) => htmls.join(''),\n )\n html = html.replace(`</body>`, `${injectedHtml}</body>`)\n return new Response(`<!DOCTYPE html>${html}`, {\n status: router.state.statusCode,\n headers: responseHeaders,\n })\n } catch (error) {\n console.error('Render to string error:', error)\n return new Response('Internal Server Error', {\n status: 500,\n headers: responseHeaders,\n })\n }\n}\n"],"names":[],"mappings":";;;AAIO,MAAM,uBAAuB,OAAO;AAAA,EACzC;AAAA,EACA;AAAA,EACA;AACF,MAIM;AACJ,MAAI;AACF,QAAI,OAAO,eAAe,eAAe,QAAQ;AACjD,WAAO,UAAW,kBAAA;AAClB,UAAM,eAAe,MAAM,QAAQ,IAAI,OAAO,UAAW,YAAY,EAAE;AAAA,MACrE,CAAC,UAAU,MAAM,KAAK,EAAE;AAAA,IAAA;AAE1B,WAAO,KAAK,QAAQ,WAAW,GAAG,YAAY,SAAS;
|
|
1
|
+
{"version":3,"file":"renderRouterToString.cjs","sources":["../../../src/ssr/renderRouterToString.tsx"],"sourcesContent":["import ReactDOMServer from 'react-dom/server'\nimport type { ReactNode } from 'react'\nimport type { AnyRouter } from '@tanstack/router-core'\n\nexport const renderRouterToString = async ({\n router,\n responseHeaders,\n children,\n}: {\n router: AnyRouter\n responseHeaders: Headers\n children: ReactNode\n}) => {\n try {\n let html = ReactDOMServer.renderToString(children)\n router.serverSsr!.setRenderFinished()\n const injectedHtml = await Promise.all(router.serverSsr!.injectedHtml).then(\n (htmls) => htmls.join(''),\n )\n html = html.replace(`</body>`, () => `${injectedHtml}</body>`)\n return new Response(`<!DOCTYPE html>${html}`, {\n status: router.state.statusCode,\n headers: responseHeaders,\n })\n } catch (error) {\n console.error('Render to string error:', error)\n return new Response('Internal Server Error', {\n status: 500,\n headers: responseHeaders,\n })\n }\n}\n"],"names":[],"mappings":";;;AAIO,MAAM,uBAAuB,OAAO;AAAA,EACzC;AAAA,EACA;AAAA,EACA;AACF,MAIM;AACJ,MAAI;AACF,QAAI,OAAO,eAAe,eAAe,QAAQ;AACjD,WAAO,UAAW,kBAAA;AAClB,UAAM,eAAe,MAAM,QAAQ,IAAI,OAAO,UAAW,YAAY,EAAE;AAAA,MACrE,CAAC,UAAU,MAAM,KAAK,EAAE;AAAA,IAAA;AAE1B,WAAO,KAAK,QAAQ,WAAW,MAAM,GAAG,YAAY,SAAS;AAC7D,WAAO,IAAI,SAAS,kBAAkB,IAAI,IAAI;AAAA,MAC5C,QAAQ,OAAO,MAAM;AAAA,MACrB,SAAS;AAAA,IAAA,CACV;AAAA,EACH,SAAS,OAAO;AACd,YAAQ,MAAM,2BAA2B,KAAK;AAC9C,WAAO,IAAI,SAAS,yBAAyB;AAAA,MAC3C,QAAQ;AAAA,MACR,SAAS;AAAA,IAAA,CACV;AAAA,EACH;AACF;;"}
|
package/dist/esm/Asset.js
CHANGED
|
@@ -102,7 +102,13 @@ function Script({
|
|
|
102
102
|
return void 0;
|
|
103
103
|
}, [attrs, children]);
|
|
104
104
|
if (!router.isServer) {
|
|
105
|
-
return
|
|
105
|
+
return /* @__PURE__ */ jsx(
|
|
106
|
+
"script",
|
|
107
|
+
{
|
|
108
|
+
suppressHydrationWarning: true,
|
|
109
|
+
dangerouslySetInnerHTML: { __html: "" }
|
|
110
|
+
}
|
|
111
|
+
);
|
|
106
112
|
}
|
|
107
113
|
if (attrs?.src && typeof attrs.src === "string") {
|
|
108
114
|
return /* @__PURE__ */ jsx("script", { ...attrs, suppressHydrationWarning: true });
|
package/dist/esm/Asset.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Asset.js","sources":["../../src/Asset.tsx"],"sourcesContent":["import * as React from 'react'\nimport { useRouter } from './useRouter'\nimport type { RouterManagedTag } from '@tanstack/router-core'\n\ninterface ScriptAttrs {\n [key: string]: string | boolean | undefined\n src?: string\n suppressHydrationWarning?: boolean\n}\n\nexport function Asset({\n tag,\n attrs,\n children,\n nonce,\n}: RouterManagedTag & { nonce?: string }): React.ReactElement | null {\n switch (tag) {\n case 'title':\n return (\n <title {...attrs} suppressHydrationWarning>\n {children}\n </title>\n )\n case 'meta':\n return <meta {...attrs} suppressHydrationWarning />\n case 'link':\n return <link {...attrs} nonce={nonce} suppressHydrationWarning />\n case 'style':\n return (\n <style\n {...attrs}\n dangerouslySetInnerHTML={{ __html: children as string }}\n nonce={nonce}\n />\n )\n case 'script':\n return <Script attrs={attrs}>{children}</Script>\n default:\n return null\n }\n}\n\nfunction Script({\n attrs,\n children,\n}: {\n attrs?: ScriptAttrs\n children?: string\n}) {\n const router = useRouter()\n\n React.useEffect(() => {\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 const script = document.createElement('script')\n\n for (const [key, value] of Object.entries(attrs)) {\n if (\n key !== 'suppressHydrationWarning' &&\n value !== undefined &&\n value !== false\n ) {\n script.setAttribute(\n key,\n typeof value === 'boolean' ? '' : String(value),\n )\n }\n }\n\n document.head.appendChild(script)\n\n return () => {\n if (script.parentNode) {\n script.parentNode.removeChild(script)\n }\n }\n }\n\n 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 const script = document.createElement('script')\n script.textContent = children\n\n if (attrs) {\n for (const [key, value] of Object.entries(attrs)) {\n if (\n key !== 'suppressHydrationWarning' &&\n value !== undefined &&\n value !== false\n ) {\n script.setAttribute(\n key,\n typeof value === 'boolean' ? '' : String(value),\n )\n }\n }\n }\n\n document.head.appendChild(script)\n\n return () => {\n if (script.parentNode) {\n script.parentNode.removeChild(script)\n }\n }\n }\n\n return undefined\n }, [attrs, children])\n\n if (!router.isServer) {\n return
|
|
1
|
+
{"version":3,"file":"Asset.js","sources":["../../src/Asset.tsx"],"sourcesContent":["import * as React from 'react'\nimport { useRouter } from './useRouter'\nimport type { RouterManagedTag } from '@tanstack/router-core'\n\ninterface ScriptAttrs {\n [key: string]: string | boolean | undefined\n src?: string\n suppressHydrationWarning?: boolean\n}\n\nexport function Asset({\n tag,\n attrs,\n children,\n nonce,\n}: RouterManagedTag & { nonce?: string }): React.ReactElement | null {\n switch (tag) {\n case 'title':\n return (\n <title {...attrs} suppressHydrationWarning>\n {children}\n </title>\n )\n case 'meta':\n return <meta {...attrs} suppressHydrationWarning />\n case 'link':\n return <link {...attrs} nonce={nonce} suppressHydrationWarning />\n case 'style':\n return (\n <style\n {...attrs}\n dangerouslySetInnerHTML={{ __html: children as string }}\n nonce={nonce}\n />\n )\n case 'script':\n return <Script attrs={attrs}>{children}</Script>\n default:\n return null\n }\n}\n\nfunction Script({\n attrs,\n children,\n}: {\n attrs?: ScriptAttrs\n children?: string\n}) {\n const router = useRouter()\n\n React.useEffect(() => {\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 const script = document.createElement('script')\n\n for (const [key, value] of Object.entries(attrs)) {\n if (\n key !== 'suppressHydrationWarning' &&\n value !== undefined &&\n value !== false\n ) {\n script.setAttribute(\n key,\n typeof value === 'boolean' ? '' : String(value),\n )\n }\n }\n\n document.head.appendChild(script)\n\n return () => {\n if (script.parentNode) {\n script.parentNode.removeChild(script)\n }\n }\n }\n\n 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 const script = document.createElement('script')\n script.textContent = children\n\n if (attrs) {\n for (const [key, value] of Object.entries(attrs)) {\n if (\n key !== 'suppressHydrationWarning' &&\n value !== undefined &&\n value !== false\n ) {\n script.setAttribute(\n key,\n typeof value === 'boolean' ? '' : String(value),\n )\n }\n }\n }\n\n document.head.appendChild(script)\n\n return () => {\n if (script.parentNode) {\n script.parentNode.removeChild(script)\n }\n }\n }\n\n return undefined\n }, [attrs, children])\n\n if (!router.isServer) {\n // render an empty script on the client just to avoid hydration errors\n return (\n <script\n suppressHydrationWarning\n dangerouslySetInnerHTML={{ __html: '' }}\n ></script>\n )\n }\n\n if (attrs?.src && typeof attrs.src === 'string') {\n return <script {...attrs} suppressHydrationWarning />\n }\n\n if (typeof children === 'string') {\n return (\n <script\n {...attrs}\n dangerouslySetInnerHTML={{ __html: children }}\n suppressHydrationWarning\n />\n )\n }\n\n return null\n}\n"],"names":[],"mappings":"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"}
|
package/dist/esm/HeadContent.js
CHANGED
|
@@ -85,12 +85,12 @@ const useTags = () => {
|
|
|
85
85
|
},
|
|
86
86
|
structuralSharing: true
|
|
87
87
|
});
|
|
88
|
-
const
|
|
88
|
+
const preloadLinks = useRouterState({
|
|
89
89
|
select: (state) => {
|
|
90
|
-
const
|
|
90
|
+
const preloadLinks2 = [];
|
|
91
91
|
state.matches.map((match) => router.looseRoutesById[match.routeId]).forEach(
|
|
92
92
|
(route) => router.ssr?.manifest?.routes[route.id]?.preloads?.filter(Boolean).forEach((preload) => {
|
|
93
|
-
|
|
93
|
+
preloadLinks2.push({
|
|
94
94
|
tag: "link",
|
|
95
95
|
attrs: {
|
|
96
96
|
rel: "modulepreload",
|
|
@@ -100,7 +100,7 @@ const useTags = () => {
|
|
|
100
100
|
});
|
|
101
101
|
})
|
|
102
102
|
);
|
|
103
|
-
return
|
|
103
|
+
return preloadLinks2;
|
|
104
104
|
},
|
|
105
105
|
structuralSharing: true
|
|
106
106
|
});
|
|
@@ -124,27 +124,12 @@ const useTags = () => {
|
|
|
124
124
|
})),
|
|
125
125
|
structuralSharing: true
|
|
126
126
|
});
|
|
127
|
-
let serverHeadScript = void 0;
|
|
128
|
-
if (router.serverSsr) {
|
|
129
|
-
const bufferedScripts = router.serverSsr.takeBufferedScripts();
|
|
130
|
-
if (bufferedScripts) {
|
|
131
|
-
serverHeadScript = {
|
|
132
|
-
tag: "script",
|
|
133
|
-
attrs: {
|
|
134
|
-
nonce,
|
|
135
|
-
className: "$tsr"
|
|
136
|
-
},
|
|
137
|
-
children: bufferedScripts
|
|
138
|
-
};
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
127
|
return uniqBy(
|
|
142
128
|
[
|
|
143
129
|
...meta,
|
|
144
|
-
...
|
|
130
|
+
...preloadLinks,
|
|
145
131
|
...links,
|
|
146
132
|
...styles,
|
|
147
|
-
...serverHeadScript ? [serverHeadScript] : [],
|
|
148
133
|
...headScripts
|
|
149
134
|
],
|
|
150
135
|
(d) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"HeadContent.js","sources":["../../src/HeadContent.tsx"],"sourcesContent":["import * as React from 'react'\nimport { Asset } from './Asset'\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: Array<RouterManagedTag> = React.useMemo(() => {\n const resultMeta: Array<RouterManagedTag> = []\n const metaByAttribute: Record<string, true> = {}\n let title: RouterManagedTag | undefined\n for (let i = routeMeta.length - 1; i >= 0; i--) {\n const metas = routeMeta[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 {\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 (nonce) {\n resultMeta.push({\n tag: 'meta',\n attrs: {\n property: 'csp-nonce',\n content: nonce,\n },\n })\n }\n resultMeta.reverse()\n\n return resultMeta\n }, [routeMeta, nonce])\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 // These are the assets extracted from the ViteManifest\n // using the `startManifestPlugin`\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: {\n ...asset.attrs,\n suppressHydrationWarning: true,\n nonce,\n },\n }) satisfies RouterManagedTag,\n )\n\n return [...constructed, ...assets]\n },\n structuralSharing: true as any,\n })\n\n const
|
|
1
|
+
{"version":3,"file":"HeadContent.js","sources":["../../src/HeadContent.tsx"],"sourcesContent":["import * as React from 'react'\nimport { Asset } from './Asset'\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: Array<RouterManagedTag> = React.useMemo(() => {\n const resultMeta: Array<RouterManagedTag> = []\n const metaByAttribute: Record<string, true> = {}\n let title: RouterManagedTag | undefined\n for (let i = routeMeta.length - 1; i >= 0; i--) {\n const metas = routeMeta[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 {\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 (nonce) {\n resultMeta.push({\n tag: 'meta',\n attrs: {\n property: 'csp-nonce',\n content: nonce,\n },\n })\n }\n resultMeta.reverse()\n\n return resultMeta\n }, [routeMeta, nonce])\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 // These are the assets extracted from the ViteManifest\n // using the `startManifestPlugin`\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: {\n ...asset.attrs,\n suppressHydrationWarning: true,\n nonce,\n },\n }) satisfies RouterManagedTag,\n )\n\n return [...constructed, ...assets]\n },\n structuralSharing: true as any,\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 structuralSharing: true as any,\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, ...attrs }) => ({\n tag: 'style',\n attrs,\n children,\n nonce,\n })),\n structuralSharing: true as any,\n })\n\n const headScripts: Array<RouterManagedTag> = 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 structuralSharing: true as any,\n })\n\n return 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\n/**\n * @description The `HeadContent` component is used to render meta tags, links, and scripts for the current route.\n * It should be rendered in the `<head>` of your document.\n */\n/**\n * Render route-managed head tags (title, meta, links, styles, head scripts).\n * Place inside the document head of your app shell.\n * @link https://tanstack.com/router/latest/docs/framework/react/guide/document-head-management\n */\nexport function HeadContent() {\n const tags = useTags()\n const router = useRouter()\n const nonce = router.options.ssr?.nonce\n return tags.map((tag) => (\n <Asset {...tag} key={`tsr-meta-${JSON.stringify(tag)}`} nonce={nonce} />\n ))\n}\n\nfunction 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"],"names":["preloadLinks"],"mappings":"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"}
|
package/dist/esm/ScriptOnce.js
CHANGED
|
@@ -11,7 +11,7 @@ function ScriptOnce({ children }) {
|
|
|
11
11
|
nonce: router.options.ssr?.nonce,
|
|
12
12
|
className: "$tsr",
|
|
13
13
|
dangerouslySetInnerHTML: {
|
|
14
|
-
__html:
|
|
14
|
+
__html: children + ';typeof $_TSR !== "undefined" && $_TSR.c()'
|
|
15
15
|
}
|
|
16
16
|
}
|
|
17
17
|
);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ScriptOnce.js","sources":["../../src/ScriptOnce.tsx"],"sourcesContent":["import { useRouter } from './useRouter'\n\n/**\n * Server-only helper to emit a script tag exactly once during SSR.\n */\nexport function ScriptOnce({ children }: { children: string }) {\n const router = useRouter()\n if (!router.isServer) {\n return null\n }\n\n return (\n <script\n nonce={router.options.ssr?.nonce}\n className=\"$tsr\"\n dangerouslySetInnerHTML={{\n __html:
|
|
1
|
+
{"version":3,"file":"ScriptOnce.js","sources":["../../src/ScriptOnce.tsx"],"sourcesContent":["import { useRouter } from './useRouter'\n\n/**\n * Server-only helper to emit a script tag exactly once during SSR.\n */\nexport function ScriptOnce({ children }: { children: string }) {\n const router = useRouter()\n if (!router.isServer) {\n return null\n }\n\n return (\n <script\n nonce={router.options.ssr?.nonce}\n className=\"$tsr\"\n dangerouslySetInnerHTML={{\n __html: children + ';typeof $_TSR !== \"undefined\" && $_TSR.c()',\n }}\n />\n )\n}\n"],"names":[],"mappings":";;AAKO,SAAS,WAAW,EAAE,YAAkC;AAC7D,QAAM,SAAS,UAAA;AACf,MAAI,CAAC,OAAO,UAAU;AACpB,WAAO;AAAA,EACT;AAEA,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,OAAO,OAAO,QAAQ,KAAK;AAAA,MAC3B,WAAU;AAAA,MACV,yBAAyB;AAAA,QACvB,QAAQ,WAAW;AAAA,MAAA;AAAA,IACrB;AAAA,EAAA;AAGN;"}
|
package/dist/esm/Scripts.js
CHANGED
|
@@ -40,7 +40,14 @@ const Scripts = () => {
|
|
|
40
40
|
}),
|
|
41
41
|
structuralSharing: true
|
|
42
42
|
});
|
|
43
|
+
let serverBufferedScript = void 0;
|
|
44
|
+
if (router.serverSsr) {
|
|
45
|
+
serverBufferedScript = router.serverSsr.takeBufferedScripts();
|
|
46
|
+
}
|
|
43
47
|
const allScripts = [...scripts, ...assetScripts];
|
|
48
|
+
if (serverBufferedScript) {
|
|
49
|
+
allScripts.unshift(serverBufferedScript);
|
|
50
|
+
}
|
|
44
51
|
return /* @__PURE__ */ jsx(Fragment, { children: allScripts.map((asset, i) => /* @__PURE__ */ createElement(Asset, { ...asset, key: `tsr-scripts-${asset.tag}-${i}` })) });
|
|
45
52
|
};
|
|
46
53
|
export {
|
package/dist/esm/Scripts.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Scripts.js","sources":["../../src/Scripts.tsx"],"sourcesContent":["import { Asset } from './Asset'\nimport { useRouterState } from './useRouterState'\nimport { useRouter } from './useRouter'\nimport type { RouterManagedTag } from '@tanstack/router-core'\n\n/**\n * Render body script tags collected from route matches and SSR manifests.\n * Should be placed near the end of the document body.\n */\n/**\n * Render body script tags collected from route matches and SSR manifests.\n * Should be placed near the end of the document body.\n */\nexport const Scripts = () => {\n const router = useRouter()\n const nonce = router.options.ssr?.nonce\n const assetScripts = useRouterState({\n select: (state) => {\n const assetScripts: Array<RouterManagedTag> = []\n const manifest = router.ssr?.manifest\n\n if (!manifest) {\n return []\n }\n\n state.matches\n .map((match) => router.looseRoutesById[match.routeId]!)\n .forEach((route) =>\n manifest.routes[route.id]?.assets\n ?.filter((d) => d.tag === 'script')\n .forEach((asset) => {\n assetScripts.push({\n tag: 'script',\n attrs: { ...asset.attrs, nonce },\n children: asset.children,\n } as any)\n }),\n )\n\n return assetScripts\n },\n structuralSharing: true as any,\n })\n\n const { scripts } = useRouterState({\n select: (state) => ({\n scripts: (\n state.matches\n .map((match) => match.scripts!)\n .flat(1)\n .filter(Boolean) as Array<RouterManagedTag>\n ).map(({ children, ...script }) => ({\n tag: 'script',\n attrs: {\n ...script,\n suppressHydrationWarning: true,\n nonce,\n },\n children,\n })),\n }),\n structuralSharing: true as any,\n })\n\n const allScripts = [...scripts, ...assetScripts] as Array<RouterManagedTag>\n\n return (\n <>\n {allScripts.map((asset, i) => (\n <Asset {...asset} key={`tsr-scripts-${asset.tag}-${i}`} />\n ))}\n </>\n )\n}\n"],"names":["assetScripts"],"mappings":";;;;;AAaO,MAAM,UAAU,MAAM;AAC3B,QAAM,SAAS,UAAA;AACf,QAAM,QAAQ,OAAO,QAAQ,KAAK;AAClC,QAAM,eAAe,eAAe;AAAA,IAClC,QAAQ,CAAC,UAAU;AACjB,YAAMA,gBAAwC,CAAA;AAC9C,YAAM,WAAW,OAAO,KAAK;AAE7B,UAAI,CAAC,UAAU;AACb,eAAO,CAAA;AAAA,MACT;AAEA,YAAM,QACH,IAAI,CAAC,UAAU,OAAO,gBAAgB,MAAM,OAAO,CAAE,EACrD;AAAA,QAAQ,CAAC,UACR,SAAS,OAAO,MAAM,EAAE,GAAG,QACvB,OAAO,CAAC,MAAM,EAAE,QAAQ,QAAQ,EACjC,QAAQ,CAAC,UAAU;AAClBA,wBAAa,KAAK;AAAA,YAChB,KAAK;AAAA,YACL,OAAO,EAAE,GAAG,MAAM,OAAO,MAAA;AAAA,YACzB,UAAU,MAAM;AAAA,UAAA,CACV;AAAA,QACV,CAAC;AAAA,MAAA;AAGP,aAAOA;AAAAA,IACT;AAAA,IACA,mBAAmB;AAAA,EAAA,CACpB;AAED,QAAM,EAAE,QAAA,IAAY,eAAe;AAAA,IACjC,QAAQ,CAAC,WAAW;AAAA,MAClB,SACE,MAAM,QACH,IAAI,CAAC,UAAU,MAAM,OAAQ,EAC7B,KAAK,CAAC,EACN,OAAO,OAAO,EACjB,IAAI,CAAC,EAAE,UAAU,GAAG,cAAc;AAAA,QAClC,KAAK;AAAA,QACL,OAAO;AAAA,UACL,GAAG;AAAA,UACH,0BAA0B;AAAA,UAC1B;AAAA,QAAA;AAAA,QAEF;AAAA,MAAA,EACA;AAAA,IAAA;AAAA,IAEJ,mBAAmB;AAAA,EAAA,CACpB;AAED,QAAM,aAAa,CAAC,GAAG,SAAS,GAAG,YAAY;AAE/C,yCAEK,UAAA,WAAW,IAAI,CAAC,OAAO,oCACrB,OAAA,EAAO,GAAG,OAAO,KAAK,eAAe,MAAM,GAAG,IAAI,CAAC,IAAI,CACzD,GACH;AAEJ;"}
|
|
1
|
+
{"version":3,"file":"Scripts.js","sources":["../../src/Scripts.tsx"],"sourcesContent":["import { Asset } from './Asset'\nimport { useRouterState } from './useRouterState'\nimport { useRouter } from './useRouter'\nimport type { RouterManagedTag } from '@tanstack/router-core'\n\n/**\n * Render body script tags collected from route matches and SSR manifests.\n * Should be placed near the end of the document body.\n */\n/**\n * Render body script tags collected from route matches and SSR manifests.\n * Should be placed near the end of the document body.\n */\nexport const Scripts = () => {\n const router = useRouter()\n const nonce = router.options.ssr?.nonce\n const assetScripts = useRouterState({\n select: (state) => {\n const assetScripts: Array<RouterManagedTag> = []\n const manifest = router.ssr?.manifest\n\n if (!manifest) {\n return []\n }\n\n state.matches\n .map((match) => router.looseRoutesById[match.routeId]!)\n .forEach((route) =>\n manifest.routes[route.id]?.assets\n ?.filter((d) => d.tag === 'script')\n .forEach((asset) => {\n assetScripts.push({\n tag: 'script',\n attrs: { ...asset.attrs, nonce },\n children: asset.children,\n } as any)\n }),\n )\n\n return assetScripts\n },\n structuralSharing: true as any,\n })\n\n const { scripts } = useRouterState({\n select: (state) => ({\n scripts: (\n state.matches\n .map((match) => match.scripts!)\n .flat(1)\n .filter(Boolean) as Array<RouterManagedTag>\n ).map(({ children, ...script }) => ({\n tag: 'script',\n attrs: {\n ...script,\n suppressHydrationWarning: true,\n nonce,\n },\n children,\n })),\n }),\n structuralSharing: true as any,\n })\n\n let serverBufferedScript: RouterManagedTag | undefined = undefined\n\n if (router.serverSsr) {\n serverBufferedScript = router.serverSsr.takeBufferedScripts()\n }\n\n const allScripts = [...scripts, ...assetScripts] as Array<RouterManagedTag>\n\n if (serverBufferedScript) {\n allScripts.unshift(serverBufferedScript)\n }\n\n return (\n <>\n {allScripts.map((asset, i) => (\n <Asset {...asset} key={`tsr-scripts-${asset.tag}-${i}`} />\n ))}\n </>\n )\n}\n"],"names":["assetScripts"],"mappings":";;;;;AAaO,MAAM,UAAU,MAAM;AAC3B,QAAM,SAAS,UAAA;AACf,QAAM,QAAQ,OAAO,QAAQ,KAAK;AAClC,QAAM,eAAe,eAAe;AAAA,IAClC,QAAQ,CAAC,UAAU;AACjB,YAAMA,gBAAwC,CAAA;AAC9C,YAAM,WAAW,OAAO,KAAK;AAE7B,UAAI,CAAC,UAAU;AACb,eAAO,CAAA;AAAA,MACT;AAEA,YAAM,QACH,IAAI,CAAC,UAAU,OAAO,gBAAgB,MAAM,OAAO,CAAE,EACrD;AAAA,QAAQ,CAAC,UACR,SAAS,OAAO,MAAM,EAAE,GAAG,QACvB,OAAO,CAAC,MAAM,EAAE,QAAQ,QAAQ,EACjC,QAAQ,CAAC,UAAU;AAClBA,wBAAa,KAAK;AAAA,YAChB,KAAK;AAAA,YACL,OAAO,EAAE,GAAG,MAAM,OAAO,MAAA;AAAA,YACzB,UAAU,MAAM;AAAA,UAAA,CACV;AAAA,QACV,CAAC;AAAA,MAAA;AAGP,aAAOA;AAAAA,IACT;AAAA,IACA,mBAAmB;AAAA,EAAA,CACpB;AAED,QAAM,EAAE,QAAA,IAAY,eAAe;AAAA,IACjC,QAAQ,CAAC,WAAW;AAAA,MAClB,SACE,MAAM,QACH,IAAI,CAAC,UAAU,MAAM,OAAQ,EAC7B,KAAK,CAAC,EACN,OAAO,OAAO,EACjB,IAAI,CAAC,EAAE,UAAU,GAAG,cAAc;AAAA,QAClC,KAAK;AAAA,QACL,OAAO;AAAA,UACL,GAAG;AAAA,UACH,0BAA0B;AAAA,UAC1B;AAAA,QAAA;AAAA,QAEF;AAAA,MAAA,EACA;AAAA,IAAA;AAAA,IAEJ,mBAAmB;AAAA,EAAA,CACpB;AAED,MAAI,uBAAqD;AAEzD,MAAI,OAAO,WAAW;AACpB,2BAAuB,OAAO,UAAU,oBAAA;AAAA,EAC1C;AAEA,QAAM,aAAa,CAAC,GAAG,SAAS,GAAG,YAAY;AAE/C,MAAI,sBAAsB;AACxB,eAAW,QAAQ,oBAAoB;AAAA,EACzC;AAEA,yCAEK,UAAA,WAAW,IAAI,CAAC,OAAO,oCACrB,OAAA,EAAO,GAAG,OAAO,KAAK,eAAe,MAAM,GAAG,IAAI,CAAC,IAAI,CACzD,GACH;AAEJ;"}
|
|
@@ -10,7 +10,7 @@ const renderRouterToString = async ({
|
|
|
10
10
|
const injectedHtml = await Promise.all(router.serverSsr.injectedHtml).then(
|
|
11
11
|
(htmls) => htmls.join("")
|
|
12
12
|
);
|
|
13
|
-
html = html.replace(`</body>`, `${injectedHtml}</body>`);
|
|
13
|
+
html = html.replace(`</body>`, () => `${injectedHtml}</body>`);
|
|
14
14
|
return new Response(`<!DOCTYPE html>${html}`, {
|
|
15
15
|
status: router.state.statusCode,
|
|
16
16
|
headers: responseHeaders
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"renderRouterToString.js","sources":["../../../src/ssr/renderRouterToString.tsx"],"sourcesContent":["import ReactDOMServer from 'react-dom/server'\nimport type { ReactNode } from 'react'\nimport type { AnyRouter } from '@tanstack/router-core'\n\nexport const renderRouterToString = async ({\n router,\n responseHeaders,\n children,\n}: {\n router: AnyRouter\n responseHeaders: Headers\n children: ReactNode\n}) => {\n try {\n let html = ReactDOMServer.renderToString(children)\n router.serverSsr!.setRenderFinished()\n const injectedHtml = await Promise.all(router.serverSsr!.injectedHtml).then(\n (htmls) => htmls.join(''),\n )\n html = html.replace(`</body>`, `${injectedHtml}</body>`)\n return new Response(`<!DOCTYPE html>${html}`, {\n status: router.state.statusCode,\n headers: responseHeaders,\n })\n } catch (error) {\n console.error('Render to string error:', error)\n return new Response('Internal Server Error', {\n status: 500,\n headers: responseHeaders,\n })\n }\n}\n"],"names":[],"mappings":";AAIO,MAAM,uBAAuB,OAAO;AAAA,EACzC;AAAA,EACA;AAAA,EACA;AACF,MAIM;AACJ,MAAI;AACF,QAAI,OAAO,eAAe,eAAe,QAAQ;AACjD,WAAO,UAAW,kBAAA;AAClB,UAAM,eAAe,MAAM,QAAQ,IAAI,OAAO,UAAW,YAAY,EAAE;AAAA,MACrE,CAAC,UAAU,MAAM,KAAK,EAAE;AAAA,IAAA;AAE1B,WAAO,KAAK,QAAQ,WAAW,GAAG,YAAY,SAAS;
|
|
1
|
+
{"version":3,"file":"renderRouterToString.js","sources":["../../../src/ssr/renderRouterToString.tsx"],"sourcesContent":["import ReactDOMServer from 'react-dom/server'\nimport type { ReactNode } from 'react'\nimport type { AnyRouter } from '@tanstack/router-core'\n\nexport const renderRouterToString = async ({\n router,\n responseHeaders,\n children,\n}: {\n router: AnyRouter\n responseHeaders: Headers\n children: ReactNode\n}) => {\n try {\n let html = ReactDOMServer.renderToString(children)\n router.serverSsr!.setRenderFinished()\n const injectedHtml = await Promise.all(router.serverSsr!.injectedHtml).then(\n (htmls) => htmls.join(''),\n )\n html = html.replace(`</body>`, () => `${injectedHtml}</body>`)\n return new Response(`<!DOCTYPE html>${html}`, {\n status: router.state.statusCode,\n headers: responseHeaders,\n })\n } catch (error) {\n console.error('Render to string error:', error)\n return new Response('Internal Server Error', {\n status: 500,\n headers: responseHeaders,\n })\n }\n}\n"],"names":[],"mappings":";AAIO,MAAM,uBAAuB,OAAO;AAAA,EACzC;AAAA,EACA;AAAA,EACA;AACF,MAIM;AACJ,MAAI;AACF,QAAI,OAAO,eAAe,eAAe,QAAQ;AACjD,WAAO,UAAW,kBAAA;AAClB,UAAM,eAAe,MAAM,QAAQ,IAAI,OAAO,UAAW,YAAY,EAAE;AAAA,MACrE,CAAC,UAAU,MAAM,KAAK,EAAE;AAAA,IAAA;AAE1B,WAAO,KAAK,QAAQ,WAAW,MAAM,GAAG,YAAY,SAAS;AAC7D,WAAO,IAAI,SAAS,kBAAkB,IAAI,IAAI;AAAA,MAC5C,QAAQ,OAAO,MAAM;AAAA,MACrB,SAAS;AAAA,IAAA,CACV;AAAA,EACH,SAAS,OAAO;AACd,YAAQ,MAAM,2BAA2B,KAAK;AAC9C,WAAO,IAAI,SAAS,yBAAyB;AAAA,MAC3C,QAAQ;AAAA,MACR,SAAS;AAAA,IAAA,CACV;AAAA,EACH;AACF;"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tanstack/react-router",
|
|
3
|
-
"version": "1.136.
|
|
3
|
+
"version": "1.136.10",
|
|
4
4
|
"description": "Modern and scalable routing for React applications",
|
|
5
5
|
"author": "Tanner Linsley",
|
|
6
6
|
"license": "MIT",
|
|
@@ -80,7 +80,7 @@
|
|
|
80
80
|
"tiny-invariant": "^1.3.3",
|
|
81
81
|
"tiny-warning": "^1.0.3",
|
|
82
82
|
"@tanstack/history": "1.133.28",
|
|
83
|
-
"@tanstack/router-core": "1.136.
|
|
83
|
+
"@tanstack/router-core": "1.136.9"
|
|
84
84
|
},
|
|
85
85
|
"devDependencies": {
|
|
86
86
|
"@testing-library/jest-dom": "^6.6.3",
|
package/src/Asset.tsx
CHANGED
|
@@ -144,7 +144,13 @@ function Script({
|
|
|
144
144
|
}, [attrs, children])
|
|
145
145
|
|
|
146
146
|
if (!router.isServer) {
|
|
147
|
-
|
|
147
|
+
// render an empty script on the client just to avoid hydration errors
|
|
148
|
+
return (
|
|
149
|
+
<script
|
|
150
|
+
suppressHydrationWarning
|
|
151
|
+
dangerouslySetInnerHTML={{ __html: '' }}
|
|
152
|
+
></script>
|
|
153
|
+
)
|
|
148
154
|
}
|
|
149
155
|
|
|
150
156
|
if (attrs?.src && typeof attrs.src === 'string') {
|
package/src/HeadContent.tsx
CHANGED
|
@@ -113,9 +113,9 @@ export const useTags = () => {
|
|
|
113
113
|
structuralSharing: true as any,
|
|
114
114
|
})
|
|
115
115
|
|
|
116
|
-
const
|
|
116
|
+
const preloadLinks = useRouterState({
|
|
117
117
|
select: (state) => {
|
|
118
|
-
const
|
|
118
|
+
const preloadLinks: Array<RouterManagedTag> = []
|
|
119
119
|
|
|
120
120
|
state.matches
|
|
121
121
|
.map((match) => router.looseRoutesById[match.routeId]!)
|
|
@@ -123,7 +123,7 @@ export const useTags = () => {
|
|
|
123
123
|
router.ssr?.manifest?.routes[route.id]?.preloads
|
|
124
124
|
?.filter(Boolean)
|
|
125
125
|
.forEach((preload) => {
|
|
126
|
-
|
|
126
|
+
preloadLinks.push({
|
|
127
127
|
tag: 'link',
|
|
128
128
|
attrs: {
|
|
129
129
|
rel: 'modulepreload',
|
|
@@ -134,7 +134,7 @@ export const useTags = () => {
|
|
|
134
134
|
}),
|
|
135
135
|
)
|
|
136
136
|
|
|
137
|
-
return
|
|
137
|
+
return preloadLinks
|
|
138
138
|
},
|
|
139
139
|
structuralSharing: true as any,
|
|
140
140
|
})
|
|
@@ -173,29 +173,12 @@ export const useTags = () => {
|
|
|
173
173
|
structuralSharing: true as any,
|
|
174
174
|
})
|
|
175
175
|
|
|
176
|
-
let serverHeadScript: RouterManagedTag | undefined = undefined
|
|
177
|
-
|
|
178
|
-
if (router.serverSsr) {
|
|
179
|
-
const bufferedScripts = router.serverSsr.takeBufferedScripts()
|
|
180
|
-
if (bufferedScripts) {
|
|
181
|
-
serverHeadScript = {
|
|
182
|
-
tag: 'script',
|
|
183
|
-
attrs: {
|
|
184
|
-
nonce,
|
|
185
|
-
className: '$tsr',
|
|
186
|
-
},
|
|
187
|
-
children: bufferedScripts,
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
|
|
192
176
|
return uniqBy(
|
|
193
177
|
[
|
|
194
178
|
...meta,
|
|
195
|
-
...
|
|
179
|
+
...preloadLinks,
|
|
196
180
|
...links,
|
|
197
181
|
...styles,
|
|
198
|
-
...(serverHeadScript ? [serverHeadScript] : []),
|
|
199
182
|
...headScripts,
|
|
200
183
|
] as Array<RouterManagedTag>,
|
|
201
184
|
(d) => {
|
package/src/ScriptOnce.tsx
CHANGED
|
@@ -14,7 +14,7 @@ export function ScriptOnce({ children }: { children: string }) {
|
|
|
14
14
|
nonce={router.options.ssr?.nonce}
|
|
15
15
|
className="$tsr"
|
|
16
16
|
dangerouslySetInnerHTML={{
|
|
17
|
-
__html:
|
|
17
|
+
__html: children + ';typeof $_TSR !== "undefined" && $_TSR.c()',
|
|
18
18
|
}}
|
|
19
19
|
/>
|
|
20
20
|
)
|
package/src/Scripts.tsx
CHANGED
|
@@ -62,8 +62,18 @@ export const Scripts = () => {
|
|
|
62
62
|
structuralSharing: true as any,
|
|
63
63
|
})
|
|
64
64
|
|
|
65
|
+
let serverBufferedScript: RouterManagedTag | undefined = undefined
|
|
66
|
+
|
|
67
|
+
if (router.serverSsr) {
|
|
68
|
+
serverBufferedScript = router.serverSsr.takeBufferedScripts()
|
|
69
|
+
}
|
|
70
|
+
|
|
65
71
|
const allScripts = [...scripts, ...assetScripts] as Array<RouterManagedTag>
|
|
66
72
|
|
|
73
|
+
if (serverBufferedScript) {
|
|
74
|
+
allScripts.unshift(serverBufferedScript)
|
|
75
|
+
}
|
|
76
|
+
|
|
67
77
|
return (
|
|
68
78
|
<>
|
|
69
79
|
{allScripts.map((asset, i) => (
|
|
@@ -17,7 +17,7 @@ export const renderRouterToString = async ({
|
|
|
17
17
|
const injectedHtml = await Promise.all(router.serverSsr!.injectedHtml).then(
|
|
18
18
|
(htmls) => htmls.join(''),
|
|
19
19
|
)
|
|
20
|
-
html = html.replace(`</body>`, `${injectedHtml}</body>`)
|
|
20
|
+
html = html.replace(`</body>`, () => `${injectedHtml}</body>`)
|
|
21
21
|
return new Response(`<!DOCTYPE html>${html}`, {
|
|
22
22
|
status: router.state.statusCode,
|
|
23
23
|
headers: responseHeaders,
|