@bleedingdev/modern-js-plugin-tanstack 3.5.0-ultramodern.9 → 3.5.0-ultramodern.91

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 (270) 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 +255 -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 +118 -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 +3 -16
  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/router.js +15 -2
  28. package/dist/cjs/runtime/rsc/CompositeComponent.js +18 -13
  29. package/dist/cjs/runtime/rsc/ReplayableStream.js +0 -4
  30. package/dist/cjs/runtime/rsc/RscNodeRenderer.js +5 -16
  31. package/dist/cjs/runtime/rsc/client.js +8 -25
  32. package/dist/cjs/runtime/rsc/flightSerialization.js +132 -0
  33. package/dist/cjs/runtime/rsc/payloadErrors.js +99 -0
  34. package/dist/cjs/runtime/rsc/payloadFetch.js +105 -0
  35. package/dist/cjs/runtime/rsc/payloadRouter.js +21 -142
  36. package/dist/cjs/runtime/rsc/payloadRoutes.js +94 -0
  37. package/dist/cjs/runtime/rsc/payloadStorage.js +58 -0
  38. package/dist/cjs/runtime/rsc/server.js +14 -18
  39. package/dist/cjs/runtime/rsc/shared.js +68 -0
  40. package/dist/cjs/runtime/rsc/slotUsageSanitizer.js +1 -0
  41. package/dist/cjs/runtime/rsc/treeGetter.js +72 -0
  42. package/dist/cjs/runtime/shared/isAbsoluteUrl.js +47 -0
  43. package/dist/cjs/runtime/ssrManagedTags.js +103 -0
  44. package/dist/cjs/runtime/ssrPreload.js +89 -0
  45. package/dist/cjs/runtime/ssrTypes.js +18 -0
  46. package/dist/cjs/runtime/state.js +1 -1
  47. package/dist/cjs/runtime/submitAction.js +161 -0
  48. package/dist/cjs/runtime/utils.js +49 -86
  49. package/dist/esm/cli/artifacts.mjs +103 -0
  50. package/dist/esm/cli/index.mjs +8 -82
  51. package/dist/esm/cli/tanstackTypes/canonicalRoutes.mjs +75 -0
  52. package/dist/esm/cli/tanstackTypes/shared.mjs +61 -0
  53. package/dist/esm/cli/tanstackTypes/source.mjs +207 -0
  54. package/dist/esm/cli/tanstackTypes.mjs +2 -339
  55. package/dist/esm/runtime/blockingSubscribe.mjs +19 -0
  56. package/dist/esm/runtime/clientBasepath.mjs +10 -0
  57. package/dist/esm/runtime/clientHydration.mjs +71 -0
  58. package/dist/esm/runtime/dataMutation.mjs +2 -167
  59. package/dist/esm/runtime/formData.mjs +63 -0
  60. package/dist/esm/runtime/loaderBridge.mjs +5 -9
  61. package/dist/esm/runtime/plugin.mjs +14 -118
  62. package/dist/esm/runtime/plugin.node.mjs +41 -121
  63. package/dist/esm/runtime/pluginShared.mjs +40 -0
  64. package/dist/esm/runtime/prefetchLink.mjs +18 -5
  65. package/dist/esm/runtime/register.mjs +1 -14
  66. package/dist/esm/runtime/routeTree/components.mjs +55 -0
  67. package/dist/esm/runtime/routeTree/construction.mjs +111 -0
  68. package/dist/esm/runtime/routeTree/index.mjs +2 -0
  69. package/dist/esm/runtime/routeTree/loaders.mjs +117 -0
  70. package/dist/esm/runtime/routeTree/paths.mjs +16 -0
  71. package/dist/esm/runtime/routeTree/staticData.mjs +22 -0
  72. package/dist/esm/runtime/routeTree/tanstackRoutes.mjs +13 -0
  73. package/dist/esm/runtime/routeTree/types.mjs +0 -0
  74. package/dist/esm/runtime/routeTree.mjs +1 -318
  75. package/dist/esm/runtime/router.mjs +16 -2
  76. package/dist/esm/runtime/rsc/CompositeComponent.mjs +18 -13
  77. package/dist/esm/runtime/rsc/ReplayableStream.mjs +1 -1
  78. package/dist/esm/runtime/rsc/RscNodeRenderer.mjs +4 -15
  79. package/dist/esm/runtime/rsc/client.mjs +6 -26
  80. package/dist/esm/runtime/rsc/flightSerialization.mjs +88 -0
  81. package/dist/esm/runtime/rsc/payloadErrors.mjs +49 -0
  82. package/dist/esm/runtime/rsc/payloadFetch.mjs +55 -0
  83. package/dist/esm/runtime/rsc/payloadRouter.mjs +12 -133
  84. package/dist/esm/runtime/rsc/payloadRoutes.mjs +50 -0
  85. package/dist/esm/runtime/rsc/payloadStorage.mjs +14 -0
  86. package/dist/esm/runtime/rsc/server.mjs +17 -21
  87. package/dist/esm/runtime/rsc/shared.mjs +21 -0
  88. package/dist/esm/runtime/rsc/slotUsageSanitizer.mjs +1 -0
  89. package/dist/esm/runtime/rsc/treeGetter.mjs +34 -0
  90. package/dist/esm/runtime/shared/isAbsoluteUrl.mjs +9 -0
  91. package/dist/esm/runtime/ssrManagedTags.mjs +53 -0
  92. package/dist/esm/runtime/ssrPreload.mjs +51 -0
  93. package/dist/esm/runtime/ssrTypes.mjs +0 -0
  94. package/dist/esm/runtime/state.mjs +1 -1
  95. package/dist/esm/runtime/submitAction.mjs +120 -0
  96. package/dist/esm/runtime/utils.mjs +47 -75
  97. package/dist/esm-node/cli/artifacts.mjs +104 -0
  98. package/dist/esm-node/cli/index.mjs +8 -82
  99. package/dist/esm-node/cli/tanstackTypes/canonicalRoutes.mjs +76 -0
  100. package/dist/esm-node/cli/tanstackTypes/shared.mjs +62 -0
  101. package/dist/esm-node/cli/tanstackTypes/source.mjs +208 -0
  102. package/dist/esm-node/cli/tanstackTypes.mjs +2 -339
  103. package/dist/esm-node/runtime/blockingSubscribe.mjs +20 -0
  104. package/dist/esm-node/runtime/clientBasepath.mjs +11 -0
  105. package/dist/esm-node/runtime/clientHydration.mjs +72 -0
  106. package/dist/esm-node/runtime/dataMutation.mjs +2 -167
  107. package/dist/esm-node/runtime/formData.mjs +64 -0
  108. package/dist/esm-node/runtime/loaderBridge.mjs +5 -9
  109. package/dist/esm-node/runtime/plugin.mjs +14 -118
  110. package/dist/esm-node/runtime/plugin.node.mjs +41 -121
  111. package/dist/esm-node/runtime/pluginShared.mjs +41 -0
  112. package/dist/esm-node/runtime/prefetchLink.mjs +18 -5
  113. package/dist/esm-node/runtime/register.mjs +1 -14
  114. package/dist/esm-node/runtime/routeTree/components.mjs +56 -0
  115. package/dist/esm-node/runtime/routeTree/construction.mjs +112 -0
  116. package/dist/esm-node/runtime/routeTree/index.mjs +3 -0
  117. package/dist/esm-node/runtime/routeTree/loaders.mjs +118 -0
  118. package/dist/esm-node/runtime/routeTree/paths.mjs +17 -0
  119. package/dist/esm-node/runtime/routeTree/staticData.mjs +23 -0
  120. package/dist/esm-node/runtime/routeTree/tanstackRoutes.mjs +14 -0
  121. package/dist/esm-node/runtime/routeTree/types.mjs +1 -0
  122. package/dist/esm-node/runtime/routeTree.mjs +1 -318
  123. package/dist/esm-node/runtime/router.mjs +16 -2
  124. package/dist/esm-node/runtime/rsc/CompositeComponent.mjs +18 -13
  125. package/dist/esm-node/runtime/rsc/ReplayableStream.mjs +1 -1
  126. package/dist/esm-node/runtime/rsc/RscNodeRenderer.mjs +4 -15
  127. package/dist/esm-node/runtime/rsc/client.mjs +6 -26
  128. package/dist/esm-node/runtime/rsc/flightSerialization.mjs +89 -0
  129. package/dist/esm-node/runtime/rsc/payloadErrors.mjs +50 -0
  130. package/dist/esm-node/runtime/rsc/payloadFetch.mjs +56 -0
  131. package/dist/esm-node/runtime/rsc/payloadRouter.mjs +12 -133
  132. package/dist/esm-node/runtime/rsc/payloadRoutes.mjs +51 -0
  133. package/dist/esm-node/runtime/rsc/payloadStorage.mjs +15 -0
  134. package/dist/esm-node/runtime/rsc/server.mjs +17 -21
  135. package/dist/esm-node/runtime/rsc/shared.mjs +22 -0
  136. package/dist/esm-node/runtime/rsc/slotUsageSanitizer.mjs +1 -0
  137. package/dist/esm-node/runtime/rsc/treeGetter.mjs +35 -0
  138. package/dist/esm-node/runtime/shared/isAbsoluteUrl.mjs +10 -0
  139. package/dist/esm-node/runtime/ssrManagedTags.mjs +54 -0
  140. package/dist/esm-node/runtime/ssrPreload.mjs +52 -0
  141. package/dist/esm-node/runtime/ssrTypes.mjs +1 -0
  142. package/dist/esm-node/runtime/state.mjs +1 -1
  143. package/dist/esm-node/runtime/submitAction.mjs +121 -0
  144. package/dist/esm-node/runtime/utils.mjs +47 -75
  145. package/dist/types/cli/artifacts.d.ts +33 -0
  146. package/dist/types/cli/index.d.ts +2 -22
  147. package/dist/types/cli/tanstackTypes/canonicalRoutes.d.ts +22 -0
  148. package/dist/types/cli/tanstackTypes/shared.d.ts +20 -0
  149. package/dist/types/cli/tanstackTypes/source.d.ts +12 -0
  150. package/dist/types/cli/tanstackTypes.d.ts +3 -31
  151. package/dist/types/runtime/blockingSubscribe.d.ts +1 -0
  152. package/dist/types/runtime/clientBasepath.d.ts +1 -0
  153. package/dist/types/runtime/clientHydration.d.ts +10 -0
  154. package/dist/types/runtime/dataMutation.d.ts +3 -12
  155. package/dist/types/runtime/formData.d.ts +24 -0
  156. package/dist/types/runtime/loaderBridge.d.ts +2 -1
  157. package/dist/types/runtime/plugin.d.ts +1 -1
  158. package/dist/types/runtime/plugin.node.d.ts +1 -1
  159. package/dist/types/runtime/pluginShared.d.ts +38 -0
  160. package/dist/types/runtime/register.d.ts +10 -9
  161. package/dist/types/runtime/routeTree/components.d.ts +7 -0
  162. package/dist/types/runtime/routeTree/construction.d.ts +5 -0
  163. package/dist/types/runtime/routeTree/index.d.ts +2 -0
  164. package/dist/types/runtime/routeTree/loaders.d.ts +4 -0
  165. package/dist/types/runtime/routeTree/paths.d.ts +3 -0
  166. package/dist/types/runtime/routeTree/staticData.d.ts +19 -0
  167. package/dist/types/runtime/routeTree/tanstackRoutes.d.ts +4 -0
  168. package/dist/types/runtime/routeTree/types.d.ts +76 -0
  169. package/dist/types/runtime/routeTree.d.ts +1 -9
  170. package/dist/types/runtime/router.d.ts +6 -7
  171. package/dist/types/runtime/rsc/CompositeComponent.d.ts +1 -2
  172. package/dist/types/runtime/rsc/ReplayableStream.d.ts +3 -2
  173. package/dist/types/runtime/rsc/RscNodeRenderer.d.ts +2 -3
  174. package/dist/types/runtime/rsc/SlotContext.d.ts +2 -1
  175. package/dist/types/runtime/rsc/client.d.ts +2 -0
  176. package/dist/types/runtime/rsc/flightSerialization.d.ts +3 -0
  177. package/dist/types/runtime/rsc/payloadErrors.d.ts +6 -0
  178. package/dist/types/runtime/rsc/payloadFetch.d.ts +8 -0
  179. package/dist/types/runtime/rsc/payloadRouter.d.ts +2 -10
  180. package/dist/types/runtime/rsc/payloadRoutes.d.ts +40 -0
  181. package/dist/types/runtime/rsc/payloadStorage.d.ts +5 -0
  182. package/dist/types/runtime/rsc/shared.d.ts +18 -0
  183. package/dist/types/runtime/rsc/treeGetter.d.ts +1 -0
  184. package/dist/types/runtime/shared/isAbsoluteUrl.d.ts +1 -0
  185. package/dist/types/runtime/ssrManagedTags.d.ts +7 -0
  186. package/dist/types/runtime/ssrPreload.d.ts +2 -0
  187. package/dist/types/runtime/ssrTypes.d.ts +44 -0
  188. package/dist/types/runtime/state.d.ts +1 -1
  189. package/dist/types/runtime/submitAction.d.ts +14 -0
  190. package/dist/types/runtime/types.d.ts +4 -10
  191. package/dist/types/runtime/utils.d.ts +4 -24
  192. package/package.json +37 -17
  193. package/rstest.config.mts +23 -0
  194. package/src/cli/artifacts.ts +255 -0
  195. package/src/cli/index.ts +7 -185
  196. package/src/cli/tanstackTypes/canonicalRoutes.ts +157 -0
  197. package/src/cli/tanstackTypes/shared.ts +113 -0
  198. package/src/cli/tanstackTypes/source.ts +414 -0
  199. package/src/cli/tanstackTypes.ts +4 -647
  200. package/src/runtime/blockingSubscribe.ts +48 -0
  201. package/src/runtime/clientBasepath.ts +12 -0
  202. package/src/runtime/clientHydration.ts +149 -0
  203. package/src/runtime/dataMutation.tsx +9 -306
  204. package/src/runtime/formData.ts +153 -0
  205. package/src/runtime/loaderBridge.ts +11 -12
  206. package/src/runtime/plugin.node.tsx +76 -291
  207. package/src/runtime/plugin.tsx +21 -257
  208. package/src/runtime/pluginShared.ts +112 -0
  209. package/src/runtime/prefetchLink.tsx +78 -15
  210. package/src/runtime/register.ts +10 -42
  211. package/src/runtime/routeTree/components.ts +130 -0
  212. package/src/runtime/routeTree/construction.ts +238 -0
  213. package/src/runtime/routeTree/index.ts +7 -0
  214. package/src/runtime/routeTree/loaders.ts +232 -0
  215. package/src/runtime/routeTree/paths.ts +36 -0
  216. package/src/runtime/routeTree/staticData.ts +66 -0
  217. package/src/runtime/routeTree/tanstackRoutes.ts +36 -0
  218. package/src/runtime/routeTree/types.ts +87 -0
  219. package/src/runtime/routeTree.ts +5 -750
  220. package/src/runtime/router.ts +35 -7
  221. package/src/runtime/rsc/CompositeComponent.tsx +29 -20
  222. package/src/runtime/rsc/ReplayableStream.ts +2 -2
  223. package/src/runtime/rsc/RscNodeRenderer.tsx +9 -27
  224. package/src/runtime/rsc/SlotContext.tsx +1 -1
  225. package/src/runtime/rsc/client.tsx +12 -40
  226. package/src/runtime/rsc/flightSerialization.ts +158 -0
  227. package/src/runtime/rsc/payloadErrors.ts +73 -0
  228. package/src/runtime/rsc/payloadFetch.ts +92 -0
  229. package/src/runtime/rsc/payloadRouter.ts +29 -234
  230. package/src/runtime/rsc/payloadRoutes.ts +122 -0
  231. package/src/runtime/rsc/payloadStorage.ts +35 -0
  232. package/src/runtime/rsc/server.tsx +20 -29
  233. package/src/runtime/rsc/shared.ts +70 -0
  234. package/src/runtime/rsc/slotUsageSanitizer.ts +4 -0
  235. package/src/runtime/rsc/treeGetter.tsx +52 -0
  236. package/src/runtime/shared/isAbsoluteUrl.ts +8 -0
  237. package/src/runtime/ssrManagedTags.ts +107 -0
  238. package/src/runtime/ssrPreload.ts +125 -0
  239. package/src/runtime/ssrTypes.ts +51 -0
  240. package/src/runtime/state.ts +9 -3
  241. package/src/runtime/submitAction.ts +201 -0
  242. package/src/runtime/types.ts +4 -18
  243. package/src/runtime/utils.tsx +76 -110
  244. package/src/types/react-server-dom-rspack.d.ts +56 -0
  245. package/tests/castRatchet.test.ts +85 -0
  246. package/tests/realm-isolation.test.ts +114 -0
  247. package/tests/router/cli.test.ts +7 -0
  248. package/tests/router/clientHydration.test.tsx +61 -0
  249. package/tests/router/dataMutation.test.tsx +59 -0
  250. package/tests/router/fixtures/legacyRuntimeRouter.ts +10 -0
  251. package/tests/router/fixtures/router-gen.golden.txt +71 -0
  252. package/tests/router/fixtures/router-gen.oxlint.json +5 -0
  253. package/tests/router/flightSerialization.roundtrip.test.tsx +137 -0
  254. package/tests/router/generateRouteArtifacts.test.ts +106 -0
  255. package/tests/router/hooks.test.ts +14 -0
  256. package/tests/router/loaderBridge.test.ts +21 -0
  257. package/tests/router/prefetchLink.test.tsx +106 -59
  258. package/tests/router/prefetchLinkPreload.test.tsx +85 -0
  259. package/tests/router/register.test.ts +1 -39
  260. package/tests/router/routeTree.test.ts +51 -11
  261. package/tests/router/routerCompatibility.test.ts +245 -0
  262. package/tests/router/rsc.test.tsx +189 -0
  263. package/tests/router/rscPayloadRouterMatrix.test.ts +197 -0
  264. package/tests/router/slotUsageSanitizer.test.ts +23 -0
  265. package/tests/router/ssrPreload.test.ts +30 -0
  266. package/tests/router/tanstackTypes.test.ts +298 -1
  267. package/dist/esm/runtime/pluginCore.mjs +0 -14
  268. package/dist/esm-node/runtime/pluginCore.mjs +0 -15
  269. package/dist/types/runtime/pluginCore.d.ts +0 -21
  270. package/src/runtime/pluginCore.ts +0 -48
