@benjavicente/angular-router-experimental 1.142.11 → 1.142.13

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.
@@ -22,43 +22,6 @@ export declare function injectRouteErrorHandler<TRouter extends AnyRouter = Regi
22
22
  export { }
23
23
 
24
24
 
25
- declare module '@benjavicente/router-core' {
26
- interface UpdatableRouteOptionsExtensions {
27
- component?: RouteComponent;
28
- errorComponent?: false | null | undefined | ErrorRouteComponent;
29
- notFoundComponent?: NotFoundRouteComponent;
30
- pendingComponent?: RouteComponent;
31
- }
32
- interface RootRouteOptionsExtensions {
33
- shellComponent?: Angular.Type<{
34
- children: any;
35
- }>;
36
- }
37
- interface RouteExtensions<in out TId extends string, in out TFullPath extends string> {
38
- injectMatch: InjectMatchRoute<TId>;
39
- injectRouteContext: InjectRouteContextRoute<TId>;
40
- injectSearch: InjectSearchRoute<TId>;
41
- injectParams: InjectParamsRoute<TId>;
42
- injectLoaderDeps: InjectLoaderDepsRoute<TId>;
43
- injectLoaderData: InjectLoaderDataRoute<TId>;
44
- injectNavigate: () => UseNavigateResult<TFullPath>;
45
- }
46
- }
47
-
48
-
49
- declare module '@benjavicente/router-core' {
50
- interface LazyRoute<in out TRoute extends AnyRoute> {
51
- injectMatch: InjectMatchRoute<TRoute['id']>;
52
- injectRouteContext: InjectRouteContextRoute<TRoute['id']>;
53
- injectSearch: InjectSearchRoute<TRoute['id']>;
54
- injectParams: InjectParamsRoute<TRoute['id']>;
55
- injectLoaderDeps: InjectLoaderDepsRoute<TRoute['id']>;
56
- injectLoaderData: InjectLoaderDataRoute<TRoute['id']>;
57
- injectNavigate: () => UseNavigateResult<TRoute['fullPath']>;
58
- }
59
- }
60
-
61
-
62
25
  declare module '@benjavicente/router-core' {
63
26
  interface RouterOptionsExtensions {
64
27
  /**
@@ -96,8 +59,46 @@ declare module '@benjavicente/router-core' {
96
59
 
97
60
 
98
61
  declare module '@benjavicente/router-core' {
62
+ interface UpdatableRouteOptionsExtensions {
63
+ component?: RouteComponent;
64
+ errorComponent?: false | null | undefined | ErrorRouteComponent;
65
+ notFoundComponent?: NotFoundRouteComponent;
66
+ pendingComponent?: RouteComponent;
67
+ }
68
+ interface RootRouteOptionsExtensions {
69
+ }
70
+ interface RouteExtensions<in out TId extends string, in out TFullPath extends string> {
71
+ injectMatch: InjectMatchRoute<TId>;
72
+ injectRouteContext: InjectRouteContextRoute<TId>;
73
+ injectSearch: InjectSearchRoute<TId>;
74
+ injectParams: InjectParamsRoute<TId>;
75
+ injectLoaderDeps: InjectLoaderDepsRoute<TId>;
76
+ injectLoaderData: InjectLoaderDataRoute<TId>;
77
+ injectNavigate: () => UseNavigateResult<TFullPath>;
78
+ }
79
+ }
80
+
81
+
82
+ declare module '@benjavicente/router-core' {
83
+ interface LazyRoute<in out TRoute extends AnyRoute> {
84
+ injectMatch: InjectMatchRoute<TRoute['id']>;
85
+ injectRouteContext: InjectRouteContextRoute<TRoute['id']>;
86
+ injectSearch: InjectSearchRoute<TRoute['id']>;
87
+ injectParams: InjectParamsRoute<TRoute['id']>;
88
+ injectLoaderDeps: InjectLoaderDepsRoute<TRoute['id']>;
89
+ injectLoaderData: InjectLoaderDataRoute<TRoute['id']>;
90
+ injectNavigate: () => UseNavigateResult<TRoute['fullPath']>;
91
+ }
92
+ }
93
+
94
+
95
+ declare module '@benjavicente/router-core' {
96
+ interface RouterReadableStore<TValue> extends Readable<TValue> {
97
+ }
99
98
  interface RouterStores<in out TRouteTree extends AnyRoute> {
99
+ /** Maps each active routeId to the matchId of its child in the match tree. */
100
100
  childMatchIdByRouteId: RouterReadableStore<Record<string, string>>;
101
+ /** Maps each pending routeId to true for quick lookup. */
101
102
  pendingRouteIds: RouterReadableStore<Record<string, boolean>>;
102
103
  }
103
104
  }
