@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.
Files changed (136) hide show
  1. package/dist/cjs/cli/index.js +89 -31
  2. package/dist/cjs/cli/routeSplitting.js +55 -0
  3. package/dist/cjs/cli/tanstackTypes.js +172 -170
  4. package/dist/cjs/cli.js +12 -8
  5. package/dist/cjs/runtime/basepathRewrite.js +12 -8
  6. package/dist/cjs/runtime/dataMutation.js +9 -5
  7. package/dist/cjs/runtime/hooks.js +20 -19
  8. package/dist/cjs/runtime/hydrationBoundary.js +48 -0
  9. package/dist/cjs/runtime/index.js +79 -35
  10. package/dist/cjs/runtime/lifecycle.js +21 -91
  11. package/dist/cjs/runtime/loaderBridge.js +173 -0
  12. package/dist/cjs/runtime/outlet.js +58 -0
  13. package/dist/cjs/runtime/plugin.js +195 -114
  14. package/dist/cjs/runtime/plugin.node.js +45 -45
  15. package/dist/cjs/runtime/plugin.worker.js +53 -0
  16. package/dist/cjs/runtime/pluginCore.js +55 -0
  17. package/dist/cjs/runtime/prefetchLink.js +10 -6
  18. package/dist/cjs/runtime/register.js +56 -0
  19. package/dist/cjs/runtime/routeTree.js +74 -207
  20. package/dist/cjs/runtime/router.js +41 -0
  21. package/dist/cjs/runtime/rsc/ClientSlot.js +9 -5
  22. package/dist/cjs/runtime/rsc/CompositeComponent.js +9 -5
  23. package/dist/cjs/runtime/rsc/ReplayableStream.js +14 -9
  24. package/dist/cjs/runtime/rsc/RscNodeRenderer.js +9 -5
  25. package/dist/cjs/runtime/rsc/SlotContext.js +9 -5
  26. package/dist/cjs/runtime/rsc/client.js +9 -5
  27. package/dist/cjs/runtime/rsc/createRscProxy.js +9 -5
  28. package/dist/cjs/runtime/rsc/index.js +9 -5
  29. package/dist/cjs/runtime/rsc/payloadRouter.js +44 -6
  30. package/dist/cjs/runtime/rsc/server.js +9 -5
  31. package/dist/cjs/runtime/rsc/slotUsageSanitizer.js +9 -5
  32. package/dist/cjs/runtime/rsc/symbols.js +20 -15
  33. package/dist/cjs/runtime/state.js +45 -0
  34. package/dist/cjs/runtime/types.js +31 -1
  35. package/dist/cjs/runtime/utils.js +9 -10
  36. package/dist/cjs/runtime.js +9 -5
  37. package/dist/esm/cli/index.mjs +75 -27
  38. package/dist/esm/cli/routeSplitting.mjs +14 -0
  39. package/dist/esm/cli/tanstackTypes.mjs +158 -160
  40. package/dist/esm/runtime/hooks.mjs +1 -8
  41. package/dist/esm/runtime/hydrationBoundary.mjs +10 -0
  42. package/dist/esm/runtime/index.mjs +5 -2
  43. package/dist/esm/runtime/lifecycle.mjs +1 -82
  44. package/dist/esm/runtime/loaderBridge.mjs +114 -0
  45. package/dist/esm/runtime/outlet.mjs +17 -0
  46. package/dist/esm/runtime/plugin.mjs +191 -114
  47. package/dist/esm/runtime/plugin.node.mjs +40 -44
  48. package/dist/esm/runtime/plugin.worker.mjs +1 -0
  49. package/dist/esm/runtime/pluginCore.mjs +14 -0
  50. package/dist/esm/runtime/prefetchLink.mjs +1 -1
  51. package/dist/esm/runtime/register.mjs +18 -0
  52. package/dist/esm/runtime/routeTree.mjs +59 -193
  53. package/dist/esm/runtime/router.mjs +2 -0
  54. package/dist/esm/runtime/rsc/payloadRouter.mjs +35 -1
  55. package/dist/esm/runtime/state.mjs +7 -0
  56. package/dist/esm/runtime/types.mjs +7 -0
  57. package/dist/esm/runtime/utils.mjs +0 -5
  58. package/dist/esm-node/cli/index.mjs +75 -27
  59. package/dist/esm-node/cli/routeSplitting.mjs +15 -0
  60. package/dist/esm-node/cli/tanstackTypes.mjs +158 -160
  61. package/dist/esm-node/runtime/hooks.mjs +1 -8
  62. package/dist/esm-node/runtime/hydrationBoundary.mjs +11 -0
  63. package/dist/esm-node/runtime/index.mjs +5 -2
  64. package/dist/esm-node/runtime/lifecycle.mjs +1 -82
  65. package/dist/esm-node/runtime/loaderBridge.mjs +115 -0
  66. package/dist/esm-node/runtime/outlet.mjs +18 -0
  67. package/dist/esm-node/runtime/plugin.mjs +191 -114
  68. package/dist/esm-node/runtime/plugin.node.mjs +40 -44
  69. package/dist/esm-node/runtime/plugin.worker.mjs +2 -0
  70. package/dist/esm-node/runtime/pluginCore.mjs +15 -0
  71. package/dist/esm-node/runtime/prefetchLink.mjs +1 -1
  72. package/dist/esm-node/runtime/register.mjs +19 -0
  73. package/dist/esm-node/runtime/routeTree.mjs +59 -193
  74. package/dist/esm-node/runtime/router.mjs +3 -0
  75. package/dist/esm-node/runtime/rsc/payloadRouter.mjs +35 -1
  76. package/dist/esm-node/runtime/state.mjs +8 -0
  77. package/dist/esm-node/runtime/types.mjs +7 -0
  78. package/dist/esm-node/runtime/utils.mjs +0 -5
  79. package/dist/types/cli/index.d.ts +14 -1
  80. package/dist/types/cli/routeSplitting.d.ts +20 -0
  81. package/dist/types/cli/tanstackTypes.d.ts +21 -1
  82. package/dist/types/runtime/hooks.d.ts +8 -33
  83. package/dist/types/runtime/hydrationBoundary.d.ts +2 -0
  84. package/dist/types/runtime/index.d.ts +8 -3
  85. package/dist/types/runtime/lifecycle.d.ts +7 -22
  86. package/dist/types/runtime/loaderBridge.d.ts +48 -0
  87. package/dist/types/runtime/outlet.d.ts +2 -0
  88. package/dist/types/runtime/plugin.d.ts +2 -15
  89. package/dist/types/runtime/plugin.node.d.ts +2 -15
  90. package/dist/types/runtime/plugin.worker.d.ts +1 -0
  91. package/dist/types/runtime/pluginCore.d.ts +21 -0
  92. package/dist/types/runtime/register.d.ts +9 -0
  93. package/dist/types/runtime/routeTree.d.ts +0 -2
  94. package/dist/types/runtime/router.d.ts +14 -0
  95. package/dist/types/runtime/state.d.ts +16 -0
  96. package/dist/types/runtime/types.d.ts +14 -53
  97. package/package.json +42 -40
  98. package/rstest.config.mts +6 -0
  99. package/src/cli/index.ts +162 -23
  100. package/src/cli/routeSplitting.ts +43 -0
  101. package/src/cli/tanstackTypes.ts +331 -187
  102. package/src/runtime/hooks.ts +10 -27
  103. package/src/runtime/hydrationBoundary.tsx +12 -0
  104. package/src/runtime/index.tsx +17 -7
  105. package/src/runtime/lifecycle.ts +16 -151
  106. package/src/runtime/loaderBridge.ts +257 -0
  107. package/src/runtime/outlet.tsx +48 -0
  108. package/src/runtime/plugin.node.tsx +72 -85
  109. package/src/runtime/plugin.tsx +361 -206
  110. package/src/runtime/plugin.worker.tsx +4 -0
  111. package/src/runtime/pluginCore.ts +48 -0
  112. package/src/runtime/prefetchLink.tsx +1 -1
  113. package/src/runtime/register.ts +58 -0
  114. package/src/runtime/routeTree.ts +163 -354
  115. package/src/runtime/router.ts +15 -0
  116. package/src/runtime/rsc/payloadRouter.ts +45 -2
  117. package/src/runtime/ssr-shim.d.ts +1 -3
  118. package/src/runtime/state.ts +29 -0
  119. package/src/runtime/types.ts +32 -66
  120. package/src/runtime/utils.tsx +3 -6
  121. package/tests/router/cli.test.ts +586 -5
  122. package/tests/router/fastDefaults.test.ts +25 -0
  123. package/tests/router/hooks.test.ts +26 -0
  124. package/tests/router/hydrationBoundary.test.tsx +23 -0
  125. package/tests/router/loaderBridge.test.ts +211 -0
  126. package/tests/router/packageSurface.test.ts +24 -0
  127. package/tests/router/prefetchLink.test.tsx +43 -7
  128. package/tests/router/register.test.ts +46 -0
  129. package/tests/router/routeTree.test.ts +381 -81
  130. package/tests/router/rsc.test.tsx +70 -0
  131. package/tests/router/tanstackTypes.test.ts +573 -1
  132. package/dist/cjs/runtime/DefaultNotFound.js +0 -47
  133. package/dist/esm/runtime/DefaultNotFound.mjs +0 -13
  134. package/dist/esm-node/runtime/DefaultNotFound.mjs +0 -14
  135. package/dist/types/runtime/DefaultNotFound.d.ts +0 -2
  136. package/src/runtime/DefaultNotFound.tsx +0 -15