@@ -0,0 +1,149 @@
1
+ // @effect-diagnostics strictBooleanExpressions:off
2
+
3
+ import { type AnyRouter, RouterProvider } from '@tanstack/react-router';
4
+ import { createElement } from 'react';
5
+ import { withModernRouteMatchContext } from './outlet';
6
+ import { pickRouteModuleComponent } from './routeTree';
7
+
8
+ export type WindowWithTanstackSsr = Window & {
9
+ $_TSR?: unknown;
10
+ };
11
+
12
+ type RouteComponentPreloadable = {
13
+ load?: () => Promise<unknown>;
14
+ preload?: () => Promise<unknown>;
15
+ };
16
+
17
+ type RouterWithPreloadableRoutes = AnyRouter & {
18
+ routesById?: Record<
19
+ string,
20
+ {
21
+ options?: {
22
+ component?: unknown;
23
+ staticData?: {
24
+ modernRouteId?: string;
25
+ };
26
+ };
27
+ }
28
+ >;
29
+ };
30
+
31
+ type RouterHydrationRecord = {
32
+ error?: unknown;
33
+ promise: Promise<unknown>;
34
+ status: 'pending' | 'fulfilled' | 'rejected';
35
+ };
36
+
37
+ const routerHydrationRecords = new WeakMap<AnyRouter, RouterHydrationRecord>();
38
+ const routeModulesKey = '_routeModules';
39
+
40
+ function getCachedRouteModule(routeId: string) {
41
+ if (typeof window === 'undefined') {
42
+ return undefined;
43
+ }
44
+
45
+ return (window as unknown as Record<string, Record<string, unknown>>)[
46
+ routeModulesKey
47
+ ]?.[routeId];
48
+ }
49
+
50
+ function preloadHydratedRouteComponents(router: AnyRouter): Promise<void> {
51
+ const preloadableRouter = router as RouterWithPreloadableRoutes;
52
+ const routesById = preloadableRouter.routesById || {};
53
+ const matches = preloadableRouter.stores.matches.get() as Array<{
54
+ routeId?: string;
55
+ }>;
56
+
57
+ return Promise.all(
58
+ matches.map(match => {
59
+ if (match.routeId === undefined || match.routeId === '') {
60
+ return undefined;
61
+ }
62
+ const route = routesById[match.routeId];
63
+ const component = route?.options?.component as RouteComponentPreloadable;
64
+ const preload = component?.load || component?.preload;
65
+ if (typeof preload !== 'function') {
66
+ return undefined;
67
+ }
68
+ return Promise.resolve(preload.call(component)).then(routeModule => {
69
+ const modernRouteId = route?.options?.staticData?.modernRouteId;
70
+ const cachedRouteModule =
71
+ typeof modernRouteId === 'string' && modernRouteId !== ''
72
+ ? getCachedRouteModule(modernRouteId)
73
+ : undefined;
74
+ const resolvedComponent = pickRouteModuleComponent(
75
+ cachedRouteModule ?? routeModule,
76
+ );
77
+ if (
78
+ resolvedComponent !== undefined &&
79
+ typeof modernRouteId === 'string' &&
80
+ modernRouteId !== ''
81
+ ) {
82
+ route.options.component = withModernRouteMatchContext(
83
+ resolvedComponent,
84
+ modernRouteId,
85
+ );
86
+ }
87
+ });
88
+ }),
89
+ ).then(() => undefined);
90
+ }
91
+
92
+ export function hydrateTanstackRouter(router: AnyRouter) {
93
+ return import('@tanstack/react-router/ssr/client').then(({ hydrate }) =>
94
+ hydrate(router),
95
+ );
96
+ }
97
+
98
+ function getTanstackSsrHydrationRecord(router: AnyRouter) {
99
+ const existingHydrationRecord = routerHydrationRecords.get(router);
100
+ if (existingHydrationRecord !== undefined) {
101
+ return existingHydrationRecord;
102
+ }
103
+
104
+ const hydrationRecord: RouterHydrationRecord = {
105
+ promise: Promise.resolve(),
106
+ status: 'pending',
107
+ };
108
+ routerHydrationRecords.set(router, hydrationRecord);
109
+ try {
110
+ hydrationRecord.promise = hydrateTanstackRouter(router)
111
+ .then(value => preloadHydratedRouteComponents(router).then(() => value))
112
+ .then(
113
+ value => {
114
+ hydrationRecord.status = 'fulfilled';
115
+ return value;
116
+ },
117
+ error => {
118
+ hydrationRecord.status = 'rejected';
119
+ hydrationRecord.error = error;
120
+ throw error;
121
+ },
122
+ );
123
+ } catch (error) {
124
+ hydrationRecord.status = 'rejected';
125
+ hydrationRecord.error = error;
126
+ hydrationRecord.promise = Promise.reject(error);
127
+ hydrationRecord.promise.catch(() => {});
128
+ }
129
+ return hydrationRecord;
130
+ }
131
+
132
+ export function getTanstackSsrHydrationPromise(router: AnyRouter) {
133
+ return getTanstackSsrHydrationRecord(router).promise;
134
+ }
135
+
136
+ export function hasTanstackSsrHydrationRecord(router: AnyRouter) {
137
+ return routerHydrationRecords.has(router);
138
+ }
139
+
140
+ export function ModernRouterClient({ router }: { router: AnyRouter }) {
141
+ const hydrationRecord = getTanstackSsrHydrationRecord(router);
142
+ if (hydrationRecord.status === 'pending') {
143
+ throw hydrationRecord.promise;
144
+ }
145
+ if (hydrationRecord.status === 'rejected') {
146
+ throw hydrationRecord.error;
147
+ }
148
+ return createElement(RouterProvider, { router });
149
+ }
@@ -1,317 +1,20 @@
1
1
  // @effect-diagnostics asyncFunction:off extendsNativeError:off strictBooleanExpressions:off
