@bleedingdev/modern-js-plugin-tanstack 3.2.0-ultramodern.99 → 3.4.0-ultramodern.1

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 (127) hide show
  1. package/dist/cjs/cli/index.js +80 -34
  2. package/dist/cjs/cli/routeSplitting.js +13 -41
  3. package/dist/cjs/cli/tanstackTypes.js +114 -200
  4. package/dist/cjs/cli.js +12 -8
  5. package/dist/cjs/runtime/basepathRewrite.js +12 -8
  6. package/dist/cjs/runtime/dataMutation.js +9 -5
  7. package/dist/cjs/runtime/hooks.js +20 -19
  8. package/dist/cjs/runtime/hydrationBoundary.js +12 -7
  9. package/dist/cjs/runtime/index.js +66 -54
  10. package/dist/cjs/runtime/lifecycle.js +21 -91
  11. package/dist/cjs/runtime/loaderBridge.js +173 -0
  12. package/dist/cjs/runtime/outlet.js +58 -0
  13. package/dist/cjs/runtime/plugin.js +190 -110
  14. package/dist/cjs/runtime/plugin.node.js +25 -40
  15. package/dist/cjs/runtime/plugin.worker.js +9 -5
  16. package/dist/cjs/runtime/pluginCore.js +55 -0
  17. package/dist/cjs/runtime/prefetchLink.js +10 -6
  18. package/dist/cjs/runtime/register.js +56 -0
  19. package/dist/cjs/runtime/routeTree.js +30 -212
  20. package/dist/cjs/runtime/router.js +41 -0
  21. package/dist/cjs/runtime/rsc/ClientSlot.js +9 -5
  22. package/dist/cjs/runtime/rsc/CompositeComponent.js +9 -5
  23. package/dist/cjs/runtime/rsc/ReplayableStream.js +14 -9
  24. package/dist/cjs/runtime/rsc/RscNodeRenderer.js +9 -5
  25. package/dist/cjs/runtime/rsc/SlotContext.js +9 -5
  26. package/dist/cjs/runtime/rsc/client.js +9 -5
  27. package/dist/cjs/runtime/rsc/createRscProxy.js +9 -5
  28. package/dist/cjs/runtime/rsc/index.js +9 -5
  29. package/dist/cjs/runtime/rsc/payloadRouter.js +44 -6
  30. package/dist/cjs/runtime/rsc/server.js +9 -5
  31. package/dist/cjs/runtime/rsc/slotUsageSanitizer.js +9 -5
  32. package/dist/cjs/runtime/rsc/symbols.js +20 -15
  33. package/dist/cjs/runtime/state.js +45 -0
  34. package/dist/cjs/runtime/types.js +13 -9
  35. package/dist/cjs/runtime/utils.js +9 -11
  36. package/dist/cjs/runtime.js +9 -5
  37. package/dist/esm/cli/index.mjs +73 -28
  38. package/dist/esm/cli/routeSplitting.mjs +1 -30
  39. package/dist/esm/cli/tanstackTypes.mjs +100 -190
  40. package/dist/esm/runtime/hooks.mjs +1 -8
  41. package/dist/esm/runtime/hydrationBoundary.mjs +3 -2
  42. package/dist/esm/runtime/index.mjs +4 -2
  43. package/dist/esm/runtime/lifecycle.mjs +1 -82
  44. package/dist/esm/runtime/loaderBridge.mjs +114 -0
  45. package/dist/esm/runtime/outlet.mjs +17 -0
  46. package/dist/esm/runtime/plugin.mjs +183 -107
  47. package/dist/esm/runtime/plugin.node.mjs +19 -38
  48. package/dist/esm/runtime/pluginCore.mjs +14 -0
  49. package/dist/esm/runtime/prefetchLink.mjs +1 -1
  50. package/dist/esm/runtime/register.mjs +18 -0
  51. package/dist/esm/runtime/routeTree.mjs +15 -198
  52. package/dist/esm/runtime/router.mjs +2 -0
  53. package/dist/esm/runtime/rsc/payloadRouter.mjs +35 -1
  54. package/dist/esm/runtime/state.mjs +7 -0
  55. package/dist/esm/runtime/utils.mjs +0 -6
  56. package/dist/esm-node/cli/index.mjs +73 -28
  57. package/dist/esm-node/cli/routeSplitting.mjs +1 -30
  58. package/dist/esm-node/cli/tanstackTypes.mjs +100 -190
  59. package/dist/esm-node/runtime/hooks.mjs +1 -8
  60. package/dist/esm-node/runtime/hydrationBoundary.mjs +3 -2
  61. package/dist/esm-node/runtime/index.mjs +4 -2
  62. package/dist/esm-node/runtime/lifecycle.mjs +1 -82
  63. package/dist/esm-node/runtime/loaderBridge.mjs +115 -0
  64. package/dist/esm-node/runtime/outlet.mjs +18 -0
  65. package/dist/esm-node/runtime/plugin.mjs +183 -107
  66. package/dist/esm-node/runtime/plugin.node.mjs +19 -38
  67. package/dist/esm-node/runtime/pluginCore.mjs +15 -0
  68. package/dist/esm-node/runtime/prefetchLink.mjs +1 -1
  69. package/dist/esm-node/runtime/register.mjs +19 -0
  70. package/dist/esm-node/runtime/routeTree.mjs +15 -198
  71. package/dist/esm-node/runtime/router.mjs +3 -0
  72. package/dist/esm-node/runtime/rsc/payloadRouter.mjs +35 -1
  73. package/dist/esm-node/runtime/state.mjs +8 -0
  74. package/dist/esm-node/runtime/utils.mjs +0 -6
  75. package/dist/types/cli/index.d.ts +11 -2
  76. package/dist/types/cli/routeSplitting.d.ts +6 -15
  77. package/dist/types/cli/tanstackTypes.d.ts +21 -1
  78. package/dist/types/runtime/hooks.d.ts +8 -33
  79. package/dist/types/runtime/index.d.ts +6 -3
  80. package/dist/types/runtime/lifecycle.d.ts +7 -22
  81. package/dist/types/runtime/loaderBridge.d.ts +48 -0
  82. package/dist/types/runtime/outlet.d.ts +2 -0
  83. package/dist/types/runtime/plugin.d.ts +1 -14
  84. package/dist/types/runtime/plugin.node.d.ts +1 -14
  85. package/dist/types/runtime/pluginCore.d.ts +21 -0
  86. package/dist/types/runtime/register.d.ts +9 -0
  87. package/dist/types/runtime/routeTree.d.ts +0 -2
  88. package/dist/types/runtime/router.d.ts +14 -0
  89. package/dist/types/runtime/state.d.ts +16 -0
  90. package/dist/types/runtime/types.d.ts +7 -53
  91. package/package.json +41 -39
  92. package/rstest.config.mts +6 -0
  93. package/src/cli/index.ts +146 -24
  94. package/src/cli/routeSplitting.ts +6 -44
  95. package/src/cli/tanstackTypes.ts +203 -208
  96. package/src/runtime/hooks.ts +10 -27
  97. package/src/runtime/hydrationBoundary.tsx +5 -5
  98. package/src/runtime/index.tsx +12 -14
  99. package/src/runtime/lifecycle.ts +16 -151
  100. package/src/runtime/loaderBridge.ts +257 -0
  101. package/src/runtime/outlet.tsx +48 -0
  102. package/src/runtime/plugin.node.tsx +28 -83
  103. package/src/runtime/plugin.tsx +350 -202
  104. package/src/runtime/pluginCore.ts +48 -0
  105. package/src/runtime/prefetchLink.tsx +1 -1
  106. package/src/runtime/register.ts +58 -0
  107. package/src/runtime/routeTree.ts +41 -365
  108. package/src/runtime/router.ts +15 -0
  109. package/src/runtime/rsc/payloadRouter.ts +45 -2
  110. package/src/runtime/ssr-shim.d.ts +1 -3
  111. package/src/runtime/state.ts +29 -0
  112. package/src/runtime/types.ts +20 -67
  113. package/src/runtime/utils.tsx +3 -7
  114. package/tests/router/cli.test.ts +373 -31
  115. package/tests/router/hooks.test.ts +26 -0
  116. package/tests/router/loaderBridge.test.ts +211 -0
  117. package/tests/router/packageSurface.test.ts +24 -0
  118. package/tests/router/prefetchLink.test.tsx +43 -7
  119. package/tests/router/register.test.ts +46 -0
  120. package/tests/router/routeTree.test.ts +176 -139
  121. package/tests/router/rsc.test.tsx +70 -0
  122. package/tests/router/tanstackTypes.test.ts +395 -7
  123. package/dist/cjs/runtime/DefaultNotFound.js +0 -47
  124. package/dist/esm/runtime/DefaultNotFound.mjs +0 -13
  125. package/dist/esm-node/runtime/DefaultNotFound.mjs +0 -14
  126. package/dist/types/runtime/DefaultNotFound.d.ts +0 -2
  127. package/src/runtime/DefaultNotFound.tsx +0 -15