@@ -28,6 +28,7 @@ import { HistoryAction } from '@benjavicente/history';
28
28
  import { HistoryLocation } from '@benjavicente/history';
29
29
  import { HistoryState } from '@benjavicente/history';
30
30
  import { InjectionToken } from '@angular/core';
31
+ import { Injector } from '@angular/core';
31
32
  import { InputSignal } from '@angular/core';
32
33
  import { isRedirect } from '@benjavicente/router-core';
33
34
  import { lazyFn } from '@benjavicente/router-core';
@@ -40,7 +41,6 @@ import { MakeRouteMatchUnion } from '@benjavicente/router-core';
40
41
  import { MaskOptions } from '@benjavicente/router-core';
41
42
  import { MatchRouteOptions } from '@benjavicente/router-core';
42
43
  import { NavigateOptions } from '@benjavicente/router-core';
43
- import { NoInfer as NoInfer_2 } from '@benjavicente/router-core';
44
44
  import { notFound } from '@benjavicente/router-core';
45
45
  import { NotFoundError } from '@benjavicente/router-core';
46
46
  import { ParsedLocation } from '@benjavicente/router-core';
@@ -158,6 +158,10 @@ export declare function buildManagedDocumentContent(router: AnyRouter): ManagedD
158
158
  */
159
159
  export declare function buildMatchManagedDocumentContent(router: AnyRouter): ManagedDocumentContent;
160
160
 
161
+ declare type CompatibilityInjectStoreOptions<TSelected> = CreateSignalOptions<TSelected> & {
162
+ injector?: Injector;
163
+ };
164
+
161
165
  export { createBrowserHistory }
162
166
 
163
167
  export { CreateFileRoute }
@@ -228,6 +232,8 @@ export declare function injectErrorState(): {
228
232
  };
229
233
  };
230
234
 
235
+ export declare function injectIsShell(): Signal<boolean>;
236
+
231
237
  export declare function injectLoaderData<TRouter extends AnyRouter = RegisteredRouter, const TFrom extends string | undefined = undefined, TStrict extends boolean = true, TSelected = unknown>(opts: InjectLoaderDataOptions<TRouter, TFrom, TStrict, TSelected>): Angular.Signal<UseLoaderDataResult<TRouter, TFrom, TStrict, TSelected>>;
232
238
 
