@bleedingdev/modern-js-plugin-tanstack 3.2.0-ultramodern.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (183) hide show
  1. package/LICENSE +21 -0
  2. package/dist/cjs/cli/index.js +268 -0
  3. package/dist/cjs/cli/tanstackTypes.js +388 -0
  4. package/dist/cjs/cli.js +65 -0
  5. package/dist/cjs/runtime/DefaultNotFound.js +47 -0
  6. package/dist/cjs/runtime/basepathRewrite.js +62 -0
  7. package/dist/cjs/runtime/dataMutation.js +345 -0
  8. package/dist/cjs/runtime/hooks.js +57 -0
  9. package/dist/cjs/runtime/index.js +114 -0
  10. package/dist/cjs/runtime/lifecycle.js +125 -0
  11. package/dist/cjs/runtime/plugin.js +250 -0
  12. package/dist/cjs/runtime/plugin.node.js +304 -0
  13. package/dist/cjs/runtime/prefetchLink.js +55 -0
  14. package/dist/cjs/runtime/routeTree.js +492 -0
  15. package/dist/cjs/runtime/rsc/ClientSlot.js +53 -0
  16. package/dist/cjs/runtime/rsc/CompositeComponent.js +75 -0
  17. package/dist/cjs/runtime/rsc/ReplayableStream.js +141 -0
  18. package/dist/cjs/runtime/rsc/RscNodeRenderer.js +65 -0
  19. package/dist/cjs/runtime/rsc/SlotContext.js +54 -0
  20. package/dist/cjs/runtime/rsc/client.js +93 -0
  21. package/dist/cjs/runtime/rsc/createRscProxy.js +141 -0
  22. package/dist/cjs/runtime/rsc/index.js +42 -0
  23. package/dist/cjs/runtime/rsc/payloadRouter.js +211 -0
  24. package/dist/cjs/runtime/rsc/server.js +246 -0
  25. package/dist/cjs/runtime/rsc/slotUsageSanitizer.js +65 -0
  26. package/dist/cjs/runtime/rsc/symbols.js +72 -0
  27. package/dist/cjs/runtime/types.js +18 -0
  28. package/dist/cjs/runtime/utils.js +142 -0
  29. package/dist/cjs/runtime.js +58 -0
  30. package/dist/esm/cli/index.mjs +201 -0
  31. package/dist/esm/cli/tanstackTypes.mjs +341 -0
  32. package/dist/esm/cli.mjs +2 -0
  33. package/dist/esm/rslib-runtime.mjs +18 -0
  34. package/dist/esm/runtime/DefaultNotFound.mjs +13 -0
  35. package/dist/esm/runtime/basepathRewrite.mjs +28 -0
  36. package/dist/esm/runtime/dataMutation.mjs +305 -0
  37. package/dist/esm/runtime/hooks.mjs +8 -0
  38. package/dist/esm/runtime/index.mjs +6 -0
  39. package/dist/esm/runtime/lifecycle.mjs +82 -0
  40. package/dist/esm/runtime/plugin.mjs +214 -0
  41. package/dist/esm/runtime/plugin.node.mjs +268 -0
  42. package/dist/esm/runtime/prefetchLink.mjs +18 -0
  43. package/dist/esm/runtime/routeTree.mjs +452 -0
  44. package/dist/esm/runtime/rsc/ClientSlot.mjs +19 -0
  45. package/dist/esm/runtime/rsc/CompositeComponent.mjs +41 -0
  46. package/dist/esm/runtime/rsc/ReplayableStream.mjs +104 -0
  47. package/dist/esm/runtime/rsc/RscNodeRenderer.mjs +31 -0
  48. package/dist/esm/runtime/rsc/SlotContext.mjs +17 -0
  49. package/dist/esm/runtime/rsc/client.mjs +53 -0
  50. package/dist/esm/runtime/rsc/createRscProxy.mjs +107 -0
  51. package/dist/esm/runtime/rsc/index.mjs +1 -0
  52. package/dist/esm/runtime/rsc/payloadRouter.mjs +162 -0
  53. package/dist/esm/runtime/rsc/server.mjs +200 -0
  54. package/dist/esm/runtime/rsc/slotUsageSanitizer.mjs +31 -0
  55. package/dist/esm/runtime/rsc/symbols.mjs +17 -0
  56. package/dist/esm/runtime/types.mjs +0 -0
  57. package/dist/esm/runtime/utils.mjs +89 -0
  58. package/dist/esm/runtime.mjs +1 -0
  59. package/dist/esm-node/cli/index.mjs +205 -0
  60. package/dist/esm-node/cli/tanstackTypes.mjs +342 -0
  61. package/dist/esm-node/cli.mjs +3 -0
  62. package/dist/esm-node/rslib-runtime.mjs +19 -0
  63. package/dist/esm-node/runtime/DefaultNotFound.mjs +14 -0
  64. package/dist/esm-node/runtime/basepathRewrite.mjs +29 -0
  65. package/dist/esm-node/runtime/dataMutation.mjs +306 -0
  66. package/dist/esm-node/runtime/hooks.mjs +9 -0
  67. package/dist/esm-node/runtime/index.mjs +7 -0
  68. package/dist/esm-node/runtime/lifecycle.mjs +83 -0
  69. package/dist/esm-node/runtime/plugin.mjs +215 -0
  70. package/dist/esm-node/runtime/plugin.node.mjs +269 -0
  71. package/dist/esm-node/runtime/prefetchLink.mjs +19 -0
  72. package/dist/esm-node/runtime/routeTree.mjs +453 -0
  73. package/dist/esm-node/runtime/rsc/ClientSlot.mjs +20 -0
  74. package/dist/esm-node/runtime/rsc/CompositeComponent.mjs +42 -0
  75. package/dist/esm-node/runtime/rsc/ReplayableStream.mjs +105 -0
  76. package/dist/esm-node/runtime/rsc/RscNodeRenderer.mjs +32 -0
  77. package/dist/esm-node/runtime/rsc/SlotContext.mjs +18 -0
  78. package/dist/esm-node/runtime/rsc/client.mjs +54 -0
  79. package/dist/esm-node/runtime/rsc/createRscProxy.mjs +108 -0
  80. package/dist/esm-node/runtime/rsc/index.mjs +2 -0
  81. package/dist/esm-node/runtime/rsc/payloadRouter.mjs +163 -0
  82. package/dist/esm-node/runtime/rsc/server.mjs +201 -0
  83. package/dist/esm-node/runtime/rsc/slotUsageSanitizer.mjs +32 -0
  84. package/dist/esm-node/runtime/rsc/symbols.mjs +18 -0
  85. package/dist/esm-node/runtime/types.mjs +1 -0
  86. package/dist/esm-node/runtime/utils.mjs +90 -0
  87. package/dist/esm-node/runtime.mjs +2 -0
  88. package/dist/types/cli/index.d.ts +20 -0
  89. package/dist/types/cli/tanstackTypes.d.ts +11 -0
  90. package/dist/types/cli.d.ts +2 -0
  91. package/dist/types/runtime/DefaultNotFound.d.ts +2 -0
  92. package/dist/types/runtime/basepathRewrite.d.ts +8 -0
  93. package/dist/types/runtime/dataMutation.d.ts +29 -0
  94. package/dist/types/runtime/hooks.d.ts +18 -0
  95. package/dist/types/runtime/index.d.ts +9 -0
  96. package/dist/types/runtime/lifecycle.d.ts +22 -0
  97. package/dist/types/runtime/plugin.d.ts +17 -0
  98. package/dist/types/runtime/plugin.node.d.ts +17 -0
  99. package/dist/types/runtime/prefetchLink.d.ts +11 -0
  100. package/dist/types/runtime/routeTree.d.ts +11 -0
  101. package/dist/types/runtime/rsc/ClientSlot.d.ts +5 -0
  102. package/dist/types/runtime/rsc/CompositeComponent.d.ts +3 -0
  103. package/dist/types/runtime/rsc/ReplayableStream.d.ts +24 -0
  104. package/dist/types/runtime/rsc/RscNodeRenderer.d.ts +5 -0
  105. package/dist/types/runtime/rsc/SlotContext.d.ts +11 -0
  106. package/dist/types/runtime/rsc/client.d.ts +11 -0
  107. package/dist/types/runtime/rsc/createRscProxy.d.ts +7 -0
  108. package/dist/types/runtime/rsc/index.d.ts +2 -0
  109. package/dist/types/runtime/rsc/payloadRouter.d.ts +24 -0
  110. package/dist/types/runtime/rsc/server.d.ts +14 -0
  111. package/dist/types/runtime/rsc/slotUsageSanitizer.d.ts +2 -0
  112. package/dist/types/runtime/rsc/symbols.d.ts +46 -0
  113. package/dist/types/runtime/types.d.ts +68 -0
  114. package/dist/types/runtime/utils.d.ts +36 -0
  115. package/dist/types/runtime.d.ts +1 -0
  116. package/dist/types-direct/cli/index.d.ts +20 -0
  117. package/dist/types-direct/cli/tanstackTypes.d.ts +11 -0
  118. package/dist/types-direct/cli.d.ts +2 -0
  119. package/dist/types-direct/runtime/DefaultNotFound.d.ts +2 -0
  120. package/dist/types-direct/runtime/basepathRewrite.d.ts +8 -0
  121. package/dist/types-direct/runtime/dataMutation.d.ts +29 -0
  122. package/dist/types-direct/runtime/hooks.d.ts +18 -0
  123. package/dist/types-direct/runtime/index.d.ts +9 -0
  124. package/dist/types-direct/runtime/lifecycle.d.ts +22 -0
  125. package/dist/types-direct/runtime/plugin.d.ts +17 -0
  126. package/dist/types-direct/runtime/plugin.node.d.ts +17 -0
  127. package/dist/types-direct/runtime/prefetchLink.d.ts +11 -0
  128. package/dist/types-direct/runtime/routeTree.d.ts +11 -0
  129. package/dist/types-direct/runtime/rsc/ClientSlot.d.ts +5 -0
  130. package/dist/types-direct/runtime/rsc/CompositeComponent.d.ts +3 -0
  131. package/dist/types-direct/runtime/rsc/ReplayableStream.d.ts +24 -0
  132. package/dist/types-direct/runtime/rsc/RscNodeRenderer.d.ts +5 -0
  133. package/dist/types-direct/runtime/rsc/SlotContext.d.ts +11 -0
  134. package/dist/types-direct/runtime/rsc/client.d.ts +11 -0
  135. package/dist/types-direct/runtime/rsc/createRscProxy.d.ts +7 -0
  136. package/dist/types-direct/runtime/rsc/index.d.ts +2 -0
  137. package/dist/types-direct/runtime/rsc/payloadRouter.d.ts +24 -0
  138. package/dist/types-direct/runtime/rsc/server.d.ts +14 -0
  139. package/dist/types-direct/runtime/rsc/slotUsageSanitizer.d.ts +2 -0
  140. package/dist/types-direct/runtime/rsc/symbols.d.ts +46 -0
  141. package/dist/types-direct/runtime/types.d.ts +68 -0
  142. package/dist/types-direct/runtime/utils.d.ts +36 -0
  143. package/dist/types-direct/runtime.d.ts +1 -0
  144. package/package.json +126 -0
  145. package/rslib.config.mts +4 -0
  146. package/rstest.config.mts +43 -0
  147. package/src/cli/index.ts +388 -0
  148. package/src/cli/tanstackTypes.ts +503 -0
  149. package/src/cli.ts +2 -0
  150. package/src/runtime/DefaultNotFound.tsx +15 -0
  151. package/src/runtime/basepathRewrite.ts +59 -0
  152. package/src/runtime/dataMutation.tsx +517 -0
  153. package/src/runtime/hooks.ts +34 -0
  154. package/src/runtime/index.tsx +30 -0
  155. package/src/runtime/lifecycle.ts +150 -0
  156. package/src/runtime/plugin.node.tsx +534 -0
  157. package/src/runtime/plugin.tsx +395 -0
  158. package/src/runtime/prefetchLink.tsx +87 -0
  159. package/src/runtime/routeTree.ts +942 -0
  160. package/src/runtime/rsc/ClientSlot.tsx +25 -0
  161. package/src/runtime/rsc/CompositeComponent.tsx +65 -0
  162. package/src/runtime/rsc/ReplayableStream.ts +155 -0
  163. package/src/runtime/rsc/RscNodeRenderer.tsx +45 -0
  164. package/src/runtime/rsc/SlotContext.tsx +31 -0
  165. package/src/runtime/rsc/client.tsx +90 -0
  166. package/src/runtime/rsc/createRscProxy.tsx +189 -0
  167. package/src/runtime/rsc/index.ts +10 -0
  168. package/src/runtime/rsc/payloadRouter.ts +318 -0
  169. package/src/runtime/rsc/server.tsx +303 -0
  170. package/src/runtime/rsc/slotUsageSanitizer.ts +76 -0
  171. package/src/runtime/rsc/symbols.ts +106 -0
  172. package/src/runtime/ssr-shim.d.ts +12 -0
  173. package/src/runtime/types.ts +83 -0
  174. package/src/runtime/utils.tsx +161 -0
  175. package/src/runtime.ts +1 -0
  176. package/tests/router/cli.test.ts +386 -0
  177. package/tests/router/dataMutation.test.tsx +396 -0
  178. package/tests/router/prefetchLink.test.tsx +43 -0
  179. package/tests/router/routeTree.test.ts +502 -0
  180. package/tests/router/rsc.test.tsx +256 -0
  181. package/tests/router/tanstackTypes.test.ts +62 -0
  182. package/tsconfig.json +12 -0
  183. package/tsconfig.tsgo.json +6 -0
