@bleedingdev/modern-js-plugin-tanstack 3.2.0-ultramodern.99 → 3.4.0-ultramodern.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/cli/index.js +80 -34
- package/dist/cjs/cli/routeSplitting.js +13 -41
- package/dist/cjs/cli/tanstackTypes.js +114 -200
- package/dist/cjs/cli.js +12 -8
- package/dist/cjs/runtime/basepathRewrite.js +12 -8
- package/dist/cjs/runtime/dataMutation.js +9 -5
- package/dist/cjs/runtime/hooks.js +20 -19
- package/dist/cjs/runtime/hydrationBoundary.js +12 -7
- package/dist/cjs/runtime/index.js +66 -54
- package/dist/cjs/runtime/lifecycle.js +21 -91
- package/dist/cjs/runtime/loaderBridge.js +173 -0
- package/dist/cjs/runtime/outlet.js +58 -0
- package/dist/cjs/runtime/plugin.js +190 -110
- package/dist/cjs/runtime/plugin.node.js +25 -40
- package/dist/cjs/runtime/plugin.worker.js +9 -5
- package/dist/cjs/runtime/pluginCore.js +55 -0
- package/dist/cjs/runtime/prefetchLink.js +10 -6
- package/dist/cjs/runtime/register.js +56 -0
- package/dist/cjs/runtime/routeTree.js +30 -212
- package/dist/cjs/runtime/router.js +41 -0
- package/dist/cjs/runtime/rsc/ClientSlot.js +9 -5
- package/dist/cjs/runtime/rsc/CompositeComponent.js +9 -5
- package/dist/cjs/runtime/rsc/ReplayableStream.js +14 -9
- package/dist/cjs/runtime/rsc/RscNodeRenderer.js +9 -5
- package/dist/cjs/runtime/rsc/SlotContext.js +9 -5
- package/dist/cjs/runtime/rsc/client.js +9 -5
- package/dist/cjs/runtime/rsc/createRscProxy.js +9 -5
- package/dist/cjs/runtime/rsc/index.js +9 -5
- package/dist/cjs/runtime/rsc/payloadRouter.js +44 -6
- package/dist/cjs/runtime/rsc/server.js +9 -5
- package/dist/cjs/runtime/rsc/slotUsageSanitizer.js +9 -5
- package/dist/cjs/runtime/rsc/symbols.js +20 -15
- package/dist/cjs/runtime/state.js +45 -0
- package/dist/cjs/runtime/types.js +13 -9
- package/dist/cjs/runtime/utils.js +9 -11
- package/dist/cjs/runtime.js +9 -5
- package/dist/esm/cli/index.mjs +73 -28
- package/dist/esm/cli/routeSplitting.mjs +1 -30
- package/dist/esm/cli/tanstackTypes.mjs +100 -190
- package/dist/esm/runtime/hooks.mjs +1 -8
- package/dist/esm/runtime/hydrationBoundary.mjs +3 -2
- package/dist/esm/runtime/index.mjs +4 -2
- package/dist/esm/runtime/lifecycle.mjs +1 -82
- package/dist/esm/runtime/loaderBridge.mjs +114 -0
- package/dist/esm/runtime/outlet.mjs +17 -0
- package/dist/esm/runtime/plugin.mjs +183 -107
- package/dist/esm/runtime/plugin.node.mjs +19 -38
- package/dist/esm/runtime/pluginCore.mjs +14 -0
- package/dist/esm/runtime/prefetchLink.mjs +1 -1
- package/dist/esm/runtime/register.mjs +18 -0
- package/dist/esm/runtime/routeTree.mjs +15 -198
- package/dist/esm/runtime/router.mjs +2 -0
- package/dist/esm/runtime/rsc/payloadRouter.mjs +35 -1
- package/dist/esm/runtime/state.mjs +7 -0
- package/dist/esm/runtime/utils.mjs +0 -6
- package/dist/esm-node/cli/index.mjs +73 -28
- package/dist/esm-node/cli/routeSplitting.mjs +1 -30
- package/dist/esm-node/cli/tanstackTypes.mjs +100 -190
- package/dist/esm-node/runtime/hooks.mjs +1 -8
- package/dist/esm-node/runtime/hydrationBoundary.mjs +3 -2
- package/dist/esm-node/runtime/index.mjs +4 -2
- package/dist/esm-node/runtime/lifecycle.mjs +1 -82
- package/dist/esm-node/runtime/loaderBridge.mjs +115 -0
- package/dist/esm-node/runtime/outlet.mjs +18 -0
- package/dist/esm-node/runtime/plugin.mjs +183 -107
- package/dist/esm-node/runtime/plugin.node.mjs +19 -38
- package/dist/esm-node/runtime/pluginCore.mjs +15 -0
- package/dist/esm-node/runtime/prefetchLink.mjs +1 -1
- package/dist/esm-node/runtime/register.mjs +19 -0
- package/dist/esm-node/runtime/routeTree.mjs +15 -198
- package/dist/esm-node/runtime/router.mjs +3 -0
- package/dist/esm-node/runtime/rsc/payloadRouter.mjs +35 -1
- package/dist/esm-node/runtime/state.mjs +8 -0
- package/dist/esm-node/runtime/utils.mjs +0 -6
- package/dist/types/cli/index.d.ts +11 -2
- package/dist/types/cli/routeSplitting.d.ts +6 -15
- package/dist/types/cli/tanstackTypes.d.ts +21 -1
- package/dist/types/runtime/hooks.d.ts +8 -33
- package/dist/types/runtime/index.d.ts +6 -3
- package/dist/types/runtime/lifecycle.d.ts +7 -22
- package/dist/types/runtime/loaderBridge.d.ts +48 -0
- package/dist/types/runtime/outlet.d.ts +2 -0
- package/dist/types/runtime/plugin.d.ts +1 -14
- package/dist/types/runtime/plugin.node.d.ts +1 -14
- package/dist/types/runtime/pluginCore.d.ts +21 -0
- package/dist/types/runtime/register.d.ts +9 -0
- package/dist/types/runtime/routeTree.d.ts +0 -2
- package/dist/types/runtime/router.d.ts +14 -0
- package/dist/types/runtime/state.d.ts +16 -0
- package/dist/types/runtime/types.d.ts +7 -53
- package/package.json +41 -39
- package/rstest.config.mts +6 -0
- package/src/cli/index.ts +146 -24
- package/src/cli/routeSplitting.ts +6 -44
- package/src/cli/tanstackTypes.ts +203 -208
- package/src/runtime/hooks.ts +10 -27
- package/src/runtime/hydrationBoundary.tsx +5 -5
- package/src/runtime/index.tsx +12 -14
- package/src/runtime/lifecycle.ts +16 -151
- package/src/runtime/loaderBridge.ts +257 -0
- package/src/runtime/outlet.tsx +48 -0
- package/src/runtime/plugin.node.tsx +28 -83
- package/src/runtime/plugin.tsx +350 -202
- package/src/runtime/pluginCore.ts +48 -0
- package/src/runtime/prefetchLink.tsx +1 -1
- package/src/runtime/register.ts +58 -0
- package/src/runtime/routeTree.ts +41 -365
- package/src/runtime/router.ts +15 -0
- package/src/runtime/rsc/payloadRouter.ts +45 -2
- package/src/runtime/ssr-shim.d.ts +1 -3
- package/src/runtime/state.ts +29 -0
- package/src/runtime/types.ts +20 -67
- package/src/runtime/utils.tsx +3 -7
- package/tests/router/cli.test.ts +373 -31
- package/tests/router/hooks.test.ts +26 -0
- package/tests/router/loaderBridge.test.ts +211 -0
- package/tests/router/packageSurface.test.ts +24 -0
- package/tests/router/prefetchLink.test.tsx +43 -7
- package/tests/router/register.test.ts +46 -0
- package/tests/router/routeTree.test.ts +176 -139
- package/tests/router/rsc.test.tsx +70 -0
- package/tests/router/tanstackTypes.test.ts +395 -7
- package/dist/cjs/runtime/DefaultNotFound.js +0 -47
- package/dist/esm/runtime/DefaultNotFound.mjs +0 -13
- package/dist/esm-node/runtime/DefaultNotFound.mjs +0 -14
- package/dist/types/runtime/DefaultNotFound.d.ts +0 -2
- package/src/runtime/DefaultNotFound.tsx +0 -15
package/src/runtime/plugin.tsx
CHANGED
|
@@ -1,16 +1,11 @@
|
|
|
1
1
|
// @effect-diagnostics globalConsole:off strictBooleanExpressions:off
|
|
2
2
|
/// <reference path="./ssr-shim.d.ts" />
|
|
3
3
|
|
|
4
|
-
import type { Plugin, RuntimePluginExtends } from '@modern-js/plugin';
|
|
5
|
-
import type { RuntimePluginAPI } from '@modern-js/plugin/runtime';
|
|
6
4
|
import {
|
|
7
5
|
getGlobalEnableRsc,
|
|
8
|
-
getGlobalLayoutApp,
|
|
9
|
-
getGlobalRoutes,
|
|
10
6
|
InternalRuntimeContext,
|
|
11
7
|
type TInternalRuntimeContext,
|
|
12
8
|
} from '@modern-js/runtime/context';
|
|
13
|
-
import { merge } from '@modern-js/runtime-utils/merge';
|
|
14
9
|
import type { RouteObject } from '@modern-js/runtime-utils/router';
|
|
15
10
|
import { normalizePathname } from '@modern-js/runtime-utils/url';
|
|
16
11
|
import {
|
|
@@ -24,23 +19,21 @@ import {
|
|
|
24
19
|
useNavigate,
|
|
25
20
|
useRouter,
|
|
26
21
|
} from '@tanstack/react-router';
|
|
27
|
-
import { RouterClient } from '@tanstack/react-router/ssr/client';
|
|
28
22
|
import { useContext, useMemo } from 'react';
|
|
29
23
|
import { createModernBasepathRewrite } from './basepathRewrite';
|
|
30
|
-
import {
|
|
31
|
-
modifyRoutes as modifyRoutesHook,
|
|
32
|
-
onAfterCreateRouter as onAfterCreateRouterHook,
|
|
33
|
-
onAfterHydrateRouter as onAfterHydrateRouterHook,
|
|
34
|
-
onBeforeCreateRouter as onBeforeCreateRouterHook,
|
|
35
|
-
onBeforeCreateRoutes as onBeforeCreateRoutesHook,
|
|
36
|
-
onBeforeHydrateRouter as onBeforeHydrateRouterHook,
|
|
37
|
-
type RouterExtendsHooks,
|
|
38
|
-
} from './hooks';
|
|
24
|
+
import { routerProviderRegistryHooks } from './hooks';
|
|
39
25
|
import { wrapTanstackSsrHydrationBoundary } from './hydrationBoundary';
|
|
40
26
|
import {
|
|
41
27
|
applyRouterRuntimeState,
|
|
42
28
|
type RouterLifecycleContext,
|
|
43
29
|
} from './lifecycle';
|
|
30
|
+
import { withModernRouteMatchContext } from './outlet';
|
|
31
|
+
import {
|
|
32
|
+
getFinalRouteConfig,
|
|
33
|
+
getMergedRouterConfig,
|
|
34
|
+
type TanstackRouterPluginAPI,
|
|
35
|
+
type TanstackRouterRuntimePlugin,
|
|
36
|
+
} from './pluginCore';
|
|
44
37
|
import { Link } from './prefetchLink';
|
|
45
38
|
import { createRouteTreeFromRouteObjects } from './routeTree';
|
|
46
39
|
import { getTanstackRscSerializationAdapters } from './rsc/client';
|
|
@@ -56,7 +49,6 @@ const BLOCKING_SUBSCRIBE_SYMBOL = Symbol.for(
|
|
|
56
49
|
const BLOCKING_STATE_SYMBOL = Symbol.for(
|
|
57
50
|
'@modern-js/plugin-tanstack:blocking-state',
|
|
58
51
|
);
|
|
59
|
-
|
|
60
52
|
type TanstackRouterWithSubscribe = {
|
|
61
53
|
[BLOCKING_STATE_SYMBOL]?: () => boolean;
|
|
62
54
|
[BLOCKING_SUBSCRIBE_SYMBOL]?: boolean;
|
|
@@ -70,24 +62,29 @@ type WindowWithTanstackSsr = Window & {
|
|
|
70
62
|
$_TSR?: unknown;
|
|
71
63
|
};
|
|
72
64
|
|
|
73
|
-
type
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
[key: string]: unknown;
|
|
65
|
+
type RouteComponentPreloadable = {
|
|
66
|
+
load?: () => Promise<unknown>;
|
|
67
|
+
preload?: () => Promise<unknown>;
|
|
77
68
|
};
|
|
78
69
|
|
|
79
|
-
type
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
extendHooks: RouterExtendsHooks;
|
|
70
|
+
type ModernRouteModule = {
|
|
71
|
+
Component?: unknown;
|
|
72
|
+
default?: unknown;
|
|
83
73
|
};
|
|
84
74
|
|
|
85
|
-
type
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
75
|
+
type RouterWithPreloadableRoutes = AnyRouter & {
|
|
76
|
+
routesById?: Record<
|
|
77
|
+
string,
|
|
78
|
+
{
|
|
79
|
+
options?: {
|
|
80
|
+
component?: unknown;
|
|
81
|
+
staticData?: {
|
|
82
|
+
modernRouteId?: string;
|
|
83
|
+
};
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
>;
|
|
87
|
+
};
|
|
91
88
|
|
|
92
89
|
function normalizeBase(b: string) {
|
|
93
90
|
if (b.length > 1 && b.endsWith('/')) {
|
|
@@ -133,18 +130,155 @@ function wrapRouterSubscribeWithBlockState(
|
|
|
133
130
|
target[BLOCKING_SUBSCRIBE_SYMBOL] = true;
|
|
134
131
|
}
|
|
135
132
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
133
|
+
type RouterHydrationRecord = {
|
|
134
|
+
error?: unknown;
|
|
135
|
+
promise: Promise<unknown>;
|
|
136
|
+
status: 'pending' | 'fulfilled' | 'rejected';
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
const routerHydrationRecords = new WeakMap<AnyRouter, RouterHydrationRecord>();
|
|
140
|
+
const routeModulesKey = '_routeModules';
|
|
141
|
+
|
|
142
|
+
function pickRouteModuleComponent(
|
|
143
|
+
routeModule: unknown,
|
|
144
|
+
seen: Set<unknown> = new Set(),
|
|
145
|
+
): unknown {
|
|
146
|
+
if (
|
|
147
|
+
typeof routeModule === 'function' ||
|
|
148
|
+
(routeModule &&
|
|
149
|
+
typeof routeModule === 'object' &&
|
|
150
|
+
'$$typeof' in routeModule)
|
|
151
|
+
) {
|
|
152
|
+
return routeModule;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (!routeModule || typeof routeModule !== 'object') {
|
|
156
|
+
return undefined;
|
|
157
|
+
}
|
|
158
|
+
if (seen.has(routeModule)) {
|
|
159
|
+
return undefined;
|
|
160
|
+
}
|
|
161
|
+
seen.add(routeModule);
|
|
162
|
+
|
|
163
|
+
const module = routeModule as ModernRouteModule;
|
|
164
|
+
for (const candidate of [module.default, module.Component]) {
|
|
165
|
+
const component = pickRouteModuleComponent(candidate, seen);
|
|
166
|
+
if (component) {
|
|
167
|
+
return component;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return undefined;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function getCachedRouteModule(routeId: string) {
|
|
175
|
+
if (typeof window === 'undefined') {
|
|
176
|
+
return undefined;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return (window as unknown as Record<string, Record<string, unknown>>)[
|
|
180
|
+
routeModulesKey
|
|
181
|
+
]?.[routeId];
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function preloadHydratedRouteComponents(router: AnyRouter): Promise<void> {
|
|
185
|
+
const preloadableRouter = router as RouterWithPreloadableRoutes;
|
|
186
|
+
const routesById = preloadableRouter.routesById || {};
|
|
187
|
+
const matches = preloadableRouter.stores.matches.get() as Array<{
|
|
188
|
+
routeId?: string;
|
|
189
|
+
}>;
|
|
190
|
+
|
|
191
|
+
return Promise.all(
|
|
192
|
+
matches.map(match => {
|
|
193
|
+
if (match.routeId === undefined || match.routeId === '') {
|
|
194
|
+
return undefined;
|
|
142
195
|
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
196
|
+
|
|
197
|
+
const route = routesById[match.routeId];
|
|
198
|
+
const component = route?.options?.component as RouteComponentPreloadable;
|
|
199
|
+
const preload = component?.load || component?.preload;
|
|
200
|
+
if (typeof preload !== 'function') {
|
|
201
|
+
return undefined;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return Promise.resolve(preload.call(component)).then(routeModule => {
|
|
205
|
+
const modernRouteId = route?.options?.staticData?.modernRouteId;
|
|
206
|
+
const cachedRouteModule =
|
|
207
|
+
typeof modernRouteId === 'string' && modernRouteId !== ''
|
|
208
|
+
? getCachedRouteModule(modernRouteId)
|
|
209
|
+
: undefined;
|
|
210
|
+
const resolvedComponent = pickRouteModuleComponent(
|
|
211
|
+
cachedRouteModule ?? routeModule,
|
|
212
|
+
);
|
|
213
|
+
if (
|
|
214
|
+
resolvedComponent !== undefined &&
|
|
215
|
+
typeof modernRouteId === 'string' &&
|
|
216
|
+
modernRouteId !== ''
|
|
217
|
+
) {
|
|
218
|
+
route.options.component = withModernRouteMatchContext(
|
|
219
|
+
resolvedComponent,
|
|
220
|
+
modernRouteId,
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
});
|
|
224
|
+
}),
|
|
225
|
+
).then(() => undefined);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function hydrateTanstackRouter(router: AnyRouter) {
|
|
229
|
+
return import('@tanstack/react-router/ssr/client').then(({ hydrate }) =>
|
|
230
|
+
hydrate(router),
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function getTanstackSsrHydrationRecord(router: AnyRouter) {
|
|
235
|
+
const existingHydrationRecord = routerHydrationRecords.get(router);
|
|
236
|
+
if (existingHydrationRecord !== undefined) {
|
|
237
|
+
return existingHydrationRecord;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
const hydrationRecord: RouterHydrationRecord = {
|
|
241
|
+
promise: Promise.resolve(),
|
|
242
|
+
status: 'pending',
|
|
243
|
+
};
|
|
244
|
+
routerHydrationRecords.set(router, hydrationRecord);
|
|
245
|
+
try {
|
|
246
|
+
hydrationRecord.promise = hydrateTanstackRouter(router)
|
|
247
|
+
.then(value => preloadHydratedRouteComponents(router).then(() => value))
|
|
248
|
+
.then(
|
|
249
|
+
value => {
|
|
250
|
+
hydrationRecord.status = 'fulfilled';
|
|
251
|
+
return value;
|
|
252
|
+
},
|
|
253
|
+
error => {
|
|
254
|
+
hydrationRecord.status = 'rejected';
|
|
255
|
+
hydrationRecord.error = error;
|
|
256
|
+
throw error;
|
|
257
|
+
},
|
|
258
|
+
);
|
|
259
|
+
} catch (error) {
|
|
260
|
+
hydrationRecord.status = 'rejected';
|
|
261
|
+
hydrationRecord.error = error;
|
|
262
|
+
hydrationRecord.promise = Promise.reject(error);
|
|
263
|
+
hydrationRecord.promise.catch(() => {});
|
|
264
|
+
}
|
|
265
|
+
return hydrationRecord;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function getTanstackSsrHydrationPromise(router: AnyRouter) {
|
|
269
|
+
return getTanstackSsrHydrationRecord(router).promise;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
function hasTanstackSsrHydrationRecord(router: AnyRouter) {
|
|
273
|
+
return routerHydrationRecords.has(router);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
function ModernRouterClient({ router }: { router: AnyRouter }) {
|
|
277
|
+
const hydrationRecord = getTanstackSsrHydrationRecord(router);
|
|
278
|
+
if (hydrationRecord.status === 'rejected') {
|
|
279
|
+
throw hydrationRecord.error;
|
|
280
|
+
}
|
|
281
|
+
return <RouterProvider router={router} />;
|
|
148
282
|
}
|
|
149
283
|
|
|
150
284
|
export const tanstackRouterPlugin = (
|
|
@@ -152,35 +286,157 @@ export const tanstackRouterPlugin = (
|
|
|
152
286
|
): TanstackRouterRuntimePlugin => {
|
|
153
287
|
const plugin: TanstackRouterRuntimePlugin = {
|
|
154
288
|
name: '@modern-js/plugin-router-tanstack',
|
|
155
|
-
registryHooks:
|
|
156
|
-
modifyRoutes: modifyRoutesHook,
|
|
157
|
-
onAfterCreateRouter: onAfterCreateRouterHook,
|
|
158
|
-
onAfterHydrateRouter: onAfterHydrateRouterHook,
|
|
159
|
-
onBeforeCreateRouter: onBeforeCreateRouterHook,
|
|
160
|
-
onBeforeCreateRoutes: onBeforeCreateRoutesHook,
|
|
161
|
-
onBeforeHydrateRouter: onBeforeHydrateRouterHook,
|
|
162
|
-
},
|
|
289
|
+
registryHooks: routerProviderRegistryHooks,
|
|
163
290
|
setup: (api: TanstackRouterPluginAPI) => {
|
|
164
|
-
api.
|
|
165
|
-
|
|
166
|
-
|
|
291
|
+
const hooks = api.getHooks();
|
|
292
|
+
let cachedRouteObjects: RouteObject[] | undefined;
|
|
293
|
+
let cachedRouteTree: ReturnType<
|
|
294
|
+
typeof createRouteTreeFromRouteObjects
|
|
295
|
+
> | null = null;
|
|
296
|
+
let cachedRouter: AnyRouter | null = null;
|
|
297
|
+
let cachedRouterBasepath: string | null = null;
|
|
298
|
+
|
|
299
|
+
const getMergedConfig = () => getMergedRouterConfig(api, userConfig);
|
|
300
|
+
|
|
301
|
+
const getRouteObjects = () => {
|
|
302
|
+
if (typeof cachedRouteObjects !== 'undefined') {
|
|
303
|
+
return cachedRouteObjects;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
const mergedConfig = getMergedConfig();
|
|
307
|
+
const { createRoutes } = mergedConfig;
|
|
308
|
+
const finalRouteConfig = getFinalRouteConfig(mergedConfig);
|
|
309
|
+
|
|
310
|
+
const routeObjects = createRoutes
|
|
311
|
+
? createRoutes()
|
|
312
|
+
: createRouteObjectsFromConfig({
|
|
313
|
+
routesConfig: finalRouteConfig,
|
|
314
|
+
}) || [];
|
|
315
|
+
|
|
316
|
+
cachedRouteObjects = hooks.modifyRoutes.call(
|
|
317
|
+
routeObjects,
|
|
318
|
+
) as RouteObject[];
|
|
319
|
+
return cachedRouteObjects;
|
|
320
|
+
};
|
|
321
|
+
|
|
322
|
+
const getRouteTree = () => {
|
|
323
|
+
if (cachedRouteTree) {
|
|
324
|
+
return cachedRouteTree;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
const routeObjects = getRouteObjects();
|
|
328
|
+
if (!routeObjects.length) {
|
|
329
|
+
return null;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
cachedRouteTree = createRouteTreeFromRouteObjects(routeObjects, {
|
|
333
|
+
rscPayloadRouter: getGlobalEnableRsc(),
|
|
334
|
+
});
|
|
335
|
+
return cachedRouteTree;
|
|
336
|
+
};
|
|
337
|
+
|
|
338
|
+
const selectBasePath = (pathname: string) => {
|
|
339
|
+
const { serverBase = [] } = getMergedConfig();
|
|
340
|
+
const match = serverBase.find(baseUrl =>
|
|
341
|
+
isSegmentPrefix(pathname, baseUrl),
|
|
342
|
+
);
|
|
343
|
+
return match || '/';
|
|
344
|
+
};
|
|
345
|
+
|
|
346
|
+
const getClientBasename = (runtimeContext: TInternalRuntimeContext) => {
|
|
347
|
+
const { basename = '' } = getMergedConfig();
|
|
348
|
+
const baseUrl = selectBasePath(location.pathname).replace(/^\/*/, '/');
|
|
349
|
+
return baseUrl === '/'
|
|
350
|
+
? urlJoin(
|
|
351
|
+
baseUrl,
|
|
352
|
+
runtimeContext._internalRouterBaseName || basename || '',
|
|
353
|
+
)
|
|
354
|
+
: baseUrl;
|
|
355
|
+
};
|
|
356
|
+
|
|
357
|
+
const getRouter = (
|
|
358
|
+
runtimeContext: TInternalRuntimeContext,
|
|
359
|
+
_basename: string,
|
|
360
|
+
) => {
|
|
361
|
+
const routeTree = getRouteTree();
|
|
362
|
+
if (!routeTree) {
|
|
363
|
+
return null;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
const lifecycleContext: RouterLifecycleContext = {
|
|
367
|
+
framework: 'tanstack',
|
|
368
|
+
phase: 'client-create',
|
|
369
|
+
routes: getRouteObjects(),
|
|
370
|
+
runtimeContext,
|
|
371
|
+
basename: _basename,
|
|
167
372
|
};
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
373
|
+
hooks.onBeforeCreateRouter.call(lifecycleContext);
|
|
374
|
+
|
|
375
|
+
if (cachedRouter && cachedRouterBasepath === _basename) {
|
|
376
|
+
wrapRouterSubscribeWithBlockState(
|
|
377
|
+
cachedRouter,
|
|
378
|
+
runtimeContext.unstable_getBlockNavState,
|
|
379
|
+
);
|
|
380
|
+
hooks.onAfterCreateRouter.call({
|
|
381
|
+
...lifecycleContext,
|
|
382
|
+
router: cachedRouter,
|
|
383
|
+
runtimeContext,
|
|
384
|
+
});
|
|
385
|
+
return cachedRouter;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
const mergedConfig = getMergedConfig();
|
|
389
|
+
const { supportHtml5History = true } = mergedConfig;
|
|
390
|
+
const history = supportHtml5History
|
|
391
|
+
? createBrowserHistory()
|
|
392
|
+
: createHashHistory();
|
|
393
|
+
const rewrite = createModernBasepathRewrite(_basename);
|
|
394
|
+
const serializationAdapters = getGlobalEnableRsc()
|
|
395
|
+
? getTanstackRscSerializationAdapters()
|
|
396
|
+
: undefined;
|
|
397
|
+
|
|
398
|
+
cachedRouter = createRouter({
|
|
399
|
+
...getModernTanstackRouterFastDefaults(mergedConfig),
|
|
400
|
+
routeTree,
|
|
401
|
+
basepath: '/',
|
|
402
|
+
rewrite,
|
|
403
|
+
history,
|
|
404
|
+
context: {},
|
|
405
|
+
...(serializationAdapters ? { serializationAdapters } : {}),
|
|
406
|
+
});
|
|
407
|
+
cachedRouterBasepath = _basename;
|
|
408
|
+
wrapRouterSubscribeWithBlockState(
|
|
409
|
+
cachedRouter,
|
|
410
|
+
runtimeContext.unstable_getBlockNavState,
|
|
411
|
+
);
|
|
412
|
+
hooks.onAfterCreateRouter.call({
|
|
413
|
+
...lifecycleContext,
|
|
414
|
+
router: cachedRouter,
|
|
415
|
+
runtimeContext,
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
return cachedRouter;
|
|
419
|
+
};
|
|
420
|
+
|
|
421
|
+
api.onBeforeRender(context => {
|
|
422
|
+
const mergedConfig = getMergedConfig();
|
|
172
423
|
if (
|
|
173
424
|
typeof window !== 'undefined' &&
|
|
174
|
-
(window as { _SSR_DATA?: unknown })._SSR_DATA &&
|
|
425
|
+
(window as { _SSR_DATA?: unknown })._SSR_DATA !== undefined &&
|
|
175
426
|
mergedConfig.unstable_reloadOnURLMismatch
|
|
176
427
|
) {
|
|
177
428
|
const { ssrContext } = context;
|
|
178
429
|
const currentPathname = normalizePathname(window.location.pathname);
|
|
179
430
|
const initialPathname =
|
|
180
|
-
ssrContext?.request?.pathname
|
|
181
|
-
|
|
431
|
+
typeof ssrContext?.request?.pathname === 'string'
|
|
432
|
+
? normalizePathname(ssrContext.request.pathname)
|
|
433
|
+
: undefined;
|
|
182
434
|
|
|
183
|
-
if (
|
|
435
|
+
if (
|
|
436
|
+
initialPathname !== undefined &&
|
|
437
|
+
initialPathname !== '' &&
|
|
438
|
+
initialPathname !== currentPathname
|
|
439
|
+
) {
|
|
184
440
|
const errorMsg = `The initial URL ${initialPathname} and the URL ${currentPathname} to be hydrated do not match, reload.`;
|
|
185
441
|
console.error(errorMsg);
|
|
186
442
|
window.location.reload();
|
|
@@ -194,159 +450,49 @@ export const tanstackRouterPlugin = (
|
|
|
194
450
|
useNavigate,
|
|
195
451
|
useRouter,
|
|
196
452
|
};
|
|
453
|
+
|
|
454
|
+
const hasSSRBootstrap =
|
|
455
|
+
typeof window !== 'undefined' &&
|
|
456
|
+
Boolean((window as WindowWithTanstackSsr).$_TSR);
|
|
457
|
+
if (hasSSRBootstrap && getRouteObjects().length > 0) {
|
|
458
|
+
const runtimeContext = context as TInternalRuntimeContext;
|
|
459
|
+
const router = getRouter(
|
|
460
|
+
runtimeContext,
|
|
461
|
+
getClientBasename(runtimeContext),
|
|
462
|
+
);
|
|
463
|
+
if (router !== undefined && router !== null) {
|
|
464
|
+
return getTanstackSsrHydrationPromise(router).then(() => undefined);
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
return;
|
|
197
469
|
});
|
|
198
470
|
|
|
199
471
|
api.wrapRoot(App => {
|
|
200
|
-
|
|
201
|
-
api.getRuntimeConfig().router || {},
|
|
202
|
-
userConfig,
|
|
203
|
-
) as RouterConfig;
|
|
204
|
-
|
|
205
|
-
const {
|
|
206
|
-
serverBase = [],
|
|
207
|
-
supportHtml5History = true,
|
|
208
|
-
basename = '',
|
|
209
|
-
routesConfig,
|
|
210
|
-
createRoutes,
|
|
211
|
-
} = mergedConfig;
|
|
212
|
-
|
|
213
|
-
const finalRouteConfig = {
|
|
214
|
-
routes: getGlobalRoutes(),
|
|
215
|
-
globalApp: getGlobalLayoutApp(),
|
|
216
|
-
...routesConfig,
|
|
217
|
-
};
|
|
218
|
-
|
|
219
|
-
if (!finalRouteConfig.routes && !createRoutes) {
|
|
472
|
+
if (getRouteObjects().length === 0) {
|
|
220
473
|
return App;
|
|
221
474
|
}
|
|
222
475
|
|
|
223
|
-
const hooks = api.getHooks();
|
|
224
|
-
|
|
225
|
-
let cachedRouteObjects: RouteObject[] | undefined;
|
|
226
|
-
|
|
227
|
-
const getRouteObjects = () => {
|
|
228
|
-
if (typeof cachedRouteObjects !== 'undefined') {
|
|
229
|
-
return cachedRouteObjects;
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
const routeObjects = createRoutes
|
|
233
|
-
? createRoutes()
|
|
234
|
-
: createRouteObjectsFromConfig({
|
|
235
|
-
routesConfig: finalRouteConfig,
|
|
236
|
-
}) || [];
|
|
237
|
-
|
|
238
|
-
const normalizedRouteObjects = createRoutes
|
|
239
|
-
? routeObjects
|
|
240
|
-
: stripSyntheticNotFoundRoute(routeObjects);
|
|
241
|
-
|
|
242
|
-
cachedRouteObjects = hooks.modifyRoutes.call(
|
|
243
|
-
normalizedRouteObjects,
|
|
244
|
-
) as RouteObject[];
|
|
245
|
-
return cachedRouteObjects;
|
|
246
|
-
};
|
|
247
|
-
|
|
248
|
-
const selectBasePath = (pathname: string) => {
|
|
249
|
-
const match = serverBase.find(baseUrl =>
|
|
250
|
-
isSegmentPrefix(pathname, baseUrl),
|
|
251
|
-
);
|
|
252
|
-
return match || '/';
|
|
253
|
-
};
|
|
254
|
-
|
|
255
|
-
let cachedRouteTree: ReturnType<
|
|
256
|
-
typeof createRouteTreeFromRouteObjects
|
|
257
|
-
> | null = null;
|
|
258
|
-
let cachedRouter: AnyRouter | null = null;
|
|
259
|
-
let cachedRouterBasepath: string | null = null;
|
|
260
|
-
|
|
261
476
|
const RouterWrapper = () => {
|
|
262
477
|
const runtimeContext = useContext(
|
|
263
478
|
InternalRuntimeContext,
|
|
264
479
|
) as TInternalRuntimeContext;
|
|
265
480
|
|
|
266
|
-
const
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
);
|
|
270
|
-
const _basename =
|
|
271
|
-
baseUrl === '/'
|
|
272
|
-
? urlJoin(
|
|
273
|
-
baseUrl,
|
|
274
|
-
runtimeContext._internalRouterBaseName || basename || '',
|
|
275
|
-
)
|
|
276
|
-
: baseUrl;
|
|
277
|
-
|
|
278
|
-
const routeTree = useMemo(() => {
|
|
279
|
-
if (cachedRouteTree) {
|
|
280
|
-
return cachedRouteTree;
|
|
281
|
-
}
|
|
282
|
-
const routeObjects = getRouteObjects();
|
|
283
|
-
if (!routeObjects.length) {
|
|
284
|
-
return null;
|
|
285
|
-
}
|
|
286
|
-
cachedRouteTree = createRouteTreeFromRouteObjects(routeObjects, {
|
|
287
|
-
rscPayloadRouter: getGlobalEnableRsc(),
|
|
288
|
-
});
|
|
289
|
-
return cachedRouteTree;
|
|
290
|
-
}, []);
|
|
481
|
+
const _basename = getClientBasename(runtimeContext);
|
|
482
|
+
|
|
483
|
+
const routeTree = useMemo(() => getRouteTree(), []);
|
|
291
484
|
|
|
292
485
|
if (!routeTree) {
|
|
293
486
|
return App ? <App /> : null;
|
|
294
487
|
}
|
|
295
488
|
|
|
296
|
-
const router = useMemo(
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
};
|
|
304
|
-
hooks.onBeforeCreateRouter.call(lifecycleContext);
|
|
305
|
-
|
|
306
|
-
if (cachedRouter && cachedRouterBasepath === _basename) {
|
|
307
|
-
wrapRouterSubscribeWithBlockState(
|
|
308
|
-
cachedRouter,
|
|
309
|
-
runtimeContext.unstable_getBlockNavState,
|
|
310
|
-
);
|
|
311
|
-
hooks.onAfterCreateRouter.call({
|
|
312
|
-
...lifecycleContext,
|
|
313
|
-
router: cachedRouter,
|
|
314
|
-
runtimeContext,
|
|
315
|
-
});
|
|
316
|
-
return cachedRouter;
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
const history = supportHtml5History
|
|
320
|
-
? createBrowserHistory()
|
|
321
|
-
: createHashHistory();
|
|
322
|
-
|
|
323
|
-
const rewrite = createModernBasepathRewrite(_basename);
|
|
324
|
-
const serializationAdapters = getGlobalEnableRsc()
|
|
325
|
-
? getTanstackRscSerializationAdapters()
|
|
326
|
-
: undefined;
|
|
327
|
-
|
|
328
|
-
cachedRouter = createRouter({
|
|
329
|
-
...getModernTanstackRouterFastDefaults(mergedConfig),
|
|
330
|
-
routeTree,
|
|
331
|
-
basepath: '/',
|
|
332
|
-
rewrite,
|
|
333
|
-
history,
|
|
334
|
-
context: {},
|
|
335
|
-
...(serializationAdapters ? { serializationAdapters } : {}),
|
|
336
|
-
});
|
|
337
|
-
cachedRouterBasepath = _basename;
|
|
338
|
-
wrapRouterSubscribeWithBlockState(
|
|
339
|
-
cachedRouter,
|
|
340
|
-
runtimeContext.unstable_getBlockNavState,
|
|
341
|
-
);
|
|
342
|
-
hooks.onAfterCreateRouter.call({
|
|
343
|
-
...lifecycleContext,
|
|
344
|
-
router: cachedRouter,
|
|
345
|
-
runtimeContext,
|
|
346
|
-
});
|
|
347
|
-
|
|
348
|
-
return cachedRouter;
|
|
349
|
-
}, [_basename, routeTree, supportHtml5History, runtimeContext]);
|
|
489
|
+
const router = useMemo(
|
|
490
|
+
() => getRouter(runtimeContext, _basename),
|
|
491
|
+
[_basename, routeTree, runtimeContext],
|
|
492
|
+
);
|
|
493
|
+
if (!router) {
|
|
494
|
+
return App ? <App /> : null;
|
|
495
|
+
}
|
|
350
496
|
const runtimeState = applyRouterRuntimeState(runtimeContext, {
|
|
351
497
|
framework: 'tanstack',
|
|
352
498
|
basename: _basename,
|
|
@@ -363,8 +509,10 @@ export const tanstackRouterPlugin = (
|
|
|
363
509
|
|
|
364
510
|
const hasSSRBootstrap =
|
|
365
511
|
typeof window !== 'undefined' &&
|
|
366
|
-
Boolean((window as WindowWithTanstackSsr).$_TSR)
|
|
367
|
-
|
|
512
|
+
(Boolean((window as WindowWithTanstackSsr).$_TSR) ||
|
|
513
|
+
hasTanstackSsrHydrationRecord(router));
|
|
514
|
+
const needsRouterClient = hasSSRBootstrap;
|
|
515
|
+
if (needsRouterClient) {
|
|
368
516
|
hooks.onBeforeHydrateRouter.call({
|
|
369
517
|
...lifecycleContext,
|
|
370
518
|
phase: 'hydrate',
|
|
@@ -373,8 +521,8 @@ export const tanstackRouterPlugin = (
|
|
|
373
521
|
});
|
|
374
522
|
}
|
|
375
523
|
|
|
376
|
-
const RouterContent =
|
|
377
|
-
<
|
|
524
|
+
const RouterContent = needsRouterClient ? (
|
|
525
|
+
<ModernRouterClient router={router} />
|
|
378
526
|
) : (
|
|
379
527
|
<RouterProvider router={router} />
|
|
380
528
|
);
|
|
@@ -382,7 +530,7 @@ export const tanstackRouterPlugin = (
|
|
|
382
530
|
RouterContent,
|
|
383
531
|
hasSSRBootstrap,
|
|
384
532
|
);
|
|
385
|
-
if (
|
|
533
|
+
if (needsRouterClient) {
|
|
386
534
|
hooks.onAfterHydrateRouter.call({
|
|
387
535
|
...lifecycleContext,
|
|
388
536
|
phase: 'hydrate',
|