@bleedingdev/modern-js-plugin-tanstack 3.5.0-ultramodern.3 → 3.5.0-ultramodern.31

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 (230) hide show
  1. package/dist/cjs/cli/artifacts.js +157 -0
  2. package/dist/cjs/cli/index.js +8 -79
  3. package/dist/cjs/cli/tanstackTypes/canonicalRoutes.js +113 -0
  4. package/dist/cjs/cli/tanstackTypes/shared.js +130 -0
  5. package/dist/cjs/cli/tanstackTypes/source.js +251 -0
  6. package/dist/cjs/cli/tanstackTypes.js +4 -350
  7. package/dist/cjs/runtime/blockingSubscribe.js +64 -0
  8. package/dist/cjs/runtime/{pluginCore.js → clientBasepath.js} +12 -19
  9. package/dist/cjs/runtime/clientHydration.js +129 -0
  10. package/dist/cjs/runtime/dataMutation.js +11 -176
  11. package/dist/cjs/runtime/formData.js +116 -0
  12. package/dist/cjs/runtime/loaderBridge.js +4 -1
  13. package/dist/cjs/runtime/plugin.js +21 -125
  14. package/dist/cjs/runtime/plugin.node.js +45 -125
  15. package/dist/cjs/runtime/pluginShared.js +90 -0
  16. package/dist/cjs/runtime/prefetchLink.js +15 -2
  17. package/dist/cjs/runtime/routeTree/components.js +102 -0
  18. package/dist/cjs/runtime/routeTree/construction.js +152 -0
  19. package/dist/cjs/runtime/routeTree/index.js +47 -0
  20. package/dist/cjs/runtime/routeTree/loaders.js +158 -0
  21. package/dist/cjs/runtime/routeTree/paths.js +57 -0
  22. package/dist/cjs/runtime/routeTree/staticData.js +63 -0
  23. package/dist/cjs/runtime/routeTree/tanstackRoutes.js +57 -0
  24. package/dist/cjs/runtime/routeTree/types.js +18 -0
  25. package/dist/cjs/runtime/routeTree.js +7 -320
  26. package/dist/cjs/runtime/rsc/CompositeComponent.js +18 -13
  27. package/dist/cjs/runtime/rsc/RscNodeRenderer.js +5 -16
  28. package/dist/cjs/runtime/rsc/client.js +8 -25
  29. package/dist/cjs/runtime/rsc/flightSerialization.js +132 -0
  30. package/dist/cjs/runtime/rsc/payloadErrors.js +99 -0
  31. package/dist/cjs/runtime/rsc/payloadFetch.js +117 -0
  32. package/dist/cjs/runtime/rsc/payloadRouter.js +21 -142
  33. package/dist/cjs/runtime/rsc/payloadRoutes.js +106 -0
  34. package/dist/cjs/runtime/rsc/payloadStorage.js +58 -0
  35. package/dist/cjs/runtime/rsc/server.js +14 -18
  36. package/dist/cjs/runtime/rsc/shared.js +71 -0
  37. package/dist/cjs/runtime/rsc/treeGetter.js +72 -0
  38. package/dist/cjs/runtime/ssrManagedTags.js +103 -0
  39. package/dist/cjs/runtime/ssrPreload.js +89 -0
  40. package/dist/cjs/runtime/ssrTypes.js +18 -0
  41. package/dist/cjs/runtime/submitAction.js +161 -0
  42. package/dist/cjs/runtime/utils.js +49 -86
  43. package/dist/esm/cli/artifacts.mjs +103 -0
  44. package/dist/esm/cli/index.mjs +4 -78
  45. package/dist/esm/cli/tanstackTypes/canonicalRoutes.mjs +75 -0
  46. package/dist/esm/cli/tanstackTypes/shared.mjs +68 -0
  47. package/dist/esm/cli/tanstackTypes/source.mjs +203 -0
  48. package/dist/esm/cli/tanstackTypes.mjs +2 -339
  49. package/dist/esm/runtime/blockingSubscribe.mjs +19 -0
  50. package/dist/esm/runtime/clientBasepath.mjs +10 -0
  51. package/dist/esm/runtime/clientHydration.mjs +70 -0
  52. package/dist/esm/runtime/dataMutation.mjs +2 -167
  53. package/dist/esm/runtime/formData.mjs +63 -0
  54. package/dist/esm/runtime/loaderBridge.mjs +4 -1
  55. package/dist/esm/runtime/plugin.mjs +14 -118
  56. package/dist/esm/runtime/plugin.node.mjs +41 -121
  57. package/dist/esm/runtime/pluginShared.mjs +40 -0
  58. package/dist/esm/runtime/prefetchLink.mjs +18 -5
  59. package/dist/esm/runtime/routeTree/components.mjs +55 -0
  60. package/dist/esm/runtime/routeTree/construction.mjs +111 -0
  61. package/dist/esm/runtime/routeTree/index.mjs +2 -0
  62. package/dist/esm/runtime/routeTree/loaders.mjs +117 -0
  63. package/dist/esm/runtime/routeTree/paths.mjs +16 -0
  64. package/dist/esm/runtime/routeTree/staticData.mjs +22 -0
  65. package/dist/esm/runtime/routeTree/tanstackRoutes.mjs +13 -0
  66. package/dist/esm/runtime/routeTree/types.mjs +0 -0
  67. package/dist/esm/runtime/routeTree.mjs +1 -318
  68. package/dist/esm/runtime/rsc/CompositeComponent.mjs +18 -13
  69. package/dist/esm/runtime/rsc/RscNodeRenderer.mjs +4 -15
  70. package/dist/esm/runtime/rsc/client.mjs +6 -26
  71. package/dist/esm/runtime/rsc/flightSerialization.mjs +88 -0
  72. package/dist/esm/runtime/rsc/payloadErrors.mjs +49 -0
  73. package/dist/esm/runtime/rsc/payloadFetch.mjs +61 -0
  74. package/dist/esm/runtime/rsc/payloadRouter.mjs +12 -133
  75. package/dist/esm/runtime/rsc/payloadRoutes.mjs +50 -0
  76. package/dist/esm/runtime/rsc/payloadStorage.mjs +14 -0
  77. package/dist/esm/runtime/rsc/server.mjs +17 -21
  78. package/dist/esm/runtime/rsc/shared.mjs +21 -0
  79. package/dist/esm/runtime/rsc/treeGetter.mjs +34 -0
  80. package/dist/esm/runtime/ssrManagedTags.mjs +53 -0
  81. package/dist/esm/runtime/ssrPreload.mjs +51 -0
  82. package/dist/esm/runtime/ssrTypes.mjs +0 -0
  83. package/dist/esm/runtime/submitAction.mjs +120 -0
  84. package/dist/esm/runtime/utils.mjs +47 -75
  85. package/dist/esm-node/cli/artifacts.mjs +104 -0
  86. package/dist/esm-node/cli/index.mjs +4 -78
  87. package/dist/esm-node/cli/tanstackTypes/canonicalRoutes.mjs +76 -0
  88. package/dist/esm-node/cli/tanstackTypes/shared.mjs +69 -0
  89. package/dist/esm-node/cli/tanstackTypes/source.mjs +204 -0
  90. package/dist/esm-node/cli/tanstackTypes.mjs +2 -339
  91. package/dist/esm-node/runtime/blockingSubscribe.mjs +20 -0
  92. package/dist/esm-node/runtime/clientBasepath.mjs +11 -0
  93. package/dist/esm-node/runtime/clientHydration.mjs +71 -0
  94. package/dist/esm-node/runtime/dataMutation.mjs +2 -167
  95. package/dist/esm-node/runtime/formData.mjs +64 -0
  96. package/dist/esm-node/runtime/loaderBridge.mjs +4 -1
  97. package/dist/esm-node/runtime/plugin.mjs +14 -118
  98. package/dist/esm-node/runtime/plugin.node.mjs +41 -121
  99. package/dist/esm-node/runtime/pluginShared.mjs +41 -0
  100. package/dist/esm-node/runtime/prefetchLink.mjs +18 -5
  101. package/dist/esm-node/runtime/routeTree/components.mjs +56 -0
  102. package/dist/esm-node/runtime/routeTree/construction.mjs +112 -0
  103. package/dist/esm-node/runtime/routeTree/index.mjs +3 -0
  104. package/dist/esm-node/runtime/routeTree/loaders.mjs +118 -0
  105. package/dist/esm-node/runtime/routeTree/paths.mjs +17 -0
  106. package/dist/esm-node/runtime/routeTree/staticData.mjs +23 -0
  107. package/dist/esm-node/runtime/routeTree/tanstackRoutes.mjs +14 -0
  108. package/dist/esm-node/runtime/routeTree/types.mjs +1 -0
  109. package/dist/esm-node/runtime/routeTree.mjs +1 -318
  110. package/dist/esm-node/runtime/rsc/CompositeComponent.mjs +18 -13
  111. package/dist/esm-node/runtime/rsc/RscNodeRenderer.mjs +4 -15
  112. package/dist/esm-node/runtime/rsc/client.mjs +6 -26
  113. package/dist/esm-node/runtime/rsc/flightSerialization.mjs +89 -0
  114. package/dist/esm-node/runtime/rsc/payloadErrors.mjs +50 -0
  115. package/dist/esm-node/runtime/rsc/payloadFetch.mjs +62 -0
  116. package/dist/esm-node/runtime/rsc/payloadRouter.mjs +12 -133
  117. package/dist/esm-node/runtime/rsc/payloadRoutes.mjs +51 -0
  118. package/dist/esm-node/runtime/rsc/payloadStorage.mjs +15 -0
  119. package/dist/esm-node/runtime/rsc/server.mjs +17 -21
  120. package/dist/esm-node/runtime/rsc/shared.mjs +22 -0
  121. package/dist/esm-node/runtime/rsc/treeGetter.mjs +35 -0
  122. package/dist/esm-node/runtime/ssrManagedTags.mjs +54 -0
  123. package/dist/esm-node/runtime/ssrPreload.mjs +52 -0
  124. package/dist/esm-node/runtime/ssrTypes.mjs +1 -0
  125. package/dist/esm-node/runtime/submitAction.mjs +121 -0
  126. package/dist/esm-node/runtime/utils.mjs +47 -75
  127. package/dist/types/cli/artifacts.d.ts +33 -0
  128. package/dist/types/cli/index.d.ts +2 -22
  129. package/dist/types/cli/tanstackTypes/canonicalRoutes.d.ts +22 -0
  130. package/dist/types/cli/tanstackTypes/shared.d.ts +20 -0
  131. package/dist/types/cli/tanstackTypes/source.d.ts +10 -0
  132. package/dist/types/cli/tanstackTypes.d.ts +3 -31
  133. package/dist/types/runtime/blockingSubscribe.d.ts +3 -0
  134. package/dist/types/runtime/clientBasepath.d.ts +1 -0
  135. package/dist/types/runtime/clientHydration.d.ts +19 -0
  136. package/dist/types/runtime/dataMutation.d.ts +3 -12
  137. package/dist/types/runtime/formData.d.ts +23 -0
  138. package/dist/types/runtime/plugin.d.ts +1 -1
  139. package/dist/types/runtime/plugin.node.d.ts +1 -1
  140. package/dist/types/runtime/pluginShared.d.ts +39 -0
  141. package/dist/types/runtime/routeTree/components.d.ts +7 -0
  142. package/dist/types/runtime/routeTree/construction.d.ts +5 -0
  143. package/dist/types/runtime/routeTree/index.d.ts +2 -0
  144. package/dist/types/runtime/routeTree/loaders.d.ts +4 -0
  145. package/dist/types/runtime/routeTree/paths.d.ts +3 -0
  146. package/dist/types/runtime/routeTree/staticData.d.ts +19 -0
  147. package/dist/types/runtime/routeTree/tanstackRoutes.d.ts +4 -0
  148. package/dist/types/runtime/routeTree/types.d.ts +75 -0
  149. package/dist/types/runtime/routeTree.d.ts +1 -9
  150. package/dist/types/runtime/rsc/CompositeComponent.d.ts +1 -2
  151. package/dist/types/runtime/rsc/RscNodeRenderer.d.ts +2 -3
  152. package/dist/types/runtime/rsc/client.d.ts +2 -0
  153. package/dist/types/runtime/rsc/flightSerialization.d.ts +3 -0
  154. package/dist/types/runtime/rsc/payloadErrors.d.ts +6 -0
  155. package/dist/types/runtime/rsc/payloadFetch.d.ts +10 -0
  156. package/dist/types/runtime/rsc/payloadRouter.d.ts +2 -10
  157. package/dist/types/runtime/rsc/payloadRoutes.d.ts +43 -0
  158. package/dist/types/runtime/rsc/payloadStorage.d.ts +5 -0
  159. package/dist/types/runtime/rsc/shared.d.ts +19 -0
  160. package/dist/types/runtime/rsc/treeGetter.d.ts +1 -0
  161. package/dist/types/runtime/ssrManagedTags.d.ts +7 -0
  162. package/dist/types/runtime/ssrPreload.d.ts +2 -0
  163. package/dist/types/runtime/ssrTypes.d.ts +43 -0
  164. package/dist/types/runtime/state.d.ts +1 -1
  165. package/dist/types/runtime/submitAction.d.ts +14 -0
  166. package/dist/types/runtime/types.d.ts +4 -9
  167. package/dist/types/runtime/utils.d.ts +4 -24
  168. package/package.json +30 -8
  169. package/rstest.config.mts +3 -0
  170. package/src/cli/artifacts.ts +255 -0
  171. package/src/cli/index.ts +7 -185
  172. package/src/cli/tanstackTypes/canonicalRoutes.ts +157 -0
  173. package/src/cli/tanstackTypes/shared.ts +119 -0
  174. package/src/cli/tanstackTypes/source.ts +405 -0
  175. package/src/cli/tanstackTypes.ts +4 -647
  176. package/src/runtime/blockingSubscribe.ts +48 -0
  177. package/src/runtime/clientBasepath.ts +12 -0
  178. package/src/runtime/clientHydration.ts +151 -0
  179. package/src/runtime/dataMutation.tsx +9 -306
  180. package/src/runtime/formData.ts +153 -0
  181. package/src/runtime/loaderBridge.ts +8 -3
  182. package/src/runtime/plugin.node.tsx +76 -291
  183. package/src/runtime/plugin.tsx +21 -257
  184. package/src/runtime/pluginShared.ts +112 -0
  185. package/src/runtime/prefetchLink.tsx +78 -15
  186. package/src/runtime/routeTree/components.ts +130 -0
  187. package/src/runtime/routeTree/construction.ts +238 -0
  188. package/src/runtime/routeTree/index.ts +7 -0
  189. package/src/runtime/routeTree/loaders.ts +232 -0
  190. package/src/runtime/routeTree/paths.ts +36 -0
  191. package/src/runtime/routeTree/staticData.ts +66 -0
  192. package/src/runtime/routeTree/tanstackRoutes.ts +36 -0
  193. package/src/runtime/routeTree/types.ts +87 -0
  194. package/src/runtime/routeTree.ts +5 -750
  195. package/src/runtime/rsc/CompositeComponent.tsx +29 -20
  196. package/src/runtime/rsc/RscNodeRenderer.tsx +9 -27
  197. package/src/runtime/rsc/client.tsx +12 -40
  198. package/src/runtime/rsc/flightSerialization.ts +158 -0
  199. package/src/runtime/rsc/payloadErrors.ts +73 -0
  200. package/src/runtime/rsc/payloadFetch.ts +97 -0
  201. package/src/runtime/rsc/payloadRouter.ts +29 -234
  202. package/src/runtime/rsc/payloadRoutes.ts +122 -0
  203. package/src/runtime/rsc/payloadStorage.ts +35 -0
  204. package/src/runtime/rsc/server.tsx +20 -29
  205. package/src/runtime/rsc/shared.ts +70 -0
  206. package/src/runtime/rsc/treeGetter.tsx +52 -0
  207. package/src/runtime/ssrManagedTags.ts +107 -0
  208. package/src/runtime/ssrPreload.ts +125 -0
  209. package/src/runtime/ssrTypes.ts +51 -0
  210. package/src/runtime/state.ts +4 -2
  211. package/src/runtime/submitAction.ts +194 -0
  212. package/src/runtime/types.ts +6 -13
  213. package/src/runtime/utils.tsx +76 -110
  214. package/src/types/react-server-dom-rspack.d.ts +56 -0
  215. package/tests/castRatchet.test.ts +85 -0
  216. package/tests/router/cli.test.ts +7 -0
  217. package/tests/router/dataMutation.test.tsx +59 -0
  218. package/tests/router/fixtures/router-gen.golden.txt +72 -0
  219. package/tests/router/generateRouteArtifacts.test.ts +106 -0
  220. package/tests/router/loaderBridge.test.ts +21 -0
  221. package/tests/router/prefetchLink.test.tsx +106 -59
  222. package/tests/router/prefetchLinkPreload.test.tsx +85 -0
  223. package/tests/router/routeTree.test.ts +46 -11
  224. package/tests/router/rsc.test.tsx +189 -0
  225. package/tests/router/ssrPreload.test.ts +30 -0
  226. package/tests/router/tanstackTypes.test.ts +254 -1
  227. package/dist/esm/runtime/pluginCore.mjs +0 -14
  228. package/dist/esm-node/runtime/pluginCore.mjs +0 -15
  229. package/dist/types/runtime/pluginCore.d.ts +0 -21
  230. package/src/runtime/pluginCore.ts +0 -48