233
239
  export declare interface InjectLoaderDataBaseOptions<TRouter extends AnyRouter, TFrom, TStrict extends boolean, TSelected> {
@@ -277,7 +283,7 @@ export declare type InjectMatchResult<TRouter extends AnyRouter, TFrom, TStrict
277
283
 
278
284
  export declare type InjectMatchRoute<out TFrom> = <TRouter extends AnyRouter = RegisteredRouter, TSelected = unknown>(opts?: InjectMatchBaseOptions<TRouter, TFrom, true, true, TSelected>) => Angular.Signal<InjectMatchResult<TRouter, TFrom, true, TSelected>>;
279
285
 
280
- export declare function injectMatchRoute<TRouter extends AnyRouter = RegisteredRouter>(): <const TFrom extends string = string, const TTo extends string | undefined = undefined, const TMaskFrom extends string = TFrom, const TMaskTo extends string = "">(opts: InjectMatchRouteOptions<TRouter, TFrom, TTo, TMaskFrom, TMaskTo>) => Angular.Signal<false | Expand<ResolveRoute<TRouter, TFrom, NoInfer_2<TTo>>["types"]["allParams"]>>;
286
+ export declare function injectMatchRoute<TRouter extends AnyRouter = RegisteredRouter>(): <const TFrom extends string = string, const TTo extends string | undefined = undefined, const TMaskFrom extends string = TFrom, const TMaskTo extends string = "">(opts: InjectMatchRouteOptions<TRouter, TFrom, TTo, TMaskFrom, TMaskTo>) => Angular.Signal<false | Expand<ResolveRoute<TRouter, TFrom, TTo>["types"]["allParams"]>>;
281
287
 
282
288
  export declare type InjectMatchRouteOptions<TRouter extends AnyRouter = RegisteredRouter, TFrom extends string = string, TTo extends string | undefined = undefined, TMaskFrom extends string = TFrom, TMaskTo extends string = ''> = ToSubOptionsProps<TRouter, TFrom, TTo> & DeepPartial<MakeOptionalSearchParams<TRouter, TFrom, TTo>> & DeepPartial<MakeOptionalPathParams<TRouter, TFrom, TTo>> & MaskOptions<TRouter, TMaskFrom, TMaskTo> & MatchRouteOptions;
283
289
 
@@ -335,7 +341,9 @@ export declare type InjectSearchRoute<out TFrom> = <TRouter extends AnyRouter =
335
341
  */
336
342
  export declare function injectSsrScrollRestorationScript(): void;
337
343
 
338
- export declare function injectStore<TState, TSelected = NoInfer<TState>>(storeOrStoreSignal: ReadableStore<TState> | (() => ReadableStore<TState>), selector?: (state: NoInfer<TState>) => TSelected, options?: CreateSignalOptions<TSelected>): Signal<TSelected>;
344
+ export declare function injectStore<TState, TSelected = NoInfer<TState>>(store: SelectionSource<TState>, selector?: (state: NoInfer<TState>) => TSelected, options?: CompatibilityInjectStoreOptions<TSelected>): Signal<TSelected>;
345
+
346
+ export declare function injectStore<TState, TSelected = NoInfer<TState>>(store: SelectionSource<TState> | (() => SelectionSource<TState>), selector?: (state: NoInfer<TState>) => TSelected, options?: CompatibilityInjectStoreOptions<TSelected>): Signal<TSelected>;
339
347
 
340
348
  declare class InternalFileRouteFactory<TFilePath extends keyof FileRoutesByPath, TParentRoute extends AnyRoute = FileRoutesByPath[TFilePath]['parentRoute'], TId extends RouteConstraints['TId'] = FileRoutesByPath[TFilePath]['id'], TPath extends RouteConstraints['TPath'] = FileRoutesByPath[TFilePath]['path'], TFullPath extends RouteConstraints['TFullPath'] = FileRoutesByPath[TFilePath]['fullPath']> {
341
349
  createRoute: <TRegister = Register, TSearchValidator = undefined, TParams = ResolveParams<TPath>, TRouteContextFn = AnyContext, TBeforeLoadFn = AnyContext, TLoaderDeps extends Record<string, any> = {}, TLoaderFn = undefined, TChildren = unknown, TSSR = unknown, TMiddlewares = unknown, THandlers = undefined>(options?: FileBaseRouteOptions<TRegister, TParentRoute, TId, TPath, TSearchValidator, TParams, TLoaderDeps, TLoaderFn, AnyContext, TRouteContextFn, TBeforeLoadFn, AnyContext, TSSR, TMiddlewares, THandlers> & UpdatableRouteOptions<TParentRoute, TId, TFullPath, TParams, TSearchValidator, TLoaderFn, TLoaderDeps, AnyContext, TRouteContextFn, TBeforeLoadFn>) => Route_2<TRegister, TParentRoute, TPath, TFullPath, TFilePath, TId, TSearchValidator, TParams, AnyContext, TRouteContextFn, TBeforeLoadFn, TLoaderDeps, TLoaderFn, TChildren, unknown, TSSR, TMiddlewares, THandlers>;
@@ -484,10 +492,6 @@ export declare function provideTanstackRouter({ router, context, options, }: Tan
484
492
  useFactory?: undefined;
485
493
  })[];
486
494
 
487
- declare type ReadableStore<TState> = {
488
- state: TState;
489
- };
490
-
491
495
  export { redirect }
492
496
 
493
497
  export { Register }
@@ -495,7 +499,7 @@ export { Register }
495
499
  export { RegisteredRouter }
496
500
 
497
501
  declare type RenderValue = {
498
- key?: string;
502
+ key?: unknown;
499
503
  component: Type<any> | null | undefined;
500
504
  inputs?: Record<string, () => unknown>;
501
505
  providers?: Array<Provider>;
@@ -556,6 +560,7 @@ export declare class RouteMatch {
556
560
  matchId: InputSignal<string>;
557
561
  router: AnyRouter;
558
562
  match: Signal<AnyRouteMatch | undefined>;
563
+ private matchSignal;
559
564
  matchData: Signal<{
560
565
  key: string | undefined;
561
566
  route: AnyRoute;
@@ -570,7 +575,7 @@ export declare class RouteMatch {
570
575
  hasPendingMatch: Signal<boolean>;
571
576
  pendingRouteIds: Signal<Record<string, boolean>>;
572
577
  nearestMatchContext: NearestMatchContextValue;
573
- isCatchingError: Signal<boolean>;
578
+ catchingErrorMatch: Signal<AnyRouteMatch | undefined>;
574
579
  render: void;
575
580
  onRendered: void;
576
581
  }
@@ -604,6 +609,13 @@ export declare class RouterProvider<TRouter extends AnyRouter = RegisteredRouter
604
609
 
605
610
  export { RouterState }
606
611
 
612
+ declare type SelectionSource<T> = {
613
+ get: () => T;
614
+ subscribe: (listener: (value: T) => void) => {
615
+ unsubscribe: () => void;
616
+ };
617
+ };
618
+
607
619
  export declare type ShouldBlockFn<TRouter extends AnyRouter = RegisteredRouter> = (args: ShouldBlockFnArgs<TRouter>) => boolean | Promise<boolean>;
608
620
 
609
621
  declare type ShouldBlockFnArgs<TRouter extends AnyRouter = RegisteredRouter> = {
@@ -645,43 +657,6 @@ export declare type UseBlockerOpts<TRouter extends AnyRouter = RegisteredRouter,
645
657
  export { }
646
658
 
647
659
 
648
- declare module '@benjavicente/router-core' {
649
- interface UpdatableRouteOptionsExtensions {
650
- component?: RouteComponent;
651
- errorComponent?: false | null | undefined | ErrorRouteComponent;
652
- notFoundComponent?: NotFoundRouteComponent;
653
- pendingComponent?: RouteComponent;
654
- }
655
- interface RootRouteOptionsExtensions {
656
- shellComponent?: Angular.Type<{
657
- children: any;
658
- }>;
659
- }
660
- interface RouteExtensions<in out TId extends string, in out TFullPath extends string> {
661
- injectMatch: InjectMatchRoute<TId>;
662
- injectRouteContext: InjectRouteContextRoute<TId>;
663
- injectSearch: InjectSearchRoute<TId>;
664
- injectParams: InjectParamsRoute<TId>;
665
- injectLoaderDeps: InjectLoaderDepsRoute<TId>;
666
- injectLoaderData: InjectLoaderDataRoute<TId>;
667
- injectNavigate: () => UseNavigateResult<TFullPath>;
668
- }
669
- }
670
-
671
-
672
- declare module '@benjavicente/router-core' {
673
- interface LazyRoute<in out TRoute extends AnyRoute> {
674
- injectMatch: InjectMatchRoute<TRoute['id']>;
675
- injectRouteContext: InjectRouteContextRoute<TRoute['id']>;
676
- injectSearch: InjectSearchRoute<TRoute['id']>;
677
- injectParams: InjectParamsRoute<TRoute['id']>;
678
- injectLoaderDeps: InjectLoaderDepsRoute<TRoute['id']>;
679
- injectLoaderData: InjectLoaderDataRoute<TRoute['id']>;
680
- injectNavigate: () => UseNavigateResult<TRoute['fullPath']>;
681
- }
682
- }
683
-
684
-
685
660
  declare module '@benjavicente/router-core' {
686
661
  interface RouterOptionsExtensions {
687
662
  /**
@@ -719,8 +694,46 @@ declare module '@benjavicente/router-core' {
719
694
 
720
695
 
721
696
  declare module '@benjavicente/router-core' {
697
+ interface UpdatableRouteOptionsExtensions {
698
+ component?: RouteComponent;
699
+ errorComponent?: false | null | undefined | ErrorRouteComponent;
700
+ notFoundComponent?: NotFoundRouteComponent;
701
+ pendingComponent?: RouteComponent;
702
+ }
703
+ interface RootRouteOptionsExtensions {
704
+ }
705
+ interface RouteExtensions<in out TId extends string, in out TFullPath extends string> {
706
+ injectMatch: InjectMatchRoute<TId>;
707
+ injectRouteContext: InjectRouteContextRoute<TId>;
708
+ injectSearch: InjectSearchRoute<TId>;
709
+ injectParams: InjectParamsRoute<TId>;
710
+ injectLoaderDeps: InjectLoaderDepsRoute<TId>;
711
+ injectLoaderData: InjectLoaderDataRoute<TId>;
712
+ injectNavigate: () => UseNavigateResult<TFullPath>;
713
+ }
714
+ }
715
+
716
+
717
+ declare module '@benjavicente/router-core' {
718
+ interface LazyRoute<in out TRoute extends AnyRoute> {
719
+ injectMatch: InjectMatchRoute<TRoute['id']>;
720
+ injectRouteContext: InjectRouteContextRoute<TRoute['id']>;
721
+ injectSearch: InjectSearchRoute<TRoute['id']>;
722
+ injectParams: InjectParamsRoute<TRoute['id']>;
723
+ injectLoaderDeps: InjectLoaderDepsRoute<TRoute['id']>;
724
+ injectLoaderData: InjectLoaderDataRoute<TRoute['id']>;
725
+ injectNavigate: () => UseNavigateResult<TRoute['fullPath']>;
726
+ }
727
+ }
728
+
729
+
730
+ declare module '@benjavicente/router-core' {
731
+ interface RouterReadableStore<TValue> extends Readable<TValue> {
732
+ }
722
733
  interface RouterStores<in out TRouteTree extends AnyRoute> {
734
+ /** Maps each active routeId to the matchId of its child in the match tree. */
723
735
  childMatchIdByRouteId: RouterReadableStore<Record<string, string>>;
736
+ /** Maps each pending routeId to true for quick lookup. */
724
737
  pendingRouteIds: RouterReadableStore<Record<string, boolean>>;
725
738
  }
726
739
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@benjavicente/angular-router-experimental",
3
- "version": "1.142.11",
3
+ "version": "1.142.13",
4
4
  "description": "Modern and scalable routing for Angular applications",
5
5
  "author": "Tanner Linsley",
6
6
  "license": "MIT",
@@ -50,10 +50,11 @@
50
50
  "node": ">=20.19"
51
51
  },
52
52
  "dependencies": {
53
+ "@tanstack/store": "^0.9.3",
53
54
  "isbot": "^5.1.22",
54
55
  "tslib": "^2.3.0",
55
- "@benjavicente/history": "1.161.6",
56
- "@benjavicente/router-core": "1.168.9"
56
+ "@benjavicente/router-core": "1.169.2",
57
+ "@benjavicente/history": "1.161.6"
57
58
  },
58
59
  "devDependencies": {
59
60
  "@analogjs/vitest-angular": "^2.2.1",
package/src/Match.ts CHANGED
@@ -16,14 +16,40 @@ import {
16
16
  rootRouteId,
17
17
  } from '@benjavicente/router-core'
18
18
  import { injectRouter } from './injectRouter'
19
- import { injectStore } from './injectStore'
19
+ import { injectStore } from './store/injectStore'
20
20
  import { DefaultNotFoundComponent } from './DefaultNotFound'
21
21
  import { MATCH_CONTEXT_INJECTOR_TOKEN } from './matchInjectorToken'
22
22
  import { injectRender } from './renderer/injectRender'
23
23
  import { ERROR_STATE_INJECTOR_TOKEN } from './injectErrorState'
24
- import { injectIsCatchingError } from './renderer/injectIsCatchingError'
24
+ import { injectCatchingErrorMatch } from './renderer/injectIsCatchingError'
25
25
  import type { Signal } from '@angular/core'
26
26
  import type { NearestMatchContextValue } from './matchInjectorToken'
27
+ import type { AnyRouteMatch } from '@benjavicente/router-core'
28
+
29
+ const dummyMatchStore = {
30
+ get: () => undefined as AnyRouteMatch | undefined,
31
+ subscribe: () => ({ unsubscribe: () => {} }),
32
+ }
33
+
34
+ @Component({
35
+ selector: 'tanstack-router-default-error',
36
+ template: `
37
+ <div role="alert">
38
+ <p>Something went wrong.</p>
39
+ <pre>{{ message() }}</pre>
40
+ </div>
41
+ `,
42
+ standalone: true,
43
+ })
44
+ export class DefaultErrorComponent {
45
+ state = inject(ERROR_STATE_INJECTOR_TOKEN)
46
+
47
+ message = computed(() => {
48
+ const error = this.state.error
49
+ if (error instanceof Error) return error.stack ?? error.message
50
+ return String(error)
51
+ })
52
+ }
27
53
 
28
54
  function injectOnRendered({
29
55
  parentRouteIsRoot,
@@ -35,7 +61,7 @@ function injectOnRendered({
35
61
  const destroyRef = inject(DestroyRef)
36
62
  const location = injectStore(
37
63
  router.stores.resolvedLocation,
38
- (resolvedLocation) => resolvedLocation?.state.__TSR_key,
64
+ (resolvedLocation) => resolvedLocation?.state?.__TSR_key,
39
65
  )
40
66
  const loadedAt = injectStore(router.stores.loadedAt, (value) => value)
41
67
 
@@ -68,8 +94,8 @@ function injectOnRendered({
68
94
  router.emit({
69
95
  type: 'onRendered',
70
96
  ...getLocationChangeInfo(
71
- router.stores.location.state,
72
- router.stores.resolvedLocation.state,
97
+ router.stores.location.get(),
98
+ router.stores.resolvedLocation.get(),
73
99
  ),
74
100
  })
75
101
  },
@@ -84,6 +110,7 @@ function injectOnRendered({
84
110
  template: '',
85
111
  standalone: true,
86
112
  host: {
113
+ hidden: '',
87
114
  '[attr.data-matchId]': 'matchId()',
88
115
  },
89
116
  })
@@ -93,11 +120,12 @@ export class RouteMatch {
93
120
  router = injectRouter()
94
121
 
95
122
  match = computed(() => {
96
- const matchId = this.matchId()
97
- return matchId
98
- ? this.router.stores.activeMatchStoresById.get(matchId)?.state
99
- : undefined
123
+ return this.matchSignal()
100
124
  })
125
+ private matchSignal = injectStore(
126
+ () => this.router.stores.matchStores.get(this.matchId()) ?? dummyMatchStore,
127
+ (value) => value,
128
+ )
101
129
 
102
130
  matchData = computed(() => {
103
131
  const match = this.match()
@@ -141,16 +169,17 @@ export class RouteMatch {
141
169
  return this.resolvedNoSsr() || !!match._displayPending
142
170
  })
143
171
 
144
- parentRouteIdSignal = computed(
145
- () => this.matchData()?.parentRouteId ?? '',
146
- )
172
+ parentRouteIdSignal = computed(() => this.matchData()?.parentRouteId ?? '')
147
173
  rootRouteIdSignal = computed(() => rootRouteId)
148
174
 
149
175
  hasPendingMatch = computed(() => {
150
176
  const routeId = this.matchData()?.route.id
151
177
  return routeId ? Boolean(this.pendingRouteIds()[routeId]) : false
152
178
  })
153
- pendingRouteIds = injectStore(this.router.stores.pendingRouteIds, (ids) => ids)
179
+ pendingRouteIds = injectStore(
180
+ this.router.stores.pendingRouteIds,
181
+ (ids) => ids,
182
+ )
154
183
  nearestMatchContext: NearestMatchContextValue = {
155
184
  matchId: this.matchId,
156
185
  routeId: computed(() => this.matchData()?.route.id),
@@ -158,7 +187,7 @@ export class RouteMatch {
158
187
  hasPending: this.hasPendingMatch,
159
188
  }
160
189
 
161
- isCatchingError = injectIsCatchingError({
190
+ catchingErrorMatch = injectCatchingErrorMatch({
162
191
  matchId: this.matchId,
163
192
  })
164
193
 
@@ -166,11 +195,15 @@ export class RouteMatch {
166
195
  const matchData = this.matchData()
167
196
  if (!matchData) return null
168
197
 
198
+ const { match, route } = matchData
199
+
169
200
  if (this.shouldClientOnly() && this.router.isServer) {
170
- return null
171
- }
201
+ const PendingComponent =
202
+ getComponent(route.options.pendingComponent) ??
203
+ getComponent(this.router.options.defaultPendingComponent)
172
204
 
173
- const { match, route } = matchData
205
+ return PendingComponent ? { component: PendingComponent } : null
206
+ }
174
207
 
175
208
  if (match.status === 'notFound') {
176
209
  const NotFoundComponent = getNotFoundComponent(this.router, route)
@@ -178,18 +211,18 @@ export class RouteMatch {
178
211
  return {
179
212
  component: NotFoundComponent,
180
213
  }
181
- } else if (match.status === 'error' || this.isCatchingError()) {
182
- const RouteErrorComponent =
183
- getComponent(route.options.errorComponent) ??
184
- getComponent(this.router.options.defaultErrorComponent)
214
+ } else if (match.status === 'error' || this.catchingErrorMatch()) {
215
+ const caughtMatch = this.catchingErrorMatch()
216
+ const RouteErrorComponent = getErrorComponent(this.router, route)
185
217
 
186
218
  return {
187
- component: RouteErrorComponent || null,
219
+ component: RouteErrorComponent,
188
220
  providers: [
189
221
  {
190
222
  provide: ERROR_STATE_INJECTOR_TOKEN,
191
223
  useValue: {
192
- error: match.error,
224
+ error:
225
+ match.status === 'error' ? match.error : caughtMatch?.error,
193
226
  reset: () => {
194
227
  this.router.invalidate()
195
228
  },
@@ -258,23 +291,27 @@ export class RouteMatch {
258
291
  selector: 'outlet,[outlet]',
259
292
  template: '',
260
293
  standalone: true,
294
+ host: {
295
+ hidden: '',
296
+ },
261
297
  })
262
298
  export class Outlet {
263
299
  router = injectRouter()
264
300
  nearestMatch = inject(MATCH_CONTEXT_INJECTOR_TOKEN)
265
301
 
266
- currentMatch = computed(() => {
267
- const matchId = this.nearestMatch.matchId()
268
- return matchId
269
- ? this.router.stores.activeMatchStoresById.get(matchId)?.state
270
- : undefined
271
- })
302
+ currentMatch = injectStore(
303
+ () => {
304
+ const matchId = this.nearestMatch.matchId()
305
+ return matchId
306
+ ? (this.router.stores.matchStores.get(matchId) ?? dummyMatchStore)
307
+ : dummyMatchStore
308
+ },
309
+ (value) => value,
310
+ )
272
311
 
273
312
  routeId = computed(() => this.currentMatch()?.routeId as string)
274
313
 
275
- route = computed(
276
- () => this.router.routesById[this.routeId()] as AnyRoute,
277
- )
314
+ route = computed(() => this.router.routesById[this.routeId()] as AnyRoute)
278
315
 
279
316
  parentGlobalNotFound = computed(
280
317
  () => this.currentMatch()?.globalNotFound ?? false,
@@ -310,7 +347,9 @@ export class Outlet {
310
347
  })
311
348
  }
312
349
 
313
- type CalledIfFunction<T> = T extends (...args: Array<any>) => any ? ReturnType<T> : T
350
+ type CalledIfFunction<T> = T extends (...args: Array<any>) => any
351
+ ? ReturnType<T>
352
+ : T
314
353
 
315
354
  function getComponent<T>(routeComponent: T): CalledIfFunction<T> {
316
355
  if (typeof routeComponent === 'function') {
@@ -336,3 +375,21 @@ function getNotFoundComponent(router: AnyRouter, route: AnyRoute) {
336
375
 
337
376
  return DefaultNotFoundComponent
338
377
  }
378
+
379
+ function getErrorComponent(router: AnyRouter, route: AnyRoute) {
380
+ const RouteErrorComponent =
381
+ getComponent(route.options.errorComponent) ??
382
+ getComponent(router.options.defaultErrorComponent)
383
+
384
+ if (RouteErrorComponent) {
385
+ return RouteErrorComponent
386
+ }
387
+
388
+ if (isDevMode() && !route.options.errorComponent) {
389
+ console.warn(
390
+ `An error was encountered on the route with ID "${route.id}", but an errorComponent option was not configured, nor was a router level defaultErrorComponent configured. Consider configuring at least one of these to avoid TanStack Router's generic defaultErrorComponent.`,
391
+ )
392
+ }
393
+
394
+ return DefaultErrorComponent
395
+ }
package/src/Matches.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Component } from '@angular/core'
2
2
  import { injectRouter } from './injectRouter'
3
- import { injectStore } from './injectStore'
3
+ import { injectStore } from './store/injectStore'
4
4
  import { injectRender } from './renderer/injectRender'
5
5
  import { RouteMatch } from './Match'
6
6
  import { injectSsrScrollRestorationScript } from './ssr-scroll-restoration'
@@ -10,11 +10,14 @@ import { injectTransitionerSetup } from './transitioner'
10
10
  selector: 'router-matches',
11
11
  template: '',
12
12
  standalone: true,
13
+ host: {
14
+ hidden: '',
15
+ },
13
16
  })
14
17
  export class Matches {
15
18
  router = injectRouter()
16
19
 
17
- private matchId = injectStore(this.router.stores.firstMatchId, (id) => id)
20
+ private matchId = injectStore(this.router.stores.firstId, (id) => id)
18
21
 
19
22
  private ssrScrollRestoration = injectSsrScrollRestorationScript()
20
23
 
@@ -75,6 +75,9 @@ export function provideTanstackRouter({
75
75
  selector: 'router-provider,[router-provider]',
76
76
  template: '',
77
77
  standalone: true,
78
+ host: {
79
+ hidden: '',
80
+ },
78
81
  })
79
82
  export class RouterProvider<TRouter extends AnyRouter = RegisteredRouter> {
80
83
  readonly injectedContext: RouterInputs<TRouter>['context'] = inject(
@@ -16,7 +16,7 @@ export function buildMatchManagedDocumentContent(
16
16
  router: AnyRouter,
17
17
  ): ManagedDocumentContent {
18
18
  const nonce = router.options.ssr?.nonce
19
- const matches = router.stores.activeMatchesSnapshot.state
19
+ const matches = router.stores.matches.get()
20
20
  const title = selectTitle(matches)
21
21
  const metaTags = selectMetaTags(matches, nonce)
22
22
  const links = selectConstructedLinks(matches, nonce)
@@ -1,6 +1,6 @@
1
1
  import { DOCUMENT } from '@angular/common'
2
2
  import * as Angular from '@angular/core'
3
- import { injectStore } from '../injectStore'
3
+ import { injectStore } from '../store/injectStore'
4
4
  import { buildMatchManagedDocumentContent } from './build-match-managed-document'
5
5
  import {
6
6
  collectDehydrationScriptManagedTags,
@@ -9,7 +9,10 @@ import {
9
9
  import { areManagedDocumentContentsEqual } from './document-equality'
10
10
  import { createManagedTagCollection } from './managed-dom'
11
11
  import type { AnyRouter } from '@benjavicente/router-core'
12
- import type { ManagedDocumentContent, ManagedTagCollection } from './managed-document-types'
12
+ import type {
13
+ ManagedDocumentContent,
14
+ ManagedTagCollection,
15
+ } from './managed-document-types'
13
16
 
14
17
  function applyManagedDocumentContent({
15
18
  document,
@@ -45,7 +48,7 @@ export function installUnifiedTanstackDocumentSync(injectedRouter: AnyRouter) {
45
48
  })
46
49
  const destroyRef = Angular.inject(Angular.DestroyRef)
47
50
  const activeMatches = injectStore(
48
- injectedRouter.stores.activeMatchesSnapshot,
51
+ injectedRouter.stores.matches,
49
52
  (matches) => matches,
50
53
  )
51
54
 
@@ -1,6 +1,6 @@
1
1
  import { DOCUMENT } from '@angular/common'
2
2
  import * as Angular from '@angular/core'
3
- import { injectStore } from '../injectStore'
3
+ import { injectStore } from '../store/injectStore'
4
4
  import { buildMatchManagedDocumentContent } from './build-match-managed-document'
5
5
  import { collectDehydrationScriptManagedTags } from './document-dehydration'
6
6
  import { areManagedTagArraysEqual } from './document-equality'
@@ -20,7 +20,7 @@ export function installTanstackBodyManagedTags(injectedRouter: AnyRouter) {
20
20
  })
21
21
  const destroyRef = Angular.inject(Angular.DestroyRef)
22
22
  const activeMatches = injectStore(
23
- injectedRouter.stores.activeMatchesSnapshot,
23
+ injectedRouter.stores.matches,
24
24
  (matches) => matches,
25
25
  )
26
26
 
@@ -1,6 +1,6 @@
1
1
  import { DOCUMENT } from '@angular/common'
2
2
  import * as Angular from '@angular/core'
3
- import { injectStore } from '../injectStore'
3
+ import { injectStore } from '../store/injectStore'
4
4
  import { buildMatchManagedDocumentContent } from './build-match-managed-document'
5
5
  import { TANSTACK_DOCUMENT_ROUTER } from './document-router-token'
6
6
  import type { AnyRouter } from '@benjavicente/router-core'
@@ -10,14 +10,13 @@ export function installTanstackDocumentTitle(injectedRouter: AnyRouter) {
10
10
  const document = Angular.inject(DOCUMENT)
11
11
  const destroyRef = Angular.inject(Angular.DestroyRef)
12
12
  const activeMatches = injectStore(
13
- injectedRouter.stores.activeMatchesSnapshot,
13
+ injectedRouter.stores.matches,
14
14
  (matches) => matches,
15
15
  )
16
16
 
17
17
  const initialTitle = document.title
18
- let currentTitle: string | undefined = buildMatchManagedDocumentContent(
19
- injectedRouter,
20
- ).title
18
+ let currentTitle: string | undefined =
19
+ buildMatchManagedDocumentContent(injectedRouter).title
21
20
 
22
21
  const applyTitle = (next?: string) => {
23
22
  if (next !== undefined) {
@@ -1,6 +1,6 @@
1
1
  import { DOCUMENT } from '@angular/common'
2
2
  import * as Angular from '@angular/core'
3
- import { injectStore } from '../injectStore'
3
+ import { injectStore } from '../store/injectStore'
4
4
  import { buildMatchManagedDocumentContent } from './build-match-managed-document'
5
5
  import { areManagedTagArraysEqual } from './document-equality'
6
6
  import { TANSTACK_DOCUMENT_ROUTER } from './document-router-token'
@@ -16,7 +16,7 @@ export function installTanstackHeadManagedTags(injectedRouter: AnyRouter) {
16
16
  })
17
17
  const destroyRef = Angular.inject(Angular.DestroyRef)
18
18
  const activeMatches = injectStore(
19
- injectedRouter.stores.activeMatchesSnapshot,
19
+ injectedRouter.stores.matches,
20
20
  (matches) => matches,
21
21
  )
22
22