@tanstack/router-core 1.155.0 → 1.157.0

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,6 +1,6 @@
1
1
  import { Store, batch } from "@tanstack/store";
2
2
  import { createBrowserHistory, parseHref } from "@tanstack/history";
3
- import { createControlledPromise, isDangerousProtocol, deepEqual, replaceEqualDeep, decodePath, functionalUpdate, last, findLast } from "./utils.js";
3
+ import { createControlledPromise, isDangerousProtocol, deepEqual, replaceEqualDeep, last, decodePath, functionalUpdate, findLast } from "./utils.js";
4
4
  import { processRouteTree, processRouteMasks, findSingleMatch, findRouteMatch, findFlatMatch } from "./new-process-route-tree.js";
5
5
  import { compileDecodeCharMap, trimPath, resolvePath, cleanPath, trimPathRight, interpolatePath } from "./path.js";
6
6
  import { createLRUCache } from "./lru-cache.js";
@@ -11,6 +11,7 @@ import { rootRouteId } from "./root.js";
11
11
  import { redirect, isRedirect } from "./redirect.js";
12
12
  import { loadMatches, loadRouteChunk, routeNeedsPreload } from "./load-matches.js";
13
13
  import { rewriteBasepath, composeRewrites, executeRewriteInput, executeRewriteOutput } from "./rewrite.js";