@@ -20,19 +20,28 @@ import {
20
20
  } from '@tanstack/react-router';
21
21
  import { useContext, useMemo } from 'react';
22
22
  import { createModernBasepathRewrite } from './basepathRewrite';
23
+ import { wrapRouterSubscribeWithBlockState } from './blockingSubscribe';
24
+ import { isSegmentPrefix } from './clientBasepath';
25
+ import {
26
+ getTanstackSsrHydrationPromise,
27
+ hasTanstackSsrHydrationRecord,
28
+ ModernRouterClient,
29
+ type WindowWithTanstackSsr,
30
+ } from './clientHydration';
23
31
  import { routerProviderRegistryHooks } from './hooks';
24
32
  import { wrapTanstackSsrHydrationBoundary } from './hydrationBoundary';
25
33
  import {
26
34
  applyRouterRuntimeState,
27
35
  type RouterLifecycleContext,
28
36
  } from './lifecycle';
29
- import { withModernRouteMatchContext } from './outlet';
30
37
  import {
31
- getFinalRouteConfig,
38
+ createTanstackRouteObjects,
32
39
  getMergedRouterConfig,
40
+ getTanstackRouteConfig,
41
+ joinBasename,
33
42
  type TanstackRouterPluginAPI,
34
43
  type TanstackRouterRuntimePlugin,
35
- } from './pluginCore';
44
+ } from './pluginShared';
36
45
  import { Link } from './prefetchLink';
