@tanstack/router-core 1.120.7 → 1.121.0-alpha.11

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.
Files changed (51) hide show
  1. package/dist/cjs/fileRoute.d.cts +6 -2
  2. package/dist/cjs/index.cjs +3 -0
  3. package/dist/cjs/index.cjs.map +1 -1
  4. package/dist/cjs/index.d.cts +7 -7
  5. package/dist/cjs/link.cjs.map +1 -1
  6. package/dist/cjs/link.d.cts +18 -1
  7. package/dist/cjs/path.cjs +130 -16
  8. package/dist/cjs/path.cjs.map +1 -1
  9. package/dist/cjs/path.d.cts +17 -0
  10. package/dist/cjs/redirect.cjs +17 -7
  11. package/dist/cjs/redirect.cjs.map +1 -1
  12. package/dist/cjs/redirect.d.cts +13 -7
  13. package/dist/cjs/route.cjs +12 -1
  14. package/dist/cjs/route.cjs.map +1 -1
  15. package/dist/cjs/route.d.cts +18 -27
  16. package/dist/cjs/router.cjs +395 -335
  17. package/dist/cjs/router.cjs.map +1 -1
  18. package/dist/cjs/router.d.cts +48 -8
  19. package/dist/cjs/typePrimitives.d.cts +2 -2
  20. package/dist/cjs/utils.cjs.map +1 -1
  21. package/dist/cjs/utils.d.cts +3 -0
  22. package/dist/esm/fileRoute.d.ts +6 -2
  23. package/dist/esm/index.d.ts +7 -7
  24. package/dist/esm/index.js +5 -2
  25. package/dist/esm/link.d.ts +18 -1
  26. package/dist/esm/link.js.map +1 -1
  27. package/dist/esm/path.d.ts +17 -0
  28. package/dist/esm/path.js +130 -16
  29. package/dist/esm/path.js.map +1 -1
  30. package/dist/esm/redirect.d.ts +13 -7
  31. package/dist/esm/redirect.js +17 -7
  32. package/dist/esm/redirect.js.map +1 -1
  33. package/dist/esm/route.d.ts +18 -27
  34. package/dist/esm/route.js +12 -1
  35. package/dist/esm/route.js.map +1 -1
  36. package/dist/esm/router.d.ts +48 -8
  37. package/dist/esm/router.js +398 -338
  38. package/dist/esm/router.js.map +1 -1
  39. package/dist/esm/typePrimitives.d.ts +2 -2
  40. package/dist/esm/utils.d.ts +3 -0
  41. package/dist/esm/utils.js.map +1 -1
  42. package/package.json +2 -2
  43. package/src/fileRoute.ts +90 -1
  44. package/src/index.ts +14 -8
  45. package/src/link.ts +97 -11
  46. package/src/path.ts +181 -16
  47. package/src/redirect.ts +39 -16
  48. package/src/route.ts +91 -64
  49. package/src/router.ts +569 -434
  50. package/src/typePrimitives.ts +2 -2
  51. package/src/utils.ts +15 -0
@@ -2,12 +2,12 @@ import { Store, batch } from "@tanstack/store";
2
2
  import { createMemoryHistory, createBrowserHistory, parseHref } from "@tanstack/history";
3
3
  import invariant from "tiny-invariant";
4
4
  import { pick, createControlledPromise, deepEqual, replaceEqualDeep, last, functionalUpdate } from "./utils.js";
5
- import { trimPath, trimPathLeft, parsePathname, resolvePath, cleanPath, trimPathRight, matchPathname, interpolatePath, joinPaths } from "./path.js";
5
+ import { trimPath, resolvePath, cleanPath, matchPathname, trimPathRight, interpolatePath, joinPaths, trimPathLeft, parsePathname } from "./path.js";
6
6
  import { isNotFound } from "./not-found.js";
7
7
  import { setupScrollRestoration } from "./scroll-restoration.js";
8
8
  import { defaultParseSearch, defaultStringifySearch } from "./searchParams.js";
9
9
  import { rootRouteId } from "./root.js";