@@ -1,5 +1,6 @@
1
+ import './register';
2
+
1
3
  export * from '@tanstack/react-router';
2
- export { useMatch } from '@tanstack/react-router';
3
4
  export type {
4
5
  Fetcher,
5
6
  FetcherState,
@@ -12,6 +13,14 @@ export {
12
13
  RouteActionResponseError,
13
14
  useFetcher,
14
15
  } from './dataMutation';
16
+ export type { ModernRouterContext } from './loaderBridge';
17
+ export {
18
+ createRouteStaticData,
19
+ isAbsoluteUrl,
20
+ modernLoaderToTanstack,
21
+ throwTanstackRedirect,
22
+ } from './loaderBridge';
23
+ export { Outlet } from './outlet';
15
24
  export {
16
25
  tanstackRouterPlugin,
17
26
  tanstackRouterPlugin as default,
@@ -22,9 +31,10 @@ export type {
22
31
  PrefetchBehavior,
23
32
  } from './prefetchLink';
24
33
  export { Link, NavLink } from './prefetchLink';
25
- export type {
26
- AnyCompositeComponent,
27
- AnyRenderableServerComponent,
28
- CompositeComponentProps,
29
- } from './rsc/client';
30
- export { CompositeComponent } from './rsc/client';
34
+ export type { TanstackRouterState } from './state';
35
+ export { getTanstackRouterState } from './state';
36
+ export type { RouterConfig } from './types';
37
+ export {
38
+ getModernTanstackRouterFastDefaults,
39
+ modernTanstackRouterFastDefaults,
40
+ } from './types';
@@ -1,151 +1,16 @@
1
- // @effect-diagnostics strictBooleanExpressions:off
2
- import type { TInternalRuntimeContext } from '@modern-js/runtime/context';
3
- import type { RouteObject } from '@modern-js/runtime-utils/router';
4
- import type {
5
- InternalRouterRuntimeState,
6
- InternalRouterServerSnapshot,
7
- RouterFramework,
8
- RouterRouteMatchSnapshot,
9
- RouterServerPrepareResult,
10
- } from './types';
11
-
12
- export type RouterLifecyclePhase = 'ssr-prepare' | 'client-create' | 'hydrate';
13
-
14
- export type RouterLifecycleContext = {
15
- framework: RouterFramework;
16
- phase: RouterLifecyclePhase;
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
+ }
@@ -0,0 +1,48 @@
1
+ import { Outlet as TanstackOutlet } from '@tanstack/react-router';
2
+ import {
3
+ createElement,
4
+ type ElementType,
5
+ memo,
6
+ type ReactElement,
7
+ } from 'react';
8
+
9
+ type RouteComponentProps = Record<string, unknown>;
10
+ type PreloadableComponent = ElementType<RouteComponentProps> & {
11
+ load?: () => Promise<unknown>;
12
+ preload?: () => Promise<unknown>;
13
+ };
14
+ type WrappedPreloadableComponent = ((
15
+ props: RouteComponentProps,
16
+ ) => ReactElement | null) & {
17
+ load?: () => Promise<unknown>;
18
+ preload?: () => Promise<unknown>;
19
+ };
20
+
21
+ export const Outlet = memo(function ModernTanstackOutlet() {
22
+ return <TanstackOutlet />;
23
+ });
24
+
25
+ export function withModernRouteMatchContext(
26
+ component: unknown,
27
+ _routeId: string,
28
+ ): unknown {
29
+ if (component === null || component === undefined) {
30
+ return component;
31
+ }
32
+
33
+ const Component = component as ElementType<RouteComponentProps>;
34
+ const WrappedRouteComponent: WrappedPreloadableComponent = props =>
35
+ createElement(Component, props);
36
+
37
+ const preloadable = component as PreloadableComponent;
38
+ if (typeof preloadable.load === 'function') {
39
+ WrappedRouteComponent.load = preloadable.load.bind(preloadable);
40
+ }
41
+ if (typeof preloadable.preload === 'function') {
42
+ WrappedRouteComponent.preload = preloadable.preload.bind(preloadable);
43
+ } else if (typeof preloadable.load === 'function') {
44
+ WrappedRouteComponent.preload = WrappedRouteComponent.load;
45
+ }
46
+
47
+ return WrappedRouteComponent;
48
+ }