@bleedingdev/modern-js-plugin-tanstack 3.2.0-ultramodern.1 → 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 +15 -6
- 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 +7 -7
- 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 +7 -7
- 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 +14 -14
- package/src/cli/index.ts +32 -18
- package/src/cli/routeSplitting.ts +81 -0
- package/src/cli/tanstackTypes.ts +217 -67
- package/src/runtime/basepathRewrite.ts +1 -0
- package/src/runtime/dataMutation.tsx +1 -0
- package/src/runtime/hydrationBoundary.tsx +12 -0
- package/src/runtime/index.tsx +107 -2
- package/src/runtime/lifecycle.ts +1 -0
- package/src/runtime/outlet.tsx +42 -0
- package/src/runtime/plugin.node.tsx +58 -8
- package/src/runtime/plugin.tsx +354 -149
- package/src/runtime/plugin.worker.tsx +4 -0
- package/src/runtime/routeTree.ts +195 -23
- package/src/runtime/rsc/ClientSlot.tsx +1 -0
- package/src/runtime/rsc/CompositeComponent.tsx +1 -0
- package/src/runtime/rsc/ReplayableStream.ts +1 -0
- package/src/runtime/rsc/RscNodeRenderer.tsx +1 -0
- package/src/runtime/rsc/client.tsx +2 -3
- package/src/runtime/rsc/createRscProxy.tsx +1 -0
- package/src/runtime/rsc/payloadRouter.ts +1 -0
- package/src/runtime/rsc/server.tsx +1 -0
- package/src/runtime/rsc/slotUsageSanitizer.ts +1 -0
- package/src/runtime/ssr-shim.d.ts +1 -3
- package/src/runtime/types.ts +13 -0
- package/src/runtime/utils.tsx +1 -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
package/src/runtime/plugin.tsx
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
// @effect-diagnostics globalConsole:off strictBooleanExpressions:off
|
|
1
2
|
/// <reference path="./ssr-shim.d.ts" />
|
|
2
3
|
|
|
3
4
|
import type { Plugin, RuntimePluginExtends } from '@modern-js/plugin';
|
|
@@ -23,8 +24,7 @@ import {
|
|
|
23
24
|
useNavigate,
|
|
24
25
|
useRouter,
|
|
25
26
|
} from '@tanstack/react-router';
|
|
26
|
-
import {
|
|
27
|
-
import * as React from 'react';
|
|
27
|
+
import { hydrate as hydrateTanstackRouter } from '@tanstack/react-router/ssr/client';
|
|
28
28
|
import { useContext, useMemo } from 'react';
|
|
29
29
|
import { createModernBasepathRewrite } from './basepathRewrite';
|
|
30
30
|
import {
|
|
@@ -36,13 +36,19 @@ import {
|
|
|
36
36
|
onBeforeHydrateRouter as onBeforeHydrateRouterHook,
|
|
37
37
|
type RouterExtendsHooks,
|
|
38
38
|
} from './hooks';
|
|
39
|
+
import { wrapTanstackSsrHydrationBoundary } from './hydrationBoundary';
|
|
39
40
|
import {
|
|
40
41
|
applyRouterRuntimeState,
|
|
41
42
|
type RouterLifecycleContext,
|
|
42
43
|
} from './lifecycle';
|
|
44
|
+
import { withModernRouteMatchContext } from './outlet';
|
|
45
|
+
import { Link } from './prefetchLink';
|
|
43
46
|
import { createRouteTreeFromRouteObjects } from './routeTree';
|
|
44
47
|
import { getTanstackRscSerializationAdapters } from './rsc/client';
|
|
45
|
-
import
|
|
48
|
+
import {
|
|
49
|
+
getModernTanstackRouterFastDefaults,
|
|
50
|
+
type RouterConfig,
|
|
51
|
+
} from './types';
|
|
46
52
|
import { createRouteObjectsFromConfig, urlJoin } from './utils';
|
|
47
53
|
|
|
48
54
|
const BLOCKING_SUBSCRIBE_SYMBOL = Symbol.for(
|
|
@@ -51,7 +57,6 @@ const BLOCKING_SUBSCRIBE_SYMBOL = Symbol.for(
|
|
|
51
57
|
const BLOCKING_STATE_SYMBOL = Symbol.for(
|
|
52
58
|
'@modern-js/plugin-tanstack:blocking-state',
|
|
53
59
|
);
|
|
54
|
-
|
|
55
60
|
type TanstackRouterWithSubscribe = {
|
|
56
61
|
[BLOCKING_STATE_SYMBOL]?: () => boolean;
|
|
57
62
|
[BLOCKING_SUBSCRIBE_SYMBOL]?: boolean;
|
|
@@ -65,6 +70,30 @@ type WindowWithTanstackSsr = Window & {
|
|
|
65
70
|
$_TSR?: unknown;
|
|
66
71
|
};
|
|
67
72
|
|
|
73
|
+
type RouteComponentPreloadable = {
|
|
74
|
+
load?: () => Promise<unknown>;
|
|
75
|
+
preload?: () => Promise<unknown>;
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
type ModernRouteModule = {
|
|
79
|
+
Component?: unknown;
|
|
80
|
+
default?: unknown;
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
type RouterWithPreloadableRoutes = AnyRouter & {
|
|
84
|
+
routesById?: Record<
|
|
85
|
+
string,
|
|
86
|
+
{
|
|
87
|
+
options?: {
|
|
88
|
+
component?: unknown;
|
|
89
|
+
staticData?: {
|
|
90
|
+
modernRouteId?: string;
|
|
91
|
+
};
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
>;
|
|
95
|
+
};
|
|
96
|
+
|
|
68
97
|
type TanstackRouterRuntimeConfig = {
|
|
69
98
|
plugins?: TanstackRouterRuntimePlugin[];
|
|
70
99
|
router?: Partial<RouterConfig>;
|
|
@@ -128,6 +157,144 @@ function wrapRouterSubscribeWithBlockState(
|
|
|
128
157
|
target[BLOCKING_SUBSCRIBE_SYMBOL] = true;
|
|
129
158
|
}
|
|
130
159
|
|
|
160
|
+
type RouterHydrationRecord = {
|
|
161
|
+
error?: unknown;
|
|
162
|
+
promise: Promise<unknown>;
|
|
163
|
+
status: 'pending' | 'fulfilled' | 'rejected';
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
const routerHydrationRecords = new WeakMap<AnyRouter, RouterHydrationRecord>();
|
|
167
|
+
const routeModulesKey = '_routeModules';
|
|
168
|
+
|
|
169
|
+
function pickRouteModuleComponent(
|
|
170
|
+
routeModule: unknown,
|
|
171
|
+
seen: Set<unknown> = new Set(),
|
|
172
|
+
): unknown {
|
|
173
|
+
if (
|
|
174
|
+
typeof routeModule === 'function' ||
|
|
175
|
+
(routeModule &&
|
|
176
|
+
typeof routeModule === 'object' &&
|
|
177
|
+
'$$typeof' in routeModule)
|
|
178
|
+
) {
|
|
179
|
+
return routeModule;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (!routeModule || typeof routeModule !== 'object') {
|
|
183
|
+
return undefined;
|
|
184
|
+
}
|
|
185
|
+
if (seen.has(routeModule)) {
|
|
186
|
+
return undefined;
|
|
187
|
+
}
|
|
188
|
+
seen.add(routeModule);
|
|
189
|
+
|
|
190
|
+
const module = routeModule as ModernRouteModule;
|
|
191
|
+
for (const candidate of [module.default, module.Component]) {
|
|
192
|
+
const component = pickRouteModuleComponent(candidate, seen);
|
|
193
|
+
if (component) {
|
|
194
|
+
return component;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
return undefined;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function getCachedRouteModule(routeId: string) {
|
|
202
|
+
if (typeof window === 'undefined') {
|
|
203
|
+
return undefined;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
return (window as unknown as Record<string, Record<string, unknown>>)[
|
|
207
|
+
routeModulesKey
|
|
208
|
+
]?.[routeId];
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
async function preloadHydratedRouteComponents(router: AnyRouter) {
|
|
212
|
+
const preloadableRouter = router as RouterWithPreloadableRoutes;
|
|
213
|
+
const routesById = preloadableRouter.routesById || {};
|
|
214
|
+
const matches = preloadableRouter.stores.matches.get() as Array<{
|
|
215
|
+
routeId?: string;
|
|
216
|
+
}>;
|
|
217
|
+
|
|
218
|
+
await Promise.all(
|
|
219
|
+
matches.map(match => {
|
|
220
|
+
if (!match.routeId) {
|
|
221
|
+
return undefined;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const route = routesById[match.routeId];
|
|
225
|
+
const component = route?.options?.component as RouteComponentPreloadable;
|
|
226
|
+
const preload = component?.load || component?.preload;
|
|
227
|
+
if (typeof preload !== 'function') {
|
|
228
|
+
return undefined;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
return Promise.resolve(preload.call(component)).then(routeModule => {
|
|
232
|
+
const modernRouteId = route?.options?.staticData?.modernRouteId;
|
|
233
|
+
const resolvedComponent = pickRouteModuleComponent(
|
|
234
|
+
(modernRouteId && getCachedRouteModule(modernRouteId)) || routeModule,
|
|
235
|
+
);
|
|
236
|
+
if (resolvedComponent && modernRouteId) {
|
|
237
|
+
route.options.component = withModernRouteMatchContext(
|
|
238
|
+
resolvedComponent,
|
|
239
|
+
modernRouteId,
|
|
240
|
+
);
|
|
241
|
+
}
|
|
242
|
+
});
|
|
243
|
+
}),
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function getTanstackSsrHydrationRecord(router: AnyRouter) {
|
|
248
|
+
let hydrationRecord = routerHydrationRecords.get(router);
|
|
249
|
+
if (!hydrationRecord) {
|
|
250
|
+
hydrationRecord = {
|
|
251
|
+
promise: Promise.resolve(),
|
|
252
|
+
status: 'pending',
|
|
253
|
+
};
|
|
254
|
+
routerHydrationRecords.set(router, hydrationRecord);
|
|
255
|
+
try {
|
|
256
|
+
hydrationRecord.promise = hydrateTanstackRouter(router)
|
|
257
|
+
.then(async value => {
|
|
258
|
+
await preloadHydratedRouteComponents(router);
|
|
259
|
+
return value;
|
|
260
|
+
})
|
|
261
|
+
.then(
|
|
262
|
+
value => {
|
|
263
|
+
hydrationRecord.status = 'fulfilled';
|
|
264
|
+
return value;
|
|
265
|
+
},
|
|
266
|
+
error => {
|
|
267
|
+
hydrationRecord.status = 'rejected';
|
|
268
|
+
hydrationRecord.error = error;
|
|
269
|
+
throw error;
|
|
270
|
+
},
|
|
271
|
+
);
|
|
272
|
+
} catch (error) {
|
|
273
|
+
hydrationRecord.status = 'rejected';
|
|
274
|
+
hydrationRecord.error = error;
|
|
275
|
+
hydrationRecord.promise = Promise.reject(error);
|
|
276
|
+
hydrationRecord.promise.catch(() => {});
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
return hydrationRecord;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
function getTanstackSsrHydrationPromise(router: AnyRouter) {
|
|
283
|
+
return getTanstackSsrHydrationRecord(router).promise;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
function hasTanstackSsrHydrationRecord(router: AnyRouter) {
|
|
287
|
+
return routerHydrationRecords.has(router);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
function ModernRouterClient({ router }: { router: AnyRouter }) {
|
|
291
|
+
const hydrationRecord = getTanstackSsrHydrationRecord(router);
|
|
292
|
+
if (hydrationRecord.status === 'rejected') {
|
|
293
|
+
throw hydrationRecord.error;
|
|
294
|
+
}
|
|
295
|
+
return <RouterProvider router={router} />;
|
|
296
|
+
}
|
|
297
|
+
|
|
131
298
|
function stripSyntheticNotFoundRoute(routes: RouteObject[]): RouteObject[] {
|
|
132
299
|
return routes
|
|
133
300
|
.filter(route => !(route.path === '*' && !route.id && !route.loader))
|
|
@@ -156,14 +323,151 @@ export const tanstackRouterPlugin = (
|
|
|
156
323
|
onBeforeHydrateRouter: onBeforeHydrateRouterHook,
|
|
157
324
|
},
|
|
158
325
|
setup: (api: TanstackRouterPluginAPI) => {
|
|
159
|
-
api.
|
|
326
|
+
const hooks = api.getHooks();
|
|
327
|
+
let cachedRouteObjects: RouteObject[] | undefined;
|
|
328
|
+
let cachedRouteTree: ReturnType<
|
|
329
|
+
typeof createRouteTreeFromRouteObjects
|
|
330
|
+
> | null = null;
|
|
331
|
+
let cachedRouter: AnyRouter | null = null;
|
|
332
|
+
let cachedRouterBasepath: string | null = null;
|
|
333
|
+
|
|
334
|
+
const getMergedConfig = () => {
|
|
160
335
|
const pluginConfig = api.getRuntimeConfig() as {
|
|
161
336
|
router?: Partial<RouterConfig>;
|
|
162
337
|
};
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
338
|
+
return merge(pluginConfig.router || {}, userConfig) as RouterConfig;
|
|
339
|
+
};
|
|
340
|
+
|
|
341
|
+
const getRouteObjects = () => {
|
|
342
|
+
if (typeof cachedRouteObjects !== 'undefined') {
|
|
343
|
+
return cachedRouteObjects;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
const mergedConfig = getMergedConfig();
|
|
347
|
+
const { routesConfig, createRoutes } = mergedConfig;
|
|
348
|
+
const finalRouteConfig = {
|
|
349
|
+
routes: getGlobalRoutes(),
|
|
350
|
+
globalApp: getGlobalLayoutApp(),
|
|
351
|
+
...routesConfig,
|
|
352
|
+
};
|
|
353
|
+
|
|
354
|
+
const routeObjects = createRoutes
|
|
355
|
+
? createRoutes()
|
|
356
|
+
: createRouteObjectsFromConfig({
|
|
357
|
+
routesConfig: finalRouteConfig,
|
|
358
|
+
}) || [];
|
|
359
|
+
|
|
360
|
+
const normalizedRouteObjects = createRoutes
|
|
361
|
+
? routeObjects
|
|
362
|
+
: stripSyntheticNotFoundRoute(routeObjects);
|
|
363
|
+
|
|
364
|
+
cachedRouteObjects = hooks.modifyRoutes.call(
|
|
365
|
+
normalizedRouteObjects,
|
|
366
|
+
) as RouteObject[];
|
|
367
|
+
return cachedRouteObjects;
|
|
368
|
+
};
|
|
369
|
+
|
|
370
|
+
const getRouteTree = () => {
|
|
371
|
+
if (cachedRouteTree) {
|
|
372
|
+
return cachedRouteTree;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
const routeObjects = getRouteObjects();
|
|
376
|
+
if (!routeObjects.length) {
|
|
377
|
+
return null;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
cachedRouteTree = createRouteTreeFromRouteObjects(routeObjects, {
|
|
381
|
+
rscPayloadRouter: getGlobalEnableRsc(),
|
|
382
|
+
});
|
|
383
|
+
return cachedRouteTree;
|
|
384
|
+
};
|
|
385
|
+
|
|
386
|
+
const selectBasePath = (pathname: string) => {
|
|
387
|
+
const { serverBase = [] } = getMergedConfig();
|
|
388
|
+
const match = serverBase.find(baseUrl =>
|
|
389
|
+
isSegmentPrefix(pathname, baseUrl),
|
|
390
|
+
);
|
|
391
|
+
return match || '/';
|
|
392
|
+
};
|
|
393
|
+
|
|
394
|
+
const getClientBasename = (runtimeContext: TInternalRuntimeContext) => {
|
|
395
|
+
const { basename = '' } = getMergedConfig();
|
|
396
|
+
const baseUrl = selectBasePath(location.pathname).replace(/^\/*/, '/');
|
|
397
|
+
return baseUrl === '/'
|
|
398
|
+
? urlJoin(
|
|
399
|
+
baseUrl,
|
|
400
|
+
runtimeContext._internalRouterBaseName || basename || '',
|
|
401
|
+
)
|
|
402
|
+
: baseUrl;
|
|
403
|
+
};
|
|
404
|
+
|
|
405
|
+
const getRouter = (
|
|
406
|
+
runtimeContext: TInternalRuntimeContext,
|
|
407
|
+
_basename: string,
|
|
408
|
+
) => {
|
|
409
|
+
const routeTree = getRouteTree();
|
|
410
|
+
if (!routeTree) {
|
|
411
|
+
return null;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
const lifecycleContext: RouterLifecycleContext = {
|
|
415
|
+
framework: 'tanstack',
|
|
416
|
+
phase: 'client-create',
|
|
417
|
+
routes: getRouteObjects(),
|
|
418
|
+
runtimeContext,
|
|
419
|
+
basename: _basename,
|
|
420
|
+
};
|
|
421
|
+
hooks.onBeforeCreateRouter.call(lifecycleContext);
|
|
422
|
+
|
|
423
|
+
if (cachedRouter && cachedRouterBasepath === _basename) {
|
|
424
|
+
wrapRouterSubscribeWithBlockState(
|
|
425
|
+
cachedRouter,
|
|
426
|
+
runtimeContext.unstable_getBlockNavState,
|
|
427
|
+
);
|
|
428
|
+
hooks.onAfterCreateRouter.call({
|
|
429
|
+
...lifecycleContext,
|
|
430
|
+
router: cachedRouter,
|
|
431
|
+
runtimeContext,
|
|
432
|
+
});
|
|
433
|
+
return cachedRouter;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
const mergedConfig = getMergedConfig();
|
|
437
|
+
const { supportHtml5History = true } = mergedConfig;
|
|
438
|
+
const history = supportHtml5History
|
|
439
|
+
? createBrowserHistory()
|
|
440
|
+
: createHashHistory();
|
|
441
|
+
const rewrite = createModernBasepathRewrite(_basename);
|
|
442
|
+
const serializationAdapters = getGlobalEnableRsc()
|
|
443
|
+
? getTanstackRscSerializationAdapters()
|
|
444
|
+
: undefined;
|
|
445
|
+
|
|
446
|
+
cachedRouter = createRouter({
|
|
447
|
+
...getModernTanstackRouterFastDefaults(mergedConfig),
|
|
448
|
+
routeTree,
|
|
449
|
+
basepath: '/',
|
|
450
|
+
rewrite,
|
|
451
|
+
history,
|
|
452
|
+
context: {},
|
|
453
|
+
...(serializationAdapters ? { serializationAdapters } : {}),
|
|
454
|
+
});
|
|
455
|
+
cachedRouterBasepath = _basename;
|
|
456
|
+
wrapRouterSubscribeWithBlockState(
|
|
457
|
+
cachedRouter,
|
|
458
|
+
runtimeContext.unstable_getBlockNavState,
|
|
459
|
+
);
|
|
460
|
+
hooks.onAfterCreateRouter.call({
|
|
461
|
+
...lifecycleContext,
|
|
462
|
+
router: cachedRouter,
|
|
463
|
+
runtimeContext,
|
|
464
|
+
});
|
|
465
|
+
|
|
466
|
+
return cachedRouter;
|
|
467
|
+
};
|
|
468
|
+
|
|
469
|
+
api.onBeforeRender(async context => {
|
|
470
|
+
const mergedConfig = getMergedConfig();
|
|
167
471
|
if (
|
|
168
472
|
typeof window !== 'undefined' &&
|
|
169
473
|
(window as { _SSR_DATA?: unknown })._SSR_DATA &&
|
|
@@ -183,104 +487,44 @@ export const tanstackRouterPlugin = (
|
|
|
183
487
|
}
|
|
184
488
|
|
|
185
489
|
context.router = {
|
|
490
|
+
Link,
|
|
186
491
|
useMatches,
|
|
187
492
|
useLocation,
|
|
188
493
|
useNavigate,
|
|
189
494
|
useRouter,
|
|
190
495
|
};
|
|
191
|
-
});
|
|
192
496
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
}
|
|
497
|
+
const hasSSRBootstrap =
|
|
498
|
+
typeof window !== 'undefined' &&
|
|
499
|
+
Boolean((window as WindowWithTanstackSsr).$_TSR);
|
|
500
|
+
if (hasSSRBootstrap && getRouteObjects().length) {
|
|
501
|
+
const runtimeContext = context as TInternalRuntimeContext;
|
|
502
|
+
const router = getRouter(
|
|
503
|
+
runtimeContext,
|
|
504
|
+
getClientBasename(runtimeContext),
|
|
505
|
+
);
|
|
506
|
+
if (router) {
|
|
507
|
+
await getTanstackSsrHydrationPromise(router);
|
|
508
|
+
}
|
|
509
|
+
}
|
|
206
510
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
globalApp: getGlobalLayoutApp(),
|
|
210
|
-
...routesConfig,
|
|
211
|
-
};
|
|
511
|
+
return;
|
|
512
|
+
});
|
|
212
513
|
|
|
213
|
-
|
|
514
|
+
api.wrapRoot(App => {
|
|
515
|
+
if (!getRouteObjects().length) {
|
|
214
516
|
return App;
|
|
215
517
|
}
|
|
216
518
|
|
|
217
|
-
const hooks = api.getHooks();
|
|
218
|
-
|
|
219
|
-
let cachedRouteObjects: RouteObject[] | undefined;
|
|
220
|
-
|
|
221
|
-
const getRouteObjects = () => {
|
|
222
|
-
if (typeof cachedRouteObjects !== 'undefined') {
|
|
223
|
-
return cachedRouteObjects;
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
const routeObjects = createRoutes
|
|
227
|
-
? createRoutes()
|
|
228
|
-
: createRouteObjectsFromConfig({
|
|
229
|
-
routesConfig: finalRouteConfig,
|
|
230
|
-
}) || [];
|
|
231
|
-
|
|
232
|
-
const normalizedRouteObjects = createRoutes
|
|
233
|
-
? routeObjects
|
|
234
|
-
: stripSyntheticNotFoundRoute(routeObjects);
|
|
235
|
-
|
|
236
|
-
cachedRouteObjects = hooks.modifyRoutes.call(
|
|
237
|
-
normalizedRouteObjects,
|
|
238
|
-
) as RouteObject[];
|
|
239
|
-
return cachedRouteObjects;
|
|
240
|
-
};
|
|
241
|
-
|
|
242
|
-
const selectBasePath = (pathname: string) => {
|
|
243
|
-
const match = serverBase.find(baseUrl =>
|
|
244
|
-
isSegmentPrefix(pathname, baseUrl),
|
|
245
|
-
);
|
|
246
|
-
return match || '/';
|
|
247
|
-
};
|
|
248
|
-
|
|
249
|
-
let cachedRouteTree: ReturnType<
|
|
250
|
-
typeof createRouteTreeFromRouteObjects
|
|
251
|
-
> | null = null;
|
|
252
|
-
let cachedRouter: AnyRouter | null = null;
|
|
253
|
-
let cachedRouterBasepath: string | null = null;
|
|
254
|
-
|
|
255
519
|
const RouterWrapper = () => {
|
|
256
520
|
const runtimeContext = useContext(
|
|
257
521
|
InternalRuntimeContext,
|
|
258
522
|
) as TInternalRuntimeContext;
|
|
259
523
|
|
|
260
|
-
const
|
|
261
|
-
/^\/*/,
|
|
262
|
-
'/',
|
|
263
|
-
);
|
|
264
|
-
const _basename =
|
|
265
|
-
baseUrl === '/'
|
|
266
|
-
? urlJoin(
|
|
267
|
-
baseUrl,
|
|
268
|
-
runtimeContext._internalRouterBaseName || basename || '',
|
|
269
|
-
)
|
|
270
|
-
: baseUrl;
|
|
524
|
+
const _basename = getClientBasename(runtimeContext);
|
|
271
525
|
|
|
272
526
|
const routeTree = useMemo(() => {
|
|
273
|
-
|
|
274
|
-
return cachedRouteTree;
|
|
275
|
-
}
|
|
276
|
-
const routeObjects = getRouteObjects();
|
|
277
|
-
if (!routeObjects.length) {
|
|
278
|
-
return null;
|
|
279
|
-
}
|
|
280
|
-
cachedRouteTree = createRouteTreeFromRouteObjects(routeObjects, {
|
|
281
|
-
rscPayloadRouter: getGlobalEnableRsc(),
|
|
282
|
-
});
|
|
283
|
-
return cachedRouteTree;
|
|
527
|
+
return getRouteTree();
|
|
284
528
|
}, []);
|
|
285
529
|
|
|
286
530
|
if (!routeTree) {
|
|
@@ -288,58 +532,11 @@ export const tanstackRouterPlugin = (
|
|
|
288
532
|
}
|
|
289
533
|
|
|
290
534
|
const router = useMemo(() => {
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
basename: _basename,
|
|
297
|
-
};
|
|
298
|
-
hooks.onBeforeCreateRouter.call(lifecycleContext);
|
|
299
|
-
|
|
300
|
-
if (cachedRouter && cachedRouterBasepath === _basename) {
|
|
301
|
-
wrapRouterSubscribeWithBlockState(
|
|
302
|
-
cachedRouter,
|
|
303
|
-
runtimeContext.unstable_getBlockNavState,
|
|
304
|
-
);
|
|
305
|
-
hooks.onAfterCreateRouter.call({
|
|
306
|
-
...lifecycleContext,
|
|
307
|
-
router: cachedRouter,
|
|
308
|
-
runtimeContext,
|
|
309
|
-
});
|
|
310
|
-
return cachedRouter;
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
const history = supportHtml5History
|
|
314
|
-
? createBrowserHistory()
|
|
315
|
-
: createHashHistory();
|
|
316
|
-
|
|
317
|
-
const rewrite = createModernBasepathRewrite(_basename);
|
|
318
|
-
const serializationAdapters = getGlobalEnableRsc()
|
|
319
|
-
? getTanstackRscSerializationAdapters()
|
|
320
|
-
: undefined;
|
|
321
|
-
|
|
322
|
-
cachedRouter = createRouter({
|
|
323
|
-
routeTree,
|
|
324
|
-
basepath: '/',
|
|
325
|
-
rewrite,
|
|
326
|
-
history,
|
|
327
|
-
context: {},
|
|
328
|
-
...(serializationAdapters ? { serializationAdapters } : {}),
|
|
329
|
-
});
|
|
330
|
-
cachedRouterBasepath = _basename;
|
|
331
|
-
wrapRouterSubscribeWithBlockState(
|
|
332
|
-
cachedRouter,
|
|
333
|
-
runtimeContext.unstable_getBlockNavState,
|
|
334
|
-
);
|
|
335
|
-
hooks.onAfterCreateRouter.call({
|
|
336
|
-
...lifecycleContext,
|
|
337
|
-
router: cachedRouter,
|
|
338
|
-
runtimeContext,
|
|
339
|
-
});
|
|
340
|
-
|
|
341
|
-
return cachedRouter;
|
|
342
|
-
}, [_basename, routeTree, supportHtml5History, runtimeContext]);
|
|
535
|
+
return getRouter(runtimeContext, _basename);
|
|
536
|
+
}, [_basename, routeTree, runtimeContext]);
|
|
537
|
+
if (!router) {
|
|
538
|
+
return App ? <App /> : null;
|
|
539
|
+
}
|
|
343
540
|
const runtimeState = applyRouterRuntimeState(runtimeContext, {
|
|
344
541
|
framework: 'tanstack',
|
|
345
542
|
basename: _basename,
|
|
@@ -356,8 +553,10 @@ export const tanstackRouterPlugin = (
|
|
|
356
553
|
|
|
357
554
|
const hasSSRBootstrap =
|
|
358
555
|
typeof window !== 'undefined' &&
|
|
359
|
-
Boolean((window as WindowWithTanstackSsr).$_TSR)
|
|
360
|
-
|
|
556
|
+
(Boolean((window as WindowWithTanstackSsr).$_TSR) ||
|
|
557
|
+
hasTanstackSsrHydrationRecord(router));
|
|
558
|
+
const needsRouterClient = hasSSRBootstrap;
|
|
559
|
+
if (needsRouterClient) {
|
|
361
560
|
hooks.onBeforeHydrateRouter.call({
|
|
362
561
|
...lifecycleContext,
|
|
363
562
|
phase: 'hydrate',
|
|
@@ -366,14 +565,16 @@ export const tanstackRouterPlugin = (
|
|
|
366
565
|
});
|
|
367
566
|
}
|
|
368
567
|
|
|
369
|
-
const RouterContent =
|
|
370
|
-
<
|
|
371
|
-
<RouterClient router={router} />
|
|
372
|
-
</React.Suspense>
|
|
568
|
+
const RouterContent = needsRouterClient ? (
|
|
569
|
+
<ModernRouterClient router={router} />
|
|
373
570
|
) : (
|
|
374
571
|
<RouterProvider router={router} />
|
|
375
572
|
);
|
|
376
|
-
|
|
573
|
+
const HydratableRouterContent = wrapTanstackSsrHydrationBoundary(
|
|
574
|
+
RouterContent,
|
|
575
|
+
hasSSRBootstrap,
|
|
576
|
+
);
|
|
577
|
+
if (needsRouterClient) {
|
|
377
578
|
hooks.onAfterHydrateRouter.call({
|
|
378
579
|
...lifecycleContext,
|
|
379
580
|
phase: 'hydrate',
|
|
@@ -382,7 +583,11 @@ export const tanstackRouterPlugin = (
|
|
|
382
583
|
});
|
|
383
584
|
}
|
|
384
585
|
|
|
385
|
-
return App ?
|
|
586
|
+
return App ? (
|
|
587
|
+
<App>{HydratableRouterContent}</App>
|
|
588
|
+
) : (
|
|
589
|
+
HydratableRouterContent
|
|
590
|
+
);
|
|
386
591
|
};
|
|
387
592
|
|
|
388
593
|
return RouterWrapper;
|