@tanstack/solid-router 1.147.1 → 1.149.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 +5 -196
- package/dist/cjs/HeadContent.cjs.map +1 -1
- package/dist/cjs/HeadContent.d.cts +1 -4
- package/dist/cjs/HeadContent.dev.cjs +36 -0
- package/dist/cjs/HeadContent.dev.cjs.map +1 -0
- package/dist/cjs/HeadContent.dev.d.cts +10 -0
- package/dist/cjs/headContentUtils.cjs +174 -0
- package/dist/cjs/headContentUtils.cjs.map +1 -0
- package/dist/cjs/headContentUtils.d.cts +7 -0
- package/dist/cjs/index.cjs +2 -1
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.cts +2 -1
- package/dist/cjs/index.dev.cjs +259 -0
- package/dist/cjs/index.dev.cjs.map +1 -0
- package/dist/cjs/index.dev.d.cts +2 -0
- package/dist/cjs/ssr/RouterServer.cjs +2 -2
- package/dist/cjs/ssr/RouterServer.cjs.map +1 -1
- package/dist/esm/HeadContent.d.ts +1 -4
- package/dist/esm/HeadContent.dev.d.ts +10 -0
- package/dist/esm/HeadContent.dev.js +36 -0
- package/dist/esm/HeadContent.dev.js.map +1 -0
- package/dist/esm/HeadContent.js +6 -181
- package/dist/esm/HeadContent.js.map +1 -1
- package/dist/esm/headContentUtils.d.ts +7 -0
- package/dist/esm/headContentUtils.js +157 -0
- package/dist/esm/headContentUtils.js.map +1 -0
- package/dist/esm/index.d.ts +2 -1
- package/dist/esm/index.dev.d.ts +2 -0
- package/dist/esm/index.dev.js +142 -0
- package/dist/esm/index.dev.js.map +1 -0
- package/dist/esm/index.js +2 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/ssr/RouterServer.js +1 -1
- package/dist/esm/ssr/RouterServer.js.map +1 -1
- package/dist/source/HeadContent.d.ts +1 -4
- package/dist/source/HeadContent.dev.d.ts +10 -0
- package/dist/source/HeadContent.dev.jsx +39 -0
- package/dist/source/HeadContent.dev.jsx.map +1 -0
- package/dist/source/HeadContent.jsx +2 -202
- package/dist/source/HeadContent.jsx.map +1 -1
- package/dist/source/headContentUtils.d.ts +7 -0
- package/dist/source/headContentUtils.jsx +181 -0
- package/dist/source/headContentUtils.jsx.map +1 -0
- package/dist/source/index.d.ts +2 -1
- package/dist/source/index.dev.d.ts +2 -0
- package/dist/source/index.dev.jsx +6 -0
- package/dist/source/index.dev.jsx.map +1 -0
- package/dist/source/index.jsx +2 -1
- package/dist/source/index.jsx.map +1 -1
- package/dist/source/ssr/RouterServer.jsx +1 -1
- package/dist/source/ssr/RouterServer.jsx.map +1 -1
- package/package.json +5 -2
- package/src/HeadContent.dev.tsx +45 -0
- package/src/HeadContent.tsx +2 -235
- package/src/headContentUtils.tsx +209 -0
- package/src/index.dev.tsx +6 -0
- package/src/index.tsx +2 -1
- package/src/ssr/RouterServer.tsx +1 -1
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import * as Solid from "solid-js";
|
|
2
|
+
import { escapeHtml } from "@tanstack/router-core";
|
|
3
|
+
import { useRouter } from "./useRouter.js";
|
|
4
|
+
import { useRouterState } from "./useRouterState.js";
|
|
5
|
+
const useTags = () => {
|
|
6
|
+
const router = useRouter();
|
|
7
|
+
const nonce = router.options.ssr?.nonce;
|
|
8
|
+
const routeMeta = useRouterState({
|
|
9
|
+
select: (state) => {
|
|
10
|
+
return state.matches.map((match) => match.meta).filter(Boolean);
|
|
11
|
+
}
|
|
12
|
+
});
|
|
13
|
+
const meta = Solid.createMemo(() => {
|
|
14
|
+
const resultMeta = [];
|
|
15
|
+
const metaByAttribute = {};
|
|
16
|
+
let title;
|
|
17
|
+
const routeMetasArray = routeMeta();
|
|
18
|
+
for (let i = routeMetasArray.length - 1; i >= 0; i--) {
|
|
19
|
+
const metas = routeMetasArray[i];
|
|
20
|
+
for (let j = metas.length - 1; j >= 0; j--) {
|
|
21
|
+
const m = metas[j];
|
|
22
|
+
if (!m) continue;
|
|
23
|
+
if (m.title) {
|
|
24
|
+
if (!title) {
|
|
25
|
+
title = {
|
|
26
|
+
tag: "title",
|
|
27
|
+
children: m.title
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
} else if ("script:ld+json" in m) {
|
|
31
|
+
try {
|
|
32
|
+
const json = JSON.stringify(m["script:ld+json"]);
|
|
33
|
+
resultMeta.push({
|
|
34
|
+
tag: "script",
|
|
35
|
+
attrs: {
|
|
36
|
+
type: "application/ld+json"
|
|
37
|
+
},
|
|
38
|
+
children: escapeHtml(json)
|
|
39
|
+
});
|
|
40
|
+
} catch {
|
|
41
|
+
}
|
|
42
|
+
} else {
|
|
43
|
+
const attribute = m.name ?? m.property;
|
|
44
|
+
if (attribute) {
|
|
45
|
+
if (metaByAttribute[attribute]) {
|
|
46
|
+
continue;
|
|
47
|
+
} else {
|
|
48
|
+
metaByAttribute[attribute] = true;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
resultMeta.push({
|
|
52
|
+
tag: "meta",
|
|
53
|
+
attrs: {
|
|
54
|
+
...m,
|
|
55
|
+
nonce
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
if (title) {
|
|
62
|
+
resultMeta.push(title);
|
|
63
|
+
}
|
|
64
|
+
if (router.options.ssr?.nonce) {
|
|
65
|
+
resultMeta.push({
|
|
66
|
+
tag: "meta",
|
|
67
|
+
attrs: {
|
|
68
|
+
property: "csp-nonce",
|
|
69
|
+
content: router.options.ssr.nonce
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
resultMeta.reverse();
|
|
74
|
+
return resultMeta;
|
|
75
|
+
});
|
|
76
|
+
const links = useRouterState({
|
|
77
|
+
select: (state) => {
|
|
78
|
+
const constructed = state.matches.map((match) => match.links).filter(Boolean).flat(1).map((link) => ({
|
|
79
|
+
tag: "link",
|
|
80
|
+
attrs: {
|
|
81
|
+
...link,
|
|
82
|
+
nonce
|
|
83
|
+
}
|
|
84
|
+
}));
|
|
85
|
+
const manifest = router.ssr?.manifest;
|
|
86
|
+
const assets = state.matches.map((match) => manifest?.routes[match.routeId]?.assets ?? []).filter(Boolean).flat(1).filter((asset) => asset.tag === "link").map((asset) => ({
|
|
87
|
+
tag: "link",
|
|
88
|
+
attrs: {
|
|
89
|
+
...asset.attrs,
|
|
90
|
+
nonce
|
|
91
|
+
}
|
|
92
|
+
}));
|
|
93
|
+
return [...constructed, ...assets];
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
const preloadLinks = useRouterState({
|
|
97
|
+
select: (state) => {
|
|
98
|
+
const preloadLinks2 = [];
|
|
99
|
+
state.matches.map((match) => router.looseRoutesById[match.routeId]).forEach((route) => router.ssr?.manifest?.routes[route.id]?.preloads?.filter(Boolean).forEach((preload) => {
|
|
100
|
+
preloadLinks2.push({
|
|
101
|
+
tag: "link",
|
|
102
|
+
attrs: {
|
|
103
|
+
rel: "modulepreload",
|
|
104
|
+
href: preload,
|
|
105
|
+
nonce
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
}));
|
|
109
|
+
return preloadLinks2;
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
const styles = useRouterState({
|
|
113
|
+
select: (state) => state.matches.map((match) => match.styles).flat(1).filter(Boolean).map(({
|
|
114
|
+
children,
|
|
115
|
+
...style
|
|
116
|
+
}) => ({
|
|
117
|
+
tag: "style",
|
|
118
|
+
attrs: {
|
|
119
|
+
...style,
|
|
120
|
+
nonce
|
|
121
|
+
},
|
|
122
|
+
children
|
|
123
|
+
}))
|
|
124
|
+
});
|
|
125
|
+
const headScripts = useRouterState({
|
|
126
|
+
select: (state) => state.matches.map((match) => match.headScripts).flat(1).filter(Boolean).map(({
|
|
127
|
+
children,
|
|
128
|
+
...script
|
|
129
|
+
}) => ({
|
|
130
|
+
tag: "script",
|
|
131
|
+
attrs: {
|
|
132
|
+
...script,
|
|
133
|
+
nonce
|
|
134
|
+
},
|
|
135
|
+
children
|
|
136
|
+
}))
|
|
137
|
+
});
|
|
138
|
+
return () => uniqBy([...meta(), ...preloadLinks(), ...links(), ...styles(), ...headScripts()], (d) => {
|
|
139
|
+
return JSON.stringify(d);
|
|
140
|
+
});
|
|
141
|
+
};
|
|
142
|
+
function uniqBy(arr, fn) {
|
|
143
|
+
const seen = /* @__PURE__ */ new Set();
|
|
144
|
+
return arr.filter((item) => {
|
|
145
|
+
const key = fn(item);
|
|
146
|
+
if (seen.has(key)) {
|
|
147
|
+
return false;
|
|
148
|
+
}
|
|
149
|
+
seen.add(key);
|
|
150
|
+
return true;
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
export {
|
|
154
|
+
uniqBy,
|
|
155
|
+
useTags
|
|
156
|
+
};
|
|
157
|
+
//# sourceMappingURL=headContentUtils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"headContentUtils.js","sources":["../../src/headContentUtils.tsx"],"sourcesContent":["import * as Solid from 'solid-js'\nimport { escapeHtml } from '@tanstack/router-core'\nimport { useRouter } from './useRouter'\nimport { useRouterState } from './useRouterState'\nimport type { RouterManagedTag } from '@tanstack/router-core'\n\n/**\n * Build the list of head/link/meta/script tags to render for active matches.\n * Used internally by `HeadContent`.\n */\nexport const useTags = () => {\n const router = useRouter()\n const nonce = router.options.ssr?.nonce\n const routeMeta = useRouterState({\n select: (state) => {\n return state.matches.map((match) => match.meta!).filter(Boolean)\n },\n })\n\n const meta: Solid.Accessor<Array<RouterManagedTag>> = Solid.createMemo(() => {\n const resultMeta: Array<RouterManagedTag> = []\n const metaByAttribute: Record<string, true> = {}\n let title: RouterManagedTag | undefined\n const routeMetasArray = routeMeta()\n for (let i = routeMetasArray.length - 1; i >= 0; i--) {\n const metas = routeMetasArray[i]!\n for (let j = metas.length - 1; j >= 0; j--) {\n const m = metas[j]\n if (!m) continue\n\n if (m.title) {\n if (!title) {\n title = {\n tag: 'title',\n children: m.title,\n }\n }\n } else if ('script:ld+json' in m) {\n // Handle JSON-LD structured data\n // Content is HTML-escaped to prevent XSS when injected via innerHTML\n try {\n const json = JSON.stringify(m['script:ld+json'])\n resultMeta.push({\n tag: 'script',\n attrs: {\n type: 'application/ld+json',\n },\n children: escapeHtml(json),\n })\n } catch {\n // Skip invalid JSON-LD objects\n }\n } else {\n const attribute = m.name ?? m.property\n if (attribute) {\n if (metaByAttribute[attribute]) {\n continue\n } else {\n metaByAttribute[attribute] = true\n }\n }\n\n resultMeta.push({\n tag: 'meta',\n attrs: {\n ...m,\n nonce,\n },\n })\n }\n }\n }\n\n if (title) {\n resultMeta.push(title)\n }\n\n if (router.options.ssr?.nonce) {\n resultMeta.push({\n tag: 'meta',\n attrs: {\n property: 'csp-nonce',\n content: router.options.ssr.nonce,\n },\n })\n }\n resultMeta.reverse()\n\n return resultMeta\n })\n\n const links = useRouterState({\n select: (state) => {\n const constructed = state.matches\n .map((match) => match.links!)\n .filter(Boolean)\n .flat(1)\n .map((link) => ({\n tag: 'link',\n attrs: {\n ...link,\n nonce,\n },\n })) satisfies Array<RouterManagedTag>\n\n const manifest = router.ssr?.manifest\n\n const assets = state.matches\n .map((match) => manifest?.routes[match.routeId]?.assets ?? [])\n .filter(Boolean)\n .flat(1)\n .filter((asset) => asset.tag === 'link')\n .map(\n (asset) =>\n ({\n tag: 'link',\n attrs: { ...asset.attrs, nonce },\n }) satisfies RouterManagedTag,\n )\n\n return [...constructed, ...assets]\n },\n })\n\n const preloadLinks = useRouterState({\n select: (state) => {\n const preloadLinks: Array<RouterManagedTag> = []\n\n state.matches\n .map((match) => router.looseRoutesById[match.routeId]!)\n .forEach((route) =>\n router.ssr?.manifest?.routes[route.id]?.preloads\n ?.filter(Boolean)\n .forEach((preload) => {\n preloadLinks.push({\n tag: 'link',\n attrs: {\n rel: 'modulepreload',\n href: preload,\n nonce,\n },\n })\n }),\n )\n\n return preloadLinks\n },\n })\n\n const styles = useRouterState({\n select: (state) =>\n (\n state.matches\n .map((match) => match.styles!)\n .flat(1)\n .filter(Boolean) as Array<RouterManagedTag>\n ).map(({ children, ...style }) => ({\n tag: 'style',\n attrs: {\n ...style,\n nonce,\n },\n children,\n })),\n })\n\n const headScripts = useRouterState({\n select: (state) =>\n (\n state.matches\n .map((match) => match.headScripts!)\n .flat(1)\n .filter(Boolean) as Array<RouterManagedTag>\n ).map(({ children, ...script }) => ({\n tag: 'script',\n attrs: {\n ...script,\n nonce,\n },\n children,\n })),\n })\n\n return () =>\n uniqBy(\n [\n ...meta(),\n ...preloadLinks(),\n ...links(),\n ...styles(),\n ...headScripts(),\n ] as Array<RouterManagedTag>,\n (d) => {\n return JSON.stringify(d)\n },\n )\n}\n\nexport function uniqBy<T>(arr: Array<T>, fn: (item: T) => string) {\n const seen = new Set<string>()\n return arr.filter((item) => {\n const key = fn(item)\n if (seen.has(key)) {\n return false\n }\n seen.add(key)\n return true\n })\n}\n"],"names":["useTags","router","useRouter","nonce","options","ssr","routeMeta","useRouterState","select","state","matches","map","match","meta","filter","Boolean","Solid","createMemo","resultMeta","metaByAttribute","title","routeMetasArray","i","length","metas","j","m","tag","children","json","JSON","stringify","push","attrs","type","escapeHtml","attribute","name","property","content","reverse","links","constructed","flat","link","manifest","assets","routes","routeId","asset","preloadLinks","looseRoutesById","forEach","route","id","preloads","preload","rel","href","styles","style","headScripts","script","uniqBy","d","arr","fn","seen","Set","item","key","has","add"],"mappings":"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"}
|
package/dist/esm/index.d.ts
CHANGED
|
@@ -45,7 +45,8 @@ export type { ValidateLinkOptions, ValidateUseSearchOptions, ValidateUseParamsOp
|
|
|
45
45
|
export type { ValidateFromPath, ValidateToPath, ValidateSearch, ValidateParams, InferFrom, InferTo, InferMaskTo, InferMaskFrom, ValidateNavigateOptions, ValidateNavigateOptionsArray, ValidateRedirectOptions, ValidateRedirectOptionsArray, ValidateId, InferStrict, InferShouldThrow, InferSelected, ValidateUseSearchResult, ValidateUseParamsResult, } from '@tanstack/router-core';
|
|
46
46
|
export { ScriptOnce } from './ScriptOnce.js';
|
|
47
47
|
export { Asset } from './Asset.js';
|
|
48
|
-
export { HeadContent
|
|
48
|
+
export { HeadContent } from './HeadContent.js';
|
|
49
|
+
export { useTags } from './headContentUtils.js';
|
|
49
50
|
export { Scripts } from './Scripts.js';
|
|
50
51
|
export { composeRewrites } from '@tanstack/router-core';
|
|
51
52
|
export type { LocationRewrite, LocationRewriteFunction, } from '@tanstack/router-core';
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { PathParamError, SearchParamError, TSR_DEFERRED_PROMISE, cleanPath, componentTypes, composeRewrites, createControlledPromise, createSerializationAdapter, deepEqual, defaultParseSearch, defaultSerializeError, defaultStringifySearch, defer, functionalUpdate, getInitialRouterState, interpolatePath, isMatch, isNotFound, isPlainArray, isPlainObject, isRedirect, joinPaths, lazyFn, notFound, parseSearchWith, redirect, replaceEqualDeep, resolvePath, retainSearchParams, rootRouteId, stringifySearchWith, stripSearchParams, trimPath, trimPathLeft, trimPathRight } from "@tanstack/router-core";
|
|
2
|
+
import { createBrowserHistory, createHashHistory, createHistory, createMemoryHistory } from "@tanstack/history";
|
|
3
|
+
import { Await, useAwaited } from "./awaited.js";
|
|
4
|
+
import { CatchBoundary, ErrorComponent } from "./CatchBoundary.js";
|
|
5
|
+
import { ClientOnly, useHydrated } from "./ClientOnly.js";
|
|
6
|
+
import { FileRoute, FileRouteLoader, LazyRoute, createFileRoute, createLazyFileRoute, createLazyRoute } from "./fileRoute.js";
|
|
7
|
+
import { lazyRouteComponent } from "./lazyRouteComponent.js";
|
|
8
|
+
import { Link, createLink, linkOptions, useLinkProps } from "./link.js";
|
|
9
|
+
import { MatchRoute, Matches, useChildMatches, useMatchRoute, useMatches, useParentMatches } from "./Matches.js";
|
|
10
|
+
import { matchContext } from "./matchContext.js";
|
|
11
|
+
import { Match, Outlet } from "./Match.js";
|
|
12
|
+
import { useMatch } from "./useMatch.js";
|
|
13
|
+
import { useLoaderDeps } from "./useLoaderDeps.js";
|
|
14
|
+
import { useLoaderData } from "./useLoaderData.js";
|
|
15
|
+
import { NotFoundRoute, RootRoute, Route, RouteApi, createRootRoute, createRootRouteWithContext, createRoute, createRouteMask, getRouteApi, rootRouteWithContext } from "./route.js";
|
|
16
|
+
import { Router, createRouter } from "./router.js";
|
|
17
|
+
import { RouterContextProvider, RouterProvider } from "./RouterProvider.js";
|
|
18
|
+
import { ScrollRestoration, useElementScrollRestoration } from "./ScrollRestoration.js";
|
|
19
|
+
import { Block, useBlocker } from "./useBlocker.js";
|
|
20
|
+
import { Navigate, useNavigate } from "./useNavigate.js";
|
|
21
|
+
import { useParams } from "./useParams.js";
|
|
22
|
+
import { useSearch } from "./useSearch.js";
|
|
23
|
+
import { getRouterContext } from "./routerContext.js";
|
|
24
|
+
import { useRouteContext } from "./useRouteContext.js";
|
|
25
|
+
import { useRouter } from "./useRouter.js";
|
|
26
|
+
import { useRouterState } from "./useRouterState.js";
|
|
27
|
+
import { useLocation } from "./useLocation.js";
|
|
28
|
+
import { useCanGoBack } from "./useCanGoBack.js";
|
|
29
|
+
import { useLayoutEffect } from "./utils.js";
|
|
30
|
+
import { CatchNotFound, DefaultGlobalNotFound } from "./not-found.js";
|
|
31
|
+
import { ScriptOnce } from "./ScriptOnce.js";
|
|
32
|
+
import { Asset } from "./Asset.js";
|
|
33
|
+
import { useTags } from "./headContentUtils.js";
|
|
34
|
+
import { Scripts } from "./Scripts.js";
|
|
35
|
+
import { HeadContent } from "./HeadContent.dev.js";
|
|
36
|
+
export {
|
|
37
|
+
Asset,
|
|
38
|
+
Await,
|
|
39
|
+
Block,
|
|
40
|
+
CatchBoundary,
|
|
41
|
+
CatchNotFound,
|
|
42
|
+
ClientOnly,
|
|
43
|
+
DefaultGlobalNotFound,
|
|
44
|
+
ErrorComponent,
|
|
45
|
+
FileRoute,
|
|
46
|
+
FileRouteLoader,
|
|
47
|
+
HeadContent,
|
|
48
|
+
LazyRoute,
|
|
49
|
+
Link,
|
|
50
|
+
Match,
|
|
51
|
+
MatchRoute,
|
|
52
|
+
Matches,
|
|
53
|
+
Navigate,
|
|
54
|
+
NotFoundRoute,
|
|
55
|
+
Outlet,
|
|
56
|
+
PathParamError,
|
|
57
|
+
RootRoute,
|
|
58
|
+
Route,
|
|
59
|
+
RouteApi,
|
|
60
|
+
Router,
|
|
61
|
+
RouterContextProvider,
|
|
62
|
+
RouterProvider,
|
|
63
|
+
ScriptOnce,
|
|
64
|
+
Scripts,
|
|
65
|
+
ScrollRestoration,
|
|
66
|
+
SearchParamError,
|
|
67
|
+
TSR_DEFERRED_PROMISE,
|
|
68
|
+
cleanPath,
|
|
69
|
+
componentTypes,
|
|
70
|
+
composeRewrites,
|
|
71
|
+
createBrowserHistory,
|
|
72
|
+
createControlledPromise,
|
|
73
|
+
createFileRoute,
|
|
74
|
+
createHashHistory,
|
|
75
|
+
createHistory,
|
|
76
|
+
createLazyFileRoute,
|
|
77
|
+
createLazyRoute,
|
|
78
|
+
createLink,
|
|
79
|
+
createMemoryHistory,
|
|
80
|
+
createRootRoute,
|
|
81
|
+
createRootRouteWithContext,
|
|
82
|
+
createRoute,
|
|
83
|
+
createRouteMask,
|
|
84
|
+
createRouter,
|
|
85
|
+
createSerializationAdapter,
|
|
86
|
+
deepEqual,
|
|
87
|
+
defaultParseSearch,
|
|
88
|
+
defaultSerializeError,
|
|
89
|
+
defaultStringifySearch,
|
|
90
|
+
defer,
|
|
91
|
+
functionalUpdate,
|
|
92
|
+
getInitialRouterState,
|
|
93
|
+
getRouteApi,
|
|
94
|
+
getRouterContext,
|
|
95
|
+
interpolatePath,
|
|
96
|
+
isMatch,
|
|
97
|
+
isNotFound,
|
|
98
|
+
isPlainArray,
|
|
99
|
+
isPlainObject,
|
|
100
|
+
isRedirect,
|
|
101
|
+
joinPaths,
|
|
102
|
+
lazyFn,
|
|
103
|
+
lazyRouteComponent,
|
|
104
|
+
linkOptions,
|
|
105
|
+
matchContext,
|
|
106
|
+
notFound,
|
|
107
|
+
parseSearchWith,
|
|
108
|
+
redirect,
|
|
109
|
+
replaceEqualDeep,
|
|
110
|
+
resolvePath,
|
|
111
|
+
retainSearchParams,
|
|
112
|
+
rootRouteId,
|
|
113
|
+
rootRouteWithContext,
|
|
114
|
+
stringifySearchWith,
|
|
115
|
+
stripSearchParams,
|
|
116
|
+
trimPath,
|
|
117
|
+
trimPathLeft,
|
|
118
|
+
trimPathRight,
|
|
119
|
+
useAwaited,
|
|
120
|
+
useBlocker,
|
|
121
|
+
useCanGoBack,
|
|
122
|
+
useChildMatches,
|
|
123
|
+
useElementScrollRestoration,
|
|
124
|
+
useHydrated,
|
|
125
|
+
useLayoutEffect,
|
|
126
|
+
useLinkProps,
|
|
127
|
+
useLoaderData,
|
|
128
|
+
useLoaderDeps,
|
|
129
|
+
useLocation,
|
|
130
|
+
useMatch,
|
|
131
|
+
useMatchRoute,
|
|
132
|
+
useMatches,
|
|
133
|
+
useNavigate,
|
|
134
|
+
useParams,
|
|
135
|
+
useParentMatches,
|
|
136
|
+
useRouteContext,
|
|
137
|
+
useRouter,
|
|
138
|
+
useRouterState,
|
|
139
|
+
useSearch,
|
|
140
|
+
useTags
|
|
141
|
+
};
|
|
142
|
+
//# sourceMappingURL=index.dev.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.dev.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
package/dist/esm/index.js
CHANGED
|
@@ -30,7 +30,8 @@ import { useLayoutEffect } from "./utils.js";
|
|
|
30
30
|
import { CatchNotFound, DefaultGlobalNotFound } from "./not-found.js";
|
|
31
31
|
import { ScriptOnce } from "./ScriptOnce.js";
|
|
32
32
|
import { Asset } from "./Asset.js";
|
|
33
|
-
import { HeadContent
|
|
33
|
+
import { HeadContent } from "./HeadContent.js";
|
|
34
|
+
import { useTags } from "./headContentUtils.js";
|
|
34
35
|
import { Scripts } from "./Scripts.js";
|
|
35
36
|
export {
|
|
36
37
|
Asset,
|
package/dist/esm/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createComponent, NoHydration, ssr, template, insert, Hydration, useAssets } from "solid-js/web";
|
|
2
2
|
import { MetaProvider } from "@solidjs/meta";
|
|
3
3
|
import { Asset } from "../Asset.js";
|
|
4
|
-
import { useTags } from "../
|
|
4
|
+
import { useTags } from "../headContentUtils.js";
|
|
5
5
|
import { RouterProvider } from "../RouterProvider.js";
|
|
6
6
|
import { Scripts } from "../Scripts.js";
|
|
7
7
|
var _tmpl$ = /* @__PURE__ */ template(`<html><head></head><body>`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RouterServer.js","sources":["../../../src/ssr/RouterServer.tsx"],"sourcesContent":["import {\n Hydration,\n HydrationScript,\n NoHydration,\n ssr,\n useAssets,\n} from 'solid-js/web'\nimport { MetaProvider } from '@solidjs/meta'\nimport { Asset } from '../Asset'\nimport { useTags } from '../
|
|
1
|
+
{"version":3,"file":"RouterServer.js","sources":["../../../src/ssr/RouterServer.tsx"],"sourcesContent":["import {\n Hydration,\n HydrationScript,\n NoHydration,\n ssr,\n useAssets,\n} from 'solid-js/web'\nimport { MetaProvider } from '@solidjs/meta'\nimport { Asset } from '../Asset'\nimport { useTags } from '../headContentUtils'\nimport { RouterProvider } from '../RouterProvider'\nimport { Scripts } from '../Scripts'\nimport type { AnyRouter } from '@tanstack/router-core'\n\nexport function ServerHeadContent() {\n const tags = useTags()\n useAssets(() => {\n return (\n <MetaProvider>\n {tags().map((tag) => (\n <Asset {...tag} />\n ))}\n </MetaProvider>\n )\n })\n return null\n}\n\nconst docType = ssr('<!DOCTYPE html>')\n\nexport function RouterServer<TRouter extends AnyRouter>(props: {\n router: TRouter\n}) {\n return (\n <NoHydration>\n {docType as any}\n <html>\n <head>\n <HydrationScript />\n </head>\n <body>\n <Hydration>\n <RouterProvider\n router={props.router}\n InnerWrap={(props) => (\n <NoHydration>\n <MetaProvider>\n <ServerHeadContent />\n <Hydration>{props.children}</Hydration>\n <Scripts />\n </MetaProvider>\n </NoHydration>\n )}\n />\n </Hydration>\n </body>\n </html>\n </NoHydration>\n )\n}\n"],"names":["ServerHeadContent","tags","useTags","useAssets","_$createComponent","MetaProvider","children","map","tag","Asset","docType","ssr","RouterServer","props","NoHydration","_el$","_tmpl$","_el$3","firstChild","_$insert","Hydration","RouterProvider","router","InnerWrap","Scripts"],"mappings":";;;;;;;AAcO,SAASA,oBAAoB;AAClC,QAAMC,OAAOC,QAAAA;AACbC,YAAU,MAAM;AACd,WAAAC,gBACGC,cAAY;AAAA,MAAA,IAAAC,WAAA;AAAA,eACVL,OAAOM,IAAKC,SAAGJ,gBACbK,OAAUD,GAAG,CACf;AAAA,MAAC;AAAA,IAAA,CAAA;AAAA,EAGR,CAAC;AACD,SAAO;AACT;AAEA,MAAME,UAAUC,IAAI,iBAAiB;AAE9B,SAASC,aAAwCC,OAErD;AACD,SAAAT,gBACGU,aAAW;AAAA,IAAA,IAAAR,WAAA;AAAA,aAAA,CACTI,UAAc,MAAA;AAAA,YAAAK,OAAAC,OAAAA,GAAAC,QAAAF,KAAAG;AAAAC,eAAAF,OAAAb,gBAMVgB,WAAS;AAAA,UAAA,IAAAd,WAAA;AAAA,mBAAAF,gBACPiB,gBAAc;AAAA,cAAA,IACbC,SAAM;AAAA,uBAAET,MAAMS;AAAAA,cAAM;AAAA,cACpBC,WAAYV,CAAAA,WAAKT,gBACdU,aAAW;AAAA,gBAAA,IAAAR,WAAA;AAAA,yBAAAF,gBACTC,cAAY;AAAA,oBAAA,IAAAC,WAAA;AAAA,6BAAA,CAAAF,gBACVJ,mBAAiB,CAAA,CAAA,GAAAI,gBACjBgB,WAAS;AAAA,wBAAA,IAAAd,WAAA;AAAA,iCAAEO,OAAMP;AAAAA,wBAAQ;AAAA,sBAAA,CAAA,GAAAF,gBACzBoB,SAAO,CAAA,CAAA,CAAA;AAAA,oBAAA;AAAA,kBAAA,CAAA;AAAA,gBAAA;AAAA,cAAA,CAAA;AAAA,YAAA,CAGb;AAAA,UAAA;AAAA,QAAA,CAAA,CAAA;AAAA,eAAAT;AAAAA,MAAA,IAAA;AAAA,IAAA;AAAA,EAAA,CAAA;AAOf;"}
|
|
@@ -1,10 +1,7 @@
|
|
|
1
|
-
import * as Solid from 'solid-js';
|
|
2
|
-
import type { RouterManagedTag } from '@tanstack/router-core';
|
|
3
|
-
export declare const useTags: () => () => RouterManagedTag[];
|
|
4
1
|
/**
|
|
5
2
|
* @description The `HeadContent` component is used to render meta tags, links, and scripts for the current route.
|
|
6
3
|
* When using full document hydration (hydrating from `<html>`), this component should be rendered in the `<body>`
|
|
7
4
|
* to ensure it's part of the reactive tree and updates correctly during client-side navigation.
|
|
8
5
|
* The component uses portals internally to render content into the `<head>` element.
|
|
9
6
|
*/
|
|
10
|
-
export declare function HeadContent():
|
|
7
|
+
export declare function HeadContent(): import("solid-js").JSX.Element;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @description The `HeadContent` component is used to render meta tags, links, and scripts for the current route.
|
|
3
|
+
* When using full document hydration (hydrating from `<html>`), this component should be rendered in the `<body>`
|
|
4
|
+
* to ensure it's part of the reactive tree and updates correctly during client-side navigation.
|
|
5
|
+
* The component uses portals internally to render content into the `<head>` element.
|
|
6
|
+
*
|
|
7
|
+
* Development version: filters out dev styles link after hydration and
|
|
8
|
+
* includes a fallback cleanup effect for hydration mismatch cases.
|
|
9
|
+
*/
|
|
10
|
+
export declare function HeadContent(): import("solid-js").JSX.Element;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { MetaProvider } from '@solidjs/meta';
|
|
2
|
+
import { For, createEffect, createMemo } from 'solid-js';
|
|
3
|
+
import { Asset } from './Asset';
|
|
4
|
+
import { useHydrated } from './ClientOnly';
|
|
5
|
+
import { useTags } from './headContentUtils';
|
|
6
|
+
const DEV_STYLES_ATTR = 'data-tanstack-router-dev-styles';
|
|
7
|
+
/**
|
|
8
|
+
* @description The `HeadContent` component is used to render meta tags, links, and scripts for the current route.
|
|
9
|
+
* When using full document hydration (hydrating from `<html>`), this component should be rendered in the `<body>`
|
|
10
|
+
* to ensure it's part of the reactive tree and updates correctly during client-side navigation.
|
|
11
|
+
* The component uses portals internally to render content into the `<head>` element.
|
|
12
|
+
*
|
|
13
|
+
* Development version: filters out dev styles link after hydration and
|
|
14
|
+
* includes a fallback cleanup effect for hydration mismatch cases.
|
|
15
|
+
*/
|
|
16
|
+
export function HeadContent() {
|
|
17
|
+
const tags = useTags();
|
|
18
|
+
const hydrated = useHydrated();
|
|
19
|
+
// Fallback cleanup for hydration mismatch cases
|
|
20
|
+
// Runs when hydration completes to remove any orphaned dev styles links from DOM
|
|
21
|
+
createEffect(() => {
|
|
22
|
+
if (hydrated()) {
|
|
23
|
+
document
|
|
24
|
+
.querySelectorAll(`link[${DEV_STYLES_ATTR}]`)
|
|
25
|
+
.forEach((el) => el.remove());
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
// Filter out dev styles after hydration
|
|
29
|
+
const filteredTags = createMemo(() => {
|
|
30
|
+
if (hydrated()) {
|
|
31
|
+
return tags().filter((tag) => !tag.attrs?.[DEV_STYLES_ATTR]);
|
|
32
|
+
}
|
|
33
|
+
return tags();
|
|
34
|
+
});
|
|
35
|
+
return (<MetaProvider>
|
|
36
|
+
<For each={filteredTags()}>{(tag) => <Asset {...tag}/>}</For>
|
|
37
|
+
</MetaProvider>);
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=HeadContent.dev.jsx.map
|
|
@@ -0,0 +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;AAE5C,MAAM,eAAe,GAAG,iCAAiC,CAAA;AAEzD;;;;;;;;GAQG;AACH,MAAM,UAAU,WAAW;IACzB,MAAM,IAAI,GAAG,OAAO,EAAE,CAAA;IACtB,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"}
|
|
@@ -1,193 +1,7 @@
|
|
|
1
|
-
import * as Solid from 'solid-js';
|
|
2
1
|
import { MetaProvider } from '@solidjs/meta';
|
|
3
|
-
import { For
|
|
4
|
-
import { buildDevStylesUrl, escapeHtml } from '@tanstack/router-core';
|
|
2
|
+
import { For } from 'solid-js';
|
|
5
3
|
import { Asset } from './Asset';
|
|
6
|
-
import {
|
|
7
|
-
import { useRouterState } from './useRouterState';
|
|
8
|
-
export const useTags = () => {
|
|
9
|
-
const router = useRouter();
|
|
10
|
-
const nonce = router.options.ssr?.nonce;
|
|
11
|
-
const routeMeta = useRouterState({
|
|
12
|
-
select: (state) => {
|
|
13
|
-
return state.matches.map((match) => match.meta).filter(Boolean);
|
|
14
|
-
},
|
|
15
|
-
});
|
|
16
|
-
const meta = Solid.createMemo(() => {
|
|
17
|
-
const resultMeta = [];
|
|
18
|
-
const metaByAttribute = {};
|
|
19
|
-
let title;
|
|
20
|
-
const routeMetasArray = routeMeta();
|
|
21
|
-
for (let i = routeMetasArray.length - 1; i >= 0; i--) {
|
|
22
|
-
const metas = routeMetasArray[i];
|
|
23
|
-
for (let j = metas.length - 1; j >= 0; j--) {
|
|
24
|
-
const m = metas[j];
|
|
25
|
-
if (!m)
|
|
26
|
-
continue;
|
|
27
|
-
if (m.title) {
|
|
28
|
-
if (!title) {
|
|
29
|
-
title = {
|
|
30
|
-
tag: 'title',
|
|
31
|
-
children: m.title,
|
|
32
|
-
};
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
else if ('script:ld+json' in m) {
|
|
36
|
-
// Handle JSON-LD structured data
|
|
37
|
-
// Content is HTML-escaped to prevent XSS when injected via innerHTML
|
|
38
|
-
try {
|
|
39
|
-
const json = JSON.stringify(m['script:ld+json']);
|
|
40
|
-
resultMeta.push({
|
|
41
|
-
tag: 'script',
|
|
42
|
-
attrs: {
|
|
43
|
-
type: 'application/ld+json',
|
|
44
|
-
},
|
|
45
|
-
children: escapeHtml(json),
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
catch {
|
|
49
|
-
// Skip invalid JSON-LD objects
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
else {
|
|
53
|
-
const attribute = m.name ?? m.property;
|
|
54
|
-
if (attribute) {
|
|
55
|
-
if (metaByAttribute[attribute]) {
|
|
56
|
-
continue;
|
|
57
|
-
}
|
|
58
|
-
else {
|
|
59
|
-
metaByAttribute[attribute] = true;
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
resultMeta.push({
|
|
63
|
-
tag: 'meta',
|
|
64
|
-
attrs: {
|
|
65
|
-
...m,
|
|
66
|
-
nonce,
|
|
67
|
-
},
|
|
68
|
-
});
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
if (title) {
|
|
73
|
-
resultMeta.push(title);
|
|
74
|
-
}
|
|
75
|
-
if (router.options.ssr?.nonce) {
|
|
76
|
-
resultMeta.push({
|
|
77
|
-
tag: 'meta',
|
|
78
|
-
attrs: {
|
|
79
|
-
property: 'csp-nonce',
|
|
80
|
-
content: router.options.ssr.nonce,
|
|
81
|
-
},
|
|
82
|
-
});
|
|
83
|
-
}
|
|
84
|
-
resultMeta.reverse();
|
|
85
|
-
return resultMeta;
|
|
86
|
-
});
|
|
87
|
-
const links = useRouterState({
|
|
88
|
-
select: (state) => {
|
|
89
|
-
const constructed = state.matches
|
|
90
|
-
.map((match) => match.links)
|
|
91
|
-
.filter(Boolean)
|
|
92
|
-
.flat(1)
|
|
93
|
-
.map((link) => ({
|
|
94
|
-
tag: 'link',
|
|
95
|
-
attrs: {
|
|
96
|
-
...link,
|
|
97
|
-
nonce,
|
|
98
|
-
},
|
|
99
|
-
}));
|
|
100
|
-
const manifest = router.ssr?.manifest;
|
|
101
|
-
// These are the assets extracted from the ViteManifest
|
|
102
|
-
// using the `startManifestPlugin`
|
|
103
|
-
const assets = state.matches
|
|
104
|
-
.map((match) => manifest?.routes[match.routeId]?.assets ?? [])
|
|
105
|
-
.filter(Boolean)
|
|
106
|
-
.flat(1)
|
|
107
|
-
.filter((asset) => asset.tag === 'link')
|
|
108
|
-
.map((asset) => ({
|
|
109
|
-
tag: 'link',
|
|
110
|
-
attrs: { ...asset.attrs, nonce },
|
|
111
|
-
}));
|
|
112
|
-
return [...constructed, ...assets];
|
|
113
|
-
},
|
|
114
|
-
});
|
|
115
|
-
const preloadLinks = useRouterState({
|
|
116
|
-
select: (state) => {
|
|
117
|
-
const preloadLinks = [];
|
|
118
|
-
state.matches
|
|
119
|
-
.map((match) => router.looseRoutesById[match.routeId])
|
|
120
|
-
.forEach((route) => router.ssr?.manifest?.routes[route.id]?.preloads
|
|
121
|
-
?.filter(Boolean)
|
|
122
|
-
.forEach((preload) => {
|
|
123
|
-
preloadLinks.push({
|
|
124
|
-
tag: 'link',
|
|
125
|
-
attrs: {
|
|
126
|
-
rel: 'modulepreload',
|
|
127
|
-
href: preload,
|
|
128
|
-
nonce,
|
|
129
|
-
},
|
|
130
|
-
});
|
|
131
|
-
}));
|
|
132
|
-
return preloadLinks;
|
|
133
|
-
},
|
|
134
|
-
});
|
|
135
|
-
const styles = useRouterState({
|
|
136
|
-
select: (state) => state.matches
|
|
137
|
-
.map((match) => match.styles)
|
|
138
|
-
.flat(1)
|
|
139
|
-
.filter(Boolean).map(({ children, ...style }) => ({
|
|
140
|
-
tag: 'style',
|
|
141
|
-
attrs: {
|
|
142
|
-
...style,
|
|
143
|
-
nonce,
|
|
144
|
-
},
|
|
145
|
-
children,
|
|
146
|
-
})),
|
|
147
|
-
});
|
|
148
|
-
const headScripts = useRouterState({
|
|
149
|
-
select: (state) => state.matches
|
|
150
|
-
.map((match) => match.headScripts)
|
|
151
|
-
.flat(1)
|
|
152
|
-
.filter(Boolean).map(({ children, ...script }) => ({
|
|
153
|
-
tag: 'script',
|
|
154
|
-
attrs: {
|
|
155
|
-
...script,
|
|
156
|
-
nonce,
|
|
157
|
-
},
|
|
158
|
-
children,
|
|
159
|
-
})),
|
|
160
|
-
});
|
|
161
|
-
return () => uniqBy([
|
|
162
|
-
...meta(),
|
|
163
|
-
...preloadLinks(),
|
|
164
|
-
...links(),
|
|
165
|
-
...styles(),
|
|
166
|
-
...headScripts(),
|
|
167
|
-
], (d) => {
|
|
168
|
-
return JSON.stringify(d);
|
|
169
|
-
});
|
|
170
|
-
};
|
|
171
|
-
/**
|
|
172
|
-
* Renders a stylesheet link for dev mode CSS collection.
|
|
173
|
-
* On the server, renders the full link with route-scoped CSS URL.
|
|
174
|
-
* On the client, renders the same link to avoid hydration mismatch,
|
|
175
|
-
* then removes it after hydration since Vite's HMR handles CSS updates.
|
|
176
|
-
*/
|
|
177
|
-
function DevStylesLink() {
|
|
178
|
-
const router = useRouter();
|
|
179
|
-
const routeIds = useRouterState({
|
|
180
|
-
select: (state) => state.matches.map((match) => match.routeId),
|
|
181
|
-
});
|
|
182
|
-
onMount(() => {
|
|
183
|
-
// After hydration, remove the SSR-rendered dev styles link
|
|
184
|
-
document
|
|
185
|
-
.querySelectorAll('[data-tanstack-start-dev-styles]')
|
|
186
|
-
.forEach((el) => el.remove());
|
|
187
|
-
});
|
|
188
|
-
const href = () => buildDevStylesUrl(router.basepath, routeIds());
|
|
189
|
-
return <link rel="stylesheet" href={href()} data-tanstack-start-dev-styles/>;
|
|
190
|
-
}
|
|
4
|
+
import { useTags } from './headContentUtils';
|
|
191
5
|
/**
|
|
192
6
|
* @description The `HeadContent` component is used to render meta tags, links, and scripts for the current route.
|
|
193
7
|
* When using full document hydration (hydrating from `<html>`), this component should be rendered in the `<body>`
|
|
@@ -197,21 +11,7 @@ function DevStylesLink() {
|
|
|
197
11
|
export function HeadContent() {
|
|
198
12
|
const tags = useTags();
|
|
199
13
|
return (<MetaProvider>
|
|
200
|
-
<Show when={process.env.NODE_ENV !== 'production'}>
|
|
201
|
-
<DevStylesLink />
|
|
202
|
-
</Show>
|
|
203
14
|
<For each={tags()}>{(tag) => <Asset {...tag}/>}</For>
|
|
204
15
|
</MetaProvider>);
|
|
205
16
|
}
|
|
206
|
-
function uniqBy(arr, fn) {
|
|
207
|
-
const seen = new Set();
|
|
208
|
-
return arr.filter((item) => {
|
|
209
|
-
const key = fn(item);
|
|
210
|
-
if (seen.has(key)) {
|
|
211
|
-
return false;
|
|
212
|
-
}
|
|
213
|
-
seen.add(key);
|
|
214
|
-
return true;
|
|
215
|
-
});
|
|
216
|
-
}
|
|
217
17
|
//# sourceMappingURL=HeadContent.jsx.map
|