@octanejs/tanstack-router 0.2.7 → 0.2.9

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.
@@ -0,0 +1,275 @@
1
+ import {
2
+ CompiledOutlet as CompiledComponent,
3
+ CompiledContext,
4
+ useContext,
5
+ useLayoutEffect,
6
+ useRef,
7
+ } from 'octane';
8
+ import type { NotFoundError } from '@tanstack/router-core';
9
+ import { hasKeys, isNotFound, rootRouteId, setupScrollRestoration } from '@tanstack/router-core';
10
+ import { getScrollRestorationScriptForRouter } from '@tanstack/router-core/scroll-restoration-script';
11
+ import { isServer } from '@tanstack/router-core/isServer';
12
+ import { routerContext, matchContext, useRouter } from './bindings';
13
+ import { useStore } from '../useStore';
14
+ import { useTransitioner, settleOwner } from '../Transitioner.tsrx';
15
+ import { useHydrated } from '../ClientOnly.tsrx';
16
+ import { ErrorComponent } from '../CatchBoundary.tsrx';
17
+ import { createLazyRouteLoader } from '../lazyRouteLoader';
18
+ import type { RouterProps } from '../RouterProvider.tsrx';
19
+
20
+ export function CompiledRouterProvider(props: RouterProps) @{
21
+ const { router, ...rest } = props;
22
+ if (hasKeys(rest)) {
23
+ router.update({
24
+ ...router.options,
25
+ ...rest,
26
+ context: { ...router.options.context, ...rest.context },
27
+ });
28
+ }
29
+ if (router.options.Wrap || router.options.InnerWrap) {
30
+ throw new Error(
31
+ 'CompiledRouterProvider does not accept Wrap or InnerWrap. Use RouterProvider for renderable wrappers.',
32
+ );
33
+ }
34
+ <CompiledContext context={routerContext} value={router}>
35
+ <CompiledMatches />
36
+ </CompiledContext>
37
+ }
38
+
39
+ function CompiledWrap(props: { component?: any; children?: unknown }) @{
40
+ @if (props.component) {
41
+ <CompiledComponent component={props.component} props={{ children: props.children }} />
42
+ } @else {
43
+ <>{props.children}</>
44
+ }
45
+ }
46
+
47
+ function CompiledMatches() @{
48
+ const router = useRouter();
49
+ const rootRoute = (router.routesById as any)[rootRouteId];
50
+ useLayoutEffect(() => {
51
+ if (router.options.scrollRestoration) setupScrollRestoration(router);
52
+ }, [router]);
53
+ const expectedMatches = useTransitioner(router);
54
+ @try {
55
+ <CompiledMatchesInner expectedMatches={expectedMatches} />
56
+ } @pending {
57
+ <CompiledPending route={rootRoute} />
58
+ } @catch (error, reset) {
59
+ if (router.options.disableGlobalCatchBoundary) throw error;
60
+ <CompiledFailure error={error} reset={reset} route={rootRoute} global={true} />
61
+ }
62
+ }
63
+
64
+ function CompiledMatchesInner(props: { expectedMatches: unknown }) @{
65
+ const router = useRouter();
66
+ const acknowledgement: Array<any> = ((router as any)._rendered ??= []);
67
+ const matches = useStore(router.stores.matches, (m: any) =>
68
+ acknowledgement[0] === props.expectedMatches ? (props.expectedMatches ?? m) : m,
69
+ ) as any;
70
+ const rootMatch = matches[0];
71
+ useLayoutEffect(() => {
72
+ if (acknowledgement[0] === matches) settleOwner(acknowledgement, true);
73
+ }, [acknowledgement, matches]);
74
+ @if (rootMatch) {
75
+ <CompiledContext context={matchContext} value={rootMatch.routeId}>
76
+ <CompiledMatch routeId={rootMatch.routeId} />
77
+ </CompiledContext>
78
+ }
79
+ }
80
+
81
+ function CompiledMatch(props: { routeId: string }) @{
82
+ const router = useRouter();
83
+ const store = router.stores.getMatchStore(props.routeId);
84
+ const ssr = useStore(store, (match: any) => match.ssr);
85
+ const route = (router.routesById as Record<string, any>)[props.routeId];
86
+ const pending = route.options.pendingComponent ?? router.options.defaultPendingComponent;
87
+ const errorComponent = route.options.errorComponent ?? router.options.defaultErrorComponent;
88
+ const noSsr = ssr === false || ssr === 'data-only';
89
+ const hasSuspense =
90
+ (!route.isRoot || route.options.wrapInSuspense || noSsr) &&
91
+ ((route.options.wrapInSuspense ?? pending ?? errorComponent?.preload) || noSsr);
92
+ <CompiledWrap component={route.isRoot ? route.options.shellComponent : undefined}>
93
+ <CompiledContext context={matchContext} value={props.routeId}>
94
+ @if (hasSuspense) {
95
+ @try {
96
+ <CompiledMatchContent route={route} routeId={props.routeId} noSsr={noSsr} />
97
+ } @pending {
98
+ <CompiledPending route={route} />
99
+ }
100
+ } @else {
101
+ <CompiledMatchContent route={route} routeId={props.routeId} noSsr={noSsr} />
102
+ }
103
+ </CompiledContext>
104
+ @if (route.parentRoute?.id === rootRouteId && router.options.scrollRestoration) {
105
+ <CompiledScrollRestoration />
106
+ }
107
+ </CompiledWrap>
108
+ }
109
+
110
+ // Without a configured Suspense boundary, both lazy imports and loader promises
111
+ // pass through to the next ancestor boundary. The match still owns its errors.
112
+ function CompiledMatchContent(props: { route: any; routeId: string; noSsr: boolean }) @{
113
+ @try {
114
+ <CompiledMatchInner routeId={props.routeId} noSsr={props.noSsr} />
115
+ } @catch (error, reset) {
116
+ <CompiledFailure error={error} reset={reset} route={props.route} />
117
+ }
118
+ }
119
+
120
+ function CompiledMatchInner(props: { routeId: string; noSsr: boolean }) @{
121
+ const router = useRouter();
122
+ const match = useStore(router.stores.getMatchStore(props.routeId), (m: any) => m);
123
+ const route = (router.routesById as Record<string, any>)[props.routeId];
124
+ const hydrated = useHydrated();
125
+ const Comp = route.options.component ?? router.options.defaultComponent;
126
+ const remountFn = route.options.remountDeps ?? router.options.defaultRemountDeps;
127
+ const remountDeps = remountFn?.({
128
+ routeId: props.routeId,
129
+ loaderDeps: match.loaderDeps,
130
+ params: match._strictParams,
131
+ search: match._strictSearch,
132
+ });
133
+ const key = remountDeps ? JSON.stringify(remountDeps) : undefined;
134
+ if (match.status === 'pending' && !props.noSsr && (router as any)._tx) {
135
+ throw (router as any)._tx[5];
136
+ }
137
+ @if ((props.noSsr && !hydrated) || match.status === 'pending') {
138
+ <CompiledPending route={route} />
139
+ } @else if (match.status === 'notFound') {
140
+ <CompiledNotFound route={route} error={match.error} />
141
+ } @else if (match.status === 'error') {
142
+ @if (isServer ?? (router as any).isServer) {
143
+ <CompiledComponent
144
+ component={
145
+ route.options.errorComponent ?? router.options.defaultErrorComponent ?? ErrorComponent
146
+ }
147
+ props={{ error: match.error, reset: undefined, info: { componentStack: '' } }}
148
+ />
149
+ } @else {
150
+ throw match.error;
151
+ <></>
152
+ }
153
+ } @else if (Comp) {
154
+ <CompiledComponent key={key} component={Comp} props={{}} />
155
+ } @else {
156
+ <CompiledOutlet />
157
+ }
158
+ }
159
+
160
+ // Reads exactly the next TanStack match, preserving nearest-match contexts and
161
+ // fuzzy/root not-found placement. This is distinct from octane's component outlet.
162
+ export function CompiledOutlet() @{
163
+ const router = useRouter();
164
+ const routeId = useContext(matchContext)!;
165
+ const store = router.stores.getMatchStore(routeId);
166
+ const notFound = useStore(
167
+ store,
168
+ (m: any) => ({ global: !!m?._notFound, error: m?.error }),
169
+ (a, b) => a.global === b.global && a.error === b.error,
170
+ );
171
+ const childId = useStore(router.stores.ids, (ids: Array<string>) => {
172
+ const i = ids.indexOf(routeId);
173
+ return i >= 0 ? ids[i + 1] : undefined;
174
+ });
175
+ @if (notFound.global) {
176
+ <CompiledNotFound route={(router.routesById as any)[routeId]} error={notFound.error} />
177
+ } @else if (childId) {
178
+ @if (routeId === rootRouteId) {
179
+ @try {
180
+ <CompiledMatch routeId={childId} />
181
+ } @pending {
182
+ <CompiledPending />
183
+ }
184
+ } @else {
185
+ <CompiledMatch routeId={childId} />
186
+ }
187
+ }
188
+ }
189
+
190
+ function CompiledPending(props: { route?: any }) @{
191
+ const router = useRouter();
192
+ const Pending = props.route?.options.pendingComponent ?? router.options.defaultPendingComponent;
193
+ @if (Pending) {
194
+ <CompiledComponent component={Pending} props={{}} />
195
+ }
196
+ }
197
+
198
+ function CompiledNotFound(props: { route: any; error?: NotFoundError }) @{
199
+ const router = useRouter();
200
+ const NotFound = props.route.options.notFoundComponent ?? router.options.defaultNotFoundComponent;
201
+ @if (NotFound) {
202
+ <CompiledComponent component={NotFound} props={props.error ?? {}} />
203
+ } @else {
204
+ <p>Not Found</p>
205
+ }
206
+ }
207
+
208
+ function CompiledFailure(props: { error: any; reset: () => void; route: any; global?: boolean }) @{
209
+ const router = useRouter();
210
+ const errorComponent = props.global
211
+ ? ErrorComponent
212
+ : (props.route.options.errorComponent ?? router.options.defaultErrorComponent);
213
+ const notFoundComponent = props.route.isRoot
214
+ ? (props.route.options.notFoundComponent ?? router.options.notFoundRoute?.options.component)
215
+ : props.route.options.notFoundComponent;
216
+ const isMissing = isNotFound(props.error);
217
+ if (isMissing) {
218
+ const missing = props.error as NotFoundError;
219
+ missing.routeId ??= props.route.id;
220
+ if (!notFoundComponent || missing.routeId !== props.route.id) throw missing;
221
+ } else if (!errorComponent) {
222
+ throw props.error;
223
+ }
224
+
225
+ // Report an error only at the boundary that owns it and retry after its
226
+ // TanStack match identity changes, just like the normal binding.
227
+ const reported = useRef<unknown>(null);
228
+ if (!isMissing && !props.global && reported.current !== props.error) {
229
+ reported.current = props.error;
230
+ const onCatch = props.route.options.onCatch ?? router.options.defaultOnCatch;
231
+ onCatch?.(props.error, { componentStack: '' });
232
+ }
233
+ const store = router.stores.getMatchStore(props.route.id);
234
+ const match = useStore(store, (m: any) => m);
235
+ const firstMatch = useRef(match);
236
+ useLayoutEffect(() => {
237
+ if (firstMatch.current !== match) {
238
+ firstMatch.current = match;
239
+ props.reset();
240
+ }
241
+ }, [match]);
242
+
243
+ @if (isMissing) {
244
+ <CompiledComponent component={notFoundComponent} props={props.error} />
245
+ } @else {
246
+ <CompiledComponent
247
+ component={errorComponent}
248
+ props={{ error: props.error, reset: props.reset, info: { componentStack: '' } }}
249
+ />
250
+ }
251
+ }
252
+
253
+ function CompiledScrollRestoration() @{
254
+ const router = useRouter();
255
+ const hydrated = useHydrated();
256
+ const option = router.options.scrollRestoration;
257
+ const script = getScrollRestorationScriptForRouter(router);
258
+ const rendersScript = typeof option !== 'function' || option({ location: router.latestLocation });
259
+ @if ((script !== null || rendersScript) && ((isServer ?? router.isServer) || !hydrated)) {
260
+ <script
261
+ nonce={router.options.ssr?.nonce}
262
+ suppressHydrationWarning
263
+ dangerouslySetInnerHTML={{ __html: script ?? '' }}
264
+ />
265
+ }
266
+ }
267
+
268
+ export function lazyCompiledRouteComponent(importer: () => Promise<any>, exportName?: string) {
269
+ const loader = createLazyRouteLoader(importer, exportName);
270
+ function Lazy(props: any) @{
271
+ <CompiledComponent component={loader.read()} props={props} />
272
+ }
273
+ Lazy.preload = loader.preload;
274
+ return Lazy;
275
+ }
@@ -0,0 +1,42 @@
1
+ 'use strong';
2
+
3
+ import { createCompiledContext, useContext } from 'octane';
4
+ import type { AnyRouter, RegisteredRouter } from '@tanstack/router-core';
5
+ import { createRouterHooks } from '../hookBindings';
6
+ import { createUseRouterState } from '../routerStateBinding';
7
+
8
+ // The normal contexts remain callable providers. These contexts belong only to
9
+ // the opt-in compiled tree so that using it never imports a generic provider.
10
+ export const routerContext = createCompiledContext<AnyRouter | undefined>(undefined);
11
+ export const matchContext = createCompiledContext<string | undefined>(undefined);
12
+
13
+ export function useRouter<TRouter extends AnyRouter = RegisteredRouter>(opts?: {
14
+ router?: TRouter;
15
+ warn?: boolean;
16
+ }): TRouter;
17
+ export function useRouter(...args: unknown[]): AnyRouter {
18
+ const opts = (args.length && typeof args[0] !== 'symbol' ? args[0] : undefined) as
19
+ { router?: AnyRouter; warn?: boolean } | undefined;
20
+ const router = opts?.router ?? useContext(routerContext);
21
+ if (!router && opts?.warn !== false) {
22
+ throw new Error('Use compiled router hooks inside <CompiledRouterProvider>.');
23
+ }
24
+ return router as AnyRouter;
25
+ }
26
+
27
+ export const {
28
+ useMatch,
29
+ useParams,
30
+ useSearch,
31
+ useLoaderData,
32
+ useLoaderDeps,
33
+ useRouteContext,
34
+ useLocation,
35
+ useMatches,
36
+ useParentMatches,
37
+ useChildMatches,
38
+ useNavigate,
39
+ useCanGoBack,
40
+ } = createRouterHooks(useRouter, () => useContext(matchContext));
41
+
42
+ export const useRouterState = createUseRouterState(useRouter);
@@ -0,0 +1,71 @@
1
+ 'use strong';
2
+
3
+ import { BaseRoute, BaseRootRoute, BaseRouteApi, notFound } from '@tanstack/router-core';
4
+ import { createRouteHookBindings } from '../routeHookBindings';
5
+ import * as hooks from './bindings';
6
+ import { Link } from './Link.tsrx';
7
+ import { splitSlot, subSlot } from '../internal';
8
+ import type * as RouteFactories from '../route';
9
+ import type * as FileFactories from '../fileRoute';
10
+
11
+ const { attachRouteHooks, attachRouteNavigation, attachLazyRouteHooks } = createRouteHookBindings(
12
+ hooks,
13
+ hooks.useRouter,
14
+ );
15
+
16
+ function attachRoute(route: any) {
17
+ attachRouteHooks(route, true);
18
+ attachRouteNavigation(route);
19
+ function RouteLink(props: any) @{
20
+ <Link from={route.fullPath} {...props} />
21
+ }
22
+ route.Link = RouteLink;
23
+ return route;
24
+ }
25
+
26
+ // These factories preserve the binding's precise route types, while constructing
27
+ // core instances directly: no ordinary-context hooks or descriptor Link are made.
28
+ export const createRoute: typeof RouteFactories.createRoute = (options: any) =>
29
+ attachRoute(new BaseRoute(options));
30
+ export const createRootRoute: typeof RouteFactories.createRootRoute = (options?: any) =>
31
+ attachRoute(new BaseRootRoute(options));
32
+ export const createRootRouteWithContext: typeof RouteFactories.createRootRouteWithContext = () =>
33
+ createRootRoute;
34
+ export const rootRouteWithContext = createRootRouteWithContext;
35
+
36
+ export const getRouteApi: typeof RouteFactories.getRouteApi = (id: any) => {
37
+ const route = new BaseRouteApi({ id }) as any;
38
+ attachRouteHooks(route, false);
39
+ route.useNavigate = (...args: any[]) => {
40
+ const [, slot] = splitSlot(args);
41
+ const router = hooks.useRouter();
42
+ return hooks.useNavigate(
43
+ { from: (router.routesById as any)[id].fullPath },
44
+ subSlot(slot, 'r:n'),
45
+ );
46
+ };
47
+ function RouteLink(props: any) @{
48
+ const router = hooks.useRouter();
49
+ <Link from={(router.routesById as any)[id].fullPath} {...props} />
50
+ }
51
+ route.Link = RouteLink;
52
+ route.notFound = (options: any) => notFound({ routeId: id, ...options });
53
+ return route;
54
+ };
55
+
56
+ export const createFileRoute: typeof FileFactories.createFileRoute = () => (options: any) => {
57
+ const route = createRoute(options) as any;
58
+ route.isRoot = false;
59
+ return route;
60
+ };
61
+
62
+ function lazyRoute(options: any) {
63
+ const route = { options } as any;
64
+ attachLazyRouteHooks(route);
65
+ return route;
66
+ }
67
+
68
+ export const createLazyRoute: typeof FileFactories.createLazyRoute = (id: any) => (options: any) =>
69
+ lazyRoute({ id, ...options });
70
+ export const createLazyFileRoute: typeof FileFactories.createLazyFileRoute = (id: any) =>
71
+ typeof id === 'object' ? lazyRoute(id) : (options: any) => lazyRoute({ id, ...options });
@@ -0,0 +1,67 @@
1
+ // Opt-in source-owned renderer for route trees with compiled bodies.
2
+ import './frameworkTypes';
3
+ export * from '@tanstack/router-core';
4
+ export { Router, createRouter } from './router';
5
+ export {
6
+ createHistory,
7
+ createBrowserHistory,
8
+ createHashHistory,
9
+ createMemoryHistory,
10
+ } from './history';
11
+ export type {
12
+ RouterHistory,
13
+ HistoryLocation,
14
+ ParsedPath,
15
+ HistoryState,
16
+ ParsedHistoryState,
17
+ HistoryAction,
18
+ BlockerFnArgs,
19
+ BlockerFn,
20
+ NavigationBlocker,
21
+ } from '@tanstack/history';
22
+ export {
23
+ useRouter,
24
+ useRouterState,
25
+ useMatch,
26
+ useParams,
27
+ useSearch,
28
+ useLoaderData,
29
+ useLoaderDeps,
30
+ useRouteContext,
31
+ useLocation,
32
+ useMatches,
33
+ useParentMatches,
34
+ useChildMatches,
35
+ useNavigate,
36
+ useCanGoBack,
37
+ } from './compiled/bindings';
38
+ export type { UseRouterStateOptions, UseRouterStateResult } from './routerStateBinding';
39
+ export {
40
+ CompiledRouterProvider,
41
+ CompiledRouterProvider as RouterProvider,
42
+ CompiledOutlet,
43
+ CompiledOutlet as Outlet,
44
+ lazyCompiledRouteComponent,
45
+ lazyCompiledRouteComponent as lazyRouteComponent,
46
+ } from './compiled/Matches.tsrx';
47
+ export {
48
+ createRoute,
49
+ createRootRoute,
50
+ createRootRouteWithContext,
51
+ rootRouteWithContext,
52
+ getRouteApi,
53
+ createFileRoute,
54
+ createLazyRoute,
55
+ createLazyFileRoute,
56
+ } from './compiled/route.tsrx';
57
+ export { Link, useLinkProps, createLink } from './compiled/Link.tsrx';
58
+ export { ClientOnly, useHydrated } from './ClientOnly.tsrx';
59
+ export { getAssetKeys } from './assetKeys';
60
+ export { Html, Head, Body, Asset, HeadContent, Scripts, useTags } from './compiled/Document.tsrx';
61
+ export type { AssetKeys } from './assetKeys';
62
+ export type {
63
+ LinkComponent,
64
+ LinkComponentProps,
65
+ LinkProps,
66
+ UseLinkPropsOptions,
67
+ } from './linkTypes';
package/src/fileRoute.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { createRoute } from './route';
2
+ import type { Route } from './route';
2
3
  import {
3
4
  useLoaderData,
4
5
  useLoaderDeps,
@@ -9,7 +10,12 @@ import {
9
10
  useSearch,
10
11
  } from './hooks';
11
12
  import { useRouter } from './context';
12
- import { splitSlot, subSlot } from './internal';
13
+ import { createRouteHookBindings } from './routeHookBindings';
14
+
15
+ const { attachLazyRouteHooks } = createRouteHookBindings(
16
+ { useMatch, useParams, useSearch, useLoaderData, useLoaderDeps, useRouteContext, useNavigate },
17
+ useRouter,
18
+ );
13
19
  import type {
14
20
  AnyContext,
15
21
  AnyRoute,
@@ -22,7 +28,6 @@ import type {
22
28
  Register,
23
29
  RegisteredRouter,
24
30
  ResolveParams,
25
- Route,
26
31
  RouteById,
27
32
  RouteConstraints,
28
33
  RouteIds,
@@ -198,63 +203,7 @@ export class LazyRoute<TRoute extends AnyRoute> {
198
203
 
199
204
  constructor(opts: { id: string } & LazyRouteOptions) {
200
205
  this.options = opts;
201
- const id = this.options.id;
202
- this.useMatch = ((...args: Array<any>) => {
203
- const [user, slot] = splitSlot(args);
204
- const options = user[0] ?? {};
205
- return useMatch(
206
- {
207
- select: options.select,
208
- from: id,
209
- structuralSharing: options.structuralSharing,
210
- },
211
- subSlot(slot, 'lr:m'),
212
- );
213
- }) as typeof this.useMatch;
214
- this.useRouteContext = ((...args: Array<any>) => {
215
- const [user, slot] = splitSlot(args);
216
- return useRouteContext({ ...(user[0] ?? {}), from: id }, subSlot(slot, 'lr:c'));
217
- }) as typeof this.useRouteContext;
218
- this.useSearch = ((...args: Array<any>) => {
219
- const [user, slot] = splitSlot(args);
220
- const options = user[0] ?? {};
221
- return useSearch(
222
- {
223
- select: options.select,
224
- from: id,
225
- structuralSharing: options.structuralSharing,
226
- },
227
- subSlot(slot, 'lr:s'),
228
- );
229
- }) as typeof this.useSearch;
230
- this.useParams = ((...args: Array<any>) => {
231
- const [user, slot] = splitSlot(args);
232
- const options = user[0] ?? {};
233
- return useParams(
234
- {
235
- select: options.select,
236
- from: id,
237
- structuralSharing: options.structuralSharing,
238
- },
239
- subSlot(slot, 'lr:p'),
240
- );
241
- }) as typeof this.useParams;
242
- this.useLoaderDeps = ((...args: Array<any>) => {
243
- const [user, slot] = splitSlot(args);
244
- return useLoaderDeps({ ...(user[0] ?? {}), from: id }, subSlot(slot, 'lr:d'));
245
- }) as typeof this.useLoaderDeps;
246
- this.useLoaderData = ((...args: Array<any>) => {
247
- const [user, slot] = splitSlot(args);
248
- return useLoaderData({ ...(user[0] ?? {}), from: id }, subSlot(slot, 'lr:l'));
249
- }) as typeof this.useLoaderData;
250
- this.useNavigate = ((...args: Array<any>) => {
251
- const [, slot] = splitSlot(args);
252
- const router = useRouter();
253
- return useNavigate(
254
- { from: (router.routesById as Record<string, any>)[id].fullPath },
255
- subSlot(slot, 'lr:n'),
256
- );
257
- }) as typeof this.useNavigate;
206
+ attachLazyRouteHooks(this);
258
207
  }
259
208
  }
260
209
 
@@ -268,7 +217,8 @@ export function createLazyRoute<
268
217
 
269
218
  export function createLazyFileRoute<
270
219
  TFilePath extends keyof FileRoutesByPath,
271
- TRoute extends FileRoutesByPath[TFilePath]['preLoaderRoute'],
220
+ TRoute extends FileRoutesByPath[TFilePath]['preLoaderRoute'] & AnyRoute =
221
+ FileRoutesByPath[TFilePath]['preLoaderRoute'],
272
222
  >(id: TFilePath): (opts: LazyRouteOptions) => LazyRoute<TRoute> {
273
223
  if (typeof id === 'object') {
274
224
  return new LazyRoute<TRoute>(id) as any;
@@ -30,13 +30,16 @@ declare module '@tanstack/router-core' {
30
30
  }
31
31
 
32
32
  interface RouteExtensions<in out TId extends string, in out TFullPath extends string> {
33
- useMatch: UseMatchRoute<TId>;
34
- useRouteContext: UseRouteContextRoute<TId>;
35
- useSearch: UseSearchRoute<TId>;
36
- useParams: UseParamsRoute<TId>;
37
- useLoaderDeps: UseLoaderDepsRoute<TId>;
38
- useLoaderData: UseLoaderDataRoute<TId>;
39
- useNavigate: () => UseNavigateResult<TFullPath>;
40
- Link: LinkComponentRoute<TFullPath>;
33
+ // Core base routes are also valid in compiled trees and do not own bound
34
+ // renderer hooks. The ordinary binding's concrete Route classes provide
35
+ // each of these members as required properties.
36
+ useMatch?: UseMatchRoute<TId>;
37
+ useRouteContext?: UseRouteContextRoute<TId>;
38
+ useSearch?: UseSearchRoute<TId>;
39
+ useParams?: UseParamsRoute<TId>;
40
+ useLoaderDeps?: UseLoaderDepsRoute<TId>;
41
+ useLoaderData?: UseLoaderDataRoute<TId>;
42
+ useNavigate?: () => UseNavigateResult<TFullPath>;
43
+ Link?: LinkComponentRoute<TFullPath>;
41
44
  }
42
45
  }