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

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