@bleedingdev/modern-js-runtime 3.2.0-ultramodern.10 → 3.2.0-ultramodern.100

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 (97) hide show
  1. package/dist/cjs/boundary-debugger/index.js +299 -0
  2. package/dist/cjs/cli/ssr/index.js +5 -15
  3. package/dist/cjs/cli/template.server.js +1 -0
  4. package/dist/cjs/core/react/wrapper.js +9 -3
  5. package/dist/cjs/core/server/federatedCss.js +47 -0
  6. package/dist/cjs/core/server/helmet.js +8 -2
  7. package/dist/cjs/core/server/scriptOrder.js +59 -0
  8. package/dist/cjs/core/server/stream/afterTemplate.js +13 -5
  9. package/dist/cjs/core/server/stream/beforeTemplate.js +13 -20
  10. package/dist/cjs/core/server/stream/beforeTemplate.worker.js +98 -0
  11. package/dist/cjs/core/server/stream/createReadableStream.js +7 -2
  12. package/dist/cjs/core/server/stream/createReadableStream.worker.js +4 -2
  13. package/dist/cjs/core/server/stream/shared.js +3 -1
  14. package/dist/cjs/core/server/string/index.js +16 -9
  15. package/dist/cjs/core/server/string/loadable.js +74 -10
  16. package/dist/cjs/exports/head.js +196 -5
  17. package/dist/cjs/exports/loadable.js +34 -4
  18. package/dist/cjs/exports/tanstack-router.js +311 -54
  19. package/dist/cjs/router/cli/code/tanstackTypes.js +116 -51
  20. package/dist/cjs/router/cli/code/templates.js +15 -9
  21. package/dist/cjs/router/runtime/tanstack/hydrationBoundary.js +44 -0
  22. package/dist/cjs/router/runtime/tanstack/outlet.js +54 -0
  23. package/dist/cjs/router/runtime/tanstack/plugin.js +190 -88
  24. package/dist/cjs/router/runtime/tanstack/plugin.node.js +4 -14
  25. package/dist/cjs/router/runtime/tanstack/routeTree.js +64 -12
  26. package/dist/cjs/rsc/server.worker.js +58 -0
  27. package/dist/cjs/ssr/serverRender/renderToString/entry.js +9 -8
  28. package/dist/esm/boundary-debugger/index.mjs +263 -0
  29. package/dist/esm/cli/ssr/index.mjs +5 -15
  30. package/dist/esm/cli/template.server.mjs +1 -0
  31. package/dist/esm/core/react/wrapper.mjs +9 -3
  32. package/dist/esm/core/server/federatedCss.mjs +13 -0
  33. package/dist/esm/core/server/helmet.mjs +5 -2
  34. package/dist/esm/core/server/scriptOrder.mjs +25 -0
  35. package/dist/esm/core/server/stream/afterTemplate.mjs +14 -6
  36. package/dist/esm/core/server/stream/beforeTemplate.mjs +14 -11
  37. package/dist/esm/core/server/stream/beforeTemplate.worker.mjs +64 -0
  38. package/dist/esm/core/server/stream/createReadableStream.mjs +7 -2
  39. package/dist/esm/core/server/stream/createReadableStream.worker.mjs +4 -2
  40. package/dist/esm/core/server/stream/shared.mjs +3 -1
  41. package/dist/esm/core/server/string/index.mjs +17 -9
  42. package/dist/esm/core/server/string/loadable.mjs +70 -9
  43. package/dist/esm/exports/head.mjs +189 -4
  44. package/dist/esm/exports/loadable.mjs +20 -3
  45. package/dist/esm/exports/tanstack-router.mjs +2 -1
  46. package/dist/esm/router/cli/code/tanstackTypes.mjs +116 -51
  47. package/dist/esm/router/cli/code/templates.mjs +15 -9
  48. package/dist/esm/router/runtime/tanstack/hydrationBoundary.mjs +10 -0
  49. package/dist/esm/router/runtime/tanstack/outlet.mjs +17 -0
  50. package/dist/esm/router/runtime/tanstack/plugin.mjs +193 -91
  51. package/dist/esm/router/runtime/tanstack/plugin.node.mjs +5 -15
  52. package/dist/esm/router/runtime/tanstack/routeTree.mjs +65 -13
  53. package/dist/esm/rsc/server.worker.mjs +1 -0
  54. package/dist/esm/ssr/serverRender/renderToString/entry.mjs +9 -6
  55. package/dist/esm-node/boundary-debugger/index.mjs +264 -0
  56. package/dist/esm-node/cli/ssr/index.mjs +5 -15
  57. package/dist/esm-node/cli/template.server.mjs +1 -0
  58. package/dist/esm-node/core/react/wrapper.mjs +9 -3
  59. package/dist/esm-node/core/server/federatedCss.mjs +14 -0
  60. package/dist/esm-node/core/server/helmet.mjs +5 -2
  61. package/dist/esm-node/core/server/scriptOrder.mjs +26 -0
  62. package/dist/esm-node/core/server/stream/afterTemplate.mjs +14 -6
  63. package/dist/esm-node/core/server/stream/beforeTemplate.mjs +14 -11
  64. package/dist/esm-node/core/server/stream/beforeTemplate.worker.mjs +65 -0
  65. package/dist/esm-node/core/server/stream/createReadableStream.mjs +7 -2
  66. package/dist/esm-node/core/server/stream/createReadableStream.worker.mjs +4 -2
  67. package/dist/esm-node/core/server/stream/shared.mjs +3 -1
  68. package/dist/esm-node/core/server/string/index.mjs +17 -9
  69. package/dist/esm-node/core/server/string/loadable.mjs +70 -9
  70. package/dist/esm-node/exports/head.mjs +189 -4
  71. package/dist/esm-node/exports/loadable.mjs +20 -3
  72. package/dist/esm-node/exports/tanstack-router.mjs +2 -1
  73. package/dist/esm-node/router/cli/code/tanstackTypes.mjs +116 -51
  74. package/dist/esm-node/router/cli/code/templates.mjs +15 -9
  75. package/dist/esm-node/router/runtime/tanstack/hydrationBoundary.mjs +11 -0
  76. package/dist/esm-node/router/runtime/tanstack/outlet.mjs +18 -0
  77. package/dist/esm-node/router/runtime/tanstack/plugin.mjs +193 -91
  78. package/dist/esm-node/router/runtime/tanstack/plugin.node.mjs +5 -15
  79. package/dist/esm-node/router/runtime/tanstack/routeTree.mjs +65 -13
  80. package/dist/esm-node/rsc/server.worker.mjs +2 -0
  81. package/dist/esm-node/ssr/serverRender/renderToString/entry.mjs +9 -6
  82. package/dist/types/boundary-debugger/index.d.ts +28 -0
  83. package/dist/types/core/context/runtime.d.ts +4 -0
  84. package/dist/types/core/server/federatedCss.d.ts +5 -0
  85. package/dist/types/core/server/helmet.d.ts +5 -3
  86. package/dist/types/core/server/scriptOrder.d.ts +1 -0
  87. package/dist/types/core/server/stream/beforeTemplate.d.ts +1 -0
  88. package/dist/types/core/server/stream/beforeTemplate.worker.d.ts +10 -0
  89. package/dist/types/core/server/stream/shared.d.ts +8 -0
  90. package/dist/types/core/server/string/loadable.d.ts +11 -0
  91. package/dist/types/exports/head.d.ts +10 -3
  92. package/dist/types/exports/loadable.d.ts +8 -1
  93. package/dist/types/exports/tanstack-router.d.ts +3 -1
  94. package/dist/types/router/runtime/tanstack/hydrationBoundary.d.ts +2 -0
  95. package/dist/types/router/runtime/tanstack/outlet.d.ts +2 -0
  96. package/dist/types/rsc/server.worker.d.ts +1 -0
  97. package/package.json +24 -18
