@bleedingdev/modern-js-plugin-tanstack 3.9.0-ultramodern.2 → 3.9.0-ultramodern.21

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 (137) hide show
  1. package/dist/cjs/cli/artifacts.js +15 -1
  2. package/dist/cjs/cli/index.js +12 -0
  3. package/dist/cjs/cli/routeMetadata.js +131 -0
  4. package/dist/cjs/cli/tanstackTypes/canonicalRoutes.js +7 -7
  5. package/dist/cjs/cli/tanstackTypes/shared.js +0 -14
  6. package/dist/cjs/cli/tanstackTypes/source.js +27 -60
  7. package/dist/cjs/runtime/basepathRewrite.js +13 -6
  8. package/dist/cjs/runtime/lifecycle.js +6 -9
  9. package/dist/cjs/runtime/loaderBridge.js +10 -4
  10. package/dist/cjs/runtime/navigation.js +102 -0
  11. package/dist/cjs/runtime/plugin.js +7 -2
  12. package/dist/cjs/runtime/plugin.node.js +86 -64
  13. package/dist/cjs/runtime/prefetchLink.js +2 -7
  14. package/dist/cjs/runtime/register.js +0 -2
  15. package/dist/cjs/runtime/routeTree/construction.js +19 -59
  16. package/dist/cjs/runtime/routeTree/loaders.js +1 -1
  17. package/dist/cjs/runtime/routeTree/paths.js +0 -6
  18. package/dist/cjs/runtime/router.js +25 -9
  19. package/dist/cjs/runtime/ssrManagedTags.js +2 -12
  20. package/dist/cjs/runtime/state.js +2 -2
  21. package/dist/cjs/runtime/utils.js +3 -0
  22. package/dist/cjs/shared/routeDescriptor.js +60 -0
  23. package/dist/esm/cli/artifacts.mjs +13 -2
  24. package/dist/esm/cli/index.mjs +12 -9
  25. package/dist/esm/cli/routeMetadata.mjs +71 -0
  26. package/dist/esm/cli/tanstackTypes/canonicalRoutes.mjs +7 -7
  27. package/dist/esm/cli/tanstackTypes/shared.mjs +1 -9
  28. package/dist/esm/cli/tanstackTypes/source.mjs +28 -61
  29. package/dist/esm/runtime/basepathRewrite.mjs +10 -3
  30. package/dist/esm/runtime/lifecycle.mjs +1 -1
  31. package/dist/esm/runtime/loaderBridge.mjs +8 -5
  32. package/dist/esm/runtime/navigation.mjs +64 -0
  33. package/dist/esm/runtime/plugin.mjs +7 -2
  34. package/dist/esm/runtime/plugin.node.mjs +88 -66
  35. package/dist/esm/runtime/prefetchLink.mjs +1 -6
  36. package/dist/esm/runtime/register.mjs +0 -2
  37. package/dist/esm/runtime/routeTree/construction.mjs +19 -59
  38. package/dist/esm/runtime/routeTree/loaders.mjs +2 -2
  39. package/dist/esm/runtime/routeTree/paths.mjs +1 -4
  40. package/dist/esm/runtime/router.mjs +27 -11
  41. package/dist/esm/runtime/ssrManagedTags.mjs +1 -8
  42. package/dist/esm/runtime/state.mjs +1 -1
  43. package/dist/esm/runtime/utils.mjs +3 -0
  44. package/dist/esm/shared/routeDescriptor.mjs +22 -0
  45. package/dist/esm-node/cli/artifacts.mjs +13 -2
  46. package/dist/esm-node/cli/index.mjs +12 -9
  47. package/dist/esm-node/cli/routeMetadata.mjs +72 -0
  48. package/dist/esm-node/cli/tanstackTypes/canonicalRoutes.mjs +7 -7
  49. package/dist/esm-node/cli/tanstackTypes/shared.mjs +1 -9
  50. package/dist/esm-node/cli/tanstackTypes/source.mjs +28 -61
  51. package/dist/esm-node/runtime/basepathRewrite.mjs +10 -3
  52. package/dist/esm-node/runtime/lifecycle.mjs +1 -1
  53. package/dist/esm-node/runtime/loaderBridge.mjs +8 -5
  54. package/dist/esm-node/runtime/navigation.mjs +65 -0
  55. package/dist/esm-node/runtime/plugin.mjs +7 -2
  56. package/dist/esm-node/runtime/plugin.node.mjs +88 -66
  57. package/dist/esm-node/runtime/prefetchLink.mjs +1 -6
  58. package/dist/esm-node/runtime/register.mjs +0 -2
  59. package/dist/esm-node/runtime/routeTree/construction.mjs +19 -59
  60. package/dist/esm-node/runtime/routeTree/loaders.mjs +2 -2
  61. package/dist/esm-node/runtime/routeTree/paths.mjs +1 -4
  62. package/dist/esm-node/runtime/router.mjs +27 -11
  63. package/dist/esm-node/runtime/ssrManagedTags.mjs +1 -8
  64. package/dist/esm-node/runtime/state.mjs +1 -1
  65. package/dist/esm-node/runtime/utils.mjs +3 -0
  66. package/dist/esm-node/shared/routeDescriptor.mjs +23 -0
  67. package/dist/types/cli/artifacts.d.ts +13 -5
  68. package/dist/types/cli/index.d.ts +1 -1
  69. package/dist/types/cli/routeMetadata.d.ts +18 -0
  70. package/dist/types/cli/tanstackTypes/shared.d.ts +0 -2
  71. package/dist/types/runtime/basepathRewrite.d.ts +2 -1
  72. package/dist/types/runtime/lifecycle.d.ts +9 -2
  73. package/dist/types/runtime/loaderBridge.d.ts +1 -0
  74. package/dist/types/runtime/navigation.d.ts +4 -0
  75. package/dist/types/runtime/register.d.ts +1 -9
  76. package/dist/types/runtime/routeTree/paths.d.ts +0 -2
  77. package/dist/types/runtime/router.d.ts +2 -13
  78. package/dist/types/runtime/ssrManagedTags.d.ts +0 -1
  79. package/dist/types/runtime/ssrTypes.d.ts +0 -7
  80. package/dist/types/runtime/state.d.ts +1 -1
  81. package/dist/types/runtime/types.d.ts +2 -1
  82. package/dist/types/shared/routeDescriptor.d.ts +26 -0
  83. package/package.json +22 -19
  84. package/rstest.config.mts +2 -24
  85. package/src/cli/artifacts.ts +53 -6
  86. package/src/cli/index.ts +11 -2
  87. package/src/cli/routeMetadata.ts +121 -0
  88. package/src/cli/tanstackTypes/canonicalRoutes.ts +17 -12
  89. package/src/cli/tanstackTypes/shared.ts +0 -14
  90. package/src/cli/tanstackTypes/source.ts +42 -116
  91. package/src/runtime/basepathRewrite.ts +11 -2
  92. package/src/runtime/lifecycle.ts +14 -4
  93. package/src/runtime/loaderBridge.ts +9 -9
  94. package/src/runtime/navigation.ts +78 -0
  95. package/src/runtime/plugin.node.tsx +120 -93
  96. package/src/runtime/plugin.tsx +11 -1
  97. package/src/runtime/prefetchLink.tsx +1 -23
  98. package/src/runtime/register.ts +1 -12
  99. package/src/runtime/routeTree/construction.ts +32 -110
  100. package/src/runtime/routeTree/loaders.ts +2 -4
  101. package/src/runtime/routeTree/paths.ts +0 -6
  102. package/src/runtime/router.ts +25 -40
  103. package/src/runtime/ssrManagedTags.ts +0 -17
  104. package/src/runtime/ssrTypes.ts +0 -7
  105. package/src/runtime/state.ts +1 -1
  106. package/src/runtime/types.ts +2 -2
  107. package/src/runtime/utils.tsx +4 -1
  108. package/src/shared/routeDescriptor.ts +46 -0
  109. package/tests/router/cli.test.ts +148 -824
  110. package/tests/router/clientNavigation.ts +13 -0
  111. package/tests/router/dataMutation.test.tsx +17 -85
  112. package/tests/router/flightSerialization.roundtrip.test.tsx +9 -6
  113. package/tests/router/generateRouteArtifacts.test.ts +139 -27
  114. package/tests/router/loaderBridge.test.ts +0 -25
  115. package/tests/router/localisedIdentity.test.tsx +100 -0
  116. package/tests/router/prefetchLink.test.tsx +46 -21
  117. package/tests/router/prefetchLinkPreload.test.tsx +11 -32
  118. package/tests/router/routeHooks.test.ts +42 -105
  119. package/tests/router/routeMetadata.test.ts +233 -0
  120. package/tests/router/routeTree.test.ts +144 -836
  121. package/tests/router/rsc.test.tsx +131 -166
  122. package/tests/router/serverPlugin.test.ts +193 -1
  123. package/tests/router/tanstackTypes.test.ts +218 -963
  124. package/dist/esm/rslib-runtime~1.mjs +0 -18
  125. package/dist/esm-node/rslib-runtime~0.mjs +0 -19
  126. package/tests/realm-isolation.test.ts +0 -114
  127. package/tests/router/fastDefaults.test.ts +0 -32
  128. package/tests/router/fixtures/legacyRuntimeRouter.ts +0 -10
  129. package/tests/router/fixtures/rscDecoderSecurityChild.cjs +0 -162
  130. package/tests/router/hooks.test.ts +0 -40
  131. package/tests/router/hydrationBoundary.test.tsx +0 -23
  132. package/tests/router/packageSurface.test.ts +0 -24
  133. package/tests/router/preloadRedirect.test.ts +0 -64
  134. package/tests/router/register.test.ts +0 -8
  135. package/tests/router/routerCompatibility.test.ts +0 -239
  136. package/tests/router/rscDecoderSecurity.test.ts +0 -113
  137. package/tests/router/rscPayloadRouterMatrix.test.ts +0 -197
