@bleedingdev/modern-js-plugin-tanstack 3.2.0-ultramodern.120 → 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 +47 -27
- package/dist/cjs/cli/routeSplitting.js +0 -32
- package/dist/cjs/cli/tanstackTypes.js +34 -199
- package/dist/cjs/runtime/hooks.js +11 -14
- package/dist/cjs/runtime/index.js +107 -319
- package/dist/cjs/runtime/lifecycle.js +12 -86
- package/dist/cjs/runtime/loaderBridge.js +173 -0
- package/dist/cjs/runtime/plugin.js +6 -30
- package/dist/cjs/runtime/plugin.node.js +7 -29
- package/dist/cjs/runtime/pluginCore.js +55 -0
- package/dist/cjs/runtime/register.js +56 -0
- package/dist/cjs/runtime/routeTree.js +10 -207
- package/dist/cjs/runtime/{DefaultNotFound.js → router.js} +5 -15
- package/dist/cjs/runtime/rsc/payloadRouter.js +35 -1
- package/dist/cjs/runtime/state.js +45 -0
- package/dist/cjs/runtime/utils.js +0 -5
- package/dist/esm/cli/index.mjs +52 -26
- package/dist/esm/cli/routeSplitting.mjs +1 -30
- package/dist/esm/cli/tanstackTypes.mjs +32 -194
- package/dist/esm/runtime/hooks.mjs +1 -8
- 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/plugin.mjs +8 -32
- package/dist/esm/runtime/plugin.node.mjs +10 -32
- package/dist/esm/runtime/pluginCore.mjs +14 -0
- package/dist/esm/runtime/register.mjs +18 -0
- package/dist/esm/runtime/routeTree.mjs +4 -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 -5
- package/dist/esm-node/cli/index.mjs +52 -26
- package/dist/esm-node/cli/routeSplitting.mjs +1 -30
- package/dist/esm-node/cli/tanstackTypes.mjs +32 -194
- package/dist/esm-node/runtime/hooks.mjs +1 -8
- 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/plugin.mjs +8 -32
- package/dist/esm-node/runtime/plugin.node.mjs +10 -32
- package/dist/esm-node/runtime/pluginCore.mjs +15 -0
- package/dist/esm-node/runtime/register.mjs +19 -0
- package/dist/esm-node/runtime/routeTree.mjs +4 -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 -5
- package/dist/types/cli/index.d.ts +9 -2
- package/dist/types/cli/routeSplitting.d.ts +6 -15
- package/dist/types/cli/tanstackTypes.d.ts +13 -2
- package/dist/types/runtime/hooks.d.ts +8 -18
- package/dist/types/runtime/index.d.ts +6 -4
- package/dist/types/runtime/lifecycle.d.ts +7 -22
- package/dist/types/runtime/loaderBridge.d.ts +48 -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 +30 -28
- package/rstest.config.mts +6 -0
- package/src/cli/index.ts +111 -29
- package/src/cli/routeSplitting.ts +6 -44
- package/src/cli/tanstackTypes.ts +78 -214
- package/src/runtime/hooks.ts +10 -27
- package/src/runtime/index.tsx +12 -107
- package/src/runtime/lifecycle.ts +16 -151
- package/src/runtime/loaderBridge.ts +257 -0
- package/src/runtime/plugin.node.tsx +14 -77
- package/src/runtime/plugin.tsx +12 -72
- package/src/runtime/pluginCore.ts +48 -0
- package/src/runtime/register.ts +58 -0
- package/src/runtime/routeTree.ts +8 -370
- package/src/runtime/router.ts +15 -0
- package/src/runtime/rsc/payloadRouter.ts +45 -2
- package/src/runtime/state.ts +29 -0
- package/src/runtime/types.ts +20 -67
- package/src/runtime/utils.tsx +3 -6
- package/tests/router/cli.test.ts +297 -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/register.test.ts +46 -0
- package/tests/router/routeTree.test.ts +65 -180
- package/tests/router/rsc.test.tsx +70 -0
- package/tests/router/tanstackTypes.test.ts +164 -6
- 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/lifecycle.ts
CHANGED
|
@@ -1,151 +1,16 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
routes: RouteObject[];
|
|
18
|
-
runtimeContext: TInternalRuntimeContext;
|
|
19
|
-
basename?: string;
|
|
20
|
-
hydrationData?: unknown;
|
|
21
|
-
router?: unknown;
|
|
22
|
-
matches?: RouterRouteMatchSnapshot[];
|
|
23
|
-
cleanup?: () => void | Promise<void>;
|
|
24
|
-
serverSnapshot?: InternalRouterServerSnapshot;
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
type RouterSnapshotLike = Partial<InternalRouterServerSnapshot>;
|
|
28
|
-
|
|
29
|
-
function toHydrationScripts(state: {
|
|
30
|
-
hydrationScript?: string;
|
|
31
|
-
hydrationScripts?: string[];
|
|
32
|
-
}) {
|
|
33
|
-
if (state.hydrationScripts?.length) {
|
|
34
|
-
return state.hydrationScripts;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
return state.hydrationScript ? [state.hydrationScript] : undefined;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function getMatchedRouteIdsFromMatches(matches?: RouterRouteMatchSnapshot[]) {
|
|
41
|
-
const routeIds = matches
|
|
42
|
-
?.map(match => match.assetRouteId ?? match.routeId)
|
|
43
|
-
.filter((routeId): routeId is string => typeof routeId === 'string');
|
|
44
|
-
|
|
45
|
-
return routeIds?.length ? routeIds : undefined;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export function createRouterServerSnapshot(
|
|
49
|
-
state: RouterSnapshotLike,
|
|
50
|
-
): InternalRouterServerSnapshot {
|
|
51
|
-
const hydrationScripts = toHydrationScripts(state);
|
|
52
|
-
const matchedRouteIds =
|
|
53
|
-
state.matchedRouteIds ?? getMatchedRouteIdsFromMatches(state.matches);
|
|
54
|
-
|
|
55
|
-
return {
|
|
56
|
-
...state,
|
|
57
|
-
...(hydrationScripts?.length
|
|
58
|
-
? {
|
|
59
|
-
hydrationScript: state.hydrationScript ?? hydrationScripts[0],
|
|
60
|
-
hydrationScripts,
|
|
61
|
-
}
|
|
62
|
-
: {}),
|
|
63
|
-
...(matchedRouteIds ? { matchedRouteIds } : {}),
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
export function createRouterRuntimeState(
|
|
68
|
-
state: InternalRouterRuntimeState,
|
|
69
|
-
): InternalRouterRuntimeState {
|
|
70
|
-
const hasSnapshotState =
|
|
71
|
-
Boolean(state.serverSnapshot) ||
|
|
72
|
-
Boolean(state.hydrationScript) ||
|
|
73
|
-
Boolean(state.hydrationScripts?.length) ||
|
|
74
|
-
Boolean(state.matchedRouteIds?.length) ||
|
|
75
|
-
Boolean(state.matches?.length);
|
|
76
|
-
const serverSnapshot = state.serverSnapshot
|
|
77
|
-
? createRouterServerSnapshot({
|
|
78
|
-
...state.serverSnapshot,
|
|
79
|
-
framework: state.serverSnapshot.framework ?? state.framework,
|
|
80
|
-
basename: state.serverSnapshot.basename ?? state.basename,
|
|
81
|
-
hydrationScript:
|
|
82
|
-
state.serverSnapshot.hydrationScript ?? state.hydrationScript,
|
|
83
|
-
hydrationScripts:
|
|
84
|
-
state.serverSnapshot.hydrationScripts ?? state.hydrationScripts,
|
|
85
|
-
matchedRouteIds:
|
|
86
|
-
state.serverSnapshot.matchedRouteIds ?? state.matchedRouteIds,
|
|
87
|
-
matches: state.serverSnapshot.matches ?? state.matches,
|
|
88
|
-
})
|
|
89
|
-
: hasSnapshotState
|
|
90
|
-
? createRouterServerSnapshot({
|
|
91
|
-
framework: state.framework,
|
|
92
|
-
basename: state.basename,
|
|
93
|
-
hydrationScript: state.hydrationScript,
|
|
94
|
-
hydrationScripts: state.hydrationScripts,
|
|
95
|
-
matchedRouteIds: state.matchedRouteIds,
|
|
96
|
-
matches: state.matches,
|
|
97
|
-
})
|
|
98
|
-
: undefined;
|
|
99
|
-
const hydrationScripts = toHydrationScripts({
|
|
100
|
-
hydrationScript: state.hydrationScript ?? serverSnapshot?.hydrationScript,
|
|
101
|
-
hydrationScripts:
|
|
102
|
-
state.hydrationScripts ?? serverSnapshot?.hydrationScripts,
|
|
103
|
-
});
|
|
104
|
-
const matchedRouteIds =
|
|
105
|
-
state.matchedRouteIds ??
|
|
106
|
-
serverSnapshot?.matchedRouteIds ??
|
|
107
|
-
getMatchedRouteIdsFromMatches(state.matches);
|
|
108
|
-
|
|
109
|
-
return {
|
|
110
|
-
...state,
|
|
111
|
-
...(hydrationScripts?.length
|
|
112
|
-
? {
|
|
113
|
-
hydrationScript: state.hydrationScript ?? hydrationScripts[0],
|
|
114
|
-
hydrationScripts,
|
|
115
|
-
}
|
|
116
|
-
: {}),
|
|
117
|
-
...(matchedRouteIds ? { matchedRouteIds } : {}),
|
|
118
|
-
...(serverSnapshot ? { serverSnapshot } : {}),
|
|
119
|
-
};
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
export function applyRouterRuntimeState(
|
|
123
|
-
runtimeContext: TInternalRuntimeContext,
|
|
124
|
-
state: InternalRouterRuntimeState,
|
|
125
|
-
) {
|
|
126
|
-
const normalized = createRouterRuntimeState(state);
|
|
127
|
-
const mutableRuntimeContext = runtimeContext as any;
|
|
128
|
-
mutableRuntimeContext.routerFramework = normalized.framework;
|
|
129
|
-
mutableRuntimeContext.routerInstance = normalized.instance;
|
|
130
|
-
mutableRuntimeContext.routerHydrationScript = normalized.hydrationScript;
|
|
131
|
-
mutableRuntimeContext.routerMatchedRouteIds = normalized.matchedRouteIds;
|
|
132
|
-
mutableRuntimeContext.routerRuntime = normalized;
|
|
133
|
-
if (normalized.serverSnapshot) {
|
|
134
|
-
mutableRuntimeContext.routerServerSnapshot = normalized.serverSnapshot;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
return runtimeContext;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
export function applyRouterServerPrepareResult(
|
|
141
|
-
runtimeContext: TInternalRuntimeContext,
|
|
142
|
-
result: RouterServerPrepareResult,
|
|
143
|
-
) {
|
|
144
|
-
const state = createRouterRuntimeState({
|
|
145
|
-
...result.state,
|
|
146
|
-
cleanup: result.cleanup ?? result.state.cleanup,
|
|
147
|
-
serverSnapshot: result.snapshot ?? result.state.serverSnapshot,
|
|
148
|
-
});
|
|
149
|
-
applyRouterRuntimeState(runtimeContext, state);
|
|
150
|
-
return runtimeContext;
|
|
151
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* The router runtime state helpers are owned by @modern-js/runtime (the same
|
|
3
|
+
* implementation backs the built-in react-router provider and the SSR
|
|
4
|
+
* pipeline). This module only re-exports them so every router provider
|
|
5
|
+
* writes to the exact same runtime-context extension slot.
|
|
6
|
+
*/
|
|
7
|
+
export {
|
|
8
|
+
applyRouterRuntimeState,
|
|
9
|
+
applyRouterServerPrepareResult,
|
|
10
|
+
createRouterRuntimeState,
|
|
11
|
+
createRouterServerSnapshot,
|
|
12
|
+
getRouterRuntimeState,
|
|
13
|
+
getRouterServerSnapshot,
|
|
14
|
+
type RouterLifecycleContext,
|
|
15
|
+
type RouterLifecyclePhase,
|
|
16
|
+
} from '@modern-js/runtime/context';
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
// @effect-diagnostics strictBooleanExpressions:off
|
|
2
|
+
/**
|
|
3
|
+
* Runtime bridge between Modern.js data loaders and TanStack Router.
|
|
4
|
+
*
|
|
5
|
+
* The generated `src/modern-tanstack/<entry>/router.gen.ts` files import these
|
|
6
|
+
* helpers instead of inlining them, so loader/redirect bugfixes ship with the
|
|
7
|
+
* package instead of requiring every app to regenerate its files. The
|
|
8
|
+
* hand-written route-tree builder (`routeTree.ts`) shares the response/redirect
|
|
9
|
+
* helpers for the same reason.
|
|
10
|
+
*/
|
|
11
|
+
import { notFound, redirect } from '@tanstack/react-router';
|
|
12
|
+
|
|
13
|
+
/** Router context shape used by the generated TanStack router types. */
|
|
14
|
+
export type ModernRouterContext = {
|
|
15
|
+
request?: Request;
|
|
16
|
+
requestContext?: unknown;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export function isResponse(value: unknown): value is Response {
|
|
20
|
+
const record = value as { headers?: unknown; status?: unknown } | null;
|
|
21
|
+
return (
|
|
22
|
+
record != null &&
|
|
23
|
+
typeof record === 'object' &&
|
|
24
|
+
typeof record.status === 'number' &&
|
|
25
|
+
typeof record.headers === 'object'
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const redirectStatusCodes = new Set([301, 302, 303, 307, 308]);
|
|
30
|
+
|
|
31
|
+
export function isRedirectResponse(res: Response): boolean {
|
|
32
|
+
return redirectStatusCodes.has(res.status);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* TanStack redirects are Response objects carrying the original redirect
|
|
37
|
+
* `options`. They must be re-thrown untouched — re-translating them through
|
|
38
|
+
* the Modern Response handling would lose `to`-based (internal) targets.
|
|
39
|
+
*/
|
|
40
|
+
export function isTanstackRedirect(value: unknown): boolean {
|
|
41
|
+
return (
|
|
42
|
+
isResponse(value) &&
|
|
43
|
+
typeof (value as { options?: unknown }).options === 'object'
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function isAbsoluteUrl(value: string): boolean {
|
|
48
|
+
try {
|
|
49
|
+
void new URL(value);
|
|
50
|
+
return true;
|
|
51
|
+
} catch {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Re-throw a Modern.js `Location` redirect as a TanStack redirect.
|
|
58
|
+
*
|
|
59
|
+
* Prefers `to` for internal/relative redirects so the basepath rewrite can be
|
|
60
|
+
* applied; absolute (external) URLs go through `href` untouched.
|
|
61
|
+
*/
|
|
62
|
+
export function throwTanstackRedirect(location: string): never {
|
|
63
|
+
const target = location || '/';
|
|
64
|
+
if (isAbsoluteUrl(target)) {
|
|
65
|
+
throw redirect({ href: target });
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
throw redirect({ to: target });
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* React Router uses `*` for splat params, TanStack Router uses `_splat`.
|
|
73
|
+
* Modern loaders expect the React Router spelling.
|
|
74
|
+
*/
|
|
75
|
+
export function mapSplatParamsForModernLoader(
|
|
76
|
+
params: Record<string, string>,
|
|
77
|
+
hasSplat: boolean,
|
|
78
|
+
): Record<string, string> {
|
|
79
|
+
if (!hasSplat) {
|
|
80
|
+
return params;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const { _splat, ...rest } = params as Record<string, string> & {
|
|
84
|
+
_splat?: string;
|
|
85
|
+
};
|
|
86
|
+
if (typeof _splat !== 'undefined') {
|
|
87
|
+
return { ...rest, '*': _splat };
|
|
88
|
+
}
|
|
89
|
+
return rest;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Static-data factory used by the generated router files: drops empty fields
|
|
94
|
+
* so route static data stays minimal.
|
|
95
|
+
*/
|
|
96
|
+
export function createRouteStaticData(opts: {
|
|
97
|
+
modernRouteId?: string;
|
|
98
|
+
modernRouteAction?: unknown;
|
|
99
|
+
modernRouteLoader?: unknown;
|
|
100
|
+
}): {
|
|
101
|
+
modernRouteId?: string;
|
|
102
|
+
modernRouteAction?: unknown;
|
|
103
|
+
modernRouteLoader?: unknown;
|
|
104
|
+
} {
|
|
105
|
+
const staticData: {
|
|
106
|
+
modernRouteId?: string;
|
|
107
|
+
modernRouteAction?: unknown;
|
|
108
|
+
modernRouteLoader?: unknown;
|
|
109
|
+
} = {};
|
|
110
|
+
|
|
111
|
+
if (typeof opts.modernRouteId === 'string' && opts.modernRouteId.length > 0) {
|
|
112
|
+
staticData.modernRouteId = opts.modernRouteId;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (typeof opts.modernRouteLoader !== 'undefined') {
|
|
116
|
+
staticData.modernRouteLoader = opts.modernRouteLoader;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (typeof opts.modernRouteAction !== 'undefined') {
|
|
120
|
+
staticData.modernRouteAction = opts.modernRouteAction;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return staticData;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
type LoaderLikeContext = {
|
|
127
|
+
abortController?: AbortController;
|
|
128
|
+
signal?: AbortSignal;
|
|
129
|
+
context?: ModernRouterContext;
|
|
130
|
+
location?:
|
|
131
|
+
| string
|
|
132
|
+
| {
|
|
133
|
+
publicHref?: string;
|
|
134
|
+
href?: string;
|
|
135
|
+
url?: { href?: string };
|
|
136
|
+
};
|
|
137
|
+
params?: Record<string, string>;
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
function getLoaderSignal(ctx: LoaderLikeContext | undefined): AbortSignal {
|
|
141
|
+
const abortSignal = ctx?.abortController?.signal;
|
|
142
|
+
if (abortSignal instanceof AbortSignal) {
|
|
143
|
+
return abortSignal;
|
|
144
|
+
}
|
|
145
|
+
if (ctx?.signal instanceof AbortSignal) {
|
|
146
|
+
return ctx.signal;
|
|
147
|
+
}
|
|
148
|
+
return new AbortController().signal;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function getLoaderHref(ctx: LoaderLikeContext | undefined): string {
|
|
152
|
+
if (typeof ctx?.location === 'string') {
|
|
153
|
+
return ctx.location;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const publicHref = ctx?.location?.publicHref;
|
|
157
|
+
if (typeof publicHref === 'string') {
|
|
158
|
+
return publicHref;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const href = ctx?.location?.href;
|
|
162
|
+
if (typeof href === 'string') {
|
|
163
|
+
return href;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const urlHref = ctx?.location?.url?.href;
|
|
167
|
+
return typeof urlHref === 'string' ? urlHref : '';
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function getLoaderParams(
|
|
171
|
+
ctx: LoaderLikeContext | undefined,
|
|
172
|
+
): Record<string, string> {
|
|
173
|
+
return typeof ctx?.params === 'object' && ctx.params !== null
|
|
174
|
+
? ctx.params
|
|
175
|
+
: {};
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function handleModernLoaderResult<LoaderResult>(
|
|
179
|
+
result: LoaderResult,
|
|
180
|
+
): LoaderResult {
|
|
181
|
+
if (isResponse(result)) {
|
|
182
|
+
if (isRedirectResponse(result)) {
|
|
183
|
+
const location = result.headers.get('Location') ?? '/';
|
|
184
|
+
throwTanstackRedirect(location);
|
|
185
|
+
}
|
|
186
|
+
if (result.status === 404) {
|
|
187
|
+
throw notFound();
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
return result;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function handleModernLoaderError(err: unknown): never {
|
|
195
|
+
if (isResponse(err)) {
|
|
196
|
+
if (isTanstackRedirect(err)) {
|
|
197
|
+
throw err;
|
|
198
|
+
}
|
|
199
|
+
if (isRedirectResponse(err)) {
|
|
200
|
+
const location = err.headers.get('Location') ?? '/';
|
|
201
|
+
throwTanstackRedirect(location);
|
|
202
|
+
}
|
|
203
|
+
if (err.status === 404) {
|
|
204
|
+
throw notFound();
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
throw err;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Wrap a Modern.js data loader (`page.data.ts` loader/action style) into a
|
|
213
|
+
* TanStack Router loader: builds a `Request` from the loader context, maps
|
|
214
|
+
* splat params, and translates Response redirects/404s into TanStack
|
|
215
|
+
* `redirect()`/`notFound()`.
|
|
216
|
+
*/
|
|
217
|
+
export function modernLoaderToTanstack<TLoader extends (args: any) => any>(
|
|
218
|
+
opts: { hasSplat: boolean },
|
|
219
|
+
modernLoader: TLoader,
|
|
220
|
+
): (ctx: unknown) => Promise<Awaited<ReturnType<TLoader>>> {
|
|
221
|
+
type LoaderResult = Awaited<ReturnType<TLoader>>;
|
|
222
|
+
|
|
223
|
+
return (rawCtx: unknown): Promise<LoaderResult> => {
|
|
224
|
+
const ctx = rawCtx as LoaderLikeContext | undefined;
|
|
225
|
+
try {
|
|
226
|
+
const signal = getLoaderSignal(ctx);
|
|
227
|
+
const baseRequest: Request | undefined =
|
|
228
|
+
ctx?.context?.request instanceof Request
|
|
229
|
+
? ctx.context.request
|
|
230
|
+
: undefined;
|
|
231
|
+
|
|
232
|
+
const href = getLoaderHref(ctx);
|
|
233
|
+
|
|
234
|
+
const request =
|
|
235
|
+
baseRequest !== undefined
|
|
236
|
+
? new Request(baseRequest, { signal })
|
|
237
|
+
: new Request(href, { signal });
|
|
238
|
+
|
|
239
|
+
const params = mapSplatParamsForModernLoader(
|
|
240
|
+
getLoaderParams(ctx),
|
|
241
|
+
opts.hasSplat,
|
|
242
|
+
);
|
|
243
|
+
|
|
244
|
+
return Promise.resolve(
|
|
245
|
+
(modernLoader as (args: unknown) => unknown)({
|
|
246
|
+
request,
|
|
247
|
+
params,
|
|
248
|
+
context: ctx?.context?.requestContext,
|
|
249
|
+
}) as LoaderResult,
|
|
250
|
+
)
|
|
251
|
+
.then((result: LoaderResult) => handleModernLoaderResult(result))
|
|
252
|
+
.catch(handleModernLoaderError);
|
|
253
|
+
} catch (err) {
|
|
254
|
+
handleModernLoaderError(err);
|
|
255
|
+
}
|
|
256
|
+
};
|
|
257
|
+
}
|
|
@@ -1,23 +1,17 @@
|
|
|
1
1
|
// @effect-diagnostics asyncFunction:off newPromise:off strictBooleanExpressions:off unnecessaryArrowBlock: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 ServerPayload,
|
|
12
8
|
type TInternalRuntimeContext,
|
|
13
9
|
} from '@modern-js/runtime/context';
|
|
14
|
-
import { merge } from '@modern-js/runtime-utils/merge';
|
|
15
10
|
import {
|
|
16
11
|
createRequestContext,
|
|
17
12
|
type RequestContext,
|
|
18
13
|
storage,
|
|
19
14
|
} from '@modern-js/runtime-utils/node';
|
|
20
|
-
import type { RouteObject } from '@modern-js/runtime-utils/router';
|
|
21
15
|
import { time } from '@modern-js/runtime-utils/time';
|
|
22
16
|
import { LOADER_REPORTER_NAME } from '@modern-js/utils/universal/constants';
|
|
23
17
|
import {
|
|
@@ -30,21 +24,20 @@ import { attachRouterServerSsrUtils } from '@tanstack/router-core/ssr/server';
|
|
|
30
24
|
import type React from 'react';
|
|
31
25
|
import { useContext } from 'react';
|
|
32
26
|
import { createModernBasepathRewrite } from './basepathRewrite';
|
|
33
|
-
import {
|
|
34
|
-
modifyRoutes as modifyRoutesHook,
|
|
35
|
-
onAfterCreateRouter as onAfterCreateRouterHook,
|
|
36
|
-
onAfterHydrateRouter as onAfterHydrateRouterHook,
|
|
37
|
-
onBeforeCreateRouter as onBeforeCreateRouterHook,
|
|
38
|
-
onBeforeCreateRoutes as onBeforeCreateRoutesHook,
|
|
39
|
-
onBeforeHydrateRouter as onBeforeHydrateRouterHook,
|
|
40
|
-
type RouterExtendsHooks,
|
|
41
|
-
} from './hooks';
|
|
27
|
+
import { routerProviderRegistryHooks } from './hooks';
|
|
42
28
|
import { wrapTanstackSsrHydrationBoundary } from './hydrationBoundary';
|
|
43
29
|
import {
|
|
44
30
|
applyRouterServerPrepareResult,
|
|
45
31
|
createRouterServerSnapshot,
|
|
32
|
+
getRouterRuntimeState,
|
|
46
33
|
type RouterLifecycleContext,
|
|
47
34
|
} from './lifecycle';
|
|
35
|
+
import {
|
|
36
|
+
getFinalRouteConfig,
|
|
37
|
+
getMergedRouterConfig,
|
|
38
|
+
type TanstackRouterPluginAPI,
|
|
39
|
+
type TanstackRouterRuntimePlugin,
|
|
40
|
+
} from './pluginCore';
|
|
48
41
|
import {
|
|
49
42
|
createRouteTreeFromRouteObjects,
|
|
50
43
|
getModernRouteIdsFromMatches,
|
|
@@ -65,25 +58,6 @@ type ModernTanstackRouterContext = {
|
|
|
65
58
|
requestContext: RequestContext<Record<string, unknown>>;
|
|
66
59
|
};
|
|
67
60
|
|
|
68
|
-
type TanstackRouterRuntimeConfig = {
|
|
69
|
-
plugins?: TanstackRouterRuntimePlugin[];
|
|
70
|
-
router?: Partial<RouterConfig>;
|
|
71
|
-
[key: string]: unknown;
|
|
72
|
-
};
|
|
73
|
-
|
|
74
|
-
type TanstackRouterRuntimeExtends = Required<
|
|
75
|
-
RuntimePluginExtends<TanstackRouterRuntimeConfig, TInternalRuntimeContext>
|
|
76
|
-
> & {
|
|
77
|
-
extendHooks: RouterExtendsHooks;
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
type TanstackRouterPluginAPI = RuntimePluginAPI<TanstackRouterRuntimeExtends>;
|
|
81
|
-
|
|
82
|
-
type TanstackRouterRuntimePlugin = Plugin<
|
|
83
|
-
TanstackRouterPluginAPI,
|
|
84
|
-
TInternalRuntimeContext
|
|
85
|
-
>;
|
|
86
|
-
|
|
87
61
|
const setTanstackRscServerPayload = (payload: ServerPayload) => {
|
|
88
62
|
const storageContext = storage.useContext?.() as
|
|
89
63
|
| { serverPayload?: ServerPayload }
|
|
@@ -300,20 +274,6 @@ function createGetSsrHref(request: Request): string {
|
|
|
300
274
|
return `${url.pathname}${url.search}${url.hash}`;
|
|
301
275
|
}
|
|
302
276
|
|
|
303
|
-
function stripSyntheticNotFoundRoute(routes: RouteObject[]): RouteObject[] {
|
|
304
|
-
return routes
|
|
305
|
-
.filter(route => !(route.path === '*' && !route.id && !route.loader))
|
|
306
|
-
.map(route => {
|
|
307
|
-
if (!route.children?.length) {
|
|
308
|
-
return route;
|
|
309
|
-
}
|
|
310
|
-
return {
|
|
311
|
-
...route,
|
|
312
|
-
children: stripSyntheticNotFoundRoute(route.children),
|
|
313
|
-
};
|
|
314
|
-
});
|
|
315
|
-
}
|
|
316
|
-
|
|
317
277
|
function collectRouterErrors(
|
|
318
278
|
tanstackRouter: AnyRouter,
|
|
319
279
|
): Record<string, unknown> | undefined {
|
|
@@ -345,35 +305,18 @@ export const tanstackRouterPlugin = (
|
|
|
345
305
|
): TanstackRouterRuntimePlugin => {
|
|
346
306
|
const plugin: TanstackRouterRuntimePlugin = {
|
|
347
307
|
name: '@modern-js/plugin-router-tanstack',
|
|
348
|
-
registryHooks:
|
|
349
|
-
modifyRoutes: modifyRoutesHook,
|
|
350
|
-
onAfterCreateRouter: onAfterCreateRouterHook,
|
|
351
|
-
onAfterHydrateRouter: onAfterHydrateRouterHook,
|
|
352
|
-
onBeforeCreateRouter: onBeforeCreateRouterHook,
|
|
353
|
-
onBeforeCreateRoutes: onBeforeCreateRoutesHook,
|
|
354
|
-
onBeforeHydrateRouter: onBeforeHydrateRouterHook,
|
|
355
|
-
},
|
|
308
|
+
registryHooks: routerProviderRegistryHooks,
|
|
356
309
|
setup: (api: TanstackRouterPluginAPI) => {
|
|
357
310
|
api.onBeforeRender(async (context, interrupt) => {
|
|
358
|
-
const
|
|
359
|
-
router?: Partial<RouterConfig>;
|
|
360
|
-
};
|
|
361
|
-
const mergedConfig = merge(
|
|
362
|
-
pluginConfig.router || {},
|
|
363
|
-
userConfig,
|
|
364
|
-
) as RouterConfig;
|
|
311
|
+
const mergedConfig = getMergedRouterConfig(api, userConfig);
|
|
365
312
|
const serializationAdapters = getGlobalEnableRsc()
|
|
366
313
|
? (await import('./rsc/server')).getTanstackRscSerializationAdapters()
|
|
367
314
|
: undefined;
|
|
368
315
|
const enableRsc = getGlobalEnableRsc();
|
|
369
316
|
|
|
370
|
-
const { basename = '',
|
|
317
|
+
const { basename = '', createRoutes } = mergedConfig;
|
|
371
318
|
|
|
372
|
-
const finalRouteConfig =
|
|
373
|
-
routes: getGlobalRoutes(),
|
|
374
|
-
globalApp: getGlobalLayoutApp(),
|
|
375
|
-
...routesConfig,
|
|
376
|
-
};
|
|
319
|
+
const finalRouteConfig = getFinalRouteConfig(mergedConfig);
|
|
377
320
|
|
|
378
321
|
if (!finalRouteConfig.routes && !createRoutes) {
|
|
379
322
|
return;
|
|
@@ -388,12 +331,7 @@ export const tanstackRouterPlugin = (
|
|
|
388
331
|
routesConfig: finalRouteConfig,
|
|
389
332
|
ssrMode: context.ssrContext?.mode,
|
|
390
333
|
}) || [];
|
|
391
|
-
const
|
|
392
|
-
? routeObjects
|
|
393
|
-
: stripSyntheticNotFoundRoute(routeObjects);
|
|
394
|
-
const modifiedRouteObjects = hooks.modifyRoutes.call(
|
|
395
|
-
normalizedRouteObjects,
|
|
396
|
-
);
|
|
334
|
+
const modifiedRouteObjects = hooks.modifyRoutes.call(routeObjects);
|
|
397
335
|
|
|
398
336
|
if (!modifiedRouteObjects.length) {
|
|
399
337
|
return;
|
|
@@ -560,8 +498,7 @@ export const tanstackRouterPlugin = (
|
|
|
560
498
|
const context = useContext(
|
|
561
499
|
InternalRuntimeContext,
|
|
562
500
|
) as unknown as TInternalRuntimeContext;
|
|
563
|
-
const router =
|
|
564
|
-
context.routerInstance ?? context.routerRuntime?.instance;
|
|
501
|
+
const router = getRouterRuntimeState(context)?.instance;
|
|
565
502
|
if (!router) {
|
|
566
503
|
return App ? <App {...props} /> : null;
|
|
567
504
|
}
|