@bleedingdev/modern-js-plugin-tanstack 3.2.0-ultramodern.99 → 3.4.0-ultramodern.1

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 (127) hide show
  1. package/dist/cjs/cli/index.js +80 -34
  2. package/dist/cjs/cli/routeSplitting.js +13 -41
  3. package/dist/cjs/cli/tanstackTypes.js +114 -200
  4. package/dist/cjs/cli.js +12 -8
  5. package/dist/cjs/runtime/basepathRewrite.js +12 -8
  6. package/dist/cjs/runtime/dataMutation.js +9 -5
  7. package/dist/cjs/runtime/hooks.js +20 -19
  8. package/dist/cjs/runtime/hydrationBoundary.js +12 -7
  9. package/dist/cjs/runtime/index.js +66 -54
  10. package/dist/cjs/runtime/lifecycle.js +21 -91
  11. package/dist/cjs/runtime/loaderBridge.js +173 -0
  12. package/dist/cjs/runtime/outlet.js +58 -0
  13. package/dist/cjs/runtime/plugin.js +190 -110
  14. package/dist/cjs/runtime/plugin.node.js +25 -40
  15. package/dist/cjs/runtime/plugin.worker.js +9 -5
  16. package/dist/cjs/runtime/pluginCore.js +55 -0
  17. package/dist/cjs/runtime/prefetchLink.js +10 -6
  18. package/dist/cjs/runtime/register.js +56 -0
  19. package/dist/cjs/runtime/routeTree.js +30 -212
  20. package/dist/cjs/runtime/router.js +41 -0
  21. package/dist/cjs/runtime/rsc/ClientSlot.js +9 -5
  22. package/dist/cjs/runtime/rsc/CompositeComponent.js +9 -5
  23. package/dist/cjs/runtime/rsc/ReplayableStream.js +14 -9
  24. package/dist/cjs/runtime/rsc/RscNodeRenderer.js +9 -5
  25. package/dist/cjs/runtime/rsc/SlotContext.js +9 -5
  26. package/dist/cjs/runtime/rsc/client.js +9 -5
  27. package/dist/cjs/runtime/rsc/createRscProxy.js +9 -5
  28. package/dist/cjs/runtime/rsc/index.js +9 -5
  29. package/dist/cjs/runtime/rsc/payloadRouter.js +44 -6
  30. package/dist/cjs/runtime/rsc/server.js +9 -5
  31. package/dist/cjs/runtime/rsc/slotUsageSanitizer.js +9 -5
  32. package/dist/cjs/runtime/rsc/symbols.js +20 -15
  33. package/dist/cjs/runtime/state.js +45 -0
  34. package/dist/cjs/runtime/types.js +13 -9
  35. package/dist/cjs/runtime/utils.js +9 -11
  36. package/dist/cjs/runtime.js +9 -5
  37. package/dist/esm/cli/index.mjs +73 -28
  38. package/dist/esm/cli/routeSplitting.mjs +1 -30
  39. package/dist/esm/cli/tanstackTypes.mjs +100 -190
  40. package/dist/esm/runtime/hooks.mjs +1 -8
  41. package/dist/esm/runtime/hydrationBoundary.mjs +3 -2
  42. package/dist/esm/runtime/index.mjs +4 -2
  43. package/dist/esm/runtime/lifecycle.mjs +1 -82
  44. package/dist/esm/runtime/loaderBridge.mjs +114 -0
  45. package/dist/esm/runtime/outlet.mjs +17 -0
  46. package/dist/esm/runtime/plugin.mjs +183 -107
  47. package/dist/esm/runtime/plugin.node.mjs +19 -38
  48. package/dist/esm/runtime/pluginCore.mjs +14 -0
  49. package/dist/esm/runtime/prefetchLink.mjs +1 -1
  50. package/dist/esm/runtime/register.mjs +18 -0
  51. package/dist/esm/runtime/routeTree.mjs +15 -198
  52. package/dist/esm/runtime/router.mjs +2 -0
  53. package/dist/esm/runtime/rsc/payloadRouter.mjs +35 -1
  54. package/dist/esm/runtime/state.mjs +7 -0
  55. package/dist/esm/runtime/utils.mjs +0 -6
  56. package/dist/esm-node/cli/index.mjs +73 -28
  57. package/dist/esm-node/cli/routeSplitting.mjs +1 -30
  58. package/dist/esm-node/cli/tanstackTypes.mjs +100 -190
  59. package/dist/esm-node/runtime/hooks.mjs +1 -8
  60. package/dist/esm-node/runtime/hydrationBoundary.mjs +3 -2
  61. package/dist/esm-node/runtime/index.mjs +4 -2
  62. package/dist/esm-node/runtime/lifecycle.mjs +1 -82
  63. package/dist/esm-node/runtime/loaderBridge.mjs +115 -0
  64. package/dist/esm-node/runtime/outlet.mjs +18 -0
  65. package/dist/esm-node/runtime/plugin.mjs +183 -107
  66. package/dist/esm-node/runtime/plugin.node.mjs +19 -38
  67. package/dist/esm-node/runtime/pluginCore.mjs +15 -0
  68. package/dist/esm-node/runtime/prefetchLink.mjs +1 -1
  69. package/dist/esm-node/runtime/register.mjs +19 -0
  70. package/dist/esm-node/runtime/routeTree.mjs +15 -198
  71. package/dist/esm-node/runtime/router.mjs +3 -0
  72. package/dist/esm-node/runtime/rsc/payloadRouter.mjs +35 -1
  73. package/dist/esm-node/runtime/state.mjs +8 -0
  74. package/dist/esm-node/runtime/utils.mjs +0 -6
  75. package/dist/types/cli/index.d.ts +11 -2
  76. package/dist/types/cli/routeSplitting.d.ts +6 -15
  77. package/dist/types/cli/tanstackTypes.d.ts +21 -1
  78. package/dist/types/runtime/hooks.d.ts +8 -33
  79. package/dist/types/runtime/index.d.ts +6 -3
  80. package/dist/types/runtime/lifecycle.d.ts +7 -22
  81. package/dist/types/runtime/loaderBridge.d.ts +48 -0
  82. package/dist/types/runtime/outlet.d.ts +2 -0
  83. package/dist/types/runtime/plugin.d.ts +1 -14
  84. package/dist/types/runtime/plugin.node.d.ts +1 -14
  85. package/dist/types/runtime/pluginCore.d.ts +21 -0
  86. package/dist/types/runtime/register.d.ts +9 -0
  87. package/dist/types/runtime/routeTree.d.ts +0 -2
  88. package/dist/types/runtime/router.d.ts +14 -0
  89. package/dist/types/runtime/state.d.ts +16 -0
  90. package/dist/types/runtime/types.d.ts +7 -53
  91. package/package.json +41 -39
  92. package/rstest.config.mts +6 -0
  93. package/src/cli/index.ts +146 -24
  94. package/src/cli/routeSplitting.ts +6 -44
  95. package/src/cli/tanstackTypes.ts +203 -208
  96. package/src/runtime/hooks.ts +10 -27
  97. package/src/runtime/hydrationBoundary.tsx +5 -5
  98. package/src/runtime/index.tsx +12 -14
  99. package/src/runtime/lifecycle.ts +16 -151
  100. package/src/runtime/loaderBridge.ts +257 -0
  101. package/src/runtime/outlet.tsx +48 -0
  102. package/src/runtime/plugin.node.tsx +28 -83
  103. package/src/runtime/plugin.tsx +350 -202
  104. package/src/runtime/pluginCore.ts +48 -0
  105. package/src/runtime/prefetchLink.tsx +1 -1
  106. package/src/runtime/register.ts +58 -0
  107. package/src/runtime/routeTree.ts +41 -365
  108. package/src/runtime/router.ts +15 -0
  109. package/src/runtime/rsc/payloadRouter.ts +45 -2
  110. package/src/runtime/ssr-shim.d.ts +1 -3
  111. package/src/runtime/state.ts +29 -0
  112. package/src/runtime/types.ts +20 -67
  113. package/src/runtime/utils.tsx +3 -7
  114. package/tests/router/cli.test.ts +373 -31
  115. package/tests/router/hooks.test.ts +26 -0
  116. package/tests/router/loaderBridge.test.ts +211 -0
  117. package/tests/router/packageSurface.test.ts +24 -0
  118. package/tests/router/prefetchLink.test.tsx +43 -7
  119. package/tests/router/register.test.ts +46 -0
  120. package/tests/router/routeTree.test.ts +176 -139
  121. package/tests/router/rsc.test.tsx +70 -0
  122. package/tests/router/tanstackTypes.test.ts +395 -7
  123. package/dist/cjs/runtime/DefaultNotFound.js +0 -47
  124. package/dist/esm/runtime/DefaultNotFound.mjs +0 -13
  125. package/dist/esm-node/runtime/DefaultNotFound.mjs +0 -14
  126. package/dist/types/runtime/DefaultNotFound.d.ts +0 -2
  127. package/src/runtime/DefaultNotFound.tsx +0 -15