@@ -0,0 +1,48 @@
1
+ import type { Plugin, RuntimePluginExtends } from '@modern-js/plugin';
2
+ import type { RuntimePluginAPI } from '@modern-js/plugin/runtime';
3
+ import {
4
+ getGlobalLayoutApp,
5
+ getGlobalRoutes,
6
+ type TInternalRuntimeContext,
7
+ } from '@modern-js/runtime/context';
8
+ import { merge } from '@modern-js/runtime-utils/merge';
9
+ import type { RouterExtendsHooks } from './hooks';
10
+ import type { RouterConfig } from './types';
11
+
12
+ export type TanstackRouterRuntimeConfig = {
13
+ plugins?: TanstackRouterRuntimePlugin[];
14
+ router?: Partial<RouterConfig>;
15
+ [key: string]: unknown;
16
+ };
17
+
18
+ type TanstackRouterRuntimeExtends = Required<
19
+ RuntimePluginExtends<TanstackRouterRuntimeConfig, TInternalRuntimeContext>
20
+ > & {
21
+ extendHooks: RouterExtendsHooks;
22
+ };
23
+
24
+ export type TanstackRouterPluginAPI =
25
+ RuntimePluginAPI<TanstackRouterRuntimeExtends>;
26
+
27
+ export type TanstackRouterRuntimePlugin = Plugin<
28
+ TanstackRouterPluginAPI,
29
+ TInternalRuntimeContext
30
+ >;
31
+
32
+ export function getMergedRouterConfig(
33
+ api: TanstackRouterPluginAPI,
34
+ userConfig: Partial<RouterConfig>,
35
+ ): RouterConfig {
36
+ const pluginConfig = api.getRuntimeConfig() as {
37
+ router?: Partial<RouterConfig>;
38
+ };
39
+ return merge(pluginConfig.router || {}, userConfig) as RouterConfig;
40
+ }
41
+
42
+ export function getFinalRouteConfig(mergedConfig: RouterConfig) {
43
+ return {
44
+ routes: getGlobalRoutes(),
45
+ globalApp: getGlobalLayoutApp(),
46
+ ...mergedConfig.routesConfig,
47
+ };
48
+ }
@@ -28,7 +28,7 @@ function resolvePreloadFromPrefetch(
28
28
  return prefetch;
29
29
  }