2
- import type { AnyRouter } from '@tanstack/react-router';
3
2
  import { useRouter } from '@tanstack/react-router';
4
3
  import type React from 'react';
5
4
  import { useCallback, useRef, useState } from 'react';
5
+ import type { SubmitOptions, SubmitTarget } from './formData';
6
+ import {
7
+ createFormDataFromSubmit,
8
+ getSubmitter,
9
+ resolveSubmitOptionsFromForm,
10
+ } from './formData';
11
+ import { submitRouteAction } from './submitAction';
6
12
 
7
- type SubmitTarget =
8
- | HTMLFormElement
9
- | FormData
10
- | URLSearchParams
11
- | Record<string, string | number | boolean | null | undefined>;
12
- type SubmitterElement = HTMLButtonElement | HTMLInputElement;
13
-
14
- export type SubmitOptions = {
15
- action?: string;
16
- method?: string;
17
- encType?: string;
18
- };
13
+ export type { SubmitOptions } from './formData';
14
+ export { RouteActionResponseError } from './submitAction';
19
15
 
20
16
  export type FetcherState = 'idle' | 'submitting' | 'loading';
21
17
 
22
- export class RouteActionResponseError<TData = unknown> extends Error {
23
- readonly response: Response;
24
- readonly data: TData;
25
-
26
- constructor(response: Response, data: TData) {
27
- super(`Route action failed with status ${response.status}`);
28
- this.name = 'RouteActionResponseError';
29
- this.response = response;
30
- this.data = data;
31
- }
32
- }
33
-
34
- type RouteAction = (args: {
35
- request: Request;
36
- params: Record<string, string>;
37
- context?: unknown;
38
- }) => Promise<unknown> | unknown;
39
-
40
- type RouteLoader = (args: {
41
- request: Request;
42
- params: Record<string, string>;
43
- context?: unknown;
44
- }) => Promise<unknown> | unknown;
45
-
46
- function formDataToUrlSearchParams(formData: FormData) {
47
- const searchParams = new URLSearchParams();
48
- formData.forEach((value, key) => {
49
- if (typeof value === 'string') {
50
- searchParams.append(key, value);
51
- }
52
- });
53
- return searchParams;
54
- }
55
-
56
- function formDataToTextPlain(formData: FormData) {
57
- return Array.from(formData.entries())
58
- .map(([key, value]) => `${key}=${String(value)}`)
59
- .join('\n');
60
- }
61
-
62
- function toFormData(target: SubmitTarget): FormData {
63
- if (target instanceof HTMLFormElement) {
64
- return new FormData(target);
65
- }
66
-
67
- if (target instanceof FormData) {
68
- return target;
69
- }
70
-
71
- if (target instanceof URLSearchParams) {
72
- const formData = new FormData();
73
- target.forEach((value, key) => {
74
- formData.append(key, value);
75
- });
76
- return formData;
77
- }
78
-
79
- const formData = new FormData();
80
- Object.entries(target).forEach(([key, value]) => {
81
- if (typeof value === 'undefined' || value === null) {
82
- return;
83
- }
84
- formData.append(key, String(value));
85
- });
86
- return formData;
87
- }
88
-
89
- function getSubmitter(event: React.FormEvent<HTMLFormElement>) {
90
- const nativeEvent = event.nativeEvent as SubmitEvent | undefined;
91
- const submitter = nativeEvent?.submitter;
92
- if (
93
- submitter instanceof HTMLButtonElement ||
94
- submitter instanceof HTMLInputElement
95
- ) {
96
- return submitter;
97
- }
98
- return null;
99
- }
100
-
101
- function createFormDataFromSubmit({
102
- form,
103
- submitter,
104
- }: {
105
- form: HTMLFormElement;
106
- submitter: SubmitterElement | null;
107
- }) {
108
- if (submitter) {
109
- try {
110
- return new FormData(form, submitter);
111
- } catch {}
112
- }
113
- return new FormData(form);
114
- }
115
-
116
- function resolveSubmitOptionsFromForm({
117
- form,
118
- submitter,
119
- action,
120
- method,
121
- encType,
122
- }: {
123
- form: HTMLFormElement;
124
- submitter: SubmitterElement | null;
125
- action?: string;
126
- method?: string;
127
- encType?: string;
128
- }): Required<SubmitOptions> {
129
- const resolvedAction =
130
- submitter?.getAttribute('formaction') ||
131
- action ||
132
- form.getAttribute('action') ||
133
- '.';
134
- const resolvedMethod = (
135
- submitter?.getAttribute('formmethod') ||
136
- method ||
137
- form.getAttribute('method') ||
138
- 'get'
139
- ).toLowerCase();
140
- const resolvedEncType =
141
- submitter?.getAttribute('formenctype') ||
142
- encType ||
143
- form.getAttribute('enctype') ||
144
- 'application/x-www-form-urlencoded';
145
-
146
- return {
147
- action: resolvedAction,
148
- method: resolvedMethod,
149
- encType: resolvedEncType,
150
- };
151
- }
152
-
153
- function resolveRouteHandlers(router: AnyRouter, actionTo: string) {
154
- const builtLocation = router.buildLocation({
155
- to: actionTo as any,
156
- } as any);
157
- const href = router.getParsedLocationHref(builtLocation as any);
158
- const matchedRoutes = router.getMatchedRoutes(
159
- (builtLocation as any).pathname,
160
- );
161
- const routeStaticData = matchedRoutes.foundRoute?.options?.staticData as
162
- | Record<string, unknown>
163
- | undefined;
164
- const action = routeStaticData?.modernRouteAction as RouteAction | undefined;
165
- const loader = routeStaticData?.modernRouteLoader as RouteLoader | undefined;
166
-
167
- return {
168
- action,
169
- loader,
170
- href,
171
- params: (matchedRoutes.routeParams || {}) as Record<string, string>,
172
- };
173
- }
174
-
175
- function isRedirectResponse(value: unknown): value is Response {
176
- if (!(value instanceof Response)) {
177
- return false;
178
- }
179
- return [301, 302, 303, 307, 308].includes(value.status);
180
- }
181
-
182
- async function parseResponseData(response: Response) {
183
- if (response.status === 204) {
184
- return null;
185
- }
186
-
187
- const contentType = response.headers.get('Content-Type') || '';
188
- if (contentType.includes('application/json')) {
189
- return response.json();
190
- }
191
- return response.text();
192
- }
193
-
194
- async function parseResponseResultOrThrow(response: Response) {
195
- const parsed = await parseResponseData(response);
196
- if (!response.ok) {
197
- throw new RouteActionResponseError(response, parsed);
198
- }
199
- return parsed;
200
- }
201
-
202
- async function submitRouteAction({
203
- router,
204
- target,
205
- options = {},
206
- isFetcher = false,
207
- onInvalidateStart,
208
- }: {
209
- router: AnyRouter;
210
- target: SubmitTarget;
211
- options?: SubmitOptions;
212
- isFetcher?: boolean;
213
- onInvalidateStart?: () => void;
214
- }) {
215
- const method = (options.method || 'post').toLowerCase();
216
- const encType = options.encType || 'application/x-www-form-urlencoded';
217
- const actionTo = options.action || '.';
218
- const formData = toFormData(target);
219
- const resolved = resolveRouteHandlers(router, actionTo);
220
-
221
- if (method === 'get') {
222
- const search = formDataToUrlSearchParams(formData).toString();
223
- const requestUrl = new URL(resolved.href, window.location.origin);
224
- requestUrl.search = search;
225
-
226
- if (isFetcher && resolved.loader) {
227
- const result = await resolved.loader({
228
- request: new Request(requestUrl, {
229
- method: 'GET',
230
- }),
231
- params: resolved.params,
232
- });
233
-
234
- if (result instanceof Response) {
235
- const redirectTo =
236
- result.headers.get('X-Modernjs-Redirect') ||
237
- result.headers.get('Location');
238
- if (redirectTo || isRedirectResponse(result)) {
239
- await router.navigate({
240
- to: (redirectTo || '/') as any,
241
- } as any);
242
- return parseResponseData(result);
243
- }
244
- return parseResponseResultOrThrow(result);
245
- }
246
-
247
- return result;
248
- }
249
-
250
- await router.navigate({
251
- href: search ? `${resolved.href}?${search}` : resolved.href,
252
- } as any);
253
- return;
254
- }
255
-
256
- if (!resolved.action) {
257
- throw new Error(`No route action found for "${actionTo}"`);
258
- }
259
-
260
- const headers = new Headers();
261
- let body: BodyInit | null = null;
262
- if (encType.includes('application/json')) {
263
- headers.set('Content-Type', 'application/json');
264
- body = JSON.stringify(
265
- Object.fromEntries(formDataToUrlSearchParams(formData).entries()),
266
- );
267
- } else if (encType.includes('text/plain')) {
268
- headers.set('Content-Type', 'text/plain;charset=UTF-8');
269
- body = formDataToTextPlain(formData);
270
- } else if (encType.includes('application/x-www-form-urlencoded')) {
271
- headers.set(
272
- 'Content-Type',
273
- 'application/x-www-form-urlencoded;charset=UTF-8',
274
- );
275
- body = formDataToUrlSearchParams(formData);
276
- } else {
277
- body = formData;
278
- }
279
-
280
- const request = new Request(new URL(resolved.href, window.location.origin), {
281
- method: method.toUpperCase(),
282
- headers,
283
- body,
284
- });
285
-
286
- const result = await resolved.action({
287
- request,
288
- params: resolved.params,
289
- });
290
-
291
- if (result instanceof Response) {
292
- const redirectTo =
293
- result.headers.get('X-Modernjs-Redirect') ||
294
- result.headers.get('Location');
295
- if (redirectTo || isRedirectResponse(result)) {
296
- await router.navigate({
297
- to: (redirectTo || '/') as any,
298
- } as any);
299
- return parseResponseData(result);
300
- }
301
-
302
- const parsed = isFetcher
303
- ? await parseResponseResultOrThrow(result)
304
- : await parseResponseData(result);
305
- onInvalidateStart?.();
306
- await router.invalidate();
307
- return parsed;
308
- }
309
-
310
- onInvalidateStart?.();
311
- await router.invalidate();
312
- return result;
313
- }
314
-
315
18
  export type FormProps = Omit<
