@bleedingdev/modern-js-plugin-tanstack 3.5.0-ultramodern.4 → 3.5.0-ultramodern.40

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 (260) 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 +123 -0
  5. package/dist/cjs/cli/tanstackTypes/source.js +256 -0
  6. package/dist/cjs/cli/tanstackTypes.js +4 -350
  7. package/dist/cjs/runtime/blockingSubscribe.js +57 -0
  8. package/dist/cjs/runtime/{pluginCore.js → clientBasepath.js} +12 -19
  9. package/dist/cjs/runtime/clientHydration.js +117 -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 +7 -11
  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 +87 -0
  16. package/dist/cjs/runtime/prefetchLink.js +15 -2
  17. package/dist/cjs/runtime/register.js +1 -47
  18. package/dist/cjs/runtime/routeTree/components.js +102 -0
  19. package/dist/cjs/runtime/routeTree/construction.js +152 -0
  20. package/dist/cjs/runtime/routeTree/index.js +47 -0
  21. package/dist/cjs/runtime/routeTree/loaders.js +158 -0
  22. package/dist/cjs/runtime/routeTree/paths.js +57 -0
  23. package/dist/cjs/runtime/routeTree/staticData.js +63 -0
  24. package/dist/cjs/runtime/routeTree/tanstackRoutes.js +57 -0
  25. package/dist/cjs/runtime/routeTree/types.js +18 -0
  26. package/dist/cjs/runtime/routeTree.js +7 -320
  27. package/dist/cjs/runtime/rsc/CompositeComponent.js +18 -13
  28. package/dist/cjs/runtime/rsc/ReplayableStream.js +0 -4
  29. package/dist/cjs/runtime/rsc/RscNodeRenderer.js +5 -16
  30. package/dist/cjs/runtime/rsc/client.js +8 -25
  31. package/dist/cjs/runtime/rsc/flightSerialization.js +132 -0
  32. package/dist/cjs/runtime/rsc/payloadErrors.js +99 -0
  33. package/dist/cjs/runtime/rsc/payloadFetch.js +105 -0
  34. package/dist/cjs/runtime/rsc/payloadRouter.js +21 -142
  35. package/dist/cjs/runtime/rsc/payloadRoutes.js +94 -0
  36. package/dist/cjs/runtime/rsc/payloadStorage.js +58 -0
  37. package/dist/cjs/runtime/rsc/server.js +14 -18
  38. package/dist/cjs/runtime/rsc/shared.js +68 -0
  39. package/dist/cjs/runtime/rsc/slotUsageSanitizer.js +1 -0
  40. package/dist/cjs/runtime/rsc/treeGetter.js +72 -0
  41. package/dist/cjs/runtime/shared/isAbsoluteUrl.js +47 -0
  42. package/dist/cjs/runtime/ssrManagedTags.js +103 -0
  43. package/dist/cjs/runtime/ssrPreload.js +89 -0
  44. package/dist/cjs/runtime/ssrTypes.js +18 -0
  45. package/dist/cjs/runtime/state.js +1 -1
  46. package/dist/cjs/runtime/submitAction.js +161 -0
  47. package/dist/cjs/runtime/utils.js +49 -86
  48. package/dist/esm/cli/artifacts.mjs +103 -0
  49. package/dist/esm/cli/index.mjs +8 -82
  50. package/dist/esm/cli/tanstackTypes/canonicalRoutes.mjs +75 -0
  51. package/dist/esm/cli/tanstackTypes/shared.mjs +61 -0
  52. package/dist/esm/cli/tanstackTypes/source.mjs +208 -0
  53. package/dist/esm/cli/tanstackTypes.mjs +2 -339
  54. package/dist/esm/runtime/blockingSubscribe.mjs +19 -0
  55. package/dist/esm/runtime/clientBasepath.mjs +10 -0
  56. package/dist/esm/runtime/clientHydration.mjs +70 -0
  57. package/dist/esm/runtime/dataMutation.mjs +2 -167
  58. package/dist/esm/runtime/formData.mjs +63 -0
  59. package/dist/esm/runtime/loaderBridge.mjs +5 -9
  60. package/dist/esm/runtime/plugin.mjs +14 -118
  61. package/dist/esm/runtime/plugin.node.mjs +41 -121
  62. package/dist/esm/runtime/pluginShared.mjs +40 -0
  63. package/dist/esm/runtime/prefetchLink.mjs +18 -5
  64. package/dist/esm/runtime/register.mjs +0 -14
  65. package/dist/esm/runtime/routeTree/components.mjs +55 -0
  66. package/dist/esm/runtime/routeTree/construction.mjs +111 -0
  67. package/dist/esm/runtime/routeTree/index.mjs +2 -0
  68. package/dist/esm/runtime/routeTree/loaders.mjs +117 -0
  69. package/dist/esm/runtime/routeTree/paths.mjs +16 -0
  70. package/dist/esm/runtime/routeTree/staticData.mjs +22 -0
  71. package/dist/esm/runtime/routeTree/tanstackRoutes.mjs +13 -0
  72. package/dist/esm/runtime/routeTree/types.mjs +0 -0
  73. package/dist/esm/runtime/routeTree.mjs +1 -318
  74. package/dist/esm/runtime/rsc/CompositeComponent.mjs +18 -13
  75. package/dist/esm/runtime/rsc/ReplayableStream.mjs +1 -1
  76. package/dist/esm/runtime/rsc/RscNodeRenderer.mjs +4 -15
  77. package/dist/esm/runtime/rsc/client.mjs +6 -26
  78. package/dist/esm/runtime/rsc/flightSerialization.mjs +88 -0
  79. package/dist/esm/runtime/rsc/payloadErrors.mjs +49 -0
  80. package/dist/esm/runtime/rsc/payloadFetch.mjs +55 -0
  81. package/dist/esm/runtime/rsc/payloadRouter.mjs +12 -133
  82. package/dist/esm/runtime/rsc/payloadRoutes.mjs +50 -0
  83. package/dist/esm/runtime/rsc/payloadStorage.mjs +14 -0
  84. package/dist/esm/runtime/rsc/server.mjs +17 -21
  85. package/dist/esm/runtime/rsc/shared.mjs +21 -0
  86. package/dist/esm/runtime/rsc/slotUsageSanitizer.mjs +1 -0
  87. package/dist/esm/runtime/rsc/treeGetter.mjs +34 -0
  88. package/dist/esm/runtime/shared/isAbsoluteUrl.mjs +9 -0
  89. package/dist/esm/runtime/ssrManagedTags.mjs +53 -0
  90. package/dist/esm/runtime/ssrPreload.mjs +51 -0
  91. package/dist/esm/runtime/ssrTypes.mjs +0 -0
  92. package/dist/esm/runtime/state.mjs +1 -1
  93. package/dist/esm/runtime/submitAction.mjs +120 -0
  94. package/dist/esm/runtime/utils.mjs +47 -75
  95. package/dist/esm-node/cli/artifacts.mjs +104 -0
  96. package/dist/esm-node/cli/index.mjs +8 -82
  97. package/dist/esm-node/cli/tanstackTypes/canonicalRoutes.mjs +76 -0
  98. package/dist/esm-node/cli/tanstackTypes/shared.mjs +62 -0
  99. package/dist/esm-node/cli/tanstackTypes/source.mjs +209 -0
  100. package/dist/esm-node/cli/tanstackTypes.mjs +2 -339
  101. package/dist/esm-node/runtime/blockingSubscribe.mjs +20 -0
  102. package/dist/esm-node/runtime/clientBasepath.mjs +11 -0
  103. package/dist/esm-node/runtime/clientHydration.mjs +71 -0
  104. package/dist/esm-node/runtime/dataMutation.mjs +2 -167
  105. package/dist/esm-node/runtime/formData.mjs +64 -0
  106. package/dist/esm-node/runtime/loaderBridge.mjs +5 -9
  107. package/dist/esm-node/runtime/plugin.mjs +14 -118
  108. package/dist/esm-node/runtime/plugin.node.mjs +41 -121
  109. package/dist/esm-node/runtime/pluginShared.mjs +41 -0
  110. package/dist/esm-node/runtime/prefetchLink.mjs +18 -5
  111. package/dist/esm-node/runtime/register.mjs +0 -14
  112. package/dist/esm-node/runtime/routeTree/components.mjs +56 -0
  113. package/dist/esm-node/runtime/routeTree/construction.mjs +112 -0
  114. package/dist/esm-node/runtime/routeTree/index.mjs +3 -0
  115. package/dist/esm-node/runtime/routeTree/loaders.mjs +118 -0
  116. package/dist/esm-node/runtime/routeTree/paths.mjs +17 -0
  117. package/dist/esm-node/runtime/routeTree/staticData.mjs +23 -0
  118. package/dist/esm-node/runtime/routeTree/tanstackRoutes.mjs +14 -0
  119. package/dist/esm-node/runtime/routeTree/types.mjs +1 -0
  120. package/dist/esm-node/runtime/routeTree.mjs +1 -318
  121. package/dist/esm-node/runtime/rsc/CompositeComponent.mjs +18 -13
  122. package/dist/esm-node/runtime/rsc/ReplayableStream.mjs +1 -1
  123. package/dist/esm-node/runtime/rsc/RscNodeRenderer.mjs +4 -15
  124. package/dist/esm-node/runtime/rsc/client.mjs +6 -26
  125. package/dist/esm-node/runtime/rsc/flightSerialization.mjs +89 -0
  126. package/dist/esm-node/runtime/rsc/payloadErrors.mjs +50 -0
  127. package/dist/esm-node/runtime/rsc/payloadFetch.mjs +56 -0
  128. package/dist/esm-node/runtime/rsc/payloadRouter.mjs +12 -133
  129. package/dist/esm-node/runtime/rsc/payloadRoutes.mjs +51 -0
  130. package/dist/esm-node/runtime/rsc/payloadStorage.mjs +15 -0
  131. package/dist/esm-node/runtime/rsc/server.mjs +17 -21
  132. package/dist/esm-node/runtime/rsc/shared.mjs +22 -0
  133. package/dist/esm-node/runtime/rsc/slotUsageSanitizer.mjs +1 -0
  134. package/dist/esm-node/runtime/rsc/treeGetter.mjs +35 -0
  135. package/dist/esm-node/runtime/shared/isAbsoluteUrl.mjs +10 -0
  136. package/dist/esm-node/runtime/ssrManagedTags.mjs +54 -0
  137. package/dist/esm-node/runtime/ssrPreload.mjs +52 -0
  138. package/dist/esm-node/runtime/ssrTypes.mjs +1 -0
  139. package/dist/esm-node/runtime/state.mjs +1 -1
  140. package/dist/esm-node/runtime/submitAction.mjs +121 -0
  141. package/dist/esm-node/runtime/utils.mjs +47 -75
  142. package/dist/types/cli/artifacts.d.ts +33 -0
  143. package/dist/types/cli/index.d.ts +2 -22
  144. package/dist/types/cli/tanstackTypes/canonicalRoutes.d.ts +22 -0
  145. package/dist/types/cli/tanstackTypes/shared.d.ts +20 -0
  146. package/dist/types/cli/tanstackTypes/source.d.ts +12 -0
  147. package/dist/types/cli/tanstackTypes.d.ts +3 -31
  148. package/dist/types/runtime/blockingSubscribe.d.ts +1 -0
  149. package/dist/types/runtime/clientBasepath.d.ts +1 -0
  150. package/dist/types/runtime/clientHydration.d.ts +10 -0
  151. package/dist/types/runtime/dataMutation.d.ts +3 -12
  152. package/dist/types/runtime/formData.d.ts +24 -0
  153. package/dist/types/runtime/loaderBridge.d.ts +2 -1
  154. package/dist/types/runtime/plugin.d.ts +1 -1
  155. package/dist/types/runtime/plugin.node.d.ts +1 -1
  156. package/dist/types/runtime/pluginShared.d.ts +38 -0
  157. package/dist/types/runtime/register.d.ts +1 -9
  158. package/dist/types/runtime/routeTree/components.d.ts +7 -0
  159. package/dist/types/runtime/routeTree/construction.d.ts +5 -0
  160. package/dist/types/runtime/routeTree/index.d.ts +2 -0
  161. package/dist/types/runtime/routeTree/loaders.d.ts +4 -0
  162. package/dist/types/runtime/routeTree/paths.d.ts +3 -0
  163. package/dist/types/runtime/routeTree/staticData.d.ts +19 -0
  164. package/dist/types/runtime/routeTree/tanstackRoutes.d.ts +4 -0
  165. package/dist/types/runtime/routeTree/types.d.ts +76 -0
  166. package/dist/types/runtime/routeTree.d.ts +1 -9
  167. package/dist/types/runtime/rsc/CompositeComponent.d.ts +1 -2
  168. package/dist/types/runtime/rsc/ReplayableStream.d.ts +3 -2
  169. package/dist/types/runtime/rsc/RscNodeRenderer.d.ts +2 -3
  170. package/dist/types/runtime/rsc/SlotContext.d.ts +2 -1
  171. package/dist/types/runtime/rsc/client.d.ts +2 -0
  172. package/dist/types/runtime/rsc/flightSerialization.d.ts +3 -0
  173. package/dist/types/runtime/rsc/payloadErrors.d.ts +6 -0
  174. package/dist/types/runtime/rsc/payloadFetch.d.ts +8 -0
  175. package/dist/types/runtime/rsc/payloadRouter.d.ts +2 -10
  176. package/dist/types/runtime/rsc/payloadRoutes.d.ts +40 -0
  177. package/dist/types/runtime/rsc/payloadStorage.d.ts +5 -0
  178. package/dist/types/runtime/rsc/shared.d.ts +18 -0
  179. package/dist/types/runtime/rsc/treeGetter.d.ts +1 -0
  180. package/dist/types/runtime/shared/isAbsoluteUrl.d.ts +1 -0
  181. package/dist/types/runtime/ssrManagedTags.d.ts +7 -0
  182. package/dist/types/runtime/ssrPreload.d.ts +2 -0
  183. package/dist/types/runtime/ssrTypes.d.ts +44 -0
  184. package/dist/types/runtime/state.d.ts +1 -1
  185. package/dist/types/runtime/submitAction.d.ts +14 -0
  186. package/dist/types/runtime/types.d.ts +4 -10
  187. package/dist/types/runtime/utils.d.ts +4 -24
  188. package/package.json +35 -14
  189. package/rstest.config.mts +6 -0
  190. package/src/cli/artifacts.ts +255 -0
  191. package/src/cli/index.ts +7 -185
  192. package/src/cli/tanstackTypes/canonicalRoutes.ts +157 -0
  193. package/src/cli/tanstackTypes/shared.ts +113 -0
  194. package/src/cli/tanstackTypes/source.ts +415 -0
  195. package/src/cli/tanstackTypes.ts +4 -647
  196. package/src/runtime/blockingSubscribe.ts +48 -0
  197. package/src/runtime/clientBasepath.ts +12 -0
  198. package/src/runtime/clientHydration.ts +146 -0
  199. package/src/runtime/dataMutation.tsx +9 -306
  200. package/src/runtime/formData.ts +153 -0
  201. package/src/runtime/loaderBridge.ts +11 -12
  202. package/src/runtime/plugin.node.tsx +76 -291
  203. package/src/runtime/plugin.tsx +21 -257
  204. package/src/runtime/pluginShared.ts +112 -0
  205. package/src/runtime/prefetchLink.tsx +78 -15
  206. package/src/runtime/register.ts +5 -37
  207. package/src/runtime/routeTree/components.ts +130 -0
  208. package/src/runtime/routeTree/construction.ts +238 -0
  209. package/src/runtime/routeTree/index.ts +7 -0
  210. package/src/runtime/routeTree/loaders.ts +232 -0
  211. package/src/runtime/routeTree/paths.ts +36 -0
  212. package/src/runtime/routeTree/staticData.ts +66 -0
  213. package/src/runtime/routeTree/tanstackRoutes.ts +36 -0
  214. package/src/runtime/routeTree/types.ts +87 -0
  215. package/src/runtime/routeTree.ts +5 -750
  216. package/src/runtime/rsc/CompositeComponent.tsx +29 -20
  217. package/src/runtime/rsc/ReplayableStream.ts +2 -2
  218. package/src/runtime/rsc/RscNodeRenderer.tsx +9 -27
  219. package/src/runtime/rsc/SlotContext.tsx +1 -1
  220. package/src/runtime/rsc/client.tsx +12 -40
  221. package/src/runtime/rsc/flightSerialization.ts +158 -0
  222. package/src/runtime/rsc/payloadErrors.ts +73 -0
  223. package/src/runtime/rsc/payloadFetch.ts +92 -0
  224. package/src/runtime/rsc/payloadRouter.ts +29 -234
  225. package/src/runtime/rsc/payloadRoutes.ts +122 -0
  226. package/src/runtime/rsc/payloadStorage.ts +35 -0
  227. package/src/runtime/rsc/server.tsx +20 -29
  228. package/src/runtime/rsc/shared.ts +70 -0
  229. package/src/runtime/rsc/slotUsageSanitizer.ts +4 -0
  230. package/src/runtime/rsc/treeGetter.tsx +52 -0
  231. package/src/runtime/shared/isAbsoluteUrl.ts +8 -0
  232. package/src/runtime/ssrManagedTags.ts +107 -0
  233. package/src/runtime/ssrPreload.ts +125 -0
  234. package/src/runtime/ssrTypes.ts +51 -0
  235. package/src/runtime/state.ts +9 -3
  236. package/src/runtime/submitAction.ts +201 -0
  237. package/src/runtime/types.ts +4 -18
  238. package/src/runtime/utils.tsx +76 -110
  239. package/src/types/react-server-dom-rspack.d.ts +56 -0
  240. package/tests/castRatchet.test.ts +85 -0
  241. package/tests/router/cli.test.ts +7 -0
  242. package/tests/router/dataMutation.test.tsx +59 -0
  243. package/tests/router/fixtures/router-gen.golden.txt +72 -0
  244. package/tests/router/flightSerialization.roundtrip.test.tsx +137 -0
  245. package/tests/router/generateRouteArtifacts.test.ts +106 -0
  246. package/tests/router/hooks.test.ts +14 -0
  247. package/tests/router/loaderBridge.test.ts +21 -0
  248. package/tests/router/prefetchLink.test.tsx +106 -59
  249. package/tests/router/prefetchLinkPreload.test.tsx +85 -0
  250. package/tests/router/register.test.ts +1 -39
  251. package/tests/router/routeTree.test.ts +51 -11
  252. package/tests/router/rsc.test.tsx +189 -0
  253. package/tests/router/rscPayloadRouterMatrix.test.ts +197 -0
  254. package/tests/router/slotUsageSanitizer.test.ts +23 -0
  255. package/tests/router/ssrPreload.test.ts +30 -0
  256. package/tests/router/tanstackTypes.test.ts +254 -1
  257. package/dist/esm/runtime/pluginCore.mjs +0 -14
  258. package/dist/esm-node/runtime/pluginCore.mjs +0 -15
  259. package/dist/types/runtime/pluginCore.d.ts +0 -21
  260. package/src/runtime/pluginCore.ts +0 -48