@@ -77,9 +77,21 @@ async function generateTanstackRouterTypesSourceForEntry(opts) {
77
77
  const topLevel = rootModern ? rootModern.children || [] : routes;
78
78
  const imports = [];
79
79
  const statements = [];
80
+ const componentImportMap = new Map();
80
81
  const loaderImportMap = new Map();
82
+ const usedRouteVarNames = new Set();
83
+ let componentIndex = 0;
81
84
  let loaderIndex = 0;
82
85
  let routeIndex = 0;
86
+ const getImportNameForComponent = (componentPath)=>{
87
+ if ('string' != typeof componentPath || 0 === componentPath.length) return null;
88
+ const existing = componentImportMap.get(componentPath);
89
+ if (existing) return existing;
90
+ const componentName = `component_${componentIndex++}`;
91
+ imports.push(`import ${componentName} from ${quote(componentPath)};`);
92
+ componentImportMap.set(componentPath, componentName);
93
+ return componentName;
94
+ };
83
95
  const getImportNamesForLoader = async (aliasedNoExtPath, inline, hasAction)=>{
84
96
  const key = `${inline ? 'inline' : 'default'}:${hasAction ? 'action' : 'loader'}:${aliasedNoExtPath}`;
85
97
  const existing = loaderImportMap.get(key);
@@ -111,10 +123,17 @@ async function generateTanstackRouterTypesSourceForEntry(opts) {
111
123
  actionName
112
124
  };
113
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
+ };
114
133
  const createRouteVarName = (route)=>{
115
134
  const id = route.id;
116
135
  const base = id ? makeLegalIdentifier(id) : `r_${routeIndex++}`;
117
- return `route_${base}`;
136
+ return reserveRouteVarName(`route_${base}`);
118
137
  };
119
138
  const buildRoute = async (opts)=>{
120
139
  const { parentVar, route } = opts;
@@ -129,6 +148,8 @@ async function generateTanstackRouterTypesSourceForEntry(opts) {
129
148
  const routeOpts = [
130
149
  `getParentRoute: () => ${parentVar},`
131
150
  ];
151
+ const componentName = getImportNameForComponent(route._component);
152
+ if (componentName) routeOpts.push(`component: ${componentName},`);
132
153
  if (isPathlessLayout(route)) {
133
154
  const id = route.id;
134
155
  routeOpts.push(`id: ${quote(id || 'pathless')},`);
@@ -143,14 +164,16 @@ async function generateTanstackRouterTypesSourceForEntry(opts) {
143
164
  actionName
144
165
  });
145
166
  if (staticDataSnippet) routeOpts.push(staticDataSnippet);
146
- statements.push(`const ${varName} = createRoute({\n ${routeOpts.join('\n ')}\n});`);
147
167
  const children = route.children;
168
+ const hasChildren = Boolean(children && children.length > 0);
169
+ const routeCtorVarName = hasChildren ? reserveRouteVarName(`${varName}__base`) : varName;
170
+ statements.push(`const ${routeCtorVarName} = createRoute({\n ${routeOpts.join('\n ')}\n});`);
148
171
  if (children && children.length > 0) {
149
172
  const childVars = await Promise.all(children.map((child)=>buildRoute({
150
- parentVar: varName,
173
+ parentVar: routeCtorVarName,
151
174
  route: child
152
175
  })));
153
- statements.push(`${varName}.addChildren([${childVars.join(', ')}]);`);
176
+ statements.push(`const ${varName} = ${routeCtorVarName}.addChildren([${childVars.join(', ')}]);`);
154
177
  }
155
178
  return varName;
156
179
  };
@@ -164,6 +187,8 @@ async function generateTanstackRouterTypesSourceForEntry(opts) {
164
187
  route
165
188
  })));
