@tanstack/router-core 1.131.3 → 1.131.5

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.
@@ -1,16 +1,14 @@
1
1
  import invariant from "tiny-invariant";
2
2
  import { batch } from "@tanstack/store";
3
3
  import { createControlledPromise } from "../utils.js";
4
- function hydrateMatch(deyhydratedMatch) {
5
- return {
6
- id: deyhydratedMatch.i,
7
- __beforeLoadContext: deyhydratedMatch.b,
8
- loaderData: deyhydratedMatch.l,
9
- status: deyhydratedMatch.s,
10
- ssr: deyhydratedMatch.ssr,
11
- updatedAt: deyhydratedMatch.u,
12
- error: deyhydratedMatch.e
13
- };
4
+ function hydrateMatch(match, deyhydratedMatch) {
5
+ match.id = deyhydratedMatch.i;
6
+ match.__beforeLoadContext = deyhydratedMatch.b;
7
+ match.loaderData = deyhydratedMatch.l;
8
+ match.status = deyhydratedMatch.s;
9
+ match.ssr = deyhydratedMatch.ssr;
10
+ match.updatedAt = deyhydratedMatch.u;
11
+ match.error = deyhydratedMatch.e;
14
12
  }
15
13
  async function hydrate(router) {
16
14
  var _a, _b, _c;
@@ -34,15 +32,17 @@ async function hydrate(router) {
34
32
  const pendingMinMs = route.options.pendingMinMs ?? router.options.defaultPendingMinMs;
35
33
  if (pendingMinMs) {
36
34
  const minPendingPromise = createControlledPromise();
37
- match.minPendingPromise = minPendingPromise;
35
+ match._nonReactive.minPendingPromise = minPendingPromise;
38
36
  match._forcePending = true;
39
37
  setTimeout(() => {
40
38
  minPendingPromise.resolve();
41
- router.updateMatch(match.id, (prev) => ({
42
- ...prev,
43
- minPendingPromise: void 0,
44
- _forcePending: void 0
45
- }));
39
+ router.updateMatch(match.id, (prev) => {
40
+ prev._nonReactive.minPendingPromise = void 0;
41
+ return {
42
+ ...prev,
43
+ _forcePending: void 0
44
+ };
45
+ });
46
46
  }, pendingMinMs);
47
47
  }
48
48
  }
@@ -52,15 +52,12 @@ async function hydrate(router) {
52
52
  (d) => d.i === match.id
53
53
  );
54
54
  if (!dehydratedMatch) {
55
- Object.assign(match, { dehydrated: false, ssr: false });
55
+ match._nonReactive.dehydrated = false;
56
+ match.ssr = false;
56
57
  return;
57
58
  }
58
- Object.assign(match, hydrateMatch(dehydratedMatch));
59
- if (match.ssr === false) {
60
- match._dehydrated = false;
61
- } else {
62
- match._dehydrated = true;
63
- }
59
+ hydrateMatch(match, dehydratedMatch);
60
+ match._nonReactive.dehydrated = match.ssr !== false;
64
61
  if (match.ssr === "data-only" || match.ssr === false) {
65
62
  if (firstNonSsrMatchIndex === void 0) {
66
63
  firstNonSsrMatchIndex = match.index;
@@ -77,23 +74,25 @@ async function hydrate(router) {
77
74
  await ((_c = (_b = router.options).hydrate) == null ? void 0 : _c.call(_b, dehydratedData));
78
75
  await Promise.all(
79
76
  router.state.matches.map(async (match) => {
80
- var _a2, _b2, _c2, _d, _e, _f;
77
+ var _a2, _b2, _c2, _d;
81
78
  const route = router.looseRoutesById[match.routeId];
82
79
  const parentMatch = router.state.matches[match.index - 1];
83
- const parentContext = (parentMatch == null ? void 0 : parentMatch.context) ?? router.options.context ?? {};
84
- const contextFnContext = {
85
- deps: match.loaderDeps,
86
- params: match.params,
87
- context: parentContext,
88
- location: router.state.location,
89
- navigate: (opts) => router.navigate({ ...opts, _fromLocation: router.state.location }),
90
- buildLocation: router.buildLocation,
91
- cause: match.cause,
92
- abortController: match.abortController,
93
- preload: false,
94
- matches
95
- };
96
- match.__routeContext = ((_b2 = (_a2 = route.options).context) == null ? void 0 : _b2.call(_a2, contextFnContext)) ?? {};
80
+ const parentContext = (parentMatch == null ? void 0 : parentMatch.context) ?? router.options.context;
81
+ if (route.options.context) {
82
+ const contextFnContext = {
83
+ deps: match.loaderDeps,
84
+ params: match.params,
85
+ context: parentContext ?? {},
86
+ location: router.state.location,
87
+ navigate: (opts) => router.navigate({ ...opts, _fromLocation: router.state.location }),
88
+ buildLocation: router.buildLocation,
89
+ cause: match.cause,
90
+ abortController: match.abortController,
91
+ preload: false,
92
+ matches
93
+ };
94
+ match.__routeContext = route.options.context(contextFnContext) ?? void 0;
95
+ }
97
96
  match.context = {
98
97
  ...parentContext,
99
98
  ...match.__routeContext,
@@ -105,8 +104,8 @@ async function hydrate(router) {
105
104
  params: match.params,
106
105
  loaderData: match.loaderData
107
106
  };
108
- const headFnContent = await ((_d = (_c2 = route.options).head) == null ? void 0 : _d.call(_c2, assetContext));
109
- const scripts = await ((_f = (_e = route.options).scripts) == null ? void 0 : _f.call(_e, assetContext));
107
+ const headFnContent = await ((_b2 = (_a2 = route.options).head) == null ? void 0 : _b2.call(_a2, assetContext));
108
+ const scripts = await ((_d = (_c2 = route.options).scripts) == null ? void 0 : _d.call(_c2, assetContext));
110
109
  match.meta = headFnContent == null ? void 0 : headFnContent.meta;
111
110
  match.links = headFnContent == null ? void 0 : headFnContent.links;
112
111
  match.headScripts = headFnContent == null ? void 0 : headFnContent.scripts;
@@ -118,7 +117,7 @@ async function hydrate(router) {
118
117
  const hasSsrFalseMatches = matches.some((m) => m.ssr === false);
119
118
  if (!hasSsrFalseMatches && !isSpaMode) {
120
119
  matches.forEach((match) => {
121
- match._dehydrated = void 0;
120
+ match._nonReactive.dehydrated = void 0;
122
121
  });
123
122
  return routeChunkPromise;
124
123
  }
@@ -133,7 +132,7 @@ async function hydrate(router) {
133
132
  );
134
133
  setMatchForcePending(match);
135
134
  match._displayPending = true;
136
- match.displayPendingPromise = loadPromise;
135
+ match._nonReactive.displayPendingPromise = loadPromise;
137
136
  loadPromise.then(() => {
138
137
  batch(() => {
139
138
  if (router.__store.state.status === "pending") {
@@ -1 +1 @@
1
- {"version":3,"file":"ssr-client.js","sources":["../../../src/ssr/ssr-client.ts"],"sourcesContent":["import invariant from 'tiny-invariant'\nimport { batch } from '@tanstack/store'\nimport { createControlledPromise } from '../utils'\nimport type { AnyRouteMatch, MakeRouteMatch } from '../Matches'\nimport type { AnyRouter } from '../router'\nimport type { Manifest } from '../manifest'\nimport type { RouteContextOptions } from '../route'\nimport type { GLOBAL_TSR } from './ssr-server'\n\ndeclare global {\n interface Window {\n [GLOBAL_TSR]?: TsrSsrGlobal\n }\n}\n\nexport interface TsrSsrGlobal {\n router?: DehydratedRouter\n // clean scripts, shortened since this is sent for each streamed script\n c: () => void\n}\n\nfunction hydrateMatch(\n deyhydratedMatch: DehydratedMatch,\n): Partial<MakeRouteMatch> {\n return {\n id: deyhydratedMatch.i,\n __beforeLoadContext: deyhydratedMatch.b,\n loaderData: deyhydratedMatch.l,\n status: deyhydratedMatch.s,\n ssr: deyhydratedMatch.ssr,\n updatedAt: deyhydratedMatch.u,\n error: deyhydratedMatch.e,\n }\n}\nexport interface DehydratedMatch {\n i: MakeRouteMatch['id']\n b?: MakeRouteMatch['__beforeLoadContext']\n l?: MakeRouteMatch['loaderData']\n e?: MakeRouteMatch['error']\n u: MakeRouteMatch['updatedAt']\n s: MakeRouteMatch['status']\n ssr?: MakeRouteMatch['ssr']\n}\n\nexport interface DehydratedRouter {\n manifest: Manifest | undefined\n dehydratedData?: any\n lastMatchId?: string\n matches: Array<DehydratedMatch>\n}\n\nexport async function hydrate(router: AnyRouter): Promise<any> {\n invariant(\n window.$_TSR?.router,\n 'Expected to find a dehydrated data on window.$_TSR.router, but we did not. Please file an issue!',\n )\n\n const { manifest, dehydratedData, lastMatchId } = window.$_TSR.router\n\n router.ssr = {\n manifest,\n }\n\n // Hydrate the router state\n const matches = router.matchRoutes(router.state.location)\n\n // kick off loading the route chunks\n const routeChunkPromise = Promise.all(\n matches.map((match) => {\n const route = router.looseRoutesById[match.routeId]!\n return router.loadRouteChunk(route)\n }),\n )\n\n function setMatchForcePending(match: AnyRouteMatch) {\n // usually the minPendingPromise is created in the Match component if a pending match is rendered\n // however, this might be too late if the match synchronously resolves\n const route = router.looseRoutesById[match.routeId]!\n const pendingMinMs =\n route.options.pendingMinMs ?? router.options.defaultPendingMinMs\n if (pendingMinMs) {\n const minPendingPromise = createControlledPromise<void>()\n match.minPendingPromise = minPendingPromise\n match._forcePending = true\n\n setTimeout(() => {\n minPendingPromise.resolve()\n // We've handled the minPendingPromise, so we can delete it\n router.updateMatch(match.id, (prev) => ({\n ...prev,\n minPendingPromise: undefined,\n _forcePending: undefined,\n }))\n }, pendingMinMs)\n }\n }\n\n // Right after hydration and before the first render, we need to rehydrate each match\n // First step is to reyhdrate loaderData and __beforeLoadContext\n let firstNonSsrMatchIndex: number | undefined = undefined\n matches.forEach((match) => {\n const dehydratedMatch = window.$_TSR!.router!.matches.find(\n (d) => d.i === match.id,\n )\n if (!dehydratedMatch) {\n Object.assign(match, { dehydrated: false, ssr: false })\n return\n }\n\n Object.assign(match, hydrateMatch(dehydratedMatch))\n\n if (match.ssr === false) {\n match._dehydrated = false\n } else {\n match._dehydrated = true\n }\n\n if (match.ssr === 'data-only' || match.ssr === false) {\n if (firstNonSsrMatchIndex === undefined) {\n firstNonSsrMatchIndex = match.index\n setMatchForcePending(match)\n }\n }\n })\n\n router.__store.setState((s) => {\n return {\n ...s,\n matches,\n }\n })\n\n // Allow the user to handle custom hydration data\n await router.options.hydrate?.(dehydratedData)\n\n // now that all necessary data is hydrated:\n // 1) fully reconstruct the route context\n // 2) execute `head()` and `scripts()` for each match\n await Promise.all(\n router.state.matches.map(async (match) => {\n const route = router.looseRoutesById[match.routeId]!\n\n const parentMatch = router.state.matches[match.index - 1]\n const parentContext = parentMatch?.context ?? router.options.context ?? {}\n\n // `context()` was already executed by `matchRoutes`, however route context was not yet fully reconstructed\n // so run it again and merge route context\n const contextFnContext: RouteContextOptions<any, any, any, any> = {\n deps: match.loaderDeps,\n params: match.params,\n context: parentContext,\n location: router.state.location,\n navigate: (opts: any) =>\n router.navigate({ ...opts, _fromLocation: router.state.location }),\n buildLocation: router.buildLocation,\n cause: match.cause,\n abortController: match.abortController,\n preload: false,\n matches,\n }\n match.__routeContext = route.options.context?.(contextFnContext) ?? {}\n\n match.context = {\n ...parentContext,\n ...match.__routeContext,\n ...match.__beforeLoadContext,\n }\n\n const assetContext = {\n matches: router.state.matches,\n match,\n params: match.params,\n loaderData: match.loaderData,\n }\n const headFnContent = await route.options.head?.(assetContext)\n\n const scripts = await route.options.scripts?.(assetContext)\n\n match.meta = headFnContent?.meta\n match.links = headFnContent?.links\n match.headScripts = headFnContent?.scripts\n match.styles = headFnContent?.styles\n match.scripts = scripts\n }),\n )\n\n const isSpaMode = matches[matches.length - 1]!.id !== lastMatchId\n const hasSsrFalseMatches = matches.some((m) => m.ssr === false)\n // all matches have data from the server and we are not in SPA mode so we don't need to kick of router.load()\n if (!hasSsrFalseMatches && !isSpaMode) {\n matches.forEach((match) => {\n // remove the _dehydrate flag since we won't run router.load() which would remove it\n match._dehydrated = undefined\n })\n return routeChunkPromise\n }\n\n // schedule router.load() to run after the next tick so we can store the promise in the match before loading starts\n const loadPromise = Promise.resolve()\n .then(() => router.load())\n .catch((err) => {\n console.error('Error during router hydration:', err)\n })\n\n // in SPA mode we need to keep the first match below the root route pending until router.load() is finished\n // this will prevent that other pending components are rendered but hydration is not blocked\n if (isSpaMode) {\n const match = matches[1]\n invariant(\n match,\n 'Expected to find a match below the root match in SPA mode.',\n )\n setMatchForcePending(match)\n\n match._displayPending = true\n match.displayPendingPromise = loadPromise\n\n loadPromise.then(() => {\n batch(() => {\n // ensure router is not in status 'pending' anymore\n // this usually happens in Transitioner but if loading synchronously resolves,\n // Transitioner won't be rendered while loading so it cannot track the change from loading:true to loading:false\n if (router.__store.state.status === 'pending') {\n router.__store.setState((s) => ({\n ...s,\n status: 'idle',\n resolvedLocation: s.location,\n }))\n }\n // hide the pending component once the load is finished\n router.updateMatch(match.id, (prev) => {\n return {\n ...prev,\n _displayPending: undefined,\n displayPendingPromise: undefined,\n }\n })\n })\n })\n }\n return routeChunkPromise\n}\n"],"names":["_b","_a","_c"],"mappings":"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"}
1
+ {"version":3,"file":"ssr-client.js","sources":["../../../src/ssr/ssr-client.ts"],"sourcesContent":["import invariant from 'tiny-invariant'\nimport { batch } from '@tanstack/store'\nimport { createControlledPromise } from '../utils'\nimport type { AnyRouteMatch, MakeRouteMatch } from '../Matches'\nimport type { AnyRouter } from '../router'\nimport type { Manifest } from '../manifest'\nimport type { RouteContextOptions } from '../route'\nimport type { GLOBAL_TSR } from './ssr-server'\n\ndeclare global {\n interface Window {\n [GLOBAL_TSR]?: TsrSsrGlobal\n }\n}\n\nexport interface TsrSsrGlobal {\n router?: DehydratedRouter\n // clean scripts, shortened since this is sent for each streamed script\n c: () => void\n}\n\nfunction hydrateMatch(\n match: AnyRouteMatch,\n deyhydratedMatch: DehydratedMatch,\n): void {\n match.id = deyhydratedMatch.i\n match.__beforeLoadContext = deyhydratedMatch.b\n match.loaderData = deyhydratedMatch.l\n match.status = deyhydratedMatch.s\n match.ssr = deyhydratedMatch.ssr\n match.updatedAt = deyhydratedMatch.u\n match.error = deyhydratedMatch.e\n}\nexport interface DehydratedMatch {\n i: MakeRouteMatch['id']\n b?: MakeRouteMatch['__beforeLoadContext']\n l?: MakeRouteMatch['loaderData']\n e?: MakeRouteMatch['error']\n u: MakeRouteMatch['updatedAt']\n s: MakeRouteMatch['status']\n ssr?: MakeRouteMatch['ssr']\n}\n\nexport interface DehydratedRouter {\n manifest: Manifest | undefined\n dehydratedData?: any\n lastMatchId?: string\n matches: Array<DehydratedMatch>\n}\n\nexport async function hydrate(router: AnyRouter): Promise<any> {\n invariant(\n window.$_TSR?.router,\n 'Expected to find a dehydrated data on window.$_TSR.router, but we did not. Please file an issue!',\n )\n\n const { manifest, dehydratedData, lastMatchId } = window.$_TSR.router\n\n router.ssr = {\n manifest,\n }\n\n // Hydrate the router state\n const matches = router.matchRoutes(router.state.location)\n\n // kick off loading the route chunks\n const routeChunkPromise = Promise.all(\n matches.map((match) => {\n const route = router.looseRoutesById[match.routeId]!\n return router.loadRouteChunk(route)\n }),\n )\n\n function setMatchForcePending(match: AnyRouteMatch) {\n // usually the minPendingPromise is created in the Match component if a pending match is rendered\n // however, this might be too late if the match synchronously resolves\n const route = router.looseRoutesById[match.routeId]!\n const pendingMinMs =\n route.options.pendingMinMs ?? router.options.defaultPendingMinMs\n if (pendingMinMs) {\n const minPendingPromise = createControlledPromise<void>()\n match._nonReactive.minPendingPromise = minPendingPromise\n match._forcePending = true\n\n setTimeout(() => {\n minPendingPromise.resolve()\n // We've handled the minPendingPromise, so we can delete it\n router.updateMatch(match.id, (prev) => {\n prev._nonReactive.minPendingPromise = undefined\n return {\n ...prev,\n _forcePending: undefined,\n }\n })\n }, pendingMinMs)\n }\n }\n\n // Right after hydration and before the first render, we need to rehydrate each match\n // First step is to reyhdrate loaderData and __beforeLoadContext\n let firstNonSsrMatchIndex: number | undefined = undefined\n matches.forEach((match) => {\n const dehydratedMatch = window.$_TSR!.router!.matches.find(\n (d) => d.i === match.id,\n )\n if (!dehydratedMatch) {\n match._nonReactive.dehydrated = false\n match.ssr = false\n return\n }\n\n hydrateMatch(match, dehydratedMatch)\n\n match._nonReactive.dehydrated = match.ssr !== false\n\n if (match.ssr === 'data-only' || match.ssr === false) {\n if (firstNonSsrMatchIndex === undefined) {\n firstNonSsrMatchIndex = match.index\n setMatchForcePending(match)\n }\n }\n })\n\n router.__store.setState((s) => {\n return {\n ...s,\n matches,\n }\n })\n\n // Allow the user to handle custom hydration data\n await router.options.hydrate?.(dehydratedData)\n\n // now that all necessary data is hydrated:\n // 1) fully reconstruct the route context\n // 2) execute `head()` and `scripts()` for each match\n await Promise.all(\n router.state.matches.map(async (match) => {\n const route = router.looseRoutesById[match.routeId]!\n\n const parentMatch = router.state.matches[match.index - 1]\n const parentContext = parentMatch?.context ?? router.options.context\n\n // `context()` was already executed by `matchRoutes`, however route context was not yet fully reconstructed\n // so run it again and merge route context\n if (route.options.context) {\n const contextFnContext: RouteContextOptions<any, any, any, any> = {\n deps: match.loaderDeps,\n params: match.params,\n context: parentContext ?? {},\n location: router.state.location,\n navigate: (opts: any) =>\n router.navigate({ ...opts, _fromLocation: router.state.location }),\n buildLocation: router.buildLocation,\n cause: match.cause,\n abortController: match.abortController,\n preload: false,\n matches,\n }\n match.__routeContext =\n route.options.context(contextFnContext) ?? undefined\n }\n\n match.context = {\n ...parentContext,\n ...match.__routeContext,\n ...match.__beforeLoadContext,\n }\n\n const assetContext = {\n matches: router.state.matches,\n match,\n params: match.params,\n loaderData: match.loaderData,\n }\n const headFnContent = await route.options.head?.(assetContext)\n\n const scripts = await route.options.scripts?.(assetContext)\n\n match.meta = headFnContent?.meta\n match.links = headFnContent?.links\n match.headScripts = headFnContent?.scripts\n match.styles = headFnContent?.styles\n match.scripts = scripts\n }),\n )\n\n const isSpaMode = matches[matches.length - 1]!.id !== lastMatchId\n const hasSsrFalseMatches = matches.some((m) => m.ssr === false)\n // all matches have data from the server and we are not in SPA mode so we don't need to kick of router.load()\n if (!hasSsrFalseMatches && !isSpaMode) {\n matches.forEach((match) => {\n // remove the dehydrated flag since we won't run router.load() which would remove it\n match._nonReactive.dehydrated = undefined\n })\n return routeChunkPromise\n }\n\n // schedule router.load() to run after the next tick so we can store the promise in the match before loading starts\n const loadPromise = Promise.resolve()\n .then(() => router.load())\n .catch((err) => {\n console.error('Error during router hydration:', err)\n })\n\n // in SPA mode we need to keep the first match below the root route pending until router.load() is finished\n // this will prevent that other pending components are rendered but hydration is not blocked\n if (isSpaMode) {\n const match = matches[1]\n invariant(\n match,\n 'Expected to find a match below the root match in SPA mode.',\n )\n setMatchForcePending(match)\n\n match._displayPending = true\n match._nonReactive.displayPendingPromise = loadPromise\n\n loadPromise.then(() => {\n batch(() => {\n // ensure router is not in status 'pending' anymore\n // this usually happens in Transitioner but if loading synchronously resolves,\n // Transitioner won't be rendered while loading so it cannot track the change from loading:true to loading:false\n if (router.__store.state.status === 'pending') {\n router.__store.setState((s) => ({\n ...s,\n status: 'idle',\n resolvedLocation: s.location,\n }))\n }\n // hide the pending component once the load is finished\n router.updateMatch(match.id, (prev) => {\n return {\n ...prev,\n _displayPending: undefined,\n displayPendingPromise: undefined,\n }\n })\n })\n })\n }\n return routeChunkPromise\n}\n"],"names":["_b","_a","_c"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/router-core",
3
- "version": "1.131.3",
3
+ "version": "1.131.5",
4
4
  "description": "Modern and scalable routing for React applications",
5
5
  "author": "Tanner Linsley",
6
6
  "license": "MIT",
package/src/Matches.ts CHANGED
@@ -136,14 +136,21 @@ export interface RouteMatch<
136
136
  paramsError: unknown
137
137
  searchError: unknown
138
138
  updatedAt: number
139
- loadPromise?: ControlledPromise<void>
140
- /** @internal */
141
- beforeLoadPromise?: ControlledPromise<void>
142
- /** @internal */
143
- loaderPromise?: ControlledPromise<void>
139
+ _nonReactive: {
140
+ /** @internal */
141
+ beforeLoadPromise?: ControlledPromise<void>
142
+ /** @internal */
143
+ loaderPromise?: ControlledPromise<void>
144
+ /** @internal */
145
+ pendingTimeout?: ReturnType<typeof setTimeout>
146
+ loadPromise?: ControlledPromise<void>
147
+ displayPendingPromise?: Promise<void>
148
+ minPendingPromise?: ControlledPromise<void>
149
+ dehydrated?: boolean
150
+ }
144
151
  loaderData?: TLoaderData
145
152
  /** @internal */
146
- __routeContext: Record<string, unknown>
153
+ __routeContext?: Record<string, unknown>
147
154
  /** @internal */
148
155
  __beforeLoadContext?: Record<string, unknown>
149
156
  context: TAllContext
@@ -158,12 +165,9 @@ export interface RouteMatch<
158
165
  headers?: Record<string, string>
159
166
  globalNotFound?: boolean
160
167
  staticData: StaticDataRouteOption
161
- minPendingPromise?: ControlledPromise<void>
162
- pendingTimeout?: ReturnType<typeof setTimeout>
168
+ /** This attribute is not reactive */
163
169
  ssr?: boolean | 'data-only'
164
- _dehydrated?: boolean
165
170
  _forcePending?: boolean
166
- displayPendingPromise?: Promise<void>
167
171
  _displayPending?: boolean
168
172
  }
169
173
 
package/src/router.ts CHANGED
@@ -1146,8 +1146,8 @@ export class RouterCore<
1146
1146
  const parentMatchId = parentMatch?.id
1147
1147
 
1148
1148
  const parentContext = !parentMatchId
1149
- ? ((this.options.context as any) ?? {})
1150
- : (parentMatch.context ?? this.options.context ?? {})
1149
+ ? ((this.options.context as any) ?? undefined)
1150
+ : (parentMatch.context ?? this.options.context ?? undefined)
1151
1151
 
1152
1152
  return parentContext
1153
1153
  }
@@ -1169,12 +1169,12 @@ export class RouterCore<
1169
1169
  ] = (() => {
1170
1170
  // Validate the search params and stabilize them
1171
1171
  const parentSearch = parentMatch?.search ?? next.search
1172
- const parentStrictSearch = parentMatch?._strictSearch ?? {}
1172
+ const parentStrictSearch = parentMatch?._strictSearch ?? undefined
1173
1173
 
1174
1174
  try {
1175
1175
  const strictSearch =
1176
1176
  validateSearch(route.options.validateSearch, { ...parentSearch }) ??
1177
- {}
1177
+ undefined
1178
1178
 
1179
1179
  return [
1180
1180
  {
@@ -1284,7 +1284,10 @@ export class RouterCore<
1284
1284
  isFetching: false,
1285
1285
  error: undefined,
1286
1286
  paramsError: parseErrors[index],
1287
- __routeContext: {},
1287
+ __routeContext: undefined,
1288
+ _nonReactive: {
1289
+ loadPromise: createControlledPromise(),
1290
+ },
1288
1291
  __beforeLoadContext: undefined,
1289
1292
  context: {},
1290
1293
  abortController: new AbortController(),
@@ -1300,7 +1303,6 @@ export class RouterCore<
1300
1303
  headScripts: undefined,
1301
1304
  meta: undefined,
1302
1305
  staticData: route.options.staticData || {},
1303
- loadPromise: createControlledPromise(),
1304
1306
  fullPath: route.fullPath,
1305
1307
  }
1306
1308
  }
@@ -1335,22 +1337,25 @@ export class RouterCore<
1335
1337
  const parentContext = getParentContext(parentMatch)
1336
1338
 
1337
1339
  // Update the match's context
1338
- const contextFnContext: RouteContextOptions<any, any, any, any> = {
1339
- deps: match.loaderDeps,
1340
- params: match.params,
1341
- context: parentContext,
1342
- location: next,
1343
- navigate: (opts: any) =>
1344
- this.navigate({ ...opts, _fromLocation: next }),
1345
- buildLocation: this.buildLocation,
1346
- cause: match.cause,
1347
- abortController: match.abortController,
1348
- preload: !!match.preload,
1349
- matches,
1350
- }
1351
1340
 
1352
- // Get the route context
1353
- match.__routeContext = route.options.context?.(contextFnContext) ?? {}
1341
+ if (route.options.context) {
1342
+ const contextFnContext: RouteContextOptions<any, any, any, any> = {
1343
+ deps: match.loaderDeps,
1344
+ params: match.params,
1345
+ context: parentContext ?? {},
1346
+ location: next,
1347
+ navigate: (opts: any) =>
1348
+ this.navigate({ ...opts, _fromLocation: next }),
1349
+ buildLocation: this.buildLocation,
1350
+ cause: match.cause,
1351
+ abortController: match.abortController,
1352
+ preload: !!match.preload,
1353
+ matches,
1354
+ }
1355
+ // Get the route context
1356
+ match.__routeContext =
1357
+ route.options.context(contextFnContext) ?? undefined
1358
+ }
1354
1359
 
1355
1360
  match.context = {
1356
1361
  ...parentContext,
@@ -1388,13 +1393,8 @@ export class RouterCore<
1388
1393
  if (!match) return
1389
1394
 
1390
1395
  match.abortController.abort()
1391
- this.updateMatch(id, (prev) => {
1392
- clearTimeout(prev.pendingTimeout)
1393
- return {
1394
- ...prev,
1395
- pendingTimeout: undefined,
1396
- }
1397
- })
1396
+ match._nonReactive.pendingTimeout = undefined
1397
+ clearTimeout(match._nonReactive.pendingTimeout)
1398
1398
  }
1399
1399
 
1400
1400
  cancelMatches = () => {
@@ -1489,13 +1489,9 @@ export class RouterCore<
1489
1489
  parseCache: this.parsePathnameCache,
1490
1490
  }).interpolatedPath
1491
1491
 
1492
- const destRoutes = this.matchRoutes(
1493
- interpolatedNextTo,
1494
- {},
1495
- {
1496
- _buildLocation: true,
1497
- },
1498
- ).map((d) => this.looseRoutesById[d.routeId]!)
1492
+ const destRoutes = this.matchRoutes(interpolatedNextTo, undefined, {
1493
+ _buildLocation: true,
1494
+ }).map((d) => this.looseRoutesById[d.routeId]!)
1499
1495
 
1500
1496
  // If there are any params, we need to stringify them
1501
1497
  if (Object.keys(nextParams).length > 0) {
@@ -2133,8 +2129,10 @@ export class RouterCore<
2133
2129
  }
2134
2130
  }
2135
2131
 
2136
- match.beforeLoadPromise?.resolve()
2137
- match.loaderPromise?.resolve()
2132
+ match._nonReactive.beforeLoadPromise?.resolve()
2133
+ match._nonReactive.loaderPromise?.resolve()
2134
+ match._nonReactive.beforeLoadPromise = undefined
2135
+ match._nonReactive.loaderPromise = undefined
2138
2136
 
2139
2137
  updateMatch(match.id, (prev) => ({
2140
2138
  ...prev,
@@ -2145,15 +2143,13 @@ export class RouterCore<
2145
2143
  : 'error',
2146
2144
  isFetching: false,
2147
2145
  error: err,
2148
- beforeLoadPromise: undefined,
2149
- loaderPromise: undefined,
2150
2146
  }))
2151
2147
 
2152
2148
  if (!(err as any).routeId) {
2153
2149
  ;(err as any).routeId = match.routeId
2154
2150
  }
2155
2151
 
2156
- match.loadPromise?.resolve()
2152
+ match._nonReactive.loadPromise?.resolve()
2157
2153
 
2158
2154
  if (isRedirect(err)) {
2159
2155
  rendered = true
@@ -2173,7 +2169,7 @@ export class RouterCore<
2173
2169
  const shouldSkipLoader = (matchId: string) => {
2174
2170
  const match = this.getMatch(matchId)!
2175
2171
  // upon hydration, we skip the loader if the match has been dehydrated on the server
2176
- if (!this.isServer && match._dehydrated) {
2172
+ if (!this.isServer && match._nonReactive.dehydrated) {
2177
2173
  return true
2178
2174
  }
2179
2175
 
@@ -2216,8 +2212,9 @@ export class RouterCore<
2216
2212
  }
2217
2213
 
2218
2214
  updateMatch(matchId, (prev) => {
2219
- prev.beforeLoadPromise?.resolve()
2220
- prev.loadPromise?.resolve()
2215
+ prev._nonReactive.beforeLoadPromise?.resolve()
2216
+ prev._nonReactive.beforeLoadPromise = undefined
2217
+ prev._nonReactive.loadPromise?.resolve()
2221
2218
 
2222
2219
  return {
2223
2220
  ...prev,
@@ -2226,7 +2223,6 @@ export class RouterCore<
2226
2223
  isFetching: false,
2227
2224
  updatedAt: Date.now(),
2228
2225
  abortController: new AbortController(),
2229
- beforeLoadPromise: undefined,
2230
2226
  }
2231
2227
  })
2232
2228
  }
@@ -2296,10 +2292,7 @@ export class RouterCore<
2296
2292
  }
2297
2293
  }
2298
2294
  }
2299
- updateMatch(matchId, (prev) => ({
2300
- ...prev,
2301
- ssr,
2302
- }))
2295
+ existingMatch.ssr = ssr
2303
2296
  }
2304
2297
 
2305
2298
  if (shouldSkipLoader(matchId)) {
@@ -2321,9 +2314,10 @@ export class RouterCore<
2321
2314
 
2322
2315
  let executeBeforeLoad = true
2323
2316
  const setupPendingTimeout = () => {
2317
+ const match = this.getMatch(matchId)!
2324
2318
  if (
2325
2319
  shouldPending &&
2326
- this.getMatch(matchId)!.pendingTimeout === undefined
2320
+ match._nonReactive.pendingTimeout === undefined
2327
2321
  ) {
2328
2322
  const pendingTimeout = setTimeout(() => {
2329
2323
  try {
@@ -2332,22 +2326,19 @@ export class RouterCore<
2332
2326
  triggerOnReady()
2333
2327
  } catch {}
2334
2328
  }, pendingMs)
2335
- updateMatch(matchId, (prev) => ({
2336
- ...prev,
2337
- pendingTimeout,
2338
- }))
2329
+ match._nonReactive.pendingTimeout = pendingTimeout
2339
2330
  }
2340
2331
  }
2341
2332
  if (
2342
2333
  // If we are in the middle of a load, either of these will be present
2343
2334
  // (not to be confused with `loadPromise`, which is always defined)
2344
- existingMatch.beforeLoadPromise ||
2345
- existingMatch.loaderPromise
2335
+ existingMatch._nonReactive.beforeLoadPromise ||
2336
+ existingMatch._nonReactive.loaderPromise
2346
2337
  ) {
2347
2338
  setupPendingTimeout()
2348
2339
 
2349
2340
  // Wait for the beforeLoad to resolve before we continue
2350
- await existingMatch.beforeLoadPromise
2341
+ await existingMatch._nonReactive.beforeLoadPromise
2351
2342
  const match = this.getMatch(matchId)!
2352
2343
  if (match.status === 'error') {
2353
2344
  executeBeforeLoad = true
@@ -2361,17 +2352,15 @@ export class RouterCore<
2361
2352
  if (executeBeforeLoad) {
2362
2353
  // If we are not in the middle of a load OR the previous load failed, start it
2363
2354
  try {
2364
- updateMatch(matchId, (prev) => {
2365
- // explicitly capture the previous loadPromise
2366
- const prevLoadPromise = prev.loadPromise
2367
- return {
2368
- ...prev,
2369
- loadPromise: createControlledPromise<void>(() => {
2370
- prevLoadPromise?.resolve()
2371
- }),
2372
- beforeLoadPromise: createControlledPromise<void>(),
2373
- }
2374
- })
2355
+ const match = this.getMatch(matchId)!
2356
+ match._nonReactive.beforeLoadPromise =
2357
+ createControlledPromise<void>()
2358
+ // explicitly capture the previous loadPromise
2359
+ const prevLoadPromise = match._nonReactive.loadPromise
2360
+ match._nonReactive.loadPromise =
2361
+ createControlledPromise<void>(() => {
2362
+ prevLoadPromise?.resolve()
2363
+ })
2375
2364
 
2376
2365
  const { paramsError, searchError } = this.getMatch(matchId)!
2377
2366
 
@@ -2388,7 +2377,7 @@ export class RouterCore<
2388
2377
  const abortController = new AbortController()
2389
2378
 
2390
2379
  const parentMatchContext =
2391
- parentMatch?.context ?? this.options.context ?? {}
2380
+ parentMatch?.context ?? this.options.context ?? undefined
2392
2381
 
2393
2382
  updateMatch(matchId, (prev) => ({
2394
2383
  ...prev,
@@ -2453,11 +2442,11 @@ export class RouterCore<
2453
2442
  }
2454
2443
 
2455
2444
  updateMatch(matchId, (prev) => {
2456
- prev.beforeLoadPromise?.resolve()
2445
+ prev._nonReactive.beforeLoadPromise?.resolve()
2446
+ prev._nonReactive.beforeLoadPromise = undefined
2457
2447
 
2458
2448
  return {
2459
2449
  ...prev,
2460
- beforeLoadPromise: undefined,
2461
2450
  isFetching: false,
2462
2451
  }
2463
2452
  })
@@ -2507,8 +2496,8 @@ export class RouterCore<
2507
2496
 
2508
2497
  const potentialPendingMinPromise = async () => {
2509
2498
  const latestMatch = this.getMatch(matchId)!
2510
- if (latestMatch.minPendingPromise) {
2511
- await latestMatch.minPendingPromise
2499
+ if (latestMatch._nonReactive.minPendingPromise) {
2500
+ await latestMatch._nonReactive.minPendingPromise
2512
2501
  }
2513
2502
  }
2514
2503
 
@@ -2524,7 +2513,7 @@ export class RouterCore<
2524
2513
  }
2525
2514
  }
2526
2515
  // there is a loaderPromise, so we are in the middle of a load
2527
- else if (prevMatch.loaderPromise) {
2516
+ else if (prevMatch._nonReactive.loaderPromise) {
2528
2517
  // do not block if we already have stale data we can show
2529
2518
  // but only if the ongoing load is not a preload since error handling is different for preloads
2530
2519
  // and we don't want to swallow errors
@@ -2535,7 +2524,7 @@ export class RouterCore<
2535
2524
  ) {
2536
2525
  return this.getMatch(matchId)!
2537
2526
  }
2538
- await prevMatch.loaderPromise
2527
+ await prevMatch._nonReactive.loaderPromise
2539
2528
  const match = this.getMatch(matchId)!
2540
2529
  if (match.error) {
2541
2530
  handleRedirectAndNotFound(match, match.error)
@@ -2592,13 +2581,16 @@ export class RouterCore<
2592
2581
  ? shouldReloadOption(getLoaderContext())
2593
2582
  : shouldReloadOption
2594
2583
 
2595
- updateMatch(matchId, (prev) => ({
2596
- ...prev,
2597
- loaderPromise: createControlledPromise<void>(),
2598
- preload:
2599
- !!preload &&
2600
- !this.state.matches.some((d) => d.id === matchId),
2601
- }))
2584
+ updateMatch(matchId, (prev) => {
2585
+ prev._nonReactive.loaderPromise =
2586
+ createControlledPromise<void>()
2587
+ return {
2588
+ ...prev,
2589
+ preload:
2590
+ !!preload &&
2591
+ !this.state.matches.some((d) => d.id === matchId),
2592
+ }
2593
+ })
2602
2594
 
2603
2595
  const runLoader = async () => {
2604
2596
  try {
@@ -2682,11 +2674,13 @@ export class RouterCore<
2682
2674
  } catch (err) {
2683
2675
  const head = await executeHead()
2684
2676
 
2685
- updateMatch(matchId, (prev) => ({
2686
- ...prev,
2687
- loaderPromise: undefined,
2688
- ...head,
2689
- }))
2677
+ updateMatch(matchId, (prev) => {
2678
+ prev._nonReactive.loaderPromise = undefined
2679
+ return {
2680
+ ...prev,
2681
+ ...head,
2682
+ }
2683
+ })
2690
2684
  handleRedirectAndNotFound(this.getMatch(matchId)!, err)
2691
2685
  }
2692
2686
  }
@@ -2703,14 +2697,10 @@ export class RouterCore<
2703
2697
  ;(async () => {
2704
2698
  try {
2705
2699
  await runLoader()
2706
- const { loaderPromise, loadPromise } =
2707
- this.getMatch(matchId)!
2708
- loaderPromise?.resolve()
2709
- loadPromise?.resolve()
2710
- updateMatch(matchId, (prev) => ({
2711
- ...prev,
2712
- loaderPromise: undefined,
2713
- }))
2700
+ const match = this.getMatch(matchId)!
2701
+ match._nonReactive.loaderPromise?.resolve()
2702
+ match._nonReactive.loadPromise?.resolve()
2703
+ match._nonReactive.loaderPromise = undefined
2714
2704
  } catch (err) {
2715
2705
  if (isRedirect(err)) {
2716
2706
  await this.navigate(err.options)
@@ -2734,25 +2724,23 @@ export class RouterCore<
2734
2724
  }
2735
2725
  }
2736
2726
  if (!loaderIsRunningAsync) {
2737
- const { loaderPromise, loadPromise } =
2738
- this.getMatch(matchId)!
2739
- loaderPromise?.resolve()
2740
- loadPromise?.resolve()
2727
+ const match = this.getMatch(matchId)!
2728
+ match._nonReactive.loaderPromise?.resolve()
2729
+ match._nonReactive.loadPromise?.resolve()
2741
2730
  }
2742
2731
 
2743
2732
  updateMatch(matchId, (prev) => {
2744
- clearTimeout(prev.pendingTimeout)
2733
+ clearTimeout(prev._nonReactive.pendingTimeout)
2734
+ prev._nonReactive.pendingTimeout = undefined
2735
+ if (!loaderIsRunningAsync)
2736
+ prev._nonReactive.loaderPromise = undefined
2737
+ prev._nonReactive.dehydrated = undefined
2745
2738
  return {
2746
2739
  ...prev,
2747
2740
  isFetching: loaderIsRunningAsync
2748
2741
  ? prev.isFetching
2749
2742
  : false,
2750
- loaderPromise: loaderIsRunningAsync
2751
- ? prev.loaderPromise
2752
- : undefined,
2753
2743
  invalid: false,
2754
- pendingTimeout: undefined,
2755
- _dehydrated: undefined,
2756
2744
  }
2757
2745
  })
2758
2746
  return this.getMatch(matchId)!
@@ -2798,7 +2786,7 @@ export class RouterCore<
2798
2786
  invalid: true,
2799
2787
  ...(opts?.forcePending || d.status === 'error'
2800
2788
  ? ({ status: 'pending', error: undefined } as const)
2801
- : {}),
2789
+ : undefined),
2802
2790
  }
2803
2791
  }
2804
2792
  return d
@@ -3569,7 +3557,8 @@ function applySearchMiddleware({
3569
3557
  try {
3570
3558
  const validatedSearch = {
3571
3559
  ...result,
3572
- ...(validateSearch(route.options.validateSearch, result) ?? {}),
3560
+ ...(validateSearch(route.options.validateSearch, result) ??
3561
+ undefined),
3573
3562
  }
3574
3563
  return validatedSearch
3575
3564
  } catch {