@bleedingdev/modern-js-plugin-tanstack 3.2.0-ultramodern.0
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/LICENSE +21 -0
- package/dist/cjs/cli/index.js +268 -0
- package/dist/cjs/cli/tanstackTypes.js +388 -0
- package/dist/cjs/cli.js +65 -0
- package/dist/cjs/runtime/DefaultNotFound.js +47 -0
- package/dist/cjs/runtime/basepathRewrite.js +62 -0
- package/dist/cjs/runtime/dataMutation.js +345 -0
- package/dist/cjs/runtime/hooks.js +57 -0
- package/dist/cjs/runtime/index.js +114 -0
- package/dist/cjs/runtime/lifecycle.js +125 -0
- package/dist/cjs/runtime/plugin.js +250 -0
- package/dist/cjs/runtime/plugin.node.js +304 -0
- package/dist/cjs/runtime/prefetchLink.js +55 -0
- package/dist/cjs/runtime/routeTree.js +492 -0
- package/dist/cjs/runtime/rsc/ClientSlot.js +53 -0
- package/dist/cjs/runtime/rsc/CompositeComponent.js +75 -0
- package/dist/cjs/runtime/rsc/ReplayableStream.js +141 -0
- package/dist/cjs/runtime/rsc/RscNodeRenderer.js +65 -0
- package/dist/cjs/runtime/rsc/SlotContext.js +54 -0
- package/dist/cjs/runtime/rsc/client.js +93 -0
- package/dist/cjs/runtime/rsc/createRscProxy.js +141 -0
- package/dist/cjs/runtime/rsc/index.js +42 -0
- package/dist/cjs/runtime/rsc/payloadRouter.js +211 -0
- package/dist/cjs/runtime/rsc/server.js +246 -0
- package/dist/cjs/runtime/rsc/slotUsageSanitizer.js +65 -0
- package/dist/cjs/runtime/rsc/symbols.js +72 -0
- package/dist/cjs/runtime/types.js +18 -0
- package/dist/cjs/runtime/utils.js +142 -0
- package/dist/cjs/runtime.js +58 -0
- package/dist/esm/cli/index.mjs +201 -0
- package/dist/esm/cli/tanstackTypes.mjs +341 -0
- package/dist/esm/cli.mjs +2 -0
- package/dist/esm/rslib-runtime.mjs +18 -0
- package/dist/esm/runtime/DefaultNotFound.mjs +13 -0
- package/dist/esm/runtime/basepathRewrite.mjs +28 -0
- package/dist/esm/runtime/dataMutation.mjs +305 -0
- package/dist/esm/runtime/hooks.mjs +8 -0
- package/dist/esm/runtime/index.mjs +6 -0
- package/dist/esm/runtime/lifecycle.mjs +82 -0
- package/dist/esm/runtime/plugin.mjs +214 -0
- package/dist/esm/runtime/plugin.node.mjs +268 -0
- package/dist/esm/runtime/prefetchLink.mjs +18 -0
- package/dist/esm/runtime/routeTree.mjs +452 -0
- package/dist/esm/runtime/rsc/ClientSlot.mjs +19 -0
- package/dist/esm/runtime/rsc/CompositeComponent.mjs +41 -0
- package/dist/esm/runtime/rsc/ReplayableStream.mjs +104 -0
- package/dist/esm/runtime/rsc/RscNodeRenderer.mjs +31 -0
- package/dist/esm/runtime/rsc/SlotContext.mjs +17 -0
- package/dist/esm/runtime/rsc/client.mjs +53 -0
- package/dist/esm/runtime/rsc/createRscProxy.mjs +107 -0
- package/dist/esm/runtime/rsc/index.mjs +1 -0
- package/dist/esm/runtime/rsc/payloadRouter.mjs +162 -0
- package/dist/esm/runtime/rsc/server.mjs +200 -0
- package/dist/esm/runtime/rsc/slotUsageSanitizer.mjs +31 -0
- package/dist/esm/runtime/rsc/symbols.mjs +17 -0
- package/dist/esm/runtime/types.mjs +0 -0
- package/dist/esm/runtime/utils.mjs +89 -0
- package/dist/esm/runtime.mjs +1 -0
- package/dist/esm-node/cli/index.mjs +205 -0
- package/dist/esm-node/cli/tanstackTypes.mjs +342 -0
- package/dist/esm-node/cli.mjs +3 -0
- package/dist/esm-node/rslib-runtime.mjs +19 -0
- package/dist/esm-node/runtime/DefaultNotFound.mjs +14 -0
- package/dist/esm-node/runtime/basepathRewrite.mjs +29 -0
- package/dist/esm-node/runtime/dataMutation.mjs +306 -0
- package/dist/esm-node/runtime/hooks.mjs +9 -0
- package/dist/esm-node/runtime/index.mjs +7 -0
- package/dist/esm-node/runtime/lifecycle.mjs +83 -0
- package/dist/esm-node/runtime/plugin.mjs +215 -0
- package/dist/esm-node/runtime/plugin.node.mjs +269 -0
- package/dist/esm-node/runtime/prefetchLink.mjs +19 -0
- package/dist/esm-node/runtime/routeTree.mjs +453 -0
- package/dist/esm-node/runtime/rsc/ClientSlot.mjs +20 -0
- package/dist/esm-node/runtime/rsc/CompositeComponent.mjs +42 -0
- package/dist/esm-node/runtime/rsc/ReplayableStream.mjs +105 -0
- package/dist/esm-node/runtime/rsc/RscNodeRenderer.mjs +32 -0
- package/dist/esm-node/runtime/rsc/SlotContext.mjs +18 -0
- package/dist/esm-node/runtime/rsc/client.mjs +54 -0
- package/dist/esm-node/runtime/rsc/createRscProxy.mjs +108 -0
- package/dist/esm-node/runtime/rsc/index.mjs +2 -0
- package/dist/esm-node/runtime/rsc/payloadRouter.mjs +163 -0
- package/dist/esm-node/runtime/rsc/server.mjs +201 -0
- package/dist/esm-node/runtime/rsc/slotUsageSanitizer.mjs +32 -0
- package/dist/esm-node/runtime/rsc/symbols.mjs +18 -0
- package/dist/esm-node/runtime/types.mjs +1 -0
- package/dist/esm-node/runtime/utils.mjs +90 -0
- package/dist/esm-node/runtime.mjs +2 -0
- package/dist/types/cli/index.d.ts +20 -0
- package/dist/types/cli/tanstackTypes.d.ts +11 -0
- package/dist/types/cli.d.ts +2 -0
- package/dist/types/runtime/DefaultNotFound.d.ts +2 -0
- package/dist/types/runtime/basepathRewrite.d.ts +8 -0
- package/dist/types/runtime/dataMutation.d.ts +29 -0
- package/dist/types/runtime/hooks.d.ts +18 -0
- package/dist/types/runtime/index.d.ts +9 -0
- package/dist/types/runtime/lifecycle.d.ts +22 -0
- package/dist/types/runtime/plugin.d.ts +17 -0
- package/dist/types/runtime/plugin.node.d.ts +17 -0
- package/dist/types/runtime/prefetchLink.d.ts +11 -0
- package/dist/types/runtime/routeTree.d.ts +11 -0
- package/dist/types/runtime/rsc/ClientSlot.d.ts +5 -0
- package/dist/types/runtime/rsc/CompositeComponent.d.ts +3 -0
- package/dist/types/runtime/rsc/ReplayableStream.d.ts +24 -0
- package/dist/types/runtime/rsc/RscNodeRenderer.d.ts +5 -0
- package/dist/types/runtime/rsc/SlotContext.d.ts +11 -0
- package/dist/types/runtime/rsc/client.d.ts +11 -0
- package/dist/types/runtime/rsc/createRscProxy.d.ts +7 -0
- package/dist/types/runtime/rsc/index.d.ts +2 -0
- package/dist/types/runtime/rsc/payloadRouter.d.ts +24 -0
- package/dist/types/runtime/rsc/server.d.ts +14 -0
- package/dist/types/runtime/rsc/slotUsageSanitizer.d.ts +2 -0
- package/dist/types/runtime/rsc/symbols.d.ts +46 -0
- package/dist/types/runtime/types.d.ts +68 -0
- package/dist/types/runtime/utils.d.ts +36 -0
- package/dist/types/runtime.d.ts +1 -0
- package/dist/types-direct/cli/index.d.ts +20 -0
- package/dist/types-direct/cli/tanstackTypes.d.ts +11 -0
- package/dist/types-direct/cli.d.ts +2 -0
- package/dist/types-direct/runtime/DefaultNotFound.d.ts +2 -0
- package/dist/types-direct/runtime/basepathRewrite.d.ts +8 -0
- package/dist/types-direct/runtime/dataMutation.d.ts +29 -0
- package/dist/types-direct/runtime/hooks.d.ts +18 -0
- package/dist/types-direct/runtime/index.d.ts +9 -0
- package/dist/types-direct/runtime/lifecycle.d.ts +22 -0
- package/dist/types-direct/runtime/plugin.d.ts +17 -0
- package/dist/types-direct/runtime/plugin.node.d.ts +17 -0
- package/dist/types-direct/runtime/prefetchLink.d.ts +11 -0
- package/dist/types-direct/runtime/routeTree.d.ts +11 -0
- package/dist/types-direct/runtime/rsc/ClientSlot.d.ts +5 -0
- package/dist/types-direct/runtime/rsc/CompositeComponent.d.ts +3 -0
- package/dist/types-direct/runtime/rsc/ReplayableStream.d.ts +24 -0
- package/dist/types-direct/runtime/rsc/RscNodeRenderer.d.ts +5 -0
- package/dist/types-direct/runtime/rsc/SlotContext.d.ts +11 -0
- package/dist/types-direct/runtime/rsc/client.d.ts +11 -0
- package/dist/types-direct/runtime/rsc/createRscProxy.d.ts +7 -0
- package/dist/types-direct/runtime/rsc/index.d.ts +2 -0
- package/dist/types-direct/runtime/rsc/payloadRouter.d.ts +24 -0
- package/dist/types-direct/runtime/rsc/server.d.ts +14 -0
- package/dist/types-direct/runtime/rsc/slotUsageSanitizer.d.ts +2 -0
- package/dist/types-direct/runtime/rsc/symbols.d.ts +46 -0
- package/dist/types-direct/runtime/types.d.ts +68 -0
- package/dist/types-direct/runtime/utils.d.ts +36 -0
- package/dist/types-direct/runtime.d.ts +1 -0
- package/package.json +126 -0
- package/rslib.config.mts +4 -0
- package/rstest.config.mts +43 -0
- package/src/cli/index.ts +388 -0
- package/src/cli/tanstackTypes.ts +503 -0
- package/src/cli.ts +2 -0
- package/src/runtime/DefaultNotFound.tsx +15 -0
- package/src/runtime/basepathRewrite.ts +59 -0
- package/src/runtime/dataMutation.tsx +517 -0
- package/src/runtime/hooks.ts +34 -0
- package/src/runtime/index.tsx +30 -0
- package/src/runtime/lifecycle.ts +150 -0
- package/src/runtime/plugin.node.tsx +534 -0
- package/src/runtime/plugin.tsx +395 -0
- package/src/runtime/prefetchLink.tsx +87 -0
- package/src/runtime/routeTree.ts +942 -0
- package/src/runtime/rsc/ClientSlot.tsx +25 -0
- package/src/runtime/rsc/CompositeComponent.tsx +65 -0
- package/src/runtime/rsc/ReplayableStream.ts +155 -0
- package/src/runtime/rsc/RscNodeRenderer.tsx +45 -0
- package/src/runtime/rsc/SlotContext.tsx +31 -0
- package/src/runtime/rsc/client.tsx +90 -0
- package/src/runtime/rsc/createRscProxy.tsx +189 -0
- package/src/runtime/rsc/index.ts +10 -0
- package/src/runtime/rsc/payloadRouter.ts +318 -0
- package/src/runtime/rsc/server.tsx +303 -0
- package/src/runtime/rsc/slotUsageSanitizer.ts +76 -0
- package/src/runtime/rsc/symbols.ts +106 -0
- package/src/runtime/ssr-shim.d.ts +12 -0
- package/src/runtime/types.ts +83 -0
- package/src/runtime/utils.tsx +161 -0
- package/src/runtime.ts +1 -0
- package/tests/router/cli.test.ts +386 -0
- package/tests/router/dataMutation.test.tsx +396 -0
- package/tests/router/prefetchLink.test.tsx +43 -0
- package/tests/router/routeTree.test.ts +502 -0
- package/tests/router/rsc.test.tsx +256 -0
- package/tests/router/tanstackTypes.test.ts +62 -0
- package/tsconfig.json +12 -0
- package/tsconfig.tsgo.json +6 -0
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import type { TInternalRuntimeContext } from '@modern-js/runtime/context';
|
|
2
|
+
import type { RouteObject } from '@modern-js/runtime-utils/router';
|
|
3
|
+
import type {
|
|
4
|
+
InternalRouterRuntimeState,
|
|
5
|
+
InternalRouterServerSnapshot,
|
|
6
|
+
RouterFramework,
|
|
7
|
+
RouterRouteMatchSnapshot,
|
|
8
|
+
RouterServerPrepareResult,
|
|
9
|
+
} from './types';
|
|
10
|
+
|
|
11
|
+
export type RouterLifecyclePhase = 'ssr-prepare' | 'client-create' | 'hydrate';
|
|
12
|
+
|
|
13
|
+
export type RouterLifecycleContext = {
|
|
14
|
+
framework: RouterFramework;
|
|
15
|
+
phase: RouterLifecyclePhase;
|
|
16
|
+
routes: RouteObject[];
|
|
17
|
+
runtimeContext: TInternalRuntimeContext;
|
|
18
|
+
basename?: string;
|
|
19
|
+
hydrationData?: unknown;
|
|
20
|
+
router?: unknown;
|
|
21
|
+
matches?: RouterRouteMatchSnapshot[];
|
|
22
|
+
cleanup?: () => void | Promise<void>;
|
|
23
|
+
serverSnapshot?: InternalRouterServerSnapshot;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
type RouterSnapshotLike = Partial<InternalRouterServerSnapshot>;
|
|
27
|
+
|
|
28
|
+
function toHydrationScripts(state: {
|
|
29
|
+
hydrationScript?: string;
|
|
30
|
+
hydrationScripts?: string[];
|
|
31
|
+
}) {
|
|
32
|
+
if (state.hydrationScripts?.length) {
|
|
33
|
+
return state.hydrationScripts;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return state.hydrationScript ? [state.hydrationScript] : undefined;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function getMatchedRouteIdsFromMatches(matches?: RouterRouteMatchSnapshot[]) {
|
|
40
|
+
const routeIds = matches
|
|
41
|
+
?.map(match => match.assetRouteId ?? match.routeId)
|
|
42
|
+
.filter((routeId): routeId is string => typeof routeId === 'string');
|
|
43
|
+
|
|
44
|
+
return routeIds?.length ? routeIds : undefined;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function createRouterServerSnapshot(
|
|
48
|
+
state: RouterSnapshotLike,
|
|
49
|
+
): InternalRouterServerSnapshot {
|
|
50
|
+
const hydrationScripts = toHydrationScripts(state);
|
|
51
|
+
const matchedRouteIds =
|
|
52
|
+
state.matchedRouteIds ?? getMatchedRouteIdsFromMatches(state.matches);
|
|
53
|
+
|
|
54
|
+
return {
|
|
55
|
+
...state,
|
|
56
|
+
...(hydrationScripts?.length
|
|
57
|
+
? {
|
|
58
|
+
hydrationScript: state.hydrationScript ?? hydrationScripts[0],
|
|
59
|
+
hydrationScripts,
|
|
60
|
+
}
|
|
61
|
+
: {}),
|
|
62
|
+
...(matchedRouteIds ? { matchedRouteIds } : {}),
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function createRouterRuntimeState(
|
|
67
|
+
state: InternalRouterRuntimeState,
|
|
68
|
+
): InternalRouterRuntimeState {
|
|
69
|
+
const hasSnapshotState =
|
|
70
|
+
Boolean(state.serverSnapshot) ||
|
|
71
|
+
Boolean(state.hydrationScript) ||
|
|
72
|
+
Boolean(state.hydrationScripts?.length) ||
|
|
73
|
+
Boolean(state.matchedRouteIds?.length) ||
|
|
74
|
+
Boolean(state.matches?.length);
|
|
75
|
+
const serverSnapshot = state.serverSnapshot
|
|
76
|
+
? createRouterServerSnapshot({
|
|
77
|
+
...state.serverSnapshot,
|
|
78
|
+
framework: state.serverSnapshot.framework ?? state.framework,
|
|
79
|
+
basename: state.serverSnapshot.basename ?? state.basename,
|
|
80
|
+
hydrationScript:
|
|
81
|
+
state.serverSnapshot.hydrationScript ?? state.hydrationScript,
|
|
82
|
+
hydrationScripts:
|
|
83
|
+
state.serverSnapshot.hydrationScripts ?? state.hydrationScripts,
|
|
84
|
+
matchedRouteIds:
|
|
85
|
+
state.serverSnapshot.matchedRouteIds ?? state.matchedRouteIds,
|
|
86
|
+
matches: state.serverSnapshot.matches ?? state.matches,
|
|
87
|
+
})
|
|
88
|
+
: hasSnapshotState
|
|
89
|
+
? createRouterServerSnapshot({
|
|
90
|
+
framework: state.framework,
|
|
91
|
+
basename: state.basename,
|
|
92
|
+
hydrationScript: state.hydrationScript,
|
|
93
|
+
hydrationScripts: state.hydrationScripts,
|
|
94
|
+
matchedRouteIds: state.matchedRouteIds,
|
|
95
|
+
matches: state.matches,
|
|
96
|
+
})
|
|
97
|
+
: undefined;
|
|
98
|
+
const hydrationScripts = toHydrationScripts({
|
|
99
|
+
hydrationScript: state.hydrationScript ?? serverSnapshot?.hydrationScript,
|
|
100
|
+
hydrationScripts:
|
|
101
|
+
state.hydrationScripts ?? serverSnapshot?.hydrationScripts,
|
|
102
|
+
});
|
|
103
|
+
const matchedRouteIds =
|
|
104
|
+
state.matchedRouteIds ??
|
|
105
|
+
serverSnapshot?.matchedRouteIds ??
|
|
106
|
+
getMatchedRouteIdsFromMatches(state.matches);
|
|
107
|
+
|
|
108
|
+
return {
|
|
109
|
+
...state,
|
|
110
|
+
...(hydrationScripts?.length
|
|
111
|
+
? {
|
|
112
|
+
hydrationScript: state.hydrationScript ?? hydrationScripts[0],
|
|
113
|
+
hydrationScripts,
|
|
114
|
+
}
|
|
115
|
+
: {}),
|
|
116
|
+
...(matchedRouteIds ? { matchedRouteIds } : {}),
|
|
117
|
+
...(serverSnapshot ? { serverSnapshot } : {}),
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export function applyRouterRuntimeState(
|
|
122
|
+
runtimeContext: TInternalRuntimeContext,
|
|
123
|
+
state: InternalRouterRuntimeState,
|
|
124
|
+
) {
|
|
125
|
+
const normalized = createRouterRuntimeState(state);
|
|
126
|
+
const mutableRuntimeContext = runtimeContext as any;
|
|
127
|
+
mutableRuntimeContext.routerFramework = normalized.framework;
|
|
128
|
+
mutableRuntimeContext.routerInstance = normalized.instance;
|
|
129
|
+
mutableRuntimeContext.routerHydrationScript = normalized.hydrationScript;
|
|
130
|
+
mutableRuntimeContext.routerMatchedRouteIds = normalized.matchedRouteIds;
|
|
131
|
+
mutableRuntimeContext.routerRuntime = normalized;
|
|
132
|
+
if (normalized.serverSnapshot) {
|
|
133
|
+
mutableRuntimeContext.routerServerSnapshot = normalized.serverSnapshot;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return runtimeContext;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export function applyRouterServerPrepareResult(
|
|
140
|
+
runtimeContext: TInternalRuntimeContext,
|
|
141
|
+
result: RouterServerPrepareResult,
|
|
142
|
+
) {
|
|
143
|
+
const state = createRouterRuntimeState({
|
|
144
|
+
...result.state,
|
|
145
|
+
cleanup: result.cleanup ?? result.state.cleanup,
|
|
146
|
+
serverSnapshot: result.snapshot ?? result.state.serverSnapshot,
|
|
147
|
+
});
|
|
148
|
+
applyRouterRuntimeState(runtimeContext, state);
|
|
149
|
+
return runtimeContext;
|
|
150
|
+
}
|
|
@@ -0,0 +1,534 @@
|
|
|
1
|
+
/// <reference path="./ssr-shim.d.ts" />
|
|
2
|
+
|
|
3
|
+
import type { Plugin, RuntimePluginExtends } from '@modern-js/plugin';
|
|
4
|
+
import type { RuntimePluginAPI } from '@modern-js/plugin/runtime';
|
|
5
|
+
import {
|
|
6
|
+
getGlobalEnableRsc,
|
|
7
|
+
getGlobalLayoutApp,
|
|
8
|
+
getGlobalRoutes,
|
|
9
|
+
InternalRuntimeContext,
|
|
10
|
+
type ServerPayload,
|
|
11
|
+
type TInternalRuntimeContext,
|
|
12
|
+
} from '@modern-js/runtime/context';
|
|
13
|
+
import { merge } from '@modern-js/runtime-utils/merge';
|
|
14
|
+
import {
|
|
15
|
+
createRequestContext,
|
|
16
|
+
type RequestContext,
|
|
17
|
+
storage,
|
|
18
|
+
} from '@modern-js/runtime-utils/node';
|
|
19
|
+
import type { RouteObject } from '@modern-js/runtime-utils/router';
|
|
20
|
+
import { time } from '@modern-js/runtime-utils/time';
|
|
21
|
+
import { LOADER_REPORTER_NAME } from '@modern-js/utils/universal/constants';
|
|
22
|
+
import {
|
|
23
|
+
type AnyRouter,
|
|
24
|
+
createMemoryHistory,
|
|
25
|
+
createRouter,
|
|
26
|
+
RouterProvider,
|
|
27
|
+
} from '@tanstack/react-router';
|
|
28
|
+
import { attachRouterServerSsrUtils } from '@tanstack/react-router/ssr/server';
|
|
29
|
+
import type React from 'react';
|
|
30
|
+
import { Suspense, useContext } from 'react';
|
|
31
|
+
import { createModernBasepathRewrite } from './basepathRewrite';
|
|
32
|
+
import {
|
|
33
|
+
modifyRoutes as modifyRoutesHook,
|
|
34
|
+
onAfterCreateRouter as onAfterCreateRouterHook,
|
|
35
|
+
onAfterHydrateRouter as onAfterHydrateRouterHook,
|
|
36
|
+
onBeforeCreateRouter as onBeforeCreateRouterHook,
|
|
37
|
+
onBeforeCreateRoutes as onBeforeCreateRoutesHook,
|
|
38
|
+
onBeforeHydrateRouter as onBeforeHydrateRouterHook,
|
|
39
|
+
type RouterExtendsHooks,
|
|
40
|
+
} from './hooks';
|
|
41
|
+
import {
|
|
42
|
+
applyRouterServerPrepareResult,
|
|
43
|
+
createRouterServerSnapshot,
|
|
44
|
+
type RouterLifecycleContext,
|
|
45
|
+
} from './lifecycle';
|
|
46
|
+
import {
|
|
47
|
+
createRouteTreeFromRouteObjects,
|
|
48
|
+
getModernRouteIdsFromMatches,
|
|
49
|
+
} from './routeTree';
|
|
50
|
+
import {
|
|
51
|
+
createTanstackRscServerPayload,
|
|
52
|
+
handleTanstackRscRedirect,
|
|
53
|
+
} from './rsc/payloadRouter';
|
|
54
|
+
import type { InternalRouterServerSnapshot, RouterConfig } from './types';
|
|
55
|
+
import { createRouteObjectsFromConfig, urlJoin } from './utils';
|
|
56
|
+
|
|
57
|
+
type ModernTanstackRouterContext = {
|
|
58
|
+
request: Request;
|
|
59
|
+
requestContext: RequestContext<Record<string, unknown>>;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
type TanstackRouterRuntimeConfig = {
|
|
63
|
+
plugins?: TanstackRouterRuntimePlugin[];
|
|
64
|
+
router?: Partial<RouterConfig>;
|
|
65
|
+
[key: string]: unknown;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
type TanstackRouterRuntimeExtends = Required<
|
|
69
|
+
RuntimePluginExtends<TanstackRouterRuntimeConfig, TInternalRuntimeContext>
|
|
70
|
+
> & {
|
|
71
|
+
extendHooks: RouterExtendsHooks;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
type TanstackRouterPluginAPI = RuntimePluginAPI<TanstackRouterRuntimeExtends>;
|
|
75
|
+
|
|
76
|
+
type TanstackRouterRuntimePlugin = Plugin<
|
|
77
|
+
TanstackRouterPluginAPI,
|
|
78
|
+
TInternalRuntimeContext
|
|
79
|
+
>;
|
|
80
|
+
|
|
81
|
+
const setTanstackRscServerPayload = (payload: ServerPayload) => {
|
|
82
|
+
const storageContext = storage.useContext?.() as
|
|
83
|
+
| { serverPayload?: ServerPayload }
|
|
84
|
+
| undefined;
|
|
85
|
+
if (storageContext) {
|
|
86
|
+
storageContext.serverPayload = payload;
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
type RouterManagedTag = {
|
|
91
|
+
attrs?: Record<string, unknown>;
|
|
92
|
+
children?: unknown;
|
|
93
|
+
tag?: unknown;
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
type RouterMatchWithError = {
|
|
97
|
+
error?: unknown;
|
|
98
|
+
route?: {
|
|
99
|
+
id?: unknown;
|
|
100
|
+
options?: RouterRouteOptions;
|
|
101
|
+
};
|
|
102
|
+
routeId?: unknown;
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
type RouterRouteOptions = {
|
|
106
|
+
component?: unknown;
|
|
107
|
+
errorComponent?: unknown;
|
|
108
|
+
notFoundComponent?: unknown;
|
|
109
|
+
pendingComponent?: unknown;
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
type RouterRouteWithOptions = {
|
|
113
|
+
options?: RouterRouteOptions;
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
type PreloadableRouteComponent = {
|
|
117
|
+
load?: (props?: Record<string, unknown>) => Promise<unknown> | unknown;
|
|
118
|
+
preload?: (props?: Record<string, unknown>) => Promise<unknown> | unknown;
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
type TanstackRouterWithServerSsr = AnyRouter & {
|
|
122
|
+
resolveRedirect?: (redirect: Response) => Response;
|
|
123
|
+
routesById?: Record<string, RouterRouteWithOptions>;
|
|
124
|
+
serverSsr?: {
|
|
125
|
+
cleanup?: () => void;
|
|
126
|
+
dehydrate?: () => Promise<void> | void;
|
|
127
|
+
isSerializationFinished?: () => boolean;
|
|
128
|
+
onSerializationFinished?: (listener: () => void) => void;
|
|
129
|
+
takeBufferedScripts?: () => unknown;
|
|
130
|
+
};
|
|
131
|
+
state: AnyRouter['state'] & {
|
|
132
|
+
matches?: unknown;
|
|
133
|
+
redirect?: Response;
|
|
134
|
+
};
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
function isPreloadableRouteComponent(
|
|
138
|
+
component: unknown,
|
|
139
|
+
): component is PreloadableRouteComponent {
|
|
140
|
+
if (!component || typeof component !== 'function') {
|
|
141
|
+
return false;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const preloadable = component as PreloadableRouteComponent;
|
|
145
|
+
return (
|
|
146
|
+
typeof preloadable.load === 'function' ||
|
|
147
|
+
typeof preloadable.preload === 'function'
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
async function preloadRouteComponent(component: unknown) {
|
|
152
|
+
if (!isPreloadableRouteComponent(component)) {
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (typeof component.load === 'function') {
|
|
157
|
+
await component.load({});
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
await component.preload?.({});
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
async function preloadMatchedRouteComponents(
|
|
165
|
+
tanstackRouter: TanstackRouterWithServerSsr,
|
|
166
|
+
) {
|
|
167
|
+
const matches = Array.isArray(tanstackRouter.state.matches)
|
|
168
|
+
? (tanstackRouter.state.matches as RouterMatchWithError[])
|
|
169
|
+
: [];
|
|
170
|
+
const routesById = tanstackRouter.routesById || {};
|
|
171
|
+
|
|
172
|
+
await Promise.all(
|
|
173
|
+
matches.map(async match => {
|
|
174
|
+
const routeId =
|
|
175
|
+
typeof match.routeId === 'string'
|
|
176
|
+
? match.routeId
|
|
177
|
+
: typeof match.route?.id === 'string'
|
|
178
|
+
? match.route.id
|
|
179
|
+
: undefined;
|
|
180
|
+
const route = routeId ? routesById[routeId] : match.route;
|
|
181
|
+
const options = route?.options;
|
|
182
|
+
if (!options) {
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
await Promise.all([
|
|
187
|
+
preloadRouteComponent(options.component),
|
|
188
|
+
preloadRouteComponent(options.pendingComponent),
|
|
189
|
+
preloadRouteComponent(options.errorComponent),
|
|
190
|
+
preloadRouteComponent(options.notFoundComponent),
|
|
191
|
+
]);
|
|
192
|
+
}),
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
async function waitForRouterSerialization(
|
|
197
|
+
tanstackRouter: TanstackRouterWithServerSsr,
|
|
198
|
+
) {
|
|
199
|
+
const serverSsr = tanstackRouter.serverSsr;
|
|
200
|
+
if (
|
|
201
|
+
!serverSsr ||
|
|
202
|
+
typeof serverSsr.onSerializationFinished !== 'function' ||
|
|
203
|
+
serverSsr.isSerializationFinished?.()
|
|
204
|
+
) {
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
await new Promise<void>(resolve => {
|
|
209
|
+
serverSsr.onSerializationFinished?.(resolve);
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function htmlEscapeAttr(value: string) {
|
|
214
|
+
return value.replace(/&/g, '&').replace(/"/g, '"');
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function routerManagedTagToHtml(tag: unknown): string {
|
|
218
|
+
if (!tag || typeof tag !== 'object') {
|
|
219
|
+
return '';
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const managedTag = tag as RouterManagedTag;
|
|
223
|
+
if (!managedTag || managedTag.tag !== 'script') {
|
|
224
|
+
return '';
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const attrs: Record<string, unknown> = managedTag.attrs || {};
|
|
228
|
+
const attrsStr = Object.entries(attrs)
|
|
229
|
+
.filter(([, v]) => v != null && v !== false)
|
|
230
|
+
.map(([k, v]) => {
|
|
231
|
+
const name = k === 'className' ? 'class' : k;
|
|
232
|
+
if (v === true) {
|
|
233
|
+
return name;
|
|
234
|
+
}
|
|
235
|
+
return `${name}="${htmlEscapeAttr(String(v))}"`;
|
|
236
|
+
})
|
|
237
|
+
.join(' ');
|
|
238
|
+
|
|
239
|
+
const open = attrsStr.length ? `<script ${attrsStr}>` : '<script>';
|
|
240
|
+
const children =
|
|
241
|
+
typeof managedTag.children === 'string' ? managedTag.children : '';
|
|
242
|
+
return `${open}${children}</script>`;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function routerManagedTagsToHtml(tags: unknown): string[] {
|
|
246
|
+
const normalizedTags = Array.isArray(tags) ? tags : [tags];
|
|
247
|
+
return normalizedTags.map(routerManagedTagToHtml).filter(Boolean);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
function createGetSsrHref(request: Request): string {
|
|
251
|
+
const url = new URL(request.url);
|
|
252
|
+
return `${url.pathname}${url.search}${url.hash}`;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function stripSyntheticNotFoundRoute(routes: RouteObject[]): RouteObject[] {
|
|
256
|
+
return routes
|
|
257
|
+
.filter(route => !(route.path === '*' && !route.id && !route.loader))
|
|
258
|
+
.map(route => {
|
|
259
|
+
if (!route.children?.length) {
|
|
260
|
+
return route;
|
|
261
|
+
}
|
|
262
|
+
return {
|
|
263
|
+
...route,
|
|
264
|
+
children: stripSyntheticNotFoundRoute(route.children),
|
|
265
|
+
};
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
function collectRouterErrors(
|
|
270
|
+
tanstackRouter: AnyRouter,
|
|
271
|
+
): Record<string, unknown> | undefined {
|
|
272
|
+
const state = tanstackRouter.state as { matches?: unknown };
|
|
273
|
+
const matches = Array.isArray(state.matches)
|
|
274
|
+
? (state.matches as RouterMatchWithError[])
|
|
275
|
+
: [];
|
|
276
|
+
const errors = matches.reduce((acc: Record<string, unknown>, match) => {
|
|
277
|
+
if (!match.error) {
|
|
278
|
+
return acc;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
const routeId =
|
|
282
|
+
typeof match.routeId === 'string'
|
|
283
|
+
? match.routeId
|
|
284
|
+
: typeof match.route?.id === 'string'
|
|
285
|
+
? match.route.id
|
|
286
|
+
: `match-${Object.keys(acc).length}`;
|
|
287
|
+
|
|
288
|
+
acc[routeId] = match.error;
|
|
289
|
+
return acc;
|
|
290
|
+
}, {});
|
|
291
|
+
|
|
292
|
+
return Object.keys(errors).length > 0 ? errors : undefined;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
export const tanstackRouterPlugin = (
|
|
296
|
+
userConfig: Partial<RouterConfig> = {},
|
|
297
|
+
): TanstackRouterRuntimePlugin => {
|
|
298
|
+
const plugin: TanstackRouterRuntimePlugin = {
|
|
299
|
+
name: '@modern-js/plugin-router-tanstack',
|
|
300
|
+
registryHooks: {
|
|
301
|
+
modifyRoutes: modifyRoutesHook,
|
|
302
|
+
onAfterCreateRouter: onAfterCreateRouterHook,
|
|
303
|
+
onAfterHydrateRouter: onAfterHydrateRouterHook,
|
|
304
|
+
onBeforeCreateRouter: onBeforeCreateRouterHook,
|
|
305
|
+
onBeforeCreateRoutes: onBeforeCreateRoutesHook,
|
|
306
|
+
onBeforeHydrateRouter: onBeforeHydrateRouterHook,
|
|
307
|
+
},
|
|
308
|
+
setup: (api: TanstackRouterPluginAPI) => {
|
|
309
|
+
api.onBeforeRender(async (context, interrupt) => {
|
|
310
|
+
const pluginConfig = api.getRuntimeConfig() as {
|
|
311
|
+
router?: Partial<RouterConfig>;
|
|
312
|
+
};
|
|
313
|
+
const mergedConfig = merge(
|
|
314
|
+
pluginConfig.router || {},
|
|
315
|
+
userConfig,
|
|
316
|
+
) as RouterConfig;
|
|
317
|
+
const serializationAdapters = getGlobalEnableRsc()
|
|
318
|
+
? (await import('./rsc/server')).getTanstackRscSerializationAdapters()
|
|
319
|
+
: undefined;
|
|
320
|
+
const enableRsc = getGlobalEnableRsc();
|
|
321
|
+
|
|
322
|
+
const { basename = '', routesConfig, createRoutes } = mergedConfig;
|
|
323
|
+
|
|
324
|
+
const finalRouteConfig = {
|
|
325
|
+
routes: getGlobalRoutes(),
|
|
326
|
+
globalApp: getGlobalLayoutApp(),
|
|
327
|
+
...routesConfig,
|
|
328
|
+
};
|
|
329
|
+
|
|
330
|
+
if (!finalRouteConfig.routes && !createRoutes) {
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
const hooks = api.getHooks();
|
|
335
|
+
await hooks.onBeforeCreateRoutes.call(context);
|
|
336
|
+
|
|
337
|
+
const routeObjects = createRoutes
|
|
338
|
+
? createRoutes()
|
|
339
|
+
: createRouteObjectsFromConfig({
|
|
340
|
+
routesConfig: finalRouteConfig,
|
|
341
|
+
ssrMode: context.ssrContext?.mode,
|
|
342
|
+
}) || [];
|
|
343
|
+
const normalizedRouteObjects = createRoutes
|
|
344
|
+
? routeObjects
|
|
345
|
+
: stripSyntheticNotFoundRoute(routeObjects);
|
|
346
|
+
const modifiedRouteObjects = hooks.modifyRoutes.call(
|
|
347
|
+
normalizedRouteObjects,
|
|
348
|
+
);
|
|
349
|
+
|
|
350
|
+
if (!modifiedRouteObjects.length) {
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
const {
|
|
355
|
+
request,
|
|
356
|
+
nonce,
|
|
357
|
+
baseUrl,
|
|
358
|
+
loaderFailureMode = 'errorBoundary',
|
|
359
|
+
} = context.ssrContext!;
|
|
360
|
+
|
|
361
|
+
const _basename =
|
|
362
|
+
baseUrl === '/' ? urlJoin(baseUrl, basename || '') : baseUrl;
|
|
363
|
+
|
|
364
|
+
const initialHref = createGetSsrHref(request.raw);
|
|
365
|
+
const isRSCNavigation =
|
|
366
|
+
enableRsc && request.raw.headers.get('x-rsc-tree') === 'true';
|
|
367
|
+
|
|
368
|
+
const requestContext = createRequestContext(
|
|
369
|
+
context.ssrContext?.loaderContext,
|
|
370
|
+
) as RequestContext<Record<string, unknown>>;
|
|
371
|
+
|
|
372
|
+
const controller = new AbortController();
|
|
373
|
+
const ssrRequest = new Request(request.raw.url, {
|
|
374
|
+
method: 'GET',
|
|
375
|
+
headers: request.raw.headers,
|
|
376
|
+
signal: controller.signal,
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
const routerContext: ModernTanstackRouterContext = {
|
|
380
|
+
request: ssrRequest,
|
|
381
|
+
requestContext,
|
|
382
|
+
};
|
|
383
|
+
|
|
384
|
+
const routeTree = createRouteTreeFromRouteObjects(modifiedRouteObjects);
|
|
385
|
+
const history = createMemoryHistory({
|
|
386
|
+
initialEntries: [initialHref],
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
const rewrite = createModernBasepathRewrite(_basename);
|
|
390
|
+
const routerLifecycleContext: RouterLifecycleContext = {
|
|
391
|
+
framework: 'tanstack',
|
|
392
|
+
phase: 'ssr-prepare',
|
|
393
|
+
routes: modifiedRouteObjects,
|
|
394
|
+
runtimeContext: context as TInternalRuntimeContext,
|
|
395
|
+
basename: _basename,
|
|
396
|
+
};
|
|
397
|
+
hooks.onBeforeCreateRouter.call(routerLifecycleContext);
|
|
398
|
+
|
|
399
|
+
const tanstackRouter = createRouter({
|
|
400
|
+
routeTree,
|
|
401
|
+
history,
|
|
402
|
+
basepath: '/',
|
|
403
|
+
rewrite,
|
|
404
|
+
origin: new URL(request.raw.url).origin,
|
|
405
|
+
ssr: { nonce },
|
|
406
|
+
context: routerContext as never,
|
|
407
|
+
...(serializationAdapters ? { serializationAdapters } : {}),
|
|
408
|
+
});
|
|
409
|
+
const serverRouter =
|
|
410
|
+
tanstackRouter as unknown as TanstackRouterWithServerSsr;
|
|
411
|
+
|
|
412
|
+
attachRouterServerSsrUtils({
|
|
413
|
+
router: serverRouter,
|
|
414
|
+
manifest: undefined,
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
const end = time();
|
|
418
|
+
|
|
419
|
+
try {
|
|
420
|
+
await tanstackRouter.load({ sync: true });
|
|
421
|
+
} finally {
|
|
422
|
+
const cost = end();
|
|
423
|
+
context.ssrContext?.onTiming?.(LOADER_REPORTER_NAME, cost);
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
if (serverRouter.state.redirect) {
|
|
427
|
+
const resolved = serverRouter.resolveRedirect
|
|
428
|
+
? serverRouter.resolveRedirect(serverRouter.state.redirect)
|
|
429
|
+
: serverRouter.state.redirect;
|
|
430
|
+
|
|
431
|
+
try {
|
|
432
|
+
serverRouter.serverSsr?.cleanup?.();
|
|
433
|
+
} catch {}
|
|
434
|
+
|
|
435
|
+
return interrupt(
|
|
436
|
+
isRSCNavigation
|
|
437
|
+
? handleTanstackRscRedirect(
|
|
438
|
+
resolved.headers,
|
|
439
|
+
_basename,
|
|
440
|
+
resolved.status,
|
|
441
|
+
)
|
|
442
|
+
: resolved,
|
|
443
|
+
);
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
const routerErrors = collectRouterErrors(tanstackRouter);
|
|
447
|
+
if (routerErrors && loaderFailureMode === 'clientRender') {
|
|
448
|
+
context.ssrContext?.response.status(200);
|
|
449
|
+
try {
|
|
450
|
+
serverRouter.serverSsr?.cleanup?.();
|
|
451
|
+
} catch {}
|
|
452
|
+
throw Object.values(routerErrors)[0];
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
await preloadMatchedRouteComponents(serverRouter);
|
|
456
|
+
|
|
457
|
+
context.ssrContext?.response.status(tanstackRouter.state.statusCode);
|
|
458
|
+
|
|
459
|
+
await serverRouter.serverSsr?.dehydrate?.();
|
|
460
|
+
await waitForRouterSerialization(serverRouter);
|
|
461
|
+
|
|
462
|
+
if (isRSCNavigation) {
|
|
463
|
+
setTanstackRscServerPayload(
|
|
464
|
+
createTanstackRscServerPayload(serverRouter, {
|
|
465
|
+
omitClientLoaderData: true,
|
|
466
|
+
}),
|
|
467
|
+
);
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
const ssrScriptTags = serverRouter.serverSsr?.takeBufferedScripts?.();
|
|
471
|
+
const hydrationScripts = routerManagedTagsToHtml(ssrScriptTags);
|
|
472
|
+
const matchedRouteIds = getModernRouteIdsFromMatches(serverRouter);
|
|
473
|
+
const routerServerSnapshot: InternalRouterServerSnapshot =
|
|
474
|
+
createRouterServerSnapshot({
|
|
475
|
+
framework: 'tanstack',
|
|
476
|
+
basename: _basename,
|
|
477
|
+
statusCode: tanstackRouter.state.statusCode,
|
|
478
|
+
errors: routerErrors,
|
|
479
|
+
matchedRouteIds,
|
|
480
|
+
hydrationScripts,
|
|
481
|
+
});
|
|
482
|
+
const runtimeContext = applyRouterServerPrepareResult(
|
|
483
|
+
context as TInternalRuntimeContext,
|
|
484
|
+
{
|
|
485
|
+
snapshot: routerServerSnapshot,
|
|
486
|
+
cleanup: () => serverRouter.serverSsr?.cleanup?.(),
|
|
487
|
+
state: {
|
|
488
|
+
framework: 'tanstack',
|
|
489
|
+
basename: _basename,
|
|
490
|
+
instance: serverRouter,
|
|
491
|
+
hydrationScripts,
|
|
492
|
+
matchedRouteIds,
|
|
493
|
+
serverSnapshot: routerServerSnapshot,
|
|
494
|
+
},
|
|
495
|
+
},
|
|
496
|
+
);
|
|
497
|
+
hooks.onAfterCreateRouter.call({
|
|
498
|
+
...routerLifecycleContext,
|
|
499
|
+
router: serverRouter,
|
|
500
|
+
serverSnapshot: routerServerSnapshot,
|
|
501
|
+
runtimeContext,
|
|
502
|
+
});
|
|
503
|
+
});
|
|
504
|
+
|
|
505
|
+
api.wrapRoot(App => {
|
|
506
|
+
const getRouteApp = () => {
|
|
507
|
+
return (props => {
|
|
508
|
+
const context = useContext(
|
|
509
|
+
InternalRuntimeContext,
|
|
510
|
+
) as unknown as TInternalRuntimeContext;
|
|
511
|
+
const router =
|
|
512
|
+
context.routerInstance ?? context.routerRuntime?.instance;
|
|
513
|
+
if (!router) {
|
|
514
|
+
return App ? <App {...props} /> : null;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
const routerWrapper = (
|
|
518
|
+
<Suspense fallback={null}>
|
|
519
|
+
<RouterProvider router={router as AnyRouter} />
|
|
520
|
+
</Suspense>
|
|
521
|
+
);
|
|
522
|
+
|
|
523
|
+
return App ? <App>{routerWrapper}</App> : routerWrapper;
|
|
524
|
+
}) as React.FC<Record<string, unknown>>;
|
|
525
|
+
};
|
|
526
|
+
|
|
527
|
+
return getRouteApp();
|
|
528
|
+
});
|
|
529
|
+
},
|
|
530
|
+
};
|
|
531
|
+
return plugin;
|
|
532
|
+
};
|
|
533
|
+
|
|
534
|
+
export default tanstackRouterPlugin;
|