10
- import { isResolvedRedirect, isRedirect } from "./redirect.js";
10
+ import { isRedirect } from "./redirect.js";
11
11
  function defaultSerializeError(err) {
12
12
  if (err instanceof Error) {
13
13
  const obj = {
@@ -46,6 +46,7 @@ class RouterCore {
46
46
  this.isScrollRestoring = false;
47
47
  this.isScrollRestorationSetup = false;
48
48
  this.startTransition = (fn) => fn();
49
+ this.isShell = false;
49
50
  this.update = (newOptions) => {
50
51
  var _a;
51
52
  if (newOptions.notFoundRoute) {
@@ -72,10 +73,7 @@ class RouterCore {
72
73
  this.basepath = `/${trimPath(newOptions.basepath)}`;
73
74
  }
74
75
  }
75
- if (
76
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
77
- !this.history || this.options.history && this.options.history !== this.history
78
- ) {
76
+ if (!this.history || this.options.history && this.options.history !== this.history) {
79
77
  this.history = this.options.history ?? (this.isServer ? createMemoryHistory({
80
78
  initialEntries: [this.basepath || "/"]
81
79
  }) : createBrowserHistory());
@@ -98,16 +96,28 @@ class RouterCore {
98
96
  });
99
97
  setupScrollRestoration(this);
100
98
  }
101
- if (typeof window !== "undefined" && "CSS" in window && // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
102
- typeof ((_a = window.CSS) == null ? void 0 : _a.supports) === "function") {
99
+ if (typeof window !== "undefined" && "CSS" in window && typeof ((_a = window.CSS) == null ? void 0 : _a.supports) === "function") {
103
100
  this.isViewTransitionTypesSupported = window.CSS.supports(
104
101
  "selector(:active-view-transition-type(a)"
105
102
  );
106
103
  }
104
+ if (this.latestLocation.search.__TSS_SHELL) {
105
+ this.isShell = true;
106
+ }
107
107
  };
108
108
  this.buildRouteTree = () => {
109
- this.routesById = {};
110
- this.routesByPath = {};
109
+ const { routesById, routesByPath, flatRoutes } = processRouteTree({
110
+ routeTree: this.routeTree,
111
+ initRoute: (route, i) => {
112
+ route.init({
113
+ originalIndex: i,
114
+ defaultSsr: this.options.defaultSsr
115
+ });
116
+ }
117
+ });
118
+ this.routesById = routesById;
119
+ this.routesByPath = routesByPath;
120
+ this.flatRoutes = flatRoutes;
111
121
  const notFoundRoute = this.options.notFoundRoute;
112
122
  if (notFoundRoute) {
113
123
  notFoundRoute.init({
@@ -116,77 +126,6 @@ class RouterCore {
116
126
  });
117
127
  this.routesById[notFoundRoute.id] = notFoundRoute;
118
128
  }
119
- const recurseRoutes = (childRoutes) => {
120
- childRoutes.forEach((childRoute, i) => {
121
- childRoute.init({
122
- originalIndex: i,
123
- defaultSsr: this.options.defaultSsr
124
- });
125
- const existingRoute = this.routesById[childRoute.id];
126
- invariant(
127
- !existingRoute,
128
- `Duplicate routes found with id: ${String(childRoute.id)}`
129
- );
130
- this.routesById[childRoute.id] = childRoute;
131
- if (!childRoute.isRoot && childRoute.path) {
132
- const trimmedFullPath = trimPathRight(childRoute.fullPath);
133
- if (!this.routesByPath[trimmedFullPath] || childRoute.fullPath.endsWith("/")) {
134
- this.routesByPath[trimmedFullPath] = childRoute;
135
- }
136
- }
137
- const children = childRoute.children;
138
- if (children == null ? void 0 : children.length) {
139
- recurseRoutes(children);
140
- }
141
- });
142
- };
143
- recurseRoutes([this.routeTree]);
144
- const scoredRoutes = [];
145
- const routes = Object.values(this.routesById);
146
- routes.forEach((d, i) => {
147
- var _a;
148
- if (d.isRoot || !d.path) {
149
- return;
150
- }
151
- const trimmed = trimPathLeft(d.fullPath);
152
- const parsed = parsePathname(trimmed);
153
- while (parsed.length > 1 && ((_a = parsed[0]) == null ? void 0 : _a.value) === "/") {
154
- parsed.shift();
155
- }
156
- const scores = parsed.map((segment) => {
157
- if (segment.value === "/") {
158
- return 0.75;
159
- }
160
- if (segment.type === "param") {
161
- return 0.5;
162
- }
163
- if (segment.type === "wildcard") {
164
- return 0.25;
165
- }
166
- return 1;
167
- });
168
- scoredRoutes.push({ child: d, trimmed, parsed, index: i, scores });
169
- });
170
- this.flatRoutes = scoredRoutes.sort((a, b) => {
171
- const minLength = Math.min(a.scores.length, b.scores.length);
172
- for (let i = 0; i < minLength; i++) {
173
- if (a.scores[i] !== b.scores[i]) {
174
- return b.scores[i] - a.scores[i];
175
- }
176
- }
177
- if (a.scores.length !== b.scores.length) {
178
- return b.scores.length - a.scores.length;
179
- }
180
- for (let i = 0; i < minLength; i++) {
181
- if (a.parsed[i].value !== b.parsed[i].value) {
182
- return a.parsed[i].value > b.parsed[i].value ? 1 : -1;
183
- }
184
- }
185
- return a.index - b.index;
186
- }).map((d, i) => {
187
- d.child.rank = i;
188
- return d.child;
189
- });
190
129
  };
191
130
  this.subscribe = (eventType, fn) => {
192
131
  const listener = {
@@ -255,41 +194,19 @@ class RouterCore {
255
194
  },
256
195
  opts
257
196
  );
258
- } else {
259
- return this.matchRoutesInternal(pathnameOrNext, locationSearchOrOpts);
260
197
  }
198
+ return this.matchRoutesInternal(pathnameOrNext, locationSearchOrOpts);
261
199
  };
262
- this.getMatchedRoutes = (next, dest) => {
263
- let routeParams = {};
264
- const trimmedPath = trimPathRight(next.pathname);
265
- const getMatchedParams = (route) => {
266
- const result = matchPathname(this.basepath, trimmedPath, {
267
- to: route.fullPath,
268
- caseSensitive: route.options.caseSensitive ?? this.options.caseSensitive,
269
- fuzzy: true
270
- });
271
- return result;
272
- };
273
- let foundRoute = (dest == null ? void 0 : dest.to) !== void 0 ? this.routesByPath[dest.to] : void 0;
274
- if (foundRoute) {
275
- routeParams = getMatchedParams(foundRoute);
276
- } else {
277
- foundRoute = this.flatRoutes.find((route) => {
278
- const matchedParams = getMatchedParams(route);
279
- if (matchedParams) {
280
- routeParams = matchedParams;
281
- return true;
282
- }
283
- return false;
284
- });
285
- }
286
- let routeCursor = foundRoute || this.routesById[rootRouteId];
287
- const matchedRoutes = [routeCursor];
288
- while (routeCursor.parentRoute) {
289
- routeCursor = routeCursor.parentRoute;
290
- matchedRoutes.unshift(routeCursor);
291
- }
292
- return { matchedRoutes, routeParams, foundRoute };
200
+ this.getMatchedRoutes = (pathname, routePathname) => {
201
+ return getMatchedRoutes({
202
+ pathname,
203
+ routePathname,
204
+ basepath: this.basepath,
205
+ caseSensitive: this.options.caseSensitive,
206
+ routesByPath: this.routesByPath,
207
+ routesById: this.routesById,
208
+ flatRoutes: this.flatRoutes
209
+ });
293
210
  };
294
211
  this.cancelMatch = (id) => {
295
212
  const match = this.getMatch(id);
@@ -304,173 +221,92 @@ class RouterCore {
304
221
  });
305
222
  };
306
223
  this.buildLocation = (opts) => {
307
- const build = (dest = {}, matchedRoutesResult) => {
308
- var _a, _b, _c, _d, _e, _f, _g;
309
- const fromMatches = dest._fromLocation ? this.matchRoutes(dest._fromLocation, { _buildLocation: true }) : this.state.matches;
310
- const fromMatch = dest.from != null ? fromMatches.find(
311
- (d) => matchPathname(this.basepath, trimPathRight(d.pathname), {
312
- to: dest.from,
313
- caseSensitive: false,
314
- fuzzy: false
315
- })
316
- ) : void 0;
317
- const fromPath = (fromMatch == null ? void 0 : fromMatch.pathname) || this.latestLocation.pathname;
318
- invariant(
319
- dest.from == null || fromMatch != null,
320
- "Could not find match for from: " + dest.from
321
- );
322
- const fromSearch = ((_a = this.state.pendingMatches) == null ? void 0 : _a.length) ? (_b = last(this.state.pendingMatches)) == null ? void 0 : _b.search : ((_c = last(fromMatches)) == null ? void 0 : _c.search) || this.latestLocation.search;
323
- const stayingMatches = matchedRoutesResult == null ? void 0 : matchedRoutesResult.matchedRoutes.filter(
324
- (d) => fromMatches.find((e) => e.routeId === d.id)
325
- );
326
- let pathname;
327
- if (dest.to) {
328
- const resolvePathTo = (fromMatch == null ? void 0 : fromMatch.fullPath) || ((_d = last(fromMatches)) == null ? void 0 : _d.fullPath) || this.latestLocation.pathname;
329
- pathname = this.resolvePathWithBase(resolvePathTo, `${dest.to}`);
330
- } else {
331
- const fromRouteByFromPathRouteId = this.routesById[(_e = stayingMatches == null ? void 0 : stayingMatches.find((route) => {
332
- const interpolatedPath = interpolatePath({
333
- path: route.fullPath,
334
- params: (matchedRoutesResult == null ? void 0 : matchedRoutesResult.routeParams) ?? {},
335
- decodeCharMap: this.pathParamsDecodeCharMap
336
- }).interpolatedPath;
337
- const pathname2 = joinPaths([this.basepath, interpolatedPath]);
338
- return pathname2 === fromPath;
339
- })) == null ? void 0 : _e.id];
340
- pathname = this.resolvePathWithBase(
341
- fromPath,
342
- (fromRouteByFromPathRouteId == null ? void 0 : fromRouteByFromPathRouteId.to) ?? fromPath
343
- );
224
+ const build = (dest = {}) => {
225
+ var _a;
226
+ const currentLocation = dest._fromLocation || this.latestLocation;
227
+ const allFromMatches = this.matchRoutes(currentLocation, {
228
+ _buildLocation: true
229
+ });
230
+ const lastMatch = last(allFromMatches);
231
+ let fromPath = lastMatch.fullPath;
232
+ if (dest.unsafeRelative === "path") {
233
+ fromPath = currentLocation.pathname;
234
+ } else if (dest.to && dest.from) {
235
+ fromPath = dest.from;
236
+ const existingFrom = [...allFromMatches].reverse().find((d) => {
237
+ return d.fullPath === fromPath || d.fullPath === joinPaths([fromPath, "/"]);
238
+ });
239
+ if (!existingFrom) {
240
+ console.warn(`Could not find match for from: ${dest.from}`);
241
+ }
344
242
  }
345
- const prevParams = { ...(_f = last(fromMatches)) == null ? void 0 : _f.params };
346
- let nextParams = (dest.params ?? true) === true ? prevParams : {
347
- ...prevParams,
348
- ...functionalUpdate(dest.params, prevParams)
243
+ const fromSearch = lastMatch.search;
244
+ const fromParams = { ...lastMatch.params };
245
+ const nextTo = dest.to ? this.resolvePathWithBase(fromPath, `${dest.to}`) : fromPath;
246
+ let nextParams = (dest.params ?? true) === true ? fromParams : {
247
+ ...fromParams,
248
+ ...functionalUpdate(dest.params, fromParams)
349
249
  };
250
+ const destRoutes = this.matchRoutes(
251
+ nextTo,
252
+ {},
253
+ {
254
+ _buildLocation: true
255
+ }
256
+ ).map((d) => this.looseRoutesById[d.routeId]);
350
257
  if (Object.keys(nextParams).length > 0) {
351
- matchedRoutesResult == null ? void 0 : matchedRoutesResult.matchedRoutes.map((route) => {
258
+ destRoutes.map((route) => {
352
259
  var _a2;
353
260
  return ((_a2 = route.options.params) == null ? void 0 : _a2.stringify) ?? route.options.stringifyParams;
354
261
  }).filter(Boolean).forEach((fn) => {
355
262
  nextParams = { ...nextParams, ...fn(nextParams) };
356
263
  });
357
264
  }
358
- pathname = interpolatePath({
359
- path: pathname,
265
+ const nextPathname = interpolatePath({
266
+ path: nextTo,
360
267
  params: nextParams ?? {},
361
268
  leaveWildcards: false,
362
269
  leaveParams: opts.leaveParams,
363
270
  decodeCharMap: this.pathParamsDecodeCharMap
364
271
  }).interpolatedPath;
365
- let search = fromSearch;
366
- if (opts._includeValidateSearch && ((_g = this.options.search) == null ? void 0 : _g.strict)) {
272
+ let nextSearch = fromSearch;
273
+ if (opts._includeValidateSearch && ((_a = this.options.search) == null ? void 0 : _a.strict)) {
367
274
  let validatedSearch = {};
368
- matchedRoutesResult == null ? void 0 : matchedRoutesResult.matchedRoutes.forEach((route) => {
275
+ destRoutes.forEach((route) => {
369
276
  try {
370
277
  if (route.options.validateSearch) {
371
278
  validatedSearch = {
372
279
  ...validatedSearch,
373
280
  ...validateSearch(route.options.validateSearch, {
374
281
  ...validatedSearch,
375
- ...search
282
+ ...nextSearch
376
283
  }) ?? {}
377
284
  };
378
285
  }
379
286
  } catch {
380
287
  }
381
288
  });
382
- search = validatedSearch;
289
+ nextSearch = validatedSearch;
383
290
  }
384
- const applyMiddlewares = (search2) => {
385
- const allMiddlewares = (matchedRoutesResult == null ? void 0 : matchedRoutesResult.matchedRoutes.reduce(
386
- (acc, route) => {
387
- var _a2;
388
- const middlewares = [];
389
- if ("search" in route.options) {
390
- if ((_a2 = route.options.search) == null ? void 0 : _a2.middlewares) {
391
- middlewares.push(...route.options.search.middlewares);
392
- }
393
- } else if (route.options.preSearchFilters || route.options.postSearchFilters) {
394
- const legacyMiddleware = ({
395
- search: search3,
396
- next
397
- }) => {
398
- let nextSearch = search3;
399
- if ("preSearchFilters" in route.options && route.options.preSearchFilters) {
400
- nextSearch = route.options.preSearchFilters.reduce(
401
- (prev, next2) => next2(prev),
402
- search3
403
- );
404
- }
405
- const result = next(nextSearch);
406
- if ("postSearchFilters" in route.options && route.options.postSearchFilters) {
407
- return route.options.postSearchFilters.reduce(
408
- (prev, next2) => next2(prev),
409
- result
410
- );
411
- }
412
- return result;
413
- };
414
- middlewares.push(legacyMiddleware);
415
- }
416
- if (opts._includeValidateSearch && route.options.validateSearch) {
417
- const validate = ({ search: search3, next }) => {
418
- const result = next(search3);
419
- try {
420
- const validatedSearch = {
421
- ...result,
422
- ...validateSearch(
423
- route.options.validateSearch,
424
- result
425
- ) ?? {}
426
- };
427
- return validatedSearch;
428
- } catch {
429
- return result;
430
- }
431
- };
432
- middlewares.push(validate);
433
- }
434
- return acc.concat(middlewares);
435
- },
436
- []
437
- )) ?? [];
438
- const final = ({ search: search3 }) => {
439
- if (!dest.search) {
440
- return {};
441
- }
442
- if (dest.search === true) {
443
- return search3;
444
- }
445
- return functionalUpdate(dest.search, search3);
446
- };
447
- allMiddlewares.push(final);
448
- const applyNext = (index, currentSearch) => {
449
- if (index >= allMiddlewares.length) {
450
- return currentSearch;
451
- }
452
- const middleware = allMiddlewares[index];
453
- const next = (newSearch) => {
454
- return applyNext(index + 1, newSearch);
455
- };
456
- return middleware({ search: currentSearch, next });
457
- };
458
- return applyNext(0, search2);
459
- };
460
- search = applyMiddlewares(search);
461
- search = replaceEqualDeep(fromSearch, search);
462
- const searchStr = this.options.stringifySearch(search);
463
- const hash = dest.hash === true ? this.latestLocation.hash : dest.hash ? functionalUpdate(dest.hash, this.latestLocation.hash) : void 0;
291
+ nextSearch = applySearchMiddleware({
292
+ search: nextSearch,
293
+ dest,
294
+ destRoutes,
295
+ _includeValidateSearch: opts._includeValidateSearch
296
+ });
297
+ nextSearch = replaceEqualDeep(fromSearch, nextSearch);
298
+ const searchStr = this.options.stringifySearch(nextSearch);
299
+ const hash = dest.hash === true ? currentLocation.hash : dest.hash ? functionalUpdate(dest.hash, currentLocation.hash) : void 0;
464
300
  const hashStr = hash ? `#${hash}` : "";
465
- let nextState = dest.state === true ? this.latestLocation.state : dest.state ? functionalUpdate(dest.state, this.latestLocation.state) : {};
466
- nextState = replaceEqualDeep(this.latestLocation.state, nextState);
301
+ let nextState = dest.state === true ? currentLocation.state : dest.state ? functionalUpdate(dest.state, currentLocation.state) : {};
302
+ nextState = replaceEqualDeep(currentLocation.state, nextState);
467
303
  return {
468
- pathname,
469
- search,
304
+ pathname: nextPathname,
305
+ search: nextSearch,
470
306
  searchStr,
471
307
  state: nextState,
472
308
  hash: hash ?? "",
473
- href: `${pathname}${searchStr}${hashStr}`,
309
+ href: `${nextPathname}${searchStr}${hashStr}`,
474
310
  unmaskOnReload: dest.unmaskOnReload
475
311
  };
476
312
  };
@@ -502,14 +338,11 @@ class RouterCore {
502
338
  maskedNext = build(maskedDest);
503
339
  }
504
340
  }
505
- const nextMatches = this.getMatchedRoutes(next, dest);
506
- const final = build(dest, nextMatches);
507
341
  if (maskedNext) {
508
- const maskedMatches = this.getMatchedRoutes(maskedNext, maskedDest);
509
- const maskedFinal = build(maskedDest, maskedMatches);
510
- final.maskedLocation = maskedFinal;
342
+ const maskedFinal = build(maskedDest);
343
+ next.maskedLocation = maskedFinal;
511
344
  }
512
- return final;
345
+ return next;
513
346
  };
514
347
  if (opts.mask) {
515
348
  return buildWithMatches(opts, {
@@ -616,6 +449,13 @@ class RouterCore {
616
449
  });
617
450
  };
618
451
  this.navigate = ({ to, reloadDocument, href, ...rest }) => {
452
+ if (!reloadDocument && href) {
453
+ try {
454
+ new URL(`${href}`);
455
+ reloadDocument = true;
456
+ } catch {
457
+ }
458
+ }
619
459
  if (reloadDocument) {
620
460
  if (!href) {
621
461
  const location = this.buildLocation({ to, ...rest });
@@ -634,8 +474,23 @@ class RouterCore {
634
474
  to
635
475
  });
636
476
  };
637
- this.load = async (opts) => {
477
+ this.beforeLoad = () => {
478
+ this.cancelMatches();
638
479
  this.latestLocation = this.parseLocation(this.latestLocation);
480
+ const pendingMatches = this.matchRoutes(this.latestLocation);
481
+ this.__store.setState((s) => ({
482
+ ...s,
483
+ status: "pending",
484
+ isLoading: true,
485
+ location: this.latestLocation,
486
+ pendingMatches,
487
+ // If a cached moved to pendingMatches, remove it from cachedMatches
488
+ cachedMatches: s.cachedMatches.filter((d) => {
489
+ return !pendingMatches.find((e) => e.id === d.id);
490
+ })
491
+ }));
492
+ };
493
+ this.load = async (opts) => {
639
494
  let redirect;
640
495
  let notFound;
641
496
  let loadPromise;
@@ -643,24 +498,9 @@ class RouterCore {
643
498
  this.startTransition(async () => {
644
499
  var _a;
645
500
  try {
501
+ this.beforeLoad();
646
502
  const next = this.latestLocation;
647
503
  const prevLocation = this.state.resolvedLocation;
648
- this.cancelMatches();
649
- let pendingMatches;
650
- batch(() => {
651
- pendingMatches = this.matchRoutes(next);
652
- this.__store.setState((s) => ({
653
- ...s,
654
- status: "pending",
655
- isLoading: true,
656
- location: next,
657
- pendingMatches,
658
- // If a cached moved to pendingMatches, remove it from cachedMatches
659
- cachedMatches: s.cachedMatches.filter((d) => {
660
- return !pendingMatches.find((e) => e.id === d.id);
661
- })
662
- }));
663
- });
664
504
  if (!this.state.redirect) {
665
505
  this.emit({
666
506
  type: "onBeforeNavigate",
@@ -679,7 +519,7 @@ class RouterCore {
679
519
  });
680
520
  await this.loadMatches({
681
521
  sync: opts == null ? void 0 : opts.sync,
682
- matches: pendingMatches,
522
+ matches: this.state.pendingMatches,
683
523
  location: next,
684
524
  // eslint-disable-next-line @typescript-eslint/require-await
685
525
  onReady: async () => {
@@ -728,11 +568,11 @@ class RouterCore {
728
568
  }
729
569
  });
730
570
  } catch (err) {
731
- if (isResolvedRedirect(err)) {
571
+ if (isRedirect(err)) {
732
572
  redirect = err;
733
573
  if (!this.isServer) {
734
574
  this.navigate({
735
- ...redirect,
575
+ ...redirect.options,
736
576
  replace: true,
737
577
  ignoreBlocker: true
738
578
  });
@@ -742,7 +582,7 @@ class RouterCore {
742
582
  }
743
583
  this.__store.setState((s) => ({
744
584
  ...s,
745
- statusCode: redirect ? redirect.statusCode : notFound ? 404 : s.matches.some((d) => d.status === "error") ? 500 : 200,
585
+ statusCode: redirect ? redirect.status : notFound ? 404 : s.matches.some((d) => d.status === "error") ? 500 : 200,
746
586
  redirect
747
587
  }));
748
588
  }
@@ -840,12 +680,14 @@ class RouterCore {
840
680
  };
841
681
  const handleRedirectAndNotFound = (match, err) => {
842
682
  var _a, _b, _c, _d;
843
- if (isResolvedRedirect(err)) {
844
- if (!err.reloadDocument) {
845
- throw err;
846
- }
847
- }
848
683
  if (isRedirect(err) || isNotFound(err)) {
684
+ if (isRedirect(err)) {
685
+ if (err.redirectHandled) {
686
+ if (!err.options.reloadDocument) {
687
+ throw err;
688
+ }
689
+ }
690
+ }
849
691
  updateMatch(match.id, (prev) => ({
850
692
  ...prev,
851
693
  status: isRedirect(err) ? "redirected" : isNotFound(err) ? "notFound" : "error",
@@ -862,7 +704,9 @@ class RouterCore {
862
704
  (_c = match.loadPromise) == null ? void 0 : _c.resolve();
863
705
  if (isRedirect(err)) {
864
706
  rendered = true;
865
- err = this.resolveRedirect({ ...err, _fromLocation: location });
707
+ err.options._fromLocation = location;
708
+ err.redirectHandled = true;
709
+ err = this.resolveRedirect(err);
866
710
  throw err;
867
711
  } else if (isNotFound(err)) {
868
712
  this._handleNotFound(matches, err, {
@@ -1070,7 +914,7 @@ class RouterCore {
1070
914
  loaderPromise: createControlledPromise(),
1071
915
  preload: !!preload && !this.state.matches.find((d) => d.id === matchId)
1072
916
  }));
1073
- const executeHead = () => {
917
+ const executeHead = async () => {
1074
918
  var _a2, _b2, _c2, _d2, _e, _f;
1075
919
  const match = this.getMatch(matchId);
1076
920
  if (!match) {
@@ -1082,20 +926,13 @@ class RouterCore {
1082
926
  params: match.params,
1083
927
  loaderData: match.loaderData
1084
928
  };
1085
- const headFnContent = (_b2 = (_a2 = route.options).head) == null ? void 0 : _b2.call(_a2, assetContext);
929
+ const headFnContent = await ((_b2 = (_a2 = route.options).head) == null ? void 0 : _b2.call(_a2, assetContext));
1086
930
  const meta = headFnContent == null ? void 0 : headFnContent.meta;
1087
931
  const links = headFnContent == null ? void 0 : headFnContent.links;
1088
932
  const headScripts = headFnContent == null ? void 0 : headFnContent.scripts;
1089
- const scripts = (_d2 = (_c2 = route.options).scripts) == null ? void 0 : _d2.call(_c2, assetContext);
1090
- const headers = (_f = (_e = route.options).headers) == null ? void 0 : _f.call(_e, assetContext);
1091
- updateMatch(matchId, (prev) => ({
1092
- ...prev,
1093
- meta,
1094
- links,
1095
- headScripts,
1096
- headers,
1097
- scripts
1098
- }));
933
+ const scripts = await ((_d2 = (_c2 = route.options).scripts) == null ? void 0 : _d2.call(_c2, assetContext));
934
+ const headers = await ((_f = (_e = route.options).headers) == null ? void 0 : _f.call(_e, assetContext));
935
+ return { meta, links, headScripts, headers, scripts };
1099
936
  };
1100
937
  const runLoader = async () => {
1101
938
  var _a2, _b2, _c2, _d2, _e;
@@ -1120,17 +957,19 @@ class RouterCore {
1120
957
  await route._lazyPromise;
1121
958
  await potentialPendingMinPromise();
1122
959
  await route._componentsPromise;
1123
- batch(() => {
1124
- updateMatch(matchId, (prev) => ({
1125
- ...prev,
1126
- error: void 0,
1127
- status: "success",
1128
- isFetching: false,
1129
- updatedAt: Date.now(),
1130
- loaderData
1131
- }));
1132
- executeHead();
1133
- });
960
+ updateMatch(matchId, (prev) => ({
961
+ ...prev,
962
+ error: void 0,
963
+ status: "success",
964
+ isFetching: false,
965
+ updatedAt: Date.now(),
966
+ loaderData
967
+ }));
968
+ const head = await executeHead();
969
+ updateMatch(matchId, (prev) => ({
970
+ ...prev,
971
+ ...head
972
+ }));
1134
973
  } catch (e) {
1135
974
  let error = e;
1136
975
  await potentialPendingMinPromise();
@@ -1144,28 +983,26 @@ class RouterCore {
1144
983
  onErrorError
1145
984
  );
1146
985
  }
1147
- batch(() => {
1148
- updateMatch(matchId, (prev) => ({
1149
- ...prev,
1150
- error,
1151
- status: "error",
1152
- isFetching: false
1153
- }));
1154
- executeHead();
1155
- });
986
+ const head = await executeHead();
987
+ updateMatch(matchId, (prev) => ({
988
+ ...prev,
989
+ error,
990
+ status: "error",
991
+ isFetching: false,
992
+ ...head
993
+ }));
1156
994
  }
1157
995
  (_e = this.serverSsr) == null ? void 0 : _e.onMatchSettled({
1158
996
  router: this,
1159
997
  match: this.getMatch(matchId)
1160
998
  });
1161
999
  } catch (err) {
1162
- batch(() => {
1163
- updateMatch(matchId, (prev) => ({
1164
- ...prev,
1165
- loaderPromise: void 0
1166
- }));
1167
- executeHead();
1168
- });
1000
+ const head = await executeHead();
1001
+ updateMatch(matchId, (prev) => ({
1002
+ ...prev,
1003
+ loaderPromise: void 0,
1004
+ ...head
1005
+ }));
1169
1006
  handleRedirectAndNotFound(this.getMatch(matchId), err);
1170
1007
  }
1171
1008
  };
@@ -1185,15 +1022,19 @@ class RouterCore {
1185
1022
  loaderPromise: void 0
1186
1023
  }));
1187
1024
  } catch (err) {
1188
- if (isResolvedRedirect(err)) {
1189
- await this.navigate(err);
1025
+ if (isRedirect(err)) {
1026
+ await this.navigate(err.options);
1190
1027
  }
1191
1028
  }
1192
1029
  })();
1193
1030
  } else if (status !== "success" || loaderShouldRunAsync && sync) {
1194
1031
  await runLoader();
1195
1032
  } else {
1196
- executeHead();
1033
+ const head = await executeHead();
1034
+ updateMatch(matchId, (prev) => ({
1035
+ ...prev,
1036
+ ...head
1037
+ }));
1197
1038
  }
1198
1039
  }
1199
1040
  if (!loaderIsRunningAsync) {
@@ -1252,10 +1093,13 @@ class RouterCore {
1252
1093
  });
1253
1094
  return this.load({ sync: opts == null ? void 0 : opts.sync });
1254
1095
  };
1255
- this.resolveRedirect = (err) => {
1256
- const redirect = err;
1257
- if (!redirect.href) {
1258
- redirect.href = this.buildLocation(redirect).href;
1096
+ this.resolveRedirect = (redirect) => {
1097
+ if (!redirect.options.href) {
1098
+ redirect.options.href = this.buildLocation(redirect.options).href;
1099
+ redirect.headers.set("Location", redirect.options.href);
1100
+ }
1101
+ if (!redirect.headers.get("Location")) {
1102
+ redirect.headers.set("Location", redirect.options.href);
1259
1103
  }
1260
1104
  return redirect;
1261
1105
  };
@@ -1357,11 +1201,11 @@ class RouterCore {
1357
1201
  return matches;
1358
1202
  } catch (err) {
1359
1203
  if (isRedirect(err)) {
1360
- if (err.reloadDocument) {
1204
+ if (err.options.reloadDocument) {
1361
1205
  return void 0;
1362
1206
  }
1363
1207
  return await this.preloadRoute({
1364
- ...err,
1208
+ ...err.options,
1365
1209
  _fromLocation: next
1366
1210
  });
1367
1211
  }
@@ -1465,9 +1309,10 @@ class RouterCore {
1465
1309
  return this.routesById;
1466
1310
  }
1467
1311
  matchRoutesInternal(next, opts) {
1312
+ var _a;
1468
1313
  const { foundRoute, matchedRoutes, routeParams } = this.getMatchedRoutes(
1469
- next,
1470
- opts == null ? void 0 : opts.dest
1314
+ next.pathname,
1315
+ (_a = opts == null ? void 0 : opts.dest) == null ? void 0 : _a.to
1471
1316
  );
1472
1317
  let isGlobalNotFound = false;
1473
1318
  if (
@@ -1498,9 +1343,9 @@ class RouterCore {
1498
1343
  return rootRouteId;
1499
1344
  })();
1500
1345
  const parseErrors = matchedRoutes.map((route) => {
1501
- var _a;
1346
+ var _a2;
1502
1347
  let parsedParamsError;
1503
- const parseParams = ((_a = route.options.params) == null ? void 0 : _a.parse) ?? route.options.parseParams;
1348
+ const parseParams = ((_a2 = route.options.params) == null ? void 0 : _a2.parse) ?? route.options.parseParams;
1504
1349
  if (parseParams) {
1505
1350
  try {
1506
1351
  const parsedParams = parseParams(routeParams);
@@ -1524,7 +1369,7 @@ class RouterCore {
1524
1369
  return parentContext;
1525
1370
  };
1526
1371
  matchedRoutes.forEach((route, index) => {
1527
- var _a, _b;
1372
+ var _a2, _b;
1528
1373
  const parentMatch = matches[index - 1];
1529
1374
  const [preMatchSearch, strictMatchSearch, searchError] = (() => {
1530
1375
  const parentSearch = (parentMatch == null ? void 0 : parentMatch.search) ?? next.search;
@@ -1552,7 +1397,7 @@ class RouterCore {
1552
1397
  return [parentSearch, {}, searchParamError];
1553
1398
  }
1554
1399
  })();
1555
- const loaderDeps = ((_b = (_a = route.options).loaderDeps) == null ? void 0 : _b.call(_a, {
1400
+ const loaderDeps = ((_b = (_a2 = route.options).loaderDeps) == null ? void 0 : _b.call(_a2, {
1556
1401
  search: preMatchSearch
1557
1402
  })) ?? "";
1558
1403
  const loaderDepsHash = loaderDeps ? JSON.stringify(loaderDeps) : "";
@@ -1630,7 +1475,7 @@ class RouterCore {
1630
1475
  matches.push(match);
1631
1476
  });
1632
1477
  matches.forEach((match, index) => {
1633
- var _a, _b;
1478
+ var _a2, _b;
1634
1479
  const route = this.looseRoutesById[match.routeId];
1635
1480
  const existingMatch = this.getMatch(match.id);
1636
1481
  if (!existingMatch && (opts == null ? void 0 : opts._buildLocation) !== true) {
@@ -1648,7 +1493,7 @@ class RouterCore {
1648
1493
  preload: !!match.preload,
1649
1494
  matches
1650
1495
  };
1651
- match.__routeContext = ((_b = (_a = route.options).context) == null ? void 0 : _b.call(_a, contextFnContext)) ?? {};
1496
+ match.__routeContext = ((_b = (_a2 = route.options).context) == null ? void 0 : _b.call(_a2, contextFnContext)) ?? {};
1652
1497
  match.context = {
1653
1498
  ...parentContext,
1654
1499
  ...match.__routeContext,
@@ -1718,6 +1563,219 @@ function routeNeedsPreload(route) {
1718
1563
  }
1719
1564
  return false;
1720
1565
  }
1566
+ function processRouteTree({
1567
+ routeTree,
1568
+ initRoute
1569
+ }) {
1570
+ const routesById = {};
1571
+ const routesByPath = {};
1572
+ const recurseRoutes = (childRoutes) => {
1573
+ childRoutes.forEach((childRoute, i) => {
1574
+ initRoute == null ? void 0 : initRoute(childRoute, i);
1575
+ const existingRoute = routesById[childRoute.id];
1576
+ invariant(
1577
+ !existingRoute,
1578
+ `Duplicate routes found with id: ${String(childRoute.id)}`
1579
+ );
1580
+ routesById[childRoute.id] = childRoute;
1581
+ if (!childRoute.isRoot && childRoute.path) {
1582
+ const trimmedFullPath = trimPathRight(childRoute.fullPath);
1583
+ if (!routesByPath[trimmedFullPath] || childRoute.fullPath.endsWith("/")) {
1584
+ routesByPath[trimmedFullPath] = childRoute;
1585
+ }
1586
+ }
1587
+ const children = childRoute.children;
1588
+ if (children == null ? void 0 : children.length) {
1589
+ recurseRoutes(children);
1590
+ }
1591
+ });
1592
+ };
1593
+ recurseRoutes([routeTree]);
1594
+ const scoredRoutes = [];
1595
+ const routes = Object.values(routesById);
1596
+ routes.forEach((d, i) => {
1597
+ var _a;
1598
+ if (d.isRoot || !d.path) {
1599
+ return;
1600
+ }
1601
+ const trimmed = trimPathLeft(d.fullPath);
1602
+ const parsed = parsePathname(trimmed);
1603
+ while (parsed.length > 1 && ((_a = parsed[0]) == null ? void 0 : _a.value) === "/") {
1604
+ parsed.shift();
1605
+ }
1606
+ const scores = parsed.map((segment) => {
1607
+ if (segment.value === "/") {
1608
+ return 0.75;
1609
+ }
1610
+ if (segment.type === "param" && segment.prefixSegment && segment.suffixSegment) {
1611
+ return 0.55;
1612
+ }
1613
+ if (segment.type === "param" && segment.prefixSegment) {
1614
+ return 0.52;
1615
+ }
1616
+ if (segment.type === "param" && segment.suffixSegment) {
1617
+ return 0.51;
1618
+ }
1619
+ if (segment.type === "param") {
1620
+ return 0.5;
1621
+ }
1622
+ if (segment.type === "wildcard" && segment.prefixSegment && segment.suffixSegment) {
1623
+ return 0.3;
1624
+ }
1625
+ if (segment.type === "wildcard" && segment.prefixSegment) {
1626
+ return 0.27;
1627
+ }
1628
+ if (segment.type === "wildcard" && segment.suffixSegment) {
1629
+ return 0.26;
1630
+ }
1631
+ if (segment.type === "wildcard") {
1632
+ return 0.25;
1633
+ }
1634
+ return 1;
1635
+ });
1636
+ scoredRoutes.push({ child: d, trimmed, parsed, index: i, scores });
1637
+ });
1638
+ const flatRoutes = scoredRoutes.sort((a, b) => {
1639
+ const minLength = Math.min(a.scores.length, b.scores.length);
1640
+ for (let i = 0; i < minLength; i++) {
1641
+ if (a.scores[i] !== b.scores[i]) {
1642
+ return b.scores[i] - a.scores[i];
1643
+ }
1644
+ }
1645
+ if (a.scores.length !== b.scores.length) {
1646
+ return b.scores.length - a.scores.length;
1647
+ }
1648
+ for (let i = 0; i < minLength; i++) {
1649
+ if (a.parsed[i].value !== b.parsed[i].value) {
1650
+ return a.parsed[i].value > b.parsed[i].value ? 1 : -1;
1651
+ }
1652
+ }
1653
+ return a.index - b.index;
1654
+ }).map((d, i) => {
1655
+ d.child.rank = i;
1656
+ return d.child;
1657
+ });
1658
+ return { routesById, routesByPath, flatRoutes };
1659
+ }
1660
+ function getMatchedRoutes({
1661
+ pathname,
1662
+ routePathname,
1663
+ basepath,
1664
+ caseSensitive,
1665
+ routesByPath,
1666
+ routesById,
1667
+ flatRoutes
1668
+ }) {
1669
+ let routeParams = {};
1670
+ const trimmedPath = trimPathRight(pathname);
1671
+ const getMatchedParams = (route) => {
1672
+ var _a;
1673
+ const result = matchPathname(basepath, trimmedPath, {
1674
+ to: route.fullPath,
1675
+ caseSensitive: ((_a = route.options) == null ? void 0 : _a.caseSensitive) ?? caseSensitive,
1676
+ fuzzy: true
1677
+ });
1678
+ return result;
1679
+ };
1680
+ let foundRoute = routePathname !== void 0 ? routesByPath[routePathname] : void 0;
1681
+ if (foundRoute) {
1682
+ routeParams = getMatchedParams(foundRoute);
1683
+ } else {
1684
+ foundRoute = flatRoutes.find((route) => {
1685
+ const matchedParams = getMatchedParams(route);
1686
+ if (matchedParams) {
1687
+ routeParams = matchedParams;
1688
+ return true;
1689
+ }
1690
+ return false;
1691
+ });
1692
+ }
1693
+ let routeCursor = foundRoute || routesById[rootRouteId];
1694
+ const matchedRoutes = [routeCursor];
1695
+ while (routeCursor.parentRoute) {
1696
+ routeCursor = routeCursor.parentRoute;
1697
+ matchedRoutes.unshift(routeCursor);
1698
+ }
1699
+ return { matchedRoutes, routeParams, foundRoute };
1700
+ }
1701
+ function applySearchMiddleware({
1702
+ search,
1703
+ dest,
1704
+ destRoutes,
1705
+ _includeValidateSearch
1706
+ }) {
1707
+ const allMiddlewares = destRoutes.reduce(
1708
+ (acc, route) => {
1709
+ var _a;
1710
+ const middlewares = [];
1711
+ if ("search" in route.options) {
1712
+ if ((_a = route.options.search) == null ? void 0 : _a.middlewares) {
1713
+ middlewares.push(...route.options.search.middlewares);
1714
+ }
1715
+ } else if (route.options.preSearchFilters || route.options.postSearchFilters) {
1716
+ const legacyMiddleware = ({
1717
+ search: search2,
1718
+ next
1719
+ }) => {
1720
+ let nextSearch = search2;
1721
+ if ("preSearchFilters" in route.options && route.options.preSearchFilters) {
1722
+ nextSearch = route.options.preSearchFilters.reduce(
1723
+ (prev, next2) => next2(prev),
1724
+ search2
1725
+ );
1726
+ }
1727
+ const result = next(nextSearch);
1728
+ if ("postSearchFilters" in route.options && route.options.postSearchFilters) {
1729
+ return route.options.postSearchFilters.reduce(
1730
+ (prev, next2) => next2(prev),
1731
+ result
1732
+ );
1733
+ }
1734
+ return result;
1735
+ };
1736
+ middlewares.push(legacyMiddleware);
1737
+ }
1738
+ if (_includeValidateSearch && route.options.validateSearch) {
1739
+ const validate = ({ search: search2, next }) => {
1740
+ const result = next(search2);
1741
+ try {
1742
+ const validatedSearch = {
1743
+ ...result,
1744
+ ...validateSearch(route.options.validateSearch, result) ?? {}
1745
+ };
1746
+ return validatedSearch;
1747
+ } catch {
1748
+ return result;
1749
+ }
1750
+ };
1751
+ middlewares.push(validate);
1752
+ }
1753
+ return acc.concat(middlewares);
1754
+ },
1755
+ []
1756
+ ) ?? [];
1757
+ const final = ({ search: search2 }) => {
1758
+ if (!dest.search) {
1759
+ return {};
1760
+ }
1761
+ if (dest.search === true) {
1762
+ return search2;
1763
+ }
1764
+ return functionalUpdate(dest.search, search2);
1765
+ };
1766
+ allMiddlewares.push(final);
1767
+ const applyNext = (index, currentSearch) => {
1768
+ if (index >= allMiddlewares.length) {
1769
+ return currentSearch;
1770
+ }
1771
+ const middleware = allMiddlewares[index];
1772
+ const next = (newSearch) => {
1773
+ return applyNext(index + 1, newSearch);
1774
+ };
1775
+ return middleware({ search: currentSearch, next });
1776
+ };
1777
+ return applyNext(0, search);
1778
+ }
1721
1779
  export {
1722
1780
  PathParamError,
1723
1781
  RouterCore,
@@ -1726,6 +1784,8 @@ export {
1726
1784
  defaultSerializeError,
1727
1785
  getInitialRouterState,
1728
1786
  getLocationChangeInfo,
1729
- lazyFn
1787
+ getMatchedRoutes,
1788
+ lazyFn,
1789
+ processRouteTree
1730
1790
  };
1731
1791
  //# sourceMappingURL=router.js.map