@@ -1,12 +1,14 @@
1
1
  // @effect-diagnostics strictBooleanExpressions:off
2
2
  import type { RouteObject } from '@modern-js/runtime-utils/router';
3
- import type { NestedRoute, PageRoute, SSRMode } from '@modern-js/types';
4
3
  import React from 'react';
4
+ import type { ModernRoute } from './types';
5
+
6
+ export type SSRMode = 'string' | 'stream';
5
7
 
6
8
  type RouterConfig = {
7
9
  routesConfig: {
8
10
  globalApp?: React.ComponentType<any>;
9
- routes?: (NestedRoute | PageRoute)[];
11
+ routes?: ModernRoute[];
10
12
  };
11
13
  };
12
14
 
@@ -19,101 +21,91 @@ type GlobalAppProps = {
19
21
  [key: string]: unknown;
20
22
  };
21
23
 
22
- export type ModernRouteObject = RouteObject & {
23
- isClientComponent?: boolean;
24
+ type ModernRouteObject = RouteObject & {
25
+ Component?: React.ComponentType | string;
26
+ action?: unknown;
27
+ clientData?: unknown;
28
+ config?: { handle?: Record<string, unknown> } | unknown;
29
+ handle?: Record<string, unknown>;
30
+ hasAction?: boolean;
24
31
  hasClientLoader?: boolean;
25
32
  hasLoader?: boolean;
26
- hasAction?: boolean;
27
33
  inValidSSRRoute?: boolean;
34
+ isClientComponent?: boolean;
28
35
  lazyImport?: () => Promise<{ default: React.ComponentType }>;
29
36
  };
30
37
 
31
- export function getRouteObjects(
32
- routes: (NestedRoute | PageRoute)[],
33
- {
34
- globalApp,
35
- ssrMode,
36
- props,
37
- }: {
38
- globalApp?: React.ComponentType<GlobalAppProps>;
39
- ssrMode?: SSRMode;
40
- props?: Record<string, unknown>;
41
- },
42
- ) {
43
- const createLayoutElement = (
44
- Component: React.ComponentType,
45
- ): React.ComponentType => {
46
- const GlobalLayout = globalApp;
47
- if (!GlobalLayout) {
48
- return Component;
49
- }
50
-
51
- const LayoutWrapper = (props: LayoutWrapperProps) => {
52
- const LayoutComponent = GlobalLayout;
53
- return <LayoutComponent Component={Component} {...props} />;
54
- };
38
+ function withGlobalLayout(
39
+ Component: React.ComponentType,
40
+ globalApp?: React.ComponentType<GlobalAppProps>,
41
+ ): React.ComponentType {
42
+ if (!globalApp) {
43
+ return Component;
44
+ }
55
45
 
56
- return LayoutWrapper;
46
+ const GlobalLayout = globalApp;
47
+ return function LayoutWrapper(props: LayoutWrapperProps) {
48
+ return <GlobalLayout Component={Component} {...props} />;
57
49
  };
50
+ }
58
51
 
59
- const routeObjects: RouteObject[] = [];
60
-
61
- for (const route of routes) {
62
- if (route.type === 'nested') {
63
- const nestedRouteObject = {
64
- path: route.path,
65
- id: route.id,
66
- loader: route.loader,
67
- action: route.action,
68
- shouldRevalidate: route.shouldRevalidate,
69
- handle: {
70
- ...route.handle,
71
- ...(typeof route.config === 'object' ? route.config?.handle : {}),
72
- },
73
- index: route.index,
74
- hasLoader: Boolean(route.loader),
75
- hasClientLoader: Boolean(route.clientData),
76
- hasAction: Boolean(route.action),
77
- ...(route.isClientComponent ? { isClientComponent: true } : {}),
78
- ...(route.inValidSSRRoute ? { inValidSSRRoute: true } : {}),
79
- lazyImport: route.lazyImport,
80
- Component: route.component ? route.component : undefined,
81
- errorElement: route.error ? <route.error /> : undefined,
82
- children: route.children
83
- ? route.children.map(
84
- child =>
85
- getRouteObjects([child], { globalApp, ssrMode, props })[0],
86
- )
87
- : undefined,
88
- } as ModernRouteObject;
52
+ function getRouteHandle(route: ModernRoute) {
53
+ return {
54
+ ...route.handle,
55
+ ...(typeof route.config === 'object' ? route.config?.handle : {}),
56
+ };
57
+ }
89
58
 
90
- routeObjects.push(nestedRouteObject);
91
- } else if (
92
- typeof route.component === 'function' ||
93
- typeof route.component === 'object'
94
- ) {
95
- const LayoutComponent = createLayoutElement(
96
- route.component as React.ComponentType,
97
- );
98
- const routeObject: RouteObject = {
99
- path: route.path,
100
- element: React.createElement(LayoutComponent),
101
- };
59
+ function toTanstackRouteObject(
60
+ route: ModernRoute,
61
+ globalApp?: React.ComponentType<GlobalAppProps>,
62
+ ): RouteObject | null {
63
+ if (route.type === 'nested') {
64
+ return {
65
+ path: route.path,
66
+ id: route.id,
67
+ loader: route.loader,
68
+ action: route.action,
69
+ shouldRevalidate: route.shouldRevalidate,
70
+ handle: getRouteHandle(route),
71
+ index: route.index,
72
+ hasLoader: route.hasLoader || Boolean(route.loader),
73
+ hasClientLoader: route.hasClientLoader || Boolean(route.clientData),
74
+ hasAction: route.hasAction || Boolean(route.action),
75
+ ...(route.isClientComponent ? { isClientComponent: true } : {}),
76
+ ...(route.inValidSSRRoute ? { inValidSSRRoute: true } : {}),
77
+ lazyImport: route.lazyImport,
78
+ Component: route.component ? route.component : undefined,
79
+ errorElement: route.error
80
+ ? React.createElement(route.error as React.ComponentType)
81
+ : undefined,
82
+ children: route.children
83
+ ? route.children
84
+ .map(child => toTanstackRouteObject(child, globalApp))
85
+ .filter((child): child is RouteObject => child !== null)
86
+ : undefined,
87
+ } as ModernRouteObject;
88
+ }
102
89
 
103
- routeObjects.push(routeObject);
104
- }
90
+ if (
91
+ typeof route.component !== 'function' &&
92
+ typeof route.component !== 'object'
93
+ ) {
94
+ return null;
105
95
  }
106
96
 
107
- // No synthetic `{ path: '*' }` 404 route here: TanStack Router handles
108
- // not-found matches through the root route's `notFoundComponent`
109
- // (see routeTree.ts), so the react-router style catch-all is unnecessary.
110
- return routeObjects;
97
+ const LayoutComponent = withGlobalLayout(
98
+ route.component as React.ComponentType,
99
+ globalApp,
100
+ );
101
+ return {
102
+ path: route.path,
103
+ element: React.createElement(LayoutComponent),
104
+ };
111
105
  }
112
106
 
113
- export function createRouteObjectsFromConfig({
107
+ export function createTanstackRouteObjectsFromConfig({
114
108
  routesConfig,
115
- props,
116
- ssrMode,
117
109
  }: {
118
110
  routesConfig: RouterConfig['routesConfig'];
119
111
  props?: Record<string, unknown>;
@@ -126,33 +118,7 @@ export function createRouteObjectsFromConfig({
126
118
  if (!routes) {
127
119
  return null;
128
120
  }
129
- return getRouteObjects(routes, {
130
- globalApp,
131
- ssrMode,
132
- props,
133
- });
134
- }
135
-
136
- export const urlJoin = (...parts: string[]) => {
137
- const separator = '/';
138
- const replace = new RegExp(`${separator}{1,}`, 'g');
139
- return standardSlash(parts.join(separator).replace(replace, separator));
140
- };
141
-
142
- export function standardSlash(str: string) {
143
- let addr = str;
144
- if (!addr || typeof addr !== 'string') {
145
- return addr;
146
- }
147
- if (addr.startsWith('.')) {
148
- addr = addr.slice(1);
149
- }
150
- if (!addr.startsWith('/')) {
151
- addr = `/${addr}`;
152
- }
153
- if (addr.endsWith('/') && addr !== '/') {
154
- addr = addr.slice(0, addr.length - 1);
155
- }
156
-
157
- return addr;
121
+ return routes
122
+ .map(route => toTanstackRouteObject(route, globalApp))
123
+ .filter((route): route is RouteObject => route !== null);
158
124
  }
@@ -0,0 +1,56 @@
1
+ declare module 'react-server-dom-rspack/client.edge' {
2
+ import type { TemporaryReferenceSet } from 'react-server-dom-rspack';
3
+
4
+ export type { TemporaryReferenceSet };
5
+
6
+ export const createTemporaryReferenceSet: (
7
+ ...args: unknown[]
8
+ ) => TemporaryReferenceSet;
9
+
10
+ export function createServerReference(
11
+ id: string,
12
+ ): (...args: unknown[]) => Promise<unknown>;
13
+
14
+ export type EncodeFormActionCallback = <A>(
15
+ id: unknown,
16
+ args: Promise<A>,
17
+ ) => ReactCustomFormAction;
18
+
19
+ export type ReactCustomFormAction = {
20
+ name?: string;
21
+ action?: string;
22
+ encType?: string;
23
+ method?: string;
24
+ target?: string;
25
+ data?: null | FormData;
26
+ };
27
+
28
+ export interface Options {
29
+ nonce?: string;
30
+ encodeFormAction?: EncodeFormActionCallback;
31
+ temporaryReferences?: TemporaryReferenceSet;
32
+ replayConsoleLogs?: boolean;
33
+ environmentName?: string;
34
+ debugChannel?: {
35
+ readable?: ReadableStream;
36
+ };
37
+ }
38
+
39
+ export function createFromFetch<T>(
40
+ promiseForResponse: Promise<Response>,
41
+ options?: Options,
42
+ ): Promise<T>;
43
+
44
+ export function createFromReadableStream<T>(
45
+ stream: ReadableStream,
46
+ options?: Options,
47
+ ): Promise<T>;
48
+
49
+ export const encodeReply: (
50
+ value: unknown,
51
+ options?: {
52
+ temporaryReferences?: TemporaryReferenceSet;
53
+ signal?: AbortSignal;
54
+ },
55
+ ) => Promise<string | FormData>;
56
+ }
@@ -0,0 +1,85 @@
1
+ import { readdirSync, readFileSync } from 'node:fs';
2
+ import path from 'node:path';
3
+
4
+ const SRC_DIR = path.join(__dirname, '../src');
5
+ const CAST_RATCHET_BASELINE = 40;
6
+ const CAST_PATTERN = /\bas\s+unknown\s+as\b|\bas\s+any\b/g;
7
+ const SOURCE_FILE_PATTERN = /\.(?:ts|tsx)$/u;
8
+
9
+ type CastLocation = {
10
+ cast: string;
11
+ columnNumber: number;
12
+ filePath: string;
13
+ lineNumber: number;
14
+ sourceLine: string;
15
+ };
16
+
17
+ const collectSourceFiles = (directory: string): string[] => {
18
+ const entries = readdirSync(directory, { withFileTypes: true }).sort((a, b) =>
19
+ a.name.localeCompare(b.name),
20
+ );
21
+
22
+ return entries.flatMap(entry => {
23
+ const entryPath = path.join(directory, entry.name);
24
+
25
+ if (entry.isDirectory()) {
26
+ return collectSourceFiles(entryPath);
27
+ }
28
+
29
+ return SOURCE_FILE_PATTERN.test(entry.name) ? [entryPath] : [];
30
+ });
31
+ };
32
+
33
+ const collectCastLocations = (): CastLocation[] =>
34
+ collectSourceFiles(SRC_DIR).flatMap(filePath => {
35
+ const relativeFilePath = path
36
+ .relative(SRC_DIR, filePath)
37
+ .split(path.sep)
38
+ .join('/');
39
+ const lines = readFileSync(filePath, 'utf-8').split(/\r?\n/u);
40
+ const locations: CastLocation[] = [];
41
+
42
+ lines.forEach((sourceLine, index) => {
43
+ CAST_PATTERN.lastIndex = 0;
44
+
45
+ let match: RegExpExecArray | null;
46
+ while ((match = CAST_PATTERN.exec(sourceLine))) {
47
+ locations.push({
48
+ cast: match[0],
49
+ columnNumber: match.index + 1,
50
+ filePath: relativeFilePath,
51
+ lineNumber: index + 1,
52
+ sourceLine: sourceLine.trim(),
53
+ });
54
+ }
55
+ });
56
+
57
+ return locations;
58
+ });
59
+
60
+ const formatCastLocations = (locations: CastLocation[]): string =>
61
+ locations
62
+ .map(
63
+ location =>
64
+ ` ${location.filePath}:${location.lineNumber}:${location.columnNumber} ${location.cast} :: ${location.sourceLine}`,
65
+ )
66
+ .join('\n');
67
+
68
+ describe('tanstack unsafe cast ratchet', () => {
69
+ test('does not add new as-any casts in src', () => {
70
+ const castLocations = collectCastLocations();
71
+
72
+ if (castLocations.length > CAST_RATCHET_BASELINE) {
73
+ throw new Error(
74
+ [
75
+ `Expected at most ${CAST_RATCHET_BASELINE} unsafe casts in plugin-tanstack src, found ${castLocations.length}.`,
76
+ 'Remove the new cast or lower the ratchet baseline after deleting existing casts.',
77
+ 'Current cast locations:',
78
+ formatCastLocations(castLocations),
79
+ ].join('\n'),
80
+ );
81
+ }
82
+
83
+ expect(castLocations.length).toBeLessThanOrEqual(CAST_RATCHET_BASELINE);
84
+ });
85
+ });
@@ -6,6 +6,7 @@ import type { Entrypoint } from '@modern-js/types';
6
6
  import { fs, NESTED_ROUTE_SPEC_FILE } from '@modern-js/utils';
7
7
  import {
8
8
  createTanstackRsbuildRouteSplittingProfile,
9
+ generateTanstackRouteArtifacts,
9
10
  tanstackRouterPlugin,
10
11
  writeTanstackRegisterFile,
11
12
  writeTanstackRouterTypesForEntries,
@@ -977,3 +978,9 @@ describe('tanstack router cli plugin', () => {
977
978
  });
978
979
  });
979
980
  });
981
+
982
+ describe('generateTanstackRouteArtifacts export', () => {
983
+ test('is exported as a function', () => {
984
+ expect(typeof generateTanstackRouteArtifacts).toBe('function');
985
+ });
986
+ });
@@ -2,6 +2,7 @@ import { fireEvent, render, screen, waitFor } from '@testing-library/react';
2
2
  import React, { act } from 'react';
3
3
  import type { Fetcher } from '../../src/runtime/dataMutation';
4
4
  import { Form, useFetcher } from '../../src/runtime/dataMutation';
5
+ import { createFormDataFromSubmit } from '../../src/runtime/formData';
5
6
 
6
7
  type RouteHandler = (args: {
7
8
  request: Request;
@@ -276,6 +277,28 @@ describe('tanstack data mutation fetcher', () => {
276
277
  expect(states).toEqual(['idle', 'loading', 'idle']);
277
278
  });
278
279
 
280
+ test('GET Form navigation replaces action query before hash', async () => {
281
+ currentRouter = createRouter({});
282
+
283
+ render(
284
+ <Form method="get" action="/products?sort=old#results">
285
+ <input name="q" defaultValue="boots" />
286
+ <button type="submit">Search</button>
287
+ </Form>,
288
+ );
289
+
290
+ const form = document.querySelector('form');
291
+ expect(form).toBeTruthy();
292
+
293
+ await act(async () => {
294
+ fireEvent.submit(form!);
295
+ });
296
+
297
+ expect(currentRouter.navigate).toHaveBeenCalledWith({
298
+ href: '/products?q=boots#results',
299
+ });
300
+ });
301
+
279
302
  test('keeps non-idle state while overlapping mutation submits are still active', async () => {
280
303
  const actionResults = [
281
304
  createDeferred<Response>(),
@@ -361,6 +384,42 @@ describe('tanstack data mutation fetcher', () => {
361
384
  expect(screen.getByTestId('data').textContent).toBe('{"count":3}');
362
385
  });
363
386
 
387
+ test('preserves submitter name value when FormData submitter overload is unavailable', () => {
388
+ const NativeFormData = globalThis.FormData;
389
+
390
+ class SingleArgumentFormData extends NativeFormData {
391
+ constructor(form?: HTMLFormElement, submitter?: HTMLElement) {
392
+ if (submitter) {
393
+ throw new TypeError('FormData submitter overload unavailable');
394
+ }
395
+ super(form);
396
+ }
397
+ }
398
+
399
+ globalThis.FormData = SingleArgumentFormData as typeof FormData;
400
+
401
+ try {
402
+ const form = document.createElement('form');
403
+ form.innerHTML = `
404
+ <input name="amount" value="2" />
405
+ <button type="submit" name="intent" value="save">Save</button>
406
+ `;
407
+
408
+ const submitter = form.querySelector('button');
409
+ expect(submitter).toBeInstanceOf(HTMLButtonElement);
410
+
411
+ const formData = createFormDataFromSubmit({
412
+ form,
413
+ submitter,
414
+ });
415
+
416
+ expect(formData.get('amount')).toBe('2');
417
+ expect(formData.get('intent')).toBe('save');
418
+ } finally {
419
+ globalThis.FormData = NativeFormData;
420
+ }
421
+ });
422
+
364
423
  test('does not throw for Form submit with non-2xx response and still invalidates', async () => {
365
424
  const action = rstest.fn(
366
425
  async () =>
@@ -0,0 +1,72 @@
1
+ /* eslint-disable */
2
+ // This file is auto-generated by Modern.js. Do not edit manually.
3
+
4
+ import {
5
+ createMemoryHistory,
6
+ createRootRouteWithContext,
7
+ createRoute,
8
+ createRouter,
9
+ createRouteStaticData,
10
+ type ModernRouterContext,
11
+ modernLoaderToTanstack,
12
+ modernTanstackRouterFastDefaults,
13
+ } from '@modern-js/plugin-tanstack/runtime';
14
+
15
+ import component_0 from "../../routes/(app)/layout";
16
+ import { loader as loader_0, action as action_0 } from "../../routes/(app)/users/(userId)/page.data";
17
+ import { validateSearch as validateSearch_0 } from "../../routes/search.contract";
18
+ import { loaderDeps as loaderDeps_0 } from "../../routes/search.contract";
19
+ import component_1 from "../../routes/(app)/users/(userId)/page";
20
+ import component_2 from "../../routes/(app)/docs/splat";
21
+
22
+ export const rootRoute = createRootRouteWithContext<ModernRouterContext>()({
23
+
24
+ staticData: createRouteStaticData({
25
+ modernRouteId: "layout",
26
+ }),
27
+ });
28
+
29
+ const route__app__layout__base = createRoute({
30
+ getParentRoute: () => rootRoute,
31
+ component: component_0,
32
+ id: "(app)/layout",
33
+ staticData: createRouteStaticData({
34
+ modernRouteId: "(app)/layout",
35
+ }),
36
+ });
37
+
38
+ const route__app__users__userId__page = createRoute({
39
+ getParentRoute: () => route__app__layout__base,
40
+ component: component_1,
41
+ path: "users/$userId",
42
+ loader: modernLoaderToTanstack({ hasSplat: false }, loader_0),
43
+ validateSearch: validateSearch_0,
44
+ loaderDeps: loaderDeps_0,
45
+ staticData: createRouteStaticData({
46
+ modernRouteId: "(app)/users/(userId)/page",
47
+ modernRouteLoader: loader_0,
48
+ modernRouteAction: action_0,
49
+ }),
50
+ });
51
+
52
+ const route__app__docs_splat_page = createRoute({
53
+ getParentRoute: () => route__app__layout__base,
54
+ component: component_2,
55
+ path: "docs/$",
56
+ staticData: createRouteStaticData({
57
+ modernRouteId: "(app)/docs/splat/page",
58
+ }),
59
+ });
60
+
61
+ const route__app__layout = route__app__layout__base.addChildren([route__app__users__userId__page, route__app__docs_splat_page]);
62
+
63
+ export const routeTree = rootRoute.addChildren([route__app__layout]);
64
+
65
+ export const router = createRouter({
66
+ ...modernTanstackRouterFastDefaults,
67
+ routeTree,
68
+ history: createMemoryHistory({
69
+ initialEntries: ['/'],
70
+ }),
71
+ context: {} as ModernRouterContext,
72
+ });
@@ -0,0 +1,137 @@
1
+ import type React from 'react';
2
+ import { isValidElement } from 'react';
3
+ import { createRscProxy } from '../../src/runtime/rsc/createRscProxy';
4
+ import {
5
+ reviveTanstackRscFlightValues,
6
+ serializeTanstackRscFlightValues,
7
+ } from '../../src/runtime/rsc/flightSerialization';
8
+ import {
9
+ RENDERABLE_RSC,
10
+ SERVER_COMPONENT_STREAM,
11
+ type ServerComponentStream,
12
+ } from '../../src/runtime/rsc/symbols';
13
+
14
+ type RoundTripFixture = {
15
+ bigintValue: bigint;
16
+ booleanValue: boolean;
17
+ circular: Record<string, unknown>;
18
+ dateValue: Date;
19
+ falseValue: boolean;
20
+ mapValue: Map<unknown, unknown>;
21
+ nested: {
22
+ array: unknown[];
23
+ object: {
24
+ child: unknown;
25
+ };
26
+ };
27
+ nullValue: null;
28
+ numberValue: number;
29
+ rscProxy: React.ReactElement & Record<PropertyKey, unknown>;
30
+ setValue: Set<unknown>;
31
+ sharedA: Record<string, unknown>;
32
+ sharedB: Record<string, unknown>;
33
+ stringValue: string;
34
+ undefinedValue: undefined;
35
+ };
36
+
37
+ function createStream(): ServerComponentStream {
38
+ return {
39
+ createReplayStream: () => new ReadableStream<Uint8Array>(),
40
+ };
41
+ }
42
+
43
+ test('round-trips TanStack RSC flight values across supported value types', async () => {
44
+ const shared = { marker: 'shared' };
45
+ const circular: Record<string, unknown> = { marker: 'circular' };
46
+ circular.self = circular;
47
+
48
+ const mapValue = new Map<unknown, unknown>([
49
+ ['primitive', 1],
50
+ ['shared', shared],
51
+ [shared, circular],
52
+ ]);
53
+ mapValue.set('self', mapValue);
54
+
55
+ const setValue = new Set<unknown>(['set-item', shared, circular]);
56
+ setValue.add(setValue);
57
+
58
+ const rscProxy = createRscProxy(() => ({ slot: 'server' }), {
59
+ renderable: true,
60
+ stream: createStream(),
61
+ }) as React.ReactElement & Record<PropertyKey, unknown>;
62
+
63
+ const fixture: RoundTripFixture = {
64
+ bigintValue: 9007199254740993n,
65
+ booleanValue: true,
66
+ circular,
67
+ dateValue: new Date('2026-07-08T12:34:56.789Z'),
68
+ falseValue: false,
69
+ mapValue,
70
+ nested: {
71
+ array: [shared, [null, undefined, 7n]],
72
+ object: {
73
+ child: shared,
74
+ },
75
+ },
76
+ nullValue: null,
77
+ numberValue: 42,
78
+ rscProxy,
79
+ setValue,
80
+ sharedA: shared,
81
+ sharedB: shared,
82
+ stringValue: 'flight',
83
+ undefinedValue: undefined,
84
+ };
85
+
86
+ const serialized = serializeTanstackRscFlightValues(
87
+ fixture,
88
+ ) as RoundTripFixture;
89
+ const revived = (await reviveTanstackRscFlightValues(
90
+ serialized,
91
+ )) as RoundTripFixture;
92
+
93
+ expect(revived).not.toBe(fixture);
94
+ expect(revived.stringValue).toBe('flight');
95
+ expect(revived.numberValue).toBe(42);
96
+ expect(revived.booleanValue).toBe(true);
97
+ expect(revived.falseValue).toBe(false);
98
+ expect(revived.nullValue).toBeNull();
99
+ expect(Object.hasOwn(revived, 'undefinedValue')).toBe(true);
100
+ expect(revived.undefinedValue).toBeUndefined();
101
+ expect(revived.bigintValue).toBe(9007199254740993n);
102
+ expect(revived.dateValue).toBeInstanceOf(Date);
103
+ expect(revived.dateValue.toISOString()).toBe('2026-07-08T12:34:56.789Z');
104
+
105
+ expect(revived.nested.array[0]).toBe(revived.sharedA);
106
+ expect(revived.nested.object.child).toBe(revived.sharedA);
107
+ expect(revived.sharedB).toBe(revived.sharedA);
108
+
109
+ expect(revived.circular).not.toBe(circular);
110
+ expect(revived.circular.self).toBe(revived.circular);
111
+
112
+ expect(revived.mapValue).toBeInstanceOf(Map);
113
+ expect(revived.mapValue).not.toBe(mapValue);
114
+ expect(revived.mapValue.get('primitive')).toBe(1);
115
+ expect(revived.mapValue.get('shared')).toBe(revived.sharedA);
116
+ expect(revived.mapValue.get(revived.sharedA)).toBe(revived.circular);
117
+ expect(revived.mapValue.get('self')).toBe(revived.mapValue);
118
+
119
+ expect(revived.setValue).toBeInstanceOf(Set);
120
+ expect(revived.setValue).not.toBe(setValue);
121
+ expect(revived.setValue.has('set-item')).toBe(true);
122
+ expect(revived.setValue.has(revived.sharedA)).toBe(true);
123
+ expect(revived.setValue.has(revived.circular)).toBe(true);
124
+ expect(revived.setValue.has(revived.setValue)).toBe(true);
125
+
126
+ expect(serialized.rscProxy).not.toBe(rscProxy);
127
+ expect(serialized.rscProxy).toMatchObject({
128
+ __modernTanstackRsc: true,
129
+ kind: 'renderable',
130
+ });
131
+ expect(serialized.rscProxy.stream).toBeInstanceOf(ReadableStream);
132
+
133
+ expect(revived.rscProxy).not.toBe(rscProxy);
134
+ expect(isValidElement(revived.rscProxy)).toBe(true);
135
+ expect(revived.rscProxy[RENDERABLE_RSC]).toBe(true);
136
+ expect(revived.rscProxy[SERVER_COMPONENT_STREAM]).toBeDefined();
137
+ });