14
+ import { isServer } from "@tanstack/router-is-server";
14
15
  function defaultSerializeError(err) {
15
16
  if (err instanceof Error) {
16
17
  const obj = {
@@ -75,7 +76,7 @@ class RouterCore {
75
76
  );
76
77
  if (!this.history || this.options.history && this.options.history !== this.history) {
77
78
  if (!this.options.history) {
78
- if (!this.isServer) {
79
+ if (!(isServer ?? this.isServer)) {
79
80
  this.history = createBrowserHistory();
80
81
  }
81
82
  } else {
@@ -84,7 +85,7 @@ class RouterCore {
84
85
  }
85
86
  this.origin = this.options.origin;
86
87
  if (!this.origin) {
87
- if (!this.isServer && window?.origin && window.origin !== "null") {
88
+ if (!(isServer ?? this.isServer) && window?.origin && window.origin !== "null") {
88
89
  this.origin = window.origin;
89
90
  } else {
90
91
  this.origin = "http://localhost";
@@ -95,7 +96,23 @@ class RouterCore {
95
96
  }
96
97
  if (this.options.routeTree !== this.routeTree) {
97
98
  this.routeTree = this.options.routeTree;
98
- this.buildRouteTree();
99
+ let processRouteTreeResult;
100
+ if ((isServer ?? this.isServer) && globalThis.__TSR_CACHE__ && globalThis.__TSR_CACHE__.routeTree === this.routeTree) {
101
+ const cached = globalThis.__TSR_CACHE__;
102
+ this.resolvePathCache = cached.resolvePathCache;
103
+ processRouteTreeResult = cached.processRouteTreeResult;
104
+ } else {
105
+ this.resolvePathCache = createLRUCache(1e3);
106
+ processRouteTreeResult = this.buildRouteTree();
107
+ if ((isServer ?? this.isServer) && globalThis.__TSR_CACHE__ === void 0) {
108
+ globalThis.__TSR_CACHE__ = {
109
+ routeTree: this.routeTree,
110
+ processRouteTreeResult,
111
+ resolvePathCache: this.resolvePathCache
112
+ };
113
+ }
114
+ }
115
+ this.setRoutes(processRouteTreeResult);
99
116
  }
100
117
  if (!this.__store && this.latestLocation) {
101
118
  this.__store = new Store(getInitialRouterState(this.latestLocation), {
@@ -153,7 +170,7 @@ class RouterCore {
153
170
  );
154
171
  };
155
172
  this.buildRouteTree = () => {
156
- const { routesById, routesByPath, processedTree } = processRouteTree(
173
+ const result = processRouteTree(
157
174
  this.routeTree,
158
175
  this.options.caseSensitive,
159
176
  (route, i) => {
@@ -163,18 +180,9 @@ class RouterCore {
163
180
  }
164
181
  );
165
182
  if (this.options.routeMasks) {
166
- processRouteMasks(this.options.routeMasks, processedTree);
167
- }
168
- this.routesById = routesById;
169
- this.routesByPath = routesByPath;
170
- this.processedTree = processedTree;
171
- const notFoundRoute = this.options.notFoundRoute;
172
- if (notFoundRoute) {
173
- notFoundRoute.init({
174
- originalIndex: 99999999999
175
- });
176
- this.routesById[notFoundRoute.id] = notFoundRoute;
183
+ processRouteMasks(this.options.routeMasks, result.processedTree);
177
184
  }
185
+ return result;
178
186
  };
179
187
  this.subscribe = (eventType, fn) => {
180
188
  const listener = {
@@ -229,7 +237,6 @@ class RouterCore {
229
237
  }
230
238
  return location;
231
239
  };
232
- this.resolvePathCache = createLRUCache(1e3);
233
240
  this.resolvePathWithBase = (from, path) => {
234
241
  const resolvedPath = resolvePath({
235
242
  base: from,
@@ -284,26 +291,23 @@ class RouterCore {
284
291
  this.buildLocation = (opts) => {
285
292
  const build = (dest = {}) => {
286
293
  const currentLocation = dest._fromLocation || this.pendingBuiltLocation || this.latestLocation;
287
- const allCurrentLocationMatches = this.matchRoutes(currentLocation, {
288
- _buildLocation: true
289
- });
290
- const lastMatch = last(allCurrentLocationMatches);
294
+ const lightweightResult = this.matchRoutesLightweight(currentLocation);
291
295
  if (dest.from && process.env.NODE_ENV !== "production" && dest._isNavigate) {
292
296
  const allFromMatches = this.getMatchedRoutes(dest.from).matchedRoutes;
293
- const matchedFrom = findLast(allCurrentLocationMatches, (d) => {
297
+ const matchedFrom = findLast(lightweightResult.matchedRoutes, (d) => {
294
298
  return comparePaths(d.fullPath, dest.from);
295
299
  });
296
300
  const matchedCurrent = findLast(allFromMatches, (d) => {
297
- return comparePaths(d.fullPath, lastMatch.fullPath);
301
+ return comparePaths(d.fullPath, lightweightResult.fullPath);
298
302
  });
299
303
  if (!matchedFrom && !matchedCurrent) {
300
304
  console.warn(`Could not find match for from: ${dest.from}`);
301
305
  }
302
306
  }
303
- const defaultedFromPath = dest.unsafeRelative === "path" ? currentLocation.pathname : dest.from ?? lastMatch.fullPath;
307
+ const defaultedFromPath = dest.unsafeRelative === "path" ? currentLocation.pathname : dest.from ?? lightweightResult.fullPath;
304
308
  const fromPath = this.resolvePathWithBase(defaultedFromPath, ".");
305
- const fromSearch = lastMatch.search;
306
- const fromParams = { ...lastMatch.params };
309
+ const fromSearch = lightweightResult.search;
310
+ const fromParams = { ...lightweightResult.params };
307
311
  const nextTo = dest.to ? this.resolvePathWithBase(fromPath, `${dest.to}`) : this.resolvePathWithBase(fromPath, ".");
308
312
  const nextParams = dest.params === false || dest.params === null ? {} : (dest.params ?? true) === true ? fromParams : Object.assign(
309
313
  fromParams,
@@ -616,7 +620,7 @@ class RouterCore {
616
620
  this.beforeLoad = () => {
617
621
  this.cancelMatches();
618
622
  this.updateLatestLocation();
619
- if (this.isServer) {
623
+ if (isServer ?? this.isServer) {
620
624
  const nextLocation = this.buildLocation({
621
625
  to: this.latestLocation.pathname,
622
626
  search: true,
@@ -736,7 +740,7 @@ class RouterCore {
736
740
  } catch (err) {
737
741
  if (isRedirect(err)) {
738
742
  redirect2 = err;
739
- if (!this.isServer) {
743
+ if (!(isServer ?? this.isServer)) {
740
744
  this.navigate({
741
745
  ...redirect2.options,
742
746
  replace: true,
@@ -1032,6 +1036,22 @@ class RouterCore {
1032
1036
  get state() {
1033
1037
  return this.__store.state;
1034
1038
  }
1039
+ setRoutes({
1040
+ routesById,
1041
+ routesByPath,
1042
+ processedTree
1043
+ }) {
1044
+ this.routesById = routesById;
1045
+ this.routesByPath = routesByPath;
1046
+ this.processedTree = processedTree;
1047
+ const notFoundRoute = this.options.notFoundRoute;
1048
+ if (notFoundRoute) {
1049
+ notFoundRoute.init({
1050
+ originalIndex: 99999999999
1051
+ });
1052
+ this.routesById[notFoundRoute.id] = notFoundRoute;
1053
+ }
1054
+ }
1035
1055
  get looseRoutesById() {
1036
1056
  return this.routesById;
1037
1057
  }
@@ -1110,32 +1130,18 @@ class RouterCore {
1110
1130
  const strictParams = existingMatch?._strictParams ?? usedParams;
1111
1131
  let paramsError = void 0;
1112
1132
  if (!existingMatch) {
1113
- if (route.options.skipRouteOnParseError) {
1114
- for (const key in usedParams) {
1115
- if (key in parsedParams) {
1116
- strictParams[key] = parsedParams[key];
1117
- }
1133
+ try {
1134
+ extractStrictParams(route, usedParams, parsedParams, strictParams);
1135
+ } catch (err) {
1136
+ if (isNotFound(err) || isRedirect(err)) {
1137
+ paramsError = err;
1138
+ } else {
1139
+ paramsError = new PathParamError(err.message, {
1140
+ cause: err
1141
+ });
1118
1142
  }
1119
- } else {
1120
- const strictParseParams = route.options.params?.parse ?? route.options.parseParams;
1121
- if (strictParseParams) {
1122
- try {
1123
- Object.assign(
1124
- strictParams,
1125
- strictParseParams(strictParams)
1126
- );
1127
- } catch (err) {
1128
- if (isNotFound(err) || isRedirect(err)) {
1129
- paramsError = err;
1130
- } else {
1131
- paramsError = new PathParamError(err.message, {
1132
- cause: err
1133
- });
1134
- }
1135
- if (opts?.throwOnError) {
1136
- throw paramsError;
1137
- }
1138
- }
1143
+ if (opts?.throwOnError) {
1144
+ throw paramsError;
1139
1145
  }
1140
1146
  }
1141
1147
  }
@@ -1155,7 +1161,7 @@ class RouterCore {
1155
1161
  const status = route.options.loader || route.options.beforeLoad || route.lazyFn || routeNeedsPreload(route) ? "pending" : "success";
1156
1162
  match = {
1157
1163
  id: matchId,
1158
- ssr: this.isServer ? void 0 : route.options.ssr,
1164
+ ssr: isServer ?? this.isServer ? void 0 : route.options.ssr,
1159
1165
  index,
1160
1166
  routeId: route.id,
1161
1167
  params: previousMatch ? replaceEqualDeep(previousMatch.params, routeParams) : routeParams,
@@ -1204,7 +1210,7 @@ class RouterCore {
1204
1210
  matches.forEach((match, index) => {
1205
1211
  const route = this.looseRoutesById[match.routeId];
1206
1212
  const existingMatch = this.getMatch(match.id);
1207
- if (!existingMatch && opts?._buildLocation !== true) {
1213
+ if (!existingMatch) {
1208
1214
  const parentMatch = matches[index - 1];
1209
1215
  const parentContext = getParentContext(parentMatch);
1210
1216
  if (route.options.context) {
@@ -1231,6 +1237,53 @@ class RouterCore {
1231
1237
  });
1232
1238
  return matches;
1233
1239
  }
1240
+ /**
1241
+ * Lightweight route matching for buildLocation.
1242
+ * Only computes fullPath, accumulated search, and params - skipping expensive
1243
+ * operations like AbortController, ControlledPromise, loaderDeps, and full match objects.
1244
+ */
1245
+ matchRoutesLightweight(location) {
1246
+ const { matchedRoutes, routeParams, parsedParams } = this.getMatchedRoutes(
1247
+ location.pathname
1248
+ );
1249
+ const lastRoute = last(matchedRoutes);
1250
+ const accumulatedSearch = { ...location.search };
1251
+ for (const route of matchedRoutes) {
1252
+ try {
1253
+ Object.assign(
1254
+ accumulatedSearch,
1255
+ validateSearch(route.options.validateSearch, accumulatedSearch)
1256
+ );
1257
+ } catch {
1258
+ }
1259
+ }
1260
+ const lastStateMatch = last(this.state.matches);
1261
+ const canReuseParams = lastStateMatch && lastStateMatch.routeId === lastRoute.id && location.pathname === this.state.location.pathname;
1262
+ let params;
1263
+ if (canReuseParams) {
1264
+ params = lastStateMatch.params;
1265
+ } else {
1266
+ const strictParams = { ...routeParams };
1267
+ for (const route of matchedRoutes) {
1268
+ try {
1269
+ extractStrictParams(
1270
+ route,
1271
+ routeParams,
1272
+ parsedParams ?? {},
1273
+ strictParams
1274
+ );
1275
+ } catch {
1276
+ }
1277
+ }
1278
+ params = strictParams;
1279
+ }
1280
+ return {
1281
+ matchedRoutes,
1282
+ fullPath: lastRoute.fullPath,
1283
+ search: accumulatedSearch,
1284
+ params
1285
+ };
1286
+ }
1234
1287
  }
1235
1288
  class SearchParamError extends Error {
1236
1289
  }
@@ -1393,6 +1446,21 @@ function findGlobalNotFoundRouteId(notFoundMode, routes) {
1393
1446
  }
1394
1447
  return rootRouteId;
1395
1448
  }
1449
+ function extractStrictParams(route, referenceParams, parsedParams, accumulatedParams) {
1450
+ const parseParams = route.options.params?.parse ?? route.options.parseParams;
1451
+ if (parseParams) {
1452
+ if (route.options.skipRouteOnParseError) {
1453
+ for (const key in referenceParams) {
1454
+ if (key in parsedParams) {
1455
+ accumulatedParams[key] = parsedParams[key];
1456
+ }
1457
+ }
1458
+ } else {
1459
+ const result = parseParams(accumulatedParams);
1460
+ Object.assign(accumulatedParams, result);
1461
+ }
1462
+ }
1463
+ }
1396
1464
  export {
1397
1465
  PathParamError,
1398
1466
  RouterCore,