@@ -5,6 +5,10 @@ import type { RouteObject } from '@modern-js/runtime-utils/router';
5
5
  import type { AnyRoute, AnyRouter } from '@tanstack/react-router';
6
6
  import { rootRouteId } from '@tanstack/react-router';
7
7
 
8
+ import {
9
+ describeRouteTree,
10
+ type RouteDescriptor,
11
+ } from '../../shared/routeDescriptor';
8
12
  import { createRouteStaticData } from '../../shared/routeStaticData';
9
13
  import { withModernRouteMatchContext } from '../outlet';
10
14
  import {
@@ -13,7 +17,6 @@ import {
13
17
  toRouteComponent,
14
18
  } from './components';
15
19
  import { createModernShouldReload, wrapRouteObjectLoader } from './loaders';
16
- import { isRouteObjectPathlessLayout, toTanstackPath } from './paths';
17
20
  import { mergeModernRouteHandle } from './staticData';
18
21
  import {
19
22
  createTanstackRootRoute,
@@ -23,25 +26,16 @@ import {
23
26
  import type {
24
27
  ModernRouteObject,
25
28
  ModernTanstackRootRoute,
26
- MutableTanstackRoute,
27
29
  RouteRevalidationState,
28
30
  RouteTreeOptions,
29
31
  TanstackRootRouteOptions,
30
32
  TanstackRouteOptions,
31
33
  } from './types';
32
34
 
33
- function createRouteFromRouteObject(opts: {
34
- options?: RouteTreeOptions;
35
- parent: AnyRoute;
36
- pathlessFallbackId?: string;
37
- routeObject: RouteObject;
38
- }): AnyRoute {
39
- const {
40
- options = {},
41
- parent,
42
- pathlessFallbackId = 'pathless',
43
- routeObject,
44
- } = opts;
35
+ function createRouteOptions(
36
+ routeObject: RouteObject,
37
+ options: RouteTreeOptions,
38
+ ): TanstackRouteOptions {
45
39
  const modernRouteObject = routeObject as ModernRouteObject;
46
40
  const revalidationState: RouteRevalidationState = {};
47
41
  const shouldRevalidate = modernRouteObject.shouldRevalidate;
@@ -49,13 +43,8 @@ function createRouteFromRouteObject(opts: {
49
43
  shouldRevalidate,
50
44
  revalidationState,
51
45
  );
52
-
53
- const stableFallbackId =
54
- routeObject.id || modernRouteObject.file || pathlessFallbackId;
55
-
56
46
  const component = toRouteComponent(routeObject);
57
47
  const base: TanstackRouteOptions = {
58
- getParentRoute: () => parent,
59
48
  component,
60
49
  pendingComponent: toPendingComponent(routeObject),
61
50
  errorComponent: toErrorComponent(routeObject),
@@ -86,116 +75,49 @@ function createRouteFromRouteObject(opts: {
86
75
  base.shouldReload = shouldReload;
87
76
  }
88
77
 
89
- if (isRouteObjectPathlessLayout(routeObject)) {
90
- base.id = stableFallbackId;
91
- } else {
92
- base.path = routeObject.index
93
- ? '/'
94
- : toTanstackPath((routeObject.path as string) || '');
95
- }
96
-
97
- const route = createTanstackRoute(base);
98
- wrapRouteComponentWithModernContext(route, component, routeObject.id);
78
+ return base;
79
+ }
99
80
 
100
- const children = routeObject.children;
101
- if (children && children.length > 0) {
102
- const childRoutes = children.map((child: RouteObject, index) =>
103
- createRouteFromRouteObject({
104
- options,
105
- parent: route,
106
- pathlessFallbackId: `pathless-${index}`,
107
- routeObject: child,
108
- }),
81
+ function createRouteFromDescriptor(
82
+ descriptor: RouteDescriptor<RouteObject>,
83
+ parent: AnyRoute,
84
+ options: RouteTreeOptions,
85
+ ): AnyRoute {
86
+ const { source, location, children } = descriptor;
87
+ const routeOptions = createRouteOptions(source, options);
88
+ const route = createTanstackRoute({
89
+ ...routeOptions,
90
+ ...location,
91
+ getParentRoute: () => parent,
92
+ });
93
+ wrapRouteComponentWithModernContext(route, routeOptions.component, source.id);
94
+ if (children.length) {
95
+ route.addChildren(
96
+ children.map(child => createRouteFromDescriptor(child, route, options)),
109
97
  );
110
- route.addChildren(childRoutes);
111
98
  }
112
-
113
99
  return route;
114
100
  }
115
101
 
116
- function getRootLikeRouteObject(routes: RouteObject[]) {
117
- return routes.find(route => route.path === '/' && !route.index);
118
- }
119
-
120
102
  export function createRouteTreeFromRouteObjects(
121
103
  routes: RouteObject[],
122
104
  options: RouteTreeOptions = {},
123
105
  ): ModernTanstackRootRoute {
124
- const rootLikeRoute = getRootLikeRouteObject(routes) as
125
- | ModernRouteObject
126
- | undefined;
127
- const rootRevalidationState: RouteRevalidationState = {};
128
- const rootShouldRevalidate = rootLikeRoute?.shouldRevalidate;
129
- const rootShouldReload = createModernShouldReload(
130
- rootShouldRevalidate,
131
- rootRevalidationState,
132
- );
133
-
134
- const rootComponent = rootLikeRoute
135
- ? toRouteComponent(rootLikeRoute)
136
- : undefined;
106
+ const { root, children } = describeRouteTree(routes);
137
107
  const rootRouteOptions: TanstackRootRouteOptions = {
138
- component: rootComponent,
139
- pendingComponent: rootLikeRoute
140
- ? toPendingComponent(rootLikeRoute)
141
- : undefined,
142
- errorComponent: rootLikeRoute ? toErrorComponent(rootLikeRoute) : undefined,
143
- validateSearch: rootLikeRoute?.validateSearch,
144
- loaderDeps: rootLikeRoute?.loaderDeps,
108
+ ...(root ? createRouteOptions(root, options) : {}),
145
109
  notFoundComponent: DefaultNotFound,
146
- staticData: createRouteStaticData({
147
- modernRouteId: rootLikeRoute?.id,
148
- modernRouteAction: rootLikeRoute?.action,
149
- modernRouteHandle: rootLikeRoute
150
- ? mergeModernRouteHandle(rootLikeRoute)
151
- : undefined,
152
- modernRouteHasAction:
153
- rootLikeRoute?.hasAction || Boolean(rootLikeRoute?.action),
154
- modernRouteHasClientLoader:
155
- rootLikeRoute?.hasClientLoader ||
156
- typeof rootLikeRoute?.clientData !== 'undefined',
157
- modernRouteHasLoader:
158
- rootLikeRoute?.hasLoader || typeof rootLikeRoute?.loader === 'function',
159
- modernRouteIsClientComponent: rootLikeRoute?.isClientComponent,
160
- modernRouteLoader: rootLikeRoute?.loader,
161
- modernRouteShouldRevalidate: rootShouldRevalidate,
162
- }),
163
- loader: rootLikeRoute
164
- ? wrapRouteObjectLoader(rootLikeRoute, rootRevalidationState, options)
165
- : undefined,
166
110
  };
167
- if (rootShouldReload) {
168
- rootRouteOptions.shouldReload = rootShouldReload;
169
- }
170
- if (rootLikeRoute?.inValidSSRRoute) {
171
- rootRouteOptions.ssr = false;
172
- }
173
-
174
111
  const rootRoute = createTanstackRootRoute(rootRouteOptions);
175
- if (rootComponent) {
112
+ if (rootRouteOptions.component) {
176
113
  rootRoute.options.component = withModernRouteMatchContext(
177
- rootComponent,
114
+ rootRouteOptions.component,
178
115
  rootRouteId,
179
116
  ) as typeof rootRoute.options.component;
180
117
  }
181
-
182
- const topLevel = rootLikeRoute
183
- ? [
184
- ...((rootLikeRoute.children as RouteObject[] | undefined) || []),
185
- ...routes.filter(route => route !== rootLikeRoute),
186
- ]
187
- : routes;
188
-
189
- const childRoutes = topLevel.map((routeObject, index) =>
190
- createRouteFromRouteObject({
191
- options,
192
- parent: rootRoute,
193
- pathlessFallbackId: `pathless-${index}`,
194
- routeObject,
195
- }),
118
+ rootRoute.addChildren(
119
+ children.map(child => createRouteFromDescriptor(child, rootRoute, options)),
196
120
  );
197
-
198
- rootRoute.addChildren(childRoutes);
199
121
  return rootRoute as unknown as ModernTanstackRootRoute;
200
122
  }
201
123
 
@@ -3,6 +3,7 @@
3
3
  import type { RouteObject } from '@modern-js/runtime-utils/router';
4
4
  import {
5
5
  getLoaderHref,
6
+ getLoaderSignal,
6
7
  handleModernLoaderError,
7
8
  handleModernLoaderResult,
8
9
  mapSplatParamsForModernLoader,
@@ -118,10 +119,7 @@ export function wrapRouteObjectLoader(
118
119
  } catch {}
119
120
  }
120
121
 
121
- const signal: AbortSignal =
122
- ctx?.abortController?.signal ||
123
- ctx?.signal ||
124
- new AbortController().signal;
122
+ const signal = getLoaderSignal(ctx);
125
123
  const baseRequest: Request | undefined =
126
124
  ctx?.context?.request instanceof Request
127
125
  ? ctx.context.request
@@ -1,7 +1,5 @@
1
1
  // @effect-diagnostics asyncFunction:off strictBooleanExpressions:off
2
2
 
3
- import type { RouteObject } from '@modern-js/runtime-utils/router';
4
-
5
3
  export function toTanstackPath(pathname: string): string {
6
4
  // TanStack Router uses `$param` and `$` (splat) style params.
7
5
  // Modern's conventional routing currently generates React Router style params (e.g. `:id`, `*`).
@@ -30,7 +28,3 @@ export function toTanstackPath(pathname: string): string {
30
28
  })
31
29
  .join('/');
32
30
  }
33
-
34
- export function isRouteObjectPathlessLayout(route: RouteObject) {
35
- return !route.path && !route.index;
36
- }
@@ -1,43 +1,28 @@
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. This module creates a framework-resolving wrapper whose
8
- * app-owned provider realm contains this module graph's TanStack factory.
9
- */
10
- import * as runtimeRouter from '@modern-js/runtime/router/internal';
1
+ /** App-local composition for custom entries using runtime router selection. */
2
+ import { routerProviderRegistryHooks } from '@modern-js/runtime/context';
3
+ import { routerPlugin as nativeRouterPlugin } from '@modern-js/runtime/router/internal';
4
+ import { createRouterPlugin } from '@modern-js/runtime-extensions/router-provider';
5
+ import { createRouterStatePlugin } from '@modern-js/runtime-extensions/router-state-plugin';
11
6
  import { tanstackRouterProviderFactory } from './register';
12
7
 
13
- type LegacyRouterPlugin = (typeof runtimeRouter)['routerPlugin'];
14
- type CurrentRouterPluginCreator = (
15
- localProviders: readonly {
16
- name: string;
17
- factory: typeof tanstackRouterProviderFactory;
18
- }[],
19
- ) => LegacyRouterPlugin;
8
+ const createProviderPlugin = createRouterPlugin({
9
+ defaultProvider: { name: 'react-router', factory: nativeRouterPlugin },
10
+ registryHooks: routerProviderRegistryHooks,
11
+ localProviders: [
12
+ { name: 'tanstack', factory: tanstackRouterProviderFactory },
13
+ ],
14
+ });
15
+ const statePlugin = createRouterStatePlugin({
16
+ registryHooks: routerProviderRegistryHooks,
17
+ });
20
18
 
21
- function getRouterPlugin(): LegacyRouterPlugin {
22
- const createRouterPlugin = Reflect.get(runtimeRouter, 'createRouterPlugin') as
23
- | CurrentRouterPluginCreator
24
- | undefined;
25
-
26
- if (typeof createRouterPlugin === 'function') {
27
- return createRouterPlugin([
28
- { name: 'tanstack', factory: tanstackRouterProviderFactory },
29
- ]);
30
- }
31
-
32
- const legacyRouterPlugin = Reflect.get(runtimeRouter, 'routerPlugin');
33
-
34
- if (typeof legacyRouterPlugin === 'function') {
35
- return tanstackRouterProviderFactory as unknown as LegacyRouterPlugin;
36
- }
37
-
38
- throw new Error(
39
- '[@modern-js/plugin-tanstack] The installed @modern-js/runtime/router/internal exports neither createRouterPlugin nor routerPlugin. Install a compatible @modern-js/runtime version.',
40
- );
41
- }
42
-
43
- export const routerPlugin: LegacyRouterPlugin = getRouterPlugin();
19
+ export const routerPlugin: typeof nativeRouterPlugin = (userConfig = {}) => {
20
+ const providerPlugin = createProviderPlugin(userConfig);
21
+ return {
22
+ ...providerPlugin,
23
+ setup(api) {
24
+ statePlugin.setup(api);
25
+ providerPlugin.setup(api);
26
+ },
27
+ };
28
+ };
@@ -6,23 +6,6 @@ import type {
6
6
  TanstackRouterWithServerSsr,
7
7
  } from './ssrTypes';
8
8
 
9
- export async function waitForRouterSerialization(
10
- tanstackRouter: TanstackRouterWithServerSsr,
11
- ) {
12
- const serverSsr = tanstackRouter.serverSsr;
13
- if (
14
- !serverSsr ||
15
- typeof serverSsr.onSerializationFinished !== 'function' ||
16
- serverSsr.isSerializationFinished?.()
17
- ) {
18
- return;
19
- }
20
-
21
- await new Promise<void>(resolve => {
22
- serverSsr.onSerializationFinished?.(resolve);
23
- });
24
- }
25
-
26
9
  function htmlEscapeAttr(value: string) {
27
10
  return value.replace(/&/g, '&amp;').replace(/"/g, '&quot;');
28
11
  }
@@ -34,13 +34,6 @@ type RouterRouteWithOptions = {
34
34
 
35
35
  export type TanstackRouterWithServerSsr = AnyRouter & {
36
36
  routesById?: Record<string, RouterRouteWithOptions>;
37
- serverSsr?: {
38
- cleanup?: () => void;
39
- dehydrate?: () => Promise<void> | void;
40
- isSerializationFinished?: () => boolean;
41
- onSerializationFinished?: (listener: () => void) => void;
42
- takeBufferedScripts?: () => unknown;
43
- };
44
37
  state: AnyRouter['state'] & {
45
38
  matches?: unknown;
46
39
  };
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  getRouterRuntimeState,
3
3
  type InternalRouterRuntimeState,
4
- } from '@modern-js/runtime/context';
4
+ } from '@modern-js/runtime-extensions/router-state';
5
5
  import type { AnyRouter } from '@tanstack/react-router';
6
6
 
7
7
  /**
@@ -2,11 +2,11 @@ import type { ModernRoute } from '@modern-js/runtime/context';
2
2
  import type { RouteObject } from '@modern-js/runtime-utils/router';
3
3
  import type React from 'react';
4
4
 
5
+ export type { ModernRoute } from '@modern-js/runtime/context';
5
6
  export type {
6
7
  InternalRouterServerSnapshot,
7
- ModernRoute,
8
8
  RouterFramework,
9
- } from '@modern-js/runtime/context';
9
+ } from '@modern-js/runtime-extensions/router-state';
10
10
 
11
11
  /** TanStack-specific router config. */
12
12
  export type RouterConfig = {
@@ -54,6 +54,8 @@ function toTanstackRouteObject(
54
54
  ): RouteObject | null {
55
55
  if (route.type === 'nested') {
56
56
  return {
57
+ modernLocalisedRoute: Reflect.get(route, 'modernLocalisedRoute'),
58
+ isRoot: Reflect.get(route, 'isRoot'),
57
59
  path: route.path,
58
60
  id: route.id,
59
61
  loader: route.loader,
@@ -91,9 +93,10 @@ function toTanstackRouteObject(
91
93
  globalApp,
92
94
  );
93
95
  return {
96
+ modernLocalisedRoute: Reflect.get(route, 'modernLocalisedRoute'),
94
97
  path: route.path,
95
98
  element: React.createElement(LayoutComponent),
96
- };
99
+ } as ModernRouteObject;
97
100
  }
98
101
 
99
102
  export function createTanstackRouteObjectsFromConfig({
@@ -0,0 +1,46 @@
1
+ import { toTanstackPath } from '../runtime/routeTree/paths';
2
+
3
+ type RouteShape = {
4
+ id?: string;
5
+ file?: string;
6
+ path?: string;
7
+ index?: boolean;
8
+ isRoot?: boolean;
9
+ children?: RouteShape[];
10
+ };
11
+
12
+ /** Browser-safe structure shared by the executable tree and concrete TS emitter. */
13
+ export type RouteDescriptor<T extends RouteShape> = {
14
+ source: T;
15
+ location: { id: string; path?: never } | { path: string; id?: never };
16
+ hasSplat: boolean;
17
+ children: RouteDescriptor<T>[];
18
+ };
19
+
20
+ export function describeRouteTree<T extends RouteShape>(routes: T[]) {
21
+ const root = routes.find(
22
+ route =>
23
+ route.isRoot === true || (route.path === '/' && route.index !== true),
24
+ );
25
+ const describe = (route: T, index: number): RouteDescriptor<T> => ({
26
+ source: route,
27
+ location:
28
+ (route.path ?? '').length === 0 && route.index !== true
29
+ ? { id: route.id || route.file || `pathless-${index}` }
30
+ : {
31
+ path: route.index === true ? '/' : toTanstackPath(route.path || ''),
32
+ },
33
+ hasSplat: Boolean(route.path?.includes('*')),
34
+ children: ((route.children ?? []) as T[]).map(describe),
35
+ });
36
+ return {
37
+ root,
38
+ children: (root !== undefined
39
+ ? [
40
+ ...((root.children ?? []) as T[]),
41
+ ...routes.filter(route => route !== root),
42
+ ]
43
+ : routes
44
+ ).map(describe),
45
+ };
46
+ }