37
46
  import { useMatches } from './routeHooks';
38
47
  import { createRouteTreeFromRouteObjects } from './routeTree';
@@ -41,245 +50,6 @@ import {
41
50
  getModernTanstackRouterFastDefaults,
42
51
  type RouterConfig,
43
52
  } from './types';
44
- import { createRouteObjectsFromConfig, urlJoin } from './utils';
45
-
46
- const BLOCKING_SUBSCRIBE_SYMBOL = Symbol.for(
47
- '@modern-js/plugin-tanstack:blocking-subscribe',
48
- );
49
- const BLOCKING_STATE_SYMBOL = Symbol.for(
50
- '@modern-js/plugin-tanstack:blocking-state',
51
- );
52
- type TanstackRouterWithSubscribe = {
53
- [BLOCKING_STATE_SYMBOL]?: () => boolean;
54
- [BLOCKING_SUBSCRIBE_SYMBOL]?: boolean;
55
- subscribe?: (
56
- eventType: string,
57
- listener: (...args: unknown[]) => void,
58
- ) => () => void;
59
- };
60
-
61
- type WindowWithTanstackSsr = Window & {
62
- $_TSR?: unknown;
63
- };
64
-
65
- type RouteComponentPreloadable = {
66
- load?: () => Promise<unknown>;
67
- preload?: () => Promise<unknown>;
68
- };
69
-
70
- type ModernRouteModule = {
71
- Component?: unknown;
72
- default?: unknown;
73
- };
74
-
75
- type RouterWithPreloadableRoutes = AnyRouter & {
76
- routesById?: Record<
77
- string,
78
- {
79
- options?: {
80
- component?: unknown;
81
- staticData?: {
82
- modernRouteId?: string;
83
- };
84
- };
85
- }
86
- >;
87
- };
88
-
89
- function normalizeBase(b: string) {
90
- if (b.length > 1 && b.endsWith('/')) {
91
- return b.slice(0, -1);
92
- }
93
- return b || '/';
94
- }
95
-
96
- function isSegmentPrefix(pathname: string, base: string) {
97
- const b = normalizeBase(base);
98
- const p = pathname || '/';
99
- return p === b || p.startsWith(`${b}/`);
100
- }
101
-
102
- function wrapRouterSubscribeWithBlockState(
103
- router: unknown,
104
- getBlockNavState?: () => boolean,
105
- ) {
106
- if (!router || typeof router !== 'object') {
107
- return;
108
- }
109
-
110
- const target = router as TanstackRouterWithSubscribe;
111
- target[BLOCKING_STATE_SYMBOL] = getBlockNavState;
112
- if (
113
- target[BLOCKING_SUBSCRIBE_SYMBOL] ||
114
- typeof target.subscribe !== 'function'
115
- ) {
116
- return;
117
- }
118
-
119
- const originSubscribe = target.subscribe.bind(target);
120
- target.subscribe = (eventType, listener) => {
121
- const wrappedListener = (...args: unknown[]) => {
122
- const blockRoute = target[BLOCKING_STATE_SYMBOL]?.() || false;
123
- if (blockRoute) {
124
- return;
125
- }
126
- return listener(...args);
127
- };
128
- return originSubscribe(eventType, wrappedListener);
129
- };
130
- target[BLOCKING_SUBSCRIBE_SYMBOL] = true;
131
- }
132
-
133
- type RouterHydrationRecord = {
134
- error?: unknown;
135
- promise: Promise<unknown>;
136
- status: 'pending' | 'fulfilled' | 'rejected';
137
- };
138
-
139
- const routerHydrationRecords = new WeakMap<AnyRouter, RouterHydrationRecord>();
140
- const routeModulesKey = '_routeModules';
141
-
142
- function pickRouteModuleComponent(
143
- routeModule: unknown,
144
- seen: Set<unknown> = new Set(),
145
- ): unknown {
146
- if (
147
- typeof routeModule === 'function' ||
148
- (routeModule &&
149
- typeof routeModule === 'object' &&
150
- '$$typeof' in routeModule)
151
- ) {
152
- return routeModule;
153
- }
154
-
155
- if (!routeModule || typeof routeModule !== 'object') {
156
- return undefined;
157
- }
158
- if (seen.has(routeModule)) {
159
- return undefined;
160
- }
161
- seen.add(routeModule);
162
-
163
- const module = routeModule as ModernRouteModule;
164
- for (const candidate of [module.default, module.Component]) {
165
- const component = pickRouteModuleComponent(candidate, seen);
166
- if (component) {
167
- return component;
168
- }
169
- }
170
-
171
- return undefined;
172
- }
173
-
174
- function getCachedRouteModule(routeId: string) {
175
- if (typeof window === 'undefined') {
176
- return undefined;
177
- }
178
-
179
- return (window as unknown as Record<string, Record<string, unknown>>)[
180
- routeModulesKey
181
- ]?.[routeId];
182
- }
183
-
184
- function preloadHydratedRouteComponents(router: AnyRouter): Promise<void> {
185
- const preloadableRouter = router as RouterWithPreloadableRoutes;
186
- const routesById = preloadableRouter.routesById || {};
187
- const matches = preloadableRouter.stores.matches.get() as Array<{
188
- routeId?: string;
189
- }>;
190
-
191
- return Promise.all(
192
- matches.map(match => {
193
- if (match.routeId === undefined || match.routeId === '') {
194
- return undefined;
195
- }
196
-
197
- const route = routesById[match.routeId];
198
- const component = route?.options?.component as RouteComponentPreloadable;
199
- const preload = component?.load || component?.preload;
200
- if (typeof preload !== 'function') {
201
- return undefined;
202
- }
203
-
204
- return Promise.resolve(preload.call(component)).then(routeModule => {
205
- const modernRouteId = route?.options?.staticData?.modernRouteId;
206
- const cachedRouteModule =
207
- typeof modernRouteId === 'string' && modernRouteId !== ''
208
- ? getCachedRouteModule(modernRouteId)
209
- : undefined;
210
- const resolvedComponent = pickRouteModuleComponent(
211
- cachedRouteModule ?? routeModule,
212
- );
213
- if (
214
- resolvedComponent !== undefined &&
215
- typeof modernRouteId === 'string' &&
216
- modernRouteId !== ''
217
- ) {
218
- route.options.component = withModernRouteMatchContext(
219
- resolvedComponent,
220
- modernRouteId,
221
- );
222
- }
223
- });
224
- }),
225
- ).then(() => undefined);
226
- }
227
-
228
- function hydrateTanstackRouter(router: AnyRouter) {
229
- return import('@tanstack/react-router/ssr/client').then(({ hydrate }) =>
230
- hydrate(router),
231
- );
232
- }
233
-
234
- function getTanstackSsrHydrationRecord(router: AnyRouter) {
235
- const existingHydrationRecord = routerHydrationRecords.get(router);
236
- if (existingHydrationRecord !== undefined) {
237
- return existingHydrationRecord;
238
- }
239
-
240
- const hydrationRecord: RouterHydrationRecord = {
241
- promise: Promise.resolve(),
242
- status: 'pending',
243
- };
244
- routerHydrationRecords.set(router, hydrationRecord);
245
- try {
246
- hydrationRecord.promise = hydrateTanstackRouter(router)
247
- .then(value => preloadHydratedRouteComponents(router).then(() => value))
248
- .then(
249
- value => {
250
- hydrationRecord.status = 'fulfilled';
251
- return value;
252
- },
253
- error => {
254
- hydrationRecord.status = 'rejected';
255
- hydrationRecord.error = error;
256
- throw error;
257
- },
258
- );
259
- } catch (error) {
260
- hydrationRecord.status = 'rejected';
261
- hydrationRecord.error = error;
262
- hydrationRecord.promise = Promise.reject(error);
263
- hydrationRecord.promise.catch(() => {});
264
- }
265
- return hydrationRecord;
266
- }
267
-
268
- function getTanstackSsrHydrationPromise(router: AnyRouter) {
269
- return getTanstackSsrHydrationRecord(router).promise;
270
- }
271
-
272
- function hasTanstackSsrHydrationRecord(router: AnyRouter) {
273
- return routerHydrationRecords.has(router);
274
- }
275
-
276
- function ModernRouterClient({ router }: { router: AnyRouter }) {
277
- const hydrationRecord = getTanstackSsrHydrationRecord(router);
278
- if (hydrationRecord.status === 'rejected') {
279
- throw hydrationRecord.error;
280
- }
281
- return <RouterProvider router={router} />;
282
- }
283
53
 