30
30
 
31
- return preload;
31
+ return 'viewport';
32
32
  }
33
33
 
34
34
  export type LinkProps<
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Registers the TanStack router provider with the @modern-js/runtime router
3
+ * provider registry. Importing '@modern-js/plugin-tanstack/runtime' is enough
4
+ * to make `runtime.router.framework: 'tanstack'` resolvable by the built-in
5
+ * router runtime plugin.
6
+ *
7
+ * This module is intentionally side-effectful (see `sideEffects` in
8
+ * package.json).
9
+ */
10
+ import {
11
+ type RouterProviderFactory,
12
+ type RouterProviderPlugin,
13
+ registerRouterProvider,
14
+ } from '@modern-js/runtime/context';
15
+ import { Form, RouteActionResponseError, useFetcher } from './dataMutation';
16
+ import { Outlet } from './outlet';
17
+ import { tanstackRouterPlugin } from './plugin';
18
+ import { Link, NavLink } from './prefetchLink';
19
+
20
+ // The TanStack runtime plugin types its API against its own hook registry,
21
+ // while the provider contract is typed against the built-in router hook
22
+ // registry of @modern-js/runtime. The two are runtime-compatible (the
23
+ // built-in router plugin registers the hooks and forwards its API to the
24
+ // resolved provider), but nominally distinct — hence the explicit adapter
25
+ // cast at this single boundary.
26
+ const tanstackRouterProviderFactory: RouterProviderFactory = userConfig =>
27
+ tanstackRouterPlugin(userConfig) as unknown as RouterProviderPlugin;
28
+
29
+ registerRouterProvider('tanstack', tanstackRouterProviderFactory);
30
+
31
+ /**
32
+ * Compatibility bindings for the deprecated `@modern-js/runtime/tanstack-router`
33
+ * alias. `@modern-js/runtime` cannot depend on this package (the dependency
34
+ * direction is plugin-tanstack -> runtime), so the alias resolves the
35
+ * Modern.js specific bindings through this `Symbol.for` slot at use time.
36
+ *
37
+ * `??=` keeps the first copy: when Module Federation evaluates a second
38
+ * bundled copy of this module, both copies are functionally identical and the
39
+ * established registration wins (mirroring the router-provider registry).
40
+ */
41
+ const COMPAT_BINDINGS_SLOT: unique symbol = Symbol.for(
42
+ '@modern-js/plugin-tanstack:runtime-compat-bindings',
43
+ );
44
+
45
+ export const tanstackRouterCompatBindings = {
46
+ Form,
47
+ Link,
48
+ NavLink,
49
+ Outlet,
50
+ RouteActionResponseError,
51
+ useFetcher,
52
+ };
53
+
54
+ (
55
+ globalThis as {
56
+ [COMPAT_BINDINGS_SLOT]?: typeof tanstackRouterCompatBindings;
57
+ }
58
+ )[COMPAT_BINDINGS_SLOT] ??= tanstackRouterCompatBindings;
@@ -1,6 +1,7 @@
1
1
  // @effect-diagnostics asyncFunction:off strictBooleanExpressions:off