166
189
  const rootOpts = [];
190
+ const rootComponentName = getImportNameForComponent(rootModern?._component);
191
+ if (rootComponentName) rootOpts.push(`component: ${rootComponentName},`);
167
192
  if (rootLoaderName) rootOpts.push(`loader: modernLoaderToTanstack({ hasSplat: false }, ${rootLoaderName}),`);
168
193
  const routerGenTs = `/* eslint-disable */
169
194
  // This file is auto-generated by Modern.js. Do not edit manually.
@@ -197,7 +222,7 @@ function isRedirectResponse(res: Response) {
197
222
  }
198
223
 
199
224
  function throwTanstackRedirect(location: string) {
200
- const target = location || '/';
225
+ const target = location.length > 0 ? location : '/';
201
226
  try {
202
227
  void new URL(target);
203
228
  throw redirect({ href: target });
@@ -223,21 +248,87 @@ function createRouteStaticData(opts: {
223
248
  modernRouteAction?: unknown;
224
249
  modernRouteLoader?: unknown;
225
250
  }) {
226
- const staticData: Record<string, unknown> = {};
251
+ const staticData: {
252
+ modernRouteId?: string;
253
+ modernRouteAction?: unknown;
254
+ modernRouteLoader?: unknown;
255
+ } = {};
227
256
 
228
- if (opts.modernRouteId) {
257
+ if (typeof opts.modernRouteId === 'string' && opts.modernRouteId.length > 0) {
229
258
  staticData.modernRouteId = opts.modernRouteId;
230
259
  }
231
260
 
232
- if (opts.modernRouteLoader) {
261
+ if (typeof opts.modernRouteLoader !== 'undefined') {
233
262
  staticData.modernRouteLoader = opts.modernRouteLoader;
234
263
  }
235
264
 
236
- if (opts.modernRouteAction) {
265
+ if (typeof opts.modernRouteAction !== 'undefined') {
237
266
  staticData.modernRouteAction = opts.modernRouteAction;
238
267
  }
239
268
 
240
- return Object.keys(staticData).length > 0 ? staticData : undefined;
269
+ return staticData;
270
+ }
271
+
272
+ function getLoaderSignal(ctx: any): AbortSignal {
273
+ const abortSignal = ctx?.abortController?.signal;
274
+ if (abortSignal instanceof AbortSignal) {
275
+ return abortSignal;
276
+ }
277
+ if (ctx?.signal instanceof AbortSignal) {
278
+ return ctx.signal;
279
+ }
280
+ return new AbortController().signal;
281
+ }
282
+
283
+ function getLoaderHref(ctx: any): string {
284
+ if (typeof ctx?.location === 'string') {
285
+ return ctx.location;
286
+ }
287
+
288
+ const publicHref = ctx?.location?.publicHref;
289
+ if (typeof publicHref === 'string') {
290
+ return publicHref;
291
+ }
292
+
293
+ const href = ctx?.location?.href;
294
+ if (typeof href === 'string') {
295
+ return href;
296
+ }
297
+
298
+ const urlHref = ctx?.location?.url?.href;
299
+ return typeof urlHref === 'string' ? urlHref : '';
300
+ }
301
+
302
+ function getLoaderParams(ctx: any): Record<string, string> {
303
+ return typeof ctx?.params === 'object' && ctx.params !== null ? ctx.params : {};
304
+ }
305
+
306
+ function handleModernLoaderResult<LoaderResult>(result: LoaderResult): LoaderResult {
307
+ if (isResponse(result)) {
308
+ if (isRedirectResponse(result)) {
309
+ const location = result.headers.get('Location') ?? '/';
310
+ throwTanstackRedirect(location);
311
+ }
312
+ if (result.status === 404) {
313
+ throw notFound();
314
+ }
315
+ }
316
+
317
+ return result;
318
+ }
319
+
320
+ function handleModernLoaderError(err: unknown): never {
321
+ if (isResponse(err)) {
322
+ if (isRedirectResponse(err)) {
323
+ const location = err.headers.get('Location') ?? '/';
324
+ throwTanstackRedirect(location);
325
+ }
326
+ if (err.status === 404) {
327
+ throw notFound();
328
+ }
329
+ }
330
+
331
+ throw err;
241
332
  }
242
333
 
243
334
  function modernLoaderToTanstack<TLoader extends (args: any) => any>(
@@ -246,57 +337,31 @@ function modernLoaderToTanstack<TLoader extends (args: any) => any>(
246
337
  ) {
247
338
  type LoaderResult = Awaited<ReturnType<TLoader>>;
248
339
 
249
- return async (ctx: any): Promise<LoaderResult> => {
340
+ return (ctx: any): Promise<LoaderResult> => {
250
341
  try {
251
- const signal: AbortSignal =
252
- ctx?.abortController?.signal ||
253
- ctx?.signal ||
254
- new AbortController().signal;
342
+ const signal = getLoaderSignal(ctx);
255
343
  const baseRequest: Request | undefined =
256
344
  ctx?.context?.request instanceof Request ? ctx.context.request : undefined;
257
345
 
258
- const href =
259
- typeof ctx?.location === 'string'
260
- ? ctx.location
261
- : ctx?.location?.publicHref ||
262
- ctx?.location?.href ||
263
- ctx?.location?.url?.href ||
264
- '';
346
+ const href = getLoaderHref(ctx);
265
347
 
266
- const request = baseRequest
348
+ const request = baseRequest !== undefined
267
349
  ? new Request(baseRequest, { signal })
268
350
  : new Request(href, { signal });
269
351
 
270
- const params = mapParamsForModernLoader(ctx?.params || {}, opts.hasSplat);
271
-
272
- const result = await (modernLoader as any)({
273
- request,
274
- params,
275
- context: ctx?.context?.requestContext,
276
- });
277
-
278
- if (isResponse(result)) {
279
- if (isRedirectResponse(result)) {
280
- const location = result.headers.get('Location') || '/';
281
- throwTanstackRedirect(location);
282
- }
283
- if (result.status === 404) {
284
- throw notFound();
285
- }
286
- }
352
+ const params = mapParamsForModernLoader(getLoaderParams(ctx), opts.hasSplat);
287
353
 
288
- return result as LoaderResult;
354
+ return Promise.resolve(
355
+ (modernLoader as any)({
356
+ request,
357
+ params,
358
+ context: ctx?.context?.requestContext,
359
+ }),
360
+ )
361
+ .then((result: LoaderResult) => handleModernLoaderResult(result))
362
+ .catch(handleModernLoaderError);
289
363
  } catch (err) {
290
- if (isResponse(err)) {
291
- if (isRedirectResponse(err)) {
292
- const location = err.headers.get('Location') || '/';
293
- throwTanstackRedirect(location);
294
- }
295
- if (err.status === 404) {
296
- throw notFound();
297
- }
298
- }
299
- throw err;
364
+ handleModernLoaderError(err);
300
365
  }
301
366
  };
302
367
  }
@@ -100,7 +100,20 @@ const fileSystemRoutes = async ({ metaName, routes, ssrMode, nestedRoutesEntry,
100
100
  const loadersMapFile = path.join(internalDirectory, entryName, TEMP_LOADERS_DIR, 'map.json');
101
101
  const importLazyCode = `
