@bleedingdev/modern-js-plugin-tanstack 3.2.0-ultramodern.10 → 3.2.0-ultramodern.100
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.
- package/dist/cjs/cli/index.js +12 -0
- package/dist/cjs/cli/routeSplitting.js +83 -0
- package/dist/cjs/cli/tanstackTypes.js +146 -58
- package/dist/cjs/runtime/hydrationBoundary.js +44 -0
- package/dist/cjs/runtime/index.js +321 -69
- package/dist/cjs/runtime/outlet.js +54 -0
- package/dist/cjs/runtime/plugin.js +194 -90
- package/dist/cjs/runtime/plugin.node.js +29 -11
- package/dist/cjs/runtime/plugin.worker.js +49 -0
- package/dist/cjs/runtime/routeTree.js +72 -12
- package/dist/cjs/runtime/types.js +27 -1
- package/dist/esm/cli/index.mjs +4 -1
- package/dist/esm/cli/routeSplitting.mjs +43 -0
- package/dist/esm/cli/tanstackTypes.mjs +146 -58
- package/dist/esm/runtime/hydrationBoundary.mjs +10 -0
- package/dist/esm/runtime/index.mjs +3 -2
- package/dist/esm/runtime/outlet.mjs +17 -0
- package/dist/esm/runtime/plugin.mjs +197 -93
- package/dist/esm/runtime/plugin.node.mjs +30 -12
- package/dist/esm/runtime/plugin.worker.mjs +1 -0
- package/dist/esm/runtime/routeTree.mjs +73 -13
- package/dist/esm/runtime/types.mjs +7 -0
- package/dist/esm-node/cli/index.mjs +4 -1
- package/dist/esm-node/cli/routeSplitting.mjs +44 -0
- package/dist/esm-node/cli/tanstackTypes.mjs +146 -58
- package/dist/esm-node/runtime/hydrationBoundary.mjs +11 -0
- package/dist/esm-node/runtime/index.mjs +3 -2
- package/dist/esm-node/runtime/outlet.mjs +18 -0
- package/dist/esm-node/runtime/plugin.mjs +197 -93
- package/dist/esm-node/runtime/plugin.node.mjs +30 -12
- package/dist/esm-node/runtime/plugin.worker.mjs +2 -0
- package/dist/esm-node/runtime/routeTree.mjs +73 -13
- package/dist/esm-node/runtime/types.mjs +7 -0
- package/dist/types/cli/index.d.ts +4 -0
- package/dist/types/cli/routeSplitting.d.ts +29 -0
- package/dist/types/runtime/hydrationBoundary.d.ts +2 -0
- package/dist/types/runtime/index.d.ts +5 -2
- package/dist/types/runtime/outlet.d.ts +2 -0
- package/dist/types/runtime/plugin.d.ts +1 -1
- package/dist/types/runtime/plugin.node.d.ts +1 -1
- package/dist/types/runtime/plugin.worker.d.ts +1 -0
- package/dist/types/runtime/types.d.ts +7 -0
- package/package.json +15 -15
- package/src/cli/index.ts +17 -0
- package/src/cli/routeSplitting.ts +81 -0
- package/src/cli/tanstackTypes.ts +216 -67
- package/src/runtime/hydrationBoundary.tsx +12 -0
- package/src/runtime/index.tsx +107 -2
- package/src/runtime/outlet.tsx +42 -0
- package/src/runtime/plugin.node.tsx +57 -8
- package/src/runtime/plugin.tsx +353 -149
- package/src/runtime/plugin.worker.tsx +4 -0
- package/src/runtime/routeTree.ts +194 -23
- package/src/runtime/ssr-shim.d.ts +1 -3
- package/src/runtime/types.ts +13 -0
- package/tests/router/cli.test.ts +239 -0
- package/tests/router/fastDefaults.test.ts +25 -0
- package/tests/router/hydrationBoundary.test.tsx +23 -0
- package/tests/router/routeTree.test.ts +416 -1
- package/tests/router/tanstackTypes.test.ts +184 -0
|
@@ -4,14 +4,18 @@ import { InternalRuntimeContext, getGlobalEnableRsc, getGlobalLayoutApp, getGlob
|
|
|
4
4
|
import { merge } from "@modern-js/runtime-utils/merge";
|
|
5
5
|
import { normalizePathname } from "@modern-js/runtime-utils/url";
|
|
6
6
|
import { RouterProvider, createBrowserHistory, createHashHistory, createRouter, useLocation, useMatches, useNavigate, useRouter } from "@tanstack/react-router";
|
|
7
|
-
import {
|
|
7
|
+
import { hydrate } from "@tanstack/react-router/ssr/client";
|
|
8
|
+
import { useContext, useMemo } from "react";
|
|
8
9
|
import { createModernBasepathRewrite } from "./basepathRewrite.mjs";
|
|
9
10
|
import { modifyRoutes, onAfterCreateRouter, onAfterHydrateRouter, onBeforeCreateRouter, onBeforeCreateRoutes, onBeforeHydrateRouter } from "./hooks.mjs";
|
|
11
|
+
import { wrapTanstackSsrHydrationBoundary } from "./hydrationBoundary.mjs";
|
|
10
12
|
import { applyRouterRuntimeState } from "./lifecycle.mjs";
|
|
13
|
+
import { withModernRouteMatchContext } from "./outlet.mjs";
|
|
14
|
+
import { Link } from "./prefetchLink.mjs";
|
|
11
15
|
import { createRouteTreeFromRouteObjects } from "./routeTree.mjs";
|
|
12
16
|
import { getTanstackRscSerializationAdapters } from "./rsc/client.mjs";
|
|
17
|
+
import { getModernTanstackRouterFastDefaults } from "./types.mjs";
|
|
13
18
|
import { createRouteObjectsFromConfig, urlJoin } from "./utils.mjs";
|
|
14
|
-
import * as __rspack_external_react from "react";
|
|
15
19
|
const BLOCKING_SUBSCRIBE_SYMBOL = Symbol.for('@modern-js/plugin-tanstack:blocking-subscribe');
|
|
16
20
|
const BLOCKING_STATE_SYMBOL = Symbol.for('@modern-js/plugin-tanstack:blocking-state');
|
|
17
21
|
function normalizeBase(b) {
|
|
@@ -39,6 +43,85 @@ function wrapRouterSubscribeWithBlockState(router, getBlockNavState) {
|
|
|
39
43
|
};
|
|
40
44
|
target[BLOCKING_SUBSCRIBE_SYMBOL] = true;
|
|
41
45
|
}
|
|
46
|
+
const routerHydrationRecords = new WeakMap();
|
|
47
|
+
const routeModulesKey = '_routeModules';
|
|
48
|
+
function pickRouteModuleComponent(routeModule, seen = new Set()) {
|
|
49
|
+
if ('function' == typeof routeModule || routeModule && 'object' == typeof routeModule && '$$typeof' in routeModule) return routeModule;
|
|
50
|
+
if (!routeModule || 'object' != typeof routeModule) return;
|
|
51
|
+
if (seen.has(routeModule)) return;
|
|
52
|
+
seen.add(routeModule);
|
|
53
|
+
const module = routeModule;
|
|
54
|
+
for (const candidate of [
|
|
55
|
+
module.default,
|
|
56
|
+
module.Component
|
|
57
|
+
]){
|
|
58
|
+
const component = pickRouteModuleComponent(candidate, seen);
|
|
59
|
+
if (component) return component;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
function getCachedRouteModule(routeId) {
|
|
63
|
+
if ("u" < typeof window) return;
|
|
64
|
+
return window[routeModulesKey]?.[routeId];
|
|
65
|
+
}
|
|
66
|
+
async function preloadHydratedRouteComponents(router) {
|
|
67
|
+
const preloadableRouter = router;
|
|
68
|
+
const routesById = preloadableRouter.routesById || {};
|
|
69
|
+
const matches = preloadableRouter.stores.matches.get();
|
|
70
|
+
await Promise.all(matches.map((match)=>{
|
|
71
|
+
if (!match.routeId) return;
|
|
72
|
+
const route = routesById[match.routeId];
|
|
73
|
+
const component = route?.options?.component;
|
|
74
|
+
const preload = component?.load || component?.preload;
|
|
75
|
+
if ('function' != typeof preload) return;
|
|
76
|
+
return Promise.resolve(preload.call(component)).then((routeModule)=>{
|
|
77
|
+
const modernRouteId = route?.options?.staticData?.modernRouteId;
|
|
78
|
+
const resolvedComponent = pickRouteModuleComponent(modernRouteId && getCachedRouteModule(modernRouteId) || routeModule);
|
|
79
|
+
if (resolvedComponent && modernRouteId) route.options.component = withModernRouteMatchContext(resolvedComponent, modernRouteId);
|
|
80
|
+
});
|
|
81
|
+
}));
|
|
82
|
+
}
|
|
83
|
+
function getTanstackSsrHydrationRecord(router) {
|
|
84
|
+
let hydrationRecord = routerHydrationRecords.get(router);
|
|
85
|
+
if (!hydrationRecord) {
|
|
86
|
+
hydrationRecord = {
|
|
87
|
+
promise: Promise.resolve(),
|
|
88
|
+
status: 'pending'
|
|
89
|
+
};
|
|
90
|
+
routerHydrationRecords.set(router, hydrationRecord);
|
|
91
|
+
try {
|
|
92
|
+
hydrationRecord.promise = hydrate(router).then(async (value)=>{
|
|
93
|
+
await preloadHydratedRouteComponents(router);
|
|
94
|
+
return value;
|
|
95
|
+
}).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
|
+
}
|
|
110
|
+
return hydrationRecord;
|
|
111
|
+
}
|
|
112
|
+
function getTanstackSsrHydrationPromise(router) {
|
|
113
|
+
return getTanstackSsrHydrationRecord(router).promise;
|
|
114
|
+
}
|
|
115
|
+
function hasTanstackSsrHydrationRecord(router) {
|
|
116
|
+
return routerHydrationRecords.has(router);
|
|
117
|
+
}
|
|
118
|
+
function ModernRouterClient({ router }) {
|
|
119
|
+
const hydrationRecord = getTanstackSsrHydrationRecord(router);
|
|
120
|
+
if ('rejected' === hydrationRecord.status) throw hydrationRecord.error;
|
|
121
|
+
return /*#__PURE__*/ jsx(RouterProvider, {
|
|
122
|
+
router: router
|
|
123
|
+
});
|
|
124
|
+
}
|
|
42
125
|
function stripSyntheticNotFoundRoute(routes) {
|
|
43
126
|
return routes.filter((route)=>!('*' === route.path && !route.id && !route.loader)).map((route)=>{
|
|
44
127
|
if (!route.children?.length) return route;
|
|
@@ -60,9 +143,97 @@ const tanstackRouterPlugin = (userConfig = {})=>{
|
|
|
60
143
|
onBeforeHydrateRouter: onBeforeHydrateRouter
|
|
61
144
|
},
|
|
62
145
|
setup: (api)=>{
|
|
63
|
-
api.
|
|
146
|
+
const hooks = api.getHooks();
|
|
147
|
+
let cachedRouteObjects;
|
|
148
|
+
let cachedRouteTree = null;
|
|
149
|
+
let cachedRouter = null;
|
|
150
|
+
let cachedRouterBasepath = null;
|
|
151
|
+
const getMergedConfig = ()=>{
|
|
64
152
|
const pluginConfig = api.getRuntimeConfig();
|
|
65
|
-
|
|
153
|
+
return merge(pluginConfig.router || {}, userConfig);
|
|
154
|
+
};
|
|
155
|
+
const getRouteObjects = ()=>{
|
|
156
|
+
if (void 0 !== cachedRouteObjects) return cachedRouteObjects;
|
|
157
|
+
const mergedConfig = getMergedConfig();
|
|
158
|
+
const { routesConfig, createRoutes } = mergedConfig;
|
|
159
|
+
const finalRouteConfig = {
|
|
160
|
+
routes: getGlobalRoutes(),
|
|
161
|
+
globalApp: getGlobalLayoutApp(),
|
|
162
|
+
...routesConfig
|
|
163
|
+
};
|
|
164
|
+
const routeObjects = createRoutes ? createRoutes() : createRouteObjectsFromConfig({
|
|
165
|
+
routesConfig: finalRouteConfig
|
|
166
|
+
}) || [];
|
|
167
|
+
const normalizedRouteObjects = createRoutes ? routeObjects : stripSyntheticNotFoundRoute(routeObjects);
|
|
168
|
+
cachedRouteObjects = hooks.modifyRoutes.call(normalizedRouteObjects);
|
|
169
|
+
return cachedRouteObjects;
|
|
170
|
+
};
|
|
171
|
+
const getRouteTree = ()=>{
|
|
172
|
+
if (cachedRouteTree) return cachedRouteTree;
|
|
173
|
+
const routeObjects = getRouteObjects();
|
|
174
|
+
if (!routeObjects.length) return null;
|
|
175
|
+
cachedRouteTree = createRouteTreeFromRouteObjects(routeObjects, {
|
|
176
|
+
rscPayloadRouter: getGlobalEnableRsc()
|
|
177
|
+
});
|
|
178
|
+
return cachedRouteTree;
|
|
179
|
+
};
|
|
180
|
+
const selectBasePath = (pathname)=>{
|
|
181
|
+
const { serverBase = [] } = getMergedConfig();
|
|
182
|
+
const match = serverBase.find((baseUrl)=>isSegmentPrefix(pathname, baseUrl));
|
|
183
|
+
return match || '/';
|
|
184
|
+
};
|
|
185
|
+
const getClientBasename = (runtimeContext)=>{
|
|
186
|
+
const { basename = '' } = getMergedConfig();
|
|
187
|
+
const baseUrl = selectBasePath(location.pathname).replace(/^\/*/, '/');
|
|
188
|
+
return '/' === baseUrl ? urlJoin(baseUrl, runtimeContext._internalRouterBaseName || basename || '') : baseUrl;
|
|
189
|
+
};
|
|
190
|
+
const getRouter = (runtimeContext, _basename)=>{
|
|
191
|
+
const routeTree = getRouteTree();
|
|
192
|
+
if (!routeTree) return null;
|
|
193
|
+
const lifecycleContext = {
|
|
194
|
+
framework: 'tanstack',
|
|
195
|
+
phase: 'client-create',
|
|
196
|
+
routes: getRouteObjects(),
|
|
197
|
+
runtimeContext,
|
|
198
|
+
basename: _basename
|
|
199
|
+
};
|
|
200
|
+
hooks.onBeforeCreateRouter.call(lifecycleContext);
|
|
201
|
+
if (cachedRouter && cachedRouterBasepath === _basename) {
|
|
202
|
+
wrapRouterSubscribeWithBlockState(cachedRouter, runtimeContext.unstable_getBlockNavState);
|
|
203
|
+
hooks.onAfterCreateRouter.call({
|
|
204
|
+
...lifecycleContext,
|
|
205
|
+
router: cachedRouter,
|
|
206
|
+
runtimeContext
|
|
207
|
+
});
|
|
208
|
+
return cachedRouter;
|
|
209
|
+
}
|
|
210
|
+
const mergedConfig = getMergedConfig();
|
|
211
|
+
const { supportHtml5History = true } = mergedConfig;
|
|
212
|
+
const history = supportHtml5History ? createBrowserHistory() : createHashHistory();
|
|
213
|
+
const rewrite = createModernBasepathRewrite(_basename);
|
|
214
|
+
const serializationAdapters = getGlobalEnableRsc() ? getTanstackRscSerializationAdapters() : void 0;
|
|
215
|
+
cachedRouter = createRouter({
|
|
216
|
+
...getModernTanstackRouterFastDefaults(mergedConfig),
|
|
217
|
+
routeTree,
|
|
218
|
+
basepath: '/',
|
|
219
|
+
rewrite,
|
|
220
|
+
history,
|
|
221
|
+
context: {},
|
|
222
|
+
...serializationAdapters ? {
|
|
223
|
+
serializationAdapters
|
|
224
|
+
} : {}
|
|
225
|
+
});
|
|
226
|
+
cachedRouterBasepath = _basename;
|
|
227
|
+
wrapRouterSubscribeWithBlockState(cachedRouter, runtimeContext.unstable_getBlockNavState);
|
|
228
|
+
hooks.onAfterCreateRouter.call({
|
|
229
|
+
...lifecycleContext,
|
|
230
|
+
router: cachedRouter,
|
|
231
|
+
runtimeContext
|
|
232
|
+
});
|
|
233
|
+
return cachedRouter;
|
|
234
|
+
};
|
|
235
|
+
api.onBeforeRender(async (context)=>{
|
|
236
|
+
const mergedConfig = getMergedConfig();
|
|
66
237
|
if ("u" > typeof window && window._SSR_DATA && mergedConfig.unstable_reloadOnURLMismatch) {
|
|
67
238
|
const { ssrContext } = context;
|
|
68
239
|
const currentPathname = normalizePathname(window.location.pathname);
|
|
@@ -74,98 +245,32 @@ const tanstackRouterPlugin = (userConfig = {})=>{
|
|
|
74
245
|
}
|
|
75
246
|
}
|
|
76
247
|
context.router = {
|
|
248
|
+
Link: Link,
|
|
77
249
|
useMatches: useMatches,
|
|
78
250
|
useLocation: useLocation,
|
|
79
251
|
useNavigate: useNavigate,
|
|
80
252
|
useRouter: useRouter
|
|
81
253
|
};
|
|
254
|
+
const hasSSRBootstrap = "u" > typeof window && Boolean(window.$_TSR);
|
|
255
|
+
if (hasSSRBootstrap && getRouteObjects().length) {
|
|
256
|
+
const runtimeContext = context;
|
|
257
|
+
const router = getRouter(runtimeContext, getClientBasename(runtimeContext));
|
|
258
|
+
if (router) await getTanstackSsrHydrationPromise(router);
|
|
259
|
+
}
|
|
82
260
|
});
|
|
83
261
|
api.wrapRoot((App)=>{
|
|
84
|
-
|
|
85
|
-
const { serverBase = [], supportHtml5History = true, basename = '', routesConfig, createRoutes } = mergedConfig;
|
|
86
|
-
const finalRouteConfig = {
|
|
87
|
-
routes: getGlobalRoutes(),
|
|
88
|
-
globalApp: getGlobalLayoutApp(),
|
|
89
|
-
...routesConfig
|
|
90
|
-
};
|
|
91
|
-
if (!finalRouteConfig.routes && !createRoutes) return App;
|
|
92
|
-
const hooks = api.getHooks();
|
|
93
|
-
let cachedRouteObjects;
|
|
94
|
-
const getRouteObjects = ()=>{
|
|
95
|
-
if (void 0 !== cachedRouteObjects) return cachedRouteObjects;
|
|
96
|
-
const routeObjects = createRoutes ? createRoutes() : createRouteObjectsFromConfig({
|
|
97
|
-
routesConfig: finalRouteConfig
|
|
98
|
-
}) || [];
|
|
99
|
-
const normalizedRouteObjects = createRoutes ? routeObjects : stripSyntheticNotFoundRoute(routeObjects);
|
|
100
|
-
cachedRouteObjects = hooks.modifyRoutes.call(normalizedRouteObjects);
|
|
101
|
-
return cachedRouteObjects;
|
|
102
|
-
};
|
|
103
|
-
const selectBasePath = (pathname)=>{
|
|
104
|
-
const match = serverBase.find((baseUrl)=>isSegmentPrefix(pathname, baseUrl));
|
|
105
|
-
return match || '/';
|
|
106
|
-
};
|
|
107
|
-
let cachedRouteTree = null;
|
|
108
|
-
let cachedRouter = null;
|
|
109
|
-
let cachedRouterBasepath = null;
|
|
262
|
+
if (!getRouteObjects().length) return App;
|
|
110
263
|
const RouterWrapper = ()=>{
|
|
111
|
-
const runtimeContext =
|
|
112
|
-
const
|
|
113
|
-
const
|
|
114
|
-
const routeTree = (0, __rspack_external_react.useMemo)(()=>{
|
|
115
|
-
if (cachedRouteTree) return cachedRouteTree;
|
|
116
|
-
const routeObjects = getRouteObjects();
|
|
117
|
-
if (!routeObjects.length) return null;
|
|
118
|
-
cachedRouteTree = createRouteTreeFromRouteObjects(routeObjects, {
|
|
119
|
-
rscPayloadRouter: getGlobalEnableRsc()
|
|
120
|
-
});
|
|
121
|
-
return cachedRouteTree;
|
|
122
|
-
}, []);
|
|
264
|
+
const runtimeContext = useContext(InternalRuntimeContext);
|
|
265
|
+
const _basename = getClientBasename(runtimeContext);
|
|
266
|
+
const routeTree = useMemo(()=>getRouteTree(), []);
|
|
123
267
|
if (!routeTree) return App ? /*#__PURE__*/ jsx(App, {}) : null;
|
|
124
|
-
const router =
|
|
125
|
-
const lifecycleContext = {
|
|
126
|
-
framework: 'tanstack',
|
|
127
|
-
phase: 'client-create',
|
|
128
|
-
routes: getRouteObjects(),
|
|
129
|
-
runtimeContext,
|
|
130
|
-
basename: _basename
|
|
131
|
-
};
|
|
132
|
-
hooks.onBeforeCreateRouter.call(lifecycleContext);
|
|
133
|
-
if (cachedRouter && cachedRouterBasepath === _basename) {
|
|
134
|
-
wrapRouterSubscribeWithBlockState(cachedRouter, runtimeContext.unstable_getBlockNavState);
|
|
135
|
-
hooks.onAfterCreateRouter.call({
|
|
136
|
-
...lifecycleContext,
|
|
137
|
-
router: cachedRouter,
|
|
138
|
-
runtimeContext
|
|
139
|
-
});
|
|
140
|
-
return cachedRouter;
|
|
141
|
-
}
|
|
142
|
-
const history = supportHtml5History ? createBrowserHistory() : createHashHistory();
|
|
143
|
-
const rewrite = createModernBasepathRewrite(_basename);
|
|
144
|
-
const serializationAdapters = getGlobalEnableRsc() ? getTanstackRscSerializationAdapters() : void 0;
|
|
145
|
-
cachedRouter = createRouter({
|
|
146
|
-
routeTree,
|
|
147
|
-
basepath: '/',
|
|
148
|
-
rewrite,
|
|
149
|
-
history,
|
|
150
|
-
context: {},
|
|
151
|
-
...serializationAdapters ? {
|
|
152
|
-
serializationAdapters
|
|
153
|
-
} : {}
|
|
154
|
-
});
|
|
155
|
-
cachedRouterBasepath = _basename;
|
|
156
|
-
wrapRouterSubscribeWithBlockState(cachedRouter, runtimeContext.unstable_getBlockNavState);
|
|
157
|
-
hooks.onAfterCreateRouter.call({
|
|
158
|
-
...lifecycleContext,
|
|
159
|
-
router: cachedRouter,
|
|
160
|
-
runtimeContext
|
|
161
|
-
});
|
|
162
|
-
return cachedRouter;
|
|
163
|
-
}, [
|
|
268
|
+
const router = useMemo(()=>getRouter(runtimeContext, _basename), [
|
|
164
269
|
_basename,
|
|
165
270
|
routeTree,
|
|
166
|
-
supportHtml5History,
|
|
167
271
|
runtimeContext
|
|
168
272
|
]);
|
|
273
|
+
if (!router) return App ? /*#__PURE__*/ jsx(App, {}) : null;
|
|
169
274
|
const runtimeState = applyRouterRuntimeState(runtimeContext, {
|
|
170
275
|
framework: 'tanstack',
|
|
171
276
|
basename: _basename,
|
|
@@ -179,30 +284,29 @@ const tanstackRouterPlugin = (userConfig = {})=>{
|
|
|
179
284
|
basename: _basename,
|
|
180
285
|
router
|
|
181
286
|
};
|
|
182
|
-
const hasSSRBootstrap = "u" > typeof window && Boolean(window.$_TSR);
|
|
183
|
-
|
|
287
|
+
const hasSSRBootstrap = "u" > typeof window && (Boolean(window.$_TSR) || hasTanstackSsrHydrationRecord(router));
|
|
288
|
+
const needsRouterClient = hasSSRBootstrap;
|
|
289
|
+
if (needsRouterClient) hooks.onBeforeHydrateRouter.call({
|
|
184
290
|
...lifecycleContext,
|
|
185
291
|
phase: 'hydrate',
|
|
186
292
|
router,
|
|
187
293
|
runtimeContext: runtimeState
|
|
188
294
|
});
|
|
189
|
-
const RouterContent =
|
|
190
|
-
|
|
191
|
-
children: /*#__PURE__*/ jsx(RouterClient, {
|
|
192
|
-
router: router
|
|
193
|
-
})
|
|
295
|
+
const RouterContent = needsRouterClient ? /*#__PURE__*/ jsx(ModernRouterClient, {
|
|
296
|
+
router: router
|
|
194
297
|
}) : /*#__PURE__*/ jsx(RouterProvider, {
|
|
195
298
|
router: router
|
|
196
299
|
});
|
|
197
|
-
|
|
300
|
+
const HydratableRouterContent = wrapTanstackSsrHydrationBoundary(RouterContent, hasSSRBootstrap);
|
|
301
|
+
if (needsRouterClient) hooks.onAfterHydrateRouter.call({
|
|
198
302
|
...lifecycleContext,
|
|
199
303
|
phase: 'hydrate',
|
|
200
304
|
router,
|
|
201
305
|
runtimeContext: runtimeState
|
|
202
306
|
});
|
|
203
307
|
return App ? /*#__PURE__*/ jsx(App, {
|
|
204
|
-
children:
|
|
205
|
-
}) :
|
|
308
|
+
children: HydratableRouterContent
|
|
309
|
+
}) : HydratableRouterContent;
|
|
206
310
|
};
|
|
207
311
|
return RouterWrapper;
|
|
208
312
|
});
|
|
@@ -6,24 +6,42 @@ import { createRequestContext, storage } from "@modern-js/runtime-utils/node";
|
|
|
6
6
|
import { time } from "@modern-js/runtime-utils/time";
|
|
7
7
|
import { LOADER_REPORTER_NAME } from "@modern-js/utils/universal/constants";
|
|
8
8
|
import { RouterProvider, createMemoryHistory, createRouter } from "@tanstack/react-router";
|
|
9
|
-
import { attachRouterServerSsrUtils } from "@tanstack/
|
|
10
|
-
import {
|
|
9
|
+
import { attachRouterServerSsrUtils } from "@tanstack/router-core/ssr/server";
|
|
10
|
+
import { useContext } from "react";
|
|
11
11
|
import { createModernBasepathRewrite } from "./basepathRewrite.mjs";
|
|
12
12
|
import { modifyRoutes, onAfterCreateRouter, onAfterHydrateRouter, onBeforeCreateRouter, onBeforeCreateRoutes, onBeforeHydrateRouter } from "./hooks.mjs";
|
|
13
|
+
import { wrapTanstackSsrHydrationBoundary } from "./hydrationBoundary.mjs";
|
|
13
14
|
import { applyRouterServerPrepareResult, createRouterServerSnapshot } from "./lifecycle.mjs";
|
|
14
15
|
import { createRouteTreeFromRouteObjects, getModernRouteIdsFromMatches } from "./routeTree.mjs";
|
|
15
16
|
import { createTanstackRscServerPayload, handleTanstackRscRedirect } from "./rsc/payloadRouter.mjs";
|
|
17
|
+
import { getModernTanstackRouterFastDefaults } from "./types.mjs";
|
|
16
18
|
import { createRouteObjectsFromConfig, urlJoin } from "./utils.mjs";
|
|
17
19
|
const setTanstackRscServerPayload = (payload)=>{
|
|
18
20
|
const storageContext = storage.useContext?.();
|
|
19
21
|
if (storageContext) storageContext.serverPayload = payload;
|
|
20
22
|
};
|
|
23
|
+
function isPromiseLike(value) {
|
|
24
|
+
return Boolean(value && 'function' == typeof value.then);
|
|
25
|
+
}
|
|
21
26
|
function isPreloadableRouteComponent(component) {
|
|
22
27
|
if (!component || 'function' != typeof component) return false;
|
|
23
28
|
const preloadable = component;
|
|
24
29
|
return 'function' == typeof preloadable.load || 'function' == typeof preloadable.preload;
|
|
25
30
|
}
|
|
31
|
+
function isReactLazyRouteComponent(component) {
|
|
32
|
+
return Boolean(component) && 'object' == typeof component && 'function' == typeof component._init && '_payload' in component;
|
|
33
|
+
}
|
|
34
|
+
async function preloadReactLazyRouteComponent(component) {
|
|
35
|
+
try {
|
|
36
|
+
component._init?.(component._payload);
|
|
37
|
+
} catch (thrown) {
|
|
38
|
+
if (!isPromiseLike(thrown)) throw thrown;
|
|
39
|
+
await thrown;
|
|
40
|
+
component._init?.(component._payload);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
26
43
|
async function preloadRouteComponent(component) {
|
|
44
|
+
if (isReactLazyRouteComponent(component)) return void await preloadReactLazyRouteComponent(component);
|
|
27
45
|
if (!isPreloadableRouteComponent(component)) return;
|
|
28
46
|
if ('function' == typeof component.load) return void await component.load({});
|
|
29
47
|
await component.preload?.({});
|
|
@@ -162,6 +180,7 @@ const tanstackRouterPlugin = (userConfig = {})=>{
|
|
|
162
180
|
};
|
|
163
181
|
hooks.onBeforeCreateRouter.call(routerLifecycleContext);
|
|
164
182
|
const tanstackRouter = createRouter({
|
|
183
|
+
...getModernTanstackRouterFastDefaults(mergedConfig),
|
|
165
184
|
routeTree,
|
|
166
185
|
history,
|
|
167
186
|
basepath: '/',
|
|
@@ -207,10 +226,12 @@ const tanstackRouterPlugin = (userConfig = {})=>{
|
|
|
207
226
|
await preloadMatchedRouteComponents(serverRouter);
|
|
208
227
|
context.ssrContext?.response.status(tanstackRouter.state.statusCode);
|
|
209
228
|
await serverRouter.serverSsr?.dehydrate?.();
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
229
|
+
if (isRSCNavigation) {
|
|
230
|
+
await waitForRouterSerialization(serverRouter);
|
|
231
|
+
setTanstackRscServerPayload(createTanstackRscServerPayload(serverRouter, {
|
|
232
|
+
omitClientLoaderData: true
|
|
233
|
+
}));
|
|
234
|
+
}
|
|
214
235
|
const ssrScriptTags = serverRouter.serverSsr?.takeBufferedScripts?.();
|
|
215
236
|
const hydrationScripts = routerManagedTagsToHtml(ssrScriptTags);
|
|
216
237
|
const matchedRouteIds = getModernRouteIdsFromMatches(serverRouter);
|
|
@@ -248,12 +269,9 @@ const tanstackRouterPlugin = (userConfig = {})=>{
|
|
|
248
269
|
if (!router) return App ? /*#__PURE__*/ jsx(App, {
|
|
249
270
|
...props
|
|
250
271
|
}) : null;
|
|
251
|
-
const routerWrapper = /*#__PURE__*/ jsx(
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
router: router
|
|
255
|
-
})
|
|
256
|
-
});
|
|
272
|
+
const routerWrapper = wrapTanstackSsrHydrationBoundary(/*#__PURE__*/ jsx(RouterProvider, {
|
|
273
|
+
router: router
|
|
274
|
+
}), true);
|
|
257
275
|
return App ? /*#__PURE__*/ jsx(App, {
|
|
258
276
|
children: routerWrapper
|
|
259
277
|
}) : routerWrapper;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import "node:module";
|
|
2
|
-
import { createRootRoute, createRoute, notFound, redirect } from "@tanstack/react-router";
|
|
2
|
+
import { createRootRoute, createRoute, notFound, redirect, rootRouteId } from "@tanstack/react-router";
|
|
3
|
+
import { createElement } from "react";
|
|
3
4
|
import { DefaultNotFound } from "./DefaultNotFound.mjs";
|
|
5
|
+
import { withModernRouteMatchContext } from "./outlet.mjs";
|
|
4
6
|
import { isTanstackRscPayloadNavigationEnabled, loadTanstackRscRouteData } from "./rsc/payloadRouter.mjs";
|
|
5
7
|
function createTanstackRoute(options) {
|
|
6
8
|
return createRoute(options);
|
|
@@ -8,6 +10,10 @@ function createTanstackRoute(options) {
|
|
|
8
10
|
function createTanstackRootRoute(options) {
|
|
9
11
|
return createRootRoute(options);
|
|
10
12
|
}
|
|
13
|
+
function wrapRouteComponentWithModernContext(route, component, routeId) {
|
|
14
|
+
const routeMatchId = routeId || route.id;
|
|
15
|
+
if (component && routeMatchId) route.options.component = withModernRouteMatchContext(component, routeMatchId);
|
|
16
|
+
}
|
|
11
17
|
function toTanstackPath(pathname) {
|
|
12
18
|
return pathname.split('/').map((segment)=>{
|
|
13
19
|
if (!segment) return segment;
|
|
@@ -55,6 +61,40 @@ function normalizeModernLoaderResponse(result) {
|
|
|
55
61
|
}
|
|
56
62
|
return normalizeModernLoaderResult(result);
|
|
57
63
|
}
|
|
64
|
+
function pickRouteModuleComponent(routeModule, seen = new Set()) {
|
|
65
|
+
if ('function' == typeof routeModule || routeModule && 'object' == typeof routeModule && '$$typeof' in routeModule) return routeModule;
|
|
66
|
+
if (!routeModule || 'object' != typeof routeModule) return;
|
|
67
|
+
if (seen.has(routeModule)) return;
|
|
68
|
+
seen.add(routeModule);
|
|
69
|
+
const module = routeModule;
|
|
70
|
+
for (const candidate of [
|
|
71
|
+
module.default,
|
|
72
|
+
module.Component
|
|
73
|
+
]){
|
|
74
|
+
const component = pickRouteModuleComponent(candidate, seen);
|
|
75
|
+
if (component) return component;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
function createServerLazyImportComponent(lazyImport, fallbackComponent) {
|
|
79
|
+
if ("u" > typeof document) return fallbackComponent;
|
|
80
|
+
let resolvedComponent;
|
|
81
|
+
let pendingLoad;
|
|
82
|
+
const load = async ()=>{
|
|
83
|
+
if (resolvedComponent) return resolvedComponent;
|
|
84
|
+
const routeModule = await lazyImport();
|
|
85
|
+
const component = pickRouteModuleComponent(routeModule);
|
|
86
|
+
if (component) resolvedComponent = component;
|
|
87
|
+
return resolvedComponent;
|
|
88
|
+
};
|
|
89
|
+
const Component = (props)=>{
|
|
90
|
+
if (resolvedComponent) return createElement(resolvedComponent, props);
|
|
91
|
+
pendingLoad ||= load();
|
|
92
|
+
throw pendingLoad;
|
|
93
|
+
};
|
|
94
|
+
Component.load = load;
|
|
95
|
+
Component.preload = load;
|
|
96
|
+
return Component;
|
|
97
|
+
}
|
|
58
98
|
function isAbsoluteUrl(value) {
|
|
59
99
|
try {
|
|
60
100
|
new URL(value);
|
|
@@ -127,7 +167,7 @@ function wrapModernLoader(modernRoute, modernLoader, revalidationState, options
|
|
|
127
167
|
const signal = ctx?.abortController?.signal || ctx?.signal || new AbortController().signal;
|
|
128
168
|
const baseRequest = ctx?.context?.request instanceof Request ? ctx.context.request : void 0;
|
|
129
169
|
const href = 'string' == typeof ctx?.location ? ctx.location : ctx?.location?.publicHref || ctx?.location?.href || ctx?.location?.url?.href || '';
|
|
130
|
-
const request = baseRequest ? new Request(baseRequest, {
|
|
170
|
+
const request = void 0 !== baseRequest ? new Request(baseRequest, {
|
|
131
171
|
signal
|
|
132
172
|
}) : createModernRequest(href, signal);
|
|
133
173
|
const params = mapParamsForModernLoader({
|
|
@@ -192,7 +232,7 @@ function wrapRouteObjectLoader(route, revalidationState, options = {}) {
|
|
|
192
232
|
const signal = ctx?.abortController?.signal || ctx?.signal || new AbortController().signal;
|
|
193
233
|
const baseRequest = ctx?.context?.request instanceof Request ? ctx.context.request : void 0;
|
|
194
234
|
const href = 'string' == typeof ctx?.location ? ctx.location : ctx?.location?.publicHref || ctx?.location?.href || ctx?.location?.url?.href || '';
|
|
195
|
-
const request = baseRequest ? new Request(baseRequest, {
|
|
235
|
+
const request = void 0 !== baseRequest ? new Request(baseRequest, {
|
|
196
236
|
signal
|
|
197
237
|
}) : createModernRequest(href, signal);
|
|
198
238
|
const params = mapParamsForRouteObjectLoader({
|
|
@@ -229,10 +269,18 @@ function wrapRouteObjectLoader(route, revalidationState, options = {}) {
|
|
|
229
269
|
}
|
|
230
270
|
function toRouteComponent(routeObject) {
|
|
231
271
|
const route = routeObject;
|
|
272
|
+
const lazyImport = 'function' == typeof route.lazyImport ? route.lazyImport : void 0;
|
|
273
|
+
const fallbackComponent = route.Component ? route.Component : route.element ? ()=>route.element : void 0;
|
|
274
|
+
if (lazyImport && fallbackComponent) return createServerLazyImportComponent(lazyImport, fallbackComponent);
|
|
232
275
|
if (route.Component) return route.Component;
|
|
233
276
|
const element = route.element;
|
|
234
277
|
if (element) return ()=>element;
|
|
235
278
|
}
|
|
279
|
+
function toModernRouteComponent(route) {
|
|
280
|
+
const component = route.component || void 0;
|
|
281
|
+
if ('function' == typeof route.lazyImport && component) return createServerLazyImportComponent(route.lazyImport, component);
|
|
282
|
+
return component;
|
|
283
|
+
}
|
|
236
284
|
function toErrorComponent(routeObject) {
|
|
237
285
|
const route = routeObject;
|
|
238
286
|
if (route.ErrorBoundary) return route.ErrorBoundary;
|
|
@@ -270,12 +318,14 @@ function createRouteFromRouteObject(opts) {
|
|
|
270
318
|
const shouldRevalidate = modernRouteObject.shouldRevalidate;
|
|
271
319
|
const shouldReload = createModernShouldReload(shouldRevalidate, revalidationState);
|
|
272
320
|
const stableFallbackId = routeObject.id || modernRouteObject.file || routeObject.path || 'pathless';
|
|
321
|
+
const component = toRouteComponent(routeObject);
|
|
273
322
|
const base = {
|
|
274
323
|
getParentRoute: ()=>parent,
|
|
275
|
-
component
|
|
324
|
+
component,
|
|
276
325
|
pendingComponent: toPendingComponent(routeObject),
|
|
277
326
|
errorComponent: toErrorComponent(routeObject),
|
|
278
|
-
|
|
327
|
+
validateSearch: modernRouteObject.validateSearch,
|
|
328
|
+
loaderDeps: modernRouteObject.loaderDeps,
|
|
279
329
|
staticData: createRouteStaticData({
|
|
280
330
|
modernRouteId: routeObject.id,
|
|
281
331
|
modernRouteAction: modernRouteObject.action,
|
|
@@ -294,6 +344,7 @@ function createRouteFromRouteObject(opts) {
|
|
|
294
344
|
if (isRouteObjectPathlessLayout(routeObject)) base.id = stableFallbackId;
|
|
295
345
|
else base.path = routeObject.index ? '/' : toTanstackPath(routeObject.path || '');
|
|
296
346
|
const route = createTanstackRoute(base);
|
|
347
|
+
wrapRouteComponentWithModernContext(route, component, routeObject.id);
|
|
297
348
|
const children = routeObject.children;
|
|
298
349
|
if (children && children.length > 0) {
|
|
299
350
|
const childRoutes = children.map((child)=>createRouteFromRouteObject({
|
|
@@ -313,7 +364,7 @@ function createRouteFromModernRoute(opts) {
|
|
|
313
364
|
const stableFallbackId = modernId || route._component || route.filename || route.data || ('function' == typeof route.loader ? route.id : void 0);
|
|
314
365
|
const pendingComponent = route.loading || route.pendingComponent;
|
|
315
366
|
const errorComponent = route.error || route.errorComponent;
|
|
316
|
-
const component = route
|
|
367
|
+
const component = toModernRouteComponent(route);
|
|
317
368
|
const modernLoader = route.loader;
|
|
318
369
|
const modernAction = route.action;
|
|
319
370
|
const modernShouldRevalidate = route.shouldRevalidate;
|
|
@@ -325,7 +376,8 @@ function createRouteFromModernRoute(opts) {
|
|
|
325
376
|
component: component || void 0,
|
|
326
377
|
pendingComponent: pendingComponent || void 0,
|
|
327
378
|
errorComponent: errorComponent || void 0,
|
|
328
|
-
|
|
379
|
+
validateSearch: route.validateSearch,
|
|
380
|
+
loaderDeps: route.loaderDeps,
|
|
329
381
|
staticData: createRouteStaticData({
|
|
330
382
|
modernRouteId: modernId,
|
|
331
383
|
modernRouteAction: modernAction,
|
|
@@ -347,6 +399,7 @@ function createRouteFromModernRoute(opts) {
|
|
|
347
399
|
base.path = isIndexRoute ? '/' : toTanstackPath(rawPath || '');
|
|
348
400
|
}
|
|
349
401
|
const tanstackRoute = createTanstackRoute(base);
|
|
402
|
+
wrapRouteComponentWithModernContext(tanstackRoute, component, modernId);
|
|
350
403
|
const children = route.children;
|
|
351
404
|
if (children && children.length > 0) {
|
|
352
405
|
const childRoutes = children.map((child)=>createRouteFromModernRoute({
|
|
@@ -360,7 +413,7 @@ function createRouteFromModernRoute(opts) {
|
|
|
360
413
|
}
|
|
361
414
|
function createRouteTreeFromModernRoutes(routes, options = {}) {
|
|
362
415
|
const rootModern = routes.find((r)=>r && 'nested' === r.type && r.isRoot);
|
|
363
|
-
const rootComponent = rootModern
|
|
416
|
+
const rootComponent = rootModern ? toModernRouteComponent(rootModern) : void 0;
|
|
364
417
|
const pendingComponent = rootModern?.loading;
|
|
365
418
|
const errorComponent = rootModern?.error;
|
|
366
419
|
const rootLoader = rootModern?.loader;
|
|
@@ -373,7 +426,8 @@ function createRouteTreeFromModernRoutes(routes, options = {}) {
|
|
|
373
426
|
component: rootComponent || void 0,
|
|
374
427
|
pendingComponent: pendingComponent || void 0,
|
|
375
428
|
errorComponent: errorComponent || void 0,
|
|
376
|
-
|
|
429
|
+
validateSearch: rootModern?.validateSearch,
|
|
430
|
+
loaderDeps: rootModern?.loaderDeps,
|
|
377
431
|
notFoundComponent: DefaultNotFound,
|
|
378
432
|
staticData: createRouteStaticData({
|
|
379
433
|
modernRouteId: rootModernId,
|
|
@@ -391,6 +445,7 @@ function createRouteTreeFromModernRoutes(routes, options = {}) {
|
|
|
391
445
|
if (rootShouldReload) rootRouteOptions.shouldReload = rootShouldReload;
|
|
392
446
|
if (rootModern?.inValidSSRRoute) rootRouteOptions.ssr = false;
|
|
393
447
|
const rootRoute = createTanstackRootRoute(rootRouteOptions);
|
|
448
|
+
if (rootComponent) rootRoute.options.component = withModernRouteMatchContext(rootComponent, rootRouteId);
|
|
394
449
|
const topLevel = rootModern ? rootModern.children || [] : routes;
|
|
395
450
|
const childRoutes = topLevel.map((child)=>createRouteFromModernRoute({
|
|
396
451
|
options,
|
|
@@ -408,11 +463,13 @@ function createRouteTreeFromRouteObjects(routes, options = {}) {
|
|
|
408
463
|
const rootRevalidationState = {};
|
|
409
464
|
const rootShouldRevalidate = rootLikeRoute?.shouldRevalidate;
|
|
410
465
|
const rootShouldReload = createModernShouldReload(rootShouldRevalidate, rootRevalidationState);
|
|
466
|
+
const rootComponent = rootLikeRoute ? toRouteComponent(rootLikeRoute) : void 0;
|
|
411
467
|
const rootRouteOptions = {
|
|
412
|
-
component:
|
|
468
|
+
component: rootComponent,
|
|
413
469
|
pendingComponent: rootLikeRoute ? toPendingComponent(rootLikeRoute) : void 0,
|
|
414
470
|
errorComponent: rootLikeRoute ? toErrorComponent(rootLikeRoute) : void 0,
|
|
415
|
-
|
|
471
|
+
validateSearch: rootLikeRoute?.validateSearch,
|
|
472
|
+
loaderDeps: rootLikeRoute?.loaderDeps,
|
|
416
473
|
notFoundComponent: DefaultNotFound,
|
|
417
474
|
staticData: createRouteStaticData({
|
|
418
475
|
modernRouteId: rootLikeRoute?.id,
|
|
@@ -430,6 +487,7 @@ function createRouteTreeFromRouteObjects(routes, options = {}) {
|
|
|
430
487
|
if (rootShouldReload) rootRouteOptions.shouldReload = rootShouldReload;
|
|
431
488
|
if (rootLikeRoute?.inValidSSRRoute) rootRouteOptions.ssr = false;
|
|
432
489
|
const rootRoute = createTanstackRootRoute(rootRouteOptions);
|
|
490
|
+
if (rootComponent) rootRoute.options.component = withModernRouteMatchContext(rootComponent, rootRouteId);
|
|
433
491
|
const topLevel = rootLikeRoute ? [
|
|
434
492
|
...rootLikeRoute.children || [],
|
|
435
493
|
...routes.filter((route)=>route !== rootLikeRoute)
|
|
@@ -444,9 +502,11 @@ function createRouteTreeFromRouteObjects(routes, options = {}) {
|
|
|
444
502
|
}
|
|
445
503
|
function getModernRouteIdsFromMatches(router) {
|
|
446
504
|
const matches = router.state.matches || [];
|
|
505
|
+
const routesById = router.routesById;
|
|
447
506
|
const ids = matches.map((match)=>{
|
|
448
|
-
const
|
|
449
|
-
|
|
507
|
+
const normalizedMatch = match;
|
|
508
|
+
const routeId = 'string' == typeof normalizedMatch.routeId ? normalizedMatch.routeId : void 0;
|
|
509
|
+
return normalizedMatch.route?.options?.staticData?.modernRouteId ?? (routeId ? routesById?.[routeId]?.options?.staticData?.modernRouteId : void 0);
|
|
450
510
|
}).filter((id)=>'string' == typeof id);
|
|
451
511
|
return Array.from(new Set(ids));
|
|
452
512
|
}
|