@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
@@ -1,648 +1,5 @@
1
- // @effect-diagnostics asyncFunction:off nodeBuiltinImport:off strictBooleanExpressions:off
2
- import type { AppToolsContext } from '@modern-js/app-tools';
3
- import { getPathWithoutExt, makeLegalIdentifier } from '@modern-js/runtime/cli';
4
- import type { NestedRouteForCli, PageRoute } from '@modern-js/types';
5
- import { findExists, formatImportPath, slash } from '@modern-js/utils';
6
- import path from 'path';
1
+ // @effect-diagnostics asyncFunction:off strictBooleanExpressions:off
7
2
 
8
- const JS_OR_TS_EXTS = [
9
- '.js',
10
- '.jsx',
11
- '.ts',
12
- '.tsx',
13
- '.mjs',
14
- '.mts',
15
- '.cjs',
16
- '.cts',
17
- ] as const;
18
-
19
- function toTanstackPath(pathname: string): string {
20
- return pathname
21
- .split('/')
22
- .map(segment => {
23
- if (!segment) {
24
- return segment;
25
- }
26
- if (segment === '*') {
27
- return '$';
28
- }
29
- if (segment.startsWith(':')) {
30
- const name = segment.slice(1);
31
- if (name.endsWith('?')) {
32
- return `{-$${name.slice(0, -1)}}`;
33
- }
34
- return `$${name}`;
35
- }
36
- return segment;
37
- })
38
- .join('/');
39
- }
40
-
41
- async function resolveFileNoExt(inputNoExtPath: string) {
42
- const file = findExists(JS_OR_TS_EXTS.map(ext => `${inputNoExtPath}${ext}`));
43
- return file ? getPathWithoutExt(file) : null;
44
- }
45
-
46
- function quote(str: string) {
47
- return JSON.stringify(str);
48
- }
49
-
50
- function normalizeRelativeImport(p: string) {
51
- const normalized = formatImportPath(slash(p));
52
- if (normalized.startsWith('.')) {
53
- return normalized;
54
- }
55
- return `./${normalized}`;
56
- }
57
-
58
- function pickModernLoaderModule(route: NestedRouteForCli | PageRoute) {
59
- const loaderPath = (route as any).data || (route as any).loader;
60
- if (!loaderPath || typeof loaderPath !== 'string') {
61
- return null;
62
- }
63
-
64
- const inline = Boolean((route as any).data);
65
- return { loaderPath, inline };
66
- }
67
-
68
- function pickRouteSearchContractModules(route: NestedRouteForCli | PageRoute) {
69
- const validateSearchPath = (route as any).validateSearch;
70
- const loaderDepsPath = (route as any).loaderDeps;
71
-
72
- return {
73
- validateSearchPath:
74
- typeof validateSearchPath === 'string' ? validateSearchPath : null,
75
- loaderDepsPath: typeof loaderDepsPath === 'string' ? loaderDepsPath : null,
76
- };
77
- }
78
-
79
- function isPathlessLayout(route: NestedRouteForCli | PageRoute) {
80
- return (
81
- (route as any).type === 'nested' &&
82
- typeof (route as any).index !== 'boolean' &&
83
- typeof (route as any).path === 'undefined'
84
- );
85
- }
86
-
87
- function isIndexRoute(route: NestedRouteForCli | PageRoute) {
88
- return (route as any).type === 'nested' && Boolean((route as any).index);
89
- }
90
-
91
- function createRouteStaticDataSnippet(opts: {
92
- modernRouteId?: string;
93
- loaderName?: string | null;
94
- actionName?: string | null;
95
- }) {
96
- const staticDataLines: string[] = [];
97
-
98
- if (opts.modernRouteId) {
99
- staticDataLines.push(`modernRouteId: ${quote(opts.modernRouteId)},`);
100
- }
101
-
102
- if (opts.loaderName) {
103
- staticDataLines.push(`modernRouteLoader: ${opts.loaderName},`);
104
- }
105
-
106
- if (opts.actionName) {
107
- staticDataLines.push(`modernRouteAction: ${opts.actionName},`);
108
- }
109
-
110
- if (!staticDataLines.length) {
111
- return null;
112
- }
113
-
114
- return `staticData: createRouteStaticData({\n ${staticDataLines.join(
115
- '\n ',
116
- )}\n }),`;
117
- }
118
-
119
- const LOCALE_PARAM_SEGMENTS = new Set([
120
- ':lang',
121
- ':locale',
122
- ':language',
123
- '$lang',
124
- '$locale',
125
- '$language',
126
- ]);
127
-
128
- type CanonicalAwareRoute = (NestedRouteForCli | PageRoute) & {
129
- modernCanonicalPath?: string;
130
- index?: boolean;
131
- isRoot?: boolean;
132
- children?: CanonicalAwareRoute[];
133
- };
134
-
135
- function paramsTypeForCanonicalPath(canonicalPath: string): string {
136
- const fields: string[] = [];
137
-
138
- for (const segment of canonicalPath.split('/')) {
139
- if (!segment) {
140
- continue;
141
- }
142
- if (segment === '*' || segment === '$') {
143
- fields.push(`'_splat'?: string`);
144
- continue;
145
- }
146
- if (segment.startsWith('{-$') && segment.endsWith('}')) {
147
- fields.push(`${JSON.stringify(segment.slice(3, -1))}?: string`);
148
- continue;
149
- }
150
- if (segment.startsWith('$')) {
151
- fields.push(`${JSON.stringify(segment.slice(1))}: string`);
152
- continue;
153
- }
154
- if (segment.startsWith(':')) {
155
- const optional = segment.endsWith('?');
156
- const name = segment.slice(1, optional ? undefined : segment.length);
157
- fields.push(
158
- `${JSON.stringify(optional ? name.slice(0, -1) : name)}${
159
- optional ? '?' : ''
160
- }: string`,
161
- );
162
- }
163
- }
164
-
165
- return fields.length > 0
166
- ? `{ ${fields.join('; ')} }`
167
- : 'Record<string, never>';
168
- }
169
-
170
- export type CollectCanonicalRoutesOptions = {
171
- /**
172
- * Whether a leading `:lang`/`:locale`/`:language` route param may be
173
- * treated as an i18n locale prefix. This MUST only be enabled when
174
- * `@modern-js/plugin-i18n` is actually installed: the emitted
175
- * `declare module '@modern-js/plugin-i18n/runtime'` augmentation breaks
176
- * typechecking (TS2664) for apps that hand-roll a `/:lang/` param without
177
- * the plugin. Routes carrying `modernCanonicalPath` metadata are always
178
- * honored — only plugin-i18n produces them.
179
- */
180
- localeParamHeuristic?: boolean;
181
- };
182
-
183
- /**
184
- * Derive the canonical (language-agnostic) route map for an entry: the
185
- * leading locale param is stripped and localized physical variants (routes
186
- * carrying `modernCanonicalPath` metadata from `@modern-js/plugin-i18n`)
187
- * collapse to their canonical pattern. Returns `null` when the entry has no
188
- * i18n routing surface (no locale param and no localized variants), so plain
189
- * TanStack apps never get a `@modern-js/plugin-i18n` module augmentation.
190
- */
191
- export function collectCanonicalRoutesForEntry(
192
- routes: (NestedRouteForCli | PageRoute)[],
193
- options: CollectCanonicalRoutesOptions = {},
194
- ): Record<string, string> | null {
195
- const { localeParamHeuristic = true } = options;
196
- const canonicalParams = new Map<string, string>();
197
- let hasI18nSurface = false;
198
-
199
- const normalizeJoined = (joined: string): string => {
200
- const collapsed = joined.replace(/\/+/g, '/');
201
- const withLeading = collapsed.startsWith('/') ? collapsed : `/${collapsed}`;
202
- return withLeading.length > 1
203
- ? withLeading.replace(/\/+$/, '')
204
- : withLeading;
205
- };
206
-
207
- const record = (canonicalPath: string) => {
208
- const normalized = normalizeJoined(canonicalPath || '/');
209
- const key = toTanstackPath(normalized);
210
- if (!canonicalParams.has(key)) {
211
- canonicalParams.set(key, paramsTypeForCanonicalPath(normalized));
212
- }
213
- };
214
-
215
- const visit = (route: CanonicalAwareRoute, parentPath: string) => {
216
- let currentPath = parentPath;
217
-
218
- if (typeof route.modernCanonicalPath === 'string') {
219
- hasI18nSurface = true;
220
- currentPath = normalizeJoined(route.modernCanonicalPath);
221
- } else if (typeof route.path === 'string' && route.path.length > 0) {
222
- const segments = route.path
223
- .replace(/\[(.+?)\]/g, ':$1')
224
- .split('/')
225
- .filter(Boolean);
226
- if (
227
- localeParamHeuristic &&
228
- parentPath === '' &&
229
- LOCALE_PARAM_SEGMENTS.has(segments[0])
230
- ) {
231
- hasI18nSurface = true;
232
- segments.shift();
233
- }
234
- currentPath = segments.length
235
- ? normalizeJoined(`${parentPath}/${segments.join('/')}`)
236
- : parentPath;
237
- }
238
-
239
- const children = route.children;
240
- if (children && children.length > 0) {
241
- for (const child of children) {
242
- visit(child, currentPath);
243
- }
244
- return;
245
- }
246
-
247
- // Leaf page or index route: a navigable target.
248
- record(currentPath || '/');
249
- };
250
-
251
- const rootModern = routes.find(
252
- route => (route as CanonicalAwareRoute).isRoot,
253
- ) as CanonicalAwareRoute | undefined;
254
- const topLevel = rootModern ? (rootModern.children ?? []) : routes;
255
-
256
- for (const route of topLevel) {
257
- visit(route as CanonicalAwareRoute, '');
258
- }
259
-
260
- if (!hasI18nSurface || canonicalParams.size === 0) {
261
- return null;
262
- }
263
-
264
- return Object.fromEntries(
265
- [...canonicalParams.entries()].sort(([a], [b]) => a.localeCompare(b)),
266
- );
267
- }
268
-
269
- export async function generateTanstackRouterTypesSourceForEntry(opts: {
270
- appContext: AppToolsContext;
271
- entryName: string;
272
- generatedDirName?: string;
273
- routes: (NestedRouteForCli | PageRoute)[];
274
- }): Promise<{
275
- routerGenTs: string;
276
- }> {
277
- const {
278
- appContext,
279
- entryName,
280
- generatedDirName = 'modern-tanstack',
281
- routes,
282
- } = opts;
283
- const outDir = path.join(
284
- appContext.srcDirectory,
285
- generatedDirName,
286
- entryName,
287
- );
288
-
289
- const rootModern = routes.find(
290
- r => r && (r as any).type === 'nested' && (r as any).isRoot,
291
- ) as NestedRouteForCli | undefined;
292
-
293
- const topLevel = rootModern
294
- ? ((rootModern as any).children as Array<NestedRouteForCli | PageRoute>) ||
295
- []
296
- : routes;
297
-
298
- const imports: string[] = [];
299
- const statements: string[] = [];
300
-
301
- const loaderImportMap = new Map<string, string>();
302
- const componentImportMap = new Map<string, Promise<string | null>>();
303
- const searchContractImportMap = new Map<string, string>();
304
- const usedRouteVarNames = new Set<string>();
305
- let loaderIndex = 0;
306
- let componentIndex = 0;
307
- let validateSearchIndex = 0;
308
- let loaderDepsIndex = 0;
309
- let routeIndex = 0;
310
-
311
- const getImportNameForComponent = (
312
- componentPath: unknown,
313
- ): Promise<string | null> => {
314
- if (typeof componentPath !== 'string' || componentPath.length === 0) {
315
- return Promise.resolve(null);
316
- }
317
-
318
- // Cache the in-flight promise: sibling routes sharing a component module
319
- // are generated concurrently and must reuse one import.
320
- let pendingImportName = componentImportMap.get(componentPath);
321
- if (!pendingImportName) {
322
- pendingImportName = (async () => {
323
- // Resolve through the same machinery as loaders: the raw `_component`
324
- // value carries the internal `@_modern_js_src` alias, which the app's
325
- // tsconfig does not map — the generated file must use relative
326
- // imports.
327
- const resolvedNoExt = await resolveRouteModuleNoExt(componentPath);
328
- if (!resolvedNoExt) {
329
- return null;
330
- }
331
-
332
- const relImport = normalizeRelativeImport(
333
- path.relative(outDir, resolvedNoExt),
334
- );
335
-
336
- const componentName = `component_${componentIndex++}`;
337
- imports.push(`import ${componentName} from ${quote(relImport)};`);
338
- return componentName;
339
- })();
340
- componentImportMap.set(componentPath, pendingImportName);
341
- }
342
-
343
- return pendingImportName;
344
- };
345
-
346
- const resolveRouteModuleNoExt = async (aliasedNoExtPath: string) => {
347
- const prefix = `${appContext.internalSrcAlias}/`;
348
- let absNoExt: string;
349
- if (aliasedNoExtPath.startsWith(prefix)) {
350
- const rel = aliasedNoExtPath.slice(prefix.length);
351
- absNoExt = path.join(appContext.srcDirectory, rel);
352
- } else if (path.isAbsolute(aliasedNoExtPath)) {
353
- absNoExt = aliasedNoExtPath;
354
- } else {
355
- // Unknown format; treat as already relative to src.
356
- absNoExt = path.join(appContext.srcDirectory, aliasedNoExtPath);
357
- }
358
-
359
- return resolveFileNoExt(absNoExt);
360
- };
361
-
362
- const getImportNamesForLoader = async (
363
- aliasedNoExtPath: string,
364
- inline: boolean,
365
- hasAction: boolean,
366
- ) => {
367
- const key = `${
368
- inline ? 'inline' : 'default'
369
- }:${hasAction ? 'action' : 'loader'}:${aliasedNoExtPath}`;
370
- const existing = loaderImportMap.get(key);
371
- if (existing) {
372
- return {
373
- loaderName: existing,
374
- actionName: hasAction ? existing.replace(/^loader_/, 'action_') : null,
375
- };
376
- }
377
-
378
- const resolvedNoExt = await resolveRouteModuleNoExt(aliasedNoExtPath);
379
- if (!resolvedNoExt) {
380
- return null;
381
- }
382
-
383
- const relImport = normalizeRelativeImport(
384
- path.relative(outDir, resolvedNoExt),
385
- );
386
-
387
- const importName = `loader_${loaderIndex++}`;
388
- const actionName = hasAction
389
- ? importName.replace(/^loader_/, 'action_')
390
- : null;
391
- if (inline) {
392
- const specifiers = [`loader as ${importName}`];
393
- if (actionName) {
394
- specifiers.push(`action as ${actionName}`);
395
- }
396
- imports.push(
397
- `import { ${specifiers.join(', ')} } from ${quote(relImport)};`,
398
- );
399
- } else {
400
- imports.push(`import ${importName} from ${quote(relImport)};`);
401
- }
402
-
403
- loaderImportMap.set(key, importName);
404
- return { loaderName: importName, actionName };
405
- };
406
-
407
- const getImportNameForSearchContract = async (
408
- aliasedNoExtPath: string,
409
- exportName: 'validateSearch' | 'loaderDeps',
410
- ) => {
411
- const key = `${exportName}:${aliasedNoExtPath}`;
412
- const existing = searchContractImportMap.get(key);
413
- if (existing) {
414
- return existing;
415
- }
416
-
417
- const resolvedNoExt = await resolveRouteModuleNoExt(aliasedNoExtPath);
418
- if (!resolvedNoExt) {
419
- return null;
420
- }
421
-
422
- const relImport = normalizeRelativeImport(
423
- path.relative(outDir, resolvedNoExt),
424
- );
425
- const importName =
426
- exportName === 'validateSearch'
427
- ? `validateSearch_${validateSearchIndex++}`
428
- : `loaderDeps_${loaderDepsIndex++}`;
429
- imports.push(
430
- `import { ${exportName} as ${importName} } from ${quote(relImport)};`,
431
- );
432
- searchContractImportMap.set(key, importName);
433
- return importName;
434
- };
435
-
436
- const reserveRouteVarName = (preferred: string) => {
437
- let candidate = preferred;
438
- let suffix = 1;
439
- while (usedRouteVarNames.has(candidate)) {
440
- candidate = `${preferred}_${suffix++}`;
441
- }
442
- usedRouteVarNames.add(candidate);
443
- return candidate;
444
- };
445
-
446
- const createRouteVarName = (route: NestedRouteForCli | PageRoute) => {
447
- const id = (route as any).id as string | undefined;
448
- const base = id ? makeLegalIdentifier(id) : `r_${routeIndex++}`;
449
- return reserveRouteVarName(`route_${base}`);
450
- };
451
-
452
- const buildRoute = async (opts: {
453
- parentVar: string;
454
- route: NestedRouteForCli | PageRoute;
455
- }): Promise<string> => {
456
- const { parentVar, route } = opts;
457
-
458
- const varName = createRouteVarName(route);
459
-
460
- const loaderInfo = pickModernLoaderModule(route);
461
- const routeAction = (route as any).action;
462
- const loaderImports = loaderInfo
463
- ? await getImportNamesForLoader(
464
- loaderInfo.loaderPath,
465
- loaderInfo.inline,
466
- Boolean(loaderInfo.inline && routeAction === loaderInfo.loaderPath),
467
- )
468
- : null;
469
- const loaderName = loaderImports?.loaderName || null;
470
- const actionName = loaderImports?.actionName || null;
471
- const searchContractInfo = pickRouteSearchContractModules(route);
472
- const validateSearchName = searchContractInfo.validateSearchPath
473
- ? await getImportNameForSearchContract(
474
- searchContractInfo.validateSearchPath,
475
- 'validateSearch',
476
- )
477
- : null;
478
- const loaderDepsName = searchContractInfo.loaderDepsPath
479
- ? await getImportNameForSearchContract(
480
- searchContractInfo.loaderDepsPath,
481
- 'loaderDeps',
482
- )
483
- : null;
484
-
485
- const rawPath = (route as any).path as string | undefined;
486
- const hasSplat = typeof rawPath === 'string' && rawPath.includes('*');
487
-
488
- const routeOpts: string[] = [`getParentRoute: () => ${parentVar},`];
489
-
490
- const componentName = await getImportNameForComponent(
491
- (route as any)._component,
492
- );
493
- if (componentName) {
494
- routeOpts.push(`component: ${componentName},`);
495
- }
496
-
497
- if (isPathlessLayout(route)) {
498
- const id = (route as any).id as string | undefined;
499
- routeOpts.push(`id: ${quote(id || 'pathless')},`);
500
- } else {
501
- const p = isIndexRoute(route) ? '/' : toTanstackPath(rawPath || '');
502
- routeOpts.push(`path: ${quote(p)},`);
503
- }
504
-
505
- if (loaderName) {
506
- routeOpts.push(
507
- `loader: modernLoaderToTanstack({ hasSplat: ${hasSplat} }, ${loaderName}),`,
508
- );
509
- }
510
- if (validateSearchName) {
511
- routeOpts.push(`validateSearch: ${validateSearchName},`);
512
- }
513
- if (loaderDepsName) {
514
- routeOpts.push(`loaderDeps: ${loaderDepsName},`);
515
- }
516
-
517
- const staticDataSnippet = createRouteStaticDataSnippet({
518
- modernRouteId: (route as any).id as string | undefined,
519
- loaderName,
520
- actionName,
521
- });
522
- if (staticDataSnippet) {
523
- routeOpts.push(staticDataSnippet);
524
- }
525
-
526
- const children = (route as any).children as
527
- | Array<NestedRouteForCli | PageRoute>
528
- | undefined;
529
- const hasChildren = Boolean(children && children.length > 0);
530
- const routeCtorVarName = hasChildren
531
- ? reserveRouteVarName(`${varName}__base`)
532
- : varName;
533
-
534
- statements.push(
535
- `const ${routeCtorVarName} = createRoute({\n ${routeOpts.join('\n ')}\n});`,
536
- );
537
-
538
- if (children && children.length > 0) {
539
- const childVars = await Promise.all(
540
- children.map(child =>
541
- buildRoute({ parentVar: routeCtorVarName, route: child }),
542
- ),
543
- );
544
- statements.push(
545
- `const ${varName} = ${routeCtorVarName}.addChildren([${childVars.join(', ')}]);`,
546
- );
547
- }
548
-
549
- return varName;
550
- };
551
-
552
- const rootLoaderInfo = rootModern ? pickModernLoaderModule(rootModern) : null;
553
- const rootAction = (rootModern as any)?.action;
554
- const rootLoaderImports = rootLoaderInfo?.loaderPath
555
- ? await getImportNamesForLoader(
556
- rootLoaderInfo.loaderPath,
557
- rootLoaderInfo.inline,
558
- Boolean(
559
- rootLoaderInfo.inline && rootAction === rootLoaderInfo.loaderPath,
560
- ),
561
- )
562
- : null;
563
- const rootLoaderName = rootLoaderImports?.loaderName || null;
564
- const rootActionName = rootLoaderImports?.actionName || null;
565
- const rootSearchContractInfo = rootModern
566
- ? pickRouteSearchContractModules(rootModern)
567
- : null;
568
- const rootValidateSearchName = rootSearchContractInfo?.validateSearchPath
569
- ? await getImportNameForSearchContract(
570
- rootSearchContractInfo.validateSearchPath,
571
- 'validateSearch',
572
- )
573
- : null;
574
- const rootLoaderDepsName = rootSearchContractInfo?.loaderDepsPath
575
- ? await getImportNameForSearchContract(
576
- rootSearchContractInfo.loaderDepsPath,
577
- 'loaderDeps',
578
- )
579
- : null;
580
-
581
- const topLevelVars = await Promise.all(
582
- topLevel.map(route => buildRoute({ parentVar: 'rootRoute', route })),
583
- );
584
-
585
- const rootOpts: string[] = [];
586
-
587
- const rootComponentName = await getImportNameForComponent(
588
- (rootModern as any)?._component,
589
- );
590
- if (rootComponentName) {
591
- rootOpts.push(`component: ${rootComponentName},`);
592
- }
593
-
594
- if (rootLoaderName) {
595
- rootOpts.push(
596
- `loader: modernLoaderToTanstack({ hasSplat: false }, ${rootLoaderName}),`,
597
- );
598
- }
599
- if (rootValidateSearchName) {
600
- rootOpts.push(`validateSearch: ${rootValidateSearchName},`);
601
- }
602
- if (rootLoaderDepsName) {
603
- rootOpts.push(`loaderDeps: ${rootLoaderDepsName},`);
604
- }
605
-
606
- const routerGenTs = `/* eslint-disable */
607
- // This file is auto-generated by Modern.js. Do not edit manually.
608
-
609
- import {
610
- createMemoryHistory,
611
- createRootRouteWithContext,
612
- createRoute,
613
- createRouter,
614
- createRouteStaticData,
615
- type ModernRouterContext,
616
- modernLoaderToTanstack,
617
- modernTanstackRouterFastDefaults,
618
- } from '@modern-js/plugin-tanstack/runtime';
619
-
620
- ${imports.join('\n')}
621
-
622
- export const rootRoute = createRootRouteWithContext<ModernRouterContext>()({
623
- ${rootOpts.join('\n ')}
624
- ${
625
- createRouteStaticDataSnippet({
626
- modernRouteId: (rootModern as any)?.id as string | undefined,
627
- loaderName: rootLoaderName,
628
- actionName: rootActionName,
629
- }) || ''
630
- }
631
- });
632
-
633
- ${statements.join('\n\n')}
634
-
635
- export const routeTree = rootRoute.addChildren([${topLevelVars.join(', ')}]);
636
-
637
- export const router = createRouter({
638
- ...modernTanstackRouterFastDefaults,
639
- routeTree,
640
- history: createMemoryHistory({
641
- initialEntries: ['/'],
642
- }),
643
- context: {} as ModernRouterContext,
644
- });
645
- `;
646
-
647
- return { routerGenTs };
648
- }
3
+ export type { CollectCanonicalRoutesOptions } from './tanstackTypes/canonicalRoutes';
4
+ export { collectCanonicalRoutesForEntry } from './tanstackTypes/canonicalRoutes';
5
+ export { generateTanstackRouterTypesSourceForEntry } from './tanstackTypes/source';
@@ -0,0 +1,48 @@
1
+ // @effect-diagnostics strictBooleanExpressions:off
2
+
3
+ const BLOCKING_SUBSCRIBE_SYMBOL = Symbol.for(
4
+ '@modern-js/plugin-tanstack:blocking-subscribe',
5
+ );
6
+ const BLOCKING_STATE_SYMBOL = Symbol.for(
7
+ '@modern-js/plugin-tanstack:blocking-state',
8
+ );
9
+
10
+ type TanstackRouterWithSubscribe = {
11
+ [BLOCKING_STATE_SYMBOL]?: () => boolean;
12
+ [BLOCKING_SUBSCRIBE_SYMBOL]?: boolean;
13
+ subscribe?: (
14
+ eventType: string,
15
+ listener: (...args: unknown[]) => void,
16
+ ) => () => void;
17
+ };
18
+
19
+ export function wrapRouterSubscribeWithBlockState(
20
+ router: unknown,
21
+ getBlockNavState?: () => boolean,
22
+ ) {
23
+ if (!router || typeof router !== 'object') {
24
+ return;
25
+ }
26
+
27
+ const target = router as TanstackRouterWithSubscribe;
28
+ target[BLOCKING_STATE_SYMBOL] = getBlockNavState;
29
+ if (
30
+ target[BLOCKING_SUBSCRIBE_SYMBOL] ||
31
+ typeof target.subscribe !== 'function'
32
+ ) {
33
+ return;
34
+ }
35
+
36
+ const originSubscribe = target.subscribe.bind(target);
37
+ target.subscribe = (eventType, listener) => {
38
+ const wrappedListener = (...args: unknown[]) => {
39
+ const blockRoute = target[BLOCKING_STATE_SYMBOL]?.() || false;
40
+ if (blockRoute) {
41
+ return;
42
+ }
43
+ return listener(...args);
44
+ };
45
+ return originSubscribe(eventType, wrappedListener);
46
+ };
47
+ target[BLOCKING_SUBSCRIBE_SYMBOL] = true;
48
+ }
@@ -0,0 +1,12 @@
1
+ function normalizeBase(b: string) {
2
+ if (b.length > 1 && b.endsWith('/')) {
3
+ return b.slice(0, -1);
4
+ }
5
+ return b || '/';
6
+ }
7
+
8
+ export function isSegmentPrefix(pathname: string, base: string) {
9
+ const b = normalizeBase(base);
10
+ const p = pathname || '/';
11
+ return p === b || p.startsWith(`${b}/`);
12
+ }