316
19
  React.FormHTMLAttributes<HTMLFormElement>,
317
20
  'onSubmit' | 'action'
@@ -0,0 +1,153 @@
1
+ // @effect-diagnostics asyncFunction:off extendsNativeError:off strictBooleanExpressions:off
2
+ import type React from 'react';
3
+
4
+ export type SubmitTarget =
5
+ | HTMLFormElement
6
+ | FormData
7
+ | URLSearchParams
8
+ | Record<string, string | number | boolean | null | undefined>;
9
+ type SubmitterElement = HTMLButtonElement | HTMLInputElement;
10
+
11
+ export type SubmitOptions = {
12
+ action?: string;
13
+ method?: string;
14
+ encType?: string;
15
+ };
16
+
17
+ export function formDataToUrlSearchParams(formData: FormData) {
18
+ const searchParams = new URLSearchParams();
19
+ formData.forEach((value, key) => {
20
+ if (typeof value === 'string') {
21
+ searchParams.append(key, value);
22
+ }
23
+ });
24
+ return searchParams;
25
+ }
26
+
27
+ export function formDataToTextPlain(formData: FormData) {
28
+ return Array.from(formData.entries())
29
+ .map(([key, value]) => `${key}=${String(value)}`)
30
+ .join('\n');
31
+ }
32
+
33
+ export function toFormData(target: SubmitTarget): FormData {
34
+ if (target instanceof HTMLFormElement) {
35
+ return new FormData(target);
36
+ }
37
+
38
+ if (target instanceof FormData) {
39
+ return target;
40
+ }
41
+
42
+ if (target instanceof URLSearchParams) {
43
+ const formData = new FormData();
44
+ target.forEach((value, key) => {
45
+ formData.append(key, value);
46
+ });
47
+ return formData;
48
+ }
49
+
50
+ const formData = new FormData();
51
+ Object.entries(target).forEach(([key, value]) => {
52
+ if (typeof value === 'undefined' || value === null) {
53
+ return;
54
+ }
55
+ formData.append(key, String(value));
56
+ });
57
+ return formData;
58
+ }
59
+
60
+ export function getSubmitter(event: React.FormEvent<HTMLFormElement>) {
61
+ const nativeEvent = event.nativeEvent as SubmitEvent | undefined;
62
+ const submitter = nativeEvent?.submitter;
63
+ if (
64
+ submitter instanceof HTMLButtonElement ||
65
+ submitter instanceof HTMLInputElement
66
+ ) {
67
+ return submitter;
68
+ }
69
+ return null;
70
+ }
71
+
72
+ function appendSubmitterValue(
73
+ formData: FormData,
74
+ form: HTMLFormElement,
75
+ submitter: SubmitterElement,
76
+ ) {
77
+ if (submitter.form !== form || submitter.disabled) {
78
+ return;
79
+ }
80
+
81
+ if (submitter instanceof HTMLInputElement && submitter.type === 'image') {
82
+ const namePrefix = submitter.name ? `${submitter.name}.` : '';
83
+ formData.append(`${namePrefix}x`, '0');
84
+ formData.append(`${namePrefix}y`, '0');
85
+ return;
86
+ }
87
+
88
+ if (
89
+ !submitter.name ||
90
+ submitter.type === 'button' ||
91
+ submitter.type === 'reset'
92
+ ) {
93
+ return;
94
+ }
95
+
96
+ formData.append(submitter.name, submitter.value);
97
+ }
98
+
99
+ export function createFormDataFromSubmit({
100
+ form,
101
+ submitter,
102
+ }: {
103
+ form: HTMLFormElement;
104
+ submitter: SubmitterElement | null;
105
+ }) {
106
+ if (submitter) {
107
+ try {
108
+ return new FormData(form, submitter);
109
+ } catch {}
110
+ }
111
+ const formData = new FormData(form);
112
+ if (submitter) {
113
+ appendSubmitterValue(formData, form, submitter);
114
+ }
115
+ return formData;
116
+ }
117
+
118
+ export function resolveSubmitOptionsFromForm({
119
+ form,
120
+ submitter,
121
+ action,
122
+ method,
123
+ encType,
124
+ }: {
125
+ form: HTMLFormElement;
126
+ submitter: SubmitterElement | null;
127
+ action?: string;
128
+ method?: string;
129
+ encType?: string;
130
+ }): Required<SubmitOptions> {
131
+ const resolvedAction =
132
+ submitter?.getAttribute('formaction') ||
133
+ action ||
134
+ form.getAttribute('action') ||
135
+ '.';
136
+ const resolvedMethod = (
137
+ submitter?.getAttribute('formmethod') ||
138
+ method ||
139
+ form.getAttribute('method') ||
140
+ 'get'
141
+ ).toLowerCase();
142
+ const resolvedEncType =
143
+ submitter?.getAttribute('formenctype') ||
144
+ encType ||
145
+ form.getAttribute('enctype') ||
146
+ 'application/x-www-form-urlencoded';
147
+
148
+ return {
149
+ action: resolvedAction,
150
+ method: resolvedMethod,
151
+ encType: resolvedEncType,
152
+ };
153
+ }
@@ -9,6 +9,9 @@
9
9
  * helpers for the same reason.