284
54
  export const tanstackRouterPlugin = (
285
55
  userConfig: Partial<RouterConfig> = {},
@@ -303,19 +73,10 @@ export const tanstackRouterPlugin = (
303
73
  return cachedRouteObjects;
304
74
  }
305
75
 
306
- const mergedConfig = getMergedConfig();
307
- const { createRoutes } = mergedConfig;
308
- const finalRouteConfig = getFinalRouteConfig(mergedConfig);
309
-
310
- const routeObjects = createRoutes
311
- ? createRoutes()
312
- : createRouteObjectsFromConfig({
313
- routesConfig: finalRouteConfig,
314
- }) || [];
315
-
316
- cachedRouteObjects = hooks.modifyRoutes.call(
317
- routeObjects,
318
- ) as RouteObject[];
76
+ cachedRouteObjects = createTanstackRouteObjects({
77
+ hooks,
78
+ routeConfig: getTanstackRouteConfig(api, userConfig),
79
+ });
319
80
  return cachedRouteObjects;
320
81
  };
321
82
 
@@ -347,7 +108,7 @@ export const tanstackRouterPlugin = (
347
108
  const { basename = '' } = getMergedConfig();
348
109
  const baseUrl = selectBasePath(location.pathname).replace(/^\/*/, '/');
349
110
  return baseUrl === '/'
350
- ? urlJoin(
111
+ ? joinBasename(
351
112
  baseUrl,
352
113
  runtimeContext._internalRouterBaseName || basename || '',
353
114
  )
@@ -394,9 +155,12 @@ export const tanstackRouterPlugin = (
394
155
  const serializationAdapters = getGlobalEnableRsc()
395
156
  ? getTanstackRscSerializationAdapters()
396
157
  : undefined;
158
+ const routerFastDefaults =
159
+ getModernTanstackRouterFastDefaults(mergedConfig);
397
160
 
398
161
  cachedRouter = createRouter({
399
- ...getModernTanstackRouterFastDefaults(mergedConfig),
162
+ ...routerFastDefaults,
163
+ ...(serializationAdapters ? { defaultStructuralSharing: false } : {}),
400
164
  routeTree,
401
165
  basepath: '/',
402
166
  rewrite,
@@ -0,0 +1,112 @@
1
+ import {
2
+ getGlobalLayoutApp,
3
+ getGlobalRoutes,
4
+ type TInternalRuntimeContext,
5
+ } from '@modern-js/runtime/context';
6
+ import { merge } from '@modern-js/runtime-utils/merge';
7
+ import type { RouteObject } from '@modern-js/runtime-utils/router';
8
+ import type { RouterExtendsHooks } from './hooks';
9
+ import type { RouterConfig } from './types';
10
+ import { createTanstackRouteObjectsFromConfig, type SSRMode } from './utils';
11
+
12
+ export type TanstackRouterRuntimeConfig = {
13
+ plugins?: TanstackRouterRuntimePlugin[];
14
+ router?: Partial<RouterConfig>;
15
+ [key: string]: unknown;
16
+ };
17
+
18
+ type RuntimeInterrupt = (value?: unknown) => unknown;
19
+
20
+ export type TanstackRouterPluginAPI = {
21
+ getRuntimeConfig: () => TanstackRouterRuntimeConfig;
22
+ getHooks: () => RouterExtendsHooks;
23
+ onBeforeRender: (
24
+ listener: (
25
+ context: TInternalRuntimeContext,
26
+ interrupt: RuntimeInterrupt,
27
+ ) => unknown,
28
+ ) => void;
29
+ wrapRoot: (listener: (App: any) => any) => void;
30
+ [key: string]: any;
31
+ };
32
+
33
+ export type TanstackRouterRuntimePlugin = {
34
+ name?: string;
35
+ registryHooks?: RouterExtendsHooks;
36
+ setup?: (api: TanstackRouterPluginAPI) => unknown;
37
+ [key: string]: unknown;
38
+ };
39
+
40
+ export type TanstackRouteConfig = {
41
+ mergedConfig: RouterConfig;
42
+ finalRouteConfig: RouterConfig['routesConfig'];
43
+ hasConfiguredRoutes: boolean;
44
+ };
45
+
46
+ export function getMergedRouterConfig(
47
+ api: TanstackRouterPluginAPI,
48
+ userConfig: Partial<RouterConfig>,
49
+ ): RouterConfig {
50
+ const pluginConfig = api.getRuntimeConfig() as {
51
+ router?: Partial<RouterConfig>;
52
+ };
53
+ return merge(pluginConfig.router || {}, userConfig) as RouterConfig;
54
+ }
55
+
56
+ export function getFinalRouteConfig(
57
+ mergedConfig: RouterConfig,
58
+ ): RouterConfig['routesConfig'] {
59
+ return {
60
+ routes: getGlobalRoutes() as RouterConfig['routesConfig']['routes'],
61
+ globalApp: getGlobalLayoutApp(),
62
+ ...mergedConfig.routesConfig,
63
+ };
64
+ }
65
+
66
+ export function getTanstackRouteConfig(
67
+ api: TanstackRouterPluginAPI,
68
+ userConfig: Partial<RouterConfig>,
69
+ ): TanstackRouteConfig {
70
+ const mergedConfig = getMergedRouterConfig(api, userConfig);
71
+ const finalRouteConfig = getFinalRouteConfig(mergedConfig);
72
+ return {
73
+ mergedConfig,
74
+ finalRouteConfig,
75
+ hasConfiguredRoutes:
76
+ Boolean(finalRouteConfig.routes) || Boolean(mergedConfig.createRoutes),
77
+ };
78
+ }
79
+
80
+ export function createTanstackRouteObjects({
81
+ hooks,
82
+ routeConfig,
83
+ ssrMode,
84
+ }: {
85
+ hooks: RouterExtendsHooks;
86
+ routeConfig: TanstackRouteConfig;
87
+ ssrMode?: SSRMode;
88
+ }): RouteObject[] {
89
+ const { mergedConfig, finalRouteConfig } = routeConfig;
90
+ const routeObjects = mergedConfig.createRoutes
91
+ ? mergedConfig.createRoutes()
92
+ : createTanstackRouteObjectsFromConfig({
93
+ routesConfig: finalRouteConfig,
94
+ ssrMode,
95
+ }) || [];
96
+
97
+ return hooks.modifyRoutes.call(routeObjects) as RouteObject[];
98
+ }
99
+
100
+ export function joinBasename(baseUrl: string, basename: string): string {
101
+ const base = baseUrl || '/';
102
+ const normalizedBase = base.startsWith('/') ? base : `/${base}`;
103
+ const trimmedBase =
104
+ normalizedBase.length > 1
105
+ ? normalizedBase.replace(/\/+$/g, '')
106
+ : normalizedBase;
107
+ const trimmedBasename = basename.replace(/^\/+|\/+$/g, '');
108
+ if (!trimmedBasename) {
109
+ return trimmedBase || '/';
110
+ }
111
+ return `${trimmedBase}/${trimmedBasename}`.replace(/\/{2,}/g, '/');
112
+ }
@@ -2,9 +2,9 @@ import {
2
2
  type AnyRouter,
3
3
  type LinkComponentProps,
4
4
  type RegisteredRouter,
5
- Link as TanStackLink,
5
+ useLinkProps,
6
6
  } from '@tanstack/react-router';
7
- import type { ReactElement } from 'react';
7
+ import type { AnchorHTMLAttributes, ReactElement, ReactNode, Ref } from 'react';
8
8
 
9
9
  export type PrefetchBehavior = 'intent' | 'render' | 'viewport' | 'none';
10
10
 
@@ -65,21 +65,84 @@ type LinkComponentImplProps = LinkProps<
65
65
  string | undefined,
66
66
  string,
67
67
  string
68
- >;
68
+ > & { ref?: Ref<HTMLAnchorElement> };
69
69
 
70
70
  const LinkComponentImpl = (props: LinkComponentImplProps) => {
71
- const { prefetch, preload, ...rest } = props;
72
- return (
73
- <TanStackLink
74
- {...rest}
75
- preload={
76
- resolvePreloadFromPrefetch(
77
- prefetch,
78
- preload,
79
- ) as LinkComponentImplProps['preload']
80
- }
81
- />
82
- );
71
+ const {
72
+ prefetch,
73
+ preload,
74
+ 'aria-current': ariaCurrentProp,
75
+ ref,
76
+ children,
77
+ ...rest
78
+ } = props as LinkComponentImplProps & {
79
+ children?:
80
+ | ReactNode
81
+ | ((state: { isActive: boolean; isTransitioning: boolean }) => ReactNode);
82
+ };
83
+ const hasAriaCurrentOverride = 'aria-current' in props;
84
+
85
+ // Children are rendered by us, not by useLinkProps() — the hook filters
86
+ // unknown/function props out of its return value, so anything routed
87
+ // through it would be silently dropped.
88
+ const linkOptions = {
89
+ ...rest,
90
+ preload: resolvePreloadFromPrefetch(
91
+ prefetch,
92
+ preload,
93
+ ) as LinkComponentImplProps['preload'],
94
+ } as LinkComponentImplProps;
95
+
96
+ // TanStack Router's useLinkProps() unconditionally spreads
97
+ // STATIC_ACTIVE_PROPS = { "data-status": "active", "aria-current": "page" }
98
+ // as the LAST entry of the props object it returns for an active link
99
+ // (@tanstack/react-router dist/esm/link.js:369 `...isActive &&
100
+ // STATIC_ACTIVE_PROPS`, defined at line 379-382, spread after
101
+ // `...propsSafeToSpread`, `...resolvedActiveProps`, and
102
+ // `...resolvedInactiveProps`). That means any caller-supplied aria-current
103
+ // -- whether passed directly or via activeProps -- is always clobbered by
104
+ // the time <Link>/createLink render the anchor; there is no props-merge
105
+ // order that lets a caller win from inside <Link>. So instead of
106
+ // delegating rendering to <Link>, we call useLinkProps() ourselves and
107
+ // re-apply the caller's original aria-current (including `false`, meaning
108
+ // "suppress the attribute entirely") as the final word on the anchor we
109
+ // render.
110
+ const linkProps = useLinkProps(
111
+ linkOptions as Parameters<typeof useLinkProps>[0],
112
+ ref as Parameters<typeof useLinkProps>[1],
113
+ ) as AnchorHTMLAttributes<HTMLAnchorElement> & {
114
+ disabled?: boolean;
115
+ type?: string;
116
+ };
117
+
118
+ // TanStack's own Link strips `type` and `disabled` before rendering the
119
+ // anchor (link.js:449-452) — mirror that.
120
+ const { disabled: _disabled, type: _type, ...anchorProps } = linkProps;
121
+
122
+ if (hasAriaCurrentOverride) {
123
+ if (ariaCurrentProp === false || typeof ariaCurrentProp === 'undefined') {
124
+ delete anchorProps['aria-current'];
125
+ } else {
126
+ anchorProps['aria-current'] = ariaCurrentProp;
127
+ }
128
+ }
129
+
130
+ // TanStack's Link resolves render-prop children with the active state
131
+ // (link.js:450) — without this, function children reach React as an
132
+ // invalid child and crash the render.
133
+ const resolvedChildren =
134
+ typeof children === 'function'
135
+ ? children({
136
+ isActive:
137
+ (anchorProps as Record<string, unknown>)['data-status'] ===
138
+ 'active',
139
+ isTransitioning:
140
+ (anchorProps as Record<string, unknown>)['data-transitioning'] ===
141
+ 'transitioning',
142
+ })
143
+ : children;
144
+
145
+ return <a {...anchorProps}>{resolvedChildren}</a>;
83
146
  };
84
147
 
85
148
  export const Link = LinkComponentImpl as LinkComponent;
@@ -0,0 +1,130 @@
1
+ // @effect-diagnostics asyncFunction:off strictBooleanExpressions:off
2
+
3
+ import type { RouteObject } from '@modern-js/runtime-utils/router';
4
+ import { createElement, type ElementType } from 'react';
5
+
6
+ import type { ModernRouteObject, RouteTreeOptions } from './types';
7
+
8
+ type ModernRouteModule = {
9
+ Component?: unknown;
10
+ default?: unknown;
11
+ };
12
+ type PreloadableComponent = {
13
+ (props: Record<string, unknown>): ReturnType<typeof createElement>;
14
+ load?: () => Promise<unknown>;
15
+ preload?: () => Promise<unknown>;
16
+ };
17
+
18
+ export function pickRouteModuleComponent(
19
+ routeModule: unknown,
20
+ seen: Set<unknown> = new Set(),
21
+ ): ElementType<Record<string, unknown>> | undefined {
22
+ if (
23
+ typeof routeModule === 'function' ||
24
+ (routeModule &&
25
+ typeof routeModule === 'object' &&
26
+ '$$typeof' in routeModule)
27
+ ) {
28
+ return routeModule as ElementType<Record<string, unknown>>;
29
+ }
30
+
31
+ if (!routeModule || typeof routeModule !== 'object') {
32
+ return undefined;
33
+ }
34
+ if (seen.has(routeModule)) {
35
+ return undefined;
36
+ }
37
+ seen.add(routeModule);
38
+
39
+ const module = routeModule as ModernRouteModule;
40
+ for (const candidate of [module.default, module.Component]) {
41
+ const component = pickRouteModuleComponent(candidate, seen);
42
+ if (component) {
43
+ return component;
44
+ }
45
+ }
46
+
47
+ return undefined;
48
+ }
49
+
50
+ function createServerLazyImportComponent(
51
+ lazyImport: () => unknown,
52
+ fallbackComponent?: unknown,
53
+ ): PreloadableComponent | unknown {
54
+ if (typeof document !== 'undefined') {
55
+ return fallbackComponent;
56
+ }
57
+
58
+ let resolvedComponent: ElementType<Record<string, unknown>> | undefined;
59
+ let pendingLoad: Promise<unknown> | undefined;
60
+
61
+ const load = async () => {
62
+ if (resolvedComponent) {
63
+ return resolvedComponent;
64
+ }
65
+
66
+ const routeModule = await lazyImport();
67
+ const component = pickRouteModuleComponent(routeModule);
68
+ if (component) {
69
+ resolvedComponent = component;
70
+ }
71
+ return resolvedComponent;
72
+ };
73
+
74
+ const Component: PreloadableComponent = props => {
75
+ if (resolvedComponent) {
76
+ return createElement(resolvedComponent, props);
77
+ }
78
+
79
+ pendingLoad ||= load();
80
+ throw pendingLoad;
81
+ };
82
+ Component.load = load;
83
+ Component.preload = load;
84
+
85
+ return Component;
86
+ }
87
+
88
+ export function toRouteComponent(
89
+ routeObject: RouteObject,
90
+ options: RouteTreeOptions = {},
91
+ ): unknown {
92
+ const route = routeObject as ModernRouteObject;
93
+ const lazyImport =
94
+ typeof route.lazyImport === 'function' ? route.lazyImport : undefined;
95
+ const routeComponent = route.Component || route.component;
96
+ const fallbackComponent = routeComponent
97
+ ? routeComponent
98
+ : route.element
99
+ ? () => route.element
100
+ : undefined;
101
+
102
+ if (lazyImport && fallbackComponent) {
103
+ return createServerLazyImportComponent(lazyImport, fallbackComponent);
104
+ }
105
+
106
+ if (routeComponent) {
107
+ return routeComponent;
108
+ }
109
+ const element = route.element;
110
+ if (element) {
111
+ return () => element;
112
+ }
113
+ return undefined;
114
+ }
115
+
116
+ export function toErrorComponent(routeObject: RouteObject): unknown {
117
+ const route = routeObject as ModernRouteObject;
118
+ if (route.ErrorBoundary) {
119
+ return route.ErrorBoundary;
120
+ }
121
+ if (route.errorElement) {
122
+ return () => route.errorElement;
123
+ }
124
+ return undefined;
125
+ }
126
+
127
+ export function toPendingComponent(routeObject: RouteObject): unknown {
128
+ const route = routeObject as ModernRouteObject;
129
+ return route.HydrateFallback || route.pendingComponent || undefined;
130
+ }