@tanstack/react-router 1.147.2 → 1.147.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.
@@ -0,0 +1,168 @@
1
+ import * as React from "react";
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 = React.useMemo(() => {
14
+ const resultMeta = [];
15
+ const metaByAttribute = {};
16
+ let title;
17
+ for (let i = routeMeta.length - 1; i >= 0; i--) {
18
+ const metas = routeMeta[i];
19
+ for (let j = metas.length - 1; j >= 0; j--) {
20
+ const m = metas[j];
21
+ if (!m) continue;
22
+ if (m.title) {
23
+ if (!title) {
24
+ title = {
25
+ tag: "title",
26
+ children: m.title
27
+ };
28
+ }
29
+ } else if ("script:ld+json" in m) {
30
+ try {
31
+ const json = JSON.stringify(m["script:ld+json"]);
32
+ resultMeta.push({
33
+ tag: "script",
34
+ attrs: {
35
+ type: "application/ld+json"
36
+ },
37
+ children: escapeHtml(json)
38
+ });
39
+ } catch {
40
+ }
41
+ } else {
42
+ const attribute = m.name ?? m.property;
43
+ if (attribute) {
44
+ if (metaByAttribute[attribute]) {
45
+ continue;
46
+ } else {
47
+ metaByAttribute[attribute] = true;
48
+ }
49
+ }
50
+ resultMeta.push({
51
+ tag: "meta",
52
+ attrs: {
53
+ ...m,
54
+ nonce
55
+ }
56
+ });
57
+ }
58
+ }
59
+ }
60
+ if (title) {
61
+ resultMeta.push(title);
62
+ }
63
+ if (nonce) {
64
+ resultMeta.push({
65
+ tag: "meta",
66
+ attrs: {
67
+ property: "csp-nonce",
68
+ content: nonce
69
+ }
70
+ });
71
+ }
72
+ resultMeta.reverse();
73
+ return resultMeta;
74
+ }, [routeMeta, nonce]);
75
+ const links = useRouterState({
76
+ select: (state) => {
77
+ const constructed = state.matches.map((match) => match.links).filter(Boolean).flat(1).map((link) => ({
78
+ tag: "link",
79
+ attrs: {
80
+ ...link,
81
+ nonce
82
+ }
83
+ }));
84
+ const manifest = router.ssr?.manifest;
85
+ const assets = state.matches.map((match) => manifest?.routes[match.routeId]?.assets ?? []).filter(Boolean).flat(1).filter((asset) => asset.tag === "link").map(
86
+ (asset) => ({
87
+ tag: "link",
88
+ attrs: {
89
+ ...asset.attrs,
90
+ suppressHydrationWarning: true,
91
+ nonce
92
+ }
93
+ })
94
+ );
95
+ return [...constructed, ...assets];
96
+ },
97
+ structuralSharing: true
98
+ });
99
+ const preloadLinks = useRouterState({
100
+ select: (state) => {
101
+ const preloadLinks2 = [];
102
+ state.matches.map((match) => router.looseRoutesById[match.routeId]).forEach(
103
+ (route) => router.ssr?.manifest?.routes[route.id]?.preloads?.filter(Boolean).forEach((preload) => {
104
+ preloadLinks2.push({
105
+ tag: "link",
106
+ attrs: {
107
+ rel: "modulepreload",
108
+ href: preload,
109
+ nonce
110
+ }
111
+ });
112
+ })
113
+ );
114
+ return preloadLinks2;
115
+ },
116
+ structuralSharing: true
117
+ });
118
+ const styles = useRouterState({
119
+ select: (state) => state.matches.map((match) => match.styles).flat(1).filter(Boolean).map(({ children, ...attrs }) => ({
120
+ tag: "style",
121
+ attrs: {
122
+ ...attrs,
123
+ nonce
124
+ },
125
+ children
126
+ })),
127
+ structuralSharing: true
128
+ });
129
+ const headScripts = useRouterState({
130
+ select: (state) => state.matches.map((match) => match.headScripts).flat(1).filter(Boolean).map(({ children, ...script }) => ({
131
+ tag: "script",
132
+ attrs: {
133
+ ...script,
134
+ nonce
135
+ },
136
+ children
137
+ })),
138
+ structuralSharing: true
139
+ });
140
+ return uniqBy(
141
+ [
142
+ ...meta,
143
+ ...preloadLinks,
144
+ ...links,
145
+ ...styles,
146
+ ...headScripts
147
+ ],
148
+ (d) => {
149
+ return JSON.stringify(d);
150
+ }
151
+ );
152
+ };
153
+ function uniqBy(arr, fn) {
154
+ const seen = /* @__PURE__ */ new Set();
155
+ return arr.filter((item) => {
156
+ const key = fn(item);
157
+ if (seen.has(key)) {
158
+ return false;
159
+ }
160
+ seen.add(key);
161
+ return true;
162
+ });
163
+ }
164
+ export {
165
+ uniqBy,
166
+ useTags
167
+ };
168
+ //# sourceMappingURL=headContentUtils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"headContentUtils.js","sources":["../../src/headContentUtils.tsx"],"sourcesContent":["import * as React from 'react'\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: Array<RouterManagedTag> = React.useMemo(() => {\n const resultMeta: Array<RouterManagedTag> = []\n const metaByAttribute: Record<string, true> = {}\n let title: RouterManagedTag | undefined\n for (let i = routeMeta.length - 1; i >= 0; i--) {\n const metas = routeMeta[i]!\n for (let j = metas.length - 1; j >= 0; j--) {\n const m = metas[j]\n if (!m) continue\n\n if (m.title) {\n if (!title) {\n title = {\n tag: 'title',\n children: m.title,\n }\n }\n } else if ('script:ld+json' in m) {\n // Handle JSON-LD structured data\n // Content is HTML-escaped to prevent XSS when injected via dangerouslySetInnerHTML\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 (nonce) {\n resultMeta.push({\n tag: 'meta',\n attrs: {\n property: 'csp-nonce',\n content: nonce,\n },\n })\n }\n resultMeta.reverse()\n\n return resultMeta\n }, [routeMeta, nonce])\n\n const links = useRouterState({\n select: (state) => {\n const constructed = state.matches\n .map((match) => match.links!)\n .filter(Boolean)\n .flat(1)\n .map((link) => ({\n tag: 'link',\n attrs: {\n ...link,\n nonce,\n },\n })) satisfies Array<RouterManagedTag>\n\n const manifest = router.ssr?.manifest\n\n // These are the assets extracted from the ViteManifest\n // using the `startManifestPlugin`\n const assets = state.matches\n .map((match) => manifest?.routes[match.routeId]?.assets ?? [])\n .filter(Boolean)\n .flat(1)\n .filter((asset) => asset.tag === 'link')\n .map(\n (asset) =>\n ({\n tag: 'link',\n attrs: {\n ...asset.attrs,\n suppressHydrationWarning: true,\n nonce,\n },\n }) satisfies RouterManagedTag,\n )\n\n return [...constructed, ...assets]\n },\n structuralSharing: true as any,\n })\n\n const preloadLinks = useRouterState({\n select: (state) => {\n const preloadLinks: Array<RouterManagedTag> = []\n\n state.matches\n .map((match) => router.looseRoutesById[match.routeId]!)\n .forEach((route) =>\n router.ssr?.manifest?.routes[route.id]?.preloads\n ?.filter(Boolean)\n .forEach((preload) => {\n preloadLinks.push({\n tag: 'link',\n attrs: {\n rel: 'modulepreload',\n href: preload,\n nonce,\n },\n })\n }),\n )\n\n return preloadLinks\n },\n structuralSharing: true as any,\n })\n\n const styles = useRouterState({\n select: (state) =>\n (\n state.matches\n .map((match) => match.styles!)\n .flat(1)\n .filter(Boolean) as Array<RouterManagedTag>\n ).map(({ children, ...attrs }) => ({\n tag: 'style',\n attrs: {\n ...attrs,\n nonce,\n },\n children,\n })),\n structuralSharing: true as any,\n })\n\n const headScripts: Array<RouterManagedTag> = useRouterState({\n select: (state) =>\n (\n state.matches\n .map((match) => match.headScripts!)\n .flat(1)\n .filter(Boolean) as Array<RouterManagedTag>\n ).map(({ children, ...script }) => ({\n tag: 'script',\n attrs: {\n ...script,\n nonce,\n },\n children,\n })),\n structuralSharing: true as any,\n })\n\n return uniqBy(\n [\n ...meta,\n ...preloadLinks,\n ...links,\n ...styles,\n ...headScripts,\n ] as Array<RouterManagedTag>,\n (d) => {\n return JSON.stringify(d)\n },\n )\n}\n\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":["preloadLinks"],"mappings":"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"}
@@ -48,6 +48,7 @@ export type { ValidateFromPath, ValidateToPath, ValidateSearch, ValidateParams,
48
48
  export { ScriptOnce } from './ScriptOnce.js';
49
49
  export { Asset } from './Asset.js';
50
50
  export { HeadContent } from './HeadContent.js';
51
+ export { useTags } from './headContentUtils.js';
51
52
  export { Scripts } from './Scripts.js';
52
53
  export type * from './ssr/serializer.js';
53
54
  export { composeRewrites } from '@tanstack/router-core';
@@ -0,0 +1,2 @@
1
+ export * from './index.js';
2
+ export { HeadContent } from './HeadContent.dev';
@@ -0,0 +1,144 @@
1
+ import { PathParamError, SearchParamError, TSR_DEFERRED_PROMISE, cleanPath, componentTypes, composeRewrites, createControlledPromise, createRouterConfig, 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, useStableCallback } 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
+ createRouterConfig,
86
+ createSerializationAdapter,
87
+ deepEqual,
88
+ defaultParseSearch,
89
+ defaultSerializeError,
90
+ defaultStringifySearch,
91
+ defer,
92
+ functionalUpdate,
93
+ getInitialRouterState,
94
+ getRouteApi,
95
+ getRouterContext,
96
+ interpolatePath,
97
+ isMatch,
98
+ isNotFound,
99
+ isPlainArray,
100
+ isPlainObject,
101
+ isRedirect,
102
+ joinPaths,
103
+ lazyFn,
104
+ lazyRouteComponent,
105
+ linkOptions,
106
+ matchContext,
107
+ notFound,
108
+ parseSearchWith,
109
+ redirect,
110
+ replaceEqualDeep,
111
+ resolvePath,
112
+ retainSearchParams,
113
+ rootRouteId,
114
+ rootRouteWithContext,
115
+ stringifySearchWith,
116
+ stripSearchParams,
117
+ trimPath,
118
+ trimPathLeft,
119
+ trimPathRight,
120
+ useAwaited,
121
+ useBlocker,
122
+ useCanGoBack,
123
+ useChildMatches,
124
+ useElementScrollRestoration,
125
+ useHydrated,
126
+ useLayoutEffect,
127
+ useLinkProps,
128
+ useLoaderData,
129
+ useLoaderDeps,
130
+ useLocation,
131
+ useMatch,
132
+ useMatchRoute,
133
+ useMatches,
134
+ useNavigate,
135
+ useParams,
136
+ useParentMatches,
137
+ useRouteContext,
138
+ useRouter,
139
+ useRouterState,
140
+ useSearch,
141
+ useStableCallback,
142
+ useTags
143
+ };
144
+ //# 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
@@ -31,6 +31,7 @@ import { CatchNotFound, DefaultGlobalNotFound } from "./not-found.js";
31
31
  import { ScriptOnce } from "./ScriptOnce.js";