10
10
  */
11
11
  import { notFound, redirect } from '@tanstack/react-router';
12
+ import { isAbsoluteUrl } from './shared/isAbsoluteUrl';
13
+
14
+ export { isAbsoluteUrl };
12
15
 
13
16
  /** Router context shape used by the generated TanStack router types. */
14
17
  export type ModernRouterContext = {
@@ -44,15 +47,6 @@ export function isTanstackRedirect(value: unknown): boolean {
44
47
  );
45
48
  }
46
49
 
47
- export function isAbsoluteUrl(value: string): boolean {
48
- try {
49
- void new URL(value);
50
- return true;
51
- } catch {
52
- return false;
53
- }
54
- }
55
-
56
50
  /**
57
51
  * Re-throw a Modern.js `Location` redirect as a TanStack redirect.
58
52
  *
@@ -232,9 +226,14 @@ export function modernLoaderToTanstack<TLoader extends (args: any) => any>(
232
226
  const href = getLoaderHref(ctx);
233
227
 
234
228
  const request =
235
- baseRequest !== undefined
236
- ? new Request(baseRequest, { signal })
237
- : new Request(href, { signal });
229
+ baseRequest !== undefined && href !== ''
230
+ ? new Request(new URL(href, baseRequest.url).href, {
231
+ headers: baseRequest.headers,
232
+ signal,
233
+ })
234
+ : baseRequest !== undefined
235
+ ? new Request(baseRequest, { signal })
236
+ : new Request(href, { signal });
238
237
 
239
238
  const params = mapSplatParamsForModernLoader(
240
239
  getLoaderParams(ctx),