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