102
102
  import { lazy } from "react";
103
- import loadable, { lazy as loadableLazy } from "@${metaName}/runtime/loadable"
103
+ import * as loadableModule from "@${metaName}/runtime/loadable"
104
+
105
+ const resolveLoadableExport = module => {
106
+ const candidates = [module, module.default, module.default?.default];
107
+ const loadable = candidates.find(candidate => typeof candidate === 'function');
108
+
109
+ if (!loadable) {
110
+ throw new TypeError('Modern.js runtime loadable export must resolve to a function');
111
+ }
112
+
113
+ return loadable;
114
+ };
115
+ const loadable = resolveLoadableExport(loadableModule);
116
+ const loadableLazy = loadableModule.lazy || loadableModule.default?.lazy || loadable.lazy;
104
117
  `;
105
118
  let rootLayoutCode = "";
106
119
  const getDataLoaderPath = ({ loaderId, clientData, action, inline, routeId, inValidSSRRoute })=>{
@@ -170,16 +183,9 @@ const fileSystemRoutes = async ({ metaName, routes, ssrMode, nestedRoutesEntry,
170
183
  webpackChunkName: true
171
184
  });
172
185
  component = 'string' === ssrMode ? `loadable(${lazyImport})` : `lazy(${lazyImport})`;
173
- } else if ('string' === ssrMode) {
186
+ } else {
174
187
  components.push(route._component);
175
188
  component = `component_${components.length - 1}`;
176
- } else {
177
- lazyImport = createLazyImport({
178
- componentPath: route._component,
179
- routeId: route.id,
180
- eager: true
181
- });
182
- component = `lazy(${lazyImport})`;
183
189
  }
184
190
  } else if (route._component) if (splitRouteChunks) {
185
191
  lazyImport = `() => import('${route._component}')`;
@@ -0,0 +1,10 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { Suspense } from "react";
3
+ function wrapTanstackSsrHydrationBoundary(routerContent, shouldWrap) {
4
+ if (shouldWrap) return /*#__PURE__*/ jsx(Suspense, {
5
+ fallback: null,
6
+ children: routerContent
7
+ });
8
+ return routerContent;
9
+ }
10
+ export { wrapTanstackSsrHydrationBoundary };
@@ -0,0 +1,17 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { Outlet } from "@tanstack/react-router";
3
+ import { createElement, memo } from "react";
4
+ const outlet_Outlet = /*#__PURE__*/ memo(function() {
5
+ return /*#__PURE__*/ jsx(Outlet, {});
6
+ });
7
+ function withModernRouteMatchContext(component, _routeId) {
8
+ if (!component) return component;
9
+ const Component = component;
10
+ const WrappedRouteComponent = (props)=>/*#__PURE__*/ createElement(Component, props);
11
+ const preloadable = component;
12
+ if ('function' == typeof preloadable.load) WrappedRouteComponent.load = preloadable.load.bind(preloadable);
13
+ if ('function' == typeof preloadable.preload) WrappedRouteComponent.preload = preloadable.preload.bind(preloadable);
14
+ else if ('function' == typeof preloadable.load) WrappedRouteComponent.preload = WrappedRouteComponent.load;
15
+ return WrappedRouteComponent;
16
+ }
17
+ export { outlet_Outlet as Outlet, withModernRouteMatchContext };
@@ -2,15 +2,18 @@ import { jsx } from "react/jsx-runtime";
2
2
  import { merge } from "@modern-js/runtime-utils/merge";
3
3
  import { normalizePathname } from "@modern-js/runtime-utils/url";
4
4
  import { RouterProvider, createBrowserHistory, createHashHistory, createRouter, useLocation, useMatches, useNavigate, useRouter } from "@tanstack/react-router";
5
- import { RouterClient } from "@tanstack/react-router/ssr/client";
5
+ import { hydrate } from "@tanstack/react-router/ssr/client";
6
+ import { useContext, useMemo } from "react";
6
7
  import { InternalRuntimeContext, getGlobalEnableRsc, getGlobalLayoutApp, getGlobalRoutes } from "../../../core/context/index.mjs";
7
8
  import { onAfterCreateRouter, onAfterHydrateRouter, onBeforeCreateRouter, onBeforeHydrateRouter } from "../hooks.mjs";
8
9
  import { applyRouterRuntimeState } from "../lifecycle.mjs";
9
10
  import { createRouteObjectsFromConfig, urlJoin } from "../utils.mjs";
10
11
  import { createModernBasepathRewrite } from "./basepathRewrite.mjs";
12
+ import { wrapTanstackSsrHydrationBoundary } from "./hydrationBoundary.mjs";
13
+ import { withModernRouteMatchContext } from "./outlet.mjs";
14
+ import { Link } from "./prefetchLink.mjs";
11
15
  import { createRouteTreeFromRouteObjects } from "./routeTree.mjs";
12
16
  import { getTanstackRscSerializationAdapters } from "./rsc/client.mjs";
13
- import * as __rspack_external_react from "react";
14
17
  const BLOCKING_SUBSCRIBE_SYMBOL = Symbol.for('@modern-js/plugin-runtime:tanstack-blocking-subscribe');
15
18
  const BLOCKING_STATE_SYMBOL = Symbol.for('@modern-js/plugin-runtime:tanstack-blocking-state');
16
19
  function normalizeBase(b) {
@@ -38,6 +41,85 @@ function wrapRouterSubscribeWithBlockState(router, getBlockNavState) {
38
41
  };
39
42
  target[BLOCKING_SUBSCRIBE_SYMBOL] = true;
40
43
  }
44
+ const routerHydrationRecords = new WeakMap();
45
+ const routeModulesKey = '_routeModules';
46
+ function pickRouteModuleComponent(routeModule, seen = new Set()) {
47
+ if ('function' == typeof routeModule || routeModule && 'object' == typeof routeModule && '$$typeof' in routeModule) return routeModule;
48
+ if (!routeModule || 'object' != typeof routeModule) return;
49
+ if (seen.has(routeModule)) return;
50
+ seen.add(routeModule);
51
+ const module = routeModule;
52
+ for (const candidate of [
53
+ module.default,
54
+ module.Component
55
+ ]){
56
+ const component = pickRouteModuleComponent(candidate, seen);
57
+ if (component) return component;
58
+ }
59
+ }
60
+ function getCachedRouteModule(routeId) {
61
+ if ("u" < typeof window) return;
62
+ return window[routeModulesKey]?.[routeId];
63
+ }
64
+ async function preloadHydratedRouteComponents(router) {
65
+ const preloadableRouter = router;
66
+ const routesById = preloadableRouter.routesById || {};
67
+ const matches = preloadableRouter.stores.matches.get();
68
+ await Promise.all(matches.map((match)=>{
69
+ if (!match.routeId) return;
70
+ const route = routesById[match.routeId];
71
+ const component = route?.options?.component;
72
+ const preload = component?.load || component?.preload;
73
+ if ('function' != typeof preload) return;
74
+ return Promise.resolve(preload.call(component)).then((routeModule)=>{
75
+ const modernRouteId = route?.options?.staticData?.modernRouteId;
76
+ const resolvedComponent = pickRouteModuleComponent(modernRouteId && getCachedRouteModule(modernRouteId) || routeModule);
77
+ if (resolvedComponent && modernRouteId) route.options.component = withModernRouteMatchContext(resolvedComponent, modernRouteId);
78
+ });
79
+ }));
80
+ }
81
+ function getTanstackSsrHydrationRecord(router) {
82
+ let hydrationRecord = routerHydrationRecords.get(router);
83
+ if (!hydrationRecord) {
84
+ hydrationRecord = {
85
+ promise: Promise.resolve(),
86
+ status: 'pending'
87
+ };
88
+ routerHydrationRecords.set(router, hydrationRecord);
89
+ try {
90
+ hydrationRecord.promise = hydrate(router).then(async (value)=>{
91
+ await preloadHydratedRouteComponents(router);
92
+ return value;
93
+ }).then((value)=>{
94
+ hydrationRecord.status = 'fulfilled';
95
+ return value;
96
+ }, (error)=>{
97
+ hydrationRecord.status = 'rejected';
98
+ hydrationRecord.error = error;
99
+ throw error;
100
+ });
101
+ } catch (error) {
102
+ hydrationRecord.status = 'rejected';
103
+ hydrationRecord.error = error;
104
+ hydrationRecord.promise = Promise.reject(error);
105
+ hydrationRecord.promise.catch(()=>{});
106
+ }
107
+ }
108
+ return hydrationRecord;
109
+ }
110
+ function getTanstackSsrHydrationPromise(router) {
111
+ return getTanstackSsrHydrationRecord(router).promise;
112
+ }
113
+ function hasTanstackSsrHydrationRecord(router) {
114
+ return routerHydrationRecords.has(router);
115
+ }
116
+ function ModernRouterClient({ router }) {
117
+ const hydrationRecord = getTanstackSsrHydrationRecord(router);
118
+ if ('rejected' === hydrationRecord.status) throw hydrationRecord.error;
119
+ return /*#__PURE__*/ jsx(RouterProvider, {
120
+ router: router
121
+ });
122
+ }
41
123
  function stripSyntheticNotFoundRoute(routes) {
42
124
  return routes.filter((route)=>!('*' === route.path && !route.id && !route.loader)).map((route)=>{
43
125
  if (!route.children?.length) return route;
@@ -56,9 +138,94 @@ const tanstackRouterPlugin = (userConfig = {})=>({
56
138
  onBeforeHydrateRouter: onBeforeHydrateRouter
57
139
  },
58
140
  setup: (api)=>{
59
- api.onBeforeRender((context)=>{
141
+ const hooks = api.getHooks();
142
+ let cachedRouteObjects;
143
+ let cachedRouteTree = null;
144
+ let cachedRouter = null;
145
+ let cachedRouterBasepath = null;
146
+ const getMergedConfig = ()=>{
60
147
  const pluginConfig = api.getRuntimeConfig();
61
- const mergedConfig = merge(pluginConfig.router || {}, userConfig);
148
+ return merge(pluginConfig.router || {}, userConfig);
149
+ };
150
+ const getRouteObjects = ()=>{
151
+ if (void 0 !== cachedRouteObjects) return cachedRouteObjects;
152
+ const mergedConfig = getMergedConfig();
153
+ const { routesConfig, createRoutes } = mergedConfig;
154
+ const finalRouteConfig = {
155
+ routes: getGlobalRoutes(),
156
+ globalApp: getGlobalLayoutApp(),
157
+ ...routesConfig
158
+ };
159
+ const routeObjects = createRoutes ? createRoutes() : createRouteObjectsFromConfig({
160
+ routesConfig: finalRouteConfig
161
+ }) || [];
162
+ const normalizedRouteObjects = createRoutes ? routeObjects : stripSyntheticNotFoundRoute(routeObjects);
163
+ cachedRouteObjects = hooks.modifyRoutes.call(normalizedRouteObjects);
164
+ return cachedRouteObjects;
165
+ };
166
+ const getRouteTree = ()=>{
167
+ if (cachedRouteTree) return cachedRouteTree;
168
+ const routeObjects = getRouteObjects();
169
+ if (!routeObjects.length) return null;
170
+ cachedRouteTree = createRouteTreeFromRouteObjects(routeObjects);
171
+ return cachedRouteTree;
172
+ };
173
+ const selectBasePath = (pathname)=>{
174
+ const { serverBase = [] } = getMergedConfig();
175
+ const match = serverBase.find((baseUrl)=>isSegmentPrefix(pathname, baseUrl));
176
+ return match || '/';
177
+ };
178
+ const getClientBasename = (runtimeContext)=>{
179
+ const { basename = '' } = getMergedConfig();
180
+ const baseUrl = selectBasePath(location.pathname).replace(/^\/*/, '/');
181
+ return '/' === baseUrl ? urlJoin(baseUrl, runtimeContext._internalRouterBaseName || basename || '') : baseUrl;
182
+ };
183
+ const getRouter = (runtimeContext, _basename)=>{
184
+ const routeTree = getRouteTree();
185
+ if (!routeTree) return null;
186
+ const lifecycleContext = {
187
+ framework: 'tanstack',
188
+ phase: 'client-create',
189
+ routes: getRouteObjects(),
190
+ runtimeContext,
191
+ basename: _basename
192
+ };
193
+ hooks.onBeforeCreateRouter.call(lifecycleContext);
194
+ if (cachedRouter && cachedRouterBasepath === _basename) {
195
+ wrapRouterSubscribeWithBlockState(cachedRouter, runtimeContext.unstable_getBlockNavState);
196
+ hooks.onAfterCreateRouter.call({
197
+ ...lifecycleContext,
198
+ router: cachedRouter,
199
+ runtimeContext
200
+ });
201
+ return cachedRouter;
202
+ }
203
+ const mergedConfig = getMergedConfig();
204
+ const { supportHtml5History = true } = mergedConfig;
205
+ const history = supportHtml5History ? createBrowserHistory() : createHashHistory();
206
+ const rewrite = createModernBasepathRewrite(_basename);
207
+ const serializationAdapters = getGlobalEnableRsc() ? getTanstackRscSerializationAdapters() : void 0;
208
+ cachedRouter = createRouter({
209
+ routeTree,
210
+ basepath: '/',
211
+ rewrite,
212
+ history,
213
+ context: {},
214
+ ...serializationAdapters ? {
215
+ serializationAdapters
216
+ } : {}
217
+ });
218
+ cachedRouterBasepath = _basename;
219
+ wrapRouterSubscribeWithBlockState(cachedRouter, runtimeContext.unstable_getBlockNavState);
220
+ hooks.onAfterCreateRouter.call({
221
+ ...lifecycleContext,
222
+ router: cachedRouter,
223
+ runtimeContext
224
+ });
225
+ return cachedRouter;
226
+ };
227
+ api.onBeforeRender(async (context)=>{
228
+ const mergedConfig = getMergedConfig();
62
229
  if ("u" > typeof window && window._SSR_DATA && mergedConfig.unstable_reloadOnURLMismatch) {
63
230
  const { ssrContext } = context;
64
231
  const currentPathname = normalizePathname(window.location.pathname);
@@ -70,96 +237,32 @@ const tanstackRouterPlugin = (userConfig = {})=>({
70
237
  }
71
238
  }
72
239
  context.router = {
240
+ Link: Link,
73
241
  useMatches: useMatches,
74
242
  useLocation: useLocation,
75
243
  useNavigate: useNavigate,
76
244
  useRouter: useRouter
77
245
  };
246
+ const hasSSRBootstrap = "u" > typeof window && Boolean(window.$_TSR);
247
+ if (hasSSRBootstrap && getRouteObjects().length) {
248
+ const runtimeContext = context;
249
+ const router = getRouter(runtimeContext, getClientBasename(runtimeContext));
250
+ if (router) await getTanstackSsrHydrationPromise(router);
251
+ }
78
252
  });
79
253
  api.wrapRoot((App)=>{
80
- const mergedConfig = merge(api.getRuntimeConfig().router || {}, userConfig);
81
- const { serverBase = [], supportHtml5History = true, basename = '', routesConfig, createRoutes } = mergedConfig;
82
- const finalRouteConfig = {
83
- routes: getGlobalRoutes(),
84
- globalApp: getGlobalLayoutApp(),
85
- ...routesConfig
86
- };
87
- if (!finalRouteConfig.routes && !createRoutes) return App;
88
- const hooks = api.getHooks();
89
- let cachedRouteObjects;
90
- const getRouteObjects = ()=>{
91
- if (void 0 !== cachedRouteObjects) return cachedRouteObjects;
92
- const routeObjects = createRoutes ? createRoutes() : createRouteObjectsFromConfig({
93
- routesConfig: finalRouteConfig
94
- }) || [];
95
- const normalizedRouteObjects = createRoutes ? routeObjects : stripSyntheticNotFoundRoute(routeObjects);
96
- cachedRouteObjects = hooks.modifyRoutes.call(normalizedRouteObjects);
97
- return cachedRouteObjects;
98
- };
99
- const selectBasePath = (pathname)=>{
100
- const match = serverBase.find((baseUrl)=>isSegmentPrefix(pathname, baseUrl));
101
- return match || '/';
102
- };
103
- let cachedRouteTree = null;
104
- let cachedRouter = null;
105
- let cachedRouterBasepath = null;
254
+ if (!getRouteObjects().length) return App;
106
255
  const RouterWrapper = ()=>{
107
- const runtimeContext = (0, __rspack_external_react.useContext)(InternalRuntimeContext);
108
- const baseUrl = selectBasePath(location.pathname).replace(/^\/*/, '/');
109
- const _basename = '/' === baseUrl ? urlJoin(baseUrl, runtimeContext._internalRouterBaseName || basename || '') : baseUrl;
110
- const routeTree = (0, __rspack_external_react.useMemo)(()=>{
111
- if (cachedRouteTree) return cachedRouteTree;
112
- const routeObjects = getRouteObjects();
113
- if (!routeObjects.length) return null;
114
- cachedRouteTree = createRouteTreeFromRouteObjects(routeObjects);
115
- return cachedRouteTree;
116
- }, []);
256
+ const runtimeContext = useContext(InternalRuntimeContext);
257
+ const _basename = getClientBasename(runtimeContext);
258
+ const routeTree = useMemo(()=>getRouteTree(), []);
117
259
  if (!routeTree) return App ? /*#__PURE__*/ jsx(App, {}) : null;
118
- const router = (0, __rspack_external_react.useMemo)(()=>{
119
- const lifecycleContext = {
120
- framework: 'tanstack',
121
- phase: 'client-create',
122
- routes: getRouteObjects(),
123
- runtimeContext,
124
- basename: _basename
125
- };
126
- hooks.onBeforeCreateRouter.call(lifecycleContext);
127
- if (cachedRouter && cachedRouterBasepath === _basename) {
128
- wrapRouterSubscribeWithBlockState(cachedRouter, runtimeContext.unstable_getBlockNavState);
129
- hooks.onAfterCreateRouter.call({
130
- ...lifecycleContext,
131
- router: cachedRouter,
132
- runtimeContext
133
- });
134
- return cachedRouter;
135
- }
136
- const history = supportHtml5History ? createBrowserHistory() : createHashHistory();
137
- const rewrite = createModernBasepathRewrite(_basename);
138
- const serializationAdapters = getGlobalEnableRsc() ? getTanstackRscSerializationAdapters() : void 0;
139
- cachedRouter = createRouter({
140
- routeTree,
141
- basepath: '/',
142
- rewrite,
143
- history,
144
- context: {},
145
- ...serializationAdapters ? {
146
- serializationAdapters
147
- } : {}
148
- });
149
- cachedRouterBasepath = _basename;
150
- wrapRouterSubscribeWithBlockState(cachedRouter, runtimeContext.unstable_getBlockNavState);
151
- hooks.onAfterCreateRouter.call({
152
- ...lifecycleContext,
153
- router: cachedRouter,
154
- runtimeContext
155
- });
156
- return cachedRouter;
157
- }, [
260
+ const router = useMemo(()=>getRouter(runtimeContext, _basename), [
158
261
  _basename,
159
262
  routeTree,
160
- supportHtml5History,
161
263
  runtimeContext
162
264
  ]);
265
+ if (!router) return App ? /*#__PURE__*/ jsx(App, {}) : null;
163
266
  const runtimeState = applyRouterRuntimeState(runtimeContext, {
164
267
  framework: 'tanstack',
165
268
  basename: _basename,
@@ -173,30 +276,29 @@ const tanstackRouterPlugin = (userConfig = {})=>({
173
276
  basename: _basename,
174
277
  router
175
278
  };
176
- const hasSSRBootstrap = "u" > typeof window && Boolean(window.$_TSR);
177
- if (hasSSRBootstrap) hooks.onBeforeHydrateRouter.call({
279
+ const hasSSRBootstrap = "u" > typeof window && (Boolean(window.$_TSR) || hasTanstackSsrHydrationRecord(router));
280
+ const needsRouterClient = hasSSRBootstrap;
281
+ if (needsRouterClient) hooks.onBeforeHydrateRouter.call({
178
282
  ...lifecycleContext,
179
283
  phase: 'hydrate',
180
284
  router,
181
285
  runtimeContext: runtimeState
182
286
  });
183
- const RouterContent = hasSSRBootstrap ? /*#__PURE__*/ jsx(__rspack_external_react.Suspense, {
184
- fallback: null,
185
- children: /*#__PURE__*/ jsx(RouterClient, {
186
- router: router
187
- })
287
+ const RouterContent = needsRouterClient ? /*#__PURE__*/ jsx(ModernRouterClient, {
288
+ router: router
188
289
  }) : /*#__PURE__*/ jsx(RouterProvider, {
189
290
  router: router
190
291
  });
191
- if (hasSSRBootstrap) hooks.onAfterHydrateRouter.call({
292
+ const HydratableRouterContent = wrapTanstackSsrHydrationBoundary(RouterContent, hasSSRBootstrap);
293
+ if (needsRouterClient) hooks.onAfterHydrateRouter.call({
192
294
  ...lifecycleContext,
193
295
  phase: 'hydrate',
194
296
  router,
195
297
  runtimeContext: runtimeState
196
298
  });
197
299
  return App ? /*#__PURE__*/ jsx(App, {
198
- children: RouterContent
199
- }) : RouterContent;
300
+ children: HydratableRouterContent
301
+ }) : HydratableRouterContent;
200
302
  };
201
303
  return RouterWrapper;
202
304
  });