32
32
  import { Asset } from "./Asset.js";
33
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,
@@ -137,6 +138,7 @@ export {
137
138
  useRouter,
138
139
  useRouterState,
139
140
  useSearch,
140
- useStableCallback
141
+ useStableCallback,
142
+ useTags
141
143
  };
142
144
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/react-router",
3
- "version": "1.147.2",
3
+ "version": "1.147.3",
4
4
  "description": "Modern and scalable routing for React applications",
5
5
  "author": "Tanner Linsley",
6
6
  "license": "MIT",
@@ -31,10 +31,12 @@
31
31
  ".": {
32
32
  "import": {
33
33
  "types": "./dist/esm/index.d.ts",
34
+ "development": "./dist/esm/index.dev.js",
34
35
  "default": "./dist/esm/index.js"
35
36
  },
36
37
  "require": {
37
38
  "types": "./dist/cjs/index.d.cts",
39
+ "development": "./dist/cjs/index.dev.cjs",
38
40
  "default": "./dist/cjs/index.cjs"
39
41
  }
40
42
  },
@@ -79,8 +81,8 @@
79
81
  "isbot": "^5.1.22",
80
82
  "tiny-invariant": "^1.3.3",
81
83
  "tiny-warning": "^1.0.3",
82
- "@tanstack/router-core": "1.147.1",
83
- "@tanstack/history": "1.145.7"
84
+ "@tanstack/history": "1.145.7",
85
+ "@tanstack/router-core": "1.147.1"
84
86
  },