2
+
3
+ import { DefaultNotFound } from '@modern-js/runtime/context';
2
4
  import type { RouteObject } from '@modern-js/runtime-utils/router';
3
- import type { NestedRoute, PageRoute } from '@modern-js/types';
4
5
  import type {
5
6
  AnyRoute,
6
7
  AnyRouter,
@@ -10,10 +11,16 @@ import {
10
11
  createRootRoute,
11
12
  createRoute,
12
13
  notFound,
13
- redirect,
14
+ rootRouteId,
14
15
  } from '@tanstack/react-router';
15
16
  import { createElement, type ElementType } from 'react';
16
- import { DefaultNotFound } from './DefaultNotFound';
17
+ import {
18
+ isRedirectResponse,
19
+ isResponse,
20
+ isTanstackRedirect,
21
+ throwTanstackRedirect,
22
+ } from './loaderBridge';
23
+ import { withModernRouteMatchContext } from './outlet';
17
24
  import {
18
25
  isTanstackRscPayloadNavigationEnabled,
19
26
  loadTanstackRscRouteData,
@@ -82,38 +89,12 @@ type ModernRouteObject = RouteObject & {
82
89
  validateSearch?: unknown;
83
90
  };
84
91
 
85
- type ModernGeneratedRoute = (NestedRoute | PageRoute) & {
86
- _component?: string;
87
- action?: unknown;
88
- children?: ModernGeneratedRoute[];
89
- component?: unknown;
90
- config?: { handle?: Record<string, unknown> } | unknown;
91
- clientData?: unknown;
92
- data?: string;
93
- error?: unknown;
94
- errorComponent?: unknown;
95
- filename?: string;
96
- handle?: Record<string, unknown>;
97
- hasAction?: boolean;
98
- hasClientLoader?: boolean;
99
- hasLoader?: boolean;
100
- inValidSSRRoute?: boolean;
101
- id?: string;
102
- index?: boolean;
103
- isClientComponent?: boolean;
104
- isRoot?: boolean;
105
- lazyImport?: () => unknown;
106
- loader?: ModernLoader;
107
- loaderDeps?: unknown;
108
- loading?: unknown;
109
- pendingComponent?: unknown;
110
- path?: string;
111
- shouldRevalidate?: ModernShouldRevalidate;
112
- validateSearch?: unknown;
113
- };
114
-
115
92
  type MutableTanstackRoute = AnyRoute & {
116
93
  addChildren: (children: AnyRoute[]) => void;
94
+ id?: string;
95
+ options: {
96
+ component?: unknown;
97
+ };
117
98
  };
118
99
 
119
100
  type TanstackRouteOptions = Record<string, unknown>;
@@ -148,6 +129,20 @@ function createTanstackRootRoute(
148
129
  return createRootRoute(options as never) as unknown as MutableTanstackRoute;
149
130
  }
150
131
 
132
+ function wrapRouteComponentWithModernContext(
133
+ route: MutableTanstackRoute,
134
+ component: unknown,
135
+ routeId?: string,
136
+ ) {
137
+ const routeMatchId = routeId || route.id;
138
+ if (component && routeMatchId) {
139
+ route.options.component = withModernRouteMatchContext(
140
+ component,
141
+ routeMatchId,
142
+ ) as typeof route.options.component;
143
+ }
144
+ }
145
+
151
146
  function toTanstackPath(pathname: string): string {
152
147
  // TanStack Router uses `$param` and `$` (splat) style params.
153
148
  // Modern's conventional routing currently generates React Router style params (e.g. `:id`, `*`).
@@ -177,28 +172,6 @@ function toTanstackPath(pathname: string): string {
177
172
  .join('/');
178
173
  }
179
174
 
180
- function isResponse(value: unknown): value is Response {
181
- const record = value as { headers?: unknown; status?: unknown } | null;
182
- return (
183
- record != null &&
184
- typeof record === 'object' &&
185
- typeof record.status === 'number' &&
186
- typeof record.headers === 'object'
187
- );
188
- }
189
-
190
- function isTanstackRedirect(value: unknown): boolean {
191
- return (
192
- isResponse(value) &&
193
- typeof (value as { options?: unknown }).options === 'object'
194
- );
195
- }
196
-
197
- const redirectStatusCodes = new Set([301, 302, 303, 307, 308]);
198
- function isRedirectResponse(res: Response) {
199
- return redirectStatusCodes.has(res.status);
200
- }
201
-
202
175
  function isModernDeferredData(
203
176
  value: unknown,
204
177
  ): value is ModernDeferredDataLike & { data: Record<string, unknown> } {
@@ -303,46 +276,6 @@ function createServerLazyImportComponent(
303
276
  return Component;
304
277
  }
305
278
 
306
- function isAbsoluteUrl(value: string) {
307
- try {
308
- void new URL(value);
309
- return true;
310
- } catch {
311
- return false;
312
- }
313
- }
314
-
315
- function throwTanstackRedirect(location: string) {
316
- const target = location || '/';
317
- // Prefer `to` for internal/relative redirects so basepath can be applied.
318
- // Use `href` for absolute redirects (external).
319
- if (isAbsoluteUrl(target)) {
320
- throw redirect({ href: target });
321
- }
322
-
323
- throw redirect({ to: target });
324
- }
325
-
326
- function mapParamsForModernLoader({
327
- modernRoute,
328
- params,
329
- }: {
330
- modernRoute: NestedRoute | PageRoute;
331
- params: RouteParams;
332
- }) {
333
- // React Router uses `*` for splat params, TanStack Router uses `_splat`.
334
- if (modernRoute.type === 'nested' && modernRoute.path?.includes('*')) {
335
- const { _splat, ...rest } = params as RouteParams & {
336
- _splat?: string;
337
- };
338
- if (typeof _splat !== 'undefined') {
339
- return { ...rest, '*': _splat };
340
- }
341
- return rest;
342
- }
343
- return params;
344
- }
345
-
346
279
  function createModernRequest(input: string, signal: AbortSignal) {
347
280
  return new Request(input, { signal });
348
281
  }
@@ -400,92 +333,6 @@ function createModernShouldReload(
400
333
  };
401
334
  }
402
335
 
403
- function wrapModernLoader(
404
- modernRoute: NestedRoute | PageRoute,
405
- modernLoader: ModernLoader | undefined,
406
- revalidationState?: RouteRevalidationState,
407
- options: RouteTreeOptions = {},
408
- ) {
409
- const route = modernRoute as ModernGeneratedRoute;
410
- return async (ctx: TanstackLoaderContext) => {
411
- try {
412
- if (revalidationState) {
413
- rememberRouteLocation(revalidationState, ctx);
414
- }
415
-
416
- if (typeof route.lazyImport === 'function') {
417
- try {
418
- await route.lazyImport();
419
- } catch {}
420
- }
421
-
422
- const signal: AbortSignal =
423
- ctx?.abortController?.signal ||
424
- ctx?.signal ||
425
- new AbortController().signal;
426
- const baseRequest: Request | undefined =
427
- ctx?.context?.request instanceof Request
428
- ? ctx.context.request
429
- : undefined;
430
-
431
- const href =
432
- typeof ctx?.location === 'string'
433
- ? ctx.location
434
- : ctx?.location?.publicHref ||
435
- ctx?.location?.href ||
436
- ctx?.location?.url?.href ||
437
- '';
438
-
439
- const request =
440
- baseRequest !== undefined
441
- ? new Request(baseRequest, { signal })
442
- : createModernRequest(href, signal);
443
- const params = mapParamsForModernLoader({
444
- modernRoute,
445
- params: ctx.params || {},
446
- });
447
-
448
- const loadModernData = async () => {
449
- const result = modernLoader
450
- ? await modernLoader({
451
- request,
452
- params,
453
- context: ctx?.context?.requestContext,
454
- })
455
- : null;
456
-
457
- return normalizeModernLoaderResponse(result);
458
- };
459
-
460
- if (options.rscPayloadRouter && isTanstackRscPayloadNavigationEnabled()) {
461
- return loadTanstackRscRouteData({
462
- hasClientLoader:
463
- route.hasClientLoader || typeof route.clientData !== 'undefined',
464
- loadClientData: loadModernData,
465
- request,
466
- routeId: ctx.route?.id,
467
- });
468
- }
469
-
470
- return loadModernData();
471
- } catch (err) {
472
- if (isResponse(err)) {
473
- if (isTanstackRedirect(err)) {
474
- throw err;
475
- }
476
- if (isRedirectResponse(err)) {
477
- const location = err.headers.get('Location') || '/';
478
- throwTanstackRedirect(location);
479
- }
480
- if (err.status === 404) {
481
- throw notFound();
482
- }
483
- }
484
- throw err;
485
- }
486
- };
487
- }
488
-
489
336
  function isRouteObjectPathlessLayout(route: RouteObject) {
490
337
  return !route.path && !route.index;
491
338
  }
@@ -627,15 +474,6 @@ function toRouteComponent(routeObject: RouteObject): unknown {
627
474
  return undefined;
628
475
  }
629
476
 
630
- function toModernRouteComponent(route: ModernGeneratedRoute): unknown {
631
- const component = route.component || undefined;
632
- if (typeof route.lazyImport === 'function' && component) {
633
- return createServerLazyImportComponent(route.lazyImport, component);
634
- }
635
-
636
- return component;
637
- }
638
-
639
477
  function toErrorComponent(routeObject: RouteObject): unknown {
640
478
  const route = routeObject as ModernRouteObject;
641
479
  if (route.ErrorBoundary) {
@@ -734,9 +572,10 @@ function createRouteFromRouteObject(opts: {
734
572
  const stableFallbackId =
735
573
  routeObject.id || modernRouteObject.file || routeObject.path || 'pathless';
736
574
 
575
+ const component = toRouteComponent(routeObject);
737
576
  const base: TanstackRouteOptions = {
738
577
  getParentRoute: () => parent,
739
- component: toRouteComponent(routeObject),
578
+ component,
740
579
  pendingComponent: toPendingComponent(routeObject),
741
580
  errorComponent: toErrorComponent(routeObject),
742
581
  validateSearch: modernRouteObject.validateSearch,
@@ -775,6 +614,7 @@ function createRouteFromRouteObject(opts: {
775
614
  }
776
615
 
777
616
  const route = createTanstackRoute(base);
617
+ wrapRouteComponentWithModernContext(route, component, routeObject.id);
778
618
 
779
619
  const children = routeObject.children;
780
620
  if (children && children.length > 0) {
@@ -791,179 +631,6 @@ function createRouteFromRouteObject(opts: {
791
631
  return route;
792
632
  }
793
633
 
794
- function createRouteFromModernRoute(opts: {
795
- options?: RouteTreeOptions;
796
- parent: AnyRoute;
797
- modernRoute: NestedRoute | PageRoute;
798
- }): AnyRoute {
799
- const { options = {}, parent, modernRoute } = opts;
800
- const route = modernRoute as ModernGeneratedRoute;
801
- const revalidationState: RouteRevalidationState = {};
802
-
803
- const modernId = route.id;
804
- const stableFallbackId =
805
- modernId ||
806
- route._component ||
807
- route.filename ||
808
- route.data ||
809
- (typeof route.loader === 'function' ? route.id : undefined);
810
-
811
- const pendingComponent = route.loading || route.pendingComponent;
812
- const errorComponent = route.error || route.errorComponent;
813
- const component = toModernRouteComponent(route);
814
- const modernLoader = route.loader;
815
- const modernAction = route.action;
816
- const modernShouldRevalidate = route.shouldRevalidate;
817
- const shouldReload = createModernShouldReload(
818
- modernShouldRevalidate,
819
- revalidationState,
820
- );
821
-
822
- // Pathless layout: no path segment, but must remain in the tree.
823
- const isPathlessLayout =
824
- route.type === 'nested' &&
825
- typeof route.index !== 'boolean' &&
826
- typeof route.path === 'undefined';
827
-
828
- const isIndexRoute = route.type === 'nested' && Boolean(route.index);
829
-
830
- const base: TanstackRouteOptions = {
831
- getParentRoute: () => parent,
832
- component: component || undefined,
833
- pendingComponent: pendingComponent || undefined,
834
- errorComponent: errorComponent || undefined,
835
- validateSearch: route.validateSearch,
836
- loaderDeps: route.loaderDeps,
837
- staticData: createRouteStaticData({
838
- modernRouteId: modernId,
839
- modernRouteAction: modernAction,
840
- modernRouteHandle: mergeModernRouteHandle(route),
841
- modernRouteHasAction: route.hasAction || Boolean(modernAction),
842
- modernRouteHasClientLoader:
843
- route.hasClientLoader || typeof route.clientData !== 'undefined',
844
- modernRouteHasLoader:
845
- route.hasLoader || typeof modernLoader === 'function',
846
- modernRouteIsClientComponent: route.isClientComponent,
847
- modernRouteLoader: modernLoader,
848
- modernRouteShouldRevalidate: modernShouldRevalidate,
849
- }),
850
- loader: wrapModernLoader(
851
- modernRoute,
852
- modernLoader,
853
- revalidationState,
854
- options,
855
- ),
856
- };
857
- if (route.inValidSSRRoute) {
858
- base.ssr = false;
859
- }
860
- if (shouldReload) {
861
- base.shouldReload = shouldReload;
862
- }
863
-
864
- if (isPathlessLayout) {
865
- // Use a stable custom id for pathless layouts to avoid hydration mismatch.
866
- base.id = stableFallbackId || 'pathless';
867
- } else {
868
- const rawPath = route.path;
869
- base.path = isIndexRoute ? '/' : toTanstackPath(rawPath || '');
870
- }
871
-
872
- const tanstackRoute = createTanstackRoute(base);
873
-
874
- const children = route.children as Array<NestedRoute | PageRoute> | undefined;
875
- if (children && children.length > 0) {
876
- const childRoutes = children.map(child =>
877
- createRouteFromModernRoute({
878
- options,
879
- parent: tanstackRoute,
880
- modernRoute: child,
881
- }),
882
- );
883
- tanstackRoute.addChildren(childRoutes);
884
- }
885
-
886
- return tanstackRoute;
887
- }
888
-
889
- export function createRouteTreeFromModernRoutes(
890
- routes: Array<NestedRoute | PageRoute>,
891
- options: RouteTreeOptions = {},
892
- ): ModernTanstackRootRoute {
893
- const rootModern = routes.find(
894
- r =>
895
- r &&
896
- (r as ModernGeneratedRoute).type === 'nested' &&
897
- (r as ModernGeneratedRoute).isRoot,
898
- ) as ModernGeneratedRoute | undefined;
899
-
900
- const rootComponent = rootModern
901
- ? toModernRouteComponent(rootModern)
902
- : undefined;
903
- const pendingComponent = rootModern?.loading;
904
- const errorComponent = rootModern?.error;
905
- const rootLoader = rootModern?.loader;
906
- const rootAction = rootModern?.action;
907
- const rootModernId = rootModern?.id;
908
- const rootShouldRevalidate = rootModern?.shouldRevalidate;
909
- const rootRevalidationState: RouteRevalidationState = {};
910
- const rootShouldReload = createModernShouldReload(
911
- rootShouldRevalidate,
912
- rootRevalidationState,
913
- );
914
-
915
- const rootRouteOptions: TanstackRootRouteOptions = {
916
- component: rootComponent || undefined,
917
- pendingComponent: pendingComponent || undefined,
918
- errorComponent: errorComponent || undefined,
919
- validateSearch: rootModern?.validateSearch,
920
- loaderDeps: rootModern?.loaderDeps,
921
- notFoundComponent: DefaultNotFound,
922
- staticData: createRouteStaticData({
923
- modernRouteId: rootModernId,
924
- modernRouteAction: rootAction,
925
- modernRouteHandle: rootModern
926
- ? mergeModernRouteHandle(rootModern)
927
- : undefined,
928
- modernRouteHasAction: rootModern?.hasAction || Boolean(rootAction),
929
- modernRouteHasClientLoader:
930
- rootModern?.hasClientLoader ||
931
- typeof rootModern?.clientData !== 'undefined',
932
- modernRouteHasLoader:
933
- rootModern?.hasLoader || typeof rootLoader === 'function',
934
- modernRouteIsClientComponent: rootModern?.isClientComponent,
935
- modernRouteLoader: rootLoader,
936
- modernRouteShouldRevalidate: rootShouldRevalidate,
937
- }),
938
- loader: rootModern
939
- ? wrapModernLoader(rootModern, rootLoader, rootRevalidationState, options)
940
- : undefined,
941
- };
942
- if (rootShouldReload) {
943
- rootRouteOptions.shouldReload = rootShouldReload;
944
- }
945
- if (rootModern?.inValidSSRRoute) {
946
- rootRouteOptions.ssr = false;
947
- }
948
-
949
- const rootRoute = createTanstackRootRoute(rootRouteOptions);
950
-
951
- const topLevel = rootModern
952
- ? (rootModern.children as Array<NestedRoute | PageRoute>) || []
953
- : routes;
954
-
955
- const childRoutes = topLevel.map(child =>
956
- createRouteFromModernRoute({
957
- options,
958
- parent: rootRoute,
959
- modernRoute: child,
960
- }),
961
- );
962
-
963
- rootRoute.addChildren(childRoutes);
964
- return rootRoute as unknown as ModernTanstackRootRoute;
965
- }
966
-
967
634
  function getRootLikeRouteObject(routes: RouteObject[]) {
968
635
  return routes.find(route => route.path === '/' && !route.index);
969
636
  }
@@ -982,8 +649,11 @@ export function createRouteTreeFromRouteObjects(
982
649
  rootRevalidationState,
983
650
  );
984
651
 
652
+ const rootComponent = rootLikeRoute
653
+ ? toRouteComponent(rootLikeRoute)
654
+ : undefined;
985
655
  const rootRouteOptions: TanstackRootRouteOptions = {
986
- component: rootLikeRoute ? toRouteComponent(rootLikeRoute) : undefined,
656
+ component: rootComponent,
987
657
  pendingComponent: rootLikeRoute
988
658
  ? toPendingComponent(rootLikeRoute)
989
659
  : undefined,
@@ -1020,6 +690,12 @@ export function createRouteTreeFromRouteObjects(
1020
690
  }
1021
691
 
1022
692
  const rootRoute = createTanstackRootRoute(rootRouteOptions);
693
+ if (rootComponent) {
694
+ rootRoute.options.component = withModernRouteMatchContext(
695
+ rootComponent,
696
+ rootRouteId,
697
+ ) as typeof rootRoute.options.component;
698
+ }
1023
699
 
1024
700
  const topLevel = rootLikeRoute
1025
701
  ? [
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Entry-injected router module for entrypoints that are NOT TanStack
3
+ * file-route entrypoints (e.g. apps configuring
4
+ * `router: { framework: 'tanstack', createRoutes }` in modern.runtime.ts).
5
+ *
6
+ * The CLI plugin injects `{ name: 'router', path: '<pkg>/runtime/router' }`
7
+ * for those entries, so the generated entry code value-imports `routerPlugin`
8
+ * from here. That single import both installs the framework-resolving router
9
+ * plugin of @modern-js/runtime AND registers the TanStack router provider
10
+ * (the './register' side effect) — the registration can therefore never be
11
+ * tree-shaken away from the entry that needs it.
12
+ */
13
+ import './register';
14
+
15
+ export { routerPlugin } from '@modern-js/runtime/router/internal';
@@ -1,5 +1,6 @@
1
- // @effect-diagnostics asyncFunction:off globalFetch:off strictBooleanExpressions:off
1
+ // @effect-diagnostics asyncFunction:off globalFetch:off processEnv:off strictBooleanExpressions:off
2
2
  import type { PayloadRoute, ServerPayload } from '@modern-js/runtime/context';
3
+ import { isRouteErrorResponse } from '@modern-js/runtime-utils/router';
3
4
  import { notFound, redirect } from '@tanstack/react-router';
4
5
 
5
6
  type PayloadDecoder = (stream: ReadableStream<Uint8Array>) => Promise<unknown>;
@@ -103,6 +104,48 @@ function toPayloadRoute(match: RouterMatchLike): PayloadRoute | undefined {
103
104
  };
104
105
  }
105
106
 
107
+ function shouldRedactServerError(status = 500) {
108
+ return (
109
+ status >= 500 &&
110
+ process.env.NODE_ENV !== 'development' &&
111
+ process.env.NODE_ENV !== 'test'
112
+ );
113
+ }
114
+
115
+ function serializePayloadError(error: unknown): unknown {
116
+ if (isRouteErrorResponse(error)) {
117
+ if (shouldRedactServerError(error.status)) {
118
+ return {
119
+ status: error.status,
120
+ statusText: 'Internal Server Error',
121
+ data: 'Unexpected Server Error',
122
+ __type: 'RouteErrorResponse',
123
+ };
124
+ }
125
+
126
+ return { ...error, __type: 'RouteErrorResponse' };
127
+ }
128
+
129
+ if (error instanceof Error) {
130
+ if (shouldRedactServerError()) {
131
+ return {
132
+ message: 'Unexpected Server Error',
133
+ stack: undefined,
134
+ __type: 'Error',
135
+ };
136
+ }
137
+
138
+ return {
139
+ message: error.message,
140
+ stack: error.stack,
141
+ __type: 'Error',
142
+ ...(error.name !== 'Error' ? { __subType: error.name } : {}),
143
+ };
144
+ }
145
+
146
+ return error;
147
+ }
148
+
106
149
  export function createTanstackRscServerPayload(
107
150
  router: TanstackPayloadRouterLike,
108
151
  options: {
@@ -133,7 +176,7 @@ export function createTanstackRscServerPayload(
133
176
  }
134
177
 
135
178
  if (typeof match.error !== 'undefined') {
136
- errors[payloadRoute.id] = match.error;
179
+ errors[payloadRoute.id] = serializePayloadError(match.error);
137
180
  }
138
181
  }
139
182
 
@@ -1,7 +1,5 @@
1
1
  declare module '@tanstack/react-router/ssr/client' {
2
- export function RouterClient(props: {
3
- router: unknown;
4
- }): import('react').JSX.Element;
2
+ export function hydrate(router: unknown): Promise<unknown>;
5
3
  }
6
4
 
7
5
  declare module '@tanstack/react-router/ssr/server' {