@@ -1,15 +1,15 @@
1
1
  import "node:module";
2
2
  import { jsx } from "react/jsx-runtime";
3
- import { InternalRuntimeContext, getGlobalEnableRsc, getGlobalLayoutApp, getGlobalRoutes } from "@modern-js/runtime/context";
4
- import { merge } from "@modern-js/runtime-utils/merge";
3
+ import { InternalRuntimeContext, getGlobalEnableRsc } from "@modern-js/runtime/context";
5
4
  import { normalizePathname } from "@modern-js/runtime-utils/url";
6
5
  import { RouterProvider, createBrowserHistory, createHashHistory, createRouter, useLocation, useMatches, useNavigate, useRouter } from "@tanstack/react-router";
7
- import { RouterClient } from "@tanstack/react-router/ssr/client";
8
6
  import { useContext, useMemo } from "react";
9
7
  import { createModernBasepathRewrite } from "./basepathRewrite.mjs";
10
- import { modifyRoutes, onAfterCreateRouter, onAfterHydrateRouter, onBeforeCreateRouter, onBeforeCreateRoutes, onBeforeHydrateRouter } from "./hooks.mjs";
8
+ import { routerProviderRegistryHooks } from "./hooks.mjs";
11
9
  import { wrapTanstackSsrHydrationBoundary } from "./hydrationBoundary.mjs";
