@bleedingdev/modern-js-plugin-tanstack 3.2.0-ultramodern.6 → 3.2.0-ultramodern.62

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.
@@ -127,6 +127,7 @@ async function generateTanstackRouterTypesSourceForEntry(opts) {
127
127
  const imports = [];
128
128
  const statements = [];
129
129
  const loaderImportMap = new Map();
130
+ const usedRouteVarNames = new Set();
130
131
  let loaderIndex = 0;
131
132
  let routeIndex = 0;
132
133
  const getImportNamesForLoader = async (aliasedNoExtPath, inline, hasAction)=>{
@@ -160,10 +161,17 @@ async function generateTanstackRouterTypesSourceForEntry(opts) {
160
161
  actionName
161
162
  };
162
163
  };
164
+ const reserveRouteVarName = (preferred)=>{
165
+ let candidate = preferred;
166
+ let suffix = 1;
167
+ while(usedRouteVarNames.has(candidate))candidate = `${preferred}_${suffix++}`;
168
+ usedRouteVarNames.add(candidate);
169
+ return candidate;
170
+ };
163
171
  const createRouteVarName = (route)=>{
164
172
  const id = route.id;
165
173
  const base = id ? makeLegalIdentifier(id) : `r_${routeIndex++}`;
166
- return `route_${base}`;
174
+ return reserveRouteVarName(`route_${base}`);
167
175
  };
168
176
  const buildRoute = async (opts)=>{
169
177
  const { parentVar, route } = opts;
@@ -192,14 +200,16 @@ async function generateTanstackRouterTypesSourceForEntry(opts) {
192
200
  actionName
193
201
  });
194
202
  if (staticDataSnippet) routeOpts.push(staticDataSnippet);
195
- statements.push(`const ${varName} = createRoute({\n ${routeOpts.join('\n ')}\n});`);
196
203
  const children = route.children;
204
+ const hasChildren = Boolean(children && children.length > 0);
205
+ const routeCtorVarName = hasChildren ? reserveRouteVarName(`${varName}__base`) : varName;
206
+ statements.push(`const ${routeCtorVarName} = createRoute({\n ${routeOpts.join('\n ')}\n});`);
197
207
  if (children && children.length > 0) {
198
208
  const childVars = await Promise.all(children.map((child)=>buildRoute({
199
- parentVar: varName,
209
+ parentVar: routeCtorVarName,
200
210
  route: child
201
211
  })));
202
- statements.push(`${varName}.addChildren([${childVars.join(', ')}]);`);
212
+ statements.push(`const ${varName} = ${routeCtorVarName}.addChildren([${childVars.join(', ')}]);`);
203
213
  }
204
214
  return varName;
205
215
  };
