@tanstack/router-core 0.0.1-beta.159 → 0.0.1-beta.160

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.
@@ -151,7 +151,7 @@ type HydrationCtx = {
151
151
  router: DehydratedRouter;
152
152
  payload: Record<string, any>;
153
153
  };
154
- interface RouteMatch<TRouteTree extends AnyRoute = RegisteredRouter['routeTree'], TRoute extends AnyRoute = Route> {
154
+ interface RouteMatch<TRouteTree extends AnyRoute = AnyRoute, TRoute extends AnyRoute = AnyRoute> {
155
155
  id: string;
156
156
  key?: string;
157
157
  routeId: string;
@@ -210,7 +210,7 @@ interface RouterOptions<TRouteTree extends AnyRoute, TDehydrated extends Record<
210
210
  dehydrate?: () => TDehydrated;
211
211
  hydrate?: (dehydrated: TDehydrated) => void;
212
212
  }
213
- interface RouterState<TRouteTree extends AnyRoute = RegisteredRouter['routeTree']> {
213
+ interface RouterState<TRouteTree extends AnyRoute = AnyRoute> {
214
214
  status: 'idle' | 'pending';
215
215
  isFetching: boolean;
216
216
  matchesById: Record<string, RouteMatch<TRouteTree, AnyRoute>>;
@@ -254,7 +254,7 @@ interface DehydratedRouter {
254
254
  }
255
255
  type RouterConstructorOptions<TRouteTree extends AnyRoute, TDehydrated extends Record<string, any>> = Omit<RouterOptions<TRouteTree, TDehydrated>, 'context'> & RouterContextOptions<TRouteTree>;
256
256
  declare const componentTypes: readonly ["component", "errorComponent", "pendingComponent"];
257
- declare class Router<TRouteTree extends AnyRoute = RegisteredRouter['routeTree'], TDehydrated extends Record<string, any> = Record<string, any>> {
257
+ declare class Router<TRouteTree extends AnyRoute = AnyRoute, TDehydrated extends Record<string, any> = Record<string, any>> {
258
258
  #private;
259
259
  types: {
260
260
  RootRoute: TRouteTree;
@@ -375,7 +375,7 @@ type AnyRouteProps = RouteProps<any, any, any, any, any>;
375
375
  type ComponentPropsFromRoute<TRoute> = TRoute extends Route<infer TParentRoute, infer TPath, infer TFullPath, infer TCustomId, infer TId, infer TLoader, infer TSearchSchema, infer TFullSearchSchema, infer TParams, infer TAllParams, infer TParentContext, infer TAllParentContext, infer TRouteContext, infer TContext, infer TRouterContext, infer TChildren, infer TRouteTree> ? RouteProps<TLoader, TFullSearchSchema, TAllParams, TRouteContext, TContext> : never;
376
376
  type ComponentFromRoute<TRoute> = RegisteredRouteComponent<ComponentPropsFromRoute<TRoute>>;
377
377
  type RouteLoaderFromRoute<TRoute extends AnyRoute> = LoaderFn<TRoute['__types']['loader'], TRoute['__types']['searchSchema'], TRoute['__types']['fullSearchSchema'], TRoute['__types']['allParams'], TRoute['__types']['routeContext'], TRoute['__types']['context']>;
378
- type RouteProps<TLoader = unknown, TFullSearchSchema extends AnySearchSchema = AnySearchSchema, TAllParams extends AnyPathParams = AnyPathParams, TRouteContext extends AnyContext = AnyContext, TContext extends AnyContext = AnyContext> = {
378
+ type RouteProps<TLoader extends any = unknown, TFullSearchSchema extends AnySearchSchema = AnySearchSchema, TAllParams extends AnyPathParams = AnyPathParams, TRouteContext extends AnyContext = AnyContext, TContext extends AnyContext = AnyContext> = {
379
379
  useMatch: () => RouteMatch<any, any>;
380
380
  useLoader: () => UseLoaderResult<TLoader>;
381
381
  useSearch: <TStrict extends boolean = true, TSearch = TFullSearchSchema, TSelected = TSearch>(opts?: {
@@ -394,12 +394,14 @@ type RouteProps<TLoader = unknown, TFullSearchSchema extends AnySearchSchema = A
394
394
  };
395
395
  type RouteOptions<TParentRoute extends AnyRoute = AnyRoute, TCustomId extends string = string, TPath extends string = string, TLoader = unknown, TParentSearchSchema extends AnySearchSchema = {}, TSearchSchema extends AnySearchSchema = {}, TFullSearchSchema extends AnySearchSchema = TSearchSchema, TParams extends AnyPathParams = AnyPathParams, TAllParams extends AnyPathParams = TParams, TParentContext extends AnyContext = AnyContext, TAllParentContext extends AnyContext = AnyContext, TRouteContext extends RouteContext = RouteContext, TAllContext extends AnyContext = AnyContext> = BaseRouteOptions<TParentRoute, TCustomId, TPath, TLoader, TParentSearchSchema, TSearchSchema, TFullSearchSchema, TParams, TAllParams, TParentContext, TAllParentContext, TRouteContext, TAllContext> & UpdatableRouteOptions<TLoader, TSearchSchema, TFullSearchSchema, TAllParams, TRouteContext, TAllContext>;
396
396
  type ParamsFallback<TPath extends string, TParams> = unknown extends TParams ? Record<ParsePathParams<TPath>, string> : TParams;
397
- type BaseRouteOptions<TParentRoute extends AnyRoute = AnyRoute, TCustomId extends string = string, TPath extends string = string, TLoader = unknown, TParentSearchSchema extends AnySearchSchema = {}, TSearchSchema extends AnySearchSchema = {}, TFullSearchSchema extends AnySearchSchema = TSearchSchema, TParams = unknown, TAllParams = ParamsFallback<TPath, TParams>, TParentContext extends AnyContext = AnyContext, TAllParentContext extends AnyContext = AnyContext, TRouteContext extends RouteContext = RouteContext, TAllContext extends AnyContext = AnyContext> = RoutePathOptions<TCustomId, TPath> & {
397
+ type BaseRouteOptions<TParentRoute extends AnyRoute = AnyRoute, TCustomId extends string = string, TPath extends string = string, TLoader = unknown, TParentSearchSchema extends AnySearchSchema = {}, TSearchSchema extends AnySearchSchema = {}, TFullSearchSchema extends AnySearchSchema = TSearchSchema, TParams extends AnyPathParams = {}, TAllParams = ParamsFallback<TPath, TParams>, TParentContext extends AnyContext = AnyContext, TAllParentContext extends AnyContext = AnyContext, TRouteContext extends RouteContext = RouteContext, TAllContext extends AnyContext = AnyContext> = RoutePathOptions<TCustomId, TPath> & {
398
398
  layoutLimit?: string;
399
399
  getParentRoute: () => TParentRoute;
400
- validateSearch?: SearchSchemaValidator<TSearchSchema, TParentSearchSchema>;
400
+ validateSearch?: SearchSchemaValidator<TSearchSchema>;
401
401
  loader?: LoaderFn<TLoader, TSearchSchema, TFullSearchSchema, TAllParams, NoInfer<TRouteContext>, TAllContext>;
402
- } & ({
402
+ } & ([TLoader] extends [never] ? {
403
+ loader: 'Loaders must return a type other than never. If you are throwing a redirect() and not returning anything, return a redirect() instead.';
404
+ } : {}) & ({
403
405
  parseParams?: (rawParams: IsAny<TPath, any, Record<ParsePathParams<TPath>, string>>) => TParams extends Record<ParsePathParams<TPath>, any> ? TParams : 'parseParams must return an object';
404
406
  stringifyParams?: (params: NoInfer<ParamsFallback<TPath, TParams>>) => Record<ParsePathParams<TPath>, string>;
405
407
  } | {
@@ -457,14 +459,11 @@ type ParseParamsFn<TPath extends string, TParams> = (rawParams: IsAny<TPath, any
457
459
  type ParseParamsObj<TPath extends string, TParams> = {
458
460
  parse?: ParseParamsFn<TPath, TParams>;
459
461
  };
460
- type SearchSchemaValidator<TReturn, TParentSchema> = SearchSchemaValidatorObj<TReturn, TParentSchema> | SearchSchemaValidatorFn<TReturn, TParentSchema>;
461
- type SearchSchemaValidatorObj<TReturn, TParentSchema> = {
462
- parse?: SearchSchemaValidatorFn<TReturn, TParentSchema>;
462
+ type SearchSchemaValidator<TReturn> = SearchSchemaValidatorObj<TReturn> | SearchSchemaValidatorFn<TReturn>;
463
+ type SearchSchemaValidatorObj<TReturn> = {
464
+ parse?: SearchSchemaValidatorFn<TReturn>;
463
465
  };
464
- type SearchSchemaValidatorFn<TReturn, TParentSchema> = (searchObj: Record<string, unknown>) => {} extends TParentSchema ? TReturn : keyof TReturn extends keyof TParentSchema ? {
465
- error: 'Top level search params cannot be redefined by child routes!';
466
- keys: keyof TReturn & keyof TParentSchema;
467
- } : TReturn;
466
+ type SearchSchemaValidatorFn<TReturn> = (searchObj: Record<string, unknown>) => TReturn;
468
467
  type DefinedPathParamWarning = 'Path params cannot be redefined by child routes!';
469
468
  type ParentParams<TParentParams> = AnyPathParams extends TParentParams ? {} : {
470
469
  [Key in keyof TParentParams]?: DefinedPathParamWarning;
@@ -578,8 +578,6 @@
578
578
 
579
579
  const rootRouteId = '__root__';
580
580
 
581
- // | ParseParamsObj<TPath, TParams>
582
-
583
581
  // The parse type here allows a zod schema to be passed directly to the validator
584
582
 
585
583
  class Route {
@@ -812,25 +810,27 @@
812
810
  this.__store = new Store(getInitialRouterState(), {
813
811
  onUpdate: () => {
814
812
  const prev = this.state;
815
- this.state = this.__store.state;
816
- const matchesByIdChanged = prev.matchesById !== this.state.matchesById;
813
+ const next = this.__store.state;
814
+ console.log(Object.values(next.matchesById).find(d => d.status === 'error'));
815
+ const matchesByIdChanged = prev.matchesById !== next.matchesById;
817
816
  let matchesChanged;
818
817
  let pendingMatchesChanged;
819
818
  if (!matchesByIdChanged) {
820
- matchesChanged = prev.matchIds.length !== this.state.matchIds.length || prev.matchIds.some((d, i) => d !== this.state.matchIds[i]);
821
- pendingMatchesChanged = prev.pendingMatchIds.length !== this.state.pendingMatchIds.length || prev.pendingMatchIds.some((d, i) => d !== this.state.pendingMatchIds[i]);
819
+ matchesChanged = prev.matchIds.length !== next.matchIds.length || prev.matchIds.some((d, i) => d !== next.matchIds[i]);
820
+ pendingMatchesChanged = prev.pendingMatchIds.length !== next.pendingMatchIds.length || prev.pendingMatchIds.some((d, i) => d !== next.pendingMatchIds[i]);
822
821
  }
823
822
  if (matchesByIdChanged || matchesChanged) {
824
- this.state.matches = this.state.matchIds.map(id => {
825
- return this.state.matchesById[id];
823
+ next.matches = next.matchIds.map(id => {
824
+ return next.matchesById[id];
826
825
  });
827
826
  }
828
827
  if (matchesByIdChanged || pendingMatchesChanged) {
829
- this.state.pendingMatches = this.state.pendingMatchIds.map(id => {
830
- return this.state.matchesById[id];
828
+ next.pendingMatches = next.pendingMatchIds.map(id => {
829
+ return next.matchesById[id];
831
830
  });
832
831
  }
833
- this.state.isFetching = [...this.state.matches, ...this.state.pendingMatches].some(d => d.isFetching);
832
+ next.isFetching = [...next.matches, ...next.pendingMatches].some(d => d.isFetching);
833
+ this.state = next;
834
834
  },
835
835
  defaultPriority: 'low'
836
836
  });
@@ -911,11 +911,12 @@
911
911
  cancelMatch = id => {
912
912
  this.getRouteMatch(id)?.abortController?.abort();
913
913
  };
914
- safeLoad = opts => {
915
- return this.load(opts).catch(err => {
916
- // console.warn(err)
917
- // invariant(false, 'Encountered an error during router.load()! ☝️.')
918
- });
914
+ safeLoad = async opts => {
915
+ try {
916
+ return this.load(opts);
917
+ } catch (err) {
918
+ // Don't do anything
919
+ }
919
920
  };
920
921
  latestLoadPromise = Promise.resolve();
921
922
  load = async opts => {
@@ -952,11 +953,16 @@
952
953
  });
953
954
  try {
954
955
  // Load the matches
955
- await this.loadMatches(pendingMatches);
956
+ try {
957
+ await this.loadMatches(pendingMatches);
958
+ } catch (err) {
959
+ // swallow this error, since we'll display the
960
+ // errors on the route components
961
+ }
956
962
 
957
963
  // Only apply the latest transition
958
964
  if (latestPromise = checkLatest()) {
959
- return await latestPromise;
965
+ return latestPromise;
960
966
  }
961
967
  const prevLocation = this.state.resolvedLocation;
962
968
  this.__store.setState(s => ({
@@ -973,7 +979,7 @@
973
979
  } catch (err) {
974
980
  // Only apply the latest transition
975
981
  if (latestPromise = checkLatest()) {
976
- return await latestPromise;
982
+ return latestPromise;
977
983
  }
978
984
  reject(err);
979
985
  }
@@ -1232,6 +1238,7 @@
1232
1238
  throw errorHandlerErr;
1233
1239
  }
1234
1240
  }
1241
+ console.log('set error');
1235
1242
  this.setRouteMatch(match.id, s => ({
1236
1243
  ...s,
1237
1244
  error: err,
@@ -1284,19 +1291,8 @@
1284
1291
  const latest = this.getRouteMatch(match.id);
1285
1292
  return latest && latest.fetchedAt !== fetchedAt ? latest.loadPromise : undefined;
1286
1293
  };
1287
- const loadPromise = (async () => {
1294
+ const load = async () => {
1288
1295
  let latestPromise;
1289
- const componentsPromise = Promise.all(componentTypes.map(async type => {
1290
- const component = route.options[type];
1291
- if (component?.preload) {
1292
- await component.preload();
1293
- }
1294
- }));
1295
- const loaderPromise = route.options.loader?.({
1296
- ...match,
1297
- preload: !!opts?.preload,
1298
- parentMatchPromise
1299
- });
1300
1296
  const handleError = err => {
1301
1297
  if (isRedirect(err)) {
1302
1298
  if (!opts?.preload) {
@@ -1307,33 +1303,46 @@
1307
1303
  return false;
1308
1304
  };
1309
1305
  try {
1306
+ const componentsPromise = Promise.all(componentTypes.map(async type => {
1307
+ const component = route.options[type];
1308
+ if (component?.preload) {
1309
+ await component.preload();
1310
+ }
1311
+ }));
1312
+ const loaderPromise = route.options.loader?.({
1313
+ ...match,
1314
+ preload: !!opts?.preload,
1315
+ parentMatchPromise
1316
+ });
1310
1317
  const [_, loader] = await Promise.all([componentsPromise, loaderPromise]);
1311
1318
  if (latestPromise = checkLatest()) return await latestPromise;
1312
1319
  this.setRouteMatchData(match.id, () => loader, opts);
1313
- } catch (err) {
1320
+ } catch (loaderError) {
1314
1321
  if (latestPromise = checkLatest()) return await latestPromise;
1315
- if (handleError(err)) {
1316
- return;
1317
- }
1318
- const errorHandler = route.options.onLoadError ?? route.options.onError;
1319
- let caughtError = err;
1322
+ handleError(loaderError);
1323
+ let error = loaderError;
1320
1324
  try {
1321
- errorHandler?.(err);
1322
- } catch (errorHandlerErr) {
1323
- caughtError = errorHandlerErr;
1324
- if (handleError(errorHandlerErr)) {
1325
- return;
1325
+ if (route.options.onLoadError) {
1326
+ route.options.onLoadError?.(loaderError);
1327
+ } else {
1328
+ route.options.onError?.(loaderError);
1326
1329
  }
1330
+ } catch (errorHandlerErr) {
1331
+ error = errorHandlerErr;
1332
+ handleError(error);
1327
1333
  }
1334
+ console.log('set error');
1328
1335
  this.setRouteMatch(match.id, s => ({
1329
1336
  ...s,
1330
- error: caughtError,
1337
+ error: error,
1331
1338
  status: 'error',
1332
1339
  isFetching: false,
1333
1340
  updatedAt: Date.now()
1334
1341
  }));
1342
+ console.log(this.getRouteMatch(match.id)?.status);
1335
1343
  }
1336
- })();
1344
+ };
1345
+ const loadPromise = load();
1337
1346
  this.setRouteMatch(match.id, s => ({
1338
1347
  ...s,
1339
1348
  status: s.status !== 'success' ? 'pending' : s.status,
@@ -1757,13 +1766,18 @@
1757
1766
  return this.state.matchesById[id];
1758
1767
  };
1759
1768
  setRouteMatch = (id, updater) => {
1760
- this.__store.setState(prev => ({
1761
- ...prev,
1762
- matchesById: {
1763
- ...prev.matchesById,
1764
- [id]: updater(prev.matchesById[id])
1769
+ this.__store.setState(prev => {
1770
+ if (!prev.matchesById[id]) {
1771
+ console.warn(`No match found with id: ${id}`);
1765
1772
  }
1766
- }));
1773
+ return {
1774
+ ...prev,
1775
+ matchesById: {
1776
+ ...prev.matchesById,
1777
+ [id]: updater(prev.matchesById[id])
1778
+ }
1779
+ };
1780
+ });
1767
1781
  };
1768
1782
  setRouteMatchData = (id, updater, opts) => {
1769
1783
  const match = this.getRouteMatch(id);
@@ -1772,6 +1786,7 @@
1772
1786
  const updatedAt = opts?.updatedAt ?? Date.now();
1773
1787
  const preloadInvalidAt = updatedAt + (opts?.maxAge ?? route.options.preloadMaxAge ?? this.options.defaultPreloadMaxAge ?? 5000);
1774
1788
  const invalidAt = updatedAt + (opts?.maxAge ?? route.options.maxAge ?? this.options.defaultMaxAge ?? Infinity);
1789
+ console.log('set success');
1775
1790
  this.setRouteMatch(id, s => ({
1776
1791
  ...s,
1777
1792
  error: undefined,