@bleedingdev/modern-js-plugin-tanstack 3.2.0-ultramodern.10 → 3.2.0-ultramodern.101
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 +14 -14
- 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
package/src/runtime/plugin.tsx
CHANGED
|
@@ -24,8 +24,7 @@ import {
|
|
|
24
24
|
useNavigate,
|
|
25
25
|
useRouter,
|
|
26
26
|
} from '@tanstack/react-router';
|
|
27
|
-
import {
|
|
28
|
-
import * as React from 'react';
|
|
27
|
+
import { hydrate as hydrateTanstackRouter } from '@tanstack/react-router/ssr/client';
|
|
29
28
|
import { useContext, useMemo } from 'react';
|
|
30
29
|
import { createModernBasepathRewrite } from './basepathRewrite';
|
|
31
30
|
import {
|
|
@@ -37,13 +36,19 @@ import {
|
|
|
37
36
|
onBeforeHydrateRouter as onBeforeHydrateRouterHook,
|
|
38
37
|
type RouterExtendsHooks,
|
|
39
38
|
} from './hooks';
|
|
39
|
+
import { wrapTanstackSsrHydrationBoundary } from './hydrationBoundary';
|
|
40
40
|
import {
|
|
41
41
|
applyRouterRuntimeState,
|
|
42
42
|
type RouterLifecycleContext,
|
|
43
43
|
} from './lifecycle';
|
|
44
|
+
import { withModernRouteMatchContext } from './outlet';
|
|
45
|
+
import { Link } from './prefetchLink';
|
|
44
46
|
import { createRouteTreeFromRouteObjects } from './routeTree';
|
|
45
47
|
import { getTanstackRscSerializationAdapters } from './rsc/client';
|
|
46
|
-
import
|
|
48
|
+
import {
|
|
49
|
+
getModernTanstackRouterFastDefaults,
|
|
50
|
+
type RouterConfig,
|
|
51
|
+
} from './types';
|
|
47
52
|
import { createRouteObjectsFromConfig, urlJoin } from './utils';
|
|
48
53
|
|
|
49
54
|
const BLOCKING_SUBSCRIBE_SYMBOL = Symbol.for(
|
|
@@ -52,7 +57,6 @@ const BLOCKING_SUBSCRIBE_SYMBOL = Symbol.for(
|
|
|
52
57
|
const BLOCKING_STATE_SYMBOL = Symbol.for(
|
|
53
58
|
'@modern-js/plugin-tanstack:blocking-state',
|
|
54
59
|
);
|
|
55
|
-
|
|
56
60
|
type TanstackRouterWithSubscribe = {
|
|
57
61
|
[BLOCKING_STATE_SYMBOL]?: () => boolean;
|
|
58
62
|
[BLOCKING_SUBSCRIBE_SYMBOL]?: boolean;
|
|
@@ -66,6 +70,30 @@ type WindowWithTanstackSsr = Window & {
|
|
|
66
70
|
$_TSR?: unknown;
|
|
67
71
|
};
|
|
68
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
|
+
|
|
69
97
|
type TanstackRouterRuntimeConfig = {
|
|
70
98
|
plugins?: TanstackRouterRuntimePlugin[];
|
|
71
99
|
router?: Partial<RouterConfig>;
|
|
@@ -129,6 +157,144 @@ function wrapRouterSubscribeWithBlockState(
|
|
|
129
157
|
target[BLOCKING_SUBSCRIBE_SYMBOL] = true;
|
|
130
158
|
}
|
|
131
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
|
+
|
|
132
298
|
function stripSyntheticNotFoundRoute(routes: RouteObject[]): RouteObject[] {
|
|
133
299
|
return routes
|
|
134
300
|
.filter(route => !(route.path === '*' && !route.id && !route.loader))
|
|
@@ -157,14 +323,151 @@ export const tanstackRouterPlugin = (
|
|
|
157
323
|
onBeforeHydrateRouter: onBeforeHydrateRouterHook,
|
|
158
324
|
},
|
|
159
325
|
setup: (api: TanstackRouterPluginAPI) => {
|
|
160
|
-
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 = () => {
|
|
161
335
|
const pluginConfig = api.getRuntimeConfig() as {
|
|
162
336
|
router?: Partial<RouterConfig>;
|
|
163
337
|
};
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
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();
|
|
168
471
|
if (
|
|
169
472
|
typeof window !== 'undefined' &&
|
|
170
473
|
(window as { _SSR_DATA?: unknown })._SSR_DATA &&
|
|
@@ -184,104 +487,44 @@ export const tanstackRouterPlugin = (
|
|
|
184
487
|
}
|
|
185
488
|
|
|
186
489
|
context.router = {
|
|
490
|
+
Link,
|
|
187
491
|
useMatches,
|
|
188
492
|
useLocation,
|
|
189
493
|
useNavigate,
|
|
190
494
|
useRouter,
|
|
191
495
|
};
|
|
192
|
-
});
|
|
193
496
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
}
|
|
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
|
+
}
|
|
207
510
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
globalApp: getGlobalLayoutApp(),
|
|
211
|
-
...routesConfig,
|
|
212
|
-
};
|
|
511
|
+
return;
|
|
512
|
+
});
|
|
213
513
|
|
|
214
|
-
|
|
514
|
+
api.wrapRoot(App => {
|
|
515
|
+
if (!getRouteObjects().length) {
|
|
215
516
|
return App;
|
|
216
517
|
}
|
|
217
518
|
|
|
218
|
-
const hooks = api.getHooks();
|
|
219
|
-
|
|
220
|
-
let cachedRouteObjects: RouteObject[] | undefined;
|
|
221
|
-
|
|
222
|
-
const getRouteObjects = () => {
|
|
223
|
-
if (typeof cachedRouteObjects !== 'undefined') {
|
|
224
|
-
return cachedRouteObjects;
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
const routeObjects = createRoutes
|
|
228
|
-
? createRoutes()
|
|
229
|
-
: createRouteObjectsFromConfig({
|
|
230
|
-
routesConfig: finalRouteConfig,
|
|
231
|
-
}) || [];
|
|
232
|
-
|
|
233
|
-
const normalizedRouteObjects = createRoutes
|
|
234
|
-
? routeObjects
|
|
235
|
-
: stripSyntheticNotFoundRoute(routeObjects);
|
|
236
|
-
|
|
237
|
-
cachedRouteObjects = hooks.modifyRoutes.call(
|
|
238
|
-
normalizedRouteObjects,
|
|
239
|
-
) as RouteObject[];
|
|
240
|
-
return cachedRouteObjects;
|
|
241
|
-
};
|
|
242
|
-
|
|
243
|
-
const selectBasePath = (pathname: string) => {
|
|
244
|
-
const match = serverBase.find(baseUrl =>
|
|
245
|
-
isSegmentPrefix(pathname, baseUrl),
|
|
246
|
-
);
|
|
247
|
-
return match || '/';
|
|
248
|
-
};
|
|
249
|
-
|
|
250
|
-
let cachedRouteTree: ReturnType<
|
|
251
|
-
typeof createRouteTreeFromRouteObjects
|
|
252
|
-
> | null = null;
|
|
253
|
-
let cachedRouter: AnyRouter | null = null;
|
|
254
|
-
let cachedRouterBasepath: string | null = null;
|
|
255
|
-
|
|
256
519
|
const RouterWrapper = () => {
|
|
257
520
|
const runtimeContext = useContext(
|
|
258
521
|
InternalRuntimeContext,
|
|
259
522
|
) as TInternalRuntimeContext;
|
|
260
523
|
|
|
261
|
-
const
|
|
262
|
-
/^\/*/,
|
|
263
|
-
'/',
|
|
264
|
-
);
|
|
265
|
-
const _basename =
|
|
266
|
-
baseUrl === '/'
|
|
267
|
-
? urlJoin(
|
|
268
|
-
baseUrl,
|
|
269
|
-
runtimeContext._internalRouterBaseName || basename || '',
|
|
270
|
-
)
|
|
271
|
-
: baseUrl;
|
|
524
|
+
const _basename = getClientBasename(runtimeContext);
|
|
272
525
|
|
|
273
526
|
const routeTree = useMemo(() => {
|
|
274
|
-
|
|
275
|
-
return cachedRouteTree;
|
|
276
|
-
}
|
|
277
|
-
const routeObjects = getRouteObjects();
|
|
278
|
-
if (!routeObjects.length) {
|
|
279
|
-
return null;
|
|
280
|
-
}
|
|
281
|
-
cachedRouteTree = createRouteTreeFromRouteObjects(routeObjects, {
|
|
282
|
-
rscPayloadRouter: getGlobalEnableRsc(),
|
|
283
|
-
});
|
|
284
|
-
return cachedRouteTree;
|
|
527
|
+
return getRouteTree();
|
|
285
528
|
}, []);
|
|
286
529
|
|
|
287
530
|
if (!routeTree) {
|
|
@@ -289,58 +532,11 @@ export const tanstackRouterPlugin = (
|
|
|
289
532
|
}
|
|
290
533
|
|
|
291
534
|
const router = useMemo(() => {
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
basename: _basename,
|
|
298
|
-
};
|
|
299
|
-
hooks.onBeforeCreateRouter.call(lifecycleContext);
|
|
300
|
-
|
|
301
|
-
if (cachedRouter && cachedRouterBasepath === _basename) {
|
|
302
|
-
wrapRouterSubscribeWithBlockState(
|
|
303
|
-
cachedRouter,
|
|
304
|
-
runtimeContext.unstable_getBlockNavState,
|
|
305
|
-
);
|
|
306
|
-
hooks.onAfterCreateRouter.call({
|
|
307
|
-
...lifecycleContext,
|
|
308
|
-
router: cachedRouter,
|
|
309
|
-
runtimeContext,
|
|
310
|
-
});
|
|
311
|
-
return cachedRouter;
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
const history = supportHtml5History
|
|
315
|
-
? createBrowserHistory()
|
|
316
|
-
: createHashHistory();
|
|
317
|
-
|
|
318
|
-
const rewrite = createModernBasepathRewrite(_basename);
|
|
319
|
-
const serializationAdapters = getGlobalEnableRsc()
|
|
320
|
-
? getTanstackRscSerializationAdapters()
|
|
321
|
-
: undefined;
|
|
322
|
-
|
|
323
|
-
cachedRouter = createRouter({
|
|
324
|
-
routeTree,
|
|
325
|
-
basepath: '/',
|
|
326
|
-
rewrite,
|
|
327
|
-
history,
|
|
328
|
-
context: {},
|
|
329
|
-
...(serializationAdapters ? { serializationAdapters } : {}),
|
|
330
|
-
});
|
|
331
|
-
cachedRouterBasepath = _basename;
|
|
332
|
-
wrapRouterSubscribeWithBlockState(
|
|
333
|
-
cachedRouter,
|
|
334
|
-
runtimeContext.unstable_getBlockNavState,
|
|
335
|
-
);
|
|
336
|
-
hooks.onAfterCreateRouter.call({
|
|
337
|
-
...lifecycleContext,
|
|
338
|
-
router: cachedRouter,
|
|
339
|
-
runtimeContext,
|
|
340
|
-
});
|
|
341
|
-
|
|
342
|
-
return cachedRouter;
|
|
343
|
-
}, [_basename, routeTree, supportHtml5History, runtimeContext]);
|
|
535
|
+
return getRouter(runtimeContext, _basename);
|
|
536
|
+
}, [_basename, routeTree, runtimeContext]);
|
|
537
|
+
if (!router) {
|
|
538
|
+
return App ? <App /> : null;
|
|
539
|
+
}
|
|
344
540
|
const runtimeState = applyRouterRuntimeState(runtimeContext, {
|
|
345
541
|
framework: 'tanstack',
|
|
346
542
|
basename: _basename,
|
|
@@ -357,8 +553,10 @@ export const tanstackRouterPlugin = (
|
|
|
357
553
|
|
|
358
554
|
const hasSSRBootstrap =
|
|
359
555
|
typeof window !== 'undefined' &&
|
|
360
|
-
Boolean((window as WindowWithTanstackSsr).$_TSR)
|
|
361
|
-
|
|
556
|
+
(Boolean((window as WindowWithTanstackSsr).$_TSR) ||
|
|
557
|
+
hasTanstackSsrHydrationRecord(router));
|
|
558
|
+
const needsRouterClient = hasSSRBootstrap;
|
|
559
|
+
if (needsRouterClient) {
|
|
362
560
|
hooks.onBeforeHydrateRouter.call({
|
|
363
561
|
...lifecycleContext,
|
|
364
562
|
phase: 'hydrate',
|
|
@@ -367,14 +565,16 @@ export const tanstackRouterPlugin = (
|
|
|
367
565
|
});
|
|
368
566
|
}
|
|
369
567
|
|
|
370
|
-
const RouterContent =
|
|
371
|
-
<
|
|
372
|
-
<RouterClient router={router} />
|
|
373
|
-
</React.Suspense>
|
|
568
|
+
const RouterContent = needsRouterClient ? (
|
|
569
|
+
<ModernRouterClient router={router} />
|
|
374
570
|
) : (
|
|
375
571
|
<RouterProvider router={router} />
|
|
376
572
|
);
|
|
377
|
-
|
|
573
|
+
const HydratableRouterContent = wrapTanstackSsrHydrationBoundary(
|
|
574
|
+
RouterContent,
|
|
575
|
+
hasSSRBootstrap,
|
|
576
|
+
);
|
|
577
|
+
if (needsRouterClient) {
|
|
378
578
|
hooks.onAfterHydrateRouter.call({
|
|
379
579
|
...lifecycleContext,
|
|
380
580
|
phase: 'hydrate',
|
|
@@ -383,7 +583,11 @@ export const tanstackRouterPlugin = (
|
|
|
383
583
|
});
|
|
384
584
|
}
|
|
385
585
|
|
|
386
|
-
return App ?
|
|
586
|
+
return App ? (
|
|
587
|
+
<App>{HydratableRouterContent}</App>
|
|
588
|
+
) : (
|
|
589
|
+
HydratableRouterContent
|
|
590
|
+
);
|
|
387
591
|
};
|
|
388
592
|
|
|
389
593
|
return RouterWrapper;
|