@@ -272,7 +282,11 @@ function createRouteStaticData(opts: {
272
282
  modernRouteAction?: unknown;
273
283
  modernRouteLoader?: unknown;
274
284
  }) {
275
- const staticData: Record<string, unknown> = {};
285
+ const staticData: {
286
+ modernRouteId?: string;
287
+ modernRouteAction?: unknown;
288
+ modernRouteLoader?: unknown;
289
+ } = {};
276
290
 
277
291
  if (opts.modernRouteId) {
278
292
  staticData.modernRouteId = opts.modernRouteId;
@@ -286,7 +300,7 @@ function createRouteStaticData(opts: {
286
300
  staticData.modernRouteAction = opts.modernRouteAction;
287
301
  }
288
302
 
289
- return Object.keys(staticData).length > 0 ? staticData : undefined;
303
+ return staticData;
290
304
  }
291
305
 
292
306
  function modernLoaderToTanstack<TLoader extends (args: any) => any>(
@@ -37,6 +37,7 @@ const external_react_namespaceObject = require("react");
37
37
  const external_basepathRewrite_js_namespaceObject = require("./basepathRewrite.js");
38
38
  const external_hooks_js_namespaceObject = require("./hooks.js");
39
39
  const external_lifecycle_js_namespaceObject = require("./lifecycle.js");
40
+ const external_prefetchLink_js_namespaceObject = require("./prefetchLink.js");
40
41
  const external_routeTree_js_namespaceObject = require("./routeTree.js");
41
42
  const client_js_namespaceObject = require("./rsc/client.js");
42
43
  const external_utils_js_namespaceObject = require("./utils.js");
@@ -102,6 +103,7 @@ const tanstackRouterPlugin = (userConfig = {})=>{
102
103
  }
103
104
  }
104
105
  context.router = {
106
+ Link: external_prefetchLink_js_namespaceObject.Link,
105
107
  useMatches: react_router_namespaceObject.useMatches,
106
108
  useLocation: react_router_namespaceObject.useLocation,
107
109
  useNavigate: react_router_namespaceObject.useNavigate,
@@ -34,7 +34,7 @@ const node_namespaceObject = require("@modern-js/runtime-utils/node");
34
34
  const time_namespaceObject = require("@modern-js/runtime-utils/time");
35
35
  const constants_namespaceObject = require("@modern-js/utils/universal/constants");
36
36
  const react_router_namespaceObject = require("@tanstack/react-router");
37
- const server_namespaceObject = require("@tanstack/react-router/ssr/server");
37
+ const server_namespaceObject = require("@tanstack/router-core/ssr/server");
38
38
  const external_react_namespaceObject = require("react");
39
39
  const external_basepathRewrite_js_namespaceObject = require("./basepathRewrite.js");
40
40
  const external_hooks_js_namespaceObject = require("./hooks.js");
@@ -46,12 +46,28 @@ const setTanstackRscServerPayload = (payload)=>{
46
46
  const storageContext = node_namespaceObject.storage.useContext?.();
47
47
  if (storageContext) storageContext.serverPayload = payload;
48
48
  };
49
+ function isPromiseLike(value) {
50
+ return Boolean(value && 'function' == typeof value.then);
51
+ }
49
52
  function isPreloadableRouteComponent(component) {
50
53
  if (!component || 'function' != typeof component) return false;
51
54
  const preloadable = component;
52
55
  return 'function' == typeof preloadable.load || 'function' == typeof preloadable.preload;
53
56
  }
57
+ function isReactLazyRouteComponent(component) {
58
+ return Boolean(component) && 'object' == typeof component && 'function' == typeof component._init && '_payload' in component;
59
+ }
60
+ async function preloadReactLazyRouteComponent(component) {
61
+ try {
62
+ component._init?.(component._payload);
63
+ } catch (thrown) {
64
+ if (!isPromiseLike(thrown)) throw thrown;
65
+ await thrown;
66
+ component._init?.(component._payload);
67
+ }
68
+ }
54
69
  async function preloadRouteComponent(component) {
70
+ if (isReactLazyRouteComponent(component)) return void await preloadReactLazyRouteComponent(component);
55
71
  if (!isPreloadableRouteComponent(component)) return;
56
72
  if ('function' == typeof component.load) return void await component.load({});
57
73
  await component.preload?.({});
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+ var __webpack_require__ = {};
3
+ (()=>{
4
+ __webpack_require__.n = (module)=>{
5
+ var getter = module && module.__esModule ? ()=>module['default'] : ()=>module;
6
+ __webpack_require__.d(getter, {
7
+ a: getter
8
+ });
9
+ return getter;
10
+ };
11
+ })();
12
+ (()=>{
13
+ __webpack_require__.d = (exports1, definition)=>{
14
+ for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
15
+ enumerable: true,
16
+ get: definition[key]
17
+ });
18
+ };
19
+ })();
20
+ (()=>{
21
+ __webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
22
+ })();
23
+ (()=>{
24
+ __webpack_require__.r = (exports1)=>{
25
+ if ("u" > typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
26
+ value: 'Module'
27
+ });
28
+ Object.defineProperty(exports1, '__esModule', {
29
+ value: true
30
+ });
31
+ };
32
+ })();
33
+ var __webpack_exports__ = {};
34
+ __webpack_require__.r(__webpack_exports__);
35
+ __webpack_require__.d(__webpack_exports__, {
36
+ default: ()=>external_plugin_node_js_default(),
37
+ tanstackRouterPlugin: ()=>external_plugin_node_js_namespaceObject.tanstackRouterPlugin
38
+ });
39
+ const external_plugin_node_js_namespaceObject = require("./plugin.node.js");
40
+ var external_plugin_node_js_default = /*#__PURE__*/ __webpack_require__.n(external_plugin_node_js_namespaceObject);
41
+ exports["default"] = __webpack_exports__["default"];
42
+ exports.tanstackRouterPlugin = __webpack_exports__.tanstackRouterPlugin;
43
+ for(var __rspack_i in __webpack_exports__)if (-1 === [
44
+ "default",
45
+ "tanstackRouterPlugin"
46
+ ].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
47
+ Object.defineProperty(exports, '__esModule', {
48
+ value: true
49
+ });
@@ -29,6 +29,7 @@ __webpack_require__.d(__webpack_exports__, {
29
29
  getModernRouteIdsFromMatches: ()=>getModernRouteIdsFromMatches
30
30
  });
31
31
  const react_router_namespaceObject = require("@tanstack/react-router");
32
+ const external_react_namespaceObject = require("react");
32
33
  const external_DefaultNotFound_js_namespaceObject = require("./DefaultNotFound.js");
33
34
  const payloadRouter_js_namespaceObject = require("./rsc/payloadRouter.js");
34
35
  function createTanstackRoute(options) {
@@ -84,6 +85,33 @@ function normalizeModernLoaderResponse(result) {
84
85
  }
85
86
  return normalizeModernLoaderResult(result);
86
87
  }
88
+ function pickRouteModuleComponent(routeModule) {
89
+ if ('function' == typeof routeModule || routeModule && 'object' == typeof routeModule && '$$typeof' in routeModule) return routeModule;
90
+ if (!routeModule || 'object' != typeof routeModule) return;
91
+ const module = routeModule;
92
+ const component = module.default || module.Component;
93
+ if ('function' == typeof component || component && 'object' == typeof component && '$$typeof' in component) return component;
94
+ }
95
+ function createServerLazyImportComponent(lazyImport, fallbackComponent) {
96
+ if ("u" > typeof document) return fallbackComponent;
97
+ let resolvedComponent;
98
+ let pendingLoad;
99
+ const load = async ()=>{
100
+ if (resolvedComponent) return resolvedComponent;
101
+ const routeModule = await lazyImport();
102
+ const component = pickRouteModuleComponent(routeModule);
103
+ if (component) resolvedComponent = component;
104
+ return resolvedComponent;
105
+ };
106
+ const Component = (props)=>{
107
+ if (resolvedComponent) return (0, external_react_namespaceObject.createElement)(resolvedComponent, props);
108
+ pendingLoad ||= load();
109
+ throw pendingLoad;
110
+ };
111
+ Component.load = load;
112
+ Component.preload = load;
113
+ return Component;
114
+ }
87
115
  function isAbsoluteUrl(value) {
88
116
  try {
89
117
  new URL(value);
@@ -258,10 +286,18 @@ function wrapRouteObjectLoader(route, revalidationState, options = {}) {
258
286
  }
259
287
  function toRouteComponent(routeObject) {
260
288
  const route = routeObject;
289
+ const lazyImport = 'function' == typeof route.lazyImport ? route.lazyImport : void 0;
290
+ const fallbackComponent = route.Component ? route.Component : route.element ? ()=>route.element : void 0;
291
+ if (lazyImport && fallbackComponent) return createServerLazyImportComponent(lazyImport, fallbackComponent);
261
292
  if (route.Component) return route.Component;
262
293
  const element = route.element;
263
294
  if (element) return ()=>element;
264
295
  }
296
+ function toModernRouteComponent(route) {
297
+ const component = route.component || void 0;
298
+ if ('function' == typeof route.lazyImport && component) return createServerLazyImportComponent(route.lazyImport, component);
299
+ return component;
300
+ }
265
301
  function toErrorComponent(routeObject) {
266
302
  const route = routeObject;
267
303
  if (route.ErrorBoundary) return route.ErrorBoundary;
@@ -342,7 +378,7 @@ function createRouteFromModernRoute(opts) {
342
378
  const stableFallbackId = modernId || route._component || route.filename || route.data || ('function' == typeof route.loader ? route.id : void 0);
343
379
  const pendingComponent = route.loading || route.pendingComponent;
344
380
  const errorComponent = route.error || route.errorComponent;
345
- const component = route.component;
381
+ const component = toModernRouteComponent(route);
346
382
  const modernLoader = route.loader;
347
383
  const modernAction = route.action;
348
384
  const modernShouldRevalidate = route.shouldRevalidate;
@@ -389,7 +425,7 @@ function createRouteFromModernRoute(opts) {
389
425
  }
390
426
  function createRouteTreeFromModernRoutes(routes, options = {}) {
391
427
  const rootModern = routes.find((r)=>r && 'nested' === r.type && r.isRoot);
392
- const rootComponent = rootModern?.component;
428
+ const rootComponent = rootModern ? toModernRouteComponent(rootModern) : void 0;
393
429
  const pendingComponent = rootModern?.loading;
394
430
  const errorComponent = rootModern?.error;
395
431
  const rootLoader = rootModern?.loader;
@@ -88,6 +88,7 @@ async function generateTanstackRouterTypesSourceForEntry(opts) {
88
88
  const imports = [];
89
89
  const statements = [];
90
90
  const loaderImportMap = new Map();
91
+ const usedRouteVarNames = new Set();
91
92
  let loaderIndex = 0;
92
93
  let routeIndex = 0;
93
94
  const getImportNamesForLoader = async (aliasedNoExtPath, inline, hasAction)=>{
@@ -121,10 +122,17 @@ async function generateTanstackRouterTypesSourceForEntry(opts) {
121
122
  actionName
122
123
  };
123
124
  };
125
+ const reserveRouteVarName = (preferred)=>{
126
+ let candidate = preferred;
127
+ let suffix = 1;
128
+ while(usedRouteVarNames.has(candidate))candidate = `${preferred}_${suffix++}`;
129
+ usedRouteVarNames.add(candidate);
130
+ return candidate;
131
+ };
124
132
  const createRouteVarName = (route)=>{
125
133
  const id = route.id;
126
134
  const base = id ? makeLegalIdentifier(id) : `r_${routeIndex++}`;
127
- return `route_${base}`;
135
+ return reserveRouteVarName(`route_${base}`);
128
136
  };
129
137
  const buildRoute = async (opts)=>{
130
138
  const { parentVar, route } = opts;
@@ -153,14 +161,16 @@ async function generateTanstackRouterTypesSourceForEntry(opts) {
153
161
  actionName
154
162
  });
155
163
  if (staticDataSnippet) routeOpts.push(staticDataSnippet);
156
- statements.push(`const ${varName} = createRoute({\n ${routeOpts.join('\n ')}\n});`);
157
164
  const children = route.children;
165
+ const hasChildren = Boolean(children && children.length > 0);
166
+ const routeCtorVarName = hasChildren ? reserveRouteVarName(`${varName}__base`) : varName;
167
+ statements.push(`const ${routeCtorVarName} = createRoute({\n ${routeOpts.join('\n ')}\n});`);
158
168
  if (children && children.length > 0) {
159
169
  const childVars = await Promise.all(children.map((child)=>buildRoute({
160
- parentVar: varName,
170
+ parentVar: routeCtorVarName,
161
171
  route: child
162
172
  })));
163
- statements.push(`${varName}.addChildren([${childVars.join(', ')}]);`);
173
+ statements.push(`const ${varName} = ${routeCtorVarName}.addChildren([${childVars.join(', ')}]);`);
164
174
  }
165
175
  return varName;
166
176
  };
@@ -233,7 +243,11 @@ function createRouteStaticData(opts: {
233
243
  modernRouteAction?: unknown;
234
244
  modernRouteLoader?: unknown;
235
245
  }) {
236
- const staticData: Record<string, unknown> = {};
246
+ const staticData: {
247
+ modernRouteId?: string;
248
+ modernRouteAction?: unknown;
249
+ modernRouteLoader?: unknown;
250
+ } = {};
237
251
 
238
252
  if (opts.modernRouteId) {
239
253
  staticData.modernRouteId = opts.modernRouteId;
@@ -247,7 +261,7 @@ function createRouteStaticData(opts: {
247
261
  staticData.modernRouteAction = opts.modernRouteAction;
248
262
  }
249
263
 
250
- return Object.keys(staticData).length > 0 ? staticData : undefined;
264
+ return staticData;
251
265
  }
252
266
 
253
267
  function modernLoaderToTanstack<TLoader extends (args: any) => any>(
@@ -7,6 +7,7 @@ import { RouterClient } from "@tanstack/react-router/ssr/client";
7
7
  import { createModernBasepathRewrite } from "./basepathRewrite.mjs";
8
8
  import { modifyRoutes, onAfterCreateRouter, onAfterHydrateRouter, onBeforeCreateRouter, onBeforeCreateRoutes, onBeforeHydrateRouter } from "./hooks.mjs";
9
9
  import { applyRouterRuntimeState } from "./lifecycle.mjs";
10
+ import { Link } from "./prefetchLink.mjs";
10
11
  import { createRouteTreeFromRouteObjects } from "./routeTree.mjs";
11
12
  import { getTanstackRscSerializationAdapters } from "./rsc/client.mjs";
12
13
  import { createRouteObjectsFromConfig, urlJoin } from "./utils.mjs";
@@ -73,6 +74,7 @@ const tanstackRouterPlugin = (userConfig = {})=>{
73
74
  }
74
75
  }
75
76
  context.router = {
77
+ Link: Link,
76
78
  useMatches: useMatches,
77
79
  useLocation: useLocation,
78
80
  useNavigate: useNavigate,
@@ -5,7 +5,7 @@ import { createRequestContext, storage } from "@modern-js/runtime-utils/node";
5
5
  import { time } from "@modern-js/runtime-utils/time";
6
6
  import { LOADER_REPORTER_NAME } from "@modern-js/utils/universal/constants";
7
7
  import { RouterProvider, createMemoryHistory, createRouter } from "@tanstack/react-router";
8
- import { attachRouterServerSsrUtils } from "@tanstack/react-router/ssr/server";
8
+ import { attachRouterServerSsrUtils } from "@tanstack/router-core/ssr/server";
9
9
  import { Suspense, useContext } from "react";
10
10
  import { createModernBasepathRewrite } from "./basepathRewrite.mjs";
11
11
  import { modifyRoutes, onAfterCreateRouter, onAfterHydrateRouter, onBeforeCreateRouter, onBeforeCreateRoutes, onBeforeHydrateRouter } from "./hooks.mjs";
@@ -17,12 +17,28 @@ const setTanstackRscServerPayload = (payload)=>{
17
17
  const storageContext = storage.useContext?.();
18
18
  if (storageContext) storageContext.serverPayload = payload;
19
19
  };
20
+ function isPromiseLike(value) {
21
+ return Boolean(value && 'function' == typeof value.then);
22
+ }
20
23
  function isPreloadableRouteComponent(component) {
21
24
  if (!component || 'function' != typeof component) return false;
22
25
  const preloadable = component;
23
26
  return 'function' == typeof preloadable.load || 'function' == typeof preloadable.preload;
24
27
  }
28
+ function isReactLazyRouteComponent(component) {
29
+ return Boolean(component) && 'object' == typeof component && 'function' == typeof component._init && '_payload' in component;
30
+ }
31
+ async function preloadReactLazyRouteComponent(component) {
32
+ try {
33
+ component._init?.(component._payload);
34
+ } catch (thrown) {
35
+ if (!isPromiseLike(thrown)) throw thrown;
36
+ await thrown;
37
+ component._init?.(component._payload);
38
+ }
39
+ }
25
40
  async function preloadRouteComponent(component) {
41
+ if (isReactLazyRouteComponent(component)) return void await preloadReactLazyRouteComponent(component);
26
42
  if (!isPreloadableRouteComponent(component)) return;
27
43
  if ('function' == typeof component.load) return void await component.load({});
28
44
  await component.preload?.({});
@@ -0,0 +1 @@
1
+ export { default, tanstackRouterPlugin } from "./plugin.node.mjs";
@@ -1,4 +1,5 @@
1
1
  import { createRootRoute, createRoute, notFound, redirect } from "@tanstack/react-router";
2
+ import { createElement } from "react";
2
3
  import { DefaultNotFound } from "./DefaultNotFound.mjs";
3
4
  import { isTanstackRscPayloadNavigationEnabled, loadTanstackRscRouteData } from "./rsc/payloadRouter.mjs";
4
5
  function createTanstackRoute(options) {
@@ -54,6 +55,33 @@ function normalizeModernLoaderResponse(result) {
54
55
  }
55
56
  return normalizeModernLoaderResult(result);
56
57
  }
58
+ function pickRouteModuleComponent(routeModule) {
59
+ if ('function' == typeof routeModule || routeModule && 'object' == typeof routeModule && '$$typeof' in routeModule) return routeModule;
60
+ if (!routeModule || 'object' != typeof routeModule) return;
61
+ const module = routeModule;
62
+ const component = module.default || module.Component;
63
+ if ('function' == typeof component || component && 'object' == typeof component && '$$typeof' in component) return component;
64
+ }
65
+ function createServerLazyImportComponent(lazyImport, fallbackComponent) {
66
+ if ("u" > typeof document) return fallbackComponent;
67
+ let resolvedComponent;
68
+ let pendingLoad;
69
+ const load = async ()=>{
70
+ if (resolvedComponent) return resolvedComponent;
71
+ const routeModule = await lazyImport();
72
+ const component = pickRouteModuleComponent(routeModule);
73
+ if (component) resolvedComponent = component;
74
+ return resolvedComponent;
75
+ };
76
+ const Component = (props)=>{
77
+ if (resolvedComponent) return createElement(resolvedComponent, props);
78
+ pendingLoad ||= load();
79
+ throw pendingLoad;
80
+ };
81
+ Component.load = load;
82
+ Component.preload = load;
83
+ return Component;
84
+ }
57
85
  function isAbsoluteUrl(value) {
58
86
  try {
59
87
  new URL(value);
@@ -228,10 +256,18 @@ function wrapRouteObjectLoader(route, revalidationState, options = {}) {
228
256
  }
229
257
  function toRouteComponent(routeObject) {
230
258
  const route = routeObject;
259
+ const lazyImport = 'function' == typeof route.lazyImport ? route.lazyImport : void 0;
260
+ const fallbackComponent = route.Component ? route.Component : route.element ? ()=>route.element : void 0;
261
+ if (lazyImport && fallbackComponent) return createServerLazyImportComponent(lazyImport, fallbackComponent);
231
262
  if (route.Component) return route.Component;
232
263
  const element = route.element;
233
264
  if (element) return ()=>element;
234
265
  }
266
+ function toModernRouteComponent(route) {
267
+ const component = route.component || void 0;
268
+ if ('function' == typeof route.lazyImport && component) return createServerLazyImportComponent(route.lazyImport, component);
269
+ return component;
270
+ }
235
271
  function toErrorComponent(routeObject) {
236
272
  const route = routeObject;
237
273
  if (route.ErrorBoundary) return route.ErrorBoundary;
@@ -312,7 +348,7 @@ function createRouteFromModernRoute(opts) {
312
348
  const stableFallbackId = modernId || route._component || route.filename || route.data || ('function' == typeof route.loader ? route.id : void 0);
313
349
  const pendingComponent = route.loading || route.pendingComponent;
314
350
  const errorComponent = route.error || route.errorComponent;
315
- const component = route.component;
351
+ const component = toModernRouteComponent(route);
316
352
  const modernLoader = route.loader;
317
353
  const modernAction = route.action;
318
354
  const modernShouldRevalidate = route.shouldRevalidate;
@@ -359,7 +395,7 @@ function createRouteFromModernRoute(opts) {
359
395
  }
360
396
  function createRouteTreeFromModernRoutes(routes, options = {}) {
361
397
  const rootModern = routes.find((r)=>r && 'nested' === r.type && r.isRoot);
362
- const rootComponent = rootModern?.component;
398
+ const rootComponent = rootModern ? toModernRouteComponent(rootModern) : void 0;
363
399
  const pendingComponent = rootModern?.loading;
364
400
  const errorComponent = rootModern?.error;
365
401
  const rootLoader = rootModern?.loader;
@@ -89,6 +89,7 @@ async function generateTanstackRouterTypesSourceForEntry(opts) {
89
89
  const imports = [];
90
90
  const statements = [];
91
91
  const loaderImportMap = new Map();
92
+ const usedRouteVarNames = new Set();
92
93
  let loaderIndex = 0;
93
94
  let routeIndex = 0;
94
95
  const getImportNamesForLoader = async (aliasedNoExtPath, inline, hasAction)=>{
@@ -122,10 +123,17 @@ async function generateTanstackRouterTypesSourceForEntry(opts) {
122
123
  actionName
123
124
  };
124
125
  };
126
+ const reserveRouteVarName = (preferred)=>{
127
+ let candidate = preferred;
128
+ let suffix = 1;
129
+ while(usedRouteVarNames.has(candidate))candidate = `${preferred}_${suffix++}`;
130
+ usedRouteVarNames.add(candidate);
131
+ return candidate;
132
+ };
125
133
  const createRouteVarName = (route)=>{
126
134
  const id = route.id;
127
135
  const base = id ? makeLegalIdentifier(id) : `r_${routeIndex++}`;
128
- return `route_${base}`;
136
+ return reserveRouteVarName(`route_${base}`);
129
137
  };
130
138
  const buildRoute = async (opts)=>{
131
139
  const { parentVar, route } = opts;
@@ -154,14 +162,16 @@ async function generateTanstackRouterTypesSourceForEntry(opts) {
154
162
  actionName
155
163
  });
156
164
  if (staticDataSnippet) routeOpts.push(staticDataSnippet);
157
- statements.push(`const ${varName} = createRoute({\n ${routeOpts.join('\n ')}\n});`);
158
165
  const children = route.children;
166
+ const hasChildren = Boolean(children && children.length > 0);
167
+ const routeCtorVarName = hasChildren ? reserveRouteVarName(`${varName}__base`) : varName;
168
+ statements.push(`const ${routeCtorVarName} = createRoute({\n ${routeOpts.join('\n ')}\n});`);
159
169
  if (children && children.length > 0) {
160
170
  const childVars = await Promise.all(children.map((child)=>buildRoute({
161
- parentVar: varName,
171
+ parentVar: routeCtorVarName,
162
172
  route: child
163
173
  })));
164
- statements.push(`${varName}.addChildren([${childVars.join(', ')}]);`);
174
+ statements.push(`const ${varName} = ${routeCtorVarName}.addChildren([${childVars.join(', ')}]);`);
165
175
  }
166
176
  return varName;
167
177
  };
@@ -234,7 +244,11 @@ function createRouteStaticData(opts: {
234
244
  modernRouteAction?: unknown;
235
245
  modernRouteLoader?: unknown;
236
246
  }) {
237
- const staticData: Record<string, unknown> = {};
247
+ const staticData: {
248
+ modernRouteId?: string;
249
+ modernRouteAction?: unknown;
250
+ modernRouteLoader?: unknown;
251
+ } = {};
238
252
 
239
253
  if (opts.modernRouteId) {
240
254
  staticData.modernRouteId = opts.modernRouteId;
@@ -248,7 +262,7 @@ function createRouteStaticData(opts: {
248
262
  staticData.modernRouteAction = opts.modernRouteAction;
249
263
  }
250
264
 
251
- return Object.keys(staticData).length > 0 ? staticData : undefined;
265
+ return staticData;
252
266
  }
253
267
 
254
268
  function modernLoaderToTanstack<TLoader extends (args: any) => any>(
@@ -8,6 +8,7 @@ import { RouterClient } from "@tanstack/react-router/ssr/client";
8
8
  import { createModernBasepathRewrite } from "./basepathRewrite.mjs";
9
9
  import { modifyRoutes, onAfterCreateRouter, onAfterHydrateRouter, onBeforeCreateRouter, onBeforeCreateRoutes, onBeforeHydrateRouter } from "./hooks.mjs";
10
10
  import { applyRouterRuntimeState } from "./lifecycle.mjs";
11
+ import { Link } from "./prefetchLink.mjs";
11
12
  import { createRouteTreeFromRouteObjects } from "./routeTree.mjs";
12
13
  import { getTanstackRscSerializationAdapters } from "./rsc/client.mjs";
13
14
  import { createRouteObjectsFromConfig, urlJoin } from "./utils.mjs";
@@ -74,6 +75,7 @@ const tanstackRouterPlugin = (userConfig = {})=>{
74
75
  }
75
76
  }
76
77
  context.router = {
78
+ Link: Link,
77
79
  useMatches: useMatches,
78
80
  useLocation: useLocation,
79
81
  useNavigate: useNavigate,
@@ -6,7 +6,7 @@ import { createRequestContext, storage } from "@modern-js/runtime-utils/node";
6
6
  import { time } from "@modern-js/runtime-utils/time";
7
7
  import { LOADER_REPORTER_NAME } from "@modern-js/utils/universal/constants";
8
8
  import { RouterProvider, createMemoryHistory, createRouter } from "@tanstack/react-router";
9
- import { attachRouterServerSsrUtils } from "@tanstack/react-router/ssr/server";
9
+ import { attachRouterServerSsrUtils } from "@tanstack/router-core/ssr/server";
10
10
  import { Suspense, useContext } from "react";
11
11
  import { createModernBasepathRewrite } from "./basepathRewrite.mjs";
12
12
  import { modifyRoutes, onAfterCreateRouter, onAfterHydrateRouter, onBeforeCreateRouter, onBeforeCreateRoutes, onBeforeHydrateRouter } from "./hooks.mjs";
@@ -18,12 +18,28 @@ const setTanstackRscServerPayload = (payload)=>{
18
18
  const storageContext = storage.useContext?.();
19
19
  if (storageContext) storageContext.serverPayload = payload;
20
20
  };
21
+ function isPromiseLike(value) {
22
+ return Boolean(value && 'function' == typeof value.then);
23
+ }
21
24
  function isPreloadableRouteComponent(component) {
22
25
  if (!component || 'function' != typeof component) return false;
23
26
  const preloadable = component;
24
27
  return 'function' == typeof preloadable.load || 'function' == typeof preloadable.preload;
25
28
  }
29
+ function isReactLazyRouteComponent(component) {
30
+ return Boolean(component) && 'object' == typeof component && 'function' == typeof component._init && '_payload' in component;
31
+ }
32
+ async function preloadReactLazyRouteComponent(component) {
33
+ try {
34
+ component._init?.(component._payload);
35
+ } catch (thrown) {
36
+ if (!isPromiseLike(thrown)) throw thrown;
37
+ await thrown;
38
+ component._init?.(component._payload);
39
+ }
40
+ }
26
41
  async function preloadRouteComponent(component) {
42
+ if (isReactLazyRouteComponent(component)) return void await preloadReactLazyRouteComponent(component);
27
43
  if (!isPreloadableRouteComponent(component)) return;
28
44
  if ('function' == typeof component.load) return void await component.load({});
29
45
  await component.preload?.({});
@@ -0,0 +1,2 @@
1
+ import "node:module";
2
+ export { default, tanstackRouterPlugin } from "./plugin.node.mjs";
@@ -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.component;
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?.component;
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.6",
21
+ "version": "3.2.0-ultramodern.62",
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.1",
90
- "@tanstack/router-core": "1.170.1",
91
- "@modern-js/plugin": "npm:@bleedingdev/modern-js-plugin@3.2.0-ultramodern.6",
92
- "@modern-js/runtime-utils": "npm:@bleedingdev/modern-js-runtime-utils@3.2.0-ultramodern.6",
93
- "@modern-js/types": "npm:@bleedingdev/modern-js-types@3.2.0-ultramodern.6",
94
- "@modern-js/utils": "npm:@bleedingdev/modern-js-utils@3.2.0-ultramodern.6"
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.62",
92
+ "@modern-js/utils": "npm:@bleedingdev/modern-js-utils@3.2.0-ultramodern.62",
93
+ "@modern-js/types": "npm:@bleedingdev/modern-js-types@3.2.0-ultramodern.62",
94
+ "@modern-js/runtime-utils": "npm:@bleedingdev/modern-js-runtime-utils@3.2.0-ultramodern.62"
95
95
  },
96
96
  "peerDependencies": {
97
- "@modern-js/runtime": "3.2.0-ultramodern.6",
97
+ "@modern-js/runtime": "3.2.0-ultramodern.62",
98
98
  "react": "^19.2.6",
99
99
  "react-dom": "^19.2.6"
100
100
  },
@@ -103,15 +103,15 @@
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.8.0",
107
- "@types/react": "^19.2.14",
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.20260516.1",
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/app-tools": "npm:@bleedingdev/modern-js-app-tools@3.2.0-ultramodern.6",
113
- "@scripts/rstest-config": "2.66.0",
114
- "@modern-js/runtime": "npm:@bleedingdev/modern-js-runtime@3.2.0-ultramodern.6"
112
+ "@modern-js/runtime": "npm:@bleedingdev/modern-js-runtime@3.2.0-ultramodern.62",
113
+ "@modern-js/app-tools": "npm:@bleedingdev/modern-js-app-tools@3.2.0-ultramodern.62",
114
+ "@scripts/rstest-config": "2.66.0"
115
115
  },
116
116
  "sideEffects": false,
117
117
  "publishConfig": {
@@ -182,6 +182,7 @@ export async function generateTanstackRouterTypesSourceForEntry(opts: {
182
182
  const statements: string[] = [];
183
183
 
184
184
  const loaderImportMap = new Map<string, string>();
185
+ const usedRouteVarNames = new Set<string>();
185
186
  let loaderIndex = 0;
186
187
  let routeIndex = 0;
187
188
 
@@ -242,10 +243,20 @@ export async function generateTanstackRouterTypesSourceForEntry(opts: {
242
243
  return { loaderName: importName, actionName };
243
244
  };
244
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
+
245
256
  const createRouteVarName = (route: NestedRouteForCli | PageRoute) => {
246
257
  const id = (route as any).id as string | undefined;
247
258
  const base = id ? makeLegalIdentifier(id) : `r_${routeIndex++}`;
248
- return `route_${base}`;
259
+ return reserveRouteVarName(`route_${base}`);
249
260
  };
250
261
 
251
262
  const buildRoute = async (opts: {
@@ -296,18 +307,27 @@ export async function generateTanstackRouterTypesSourceForEntry(opts: {
296
307
  routeOpts.push(staticDataSnippet);
297
308
  }
298
309
 
299
- statements.push(
300
- `const ${varName} = createRoute({\n ${routeOpts.join('\n ')}\n});`,
301
- );
302
-
303
310
  const children = (route as any).children as
304
311
  | Array<NestedRouteForCli | PageRoute>
305
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
+
306
322
  if (children && children.length > 0) {
307
323
  const childVars = await Promise.all(
308
- children.map(child => buildRoute({ parentVar: varName, route: child })),
324
+ children.map(child =>
325
+ buildRoute({ parentVar: routeCtorVarName, route: child }),
326
+ ),
327
+ );
328
+ statements.push(
329
+ `const ${varName} = ${routeCtorVarName}.addChildren([${childVars.join(', ')}]);`,
309
330
  );
310
- statements.push(`${varName}.addChildren([${childVars.join(', ')}]);`);
311
331
  }
312
332
 
313
333
  return varName;
@@ -396,7 +416,11 @@ function createRouteStaticData(opts: {
396
416
  modernRouteAction?: unknown;
397
417
  modernRouteLoader?: unknown;
398
418
  }) {
399
- const staticData: Record<string, unknown> = {};
419
+ const staticData: {
420
+ modernRouteId?: string;
421
+ modernRouteAction?: unknown;
422
+ modernRouteLoader?: unknown;
423
+ } = {};
400
424
 
401
425
  if (opts.modernRouteId) {
402
426
  staticData.modernRouteId = opts.modernRouteId;
@@ -410,7 +434,7 @@ function createRouteStaticData(opts: {
410
434
  staticData.modernRouteAction = opts.modernRouteAction;
411
435
  }
412
436
 
413
- return Object.keys(staticData).length > 0 ? staticData : undefined;
437
+ return staticData;
414
438
  }
415
439
 
416
440
  function modernLoaderToTanstack<TLoader extends (args: any) => any>(
@@ -26,7 +26,7 @@ import {
26
26
  createRouter,
27
27
  RouterProvider,
28
28
  } from '@tanstack/react-router';
29
- import { attachRouterServerSsrUtils } from '@tanstack/react-router/ssr/server';
29
+ import { attachRouterServerSsrUtils } from '@tanstack/router-core/ssr/server';
30
30
  import type React from 'react';
31
31
  import { Suspense, useContext } from 'react';
32
32
  import { createModernBasepathRewrite } from './basepathRewrite';
@@ -119,6 +119,17 @@ type PreloadableRouteComponent = {
119
119
  preload?: (props?: Record<string, unknown>) => Promise<unknown> | unknown;
120
120
  };
121
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
+
122
133
  type TanstackRouterWithServerSsr = AnyRouter & {
123
134
  resolveRedirect?: (redirect: Response) => Response;
124
135
  routesById?: Record<string, RouterRouteWithOptions>;
@@ -149,7 +160,37 @@ function isPreloadableRouteComponent(
149
160
  );
150
161
  }
151
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
+
152
188
  async function preloadRouteComponent(component: unknown) {
189
+ if (isReactLazyRouteComponent(component)) {
190
+ await preloadReactLazyRouteComponent(component);
191
+ return;
192
+ }
193
+
153
194
  if (!isPreloadableRouteComponent(component)) {
154
195
  return;
155
196
  }
@@ -41,6 +41,7 @@ import {
41
41
  applyRouterRuntimeState,
42
42
  type RouterLifecycleContext,
43
43
  } from './lifecycle';
44
+ import { Link } from './prefetchLink';
44
45
  import { createRouteTreeFromRouteObjects } from './routeTree';
45
46
  import { getTanstackRscSerializationAdapters } from './rsc/client';
46
47
  import type { RouterConfig } from './types';
@@ -184,6 +185,7 @@ export const tanstackRouterPlugin = (
184
185
  }
185
186
 
186
187
  context.router = {
188
+ Link,
187
189
  useMatches,
188
190
  useLocation,
189
191
  useNavigate,
@@ -0,0 +1,4 @@
1
+ export {
2
+ default,
3
+ tanstackRouterPlugin,
4
+ } from './plugin.node';
@@ -12,6 +12,7 @@ import {
12
12
  notFound,
13
13
  redirect,
14
14
  } from '@tanstack/react-router';
15
+ import { createElement, type ElementType } from 'react';
15
16
  import { DefaultNotFound } from './DefaultNotFound';
16
17
  import {
17
18
  isTanstackRscPayloadNavigationEnabled,
@@ -118,6 +119,15 @@ type ModernDeferredDataLike = {
118
119
  __modern_deferred?: unknown;
119
120
  data?: unknown;
120
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
+ };
121
131
  type RouteTreeOptions = {
122
132
  rscPayloadRouter?: boolean;
123
133
  };
@@ -219,6 +229,72 @@ function normalizeModernLoaderResponse(result: unknown): unknown {
219
229
  return normalizeModernLoaderResult(result);
220
230
  }
221
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
+
222
298
  function isAbsoluteUrl(value: string) {
223
299
  try {
224
300
  void new URL(value);
@@ -519,6 +595,18 @@ function wrapRouteObjectLoader(
519
595
 
520
596
  function toRouteComponent(routeObject: RouteObject): unknown {
521
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
+
522
610
  if (route.Component) {
523
611
  return route.Component;
524
612
  }
@@ -529,6 +617,15 @@ function toRouteComponent(routeObject: RouteObject): unknown {
529
617
  return undefined;
530
618
  }
531
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
+
532
629
  function toErrorComponent(routeObject: RouteObject): unknown {
533
630
  const route = routeObject as ModernRouteObject;
534
631
  if (route.ErrorBoundary) {
@@ -702,7 +799,7 @@ function createRouteFromModernRoute(opts: {
702
799
 
703
800
  const pendingComponent = route.loading || route.pendingComponent;
704
801
  const errorComponent = route.error || route.errorComponent;
705
- const component = route.component;
802
+ const component = toModernRouteComponent(route);
706
803
  const modernLoader = route.loader;
707
804
  const modernAction = route.action;
708
805
  const modernShouldRevalidate = route.shouldRevalidate;
@@ -788,7 +885,9 @@ export function createRouteTreeFromModernRoutes(
788
885
  (r as ModernGeneratedRoute).isRoot,
789
886
  ) as ModernGeneratedRoute | undefined;
790
887
 
791
- const rootComponent = rootModern?.component;
888
+ const rootComponent = rootModern
889
+ ? toModernRouteComponent(rootModern)
890
+ : undefined;
792
891
  const pendingComponent = rootModern?.loading;
793
892
  const errorComponent = rootModern?.error;
794
893
  const rootLoader = rootModern?.loader;
@@ -1,7 +1,10 @@
1
1
  import type { RouteObject } from '@modern-js/runtime-utils/router';
2
2
  import type { NestedRoute } from '@modern-js/types';
3
3
  import { createMemoryHistory } from '@tanstack/history';
4
- import { createRouter } from '@tanstack/react-router';
4
+ import { createRouter, Outlet, RouterProvider } from '@tanstack/react-router';
5
+ import type { ComponentType } from 'react';
6
+ import { createElement, lazy } from 'react';
7
+ import { renderToStaticMarkup } from 'react-dom/server';
5
8
  import {
6
9
  createRouteTreeFromModernRoutes,
7
10
  createRouteTreeFromRouteObjects,
@@ -23,6 +26,7 @@ type TestRouteObject = RouteObject & {
23
26
  hasLoader?: boolean;
24
27
  inValidSSRRoute?: boolean;
25
28
  isClientComponent?: boolean;
29
+ lazyImport?: () => Promise<{ default: ComponentType }>;
26
30
  };
27
31
 
28
32
  type TestNestedRoute = NestedRoute & {
@@ -234,6 +238,73 @@ describe('tanstack route tree from RouteObject[]', () => {
234
238
  expect(splatParamValue).toBe('a/b/c');
235
239
  });
236
240
 
241
+ test('preloads lazy Modern route components for server rendering', async () => {
242
+ const LazyRouteComponent = () =>
243
+ createElement('main', null, 'Lazy route ready');
244
+ const lazyImport = rstest.fn(async () => ({
245
+ default: LazyRouteComponent,
246
+ }));
247
+ const routes: TestRouteObject[] = [
248
+ {
249
+ id: 'root',
250
+ path: '/',
251
+ Component: () => null,
252
+ children: [
253
+ {
254
+ id: 'lazy',
255
+ path: 'lazy',
256
+ Component: lazy(lazyImport),
257
+ lazyImport,
258
+ },
259
+ ],
260
+ },
261
+ ];
262
+
263
+ const routeTree = createRouteTreeFromRouteObjects(routes);
264
+ const router = await loadRouteTree(routeTree, '/lazy');
265
+ const lazyRoute = getLooseRoute(router, '/lazy');
266
+ const component = lazyRoute.options.component as ComponentType & {
267
+ preload?: () => Promise<unknown>;
268
+ };
269
+
270
+ await component.preload?.();
271
+
272
+ expect(renderToStaticMarkup(createElement(component))).toContain(
273
+ 'Lazy route ready',
274
+ );
275
+ expect(lazyImport).toHaveBeenCalled();
276
+ });
277
+
278
+ test('renders preloaded lazy child routes through TanStack router SSR', async () => {
279
+ const LazyRouteComponent = () =>
280
+ createElement('main', null, 'Lazy child route ready');
281
+ const lazyImport = rstest.fn(async () => ({
282
+ default: LazyRouteComponent,
283
+ }));
284
+ const routes: TestRouteObject[] = [
285
+ {
286
+ id: 'root',
287
+ path: '/',
288
+ Component: () => createElement('section', null, createElement(Outlet)),
289
+ children: [
290
+ {
291
+ id: 'lazy',
292
+ path: 'lazy',
293
+ Component: lazy(lazyImport),
294
+ lazyImport,
295
+ },
296
+ ],
297
+ },
298
+ ];
299
+
300
+ const routeTree = createRouteTreeFromRouteObjects(routes);
301
+ const router = await loadRouteTree(routeTree, '/lazy');
302
+
303
+ expect(
304
+ renderToStaticMarkup(createElement(RouterProvider, { router } as never)),
305
+ ).toContain('Lazy child route ready');
306
+ });
307
+
237
308
  test('preserves route handle and maps shouldRevalidate to shouldReload', async () => {
238
309
  const shouldRevalidate = rstest.fn(({ nextUrl }: ShouldRevalidateArgs) =>
239
310
  nextUrl.pathname.endsWith('/456'),
@@ -55,8 +55,72 @@ describe('tanstack router type generation', () => {
55
55
  );
56
56
  expect(routerGenTs).toContain('modernRouteLoader: loader_0');
57
57
  expect(routerGenTs).toContain('modernRouteAction: action_0');
58
+ expect(routerGenTs).toContain('modernRouteId?: string;');
59
+ expect(routerGenTs).not.toContain(
60
+ 'return Object.keys(staticData).length > 0 ? staticData : undefined;',
61
+ );
58
62
  expect(routerGenTs).toContain(
59
63
  "} from '@modern-js/plugin-tanstack/runtime';",
60
64
  );
61
65
  });
66
+
67
+ test('preserves typed child trees for localized nested route aliases', async () => {
68
+ tempDir = await mkdtemp(path.join(tmpdir(), 'modern-tanstack-types-'));
69
+ const srcDirectory = path.join(tempDir, 'src');
70
+
71
+ const { routerGenTs } = await generateTanstackRouterTypesSourceForEntry({
72
+ appContext: {
73
+ srcDirectory,
74
+ internalSrcAlias: '@/_',
75
+ } as any,
76
+ entryName: 'index',
77
+ routes: [
78
+ {
79
+ type: 'nested',
80
+ id: 'layout',
81
+ isRoot: true,
82
+ children: [
83
+ {
84
+ type: 'nested',
85
+ id: '(lang)/layout',
86
+ path: ':lang',
87
+ children: [
88
+ {
89
+ type: 'nested',
90
+ id: '(lang)/products/(slug)/page',
91
+ path: 'products/:slug',
92
+ },
93
+ {
94
+ type: 'nested',
95
+ id: '(lang)/products/(slug)/page__localised_produkty_slug',
96
+ path: 'produkty/:slug',
97
+ },
98
+ {
99
+ type: 'nested',
100
+ id: '(lang)/optional/(slug$)/page__localised_volitelne_slug',
101
+ path: 'volitelne/:slug?',
102
+ },
103
+ ],
104
+ },
105
+ ],
106
+ },
107
+ ] as any,
108
+ });
109
+
110
+ expect(routerGenTs).toContain(
111
+ 'const route__lang__layout__base = createRoute({',
112
+ );
113
+ expect(routerGenTs).toContain(
114
+ 'getParentRoute: () => route__lang__layout__base,',
115
+ );
116
+ expect(routerGenTs).toContain('path: "produkty/$slug",');
117
+ expect(routerGenTs).toContain('path: "volitelne/{-$slug}",');
118
+ expect(routerGenTs).toContain(
119
+ 'const route__lang__layout = route__lang__layout__base.addChildren([route__lang__products__slug__page, route__lang__products__slug__page__localised_produkty_slug, route__lang__optional__slug$__page__localised_volitelne_slug]);',
120
+ );
121
+ expect(routerGenTs).toContain(
122
+ 'export const routeTree = rootRoute.addChildren([route__lang__layout]);',
123
+ );
124
+ expect(routerGenTs).not.toContain('route__lang__layout.addChildren([');
125
+ });
62
126
  });