85
87
  "devDependencies": {
86
88
  "@testing-library/jest-dom": "^6.6.3",
@@ -0,0 +1,46 @@
1
+ import * as React from 'react'
2
+ import { Asset } from './Asset'
3
+ import { useRouter } from './useRouter'
4
+ import { useHydrated } from './ClientOnly'
5
+ import { useTags } from './headContentUtils'
6
+
7
+ const DEV_STYLES_ATTR = 'data-tanstack-router-dev-styles'
8
+
9
+ /**
10
+ * Render route-managed head tags (title, meta, links, styles, head scripts).
11
+ * Place inside the document head of your app shell.
12
+ *
13
+ * Development version: filters out dev styles link after hydration and
14
+ * includes a fallback cleanup effect for hydration mismatch cases.
15
+ *
16
+ * @link https://tanstack.com/router/latest/docs/framework/react/guide/document-head-management
17
+ */
18
+ export function HeadContent() {
19
+ const tags = useTags()
20
+ const router = useRouter()
21
+ const nonce = router.options.ssr?.nonce
22
+ const hydrated = useHydrated()
23
+
24
+ // Fallback cleanup for hydration mismatch cases
25
+ // Runs when hydration completes to remove any orphaned dev styles links from DOM
26
+ React.useEffect(() => {
27
+ if (hydrated) {
28
+ document
29
+ .querySelectorAll(`link[${DEV_STYLES_ATTR}]`)
30
+ .forEach((el) => el.remove())
31
+ }
32
+ }, [hydrated])
33
+
34
+ // Filter out dev styles after hydration
35
+ const filteredTags = hydrated
36
+ ? tags.filter((tag) => !tag.attrs?.[DEV_STYLES_ATTR])
37
+ : tags
38
+
39
+ return (
40
+ <>
41
+ {filteredTags.map((tag) => (
42
+ <Asset {...tag} key={`tsr-meta-${JSON.stringify(tag)}`} nonce={nonce} />
43
+ ))}
44
+ </>
45
+ )
46
+ }