12
10
  import { applyRouterRuntimeState } from "./lifecycle.mjs";
11
+ import { withModernRouteMatchContext } from "./outlet.mjs";
12
+ import { getFinalRouteConfig, getMergedRouterConfig } from "./pluginCore.mjs";
13
13
  import { Link } from "./prefetchLink.mjs";
14
14
  import { createRouteTreeFromRouteObjects } from "./routeTree.mjs";
15
15
  import { getTanstackRscSerializationAdapters } from "./rsc/client.mjs";
@@ -42,35 +42,178 @@ function wrapRouterSubscribeWithBlockState(router, getBlockNavState) {
42
42
  };
43
43
  target[BLOCKING_SUBSCRIBE_SYMBOL] = true;
44
44
  }
45
- function stripSyntheticNotFoundRoute(routes) {
46
- return routes.filter((route)=>!('*' === route.path && !route.id && !route.loader)).map((route)=>{
47
- if (!route.children?.length) return route;
48
- return {
49
- ...route,
50
- children: stripSyntheticNotFoundRoute(route.children)
51
- };
45
+ const routerHydrationRecords = new WeakMap();
46
+ const routeModulesKey = '_routeModules';
47
+ function pickRouteModuleComponent(routeModule, seen = new Set()) {
48
+ if ('function' == typeof routeModule || routeModule && 'object' == typeof routeModule && '$$typeof' in routeModule) return routeModule;
49
+ if (!routeModule || 'object' != typeof routeModule) return;
50
+ if (seen.has(routeModule)) return;
51
+ seen.add(routeModule);
52
+ const module = routeModule;
53
+ for (const candidate of [
54
+ module.default,
55
+ module.Component
56
+ ]){
57
+ const component = pickRouteModuleComponent(candidate, seen);
58
+ if (component) return component;
59
+ }
60
+ }
61
+ function getCachedRouteModule(routeId) {
62
+ if ("u" < typeof window) return;
63
+ return window[routeModulesKey]?.[routeId];
64
+ }
65
+ function preloadHydratedRouteComponents(router) {
66
+ const preloadableRouter = router;
67
+ const routesById = preloadableRouter.routesById || {};
68
+ const matches = preloadableRouter.stores.matches.get();
69
+ return Promise.all(matches.map((match)=>{
70
+ if (void 0 === match.routeId || '' === match.routeId) return;
71
+ const route = routesById[match.routeId];
72
+ const component = route?.options?.component;
73
+ const preload = component?.load || component?.preload;
74
+ if ('function' != typeof preload) return;
75
+ return Promise.resolve(preload.call(component)).then((routeModule)=>{
76
+ const modernRouteId = route?.options?.staticData?.modernRouteId;
77
+ const cachedRouteModule = 'string' == typeof modernRouteId && '' !== modernRouteId ? getCachedRouteModule(modernRouteId) : void 0;
78
+ const resolvedComponent = pickRouteModuleComponent(cachedRouteModule ?? routeModule);
79
+ if (void 0 !== resolvedComponent && 'string' == typeof modernRouteId && '' !== modernRouteId) route.options.component = withModernRouteMatchContext(resolvedComponent, modernRouteId);
80
+ });
81
+ })).then(()=>void 0);
82
+ }
83
+ function hydrateTanstackRouter(router) {
84
+ return import("@tanstack/react-router/ssr/client").then(({ hydrate })=>hydrate(router));
85
+ }
86
+ function getTanstackSsrHydrationRecord(router) {
87
+ const existingHydrationRecord = routerHydrationRecords.get(router);
88
+ if (void 0 !== existingHydrationRecord) return existingHydrationRecord;
89
+ const hydrationRecord = {
90
+ promise: Promise.resolve(),
91
+ status: 'pending'
92
+ };
93
+ routerHydrationRecords.set(router, hydrationRecord);
94
+ try {
95
+ hydrationRecord.promise = hydrateTanstackRouter(router).then((value)=>preloadHydratedRouteComponents(router).then(()=>value)).then((value)=>{
96
+ hydrationRecord.status = 'fulfilled';
97
+ return value;
98
+ }, (error)=>{
99
+ hydrationRecord.status = 'rejected';
100
+ hydrationRecord.error = error;
101
+ throw error;
102
+ });
103
+ } catch (error) {
104
+ hydrationRecord.status = 'rejected';
105
+ hydrationRecord.error = error;
106
+ hydrationRecord.promise = Promise.reject(error);
107
+ hydrationRecord.promise.catch(()=>{});
108
+ }
109
+ return hydrationRecord;
110
+ }
111
+ function getTanstackSsrHydrationPromise(router) {
112
+ return getTanstackSsrHydrationRecord(router).promise;
113
+ }
114
+ function hasTanstackSsrHydrationRecord(router) {
115
+ return routerHydrationRecords.has(router);
116
+ }
117
+ function ModernRouterClient({ router }) {
118
+ const hydrationRecord = getTanstackSsrHydrationRecord(router);
119
+ if ('rejected' === hydrationRecord.status) throw hydrationRecord.error;
120
+ return /*#__PURE__*/ jsx(RouterProvider, {
121
+ router: router
52
122
  });
53
123
  }
54
124
  const tanstackRouterPlugin = (userConfig = {})=>{
55
125
  const plugin = {
56
126
  name: '@modern-js/plugin-router-tanstack',
57
- registryHooks: {
58
- modifyRoutes: modifyRoutes,
59
- onAfterCreateRouter: onAfterCreateRouter,
60
- onAfterHydrateRouter: onAfterHydrateRouter,
61
- onBeforeCreateRouter: onBeforeCreateRouter,
62
- onBeforeCreateRoutes: onBeforeCreateRoutes,
63
- onBeforeHydrateRouter: onBeforeHydrateRouter
64
- },
127
+ registryHooks: routerProviderRegistryHooks,
65
128
  setup: (api)=>{
129
+ const hooks = api.getHooks();
130
+ let cachedRouteObjects;
131
+ let cachedRouteTree = null;
132
+ let cachedRouter = null;
133
+ let cachedRouterBasepath = null;
134
+ const getMergedConfig = ()=>getMergedRouterConfig(api, userConfig);
135
+ const getRouteObjects = ()=>{
136
+ if (void 0 !== cachedRouteObjects) return cachedRouteObjects;
137
+ const mergedConfig = getMergedConfig();
138
+ const { createRoutes } = mergedConfig;
139
+ const finalRouteConfig = getFinalRouteConfig(mergedConfig);
140
+ const routeObjects = createRoutes ? createRoutes() : createRouteObjectsFromConfig({
141
+ routesConfig: finalRouteConfig
142
+ }) || [];
143
+ cachedRouteObjects = hooks.modifyRoutes.call(routeObjects);
144
+ return cachedRouteObjects;
145
+ };
146
+ const getRouteTree = ()=>{
147
+ if (cachedRouteTree) return cachedRouteTree;
148
+ const routeObjects = getRouteObjects();
149
+ if (!routeObjects.length) return null;
150
+ cachedRouteTree = createRouteTreeFromRouteObjects(routeObjects, {
151
+ rscPayloadRouter: getGlobalEnableRsc()
152
+ });
153
+ return cachedRouteTree;
154
+ };
155
+ const selectBasePath = (pathname)=>{
156
+ const { serverBase = [] } = getMergedConfig();
157
+ const match = serverBase.find((baseUrl)=>isSegmentPrefix(pathname, baseUrl));
158
+ return match || '/';
159
+ };
160
+ const getClientBasename = (runtimeContext)=>{
161
+ const { basename = '' } = getMergedConfig();
162
+ const baseUrl = selectBasePath(location.pathname).replace(/^\/*/, '/');
163
+ return '/' === baseUrl ? urlJoin(baseUrl, runtimeContext._internalRouterBaseName || basename || '') : baseUrl;
164
+ };
165
+ const getRouter = (runtimeContext, _basename)=>{
166
+ const routeTree = getRouteTree();
167
+ if (!routeTree) return null;
168
+ const lifecycleContext = {
169
+ framework: 'tanstack',
170
+ phase: 'client-create',
171
+ routes: getRouteObjects(),
172
+ runtimeContext,
173
+ basename: _basename
174
+ };
175
+ hooks.onBeforeCreateRouter.call(lifecycleContext);
176
+ if (cachedRouter && cachedRouterBasepath === _basename) {
177
+ wrapRouterSubscribeWithBlockState(cachedRouter, runtimeContext.unstable_getBlockNavState);
178
+ hooks.onAfterCreateRouter.call({
179
+ ...lifecycleContext,
180
+ router: cachedRouter,
181
+ runtimeContext
182
+ });
183
+ return cachedRouter;
184
+ }
185
+ const mergedConfig = getMergedConfig();
186
+ const { supportHtml5History = true } = mergedConfig;
187
+ const history = supportHtml5History ? createBrowserHistory() : createHashHistory();
188
+ const rewrite = createModernBasepathRewrite(_basename);
189
+ const serializationAdapters = getGlobalEnableRsc() ? getTanstackRscSerializationAdapters() : void 0;
190
+ cachedRouter = createRouter({
191
+ ...getModernTanstackRouterFastDefaults(mergedConfig),
192
+ routeTree,
193
+ basepath: '/',
194
+ rewrite,
195
+ history,
196
+ context: {},
197
+ ...serializationAdapters ? {
198
+ serializationAdapters
199
+ } : {}
200
+ });
201
+ cachedRouterBasepath = _basename;
202
+ wrapRouterSubscribeWithBlockState(cachedRouter, runtimeContext.unstable_getBlockNavState);
203
+ hooks.onAfterCreateRouter.call({
204
+ ...lifecycleContext,
205
+ router: cachedRouter,
206
+ runtimeContext
207
+ });
208
+ return cachedRouter;
209
+ };
66
210
  api.onBeforeRender((context)=>{
67
- const pluginConfig = api.getRuntimeConfig();
68
- const mergedConfig = merge(pluginConfig.router || {}, userConfig);
69
- if ("u" > typeof window && window._SSR_DATA && mergedConfig.unstable_reloadOnURLMismatch) {
211
+ const mergedConfig = getMergedConfig();
212
+ if ("u" > typeof window && void 0 !== window._SSR_DATA && mergedConfig.unstable_reloadOnURLMismatch) {
70
213
  const { ssrContext } = context;
71
214
  const currentPathname = normalizePathname(window.location.pathname);
72
- const initialPathname = ssrContext?.request?.pathname && normalizePathname(ssrContext.request.pathname);
73
- if (initialPathname && initialPathname !== currentPathname) {
215
+ const initialPathname = 'string' == typeof ssrContext?.request?.pathname ? normalizePathname(ssrContext.request.pathname) : void 0;
216
+ if (void 0 !== initialPathname && '' !== initialPathname && initialPathname !== currentPathname) {
74
217
  const errorMsg = `The initial URL ${initialPathname} and the URL ${currentPathname} to be hydrated do not match, reload.`;
75
218
  console.error(errorMsg);
76
219
  window.location.reload();
@@ -83,94 +226,26 @@ const tanstackRouterPlugin = (userConfig = {})=>{
83
226
  useNavigate: useNavigate,
84
227
  useRouter: useRouter
85
228
  };
229
+ const hasSSRBootstrap = "u" > typeof window && Boolean(window.$_TSR);
230
+ if (hasSSRBootstrap && getRouteObjects().length > 0) {
231
+ const runtimeContext = context;
232
+ const router = getRouter(runtimeContext, getClientBasename(runtimeContext));
233
+ if (null != router) return getTanstackSsrHydrationPromise(router).then(()=>void 0);
234
+ }
86
235
  });
87
236
  api.wrapRoot((App)=>{
88
- const mergedConfig = merge(api.getRuntimeConfig().router || {}, userConfig);
89
- const { serverBase = [], supportHtml5History = true, basename = '', routesConfig, createRoutes } = mergedConfig;
90
- const finalRouteConfig = {
91
- routes: getGlobalRoutes(),
92
- globalApp: getGlobalLayoutApp(),
93
- ...routesConfig
94
- };
95
- if (!finalRouteConfig.routes && !createRoutes) return App;
96
- const hooks = api.getHooks();
97
- let cachedRouteObjects;
98
- const getRouteObjects = ()=>{
99
- if (void 0 !== cachedRouteObjects) return cachedRouteObjects;
100
- const routeObjects = createRoutes ? createRoutes() : createRouteObjectsFromConfig({
101
- routesConfig: finalRouteConfig
102
- }) || [];
103
- const normalizedRouteObjects = createRoutes ? routeObjects : stripSyntheticNotFoundRoute(routeObjects);
104
- cachedRouteObjects = hooks.modifyRoutes.call(normalizedRouteObjects);
105
- return cachedRouteObjects;
106
- };
107
- const selectBasePath = (pathname)=>{
108
- const match = serverBase.find((baseUrl)=>isSegmentPrefix(pathname, baseUrl));
109
- return match || '/';
110
- };
111
- let cachedRouteTree = null;
112
- let cachedRouter = null;
113
- let cachedRouterBasepath = null;
237
+ if (0 === getRouteObjects().length) return App;
114
238
  const RouterWrapper = ()=>{
115
239
  const runtimeContext = useContext(InternalRuntimeContext);
116
- const baseUrl = selectBasePath(location.pathname).replace(/^\/*/, '/');
117
- const _basename = '/' === baseUrl ? urlJoin(baseUrl, runtimeContext._internalRouterBaseName || basename || '') : baseUrl;
118
- const routeTree = useMemo(()=>{
119
- if (cachedRouteTree) return cachedRouteTree;
120
- const routeObjects = getRouteObjects();
121
- if (!routeObjects.length) return null;
122
- cachedRouteTree = createRouteTreeFromRouteObjects(routeObjects, {
123
- rscPayloadRouter: getGlobalEnableRsc()
124
- });
125
- return cachedRouteTree;
126
- }, []);
240
+ const _basename = getClientBasename(runtimeContext);
241
+ const routeTree = useMemo(()=>getRouteTree(), []);
127
242
  if (!routeTree) return App ? /*#__PURE__*/ jsx(App, {}) : null;
128
- const router = useMemo(()=>{
129
- const lifecycleContext = {
130
- framework: 'tanstack',
131
- phase: 'client-create',
132
- routes: getRouteObjects(),
133
- runtimeContext,
134
- basename: _basename
135
- };
136
- hooks.onBeforeCreateRouter.call(lifecycleContext);
137
- if (cachedRouter && cachedRouterBasepath === _basename) {
138
- wrapRouterSubscribeWithBlockState(cachedRouter, runtimeContext.unstable_getBlockNavState);
139
- hooks.onAfterCreateRouter.call({
140
- ...lifecycleContext,
141
- router: cachedRouter,
142
- runtimeContext
143
- });
144
- return cachedRouter;
145
- }
146
- const history = supportHtml5History ? createBrowserHistory() : createHashHistory();
147
- const rewrite = createModernBasepathRewrite(_basename);
148
- const serializationAdapters = getGlobalEnableRsc() ? getTanstackRscSerializationAdapters() : void 0;
149
- cachedRouter = createRouter({
150
- ...getModernTanstackRouterFastDefaults(mergedConfig),
151
- routeTree,
152
- basepath: '/',
153
- rewrite,
154
- history,
155
- context: {},
156
- ...serializationAdapters ? {
157
- serializationAdapters
158
- } : {}
159
- });
160
- cachedRouterBasepath = _basename;
161
- wrapRouterSubscribeWithBlockState(cachedRouter, runtimeContext.unstable_getBlockNavState);
162
- hooks.onAfterCreateRouter.call({
163
- ...lifecycleContext,
164
- router: cachedRouter,
165
- runtimeContext
166
- });
167
- return cachedRouter;
168
- }, [
243
+ const router = useMemo(()=>getRouter(runtimeContext, _basename), [
169
244
  _basename,
170
245
  routeTree,
171
- supportHtml5History,
172
246
  runtimeContext
173
247
  ]);
248
+ if (!router) return App ? /*#__PURE__*/ jsx(App, {}) : null;
174
249
  const runtimeState = applyRouterRuntimeState(runtimeContext, {
175
250
  framework: 'tanstack',
176
251
  basename: _basename,
@@ -184,20 +259,21 @@ const tanstackRouterPlugin = (userConfig = {})=>{
184
259
  basename: _basename,
185
260
  router
186
261
  };
187
- const hasSSRBootstrap = "u" > typeof window && Boolean(window.$_TSR);
188
- if (hasSSRBootstrap) hooks.onBeforeHydrateRouter.call({
262
+ const hasSSRBootstrap = "u" > typeof window && (Boolean(window.$_TSR) || hasTanstackSsrHydrationRecord(router));
263
+ const needsRouterClient = hasSSRBootstrap;
264
+ if (needsRouterClient) hooks.onBeforeHydrateRouter.call({
189
265
  ...lifecycleContext,
190
266
  phase: 'hydrate',
191
267
  router,
192
268
  runtimeContext: runtimeState
193
269
  });
194
- const RouterContent = hasSSRBootstrap ? /*#__PURE__*/ jsx(RouterClient, {
270
+ const RouterContent = needsRouterClient ? /*#__PURE__*/ jsx(ModernRouterClient, {
195
271
  router: router
196
272
  }) : /*#__PURE__*/ jsx(RouterProvider, {
197
273
  router: router
198
274
  });
199
275
  const HydratableRouterContent = wrapTanstackSsrHydrationBoundary(RouterContent, hasSSRBootstrap);
200
- if (hasSSRBootstrap) hooks.onAfterHydrateRouter.call({
276
+ if (needsRouterClient) hooks.onAfterHydrateRouter.call({
201
277
  ...lifecycleContext,
202
278
  phase: 'hydrate',
203
279
  router,
@@ -1,17 +1,16 @@
1
1
  import "node:module";
2
2
  import { jsx } from "react/jsx-runtime";
3
- import { InternalRuntimeContext, getGlobalEnableRsc, getGlobalLayoutApp, getGlobalRoutes } from "@modern-js/runtime/context";
4
- import { merge } from "@modern-js/runtime-utils/merge";
3
+ import { InternalRuntimeContext, getGlobalEnableRsc } from "@modern-js/runtime/context";
5
4
  import { createRequestContext, storage } from "@modern-js/runtime-utils/node";
6
5
  import { time } from "@modern-js/runtime-utils/time";
7
6
  import { LOADER_REPORTER_NAME } from "@modern-js/utils/universal/constants";
8
7
  import { RouterProvider, createMemoryHistory, createRouter } from "@tanstack/react-router";
9
- import { attachRouterServerSsrUtils } from "@tanstack/router-core/ssr/server";
10
8
  import { useContext } from "react";
11
9
  import { createModernBasepathRewrite } from "./basepathRewrite.mjs";
12
- import { modifyRoutes, onAfterCreateRouter, onAfterHydrateRouter, onBeforeCreateRouter, onBeforeCreateRoutes, onBeforeHydrateRouter } from "./hooks.mjs";
10
+ import { routerProviderRegistryHooks } from "./hooks.mjs";
13
11
  import { wrapTanstackSsrHydrationBoundary } from "./hydrationBoundary.mjs";
14
- import { applyRouterServerPrepareResult, createRouterServerSnapshot } from "./lifecycle.mjs";
12
+ import { applyRouterServerPrepareResult, createRouterServerSnapshot, getRouterRuntimeState } from "./lifecycle.mjs";
13
+ import { getFinalRouteConfig, getMergedRouterConfig } from "./pluginCore.mjs";
15
14
  import { createRouteTreeFromRouteObjects, getModernRouteIdsFromMatches } from "./routeTree.mjs";
16
15
  import { createTanstackRscServerPayload, handleTanstackRscRedirect } from "./rsc/payloadRouter.mjs";
17
16
  import { getModernTanstackRouterFastDefaults } from "./types.mjs";
@@ -29,7 +28,7 @@ function isPreloadableRouteComponent(component) {
29
28
  return 'function' == typeof preloadable.load || 'function' == typeof preloadable.preload;
30
29
  }
31
30
  function isReactLazyRouteComponent(component) {
32
- return Boolean(component) && 'object' == typeof component && 'function' == typeof component._init && '_payload' in component;
31
+ return null != component && 'object' == typeof component && 'function' == typeof component._init && '_payload' in component;
33
32
  }
34
33
  async function preloadReactLazyRouteComponent(component) {
35
34
  try {
@@ -96,15 +95,6 @@ function createGetSsrHref(request) {
96
95
  const url = new URL(request.url);
97
96
  return `${url.pathname}${url.search}${url.hash}`;
98
97
  }
99
- function stripSyntheticNotFoundRoute(routes) {
100
- return routes.filter((route)=>!('*' === route.path && !route.id && !route.loader)).map((route)=>{
101
- if (!route.children?.length) return route;
102
- return {
103
- ...route,
104
- children: stripSyntheticNotFoundRoute(route.children)
105
- };
106
- });
107
- }
108
98
  function collectRouterErrors(tanstackRouter) {
109
99
  const state = tanstackRouter.state;
110
100
  const matches = Array.isArray(state.matches) ? state.matches : [];
@@ -116,29 +106,24 @@ function collectRouterErrors(tanstackRouter) {
116
106
  }, {});
117
107
  return Object.keys(errors).length > 0 ? errors : void 0;
118
108
  }
109
+ async function attachServerSsrUtils(router) {
110
+ const { attachRouterServerSsrUtils } = await import("@tanstack/router-core/ssr/server");
111
+ attachRouterServerSsrUtils({
112
+ router,
113
+ manifest: void 0
114
+ });
115
+ }
119
116
  const tanstackRouterPlugin = (userConfig = {})=>{
120
117
  const plugin = {
121
118
  name: '@modern-js/plugin-router-tanstack',
122
- registryHooks: {
123
- modifyRoutes: modifyRoutes,
124
- onAfterCreateRouter: onAfterCreateRouter,
125
- onAfterHydrateRouter: onAfterHydrateRouter,
126
- onBeforeCreateRouter: onBeforeCreateRouter,
127
- onBeforeCreateRoutes: onBeforeCreateRoutes,
128
- onBeforeHydrateRouter: onBeforeHydrateRouter
129
- },
119
+ registryHooks: routerProviderRegistryHooks,
130
120
  setup: (api)=>{
131
121
  api.onBeforeRender(async (context, interrupt)=>{
132
- const pluginConfig = api.getRuntimeConfig();
133
- const mergedConfig = merge(pluginConfig.router || {}, userConfig);
122
+ const mergedConfig = getMergedRouterConfig(api, userConfig);
134
123
  const serializationAdapters = getGlobalEnableRsc() ? (await import("./rsc/server.mjs")).getTanstackRscSerializationAdapters() : void 0;
135
124
  const enableRsc = getGlobalEnableRsc();
136
- const { basename = '', routesConfig, createRoutes } = mergedConfig;
137
- const finalRouteConfig = {
138
- routes: getGlobalRoutes(),
139
- globalApp: getGlobalLayoutApp(),
140
- ...routesConfig
141
- };
125
+ const { basename = '', createRoutes } = mergedConfig;
126
+ const finalRouteConfig = getFinalRouteConfig(mergedConfig);
142
127
  if (!finalRouteConfig.routes && !createRoutes) return;
143
128
  const hooks = api.getHooks();
144
129
  await hooks.onBeforeCreateRoutes.call(context);
@@ -146,8 +131,7 @@ const tanstackRouterPlugin = (userConfig = {})=>{
146
131
  routesConfig: finalRouteConfig,
147
132
  ssrMode: context.ssrContext?.mode
148
133
  }) || [];
149
- const normalizedRouteObjects = createRoutes ? routeObjects : stripSyntheticNotFoundRoute(routeObjects);
150
- const modifiedRouteObjects = hooks.modifyRoutes.call(normalizedRouteObjects);
134
+ const modifiedRouteObjects = hooks.modifyRoutes.call(routeObjects);
151
135
  if (!modifiedRouteObjects.length) return;
152
136
  const { request, nonce, baseUrl, loaderFailureMode = 'errorBoundary' } = context.ssrContext;
153
137
  const _basename = '/' === baseUrl ? urlJoin(baseUrl, basename || '') : baseUrl;
@@ -195,10 +179,7 @@ const tanstackRouterPlugin = (userConfig = {})=>{
195
179
  } : {}
196
180
  });
197
181
  const serverRouter = tanstackRouter;
198
- attachRouterServerSsrUtils({
199
- router: serverRouter,
200
- manifest: void 0
201
- });
182
+ await attachServerSsrUtils(serverRouter);
202
183
  const end = time();
203
184
  try {
204
185
  await tanstackRouter.load({
@@ -265,7 +246,7 @@ const tanstackRouterPlugin = (userConfig = {})=>{
265
246
  api.wrapRoot((App)=>{
266
247
  const getRouteApp = ()=>(props)=>{
267
248
  const context = useContext(InternalRuntimeContext);
268
- const router = context.routerInstance ?? context.routerRuntime?.instance;
249
+ const router = getRouterRuntimeState(context)?.instance;
269
250
  if (!router) return App ? /*#__PURE__*/ jsx(App, {
270
251
  ...props
271
252
  }) : null;
@@ -0,0 +1,15 @@
1
+ import "node:module";
2
+ import { getGlobalLayoutApp, getGlobalRoutes } from "@modern-js/runtime/context";
3
+ import { merge } from "@modern-js/runtime-utils/merge";
4
+ function getMergedRouterConfig(api, userConfig) {
5
+ const pluginConfig = api.getRuntimeConfig();
6
+ return merge(pluginConfig.router || {}, userConfig);
7
+ }
8
+ function getFinalRouteConfig(mergedConfig) {
9
+ return {
10
+ routes: getGlobalRoutes(),
11
+ globalApp: getGlobalLayoutApp(),
12
+ ...mergedConfig.routesConfig
13
+ };
14
+ }
15
+ export { getFinalRouteConfig, getMergedRouterConfig };
@@ -5,7 +5,7 @@ function resolvePreloadFromPrefetch(prefetch, preload) {
5
5
  if (void 0 !== preload) return preload;
6
6
  if ('none' === prefetch) return false;
7
7
  if ('intent' === prefetch || 'render' === prefetch || 'viewport' === prefetch) return prefetch;
8
- return preload;
8
+ return 'viewport';
9
9
  }
10
10
  const LinkComponentImpl = (props)=>{
11
11
  const { prefetch, preload, ...rest } = props;
@@ -0,0 +1,19 @@
1
+ import "node:module";
2
+ import { registerRouterProvider } from "@modern-js/runtime/context";
3
+ import { Form, RouteActionResponseError, useFetcher } from "./dataMutation.mjs";
4
+ import { Outlet } from "./outlet.mjs";
5
+ import { tanstackRouterPlugin } from "./plugin.mjs";
6
+ import { Link, NavLink } from "./prefetchLink.mjs";
7
+ const tanstackRouterProviderFactory = (userConfig)=>tanstackRouterPlugin(userConfig);
8
+ registerRouterProvider('tanstack', tanstackRouterProviderFactory);
9
+ const COMPAT_BINDINGS_SLOT = Symbol.for('@modern-js/plugin-tanstack:runtime-compat-bindings');
10
+ const tanstackRouterCompatBindings = {
11
+ Form: Form,
12
+ Link: Link,
13
+ NavLink: NavLink,
14
+ Outlet: Outlet,
15
+ RouteActionResponseError: RouteActionResponseError,
16
+ useFetcher: useFetcher
17
+ };
18
+ globalThis[COMPAT_BINDINGS_SLOT] ??= tanstackRouterCompatBindings;
19
+ export { tanstackRouterCompatBindings };