@@ -0,0 +1,942 @@
1
+ import type { RouteObject } from '@modern-js/runtime-utils/router';
2
+ import type { NestedRoute, PageRoute } from '@modern-js/types';
3
+ import type {
4
+ AnyRoute,
5
+ AnyRouter,
6
+ RootRoute as TanstackRootRoute,
7
+ } from '@tanstack/react-router';
8
+ import {
9
+ createRootRoute,
10
+ createRoute,
11
+ notFound,
12
+ redirect,
13
+ } from '@tanstack/react-router';
14
+ import { DefaultNotFound } from './DefaultNotFound';
15
+ import {
16
+ isTanstackRscPayloadNavigationEnabled,
17
+ loadTanstackRscRouteData,
18
+ } from './rsc/payloadRouter';
19
+
20
+ type RouteParams = Record<string, string>;
21
+
22
+ type ModernLoader = (args: {
23
+ request: Request;
24
+ params: RouteParams;
25
+ context?: unknown;
26
+ }) => unknown | Promise<unknown>;
27
+
28
+ type ModernShouldRevalidate = (args: {
29
+ currentParams: RouteParams;
30
+ currentUrl: URL;
31
+ nextParams: RouteParams;
32
+ nextUrl: URL;
33
+ defaultShouldRevalidate?: boolean;
34
+ }) => boolean | undefined;
35
+
36
+ type TanstackLoaderContext = {
37
+ abortController?: AbortController;
38
+ cause?: string;
39
+ signal?: AbortSignal;
40
+ context?: {
41
+ request?: Request;
42
+ requestContext?: unknown;
43
+ };
44
+ location?:
45
+ | string
46
+ | {
47
+ publicHref?: string;
48
+ href?: string;
49
+ url?: { href?: string };
50
+ };
51
+ params?: RouteParams;
52
+ route?: {
53
+ id?: string;
54
+ };
55
+ };
56
+
57
+ type RouteRevalidationState = {
58
+ currentParams?: RouteParams;
59
+ currentUrl?: URL;
60
+ };
61
+
62
+ type ModernRouteObject = RouteObject & {
63
+ ErrorBoundary?: unknown;
64
+ HydrateFallback?: unknown;
65
+ action?: unknown;
66
+ clientData?: unknown;
67
+ config?: { handle?: Record<string, unknown> } | unknown;
68
+ file?: string;
69
+ handle?: Record<string, unknown>;
70
+ hasAction?: boolean;
71
+ hasClientLoader?: boolean;
72
+ hasLoader?: boolean;
73
+ inValidSSRRoute?: boolean;
74
+ isClientComponent?: boolean;
75
+ lazyImport?: () => unknown;
76
+ loader?: ModernLoader;
77
+ pendingComponent?: unknown;
78
+ shouldRevalidate?: ModernShouldRevalidate;
79
+ };
80
+
81
+ type ModernGeneratedRoute = (NestedRoute | PageRoute) & {
82
+ _component?: string;
83
+ action?: unknown;
84
+ children?: ModernGeneratedRoute[];
85
+ component?: unknown;
86
+ config?: { handle?: Record<string, unknown> } | unknown;
87
+ clientData?: unknown;
88
+ data?: string;
89
+ error?: unknown;
90
+ errorComponent?: unknown;
91
+ filename?: string;
92
+ handle?: Record<string, unknown>;
93
+ hasAction?: boolean;
94
+ hasClientLoader?: boolean;
95
+ hasLoader?: boolean;
96
+ inValidSSRRoute?: boolean;
97
+ id?: string;
98
+ index?: boolean;
99
+ isClientComponent?: boolean;
100
+ isRoot?: boolean;
101
+ lazyImport?: () => unknown;
102
+ loader?: ModernLoader;
103
+ loading?: unknown;
104
+ pendingComponent?: unknown;
105
+ path?: string;
106
+ shouldRevalidate?: ModernShouldRevalidate;
107
+ };
108
+
109
+ type MutableTanstackRoute = AnyRoute & {
110
+ addChildren: (children: AnyRoute[]) => void;
111
+ };
112
+
113
+ type TanstackRouteOptions = Record<string, unknown>;
114
+ type TanstackRootRouteOptions = Record<string, unknown>;
115
+ type ModernTanstackRootRoute = TanstackRootRoute;
116
+ type ModernDeferredDataLike = {
117
+ __modern_deferred?: unknown;
118
+ data?: unknown;
119
+ };
120
+ type RouteTreeOptions = {
121
+ rscPayloadRouter?: boolean;
122
+ };
123
+
124
+ function createTanstackRoute(
125
+ options: TanstackRouteOptions,
126
+ ): MutableTanstackRoute {
127
+ return createRoute(options as never) as unknown as MutableTanstackRoute;
128
+ }
129
+
130
+ function createTanstackRootRoute(
131
+ options: TanstackRootRouteOptions,
132
+ ): MutableTanstackRoute {
133
+ return createRootRoute(options as never) as unknown as MutableTanstackRoute;
134
+ }
135
+
136
+ function toTanstackPath(pathname: string): string {
137
+ // TanStack Router uses `$param` and `$` (splat) style params.
138
+ // Modern's conventional routing currently generates React Router style params (e.g. `:id`, `*`).
139
+ //
140
+ // We only convert the subset Modern generates today:
141
+ // - `:id` -> `$id`
142
+ // - `:id?` -> `{-$id}` (optional param)
143
+ // - `*` -> `$`
144
+ return pathname
145
+ .split('/')
146
+ .map(segment => {
147
+ if (!segment) {
148
+ return segment;
149
+ }
150
+ if (segment === '*') {
151
+ return '$';
152
+ }
153
+ if (segment.startsWith(':')) {
154
+ const name = segment.slice(1);
155
+ if (name.endsWith('?')) {
156
+ return `{-$${name.slice(0, -1)}}`;
157
+ }
158
+ return `$${name}`;
159
+ }
160
+ return segment;
161
+ })
162
+ .join('/');
163
+ }
164
+
165
+ function isResponse(value: unknown): value is Response {
166
+ const record = value as { headers?: unknown; status?: unknown } | null;
167
+ return (
168
+ record != null &&
169
+ typeof record === 'object' &&
170
+ typeof record.status === 'number' &&
171
+ typeof record.headers === 'object'
172
+ );
173
+ }
174
+
175
+ function isTanstackRedirect(value: unknown): boolean {
176
+ return (
177
+ isResponse(value) &&
178
+ typeof (value as { options?: unknown }).options === 'object'
179
+ );
180
+ }
181
+
182
+ const redirectStatusCodes = new Set([301, 302, 303, 307, 308]);
183
+ function isRedirectResponse(res: Response) {
184
+ return redirectStatusCodes.has(res.status);
185
+ }
186
+
187
+ function isModernDeferredData(
188
+ value: unknown,
189
+ ): value is ModernDeferredDataLike & { data: Record<string, unknown> } {
190
+ if (!value || typeof value !== 'object') {
191
+ return false;
192
+ }
193
+
194
+ const deferred = value as ModernDeferredDataLike;
195
+ return (
196
+ deferred.__modern_deferred === true &&
197
+ Boolean(deferred.data) &&
198
+ typeof deferred.data === 'object' &&
199
+ !Array.isArray(deferred.data)
200
+ );
201
+ }
202
+
203
+ function normalizeModernLoaderResult(result: unknown): unknown {
204
+ return isModernDeferredData(result) ? result.data : result;
205
+ }
206
+
207
+ function normalizeModernLoaderResponse(result: unknown): unknown {
208
+ if (isResponse(result)) {
209
+ if (isRedirectResponse(result)) {
210
+ const location = result.headers.get('Location') || '/';
211
+ throwTanstackRedirect(location);
212
+ }
213
+ if (result.status === 404) {
214
+ throw notFound();
215
+ }
216
+ }
217
+
218
+ return normalizeModernLoaderResult(result);
219
+ }
220
+
221
+ function isAbsoluteUrl(value: string) {
222
+ try {
223
+ void new URL(value);
224
+ return true;
225
+ } catch {
226
+ return false;
227
+ }
228
+ }
229
+
230
+ function throwTanstackRedirect(location: string) {
231
+ const target = location || '/';
232
+ // Prefer `to` for internal/relative redirects so basepath can be applied.
233
+ // Use `href` for absolute redirects (external).
234
+ if (isAbsoluteUrl(target)) {
235
+ throw redirect({ href: target });
236
+ }
237
+
238
+ throw redirect({ to: target });
239
+ }
240
+
241
+ function mapParamsForModernLoader({
242
+ modernRoute,
243
+ params,
244
+ }: {
245
+ modernRoute: NestedRoute | PageRoute;
246
+ params: RouteParams;
247
+ }) {
248
+ // React Router uses `*` for splat params, TanStack Router uses `_splat`.
249
+ if (modernRoute.type === 'nested' && modernRoute.path?.includes('*')) {
250
+ const { _splat, ...rest } = params as RouteParams & {
251
+ _splat?: string;
252
+ };
253
+ if (typeof _splat !== 'undefined') {
254
+ return { ...rest, '*': _splat };
255
+ }
256
+ return rest;
257
+ }
258
+ return params;
259
+ }
260
+
261
+ function createModernRequest(input: string, signal: AbortSignal) {
262
+ return new Request(input, { signal });
263
+ }
264
+
265
+ function getModernUrlFromLoaderContext(ctx: TanstackLoaderContext): URL {
266
+ const href =
267
+ typeof ctx?.location === 'string'
268
+ ? ctx.location
269
+ : ctx?.location?.publicHref ||
270
+ ctx?.location?.href ||
271
+ ctx?.location?.url?.href ||
272
+ '/';
273
+ const baseRequest: Request | undefined =
274
+ ctx?.context?.request instanceof Request ? ctx.context.request : undefined;
275
+ const baseUrl =
276
+ baseRequest?.url ||
277
+ (typeof window !== 'undefined' ? window.origin : 'http://localhost');
278
+
279
+ return new URL(href || '/', baseUrl);
280
+ }
281
+
282
+ function rememberRouteLocation(
283
+ state: RouteRevalidationState,
284
+ ctx: TanstackLoaderContext,
285
+ ) {
286
+ state.currentUrl = getModernUrlFromLoaderContext(ctx);
287
+ state.currentParams = ctx?.params || {};
288
+ }
289
+
290
+ function createModernShouldReload(
291
+ shouldRevalidate: unknown,
292
+ state: RouteRevalidationState,
293
+ ) {
294
+ if (typeof shouldRevalidate !== 'function') {
295
+ return undefined;
296
+ }
297
+ const revalidate = shouldRevalidate as ModernShouldRevalidate;
298
+
299
+ return (ctx: TanstackLoaderContext) => {
300
+ const nextUrl = getModernUrlFromLoaderContext(ctx);
301
+ const nextParams = ctx?.params || {};
302
+ const result = revalidate({
303
+ currentUrl: state.currentUrl || nextUrl,
304
+ currentParams: state.currentParams || nextParams,
305
+ nextUrl,
306
+ nextParams,
307
+ // Returning undefined keeps TanStack's native stale-reload default.
308
+ defaultShouldRevalidate: undefined,
309
+ });
310
+
311
+ state.currentUrl = nextUrl;
312
+ state.currentParams = nextParams;
313
+
314
+ return typeof result === 'boolean' ? result : undefined;
315
+ };
316
+ }
317
+
318
+ function wrapModernLoader(
319
+ modernRoute: NestedRoute | PageRoute,
320
+ modernLoader: ModernLoader | undefined,
321
+ revalidationState?: RouteRevalidationState,
322
+ options: RouteTreeOptions = {},
323
+ ) {
324
+ const route = modernRoute as ModernGeneratedRoute;
325
+ return async (ctx: TanstackLoaderContext) => {
326
+ try {
327
+ if (revalidationState) {
328
+ rememberRouteLocation(revalidationState, ctx);
329
+ }
330
+
331
+ if (typeof route.lazyImport === 'function') {
332
+ try {
333
+ await route.lazyImport();
334
+ } catch {}
335
+ }
336
+
337
+ const signal: AbortSignal =
338
+ ctx?.abortController?.signal ||
339
+ ctx?.signal ||
340
+ new AbortController().signal;
341
+ const baseRequest: Request | undefined =
342
+ ctx?.context?.request instanceof Request
343
+ ? ctx.context.request
344
+ : undefined;
345
+
346
+ const href =
347
+ typeof ctx?.location === 'string'
348
+ ? ctx.location
349
+ : ctx?.location?.publicHref ||
350
+ ctx?.location?.href ||
351
+ ctx?.location?.url?.href ||
352
+ '';
353
+
354
+ const request = baseRequest
355
+ ? new Request(baseRequest, { signal })
356
+ : createModernRequest(href, signal);
357
+ const params = mapParamsForModernLoader({
358
+ modernRoute,
359
+ params: ctx.params || {},
360
+ });
361
+
362
+ const loadModernData = async () => {
363
+ const result = modernLoader
364
+ ? await modernLoader({
365
+ request,
366
+ params,
367
+ context: ctx?.context?.requestContext,
368
+ })
369
+ : null;
370
+
371
+ return normalizeModernLoaderResponse(result);
372
+ };
373
+
374
+ if (options.rscPayloadRouter && isTanstackRscPayloadNavigationEnabled()) {
375
+ return loadTanstackRscRouteData({
376
+ hasClientLoader:
377
+ route.hasClientLoader || typeof route.clientData !== 'undefined',
378
+ loadClientData: loadModernData,
379
+ request,
380
+ routeId: ctx.route?.id,
381
+ });
382
+ }
383
+
384
+ return loadModernData();
385
+ } catch (err) {
386
+ if (isResponse(err)) {
387
+ if (isTanstackRedirect(err)) {
388
+ throw err;
389
+ }
390
+ if (isRedirectResponse(err)) {
391
+ const location = err.headers.get('Location') || '/';
392
+ throwTanstackRedirect(location);
393
+ }
394
+ if (err.status === 404) {
395
+ throw notFound();
396
+ }
397
+ }
398
+ throw err;
399
+ }
400
+ };
401
+ }
402
+
403
+ function isRouteObjectPathlessLayout(route: RouteObject) {
404
+ return !route.path && !route.index;
405
+ }
406
+
407
+ function isRouteObjectSplatRoute(route: RouteObject) {
408
+ return typeof route.path === 'string' && route.path.includes('*');
409
+ }
410
+
411
+ function mapParamsForRouteObjectLoader({
412
+ route,
413
+ params,
414
+ }: {
415
+ route: RouteObject;
416
+ params: RouteParams;
417
+ }) {
418
+ if (isRouteObjectSplatRoute(route)) {
419
+ const { _splat, ...rest } = params as RouteParams & {
420
+ _splat?: string;
421
+ };
422
+ if (typeof _splat !== 'undefined') {
423
+ return { ...rest, '*': _splat };
424
+ }
425
+ return rest;
426
+ }
427
+ return params;
428
+ }
429
+
430
+ function wrapRouteObjectLoader(
431
+ route: RouteObject,
432
+ revalidationState?: RouteRevalidationState,
433
+ options: RouteTreeOptions = {},
434
+ ) {
435
+ const modernRoute = route as ModernRouteObject;
436
+ const routeLoader = modernRoute.loader;
437
+ if (typeof routeLoader !== 'function') {
438
+ return undefined;
439
+ }
440
+
441
+ return async (ctx: TanstackLoaderContext) => {
442
+ try {
443
+ if (revalidationState) {
444
+ rememberRouteLocation(revalidationState, ctx);
445
+ }
446
+
447
+ if (typeof modernRoute.lazyImport === 'function') {
448
+ try {
449
+ await modernRoute.lazyImport();
450
+ } catch {}
451
+ }
452
+
453
+ const signal: AbortSignal =
454
+ ctx?.abortController?.signal ||
455
+ ctx?.signal ||
456
+ new AbortController().signal;
457
+ const baseRequest: Request | undefined =
458
+ ctx?.context?.request instanceof Request
459
+ ? ctx.context.request
460
+ : undefined;
461
+
462
+ const href =
463
+ typeof ctx?.location === 'string'
464
+ ? ctx.location
465
+ : ctx?.location?.publicHref ||
466
+ ctx?.location?.href ||
467
+ ctx?.location?.url?.href ||
468
+ '';
469
+
470
+ const request = baseRequest
471
+ ? new Request(baseRequest, { signal })
472
+ : createModernRequest(href, signal);
473
+
474
+ const params = mapParamsForRouteObjectLoader({
475
+ route,
476
+ params: ctx.params || {},
477
+ });
478
+
479
+ const loadModernData = async () => {
480
+ const result = await routeLoader({
481
+ request,
482
+ params,
483
+ context: ctx?.context?.requestContext,
484
+ });
485
+
486
+ return normalizeModernLoaderResponse(result);
487
+ };
488
+
489
+ if (options.rscPayloadRouter && isTanstackRscPayloadNavigationEnabled()) {
490
+ return loadTanstackRscRouteData({
491
+ hasClientLoader:
492
+ modernRoute.hasClientLoader ||
493
+ typeof modernRoute.clientData !== 'undefined',
494
+ loadClientData: loadModernData,
495
+ request,
496
+ routeId: ctx.route?.id,
497
+ });
498
+ }
499
+
500
+ return loadModernData();
501
+ } catch (err) {
502
+ if (isResponse(err)) {
503
+ if (isTanstackRedirect(err)) {
504
+ throw err;
505
+ }
506
+ if (isRedirectResponse(err)) {
507
+ const location = err.headers.get('Location') || '/';
508
+ throwTanstackRedirect(location);
509
+ }
510
+ if (err.status === 404) {
511
+ throw notFound();
512
+ }
513
+ }
514
+ throw err;
515
+ }
516
+ };
517
+ }
518
+
519
+ function toRouteComponent(routeObject: RouteObject): unknown {
520
+ const route = routeObject as ModernRouteObject;
521
+ if (route.Component) {
522
+ return route.Component;
523
+ }
524
+ const element = route.element;
525
+ if (element) {
526
+ return () => element;
527
+ }
528
+ return undefined;
529
+ }
530
+
531
+ function toErrorComponent(routeObject: RouteObject): unknown {
532
+ const route = routeObject as ModernRouteObject;
533
+ if (route.ErrorBoundary) {
534
+ return route.ErrorBoundary;
535
+ }
536
+ if (route.errorElement) {
537
+ return () => route.errorElement;
538
+ }
539
+ return undefined;
540
+ }
541
+
542
+ function toPendingComponent(routeObject: RouteObject): unknown {
543
+ const route = routeObject as ModernRouteObject;
544
+ return route.HydrateFallback || route.pendingComponent || undefined;
545
+ }
546
+
547
+ function mergeModernRouteHandle(route: {
548
+ config?: { handle?: Record<string, unknown> } | unknown;
549
+ handle?: Record<string, unknown>;
550
+ }) {
551
+ const config = route.config as { handle?: Record<string, unknown> } | null;
552
+ const handle = {
553
+ ...route.handle,
554
+ ...(config && typeof config === 'object' ? config.handle : {}),
555
+ };
556
+
557
+ return Object.keys(handle).length > 0 ? handle : undefined;
558
+ }
559
+
560
+ function createRouteStaticData(opts: {
561
+ modernRouteId?: string;
562
+ modernRouteAction?: unknown;
563
+ modernRouteHandle?: unknown;
564
+ modernRouteHasAction?: boolean;
565
+ modernRouteHasClientLoader?: boolean;
566
+ modernRouteHasLoader?: boolean;
567
+ modernRouteIsClientComponent?: boolean;
568
+ modernRouteLoader?: unknown;
569
+ modernRouteShouldRevalidate?: unknown;
570
+ }) {
571
+ const staticData: Record<string, unknown> = {};
572
+
573
+ if (opts.modernRouteId) {
574
+ staticData.modernRouteId = opts.modernRouteId;
575
+ }
576
+
577
+ if (opts.modernRouteAction) {
578
+ staticData.modernRouteAction = opts.modernRouteAction;
579
+ }
580
+
581
+ if (opts.modernRouteHandle) {
582
+ staticData.modernRouteHandle = opts.modernRouteHandle;
583
+ }
584
+
585
+ if (opts.modernRouteHasAction) {
586
+ staticData.modernRouteHasAction = true;
587
+ }
588
+
589
+ if (opts.modernRouteHasClientLoader) {
590
+ staticData.modernRouteHasClientLoader = true;
591
+ }
592
+
593
+ if (opts.modernRouteHasLoader) {
594
+ staticData.modernRouteHasLoader = true;
595
+ }
596
+
597
+ if (opts.modernRouteIsClientComponent) {
598
+ staticData.modernRouteIsClientComponent = true;
599
+ }
600
+
601
+ if (opts.modernRouteLoader) {
602
+ staticData.modernRouteLoader = opts.modernRouteLoader;
603
+ }
604
+
605
+ if (opts.modernRouteShouldRevalidate) {
606
+ staticData.modernRouteShouldRevalidate = opts.modernRouteShouldRevalidate;
607
+ }
608
+
609
+ return Object.keys(staticData).length > 0 ? staticData : undefined;
610
+ }
611
+
612
+ function createRouteFromRouteObject(opts: {
613
+ options?: RouteTreeOptions;
614
+ parent: AnyRoute;
615
+ routeObject: RouteObject;
616
+ }): AnyRoute {
617
+ const { options = {}, parent, routeObject } = opts;
618
+ const modernRouteObject = routeObject as ModernRouteObject;
619
+ const revalidationState: RouteRevalidationState = {};
620
+ const shouldRevalidate = modernRouteObject.shouldRevalidate;
621
+ const shouldReload = createModernShouldReload(
622
+ shouldRevalidate,
623
+ revalidationState,
624
+ );
625
+
626
+ const stableFallbackId =
627
+ routeObject.id || modernRouteObject.file || routeObject.path || 'pathless';
628
+
629
+ const base: TanstackRouteOptions = {
630
+ getParentRoute: () => parent,
631
+ component: toRouteComponent(routeObject),
632
+ pendingComponent: toPendingComponent(routeObject),
633
+ errorComponent: toErrorComponent(routeObject),
634
+ wrapInSuspense: true,
635
+ staticData: createRouteStaticData({
636
+ modernRouteId: routeObject.id,
637
+ modernRouteAction: modernRouteObject.action,
638
+ modernRouteHandle: mergeModernRouteHandle(modernRouteObject),
639
+ modernRouteHasAction:
640
+ modernRouteObject.hasAction || Boolean(modernRouteObject.action),
641
+ modernRouteHasClientLoader:
642
+ modernRouteObject.hasClientLoader ||
643
+ typeof modernRouteObject.clientData !== 'undefined',
644
+ modernRouteHasLoader:
645
+ modernRouteObject.hasLoader ||
646
+ typeof modernRouteObject.loader === 'function',
647
+ modernRouteIsClientComponent: modernRouteObject.isClientComponent,
648
+ modernRouteLoader: modernRouteObject.loader,
649
+ modernRouteShouldRevalidate: shouldRevalidate,
650
+ }),
651
+ loader: wrapRouteObjectLoader(routeObject, revalidationState, options),
652
+ };
653
+ if (modernRouteObject.inValidSSRRoute) {
654
+ base.ssr = false;
655
+ }
656
+ if (shouldReload) {
657
+ base.shouldReload = shouldReload;
658
+ }
659
+
660
+ if (isRouteObjectPathlessLayout(routeObject)) {
661
+ base.id = stableFallbackId;
662
+ } else {
663
+ base.path = routeObject.index
664
+ ? '/'
665
+ : toTanstackPath((routeObject.path as string) || '');
666
+ }
667
+
668
+ const route = createTanstackRoute(base);
669
+
670
+ const children = routeObject.children;
671
+ if (children && children.length > 0) {
672
+ const childRoutes = children.map((child: RouteObject) =>
673
+ createRouteFromRouteObject({
674
+ options,
675
+ parent: route,
676
+ routeObject: child,
677
+ }),
678
+ );
679
+ route.addChildren(childRoutes);
680
+ }
681
+
682
+ return route;
683
+ }
684
+
685
+ function createRouteFromModernRoute(opts: {
686
+ options?: RouteTreeOptions;
687
+ parent: AnyRoute;
688
+ modernRoute: NestedRoute | PageRoute;
689
+ }): AnyRoute {
690
+ const { options = {}, parent, modernRoute } = opts;
691
+ const route = modernRoute as ModernGeneratedRoute;
692
+ const revalidationState: RouteRevalidationState = {};
693
+
694
+ const modernId = route.id;
695
+ const stableFallbackId =
696
+ modernId ||
697
+ route._component ||
698
+ route.filename ||
699
+ route.data ||
700
+ (typeof route.loader === 'function' ? route.id : undefined);
701
+
702
+ const pendingComponent = route.loading || route.pendingComponent;
703
+ const errorComponent = route.error || route.errorComponent;
704
+ const component = route.component;
705
+ const modernLoader = route.loader;
706
+ const modernAction = route.action;
707
+ const modernShouldRevalidate = route.shouldRevalidate;
708
+ const shouldReload = createModernShouldReload(
709
+ modernShouldRevalidate,
710
+ revalidationState,
711
+ );
712
+
713
+ // Pathless layout: no path segment, but must remain in the tree.
714
+ const isPathlessLayout =
715
+ route.type === 'nested' &&
716
+ typeof route.index !== 'boolean' &&
717
+ typeof route.path === 'undefined';
718
+
719
+ const isIndexRoute = route.type === 'nested' && Boolean(route.index);
720
+
721
+ const base: TanstackRouteOptions = {
722
+ getParentRoute: () => parent,
723
+ component: component || undefined,
724
+ pendingComponent: pendingComponent || undefined,
725
+ errorComponent: errorComponent || undefined,
726
+ wrapInSuspense: true,
727
+ staticData: createRouteStaticData({
728
+ modernRouteId: modernId,
729
+ modernRouteAction: modernAction,
730
+ modernRouteHandle: mergeModernRouteHandle(route),
731
+ modernRouteHasAction: route.hasAction || Boolean(modernAction),
732
+ modernRouteHasClientLoader:
733
+ route.hasClientLoader || typeof route.clientData !== 'undefined',
734
+ modernRouteHasLoader:
735
+ route.hasLoader || typeof modernLoader === 'function',
736
+ modernRouteIsClientComponent: route.isClientComponent,
737
+ modernRouteLoader: modernLoader,
738
+ modernRouteShouldRevalidate: modernShouldRevalidate,
739
+ }),
740
+ loader: wrapModernLoader(
741
+ modernRoute,
742
+ modernLoader,
743
+ revalidationState,
744
+ options,
745
+ ),
746
+ };
747
+ if (route.inValidSSRRoute) {
748
+ base.ssr = false;
749
+ }
750
+ if (shouldReload) {
751
+ base.shouldReload = shouldReload;
752
+ }
753
+
754
+ if (isPathlessLayout) {
755
+ // Use a stable custom id for pathless layouts to avoid hydration mismatch.
756
+ base.id = stableFallbackId || 'pathless';
757
+ } else {
758
+ const rawPath = route.path;
759
+ base.path = isIndexRoute ? '/' : toTanstackPath(rawPath || '');
760
+ }
761
+
762
+ const tanstackRoute = createTanstackRoute(base);
763
+
764
+ const children = route.children as Array<NestedRoute | PageRoute> | undefined;
765
+ if (children && children.length > 0) {
766
+ const childRoutes = children.map(child =>
767
+ createRouteFromModernRoute({
768
+ options,
769
+ parent: tanstackRoute,
770
+ modernRoute: child,
771
+ }),
772
+ );
773
+ tanstackRoute.addChildren(childRoutes);
774
+ }
775
+
776
+ return tanstackRoute;
777
+ }
778
+
779
+ export function createRouteTreeFromModernRoutes(
780
+ routes: Array<NestedRoute | PageRoute>,
781
+ options: RouteTreeOptions = {},
782
+ ): ModernTanstackRootRoute {
783
+ const rootModern = routes.find(
784
+ r =>
785
+ r &&
786
+ (r as ModernGeneratedRoute).type === 'nested' &&
787
+ (r as ModernGeneratedRoute).isRoot,
788
+ ) as ModernGeneratedRoute | undefined;
789
+
790
+ const rootComponent = rootModern?.component;
791
+ const pendingComponent = rootModern?.loading;
792
+ const errorComponent = rootModern?.error;
793
+ const rootLoader = rootModern?.loader;
794
+ const rootAction = rootModern?.action;
795
+ const rootModernId = rootModern?.id;
796
+ const rootShouldRevalidate = rootModern?.shouldRevalidate;
797
+ const rootRevalidationState: RouteRevalidationState = {};
798
+ const rootShouldReload = createModernShouldReload(
799
+ rootShouldRevalidate,
800
+ rootRevalidationState,
801
+ );
802
+
803
+ const rootRouteOptions: TanstackRootRouteOptions = {
804
+ component: rootComponent || undefined,
805
+ pendingComponent: pendingComponent || undefined,
806
+ errorComponent: errorComponent || undefined,
807
+ wrapInSuspense: true,
808
+ notFoundComponent: DefaultNotFound,
809
+ staticData: createRouteStaticData({
810
+ modernRouteId: rootModernId,
811
+ modernRouteAction: rootAction,
812
+ modernRouteHandle: rootModern
813
+ ? mergeModernRouteHandle(rootModern)
814
+ : undefined,
815
+ modernRouteHasAction: rootModern?.hasAction || Boolean(rootAction),
816
+ modernRouteHasClientLoader:
817
+ rootModern?.hasClientLoader ||
818
+ typeof rootModern?.clientData !== 'undefined',
819
+ modernRouteHasLoader:
820
+ rootModern?.hasLoader || typeof rootLoader === 'function',
821
+ modernRouteIsClientComponent: rootModern?.isClientComponent,
822
+ modernRouteLoader: rootLoader,
823
+ modernRouteShouldRevalidate: rootShouldRevalidate,
824
+ }),
825
+ loader: rootModern
826
+ ? wrapModernLoader(rootModern, rootLoader, rootRevalidationState, options)
827
+ : undefined,
828
+ };
829
+ if (rootShouldReload) {
830
+ rootRouteOptions.shouldReload = rootShouldReload;
831
+ }
832
+ if (rootModern?.inValidSSRRoute) {
833
+ rootRouteOptions.ssr = false;
834
+ }
835
+
836
+ const rootRoute = createTanstackRootRoute(rootRouteOptions);
837
+
838
+ const topLevel = rootModern
839
+ ? (rootModern.children as Array<NestedRoute | PageRoute>) || []
840
+ : routes;
841
+
842
+ const childRoutes = topLevel.map(child =>
843
+ createRouteFromModernRoute({
844
+ options,
845
+ parent: rootRoute,
846
+ modernRoute: child,
847
+ }),
848
+ );
849
+
850
+ rootRoute.addChildren(childRoutes);
851
+ return rootRoute as unknown as ModernTanstackRootRoute;
852
+ }
853
+
854
+ function getRootLikeRouteObject(routes: RouteObject[]) {
855
+ return routes.find(route => route.path === '/' && !route.index);
856
+ }
857
+
858
+ export function createRouteTreeFromRouteObjects(
859
+ routes: RouteObject[],
860
+ options: RouteTreeOptions = {},
861
+ ): ModernTanstackRootRoute {
862
+ const rootLikeRoute = getRootLikeRouteObject(routes) as
863
+ | ModernRouteObject
864
+ | undefined;
865
+ const rootRevalidationState: RouteRevalidationState = {};
866
+ const rootShouldRevalidate = rootLikeRoute?.shouldRevalidate;
867
+ const rootShouldReload = createModernShouldReload(
868
+ rootShouldRevalidate,
869
+ rootRevalidationState,
870
+ );
871
+
872
+ const rootRouteOptions: TanstackRootRouteOptions = {
873
+ component: rootLikeRoute ? toRouteComponent(rootLikeRoute) : undefined,
874
+ pendingComponent: rootLikeRoute
875
+ ? toPendingComponent(rootLikeRoute)
876
+ : undefined,
877
+ errorComponent: rootLikeRoute ? toErrorComponent(rootLikeRoute) : undefined,
878
+ wrapInSuspense: true,
879
+ notFoundComponent: DefaultNotFound,
880
+ staticData: createRouteStaticData({
881
+ modernRouteId: rootLikeRoute?.id,
882
+ modernRouteAction: rootLikeRoute?.action,
883
+ modernRouteHandle: rootLikeRoute
884
+ ? mergeModernRouteHandle(rootLikeRoute)
885
+ : undefined,
886
+ modernRouteHasAction:
887
+ rootLikeRoute?.hasAction || Boolean(rootLikeRoute?.action),
888
+ modernRouteHasClientLoader:
889
+ rootLikeRoute?.hasClientLoader ||
890
+ typeof rootLikeRoute?.clientData !== 'undefined',
891
+ modernRouteHasLoader:
892
+ rootLikeRoute?.hasLoader || typeof rootLikeRoute?.loader === 'function',
893
+ modernRouteIsClientComponent: rootLikeRoute?.isClientComponent,
894
+ modernRouteLoader: rootLikeRoute?.loader,
895
+ modernRouteShouldRevalidate: rootShouldRevalidate,
896
+ }),
897
+ loader: rootLikeRoute
898
+ ? wrapRouteObjectLoader(rootLikeRoute, rootRevalidationState, options)
899
+ : undefined,
900
+ };
901
+ if (rootShouldReload) {
902
+ rootRouteOptions.shouldReload = rootShouldReload;
903
+ }
904
+ if (rootLikeRoute?.inValidSSRRoute) {
905
+ rootRouteOptions.ssr = false;
906
+ }
907
+
908
+ const rootRoute = createTanstackRootRoute(rootRouteOptions);
909
+
910
+ const topLevel = rootLikeRoute
911
+ ? [
912
+ ...((rootLikeRoute.children as RouteObject[] | undefined) || []),
913
+ ...routes.filter(route => route !== rootLikeRoute),
914
+ ]
915
+ : routes;
916
+
917
+ const childRoutes = topLevel.map(routeObject =>
918
+ createRouteFromRouteObject({ options, parent: rootRoute, routeObject }),
919
+ );
920
+
921
+ rootRoute.addChildren(childRoutes);
922
+ return rootRoute as unknown as ModernTanstackRootRoute;
923
+ }
924
+
925
+ export function getModernRouteIdsFromMatches(router: AnyRouter): string[] {
926
+ const matches = router.state.matches || [];
927
+ const ids = matches
928
+ .map(match => {
929
+ const route = (
930
+ match as {
931
+ route?: {
932
+ options?: {
933
+ staticData?: { modernRouteId?: unknown };
934
+ };
935
+ };
936
+ }
937
+ ).route;
938
+ return route?.options?.staticData?.modernRouteId;
939
+ })
940
+ .filter((id): id is string => typeof id === 'string');
941
+ return Array.from(new Set(ids));
942
+ }