@bleedingdev/modern-js-plugin-tanstack 3.2.0-ultramodern.5 → 3.2.0-ultramodern.50
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.
- package/dist/cjs/cli/index.js +3 -6
- package/dist/cjs/cli/tanstackTypes.js +20 -6
- package/dist/cjs/runtime/plugin.js +2 -0
- package/dist/cjs/runtime/plugin.node.js +17 -1
- package/dist/cjs/runtime/plugin.worker.js +49 -0
- package/dist/cjs/runtime/routeTree.js +38 -2
- package/dist/esm/cli/index.mjs +3 -6
- package/dist/esm/cli/tanstackTypes.mjs +20 -6
- package/dist/esm/runtime/plugin.mjs +2 -0
- package/dist/esm/runtime/plugin.node.mjs +17 -1
- package/dist/esm/runtime/plugin.worker.mjs +1 -0
- package/dist/esm/runtime/routeTree.mjs +38 -2
- package/dist/esm-node/cli/index.mjs +3 -6
- package/dist/esm-node/cli/tanstackTypes.mjs +20 -6
- package/dist/esm-node/runtime/plugin.mjs +2 -0
- package/dist/esm-node/runtime/plugin.node.mjs +17 -1
- package/dist/esm-node/runtime/plugin.worker.mjs +2 -0
- package/dist/esm-node/runtime/routeTree.mjs +38 -2
- package/dist/types/runtime/plugin.worker.d.ts +1 -0
- package/package.json +13 -13
- package/src/cli/index.ts +15 -18
- package/src/cli/tanstackTypes.ts +34 -9
- package/src/runtime/basepathRewrite.ts +1 -0
- package/src/runtime/dataMutation.tsx +1 -0
- package/src/runtime/lifecycle.ts +1 -0
- package/src/runtime/plugin.node.tsx +43 -1
- package/src/runtime/plugin.tsx +3 -0
- package/src/runtime/plugin.worker.tsx +4 -0
- package/src/runtime/routeTree.ts +102 -2
- package/src/runtime/rsc/ClientSlot.tsx +1 -0
- package/src/runtime/rsc/CompositeComponent.tsx +1 -0
- package/src/runtime/rsc/ReplayableStream.ts +1 -0
- package/src/runtime/rsc/RscNodeRenderer.tsx +1 -0
- package/src/runtime/rsc/client.tsx +2 -3
- package/src/runtime/rsc/createRscProxy.tsx +1 -0
- package/src/runtime/rsc/payloadRouter.ts +1 -0
- package/src/runtime/rsc/server.tsx +1 -0
- package/src/runtime/rsc/slotUsageSanitizer.ts +1 -0
- package/src/runtime/utils.tsx +1 -0
- package/tests/router/routeTree.test.ts +72 -1
- package/tests/router/tanstackTypes.test.ts +64 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import "node:module";
|
|
2
2
|
import { createRootRoute, createRoute, notFound, redirect } from "@tanstack/react-router";
|
|
3
|
+
import { createElement } from "react";
|
|
3
4
|
import { DefaultNotFound } from "./DefaultNotFound.mjs";
|
|
4
5
|
import { isTanstackRscPayloadNavigationEnabled, loadTanstackRscRouteData } from "./rsc/payloadRouter.mjs";
|
|
5
6
|
function createTanstackRoute(options) {
|
|
@@ -55,6 +56,33 @@ function normalizeModernLoaderResponse(result) {
|
|
|
55
56
|
}
|
|
56
57
|
return normalizeModernLoaderResult(result);
|
|
57
58
|
}
|
|
59
|
+
function pickRouteModuleComponent(routeModule) {
|
|
60
|
+
if ('function' == typeof routeModule || routeModule && 'object' == typeof routeModule && '$$typeof' in routeModule) return routeModule;
|
|
61
|
+
if (!routeModule || 'object' != typeof routeModule) return;
|
|
62
|
+
const module = routeModule;
|
|
63
|
+
const component = module.default || module.Component;
|
|
64
|
+
if ('function' == typeof component || component && 'object' == typeof component && '$$typeof' in component) return component;
|
|
65
|
+
}
|
|
66
|
+
function createServerLazyImportComponent(lazyImport, fallbackComponent) {
|
|
67
|
+
if ("u" > typeof document) return fallbackComponent;
|
|
68
|
+
let resolvedComponent;
|
|
69
|
+
let pendingLoad;
|
|
70
|
+
const load = async ()=>{
|
|
71
|
+
if (resolvedComponent) return resolvedComponent;
|
|
72
|
+
const routeModule = await lazyImport();
|
|
73
|
+
const component = pickRouteModuleComponent(routeModule);
|
|
74
|
+
if (component) resolvedComponent = component;
|
|
75
|
+
return resolvedComponent;
|
|
76
|
+
};
|
|
77
|
+
const Component = (props)=>{
|
|
78
|
+
if (resolvedComponent) return createElement(resolvedComponent, props);
|
|
79
|
+
pendingLoad ||= load();
|
|
80
|
+
throw pendingLoad;
|
|
81
|
+
};
|
|
82
|
+
Component.load = load;
|
|
83
|
+
Component.preload = load;
|
|
84
|
+
return Component;
|
|
85
|
+
}
|
|
58
86
|
function isAbsoluteUrl(value) {
|
|
59
87
|
try {
|
|
60
88
|
new URL(value);
|
|
@@ -229,10 +257,18 @@ function wrapRouteObjectLoader(route, revalidationState, options = {}) {
|
|
|
229
257
|
}
|
|
230
258
|
function toRouteComponent(routeObject) {
|
|
231
259
|
const route = routeObject;
|
|
260
|
+
const lazyImport = 'function' == typeof route.lazyImport ? route.lazyImport : void 0;
|
|
261
|
+
const fallbackComponent = route.Component ? route.Component : route.element ? ()=>route.element : void 0;
|
|
262
|
+
if (lazyImport && fallbackComponent) return createServerLazyImportComponent(lazyImport, fallbackComponent);
|
|
232
263
|
if (route.Component) return route.Component;
|
|
233
264
|
const element = route.element;
|
|
234
265
|
if (element) return ()=>element;
|
|
235
266
|
}
|
|
267
|
+
function toModernRouteComponent(route) {
|
|
268
|
+
const component = route.component || void 0;
|
|
269
|
+
if ('function' == typeof route.lazyImport && component) return createServerLazyImportComponent(route.lazyImport, component);
|
|
270
|
+
return component;
|
|
271
|
+
}
|
|
236
272
|
function toErrorComponent(routeObject) {
|
|
237
273
|
const route = routeObject;
|
|
238
274
|
if (route.ErrorBoundary) return route.ErrorBoundary;
|
|
@@ -313,7 +349,7 @@ function createRouteFromModernRoute(opts) {
|
|
|
313
349
|
const stableFallbackId = modernId || route._component || route.filename || route.data || ('function' == typeof route.loader ? route.id : void 0);
|
|
314
350
|
const pendingComponent = route.loading || route.pendingComponent;
|
|
315
351
|
const errorComponent = route.error || route.errorComponent;
|
|
316
|
-
const component = route
|
|
352
|
+
const component = toModernRouteComponent(route);
|
|
317
353
|
const modernLoader = route.loader;
|
|
318
354
|
const modernAction = route.action;
|
|
319
355
|
const modernShouldRevalidate = route.shouldRevalidate;
|
|
@@ -360,7 +396,7 @@ function createRouteFromModernRoute(opts) {
|
|
|
360
396
|
}
|
|
361
397
|
function createRouteTreeFromModernRoutes(routes, options = {}) {
|
|
362
398
|
const rootModern = routes.find((r)=>r && 'nested' === r.type && r.isRoot);
|
|
363
|
-
const rootComponent = rootModern
|
|
399
|
+
const rootComponent = rootModern ? toModernRouteComponent(rootModern) : void 0;
|
|
364
400
|
const pendingComponent = rootModern?.loading;
|
|
365
401
|
const errorComponent = rootModern?.error;
|
|
366
402
|
const rootLoader = rootModern?.loader;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default, tanstackRouterPlugin, } from './plugin.node';
|
package/package.json
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"modern.js",
|
|
19
19
|
"tanstack-router"
|
|
20
20
|
],
|
|
21
|
-
"version": "3.2.0-ultramodern.
|
|
21
|
+
"version": "3.2.0-ultramodern.50",
|
|
22
22
|
"engines": {
|
|
23
23
|
"node": ">=20"
|
|
24
24
|
},
|
|
@@ -86,15 +86,15 @@
|
|
|
86
86
|
},
|
|
87
87
|
"dependencies": {
|
|
88
88
|
"@swc/helpers": "^0.5.21",
|
|
89
|
-
"@tanstack/react-router": "1.170.
|
|
90
|
-
"@tanstack/router-core": "1.
|
|
91
|
-
"@modern-js/plugin": "npm:@bleedingdev/modern-js-plugin@3.2.0-ultramodern.
|
|
92
|
-
"@modern-js/runtime-utils": "npm:@bleedingdev/modern-js-runtime-utils@3.2.0-ultramodern.
|
|
93
|
-
"@modern-js/types": "npm:@bleedingdev/modern-js-types@3.2.0-ultramodern.
|
|
94
|
-
"@modern-js/utils": "npm:@bleedingdev/modern-js-utils@3.2.0-ultramodern.
|
|
89
|
+
"@tanstack/react-router": "1.170.8",
|
|
90
|
+
"@tanstack/router-core": "1.171.6",
|
|
91
|
+
"@modern-js/plugin": "npm:@bleedingdev/modern-js-plugin@3.2.0-ultramodern.50",
|
|
92
|
+
"@modern-js/runtime-utils": "npm:@bleedingdev/modern-js-runtime-utils@3.2.0-ultramodern.50",
|
|
93
|
+
"@modern-js/types": "npm:@bleedingdev/modern-js-types@3.2.0-ultramodern.50",
|
|
94
|
+
"@modern-js/utils": "npm:@bleedingdev/modern-js-utils@3.2.0-ultramodern.50"
|
|
95
95
|
},
|
|
96
96
|
"peerDependencies": {
|
|
97
|
-
"@modern-js/runtime": "3.2.0-ultramodern.
|
|
97
|
+
"@modern-js/runtime": "3.2.0-ultramodern.50",
|
|
98
98
|
"react": "^19.2.6",
|
|
99
99
|
"react-dom": "^19.2.6"
|
|
100
100
|
},
|
|
@@ -103,14 +103,14 @@
|
|
|
103
103
|
"@tanstack/history": "1.162.0",
|
|
104
104
|
"@testing-library/dom": "^10.4.1",
|
|
105
105
|
"@testing-library/react": "^16.3.2",
|
|
106
|
-
"@types/node": "^25.
|
|
107
|
-
"@types/react": "^19.2.
|
|
106
|
+
"@types/node": "^25.9.1",
|
|
107
|
+
"@types/react": "^19.2.15",
|
|
108
108
|
"@types/react-dom": "^19.2.3",
|
|
109
|
-
"@typescript/native-preview": "7.0.0-dev.
|
|
109
|
+
"@typescript/native-preview": "7.0.0-dev.20260527.2",
|
|
110
110
|
"react": "^19.2.6",
|
|
111
111
|
"react-dom": "^19.2.6",
|
|
112
|
-
"@modern-js/
|
|
113
|
-
"@modern-js/
|
|
112
|
+
"@modern-js/runtime": "npm:@bleedingdev/modern-js-runtime@3.2.0-ultramodern.50",
|
|
113
|
+
"@modern-js/app-tools": "npm:@bleedingdev/modern-js-app-tools@3.2.0-ultramodern.50",
|
|
114
114
|
"@scripts/rstest-config": "2.66.0"
|
|
115
115
|
},
|
|
116
116
|
"sideEffects": false,
|
package/src/cli/index.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
// @effect-diagnostics asyncFunction:off nodeBuiltinImport:off strictBooleanExpressions:off
|
|
1
2
|
import path from 'node:path';
|
|
2
3
|
import type {
|
|
3
4
|
AppNormalizedConfig,
|
|
@@ -257,24 +258,20 @@ export function tanstackRouterPlugin(
|
|
|
257
258
|
return { entrypoint, plugins };
|
|
258
259
|
});
|
|
259
260
|
|
|
260
|
-
api.checkEntryPoint(({ path: entryPath, entry }) => {
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
],
|
|
275
|
-
},
|
|
276
|
-
};
|
|
277
|
-
});
|
|
261
|
+
api.checkEntryPoint(({ path: entryPath, entry }) => ({
|
|
262
|
+
path: entryPath,
|
|
263
|
+
entry:
|
|
264
|
+
entry || getRuntimeRouterCli().isRouteEntry(entryPath, routesDir),
|
|
265
|
+
}));
|
|
266
|
+
|
|
267
|
+
api.config(() => ({
|
|
268
|
+
source: {
|
|
269
|
+
include: [
|
|
270
|
+
/[\\/]node_modules[\\/]@tanstack[\\/]react-router[\\/]/,
|
|
271
|
+
path.resolve(__dirname, '../runtime').replace('cjs', 'esm'),
|
|
272
|
+
],
|
|
273
|
+
},
|
|
274
|
+
}));
|
|
278
275
|
|
|
279
276
|
api.modifyEntrypoints(async ({ entrypoints }) => {
|
|
280
277
|
const { handleModifyEntrypoints } = getRuntimeRouterCli();
|
package/src/cli/tanstackTypes.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
// @effect-diagnostics asyncFunction:off nodeBuiltinImport:off strictBooleanExpressions:off
|
|
1
2
|
import type { AppToolsContext } from '@modern-js/app-tools';
|
|
2
3
|
import type { NestedRouteForCli, PageRoute } from '@modern-js/types';
|
|
3
4
|
import { findExists, formatImportPath, fs, slash } from '@modern-js/utils';
|
|
@@ -181,6 +182,7 @@ export async function generateTanstackRouterTypesSourceForEntry(opts: {
|
|
|
181
182
|
const statements: string[] = [];
|
|
182
183
|
|
|
183
184
|
const loaderImportMap = new Map<string, string>();
|
|
185
|
+
const usedRouteVarNames = new Set<string>();
|
|
184
186
|
let loaderIndex = 0;
|
|
185
187
|
let routeIndex = 0;
|
|
186
188
|
|
|
@@ -241,10 +243,20 @@ export async function generateTanstackRouterTypesSourceForEntry(opts: {
|
|
|
241
243
|
return { loaderName: importName, actionName };
|
|
242
244
|
};
|
|
243
245
|
|
|
246
|
+
const reserveRouteVarName = (preferred: string) => {
|
|
247
|
+
let candidate = preferred;
|
|
248
|
+
let suffix = 1;
|
|
249
|
+
while (usedRouteVarNames.has(candidate)) {
|
|
250
|
+
candidate = `${preferred}_${suffix++}`;
|
|
251
|
+
}
|
|
252
|
+
usedRouteVarNames.add(candidate);
|
|
253
|
+
return candidate;
|
|
254
|
+
};
|
|
255
|
+
|
|
244
256
|
const createRouteVarName = (route: NestedRouteForCli | PageRoute) => {
|
|
245
257
|
const id = (route as any).id as string | undefined;
|
|
246
258
|
const base = id ? makeLegalIdentifier(id) : `r_${routeIndex++}`;
|
|
247
|
-
return `route_${base}
|
|
259
|
+
return reserveRouteVarName(`route_${base}`);
|
|
248
260
|
};
|
|
249
261
|
|
|
250
262
|
const buildRoute = async (opts: {
|
|
@@ -295,18 +307,27 @@ export async function generateTanstackRouterTypesSourceForEntry(opts: {
|
|
|
295
307
|
routeOpts.push(staticDataSnippet);
|
|
296
308
|
}
|
|
297
309
|
|
|
298
|
-
statements.push(
|
|
299
|
-
`const ${varName} = createRoute({\n ${routeOpts.join('\n ')}\n});`,
|
|
300
|
-
);
|
|
301
|
-
|
|
302
310
|
const children = (route as any).children as
|
|
303
311
|
| Array<NestedRouteForCli | PageRoute>
|
|
304
312
|
| undefined;
|
|
313
|
+
const hasChildren = Boolean(children && children.length > 0);
|
|
314
|
+
const routeCtorVarName = hasChildren
|
|
315
|
+
? reserveRouteVarName(`${varName}__base`)
|
|
316
|
+
: varName;
|
|
317
|
+
|
|
318
|
+
statements.push(
|
|
319
|
+
`const ${routeCtorVarName} = createRoute({\n ${routeOpts.join('\n ')}\n});`,
|
|
320
|
+
);
|
|
321
|
+
|
|
305
322
|
if (children && children.length > 0) {
|
|
306
323
|
const childVars = await Promise.all(
|
|
307
|
-
children.map(child =>
|
|
324
|
+
children.map(child =>
|
|
325
|
+
buildRoute({ parentVar: routeCtorVarName, route: child }),
|
|
326
|
+
),
|
|
327
|
+
);
|
|
328
|
+
statements.push(
|
|
329
|
+
`const ${varName} = ${routeCtorVarName}.addChildren([${childVars.join(', ')}]);`,
|
|
308
330
|
);
|
|
309
|
-
statements.push(`${varName}.addChildren([${childVars.join(', ')}]);`);
|
|
310
331
|
}
|
|
311
332
|
|
|
312
333
|
return varName;
|
|
@@ -395,7 +416,11 @@ function createRouteStaticData(opts: {
|
|
|
395
416
|
modernRouteAction?: unknown;
|
|
396
417
|
modernRouteLoader?: unknown;
|
|
397
418
|
}) {
|
|
398
|
-
const staticData:
|
|
419
|
+
const staticData: {
|
|
420
|
+
modernRouteId?: string;
|
|
421
|
+
modernRouteAction?: unknown;
|
|
422
|
+
modernRouteLoader?: unknown;
|
|
423
|
+
} = {};
|
|
399
424
|
|
|
400
425
|
if (opts.modernRouteId) {
|
|
401
426
|
staticData.modernRouteId = opts.modernRouteId;
|
|
@@ -409,7 +434,7 @@ function createRouteStaticData(opts: {
|
|
|
409
434
|
staticData.modernRouteAction = opts.modernRouteAction;
|
|
410
435
|
}
|
|
411
436
|
|
|
412
|
-
return
|
|
437
|
+
return staticData;
|
|
413
438
|
}
|
|
414
439
|
|
|
415
440
|
function modernLoaderToTanstack<TLoader extends (args: any) => any>(
|
package/src/runtime/lifecycle.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
// @effect-diagnostics asyncFunction:off newPromise:off strictBooleanExpressions:off unnecessaryArrowBlock:off
|
|
1
2
|
/// <reference path="./ssr-shim.d.ts" />
|
|
2
3
|
|
|
3
4
|
import type { Plugin, RuntimePluginExtends } from '@modern-js/plugin';
|
|
@@ -25,7 +26,7 @@ import {
|
|
|
25
26
|
createRouter,
|
|
26
27
|
RouterProvider,
|
|
27
28
|
} from '@tanstack/react-router';
|
|
28
|
-
import { attachRouterServerSsrUtils } from '@tanstack/
|
|
29
|
+
import { attachRouterServerSsrUtils } from '@tanstack/router-core/ssr/server';
|
|
29
30
|
import type React from 'react';
|
|
30
31
|
import { Suspense, useContext } from 'react';
|
|
31
32
|
import { createModernBasepathRewrite } from './basepathRewrite';
|
|
@@ -118,6 +119,17 @@ type PreloadableRouteComponent = {
|
|
|
118
119
|
preload?: (props?: Record<string, unknown>) => Promise<unknown> | unknown;
|
|
119
120
|
};
|
|
120
121
|
|
|
122
|
+
type ReactLazyRouteComponent = {
|
|
123
|
+
_init?: (payload: unknown) => unknown;
|
|
124
|
+
_payload?: unknown;
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
function isPromiseLike(value: unknown): value is PromiseLike<unknown> {
|
|
128
|
+
return Boolean(
|
|
129
|
+
value && typeof (value as PromiseLike<unknown>).then === 'function',
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
|
|
121
133
|
type TanstackRouterWithServerSsr = AnyRouter & {
|
|
122
134
|
resolveRedirect?: (redirect: Response) => Response;
|
|
123
135
|
routesById?: Record<string, RouterRouteWithOptions>;
|
|
@@ -148,7 +160,37 @@ function isPreloadableRouteComponent(
|
|
|
148
160
|
);
|
|
149
161
|
}
|
|
150
162
|
|
|
163
|
+
function isReactLazyRouteComponent(
|
|
164
|
+
component: unknown,
|
|
165
|
+
): component is ReactLazyRouteComponent {
|
|
166
|
+
return (
|
|
167
|
+
Boolean(component) &&
|
|
168
|
+
typeof component === 'object' &&
|
|
169
|
+
typeof (component as ReactLazyRouteComponent)._init === 'function' &&
|
|
170
|
+
'_payload' in component
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
async function preloadReactLazyRouteComponent(
|
|
175
|
+
component: ReactLazyRouteComponent,
|
|
176
|
+
) {
|
|
177
|
+
try {
|
|
178
|
+
component._init?.(component._payload);
|
|
179
|
+
} catch (thrown) {
|
|
180
|
+
if (!isPromiseLike(thrown)) {
|
|
181
|
+
throw thrown;
|
|
182
|
+
}
|
|
183
|
+
await thrown;
|
|
184
|
+
component._init?.(component._payload);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
151
188
|
async function preloadRouteComponent(component: unknown) {
|
|
189
|
+
if (isReactLazyRouteComponent(component)) {
|
|
190
|
+
await preloadReactLazyRouteComponent(component);
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
|
|
152
194
|
if (!isPreloadableRouteComponent(component)) {
|
|
153
195
|
return;
|
|
154
196
|
}
|
package/src/runtime/plugin.tsx
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
// @effect-diagnostics globalConsole:off strictBooleanExpressions:off
|
|
1
2
|
/// <reference path="./ssr-shim.d.ts" />
|
|
2
3
|
|
|
3
4
|
import type { Plugin, RuntimePluginExtends } from '@modern-js/plugin';
|
|
@@ -40,6 +41,7 @@ import {
|
|
|
40
41
|
applyRouterRuntimeState,
|
|
41
42
|
type RouterLifecycleContext,
|
|
42
43
|
} from './lifecycle';
|
|
44
|
+
import { Link } from './prefetchLink';
|
|
43
45
|
import { createRouteTreeFromRouteObjects } from './routeTree';
|
|
44
46
|
import { getTanstackRscSerializationAdapters } from './rsc/client';
|
|
45
47
|
import type { RouterConfig } from './types';
|
|
@@ -183,6 +185,7 @@ export const tanstackRouterPlugin = (
|
|
|
183
185
|
}
|
|
184
186
|
|
|
185
187
|
context.router = {
|
|
188
|
+
Link,
|
|
186
189
|
useMatches,
|
|
187
190
|
useLocation,
|
|
188
191
|
useNavigate,
|
package/src/runtime/routeTree.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
// @effect-diagnostics asyncFunction:off strictBooleanExpressions:off
|
|
1
2
|
import type { RouteObject } from '@modern-js/runtime-utils/router';
|
|
2
3
|
import type { NestedRoute, PageRoute } from '@modern-js/types';
|
|
3
4
|
import type {
|
|
@@ -11,6 +12,7 @@ import {
|
|
|
11
12
|
notFound,
|
|
12
13
|
redirect,
|
|
13
14
|
} from '@tanstack/react-router';
|
|
15
|
+
import { createElement, type ElementType } from 'react';
|
|
14
16
|
import { DefaultNotFound } from './DefaultNotFound';
|
|
15
17
|
import {
|
|
16
18
|
isTanstackRscPayloadNavigationEnabled,
|
|
@@ -117,6 +119,15 @@ type ModernDeferredDataLike = {
|
|
|
117
119
|
__modern_deferred?: unknown;
|
|
118
120
|
data?: unknown;
|
|
119
121
|
};
|
|
122
|
+
type ModernRouteModule = {
|
|
123
|
+
Component?: unknown;
|
|
124
|
+
default?: unknown;
|
|
125
|
+
};
|
|
126
|
+
type PreloadableComponent = {
|
|
127
|
+
(props: Record<string, unknown>): ReturnType<typeof createElement>;
|
|
128
|
+
load?: () => Promise<unknown>;
|
|
129
|
+
preload?: () => Promise<unknown>;
|
|
130
|
+
};
|
|
120
131
|
type RouteTreeOptions = {
|
|
121
132
|
rscPayloadRouter?: boolean;
|
|
122
133
|
};
|
|
@@ -218,6 +229,72 @@ function normalizeModernLoaderResponse(result: unknown): unknown {
|
|
|
218
229
|
return normalizeModernLoaderResult(result);
|
|
219
230
|
}
|
|
220
231
|
|
|
232
|
+
function pickRouteModuleComponent(
|
|
233
|
+
routeModule: unknown,
|
|
234
|
+
): ElementType<Record<string, unknown>> | undefined {
|
|
235
|
+
if (
|
|
236
|
+
typeof routeModule === 'function' ||
|
|
237
|
+
(routeModule &&
|
|
238
|
+
typeof routeModule === 'object' &&
|
|
239
|
+
'$$typeof' in routeModule)
|
|
240
|
+
) {
|
|
241
|
+
return routeModule as ElementType<Record<string, unknown>>;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
if (!routeModule || typeof routeModule !== 'object') {
|
|
245
|
+
return undefined;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
const module = routeModule as ModernRouteModule;
|
|
249
|
+
const component = module.default || module.Component;
|
|
250
|
+
if (
|
|
251
|
+
typeof component === 'function' ||
|
|
252
|
+
(component && typeof component === 'object' && '$$typeof' in component)
|
|
253
|
+
) {
|
|
254
|
+
return component as ElementType<Record<string, unknown>>;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
return undefined;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function createServerLazyImportComponent(
|
|
261
|
+
lazyImport: () => unknown,
|
|
262
|
+
fallbackComponent?: unknown,
|
|
263
|
+
): PreloadableComponent | unknown {
|
|
264
|
+
if (typeof document !== 'undefined') {
|
|
265
|
+
return fallbackComponent;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
let resolvedComponent: ElementType<Record<string, unknown>> | undefined;
|
|
269
|
+
let pendingLoad: Promise<unknown> | undefined;
|
|
270
|
+
|
|
271
|
+
const load = async () => {
|
|
272
|
+
if (resolvedComponent) {
|
|
273
|
+
return resolvedComponent;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
const routeModule = await lazyImport();
|
|
277
|
+
const component = pickRouteModuleComponent(routeModule);
|
|
278
|
+
if (component) {
|
|
279
|
+
resolvedComponent = component;
|
|
280
|
+
}
|
|
281
|
+
return resolvedComponent;
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
const Component: PreloadableComponent = props => {
|
|
285
|
+
if (resolvedComponent) {
|
|
286
|
+
return createElement(resolvedComponent, props);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
pendingLoad ||= load();
|
|
290
|
+
throw pendingLoad;
|
|
291
|
+
};
|
|
292
|
+
Component.load = load;
|
|
293
|
+
Component.preload = load;
|
|
294
|
+
|
|
295
|
+
return Component;
|
|
296
|
+
}
|
|
297
|
+
|
|
221
298
|
function isAbsoluteUrl(value: string) {
|
|
222
299
|
try {
|
|
223
300
|
void new URL(value);
|
|
@@ -518,6 +595,18 @@ function wrapRouteObjectLoader(
|
|
|
518
595
|
|
|
519
596
|
function toRouteComponent(routeObject: RouteObject): unknown {
|
|
520
597
|
const route = routeObject as ModernRouteObject;
|
|
598
|
+
const lazyImport =
|
|
599
|
+
typeof route.lazyImport === 'function' ? route.lazyImport : undefined;
|
|
600
|
+
const fallbackComponent = route.Component
|
|
601
|
+
? route.Component
|
|
602
|
+
: route.element
|
|
603
|
+
? () => route.element
|
|
604
|
+
: undefined;
|
|
605
|
+
|
|
606
|
+
if (lazyImport && fallbackComponent) {
|
|
607
|
+
return createServerLazyImportComponent(lazyImport, fallbackComponent);
|
|
608
|
+
}
|
|
609
|
+
|
|
521
610
|
if (route.Component) {
|
|
522
611
|
return route.Component;
|
|
523
612
|
}
|
|
@@ -528,6 +617,15 @@ function toRouteComponent(routeObject: RouteObject): unknown {
|
|
|
528
617
|
return undefined;
|
|
529
618
|
}
|
|
530
619
|
|
|
620
|
+
function toModernRouteComponent(route: ModernGeneratedRoute): unknown {
|
|
621
|
+
const component = route.component || undefined;
|
|
622
|
+
if (typeof route.lazyImport === 'function' && component) {
|
|
623
|
+
return createServerLazyImportComponent(route.lazyImport, component);
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
return component;
|
|
627
|
+
}
|
|
628
|
+
|
|
531
629
|
function toErrorComponent(routeObject: RouteObject): unknown {
|
|
532
630
|
const route = routeObject as ModernRouteObject;
|
|
533
631
|
if (route.ErrorBoundary) {
|
|
@@ -701,7 +799,7 @@ function createRouteFromModernRoute(opts: {
|
|
|
701
799
|
|
|
702
800
|
const pendingComponent = route.loading || route.pendingComponent;
|
|
703
801
|
const errorComponent = route.error || route.errorComponent;
|
|
704
|
-
const component = route
|
|
802
|
+
const component = toModernRouteComponent(route);
|
|
705
803
|
const modernLoader = route.loader;
|
|
706
804
|
const modernAction = route.action;
|
|
707
805
|
const modernShouldRevalidate = route.shouldRevalidate;
|
|
@@ -787,7 +885,9 @@ export function createRouteTreeFromModernRoutes(
|
|
|
787
885
|
(r as ModernGeneratedRoute).isRoot,
|
|
788
886
|
) as ModernGeneratedRoute | undefined;
|
|
789
887
|
|
|
790
|
-
const rootComponent = rootModern
|
|
888
|
+
const rootComponent = rootModern
|
|
889
|
+
? toModernRouteComponent(rootModern)
|
|
890
|
+
: undefined;
|
|
791
891
|
const pendingComponent = rootModern?.loading;
|
|
792
892
|
const errorComponent = rootModern?.error;
|
|
793
893
|
const rootLoader = rootModern?.loader;
|
|
@@ -78,9 +78,8 @@ const adapter = createSerializationAdapter({
|
|
|
78
78
|
toSerializable: (): never => {
|
|
79
79
|
throw new Error('TanStack RSC data cannot be serialized on client.');
|
|
80
80
|
},
|
|
81
|
-
fromSerializable: (value: SerializedRsc): AnyCompositeComponent =>
|
|
82
|
-
|
|
83
|
-
},
|
|
81
|
+
fromSerializable: (value: SerializedRsc): AnyCompositeComponent =>
|
|
82
|
+
createFromFlightStream(value) as AnyCompositeComponent,
|
|
84
83
|
});
|
|
85
84
|
|
|
86
85
|
export function getTanstackRscSerializationAdapters() {
|
package/src/runtime/utils.tsx
CHANGED