@tanstack/solid-router 1.168.2 → 1.168.3
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/HeadContent.cjs +2 -2
- package/dist/cjs/HeadContent.cjs.map +1 -1
- package/dist/cjs/HeadContent.d.cts +5 -1
- package/dist/cjs/HeadContent.dev.cjs +2 -2
- package/dist/cjs/HeadContent.dev.cjs.map +1 -1
- package/dist/cjs/HeadContent.dev.d.cts +2 -1
- package/dist/cjs/headContentUtils.cjs +5 -2
- package/dist/cjs/headContentUtils.cjs.map +1 -1
- package/dist/cjs/headContentUtils.d.cts +2 -2
- package/dist/esm/HeadContent.d.ts +5 -1
- package/dist/esm/HeadContent.dev.d.ts +2 -1
- package/dist/esm/HeadContent.dev.js +2 -2
- package/dist/esm/HeadContent.dev.js.map +1 -1
- package/dist/esm/HeadContent.js +2 -2
- package/dist/esm/HeadContent.js.map +1 -1
- package/dist/esm/headContentUtils.d.ts +2 -2
- package/dist/esm/headContentUtils.js +6 -3
- package/dist/esm/headContentUtils.js.map +1 -1
- package/dist/source/HeadContent.d.ts +5 -1
- package/dist/source/HeadContent.dev.d.ts +2 -1
- package/dist/source/HeadContent.dev.jsx +2 -2
- package/dist/source/HeadContent.dev.jsx.map +1 -1
- package/dist/source/HeadContent.jsx +2 -2
- package/dist/source/HeadContent.jsx.map +1 -1
- package/dist/source/headContentUtils.d.ts +2 -2
- package/dist/source/headContentUtils.jsx +12 -4
- package/dist/source/headContentUtils.jsx.map +1 -1
- package/package.json +2 -2
- package/src/HeadContent.dev.tsx +3 -2
- package/src/HeadContent.tsx +7 -2
- package/src/headContentUtils.tsx +23 -5
package/dist/cjs/HeadContent.cjs
CHANGED
|
@@ -11,8 +11,8 @@ let _solidjs_meta = require("@solidjs/meta");
|
|
|
11
11
|
* to ensure it's part of the reactive tree and updates correctly during client-side navigation.
|
|
12
12
|
* The component uses portals internally to render content into the `<head>` element.
|
|
13
13
|
*/
|
|
14
|
-
function HeadContent() {
|
|
15
|
-
const tags = require_headContentUtils.useTags();
|
|
14
|
+
function HeadContent(props) {
|
|
15
|
+
const tags = require_headContentUtils.useTags(props.assetCrossOrigin);
|
|
16
16
|
return (0, solid_js_web.createComponent)(_solidjs_meta.MetaProvider, { get children() {
|
|
17
17
|
return (0, solid_js_web.createComponent)(solid_js.For, {
|
|
18
18
|
get each() {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"HeadContent.cjs","names":["MetaProvider","For","Asset","useTags","HeadContent","tags","_$createComponent","children","each","tag"],"sources":["../../src/HeadContent.tsx"],"sourcesContent":["import { MetaProvider } from '@solidjs/meta'\nimport { For } from 'solid-js'\nimport { Asset } from './Asset'\nimport { useTags } from './headContentUtils'\n\n/**\n * @description The `HeadContent` component is used to render meta tags, links, and scripts for the current route.\n * When using full document hydration (hydrating from `<html>`), this component should be rendered in the `<body>`\n * to ensure it's part of the reactive tree and updates correctly during client-side navigation.\n * The component uses portals internally to render content into the `<head>` element.\n */\nexport function HeadContent() {\n const tags = useTags()\n\n return (\n <MetaProvider>\n <For each={tags()}>{(tag) => <Asset {...tag} />}</For>\n </MetaProvider>\n )\n}\n"],"mappings":";;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"HeadContent.cjs","names":["MetaProvider","For","Asset","useTags","AssetCrossOriginConfig","HeadContentProps","assetCrossOrigin","HeadContent","props","tags","_$createComponent","children","each","tag"],"sources":["../../src/HeadContent.tsx"],"sourcesContent":["import { MetaProvider } from '@solidjs/meta'\nimport { For } from 'solid-js'\nimport { Asset } from './Asset'\nimport { useTags } from './headContentUtils'\nimport type { AssetCrossOriginConfig } from '@tanstack/router-core'\n\nexport interface HeadContentProps {\n assetCrossOrigin?: AssetCrossOriginConfig\n}\n\n/**\n * @description The `HeadContent` component is used to render meta tags, links, and scripts for the current route.\n * When using full document hydration (hydrating from `<html>`), this component should be rendered in the `<body>`\n * to ensure it's part of the reactive tree and updates correctly during client-side navigation.\n * The component uses portals internally to render content into the `<head>` element.\n */\nexport function HeadContent(props: HeadContentProps) {\n const tags = useTags(props.assetCrossOrigin)\n\n return (\n <MetaProvider>\n <For each={tags()}>{(tag) => <Asset {...tag} />}</For>\n </MetaProvider>\n )\n}\n"],"mappings":";;;;;;;;;;;;;AAgBA,SAAgBO,YAAYC,OAAyB;CACnD,MAAMC,OAAON,yBAAAA,QAAQK,MAAMF,iBAAiB;AAE5C,SAAA,GAAA,aAAA,iBACGN,cAAAA,cAAY,EAAA,IAAAW,WAAA;AAAA,UAAA,GAAA,aAAA,iBACVV,SAAAA,KAAG;GAAA,IAACW,OAAI;AAAA,WAAEH,MAAM;;GAAAE,WAAIE,SAAAA,GAAAA,aAAAA,iBAASX,cAAAA,OAAUW,IAAG;GAAI,CAAA;IAAA,CAAA"}
|
|
@@ -1,7 +1,11 @@
|
|
|
1
|
+
import { AssetCrossOriginConfig } from '@tanstack/router-core';
|
|
2
|
+
export interface HeadContentProps {
|
|
3
|
+
assetCrossOrigin?: AssetCrossOriginConfig;
|
|
4
|
+
}
|
|
1
5
|
/**
|
|
2
6
|
* @description The `HeadContent` component is used to render meta tags, links, and scripts for the current route.
|
|
3
7
|
* When using full document hydration (hydrating from `<html>`), this component should be rendered in the `<body>`
|
|
4
8
|
* to ensure it's part of the reactive tree and updates correctly during client-side navigation.
|
|
5
9
|
* The component uses portals internally to render content into the `<head>` element.
|
|
6
10
|
*/
|
|
7
|
-
export declare function HeadContent(): import("solid-js").JSX.Element;
|
|
11
|
+
export declare function HeadContent(props: HeadContentProps): import("solid-js").JSX.Element;
|
|
@@ -16,8 +16,8 @@ var DEV_STYLES_ATTR = "data-tanstack-router-dev-styles";
|
|
|
16
16
|
* Development version: filters out dev styles link after hydration and
|
|
17
17
|
* includes a fallback cleanup effect for hydration mismatch cases.
|
|
18
18
|
*/
|
|
19
|
-
function HeadContent() {
|
|
20
|
-
const tags = require_headContentUtils.useTags();
|
|
19
|
+
function HeadContent(props) {
|
|
20
|
+
const tags = require_headContentUtils.useTags(props.assetCrossOrigin);
|
|
21
21
|
const hydrated = require_ClientOnly.useHydrated();
|
|
22
22
|
(0, solid_js.createEffect)(() => {
|
|
23
23
|
if (hydrated()) document.querySelectorAll(`link[${DEV_STYLES_ATTR}]`).forEach((el) => el.remove());
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"HeadContent.dev.cjs","names":["MetaProvider","For","createEffect","createMemo","Asset","useHydrated","useTags","DEV_STYLES_ATTR","HeadContent","tags","hydrated","document","querySelectorAll","forEach","el","remove","filteredTags","filter","tag","attrs","_$createComponent","children","each"],"sources":["../../src/HeadContent.dev.tsx"],"sourcesContent":["import { MetaProvider } from '@solidjs/meta'\nimport { For, createEffect, createMemo } from 'solid-js'\nimport { Asset } from './Asset'\nimport { useHydrated } from './ClientOnly'\nimport { useTags } from './headContentUtils'\n\nconst DEV_STYLES_ATTR = 'data-tanstack-router-dev-styles'\n\n/**\n * @description The `HeadContent` component is used to render meta tags, links, and scripts for the current route.\n * When using full document hydration (hydrating from `<html>`), this component should be rendered in the `<body>`\n * to ensure it's part of the reactive tree and updates correctly during client-side navigation.\n * The component uses portals internally to render content into the `<head>` element.\n *\n * Development version: filters out dev styles link after hydration and\n * includes a fallback cleanup effect for hydration mismatch cases.\n */\nexport function HeadContent() {\n const tags = useTags()\n const hydrated = useHydrated()\n\n // Fallback cleanup for hydration mismatch cases\n // Runs when hydration completes to remove any orphaned dev styles links from DOM\n createEffect(() => {\n if (hydrated()) {\n document\n .querySelectorAll(`link[${DEV_STYLES_ATTR}]`)\n .forEach((el) => el.remove())\n }\n })\n\n // Filter out dev styles after hydration\n const filteredTags = createMemo(() => {\n if (hydrated()) {\n return tags().filter((tag) => !tag.attrs?.[DEV_STYLES_ATTR])\n }\n return tags()\n })\n\n return (\n <MetaProvider>\n <For each={filteredTags()}>{(tag) => <Asset {...tag} />}</For>\n </MetaProvider>\n )\n}\n"],"mappings":";;;;;;;;
|
|
1
|
+
{"version":3,"file":"HeadContent.dev.cjs","names":["MetaProvider","For","createEffect","createMemo","Asset","useHydrated","useTags","HeadContentProps","DEV_STYLES_ATTR","HeadContent","props","tags","assetCrossOrigin","hydrated","document","querySelectorAll","forEach","el","remove","filteredTags","filter","tag","attrs","_$createComponent","children","each"],"sources":["../../src/HeadContent.dev.tsx"],"sourcesContent":["import { MetaProvider } from '@solidjs/meta'\nimport { For, createEffect, createMemo } from 'solid-js'\nimport { Asset } from './Asset'\nimport { useHydrated } from './ClientOnly'\nimport { useTags } from './headContentUtils'\nimport type { HeadContentProps } from './HeadContent'\n\nconst DEV_STYLES_ATTR = 'data-tanstack-router-dev-styles'\n\n/**\n * @description The `HeadContent` component is used to render meta tags, links, and scripts for the current route.\n * When using full document hydration (hydrating from `<html>`), this component should be rendered in the `<body>`\n * to ensure it's part of the reactive tree and updates correctly during client-side navigation.\n * The component uses portals internally to render content into the `<head>` element.\n *\n * Development version: filters out dev styles link after hydration and\n * includes a fallback cleanup effect for hydration mismatch cases.\n */\nexport function HeadContent(props: HeadContentProps) {\n const tags = useTags(props.assetCrossOrigin)\n const hydrated = useHydrated()\n\n // Fallback cleanup for hydration mismatch cases\n // Runs when hydration completes to remove any orphaned dev styles links from DOM\n createEffect(() => {\n if (hydrated()) {\n document\n .querySelectorAll(`link[${DEV_STYLES_ATTR}]`)\n .forEach((el) => el.remove())\n }\n })\n\n // Filter out dev styles after hydration\n const filteredTags = createMemo(() => {\n if (hydrated()) {\n return tags().filter((tag) => !tag.attrs?.[DEV_STYLES_ATTR])\n }\n return tags()\n })\n\n return (\n <MetaProvider>\n <For each={filteredTags()}>{(tag) => <Asset {...tag} />}</For>\n </MetaProvider>\n )\n}\n"],"mappings":";;;;;;;;AAOA,IAAMQ,kBAAkB;;;;;;;;;;AAWxB,SAAgBC,YAAYC,OAAyB;CACnD,MAAMC,OAAOL,yBAAAA,QAAQI,MAAME,iBAAiB;CAC5C,MAAMC,WAAWR,mBAAAA,aAAa;AAI9BH,EAAAA,GAAAA,SAAAA,oBAAmB;AACjB,MAAIW,UAAU,CACZC,UACGC,iBAAiB,QAAQP,gBAAe,GAAI,CAC5CQ,SAASC,OAAOA,GAAGC,QAAQ,CAAC;GAEjC;CAGF,MAAMC,gBAAAA,GAAAA,SAAAA,kBAAgC;AACpC,MAAIN,UAAU,CACZ,QAAOF,MAAM,CAACS,QAAQC,QAAQ,CAACA,IAAIC,QAAQd,iBAAiB;AAE9D,SAAOG,MAAM;GACb;AAEF,SAAA,GAAA,aAAA,iBACGX,cAAAA,cAAY,EAAA,IAAAwB,WAAA;AAAA,UAAA,GAAA,aAAA,iBACVvB,SAAAA,KAAG;GAAA,IAACwB,OAAI;AAAA,WAAEN,cAAc;;GAAAK,WAAIH,SAAAA,GAAAA,aAAAA,iBAASjB,cAAAA,OAAUiB,IAAG;GAAI,CAAA;IAAA,CAAA"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { HeadContentProps } from './HeadContent.cjs';
|
|
1
2
|
/**
|
|
2
3
|
* @description The `HeadContent` component is used to render meta tags, links, and scripts for the current route.
|
|
3
4
|
* When using full document hydration (hydrating from `<html>`), this component should be rendered in the `<body>`
|
|
@@ -7,4 +8,4 @@
|
|
|
7
8
|
* Development version: filters out dev styles link after hydration and
|
|
8
9
|
* includes a fallback cleanup effect for hydration mismatch cases.
|
|
9
10
|
*/
|
|
10
|
-
export declare function HeadContent(): import("solid-js").JSX.Element;
|
|
11
|
+
export declare function HeadContent(props: HeadContentProps): import("solid-js").JSX.Element;
|
|
@@ -8,7 +8,7 @@ solid_js = require_runtime.__toESM(solid_js);
|
|
|
8
8
|
* Build the list of head/link/meta/script tags to render for active matches.
|
|
9
9
|
* Used internally by `HeadContent`.
|
|
10
10
|
*/
|
|
11
|
-
var useTags = () => {
|
|
11
|
+
var useTags = (assetCrossOrigin) => {
|
|
12
12
|
const router = require_useRouter.useRouter();
|
|
13
13
|
const nonce = router.options.ssr?.nonce;
|
|
14
14
|
const activeMatches = solid_js.createMemo(() => router.stores.activeMatchesSnapshot.state);
|
|
@@ -75,6 +75,7 @@ var useTags = () => {
|
|
|
75
75
|
tag: "link",
|
|
76
76
|
attrs: {
|
|
77
77
|
...asset.attrs,
|
|
78
|
+
crossOrigin: (0, _tanstack_router_core.getAssetCrossOrigin)(assetCrossOrigin, "stylesheet") ?? asset.attrs?.crossOrigin,
|
|
78
79
|
nonce
|
|
79
80
|
}
|
|
80
81
|
}));
|
|
@@ -84,11 +85,13 @@ var useTags = () => {
|
|
|
84
85
|
const matches = activeMatches();
|
|
85
86
|
const preloadLinks = [];
|
|
86
87
|
matches.map((match) => router.looseRoutesById[match.routeId]).forEach((route) => router.ssr?.manifest?.routes[route.id]?.preloads?.filter(Boolean).forEach((preload) => {
|
|
88
|
+
const preloadLink = (0, _tanstack_router_core.resolveManifestAssetLink)(preload);
|
|
87
89
|
preloadLinks.push({
|
|
88
90
|
tag: "link",
|
|
89
91
|
attrs: {
|
|
90
92
|
rel: "modulepreload",
|
|
91
|
-
href:
|
|
93
|
+
href: preloadLink.href,
|
|
94
|
+
crossOrigin: (0, _tanstack_router_core.getAssetCrossOrigin)(assetCrossOrigin, "modulepreload") ?? preloadLink.crossOrigin,
|
|
92
95
|
nonce
|
|
93
96
|
}
|
|
94
97
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"headContentUtils.cjs","names":["Solid","escapeHtml","replaceEqualDeep","useRouter","RouterManagedTag","useTags","router","nonce","options","ssr","activeMatches","createMemo","stores","activeMatchesSnapshot","state","routeMeta","map","match","meta","filter","Boolean","Accessor","Array","resultMeta","metaByAttribute","Record","title","routeMetasArray","i","length","metas","j","m","tag","children","json","JSON","stringify","push","attrs","type","attribute","name","property","content","reverse","links","matches","constructed","flat","link","manifest","assets","routes","routeId","asset","preloadLinks","looseRoutesById","forEach","route","id","preloads","preload","rel","href","styles","style","headScripts","script","prev","next","uniqBy","d","undefined","arr","T","fn","item","seen","Set","key","has","add"],"sources":["../../src/headContentUtils.tsx"],"sourcesContent":["import * as Solid from 'solid-js'\nimport { escapeHtml, replaceEqualDeep } from '@tanstack/router-core'\nimport { useRouter } from './useRouter'\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 activeMatches = Solid.createMemo(\n () => router.stores.activeMatchesSnapshot.state,\n )\n const routeMeta = Solid.createMemo(() =>\n activeMatches()\n .map((match) => match.meta!)\n .filter(Boolean),\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 = Solid.createMemo(() => {\n const matches = activeMatches()\n const constructed = 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 = 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 const preloadLinks = Solid.createMemo(() => {\n const matches = activeMatches()\n const preloadLinks: Array<RouterManagedTag> = []\n\n 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 const styles = Solid.createMemo(() =>\n (\n activeMatches()\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 = Solid.createMemo(() =>\n (\n activeMatches()\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 Solid.createMemo((prev: Array<RouterManagedTag> | undefined) => {\n const next = 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 if (prev === undefined) {\n return next\n }\n return replaceEqualDeep(prev, next)\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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"}
|
|
1
|
+
{"version":3,"file":"headContentUtils.cjs","names":["Solid","escapeHtml","getAssetCrossOrigin","replaceEqualDeep","resolveManifestAssetLink","useRouter","AssetCrossOriginConfig","RouterManagedTag","useTags","assetCrossOrigin","router","nonce","options","ssr","activeMatches","createMemo","stores","activeMatchesSnapshot","state","routeMeta","map","match","meta","filter","Boolean","Accessor","Array","resultMeta","metaByAttribute","Record","title","routeMetasArray","i","length","metas","j","m","tag","children","json","JSON","stringify","push","attrs","type","attribute","name","property","content","reverse","links","matches","constructed","flat","link","manifest","assets","routes","routeId","asset","crossOrigin","preloadLinks","looseRoutesById","forEach","route","id","preloads","preload","preloadLink","rel","href","styles","style","headScripts","script","prev","next","uniqBy","d","undefined","arr","T","fn","item","seen","Set","key","has","add"],"sources":["../../src/headContentUtils.tsx"],"sourcesContent":["import * as Solid from 'solid-js'\nimport {\n escapeHtml,\n getAssetCrossOrigin,\n replaceEqualDeep,\n resolveManifestAssetLink,\n} from '@tanstack/router-core'\nimport { useRouter } from './useRouter'\nimport type {\n AssetCrossOriginConfig,\n RouterManagedTag,\n} 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 = (assetCrossOrigin?: AssetCrossOriginConfig) => {\n const router = useRouter()\n const nonce = router.options.ssr?.nonce\n const activeMatches = Solid.createMemo(\n () => router.stores.activeMatchesSnapshot.state,\n )\n const routeMeta = Solid.createMemo(() =>\n activeMatches()\n .map((match) => match.meta!)\n .filter(Boolean),\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 = Solid.createMemo(() => {\n const matches = activeMatches()\n const constructed = 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 = 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 crossOrigin:\n getAssetCrossOrigin(assetCrossOrigin, 'stylesheet') ??\n asset.attrs?.crossOrigin,\n nonce,\n },\n }) satisfies RouterManagedTag,\n )\n\n return [...constructed, ...assets]\n })\n\n const preloadLinks = Solid.createMemo(() => {\n const matches = activeMatches()\n const preloadLinks: Array<RouterManagedTag> = []\n\n 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 const preloadLink = resolveManifestAssetLink(preload)\n preloadLinks.push({\n tag: 'link',\n attrs: {\n rel: 'modulepreload',\n href: preloadLink.href,\n crossOrigin:\n getAssetCrossOrigin(assetCrossOrigin, 'modulepreload') ??\n preloadLink.crossOrigin,\n nonce,\n },\n })\n }),\n )\n\n return preloadLinks\n })\n\n const styles = Solid.createMemo(() =>\n (\n activeMatches()\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 = Solid.createMemo(() =>\n (\n activeMatches()\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 Solid.createMemo((prev: Array<RouterManagedTag> | undefined) => {\n const next = 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 if (prev === undefined) {\n return next\n }\n return replaceEqualDeep(prev, next)\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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"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { RouterManagedTag } from '@tanstack/router-core';
|
|
1
|
+
import { AssetCrossOriginConfig, RouterManagedTag } from '@tanstack/router-core';
|
|
2
2
|
import * as Solid from 'solid-js';
|
|
3
3
|
/**
|
|
4
4
|
* Build the list of head/link/meta/script tags to render for active matches.
|
|
5
5
|
* Used internally by `HeadContent`.
|
|
6
6
|
*/
|
|
7
|
-
export declare const useTags: () => Solid.Accessor<RouterManagedTag[]>;
|
|
7
|
+
export declare const useTags: (assetCrossOrigin?: AssetCrossOriginConfig) => Solid.Accessor<RouterManagedTag[]>;
|
|
8
8
|
export declare function uniqBy<T>(arr: Array<T>, fn: (item: T) => string): T[];
|
|
@@ -1,7 +1,11 @@
|
|
|
1
|
+
import { AssetCrossOriginConfig } from '@tanstack/router-core';
|
|
2
|
+
export interface HeadContentProps {
|
|
3
|
+
assetCrossOrigin?: AssetCrossOriginConfig;
|
|
4
|
+
}
|
|
1
5
|
/**
|
|
2
6
|
* @description The `HeadContent` component is used to render meta tags, links, and scripts for the current route.
|
|
3
7
|
* When using full document hydration (hydrating from `<html>`), this component should be rendered in the `<body>`
|
|
4
8
|
* to ensure it's part of the reactive tree and updates correctly during client-side navigation.
|
|
5
9
|
* The component uses portals internally to render content into the `<head>` element.
|
|
6
10
|
*/
|
|
7
|
-
export declare function HeadContent(): import("solid-js").JSX.Element;
|
|
11
|
+
export declare function HeadContent(props: HeadContentProps): import("solid-js").JSX.Element;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { HeadContentProps } from './HeadContent.js';
|
|
1
2
|
/**
|
|
2
3
|
* @description The `HeadContent` component is used to render meta tags, links, and scripts for the current route.
|
|
3
4
|
* When using full document hydration (hydrating from `<html>`), this component should be rendered in the `<body>`
|
|
@@ -7,4 +8,4 @@
|
|
|
7
8
|
* Development version: filters out dev styles link after hydration and
|
|
8
9
|
* includes a fallback cleanup effect for hydration mismatch cases.
|
|
9
10
|
*/
|
|
10
|
-
export declare function HeadContent(): import("solid-js").JSX.Element;
|
|
11
|
+
export declare function HeadContent(props: HeadContentProps): import("solid-js").JSX.Element;
|
|
@@ -15,8 +15,8 @@ var DEV_STYLES_ATTR = "data-tanstack-router-dev-styles";
|
|
|
15
15
|
* Development version: filters out dev styles link after hydration and
|
|
16
16
|
* includes a fallback cleanup effect for hydration mismatch cases.
|
|
17
17
|
*/
|
|
18
|
-
function HeadContent() {
|
|
19
|
-
const tags = useTags();
|
|
18
|
+
function HeadContent(props) {
|
|
19
|
+
const tags = useTags(props.assetCrossOrigin);
|
|
20
20
|
const hydrated = useHydrated();
|
|
21
21
|
createEffect(() => {
|
|
22
22
|
if (hydrated()) document.querySelectorAll(`link[${DEV_STYLES_ATTR}]`).forEach((el) => el.remove());
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"HeadContent.dev.js","names":["MetaProvider","For","createEffect","createMemo","Asset","useHydrated","useTags","DEV_STYLES_ATTR","HeadContent","tags","hydrated","document","querySelectorAll","forEach","el","remove","filteredTags","filter","tag","attrs","_$createComponent","children","each"],"sources":["../../src/HeadContent.dev.tsx"],"sourcesContent":["import { MetaProvider } from '@solidjs/meta'\nimport { For, createEffect, createMemo } from 'solid-js'\nimport { Asset } from './Asset'\nimport { useHydrated } from './ClientOnly'\nimport { useTags } from './headContentUtils'\n\nconst DEV_STYLES_ATTR = 'data-tanstack-router-dev-styles'\n\n/**\n * @description The `HeadContent` component is used to render meta tags, links, and scripts for the current route.\n * When using full document hydration (hydrating from `<html>`), this component should be rendered in the `<body>`\n * to ensure it's part of the reactive tree and updates correctly during client-side navigation.\n * The component uses portals internally to render content into the `<head>` element.\n *\n * Development version: filters out dev styles link after hydration and\n * includes a fallback cleanup effect for hydration mismatch cases.\n */\nexport function HeadContent() {\n const tags = useTags()\n const hydrated = useHydrated()\n\n // Fallback cleanup for hydration mismatch cases\n // Runs when hydration completes to remove any orphaned dev styles links from DOM\n createEffect(() => {\n if (hydrated()) {\n document\n .querySelectorAll(`link[${DEV_STYLES_ATTR}]`)\n .forEach((el) => el.remove())\n }\n })\n\n // Filter out dev styles after hydration\n const filteredTags = createMemo(() => {\n if (hydrated()) {\n return tags().filter((tag) => !tag.attrs?.[DEV_STYLES_ATTR])\n }\n return tags()\n })\n\n return (\n <MetaProvider>\n <For each={filteredTags()}>{(tag) => <Asset {...tag} />}</For>\n </MetaProvider>\n )\n}\n"],"mappings":";;;;;;;
|
|
1
|
+
{"version":3,"file":"HeadContent.dev.js","names":["MetaProvider","For","createEffect","createMemo","Asset","useHydrated","useTags","HeadContentProps","DEV_STYLES_ATTR","HeadContent","props","tags","assetCrossOrigin","hydrated","document","querySelectorAll","forEach","el","remove","filteredTags","filter","tag","attrs","_$createComponent","children","each"],"sources":["../../src/HeadContent.dev.tsx"],"sourcesContent":["import { MetaProvider } from '@solidjs/meta'\nimport { For, createEffect, createMemo } from 'solid-js'\nimport { Asset } from './Asset'\nimport { useHydrated } from './ClientOnly'\nimport { useTags } from './headContentUtils'\nimport type { HeadContentProps } from './HeadContent'\n\nconst DEV_STYLES_ATTR = 'data-tanstack-router-dev-styles'\n\n/**\n * @description The `HeadContent` component is used to render meta tags, links, and scripts for the current route.\n * When using full document hydration (hydrating from `<html>`), this component should be rendered in the `<body>`\n * to ensure it's part of the reactive tree and updates correctly during client-side navigation.\n * The component uses portals internally to render content into the `<head>` element.\n *\n * Development version: filters out dev styles link after hydration and\n * includes a fallback cleanup effect for hydration mismatch cases.\n */\nexport function HeadContent(props: HeadContentProps) {\n const tags = useTags(props.assetCrossOrigin)\n const hydrated = useHydrated()\n\n // Fallback cleanup for hydration mismatch cases\n // Runs when hydration completes to remove any orphaned dev styles links from DOM\n createEffect(() => {\n if (hydrated()) {\n document\n .querySelectorAll(`link[${DEV_STYLES_ATTR}]`)\n .forEach((el) => el.remove())\n }\n })\n\n // Filter out dev styles after hydration\n const filteredTags = createMemo(() => {\n if (hydrated()) {\n return tags().filter((tag) => !tag.attrs?.[DEV_STYLES_ATTR])\n }\n return tags()\n })\n\n return (\n <MetaProvider>\n <For each={filteredTags()}>{(tag) => <Asset {...tag} />}</For>\n </MetaProvider>\n )\n}\n"],"mappings":";;;;;;;AAOA,IAAMQ,kBAAkB;;;;;;;;;;AAWxB,SAAgBC,YAAYC,OAAyB;CACnD,MAAMC,OAAOL,QAAQI,MAAME,iBAAiB;CAC5C,MAAMC,WAAWR,aAAa;AAI9BH,oBAAmB;AACjB,MAAIW,UAAU,CACZC,UACGC,iBAAiB,QAAQP,gBAAe,GAAI,CAC5CQ,SAASC,OAAOA,GAAGC,QAAQ,CAAC;GAEjC;CAGF,MAAMC,eAAehB,iBAAiB;AACpC,MAAIU,UAAU,CACZ,QAAOF,MAAM,CAACS,QAAQC,QAAQ,CAACA,IAAIC,QAAQd,iBAAiB;AAE9D,SAAOG,MAAM;GACb;AAEF,QAAAY,gBACGvB,cAAY,EAAA,IAAAwB,WAAA;AAAA,SAAAD,gBACVtB,KAAG;GAAA,IAACwB,OAAI;AAAA,WAAEN,cAAc;;GAAAK,WAAIH,QAAGE,gBAAMnB,OAAUiB,IAAG;GAAI,CAAA;IAAA,CAAA"}
|
package/dist/esm/HeadContent.js
CHANGED
|
@@ -10,8 +10,8 @@ import { MetaProvider } from "@solidjs/meta";
|
|
|
10
10
|
* to ensure it's part of the reactive tree and updates correctly during client-side navigation.
|
|
11
11
|
* The component uses portals internally to render content into the `<head>` element.
|
|
12
12
|
*/
|
|
13
|
-
function HeadContent() {
|
|
14
|
-
const tags = useTags();
|
|
13
|
+
function HeadContent(props) {
|
|
14
|
+
const tags = useTags(props.assetCrossOrigin);
|
|
15
15
|
return createComponent(MetaProvider, { get children() {
|
|
16
16
|
return createComponent(For, {
|
|
17
17
|
get each() {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"HeadContent.js","names":["MetaProvider","For","Asset","useTags","HeadContent","tags","_$createComponent","children","each","tag"],"sources":["../../src/HeadContent.tsx"],"sourcesContent":["import { MetaProvider } from '@solidjs/meta'\nimport { For } from 'solid-js'\nimport { Asset } from './Asset'\nimport { useTags } from './headContentUtils'\n\n/**\n * @description The `HeadContent` component is used to render meta tags, links, and scripts for the current route.\n * When using full document hydration (hydrating from `<html>`), this component should be rendered in the `<body>`\n * to ensure it's part of the reactive tree and updates correctly during client-side navigation.\n * The component uses portals internally to render content into the `<head>` element.\n */\nexport function HeadContent() {\n const tags = useTags()\n\n return (\n <MetaProvider>\n <For each={tags()}>{(tag) => <Asset {...tag} />}</For>\n </MetaProvider>\n )\n}\n"],"mappings":";;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"HeadContent.js","names":["MetaProvider","For","Asset","useTags","AssetCrossOriginConfig","HeadContentProps","assetCrossOrigin","HeadContent","props","tags","_$createComponent","children","each","tag"],"sources":["../../src/HeadContent.tsx"],"sourcesContent":["import { MetaProvider } from '@solidjs/meta'\nimport { For } from 'solid-js'\nimport { Asset } from './Asset'\nimport { useTags } from './headContentUtils'\nimport type { AssetCrossOriginConfig } from '@tanstack/router-core'\n\nexport interface HeadContentProps {\n assetCrossOrigin?: AssetCrossOriginConfig\n}\n\n/**\n * @description The `HeadContent` component is used to render meta tags, links, and scripts for the current route.\n * When using full document hydration (hydrating from `<html>`), this component should be rendered in the `<body>`\n * to ensure it's part of the reactive tree and updates correctly during client-side navigation.\n * The component uses portals internally to render content into the `<head>` element.\n */\nexport function HeadContent(props: HeadContentProps) {\n const tags = useTags(props.assetCrossOrigin)\n\n return (\n <MetaProvider>\n <For each={tags()}>{(tag) => <Asset {...tag} />}</For>\n </MetaProvider>\n )\n}\n"],"mappings":";;;;;;;;;;;;AAgBA,SAAgBO,YAAYC,OAAyB;CACnD,MAAMC,OAAON,QAAQK,MAAMF,iBAAiB;AAE5C,QAAAI,gBACGV,cAAY,EAAA,IAAAW,WAAA;AAAA,SAAAD,gBACVT,KAAG;GAAA,IAACW,OAAI;AAAA,WAAEH,MAAM;;GAAAE,WAAIE,QAAGH,gBAAMR,OAAUW,IAAG;GAAI,CAAA;IAAA,CAAA"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { RouterManagedTag } from '@tanstack/router-core';
|
|
1
|
+
import { AssetCrossOriginConfig, RouterManagedTag } from '@tanstack/router-core';
|
|
2
2
|
import * as Solid from 'solid-js';
|
|
3
3
|
/**
|
|
4
4
|
* Build the list of head/link/meta/script tags to render for active matches.
|
|
5
5
|
* Used internally by `HeadContent`.
|
|
6
6
|
*/
|
|
7
|
-
export declare const useTags: () => Solid.Accessor<RouterManagedTag[]>;
|
|
7
|
+
export declare const useTags: (assetCrossOrigin?: AssetCrossOriginConfig) => Solid.Accessor<RouterManagedTag[]>;
|
|
8
8
|
export declare function uniqBy<T>(arr: Array<T>, fn: (item: T) => string): T[];
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { useRouter } from "./useRouter.js";
|
|
2
|
-
import { escapeHtml, replaceEqualDeep } from "@tanstack/router-core";
|
|
2
|
+
import { escapeHtml, getAssetCrossOrigin, replaceEqualDeep, resolveManifestAssetLink } from "@tanstack/router-core";
|
|
3
3
|
import * as Solid from "solid-js";
|
|
4
4
|
//#region src/headContentUtils.tsx
|
|
5
5
|
/**
|
|
6
6
|
* Build the list of head/link/meta/script tags to render for active matches.
|
|
7
7
|
* Used internally by `HeadContent`.
|
|
8
8
|
*/
|
|
9
|
-
var useTags = () => {
|
|
9
|
+
var useTags = (assetCrossOrigin) => {
|
|
10
10
|
const router = useRouter();
|
|
11
11
|
const nonce = router.options.ssr?.nonce;
|
|
12
12
|
const activeMatches = Solid.createMemo(() => router.stores.activeMatchesSnapshot.state);
|
|
@@ -73,6 +73,7 @@ var useTags = () => {
|
|
|
73
73
|
tag: "link",
|
|
74
74
|
attrs: {
|
|
75
75
|
...asset.attrs,
|
|
76
|
+
crossOrigin: getAssetCrossOrigin(assetCrossOrigin, "stylesheet") ?? asset.attrs?.crossOrigin,
|
|
76
77
|
nonce
|
|
77
78
|
}
|
|
78
79
|
}));
|
|
@@ -82,11 +83,13 @@ var useTags = () => {
|
|
|
82
83
|
const matches = activeMatches();
|
|
83
84
|
const preloadLinks = [];
|
|
84
85
|
matches.map((match) => router.looseRoutesById[match.routeId]).forEach((route) => router.ssr?.manifest?.routes[route.id]?.preloads?.filter(Boolean).forEach((preload) => {
|
|
86
|
+
const preloadLink = resolveManifestAssetLink(preload);
|
|
85
87
|
preloadLinks.push({
|
|
86
88
|
tag: "link",
|
|
87
89
|
attrs: {
|
|
88
90
|
rel: "modulepreload",
|
|
89
|
-
href:
|
|
91
|
+
href: preloadLink.href,
|
|
92
|
+
crossOrigin: getAssetCrossOrigin(assetCrossOrigin, "modulepreload") ?? preloadLink.crossOrigin,
|
|
90
93
|
nonce
|
|
91
94
|
}
|
|
92
95
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"headContentUtils.js","names":["Solid","escapeHtml","replaceEqualDeep","useRouter","RouterManagedTag","useTags","router","nonce","options","ssr","activeMatches","createMemo","stores","activeMatchesSnapshot","state","routeMeta","map","match","meta","filter","Boolean","Accessor","Array","resultMeta","metaByAttribute","Record","title","routeMetasArray","i","length","metas","j","m","tag","children","json","JSON","stringify","push","attrs","type","attribute","name","property","content","reverse","links","matches","constructed","flat","link","manifest","assets","routes","routeId","asset","preloadLinks","looseRoutesById","forEach","route","id","preloads","preload","rel","href","styles","style","headScripts","script","prev","next","uniqBy","d","undefined","arr","T","fn","item","seen","Set","key","has","add"],"sources":["../../src/headContentUtils.tsx"],"sourcesContent":["import * as Solid from 'solid-js'\nimport { escapeHtml, replaceEqualDeep } from '@tanstack/router-core'\nimport { useRouter } from './useRouter'\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 activeMatches = Solid.createMemo(\n () => router.stores.activeMatchesSnapshot.state,\n )\n const routeMeta = Solid.createMemo(() =>\n activeMatches()\n .map((match) => match.meta!)\n .filter(Boolean),\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 = Solid.createMemo(() => {\n const matches = activeMatches()\n const constructed = 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 = 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 const preloadLinks = Solid.createMemo(() => {\n const matches = activeMatches()\n const preloadLinks: Array<RouterManagedTag> = []\n\n 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 const styles = Solid.createMemo(() =>\n (\n activeMatches()\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 = Solid.createMemo(() =>\n (\n activeMatches()\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 Solid.createMemo((prev: Array<RouterManagedTag> | undefined) => {\n const next = 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 if (prev === undefined) {\n return next\n }\n return replaceEqualDeep(prev, next)\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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"}
|
|
1
|
+
{"version":3,"file":"headContentUtils.js","names":["Solid","escapeHtml","getAssetCrossOrigin","replaceEqualDeep","resolveManifestAssetLink","useRouter","AssetCrossOriginConfig","RouterManagedTag","useTags","assetCrossOrigin","router","nonce","options","ssr","activeMatches","createMemo","stores","activeMatchesSnapshot","state","routeMeta","map","match","meta","filter","Boolean","Accessor","Array","resultMeta","metaByAttribute","Record","title","routeMetasArray","i","length","metas","j","m","tag","children","json","JSON","stringify","push","attrs","type","attribute","name","property","content","reverse","links","matches","constructed","flat","link","manifest","assets","routes","routeId","asset","crossOrigin","preloadLinks","looseRoutesById","forEach","route","id","preloads","preload","preloadLink","rel","href","styles","style","headScripts","script","prev","next","uniqBy","d","undefined","arr","T","fn","item","seen","Set","key","has","add"],"sources":["../../src/headContentUtils.tsx"],"sourcesContent":["import * as Solid from 'solid-js'\nimport {\n escapeHtml,\n getAssetCrossOrigin,\n replaceEqualDeep,\n resolveManifestAssetLink,\n} from '@tanstack/router-core'\nimport { useRouter } from './useRouter'\nimport type {\n AssetCrossOriginConfig,\n RouterManagedTag,\n} 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 = (assetCrossOrigin?: AssetCrossOriginConfig) => {\n const router = useRouter()\n const nonce = router.options.ssr?.nonce\n const activeMatches = Solid.createMemo(\n () => router.stores.activeMatchesSnapshot.state,\n )\n const routeMeta = Solid.createMemo(() =>\n activeMatches()\n .map((match) => match.meta!)\n .filter(Boolean),\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 = Solid.createMemo(() => {\n const matches = activeMatches()\n const constructed = 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 = 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 crossOrigin:\n getAssetCrossOrigin(assetCrossOrigin, 'stylesheet') ??\n asset.attrs?.crossOrigin,\n nonce,\n },\n }) satisfies RouterManagedTag,\n )\n\n return [...constructed, ...assets]\n })\n\n const preloadLinks = Solid.createMemo(() => {\n const matches = activeMatches()\n const preloadLinks: Array<RouterManagedTag> = []\n\n 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 const preloadLink = resolveManifestAssetLink(preload)\n preloadLinks.push({\n tag: 'link',\n attrs: {\n rel: 'modulepreload',\n href: preloadLink.href,\n crossOrigin:\n getAssetCrossOrigin(assetCrossOrigin, 'modulepreload') ??\n preloadLink.crossOrigin,\n nonce,\n },\n })\n }),\n )\n\n return preloadLinks\n })\n\n const styles = Solid.createMemo(() =>\n (\n activeMatches()\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 = Solid.createMemo(() =>\n (\n activeMatches()\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 Solid.createMemo((prev: Array<RouterManagedTag> | undefined) => {\n const next = 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 if (prev === undefined) {\n return next\n }\n return replaceEqualDeep(prev, next)\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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"}
|
|
@@ -1,7 +1,11 @@
|
|
|
1
|
+
import type { AssetCrossOriginConfig } from '@tanstack/router-core';
|
|
2
|
+
export interface HeadContentProps {
|
|
3
|
+
assetCrossOrigin?: AssetCrossOriginConfig;
|
|
4
|
+
}
|
|
1
5
|
/**
|
|
2
6
|
* @description The `HeadContent` component is used to render meta tags, links, and scripts for the current route.
|
|
3
7
|
* When using full document hydration (hydrating from `<html>`), this component should be rendered in the `<body>`
|
|
4
8
|
* to ensure it's part of the reactive tree and updates correctly during client-side navigation.
|
|
5
9
|
* The component uses portals internally to render content into the `<head>` element.
|
|
6
10
|
*/
|
|
7
|
-
export declare function HeadContent(): import("solid-js").JSX.Element;
|
|
11
|
+
export declare function HeadContent(props: HeadContentProps): import("solid-js").JSX.Element;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { HeadContentProps } from './HeadContent';
|
|
1
2
|
/**
|
|
2
3
|
* @description The `HeadContent` component is used to render meta tags, links, and scripts for the current route.
|
|
3
4
|
* When using full document hydration (hydrating from `<html>`), this component should be rendered in the `<body>`
|
|
@@ -7,4 +8,4 @@
|
|
|
7
8
|
* Development version: filters out dev styles link after hydration and
|
|
8
9
|
* includes a fallback cleanup effect for hydration mismatch cases.
|
|
9
10
|
*/
|
|
10
|
-
export declare function HeadContent(): import("solid-js").JSX.Element;
|
|
11
|
+
export declare function HeadContent(props: HeadContentProps): import("solid-js").JSX.Element;
|
|
@@ -13,8 +13,8 @@ const DEV_STYLES_ATTR = 'data-tanstack-router-dev-styles';
|
|
|
13
13
|
* Development version: filters out dev styles link after hydration and
|
|
14
14
|
* includes a fallback cleanup effect for hydration mismatch cases.
|
|
15
15
|
*/
|
|
16
|
-
export function HeadContent() {
|
|
17
|
-
const tags = useTags();
|
|
16
|
+
export function HeadContent(props) {
|
|
17
|
+
const tags = useTags(props.assetCrossOrigin);
|
|
18
18
|
const hydrated = useHydrated();
|
|
19
19
|
// Fallback cleanup for hydration mismatch cases
|
|
20
20
|
// Runs when hydration completes to remove any orphaned dev styles links from DOM
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"HeadContent.dev.jsx","sourceRoot":"","sources":["../../src/HeadContent.dev.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAC5C,OAAO,EAAE,GAAG,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AACxD,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAA;AAC/B,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAC1C,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAA;
|
|
1
|
+
{"version":3,"file":"HeadContent.dev.jsx","sourceRoot":"","sources":["../../src/HeadContent.dev.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAC5C,OAAO,EAAE,GAAG,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AACxD,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAA;AAC/B,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAC1C,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAA;AAG5C,MAAM,eAAe,GAAG,iCAAiC,CAAA;AAEzD;;;;;;;;GAQG;AACH,MAAM,UAAU,WAAW,CAAC,KAAuB;IACjD,MAAM,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAA;IAC5C,MAAM,QAAQ,GAAG,WAAW,EAAE,CAAA;IAE9B,gDAAgD;IAChD,iFAAiF;IACjF,YAAY,CAAC,GAAG,EAAE;QAChB,IAAI,QAAQ,EAAE,EAAE,CAAC;YACf,QAAQ;iBACL,gBAAgB,CAAC,QAAQ,eAAe,GAAG,CAAC;iBAC5C,OAAO,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,CAAA;QACjC,CAAC;IACH,CAAC,CAAC,CAAA;IAEF,wCAAwC;IACxC,MAAM,YAAY,GAAG,UAAU,CAAC,GAAG,EAAE;QACnC,IAAI,QAAQ,EAAE,EAAE,CAAC;YACf,OAAO,IAAI,EAAE,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,eAAe,CAAC,CAAC,CAAA;QAC9D,CAAC;QACD,OAAO,IAAI,EAAE,CAAA;IACf,CAAC,CAAC,CAAA;IAEF,OAAO,CACL,CAAC,YAAY,CACX;MAAA,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,EAAG,CAAC,EAAE,GAAG,CAC/D;IAAA,EAAE,YAAY,CAAC,CAChB,CAAA;AACH,CAAC"}
|
|
@@ -8,8 +8,8 @@ import { useTags } from './headContentUtils';
|
|
|
8
8
|
* to ensure it's part of the reactive tree and updates correctly during client-side navigation.
|
|
9
9
|
* The component uses portals internally to render content into the `<head>` element.
|
|
10
10
|
*/
|
|
11
|
-
export function HeadContent() {
|
|
12
|
-
const tags = useTags();
|
|
11
|
+
export function HeadContent(props) {
|
|
12
|
+
const tags = useTags(props.assetCrossOrigin);
|
|
13
13
|
return (<MetaProvider>
|
|
14
14
|
<For each={tags()}>{(tag) => <Asset {...tag}/>}</For>
|
|
15
15
|
</MetaProvider>);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"HeadContent.jsx","sourceRoot":"","sources":["../../src/HeadContent.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAC5C,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA;AAC9B,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAA;AAC/B,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAA;
|
|
1
|
+
{"version":3,"file":"HeadContent.jsx","sourceRoot":"","sources":["../../src/HeadContent.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAC5C,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA;AAC9B,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAA;AAC/B,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAA;AAO5C;;;;;GAKG;AACH,MAAM,UAAU,WAAW,CAAC,KAAuB;IACjD,MAAM,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAA;IAE5C,OAAO,CACL,CAAC,YAAY,CACX;MAAA,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,EAAG,CAAC,EAAE,GAAG,CACvD;IAAA,EAAE,YAAY,CAAC,CAChB,CAAA;AACH,CAAC"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import * as Solid from 'solid-js';
|
|
2
|
-
import type { RouterManagedTag } from '@tanstack/router-core';
|
|
2
|
+
import type { AssetCrossOriginConfig, RouterManagedTag } from '@tanstack/router-core';
|
|
3
3
|
/**
|
|
4
4
|
* Build the list of head/link/meta/script tags to render for active matches.
|
|
5
5
|
* Used internally by `HeadContent`.
|
|
6
6
|
*/
|
|
7
|
-
export declare const useTags: () => Solid.Accessor<RouterManagedTag[]>;
|
|
7
|
+
export declare const useTags: (assetCrossOrigin?: AssetCrossOriginConfig) => Solid.Accessor<RouterManagedTag[]>;
|
|
8
8
|
export declare function uniqBy<T>(arr: Array<T>, fn: (item: T) => string): T[];
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import * as Solid from 'solid-js';
|
|
2
|
-
import { escapeHtml, replaceEqualDeep } from '@tanstack/router-core';
|
|
2
|
+
import { escapeHtml, getAssetCrossOrigin, replaceEqualDeep, resolveManifestAssetLink, } from '@tanstack/router-core';
|
|
3
3
|
import { useRouter } from './useRouter';
|
|
4
4
|
/**
|
|
5
5
|
* Build the list of head/link/meta/script tags to render for active matches.
|
|
6
6
|
* Used internally by `HeadContent`.
|
|
7
7
|
*/
|
|
8
|
-
export const useTags = () => {
|
|
8
|
+
export const useTags = (assetCrossOrigin) => {
|
|
9
9
|
const router = useRouter();
|
|
10
10
|
const nonce = router.options.ssr?.nonce;
|
|
11
11
|
const activeMatches = Solid.createMemo(() => router.stores.activeMatchesSnapshot.state);
|
|
@@ -104,7 +104,12 @@ export const useTags = () => {
|
|
|
104
104
|
.filter((asset) => asset.tag === 'link')
|
|
105
105
|
.map((asset) => ({
|
|
106
106
|
tag: 'link',
|
|
107
|
-
attrs: {
|
|
107
|
+
attrs: {
|
|
108
|
+
...asset.attrs,
|
|
109
|
+
crossOrigin: getAssetCrossOrigin(assetCrossOrigin, 'stylesheet') ??
|
|
110
|
+
asset.attrs?.crossOrigin,
|
|
111
|
+
nonce,
|
|
112
|
+
},
|
|
108
113
|
}));
|
|
109
114
|
return [...constructed, ...assets];
|
|
110
115
|
});
|
|
@@ -116,11 +121,14 @@ export const useTags = () => {
|
|
|
116
121
|
.forEach((route) => router.ssr?.manifest?.routes[route.id]?.preloads
|
|
117
122
|
?.filter(Boolean)
|
|
118
123
|
.forEach((preload) => {
|
|
124
|
+
const preloadLink = resolveManifestAssetLink(preload);
|
|
119
125
|
preloadLinks.push({
|
|
120
126
|
tag: 'link',
|
|
121
127
|
attrs: {
|
|
122
128
|
rel: 'modulepreload',
|
|
123
|
-
href:
|
|
129
|
+
href: preloadLink.href,
|
|
130
|
+
crossOrigin: getAssetCrossOrigin(assetCrossOrigin, 'modulepreload') ??
|
|
131
|
+
preloadLink.crossOrigin,
|
|
124
132
|
nonce,
|
|
125
133
|
},
|
|
126
134
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"headContentUtils.jsx","sourceRoot":"","sources":["../../src/headContentUtils.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,UAAU,CAAA;AACjC,OAAO,
|
|
1
|
+
{"version":3,"file":"headContentUtils.jsx","sourceRoot":"","sources":["../../src/headContentUtils.tsx"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tanstack/solid-router",
|
|
3
|
-
"version": "1.168.
|
|
3
|
+
"version": "1.168.3",
|
|
4
4
|
"description": "Modern and scalable routing for Solid applications",
|
|
5
5
|
"author": "Tanner Linsley",
|
|
6
6
|
"license": "MIT",
|
|
@@ -92,7 +92,7 @@
|
|
|
92
92
|
"@solidjs/meta": "^0.29.4",
|
|
93
93
|
"isbot": "^5.1.22",
|
|
94
94
|
"@tanstack/history": "1.161.6",
|
|
95
|
-
"@tanstack/router-core": "1.168.
|
|
95
|
+
"@tanstack/router-core": "1.168.3"
|
|
96
96
|
},
|
|
97
97
|
"devDependencies": {
|
|
98
98
|
"@solidjs/testing-library": "^0.8.10",
|
package/src/HeadContent.dev.tsx
CHANGED
|
@@ -3,6 +3,7 @@ import { For, createEffect, createMemo } from 'solid-js'
|
|
|
3
3
|
import { Asset } from './Asset'
|
|
4
4
|
import { useHydrated } from './ClientOnly'
|
|
5
5
|
import { useTags } from './headContentUtils'
|
|
6
|
+
import type { HeadContentProps } from './HeadContent'
|
|
6
7
|
|
|
7
8
|
const DEV_STYLES_ATTR = 'data-tanstack-router-dev-styles'
|
|
8
9
|
|
|
@@ -15,8 +16,8 @@ const DEV_STYLES_ATTR = 'data-tanstack-router-dev-styles'
|
|
|
15
16
|
* Development version: filters out dev styles link after hydration and
|
|
16
17
|
* includes a fallback cleanup effect for hydration mismatch cases.
|
|
17
18
|
*/
|
|
18
|
-
export function HeadContent() {
|
|
19
|
-
const tags = useTags()
|
|
19
|
+
export function HeadContent(props: HeadContentProps) {
|
|
20
|
+
const tags = useTags(props.assetCrossOrigin)
|
|
20
21
|
const hydrated = useHydrated()
|
|
21
22
|
|
|
22
23
|
// Fallback cleanup for hydration mismatch cases
|
package/src/HeadContent.tsx
CHANGED
|
@@ -2,6 +2,11 @@ import { MetaProvider } from '@solidjs/meta'
|
|
|
2
2
|
import { For } from 'solid-js'
|
|
3
3
|
import { Asset } from './Asset'
|
|
4
4
|
import { useTags } from './headContentUtils'
|
|
5
|
+
import type { AssetCrossOriginConfig } from '@tanstack/router-core'
|
|
6
|
+
|
|
7
|
+
export interface HeadContentProps {
|
|
8
|
+
assetCrossOrigin?: AssetCrossOriginConfig
|
|
9
|
+
}
|
|
5
10
|
|
|
6
11
|
/**
|
|
7
12
|
* @description The `HeadContent` component is used to render meta tags, links, and scripts for the current route.
|
|
@@ -9,8 +14,8 @@ import { useTags } from './headContentUtils'
|
|
|
9
14
|
* to ensure it's part of the reactive tree and updates correctly during client-side navigation.
|
|
10
15
|
* The component uses portals internally to render content into the `<head>` element.
|
|
11
16
|
*/
|
|
12
|
-
export function HeadContent() {
|
|
13
|
-
const tags = useTags()
|
|
17
|
+
export function HeadContent(props: HeadContentProps) {
|
|
18
|
+
const tags = useTags(props.assetCrossOrigin)
|
|
14
19
|
|
|
15
20
|
return (
|
|
16
21
|
<MetaProvider>
|
package/src/headContentUtils.tsx
CHANGED
|
@@ -1,13 +1,21 @@
|
|
|
1
1
|
import * as Solid from 'solid-js'
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
escapeHtml,
|
|
4
|
+
getAssetCrossOrigin,
|
|
5
|
+
replaceEqualDeep,
|
|
6
|
+
resolveManifestAssetLink,
|
|
7
|
+
} from '@tanstack/router-core'
|
|
3
8
|
import { useRouter } from './useRouter'
|
|
4
|
-
import type {
|
|
9
|
+
import type {
|
|
10
|
+
AssetCrossOriginConfig,
|
|
11
|
+
RouterManagedTag,
|
|
12
|
+
} from '@tanstack/router-core'
|
|
5
13
|
|
|
6
14
|
/**
|
|
7
15
|
* Build the list of head/link/meta/script tags to render for active matches.
|
|
8
16
|
* Used internally by `HeadContent`.
|
|
9
17
|
*/
|
|
10
|
-
export const useTags = () => {
|
|
18
|
+
export const useTags = (assetCrossOrigin?: AssetCrossOriginConfig) => {
|
|
11
19
|
const router = useRouter()
|
|
12
20
|
const nonce = router.options.ssr?.nonce
|
|
13
21
|
const activeMatches = Solid.createMemo(
|
|
@@ -116,7 +124,13 @@ export const useTags = () => {
|
|
|
116
124
|
(asset) =>
|
|
117
125
|
({
|
|
118
126
|
tag: 'link',
|
|
119
|
-
attrs: {
|
|
127
|
+
attrs: {
|
|
128
|
+
...asset.attrs,
|
|
129
|
+
crossOrigin:
|
|
130
|
+
getAssetCrossOrigin(assetCrossOrigin, 'stylesheet') ??
|
|
131
|
+
asset.attrs?.crossOrigin,
|
|
132
|
+
nonce,
|
|
133
|
+
},
|
|
120
134
|
}) satisfies RouterManagedTag,
|
|
121
135
|
)
|
|
122
136
|
|
|
@@ -133,11 +147,15 @@ export const useTags = () => {
|
|
|
133
147
|
router.ssr?.manifest?.routes[route.id]?.preloads
|
|
134
148
|
?.filter(Boolean)
|
|
135
149
|
.forEach((preload) => {
|
|
150
|
+
const preloadLink = resolveManifestAssetLink(preload)
|
|
136
151
|
preloadLinks.push({
|
|
137
152
|
tag: 'link',
|
|
138
153
|
attrs: {
|
|
139
154
|
rel: 'modulepreload',
|
|
140
|
-
href:
|
|
155
|
+
href: preloadLink.href,
|
|
156
|
+
crossOrigin:
|
|
157
|
+
getAssetCrossOrigin(assetCrossOrigin, 'modulepreload') ??
|
|
158
|
+
preloadLink.crossOrigin,
|
|
141
159
|
nonce,
|
|
142
160
|
},
|
|
143
161
|
})
|