@bleedingdev/modern-js-plugin-tanstack 3.5.0-ultramodern.9 → 3.5.0-ultramodern.90
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/artifacts.js +157 -0
- package/dist/cjs/cli/index.js +8 -79
- package/dist/cjs/cli/tanstackTypes/canonicalRoutes.js +113 -0
- package/dist/cjs/cli/tanstackTypes/shared.js +123 -0
- package/dist/cjs/cli/tanstackTypes/source.js +255 -0
- package/dist/cjs/cli/tanstackTypes.js +4 -350
- package/dist/cjs/runtime/blockingSubscribe.js +57 -0
- package/dist/cjs/runtime/{pluginCore.js → clientBasepath.js} +12 -19
- package/dist/cjs/runtime/clientHydration.js +118 -0
- package/dist/cjs/runtime/dataMutation.js +11 -176
- package/dist/cjs/runtime/formData.js +116 -0
- package/dist/cjs/runtime/loaderBridge.js +7 -11
- package/dist/cjs/runtime/plugin.js +21 -125
- package/dist/cjs/runtime/plugin.node.js +45 -125
- package/dist/cjs/runtime/pluginShared.js +87 -0
- package/dist/cjs/runtime/prefetchLink.js +15 -2
- package/dist/cjs/runtime/register.js +3 -16
- package/dist/cjs/runtime/routeTree/components.js +102 -0
- package/dist/cjs/runtime/routeTree/construction.js +152 -0
- package/dist/cjs/runtime/routeTree/index.js +47 -0
- package/dist/cjs/runtime/routeTree/loaders.js +158 -0
- package/dist/cjs/runtime/routeTree/paths.js +57 -0
- package/dist/cjs/runtime/routeTree/staticData.js +63 -0
- package/dist/cjs/runtime/routeTree/tanstackRoutes.js +57 -0
- package/dist/cjs/runtime/routeTree/types.js +18 -0
- package/dist/cjs/runtime/routeTree.js +7 -320
- package/dist/cjs/runtime/router.js +15 -2
- package/dist/cjs/runtime/rsc/CompositeComponent.js +18 -13
- package/dist/cjs/runtime/rsc/ReplayableStream.js +0 -4
- package/dist/cjs/runtime/rsc/RscNodeRenderer.js +5 -16
- package/dist/cjs/runtime/rsc/client.js +8 -25
- package/dist/cjs/runtime/rsc/flightSerialization.js +132 -0
- package/dist/cjs/runtime/rsc/payloadErrors.js +99 -0
- package/dist/cjs/runtime/rsc/payloadFetch.js +105 -0
- package/dist/cjs/runtime/rsc/payloadRouter.js +21 -142
- package/dist/cjs/runtime/rsc/payloadRoutes.js +94 -0
- package/dist/cjs/runtime/rsc/payloadStorage.js +58 -0
- package/dist/cjs/runtime/rsc/server.js +14 -18
- package/dist/cjs/runtime/rsc/shared.js +68 -0
- package/dist/cjs/runtime/rsc/slotUsageSanitizer.js +1 -0
- package/dist/cjs/runtime/rsc/treeGetter.js +72 -0
- package/dist/cjs/runtime/shared/isAbsoluteUrl.js +47 -0
- package/dist/cjs/runtime/ssrManagedTags.js +103 -0
- package/dist/cjs/runtime/ssrPreload.js +89 -0
- package/dist/cjs/runtime/ssrTypes.js +18 -0
- package/dist/cjs/runtime/state.js +1 -1
- package/dist/cjs/runtime/submitAction.js +161 -0
- package/dist/cjs/runtime/utils.js +49 -86
- package/dist/esm/cli/artifacts.mjs +103 -0
- package/dist/esm/cli/index.mjs +8 -82
- package/dist/esm/cli/tanstackTypes/canonicalRoutes.mjs +75 -0
- package/dist/esm/cli/tanstackTypes/shared.mjs +61 -0
- package/dist/esm/cli/tanstackTypes/source.mjs +207 -0
- package/dist/esm/cli/tanstackTypes.mjs +2 -339
- package/dist/esm/runtime/blockingSubscribe.mjs +19 -0
- package/dist/esm/runtime/clientBasepath.mjs +10 -0
- package/dist/esm/runtime/clientHydration.mjs +71 -0
- package/dist/esm/runtime/dataMutation.mjs +2 -167
- package/dist/esm/runtime/formData.mjs +63 -0
- package/dist/esm/runtime/loaderBridge.mjs +5 -9
- package/dist/esm/runtime/plugin.mjs +14 -118
- package/dist/esm/runtime/plugin.node.mjs +41 -121
- package/dist/esm/runtime/pluginShared.mjs +40 -0
- package/dist/esm/runtime/prefetchLink.mjs +18 -5
- package/dist/esm/runtime/register.mjs +1 -14
- package/dist/esm/runtime/routeTree/components.mjs +55 -0
- package/dist/esm/runtime/routeTree/construction.mjs +111 -0
- package/dist/esm/runtime/routeTree/index.mjs +2 -0
- package/dist/esm/runtime/routeTree/loaders.mjs +117 -0
- package/dist/esm/runtime/routeTree/paths.mjs +16 -0
- package/dist/esm/runtime/routeTree/staticData.mjs +22 -0
- package/dist/esm/runtime/routeTree/tanstackRoutes.mjs +13 -0
- package/dist/esm/runtime/routeTree/types.mjs +0 -0
- package/dist/esm/runtime/routeTree.mjs +1 -318
- package/dist/esm/runtime/router.mjs +16 -2
- package/dist/esm/runtime/rsc/CompositeComponent.mjs +18 -13
- package/dist/esm/runtime/rsc/ReplayableStream.mjs +1 -1
- package/dist/esm/runtime/rsc/RscNodeRenderer.mjs +4 -15
- package/dist/esm/runtime/rsc/client.mjs +6 -26
- package/dist/esm/runtime/rsc/flightSerialization.mjs +88 -0
- package/dist/esm/runtime/rsc/payloadErrors.mjs +49 -0
- package/dist/esm/runtime/rsc/payloadFetch.mjs +55 -0
- package/dist/esm/runtime/rsc/payloadRouter.mjs +12 -133
- package/dist/esm/runtime/rsc/payloadRoutes.mjs +50 -0
- package/dist/esm/runtime/rsc/payloadStorage.mjs +14 -0
- package/dist/esm/runtime/rsc/server.mjs +17 -21
- package/dist/esm/runtime/rsc/shared.mjs +21 -0
- package/dist/esm/runtime/rsc/slotUsageSanitizer.mjs +1 -0
- package/dist/esm/runtime/rsc/treeGetter.mjs +34 -0
- package/dist/esm/runtime/shared/isAbsoluteUrl.mjs +9 -0
- package/dist/esm/runtime/ssrManagedTags.mjs +53 -0
- package/dist/esm/runtime/ssrPreload.mjs +51 -0
- package/dist/esm/runtime/ssrTypes.mjs +0 -0
- package/dist/esm/runtime/state.mjs +1 -1
- package/dist/esm/runtime/submitAction.mjs +120 -0
- package/dist/esm/runtime/utils.mjs +47 -75
- package/dist/esm-node/cli/artifacts.mjs +104 -0
- package/dist/esm-node/cli/index.mjs +8 -82
- package/dist/esm-node/cli/tanstackTypes/canonicalRoutes.mjs +76 -0
- package/dist/esm-node/cli/tanstackTypes/shared.mjs +62 -0
- package/dist/esm-node/cli/tanstackTypes/source.mjs +208 -0
- package/dist/esm-node/cli/tanstackTypes.mjs +2 -339
- package/dist/esm-node/runtime/blockingSubscribe.mjs +20 -0
- package/dist/esm-node/runtime/clientBasepath.mjs +11 -0
- package/dist/esm-node/runtime/clientHydration.mjs +72 -0
- package/dist/esm-node/runtime/dataMutation.mjs +2 -167
- package/dist/esm-node/runtime/formData.mjs +64 -0
- package/dist/esm-node/runtime/loaderBridge.mjs +5 -9
- package/dist/esm-node/runtime/plugin.mjs +14 -118
- package/dist/esm-node/runtime/plugin.node.mjs +41 -121
- package/dist/esm-node/runtime/pluginShared.mjs +41 -0
- package/dist/esm-node/runtime/prefetchLink.mjs +18 -5
- package/dist/esm-node/runtime/register.mjs +1 -14
- package/dist/esm-node/runtime/routeTree/components.mjs +56 -0
- package/dist/esm-node/runtime/routeTree/construction.mjs +112 -0
- package/dist/esm-node/runtime/routeTree/index.mjs +3 -0
- package/dist/esm-node/runtime/routeTree/loaders.mjs +118 -0
- package/dist/esm-node/runtime/routeTree/paths.mjs +17 -0
- package/dist/esm-node/runtime/routeTree/staticData.mjs +23 -0
- package/dist/esm-node/runtime/routeTree/tanstackRoutes.mjs +14 -0
- package/dist/esm-node/runtime/routeTree/types.mjs +1 -0
- package/dist/esm-node/runtime/routeTree.mjs +1 -318
- package/dist/esm-node/runtime/router.mjs +16 -2
- package/dist/esm-node/runtime/rsc/CompositeComponent.mjs +18 -13
- package/dist/esm-node/runtime/rsc/ReplayableStream.mjs +1 -1
- package/dist/esm-node/runtime/rsc/RscNodeRenderer.mjs +4 -15
- package/dist/esm-node/runtime/rsc/client.mjs +6 -26
- package/dist/esm-node/runtime/rsc/flightSerialization.mjs +89 -0
- package/dist/esm-node/runtime/rsc/payloadErrors.mjs +50 -0
- package/dist/esm-node/runtime/rsc/payloadFetch.mjs +56 -0
- package/dist/esm-node/runtime/rsc/payloadRouter.mjs +12 -133
- package/dist/esm-node/runtime/rsc/payloadRoutes.mjs +51 -0
- package/dist/esm-node/runtime/rsc/payloadStorage.mjs +15 -0
- package/dist/esm-node/runtime/rsc/server.mjs +17 -21
- package/dist/esm-node/runtime/rsc/shared.mjs +22 -0
- package/dist/esm-node/runtime/rsc/slotUsageSanitizer.mjs +1 -0
- package/dist/esm-node/runtime/rsc/treeGetter.mjs +35 -0
- package/dist/esm-node/runtime/shared/isAbsoluteUrl.mjs +10 -0
- package/dist/esm-node/runtime/ssrManagedTags.mjs +54 -0
- package/dist/esm-node/runtime/ssrPreload.mjs +52 -0
- package/dist/esm-node/runtime/ssrTypes.mjs +1 -0
- package/dist/esm-node/runtime/state.mjs +1 -1
- package/dist/esm-node/runtime/submitAction.mjs +121 -0
- package/dist/esm-node/runtime/utils.mjs +47 -75
- package/dist/types/cli/artifacts.d.ts +33 -0
- package/dist/types/cli/index.d.ts +2 -22
- package/dist/types/cli/tanstackTypes/canonicalRoutes.d.ts +22 -0
- package/dist/types/cli/tanstackTypes/shared.d.ts +20 -0
- package/dist/types/cli/tanstackTypes/source.d.ts +12 -0
- package/dist/types/cli/tanstackTypes.d.ts +3 -31
- package/dist/types/runtime/blockingSubscribe.d.ts +1 -0
- package/dist/types/runtime/clientBasepath.d.ts +1 -0
- package/dist/types/runtime/clientHydration.d.ts +10 -0
- package/dist/types/runtime/dataMutation.d.ts +3 -12
- package/dist/types/runtime/formData.d.ts +24 -0
- package/dist/types/runtime/loaderBridge.d.ts +2 -1
- package/dist/types/runtime/plugin.d.ts +1 -1
- package/dist/types/runtime/plugin.node.d.ts +1 -1
- package/dist/types/runtime/pluginShared.d.ts +38 -0
- package/dist/types/runtime/register.d.ts +10 -9
- package/dist/types/runtime/routeTree/components.d.ts +7 -0
- package/dist/types/runtime/routeTree/construction.d.ts +5 -0
- package/dist/types/runtime/routeTree/index.d.ts +2 -0
- package/dist/types/runtime/routeTree/loaders.d.ts +4 -0
- package/dist/types/runtime/routeTree/paths.d.ts +3 -0
- package/dist/types/runtime/routeTree/staticData.d.ts +19 -0
- package/dist/types/runtime/routeTree/tanstackRoutes.d.ts +4 -0
- package/dist/types/runtime/routeTree/types.d.ts +76 -0
- package/dist/types/runtime/routeTree.d.ts +1 -9
- package/dist/types/runtime/router.d.ts +6 -7
- package/dist/types/runtime/rsc/CompositeComponent.d.ts +1 -2
- package/dist/types/runtime/rsc/ReplayableStream.d.ts +3 -2
- package/dist/types/runtime/rsc/RscNodeRenderer.d.ts +2 -3
- package/dist/types/runtime/rsc/SlotContext.d.ts +2 -1
- package/dist/types/runtime/rsc/client.d.ts +2 -0
- package/dist/types/runtime/rsc/flightSerialization.d.ts +3 -0
- package/dist/types/runtime/rsc/payloadErrors.d.ts +6 -0
- package/dist/types/runtime/rsc/payloadFetch.d.ts +8 -0
- package/dist/types/runtime/rsc/payloadRouter.d.ts +2 -10
- package/dist/types/runtime/rsc/payloadRoutes.d.ts +40 -0
- package/dist/types/runtime/rsc/payloadStorage.d.ts +5 -0
- package/dist/types/runtime/rsc/shared.d.ts +18 -0
- package/dist/types/runtime/rsc/treeGetter.d.ts +1 -0
- package/dist/types/runtime/shared/isAbsoluteUrl.d.ts +1 -0
- package/dist/types/runtime/ssrManagedTags.d.ts +7 -0
- package/dist/types/runtime/ssrPreload.d.ts +2 -0
- package/dist/types/runtime/ssrTypes.d.ts +44 -0
- package/dist/types/runtime/state.d.ts +1 -1
- package/dist/types/runtime/submitAction.d.ts +14 -0
- package/dist/types/runtime/types.d.ts +4 -10
- package/dist/types/runtime/utils.d.ts +4 -24
- package/package.json +37 -17
- package/rstest.config.mts +23 -0
- package/src/cli/artifacts.ts +255 -0
- package/src/cli/index.ts +7 -185
- package/src/cli/tanstackTypes/canonicalRoutes.ts +157 -0
- package/src/cli/tanstackTypes/shared.ts +113 -0
- package/src/cli/tanstackTypes/source.ts +414 -0
- package/src/cli/tanstackTypes.ts +4 -647
- package/src/runtime/blockingSubscribe.ts +48 -0
- package/src/runtime/clientBasepath.ts +12 -0
- package/src/runtime/clientHydration.ts +149 -0
- package/src/runtime/dataMutation.tsx +9 -306
- package/src/runtime/formData.ts +153 -0
- package/src/runtime/loaderBridge.ts +11 -12
- package/src/runtime/plugin.node.tsx +76 -291
- package/src/runtime/plugin.tsx +21 -257
- package/src/runtime/pluginShared.ts +112 -0
- package/src/runtime/prefetchLink.tsx +78 -15
- package/src/runtime/register.ts +10 -42
- package/src/runtime/routeTree/components.ts +130 -0
- package/src/runtime/routeTree/construction.ts +238 -0
- package/src/runtime/routeTree/index.ts +7 -0
- package/src/runtime/routeTree/loaders.ts +232 -0
- package/src/runtime/routeTree/paths.ts +36 -0
- package/src/runtime/routeTree/staticData.ts +66 -0
- package/src/runtime/routeTree/tanstackRoutes.ts +36 -0
- package/src/runtime/routeTree/types.ts +87 -0
- package/src/runtime/routeTree.ts +5 -750
- package/src/runtime/router.ts +35 -7
- package/src/runtime/rsc/CompositeComponent.tsx +29 -20
- package/src/runtime/rsc/ReplayableStream.ts +2 -2
- package/src/runtime/rsc/RscNodeRenderer.tsx +9 -27
- package/src/runtime/rsc/SlotContext.tsx +1 -1
- package/src/runtime/rsc/client.tsx +12 -40
- package/src/runtime/rsc/flightSerialization.ts +158 -0
- package/src/runtime/rsc/payloadErrors.ts +73 -0
- package/src/runtime/rsc/payloadFetch.ts +92 -0
- package/src/runtime/rsc/payloadRouter.ts +29 -234
- package/src/runtime/rsc/payloadRoutes.ts +122 -0
- package/src/runtime/rsc/payloadStorage.ts +35 -0
- package/src/runtime/rsc/server.tsx +20 -29
- package/src/runtime/rsc/shared.ts +70 -0
- package/src/runtime/rsc/slotUsageSanitizer.ts +4 -0
- package/src/runtime/rsc/treeGetter.tsx +52 -0
- package/src/runtime/shared/isAbsoluteUrl.ts +8 -0
- package/src/runtime/ssrManagedTags.ts +107 -0
- package/src/runtime/ssrPreload.ts +125 -0
- package/src/runtime/ssrTypes.ts +51 -0
- package/src/runtime/state.ts +9 -3
- package/src/runtime/submitAction.ts +201 -0
- package/src/runtime/types.ts +4 -18
- package/src/runtime/utils.tsx +76 -110
- package/src/types/react-server-dom-rspack.d.ts +56 -0
- package/tests/castRatchet.test.ts +85 -0
- package/tests/realm-isolation.test.ts +114 -0
- package/tests/router/cli.test.ts +7 -0
- package/tests/router/clientHydration.test.tsx +61 -0
- package/tests/router/dataMutation.test.tsx +59 -0
- package/tests/router/fixtures/legacyRuntimeRouter.ts +10 -0
- package/tests/router/fixtures/router-gen.golden.txt +71 -0
- package/tests/router/fixtures/router-gen.oxlint.json +5 -0
- package/tests/router/flightSerialization.roundtrip.test.tsx +137 -0
- package/tests/router/generateRouteArtifacts.test.ts +106 -0
- package/tests/router/hooks.test.ts +14 -0
- package/tests/router/loaderBridge.test.ts +21 -0
- package/tests/router/prefetchLink.test.tsx +106 -59
- package/tests/router/prefetchLinkPreload.test.tsx +85 -0
- package/tests/router/register.test.ts +1 -39
- package/tests/router/routeTree.test.ts +51 -11
- package/tests/router/routerCompatibility.test.ts +245 -0
- package/tests/router/rsc.test.tsx +189 -0
- package/tests/router/rscPayloadRouterMatrix.test.ts +197 -0
- package/tests/router/slotUsageSanitizer.test.ts +23 -0
- package/tests/router/ssrPreload.test.ts +30 -0
- package/tests/router/tanstackTypes.test.ts +298 -1
- package/dist/esm/runtime/pluginCore.mjs +0 -14
- package/dist/esm-node/runtime/pluginCore.mjs +0 -15
- package/dist/types/runtime/pluginCore.d.ts +0 -21
- package/src/runtime/pluginCore.ts +0 -48
|
@@ -1,46 +1,8 @@
|
|
|
1
1
|
import { resolveRouterProvider } from '@modern-js/runtime/context';
|
|
2
|
-
import
|
|
3
|
-
Form,
|
|
4
|
-
Link,
|
|
5
|
-
NavLink,
|
|
6
|
-
Outlet,
|
|
7
|
-
RouteActionResponseError,
|
|
8
|
-
useFetcher,
|
|
9
|
-
} from '../../src/runtime';
|
|
10
|
-
import { tanstackRouterCompatBindings } from '../../src/runtime/register';
|
|
11
|
-
|
|
12
|
-
const COMPAT_BINDINGS_SLOT = Symbol.for(
|
|
13
|
-
'@modern-js/plugin-tanstack:runtime-compat-bindings',
|
|
14
|
-
);
|
|
2
|
+
import '../../src/runtime/register';
|
|
15
3
|
|
|
16
4
|
describe("'@modern-js/plugin-tanstack/runtime' import side effects", () => {
|
|
17
5
|
it('registers the tanstack router provider', () => {
|
|
18
6
|
expect(typeof resolveRouterProvider('tanstack')).toBe('function');
|
|
19
7
|
});
|
|
20
|
-
|
|
21
|
-
it("publishes the compat bindings consumed by '@modern-js/runtime/tanstack-router'", () => {
|
|
22
|
-
const bindings = (globalThis as Record<symbol, unknown>)[
|
|
23
|
-
COMPAT_BINDINGS_SLOT
|
|
24
|
-
] as typeof tanstackRouterCompatBindings;
|
|
25
|
-
|
|
26
|
-
expect(bindings).toBeDefined();
|
|
27
|
-
expect(bindings).toBe(tanstackRouterCompatBindings);
|
|
28
|
-
expect(bindings.Form).toBe(Form);
|
|
29
|
-
expect(bindings.Link).toBe(Link);
|
|
30
|
-
expect(bindings.NavLink).toBe(NavLink);
|
|
31
|
-
expect(bindings.Outlet).toBe(Outlet);
|
|
32
|
-
expect(bindings.RouteActionResponseError).toBe(RouteActionResponseError);
|
|
33
|
-
expect(bindings.useFetcher).toBe(useFetcher);
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
it('keeps the first published bindings when a duplicate module copy evaluates', () => {
|
|
37
|
-
// Simulates a Module Federation remote evaluating its own copy of
|
|
38
|
-
// register.ts: `??=` must keep the established bindings.
|
|
39
|
-
const host = globalThis as Record<symbol, unknown>;
|
|
40
|
-
const established = host[COMPAT_BINDINGS_SLOT];
|
|
41
|
-
expect(established).toBeDefined();
|
|
42
|
-
|
|
43
|
-
host[COMPAT_BINDINGS_SLOT] ??= { duplicate: true };
|
|
44
|
-
expect(host[COMPAT_BINDINGS_SLOT]).toBe(established);
|
|
45
|
-
});
|
|
46
8
|
});
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
getModernRouteIdsFromMatches,
|
|
16
16
|
} from '../../src/runtime/routeTree';
|
|
17
17
|
import { __setTanstackRscPayloadDecoderForTests } from '../../src/runtime/rsc/payloadRouter';
|
|
18
|
-
import {
|
|
18
|
+
import { createTanstackRouteObjectsFromConfig } from '../../src/runtime/utils';
|
|
19
19
|
|
|
20
20
|
type LoaderArgs = {
|
|
21
21
|
params: Record<string, string>;
|
|
@@ -151,15 +151,15 @@ describe('tanstack runtime public exports', () => {
|
|
|
151
151
|
};
|
|
152
152
|
|
|
153
153
|
expect(packageJson.exports['./runtime/rsc']).toBeUndefined();
|
|
154
|
-
expect(packageJson.exports['./runtime/rsc/client']).
|
|
155
|
-
expect(packageJson.exports['./runtime/rsc/server']).
|
|
154
|
+
expect(packageJson.exports['./runtime/rsc/client']).toBeDefined();
|
|
155
|
+
expect(packageJson.exports['./runtime/rsc/server']).toBeDefined();
|
|
156
156
|
expect(packageJson.typesVersions?.['*']?.['runtime/rsc']).toBeUndefined();
|
|
157
|
-
expect(
|
|
158
|
-
|
|
159
|
-
)
|
|
160
|
-
expect(
|
|
161
|
-
|
|
162
|
-
)
|
|
157
|
+
expect(packageJson.typesVersions?.['*']?.['runtime/rsc/client']).toEqual([
|
|
158
|
+
'./dist/types/runtime/rsc/client.d.ts',
|
|
159
|
+
]);
|
|
160
|
+
expect(packageJson.typesVersions?.['*']?.['runtime/rsc/server']).toEqual([
|
|
161
|
+
'./dist/types/runtime/rsc/server.d.ts',
|
|
162
|
+
]);
|
|
163
163
|
});
|
|
164
164
|
});
|
|
165
165
|
|
|
@@ -201,6 +201,41 @@ describe('tanstack route tree from RouteObject[]', () => {
|
|
|
201
201
|
expect(userMatch?.loaderData).toEqual({ id: '123' });
|
|
202
202
|
});
|
|
203
203
|
|
|
204
|
+
test('keeps unnamed sibling pathless layouts distinct', async () => {
|
|
205
|
+
const routes: RouteObject[] = [
|
|
206
|
+
{
|
|
207
|
+
Component: () => null,
|
|
208
|
+
children: [
|
|
209
|
+
{
|
|
210
|
+
path: 'alpha',
|
|
211
|
+
loader: () => ({ value: 'alpha' }),
|
|
212
|
+
Component: () => null,
|
|
213
|
+
},
|
|
214
|
+
],
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
Component: () => null,
|
|
218
|
+
children: [
|
|
219
|
+
{
|
|
220
|
+
path: 'beta',
|
|
221
|
+
loader: () => ({ value: 'beta' }),
|
|
222
|
+
Component: () => null,
|
|
223
|
+
},
|
|
224
|
+
],
|
|
225
|
+
},
|
|
226
|
+
];
|
|
227
|
+
|
|
228
|
+
const routeTree = createRouteTreeFromRouteObjects(routes);
|
|
229
|
+
const router = await loadRouteTree(routeTree, '/beta');
|
|
230
|
+
|
|
231
|
+
const matchedLoaderData = router.state.matches
|
|
232
|
+
.map(match => match.loaderData)
|
|
233
|
+
.filter(Boolean);
|
|
234
|
+
|
|
235
|
+
expect(matchedLoaderData).toContainEqual({ value: 'beta' });
|
|
236
|
+
expect(matchedLoaderData).not.toContainEqual({ value: 'alpha' });
|
|
237
|
+
});
|
|
238
|
+
|
|
204
239
|
test('reports native TanStack unknown routes as HTTP 404', async () => {
|
|
205
240
|
const routes: RouteObject[] = [
|
|
206
241
|
{
|
|
@@ -774,13 +809,16 @@ describe('tanstack route tree from RouteObject[]', () => {
|
|
|
774
809
|
path: 'client',
|
|
775
810
|
clientData: './client.data',
|
|
776
811
|
data: './data',
|
|
812
|
+
hasAction: true,
|
|
813
|
+
hasClientLoader: true,
|
|
814
|
+
hasLoader: true,
|
|
777
815
|
inValidSSRRoute: true,
|
|
778
816
|
isClientComponent: true,
|
|
779
817
|
},
|
|
780
818
|
],
|
|
781
819
|
},
|
|
782
820
|
];
|
|
783
|
-
const routeObjects =
|
|
821
|
+
const routeObjects = createTanstackRouteObjectsFromConfig({
|
|
784
822
|
routesConfig: { routes: modernRoutes },
|
|
785
823
|
});
|
|
786
824
|
const routeTree = createRouteTreeFromRouteObjects(routeObjects || []);
|
|
@@ -795,7 +833,9 @@ describe('tanstack route tree from RouteObject[]', () => {
|
|
|
795
833
|
|
|
796
834
|
expect(clientRoute.options.ssr).toBe(false);
|
|
797
835
|
expect(clientRoute.options.staticData).toMatchObject({
|
|
836
|
+
modernRouteHasAction: true,
|
|
798
837
|
modernRouteHasClientLoader: true,
|
|
838
|
+
modernRouteHasLoader: true,
|
|
799
839
|
modernRouteIsClientComponent: true,
|
|
800
840
|
});
|
|
801
841
|
});
|
|
@@ -825,7 +865,7 @@ describe('tanstack route tree from RouteObject[]', () => {
|
|
|
825
865
|
},
|
|
826
866
|
];
|
|
827
867
|
|
|
828
|
-
const routeObjects =
|
|
868
|
+
const routeObjects = createTanstackRouteObjectsFromConfig({
|
|
829
869
|
routesConfig: { routes: modernRoutes },
|
|
830
870
|
});
|
|
831
871
|
const rootRoute = routeObjects?.[0];
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import { execFileSync } from 'node:child_process';
|
|
2
|
+
import {
|
|
3
|
+
mkdirSync,
|
|
4
|
+
mkdtempSync,
|
|
5
|
+
readFileSync,
|
|
6
|
+
rmSync,
|
|
7
|
+
writeFileSync,
|
|
8
|
+
} from 'node:fs';
|
|
9
|
+
import { tmpdir } from 'node:os';
|
|
10
|
+
import path from 'node:path';
|
|
11
|
+
|
|
12
|
+
const REGISTRY_SLOT: unique symbol = Symbol.for(
|
|
13
|
+
'@modern-js/runtime:router-providers:v3',
|
|
14
|
+
);
|
|
15
|
+
const registryHost = globalThis as { [REGISTRY_SLOT]?: unknown };
|
|
16
|
+
const webpackHost = globalThis as typeof globalThis & {
|
|
17
|
+
__webpack_require__?: { u: (chunkId: unknown) => string };
|
|
18
|
+
};
|
|
19
|
+
const installWebpackRequire = () => {
|
|
20
|
+
webpackHost.__webpack_require__ = { u: chunkId => String(chunkId) };
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
type LegacyRuntimeRouterModule = {
|
|
24
|
+
getLegacyRouterPluginInvocationCount: () => number;
|
|
25
|
+
routerPlugin: (...args: unknown[]) => unknown;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
type CompatibilityGraph = {
|
|
29
|
+
factory: (...args: any[]) => any;
|
|
30
|
+
legacyRuntimeRouter: LegacyRuntimeRouterModule;
|
|
31
|
+
resolveRouterProvider: (
|
|
32
|
+
framework?: string,
|
|
33
|
+
) => (...args: unknown[]) => unknown;
|
|
34
|
+
routerPlugin: (...args: any[]) => any;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
async function loadCompatibilityGraph(): Promise<CompatibilityGraph> {
|
|
38
|
+
rstest.resetModules();
|
|
39
|
+
installWebpackRequire();
|
|
40
|
+
|
|
41
|
+
const legacyRuntimeRouter = (await import(
|
|
42
|
+
'@modern-js/runtime/router/internal'
|
|
43
|
+
)) as unknown as LegacyRuntimeRouterModule;
|
|
44
|
+
const { routerPlugin } = await import('../../src/runtime/router');
|
|
45
|
+
const { tanstackRouterProviderFactory } = await import(
|
|
46
|
+
'../../src/runtime/register'
|
|
47
|
+
);
|
|
48
|
+
const { resolveRouterProvider } = await import('@modern-js/runtime/context');
|
|
49
|
+
|
|
50
|
+
return {
|
|
51
|
+
factory: tanstackRouterProviderFactory,
|
|
52
|
+
legacyRuntimeRouter,
|
|
53
|
+
resolveRouterProvider,
|
|
54
|
+
routerPlugin,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function resolveTsgoBin(): string {
|
|
59
|
+
const packageJsonPath = require.resolve(
|
|
60
|
+
'@typescript/native-preview/package.json',
|
|
61
|
+
);
|
|
62
|
+
const packageDirectory = path.dirname(packageJsonPath);
|
|
63
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8')) as {
|
|
64
|
+
bin?: string | { tsgo?: string };
|
|
65
|
+
};
|
|
66
|
+
const binEntry =
|
|
67
|
+
typeof packageJson.bin === 'string'
|
|
68
|
+
? packageJson.bin
|
|
69
|
+
: packageJson.bin?.tsgo;
|
|
70
|
+
|
|
71
|
+
return path.resolve(packageDirectory, binEntry ?? 'bin/tsgo.js');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function runTsgo(tsgoBin: string, configPath: string): void {
|
|
75
|
+
try {
|
|
76
|
+
execFileSync(process.execPath, [tsgoBin, '-p', configPath], {
|
|
77
|
+
stdio: 'pipe',
|
|
78
|
+
});
|
|
79
|
+
} catch (error) {
|
|
80
|
+
const result = error as { stderr?: Buffer; stdout?: Buffer };
|
|
81
|
+
throw new Error(
|
|
82
|
+
`TS-Go compatibility check failed:\n${String(result.stdout ?? '')}${String(
|
|
83
|
+
result.stderr ?? '',
|
|
84
|
+
)}`,
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
installWebpackRequire();
|
|
90
|
+
|
|
91
|
+
describe('runtime router compatibility', () => {
|
|
92
|
+
beforeEach(() => {
|
|
93
|
+
delete registryHost[REGISTRY_SLOT];
|
|
94
|
+
rstest.resetModules();
|
|
95
|
+
installWebpackRequire();
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
afterEach(() => {
|
|
99
|
+
delete registryHost[REGISTRY_SLOT];
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('uses the app-local factory instead of the legacy global wrapper', async () => {
|
|
103
|
+
const graph = await loadCompatibilityGraph();
|
|
104
|
+
|
|
105
|
+
expect('createRouterPlugin' in graph.legacyRuntimeRouter).toBe(false);
|
|
106
|
+
expect(graph.routerPlugin).toBe(graph.factory);
|
|
107
|
+
expect(graph.routerPlugin({ framework: 'tanstack' })).toMatchObject({
|
|
108
|
+
name: '@modern-js/plugin-router-tanstack',
|
|
109
|
+
});
|
|
110
|
+
expect(
|
|
111
|
+
graph.legacyRuntimeRouter.getLegacyRouterPluginInvocationCount(),
|
|
112
|
+
).toBe(0);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it('keeps independently evaluated legacy module graphs app-local', async () => {
|
|
116
|
+
const graphA = await loadCompatibilityGraph();
|
|
117
|
+
const graphB = await loadCompatibilityGraph();
|
|
118
|
+
|
|
119
|
+
expect(graphB.factory).not.toBe(graphA.factory);
|
|
120
|
+
expect(graphB.routerPlugin).toBe(graphB.factory);
|
|
121
|
+
expect(graphB.resolveRouterProvider('tanstack')).toBe(graphA.factory);
|
|
122
|
+
expect(graphB.routerPlugin({ framework: 'tanstack' })).toMatchObject({
|
|
123
|
+
name: '@modern-js/plugin-router-tanstack',
|
|
124
|
+
});
|
|
125
|
+
expect(
|
|
126
|
+
graphA.legacyRuntimeRouter.getLegacyRouterPluginInvocationCount(),
|
|
127
|
+
).toBe(0);
|
|
128
|
+
expect(
|
|
129
|
+
graphB.legacyRuntimeRouter.getLegacyRouterPluginInvocationCount(),
|
|
130
|
+
).toBe(0);
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it('emits declarations compatible with the oldest runtime router shape', () => {
|
|
134
|
+
const packageRoot = path.resolve(__dirname, '../..');
|
|
135
|
+
const sourcePath = path.join(packageRoot, 'src/runtime/router.ts');
|
|
136
|
+
const legacyFixturePath = path.join(
|
|
137
|
+
__dirname,
|
|
138
|
+
'fixtures/legacyRuntimeRouter.ts',
|
|
139
|
+
);
|
|
140
|
+
const tempRoot = mkdtempSync(
|
|
141
|
+
path.join(tmpdir(), 'plugin-tanstack-router-compatibility-'),
|
|
142
|
+
);
|
|
143
|
+
const sourceRoot = path.join(tempRoot, 'src');
|
|
144
|
+
const emittedRoot = path.join(tempRoot, 'types');
|
|
145
|
+
const tsgoBin = resolveTsgoBin();
|
|
146
|
+
|
|
147
|
+
try {
|
|
148
|
+
mkdirSync(sourceRoot, { recursive: true });
|
|
149
|
+
writeFileSync(
|
|
150
|
+
path.join(sourceRoot, 'router.ts'),
|
|
151
|
+
readFileSync(sourcePath, 'utf8'),
|
|
152
|
+
);
|
|
153
|
+
writeFileSync(
|
|
154
|
+
path.join(sourceRoot, 'register.ts'),
|
|
155
|
+
[
|
|
156
|
+
'export declare const tanstackRouterProviderFactory:',
|
|
157
|
+
' (userConfig?: Record<string, unknown>) => { name?: string };',
|
|
158
|
+
].join('\n'),
|
|
159
|
+
);
|
|
160
|
+
writeFileSync(
|
|
161
|
+
path.join(tempRoot, 'currentRuntimeRouter.d.ts'),
|
|
162
|
+
[
|
|
163
|
+
'export type RouterPluginFactory =',
|
|
164
|
+
' (userConfig?: Record<string, unknown>) => { name?: string };',
|
|
165
|
+
'export declare const routerPlugin: RouterPluginFactory;',
|
|
166
|
+
'export declare const createRouterPlugin:',
|
|
167
|
+
' (providers: readonly unknown[]) => RouterPluginFactory;',
|
|
168
|
+
].join('\n'),
|
|
169
|
+
);
|
|
170
|
+
writeFileSync(
|
|
171
|
+
path.join(tempRoot, 'emit.json'),
|
|
172
|
+
JSON.stringify(
|
|
173
|
+
{
|
|
174
|
+
compilerOptions: {
|
|
175
|
+
declaration: true,
|
|
176
|
+
emitDeclarationOnly: true,
|
|
177
|
+
module: 'Preserve',
|
|
178
|
+
moduleResolution: 'Bundler',
|
|
179
|
+
noCheck: true,
|
|
180
|
+
outDir: emittedRoot,
|
|
181
|
+
paths: {
|
|
182
|
+
'@modern-js/runtime/router/internal': [
|
|
183
|
+
'./currentRuntimeRouter.d.ts',
|
|
184
|
+
],
|
|
185
|
+
},
|
|
186
|
+
rootDir: sourceRoot,
|
|
187
|
+
skipLibCheck: false,
|
|
188
|
+
strict: true,
|
|
189
|
+
target: 'ESNext',
|
|
190
|
+
types: [],
|
|
191
|
+
},
|
|
192
|
+
files: [path.join(sourceRoot, 'router.ts')],
|
|
193
|
+
},
|
|
194
|
+
null,
|
|
195
|
+
2,
|
|
196
|
+
),
|
|
197
|
+
);
|
|
198
|
+
|
|
199
|
+
runTsgo(tsgoBin, path.join(tempRoot, 'emit.json'));
|
|
200
|
+
|
|
201
|
+
const declarationPath = path.join(emittedRoot, 'router.d.ts');
|
|
202
|
+
const declaration = readFileSync(declarationPath, 'utf8');
|
|
203
|
+
expect(declaration).not.toMatch(
|
|
204
|
+
/\b(?:createRouterPlugin|RouterPluginFactory)\b/u,
|
|
205
|
+
);
|
|
206
|
+
expect(declaration).toContain("['routerPlugin']");
|
|
207
|
+
|
|
208
|
+
writeFileSync(
|
|
209
|
+
path.join(tempRoot, 'legacyRuntimeRouter.ts'),
|
|
210
|
+
readFileSync(legacyFixturePath, 'utf8'),
|
|
211
|
+
);
|
|
212
|
+
writeFileSync(
|
|
213
|
+
path.join(tempRoot, 'compatibility.json'),
|
|
214
|
+
JSON.stringify(
|
|
215
|
+
{
|
|
216
|
+
compilerOptions: {
|
|
217
|
+
module: 'Preserve',
|
|
218
|
+
moduleResolution: 'Bundler',
|
|
219
|
+
noEmit: true,
|
|
220
|
+
paths: {
|
|
221
|
+
'@modern-js/runtime/router/internal': [
|
|
222
|
+
'./legacyRuntimeRouter.ts',
|
|
223
|
+
],
|
|
224
|
+
},
|
|
225
|
+
skipLibCheck: false,
|
|
226
|
+
strict: true,
|
|
227
|
+
target: 'ESNext',
|
|
228
|
+
types: [],
|
|
229
|
+
},
|
|
230
|
+
files: [
|
|
231
|
+
declarationPath,
|
|
232
|
+
path.join(tempRoot, 'legacyRuntimeRouter.ts'),
|
|
233
|
+
],
|
|
234
|
+
},
|
|
235
|
+
null,
|
|
236
|
+
2,
|
|
237
|
+
),
|
|
238
|
+
);
|
|
239
|
+
|
|
240
|
+
runTsgo(tsgoBin, path.join(tempRoot, 'compatibility.json'));
|
|
241
|
+
} finally {
|
|
242
|
+
rmSync(tempRoot, { force: true, recursive: true });
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
});
|
|
@@ -2,6 +2,10 @@ import { UNSAFE_ErrorResponseImpl as ErrorResponseImpl } from '@modern-js/runtim
|
|
|
2
2
|
import type React from 'react';
|
|
3
3
|
import { isValidElement } from 'react';
|
|
4
4
|
import { createRscProxy } from '../../src/runtime/rsc/createRscProxy';
|
|
5
|
+
import {
|
|
6
|
+
reviveTanstackRscFlightValues,
|
|
7
|
+
serializeTanstackRscFlightValues,
|
|
8
|
+
} from '../../src/runtime/rsc/flightSerialization';
|
|
5
9
|
import {
|
|
6
10
|
__setTanstackRscPayloadDecoderForTests,
|
|
7
11
|
createTanstackRscServerPayload,
|
|
@@ -65,6 +69,54 @@ describe('tanstack rsc runtime helpers', () => {
|
|
|
65
69
|
]);
|
|
66
70
|
});
|
|
67
71
|
|
|
72
|
+
test('revives shared structures that the Flight serializer preserves', async () => {
|
|
73
|
+
const shared = { label: 'shared' };
|
|
74
|
+
const serialized = serializeTanstackRscFlightValues({
|
|
75
|
+
left: shared,
|
|
76
|
+
right: shared,
|
|
77
|
+
}) as { left: unknown; right: unknown };
|
|
78
|
+
|
|
79
|
+
const revived = (await reviveTanstackRscFlightValues(serialized)) as {
|
|
80
|
+
left: unknown;
|
|
81
|
+
right: unknown;
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
expect(serialized.left).toBe(serialized.right);
|
|
85
|
+
expect(revived.left).toBe(revived.right);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test('serializes TanStack RSC flight values nested in map and set containers', () => {
|
|
89
|
+
const stream: ServerComponentStream = {
|
|
90
|
+
createReplayStream: () => new ReadableStream<Uint8Array>(),
|
|
91
|
+
};
|
|
92
|
+
const proxy = createRscProxy(() => null, {
|
|
93
|
+
renderable: true,
|
|
94
|
+
stream,
|
|
95
|
+
}) as React.ReactElement & Record<PropertyKey, unknown>;
|
|
96
|
+
|
|
97
|
+
const serialized = serializeTanstackRscFlightValues({
|
|
98
|
+
map: new Map([['slot', proxy]]),
|
|
99
|
+
set: new Set([proxy]),
|
|
100
|
+
}) as {
|
|
101
|
+
map: Map<string, Record<string, unknown>>;
|
|
102
|
+
set: Set<Record<string, unknown>>;
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
const mapValue = serialized.map.get('slot');
|
|
106
|
+
const [setValue] = serialized.set;
|
|
107
|
+
|
|
108
|
+
expect(mapValue).not.toBe(proxy);
|
|
109
|
+
expect(setValue).not.toBe(proxy);
|
|
110
|
+
expect(mapValue).toMatchObject({
|
|
111
|
+
__modernTanstackRsc: true,
|
|
112
|
+
kind: 'renderable',
|
|
113
|
+
});
|
|
114
|
+
expect(setValue).toMatchObject({
|
|
115
|
+
__modernTanstackRsc: true,
|
|
116
|
+
kind: 'renderable',
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
|
|
68
120
|
test('renderable RSC proxies preserve React element behavior and metadata', () => {
|
|
69
121
|
const stream: ServerComponentStream = {
|
|
70
122
|
createReplayStream: () => new ReadableStream<Uint8Array>(),
|
|
@@ -219,6 +271,36 @@ describe('tanstack rsc runtime helpers', () => {
|
|
|
219
271
|
expect(JSON.stringify(payload.errors)).not.toContain('stack secret');
|
|
220
272
|
});
|
|
221
273
|
|
|
274
|
+
test('redacts production TanStack RSC non-Error server payload errors', () => {
|
|
275
|
+
const payload = withNodeEnv('production', () =>
|
|
276
|
+
createTanstackRscServerPayload({
|
|
277
|
+
state: {
|
|
278
|
+
location: { href: '/plain' },
|
|
279
|
+
matches: [
|
|
280
|
+
{
|
|
281
|
+
error: { message: 'plain secret', token: 'token secret' },
|
|
282
|
+
params: {},
|
|
283
|
+
pathname: '/plain',
|
|
284
|
+
pathnameBase: '/plain',
|
|
285
|
+
route: { id: '/plain' },
|
|
286
|
+
routeId: '/plain',
|
|
287
|
+
},
|
|
288
|
+
],
|
|
289
|
+
},
|
|
290
|
+
}),
|
|
291
|
+
);
|
|
292
|
+
|
|
293
|
+
expect(payload.errors).toMatchObject({
|
|
294
|
+
'/plain': {
|
|
295
|
+
message: 'Unexpected Server Error',
|
|
296
|
+
stack: undefined,
|
|
297
|
+
__type: 'Error',
|
|
298
|
+
},
|
|
299
|
+
});
|
|
300
|
+
expect(JSON.stringify(payload.errors)).not.toContain('plain secret');
|
|
301
|
+
expect(JSON.stringify(payload.errors)).not.toContain('token secret');
|
|
302
|
+
});
|
|
303
|
+
|
|
222
304
|
test('converts TanStack RSC redirects to Modern RSC navigation headers', () => {
|
|
223
305
|
const response = handleTanstackRscRedirect(
|
|
224
306
|
new Headers({ Location: '/base/login' }),
|
|
@@ -232,6 +314,54 @@ describe('tanstack rsc runtime helpers', () => {
|
|
|
232
314
|
expect(response.headers.get('X-Modernjs-BaseUrl')).toBe('/base');
|
|
233
315
|
});
|
|
234
316
|
|
|
317
|
+
test('preserves TanStack RSC redirect paths that do not start with the basename', () => {
|
|
318
|
+
const response = handleTanstackRscRedirect(
|
|
319
|
+
new Headers({ Location: '/shop/base/login' }),
|
|
320
|
+
'/base',
|
|
321
|
+
302,
|
|
322
|
+
);
|
|
323
|
+
|
|
324
|
+
expect(response.headers.get('X-Modernjs-Redirect')).toBe(
|
|
325
|
+
'/shop/base/login',
|
|
326
|
+
);
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
test('preserves RSC redirect response status and headers in TanStack redirects', async () => {
|
|
330
|
+
rstest.stubGlobal(
|
|
331
|
+
'fetch',
|
|
332
|
+
rstest.fn(() =>
|
|
333
|
+
Promise.resolve(
|
|
334
|
+
new Response(null, {
|
|
335
|
+
headers: {
|
|
336
|
+
'X-Modernjs-Redirect': '/login',
|
|
337
|
+
'X-Trace': 'preserved',
|
|
338
|
+
},
|
|
339
|
+
status: 308,
|
|
340
|
+
}),
|
|
341
|
+
),
|
|
342
|
+
),
|
|
343
|
+
);
|
|
344
|
+
|
|
345
|
+
const thrown = (await loadTanstackRscRouteData({
|
|
346
|
+
loadClientData: async () => ({ fallback: true }),
|
|
347
|
+
request: new Request('http://localhost/products'),
|
|
348
|
+
routeId: '/products',
|
|
349
|
+
}).then(
|
|
350
|
+
() => {
|
|
351
|
+
throw new Error('expected redirect');
|
|
352
|
+
},
|
|
353
|
+
err => err,
|
|
354
|
+
)) as Response & {
|
|
355
|
+
options?: { statusCode?: number; to?: string };
|
|
356
|
+
};
|
|
357
|
+
|
|
358
|
+
expect(thrown).toBeInstanceOf(Response);
|
|
359
|
+
expect(thrown.status).toBe(308);
|
|
360
|
+
expect(thrown.headers.get('X-Trace')).toBe('preserved');
|
|
361
|
+
expect(thrown.options?.statusCode).toBe(308);
|
|
362
|
+
expect(thrown.options?.to).toBe('/login');
|
|
363
|
+
});
|
|
364
|
+
|
|
235
365
|
test('loads one RSC payload for multiple server route loaders', async () => {
|
|
236
366
|
const payload = {
|
|
237
367
|
type: 'render',
|
|
@@ -275,6 +405,65 @@ describe('tanstack rsc runtime helpers', () => {
|
|
|
275
405
|
expect(decodeMock).toHaveBeenCalledTimes(1);
|
|
276
406
|
});
|
|
277
407
|
|
|
408
|
+
test('separates RSC payload cache entries by forwarded request headers', async () => {
|
|
409
|
+
const payloadByUser: Record<string, unknown> = {
|
|
410
|
+
alice: {
|
|
411
|
+
type: 'render',
|
|
412
|
+
actionData: null,
|
|
413
|
+
errors: null,
|
|
414
|
+
loaderData: {
|
|
415
|
+
'/profile': { user: 'alice' },
|
|
416
|
+
},
|
|
417
|
+
location: { href: '/profile' },
|
|
418
|
+
routes: [{ id: '/profile', hasLoader: true }],
|
|
419
|
+
},
|
|
420
|
+
bob: {
|
|
421
|
+
type: 'render',
|
|
422
|
+
actionData: null,
|
|
423
|
+
errors: null,
|
|
424
|
+
loaderData: {
|
|
425
|
+
'/profile': { user: 'bob' },
|
|
426
|
+
},
|
|
427
|
+
location: { href: '/profile' },
|
|
428
|
+
routes: [{ id: '/profile', hasLoader: true }],
|
|
429
|
+
},
|
|
430
|
+
};
|
|
431
|
+
const fetchMock = rstest.fn(
|
|
432
|
+
(_url: string | URL | Request, init?: RequestInit) => {
|
|
433
|
+
const user =
|
|
434
|
+
init?.headers instanceof Headers ? init.headers.get('x-user') : null;
|
|
435
|
+
return Promise.resolve(
|
|
436
|
+
new Response(JSON.stringify(payloadByUser[user || ''])),
|
|
437
|
+
);
|
|
438
|
+
},
|
|
439
|
+
);
|
|
440
|
+
rstest.stubGlobal('fetch', fetchMock);
|
|
441
|
+
__setTanstackRscPayloadDecoderForTests(async stream =>
|
|
442
|
+
JSON.parse(await new Response(stream).text()),
|
|
443
|
+
);
|
|
444
|
+
|
|
445
|
+
const [aliceData, bobData] = await Promise.all([
|
|
446
|
+
loadTanstackRscRouteData({
|
|
447
|
+
loadClientData: async () => ({ fallback: 'alice' }),
|
|
448
|
+
request: new Request('http://localhost/profile', {
|
|
449
|
+
headers: { 'x-user': 'alice' },
|
|
450
|
+
}),
|
|
451
|
+
routeId: '/profile',
|
|
452
|
+
}),
|
|
453
|
+
loadTanstackRscRouteData({
|
|
454
|
+
loadClientData: async () => ({ fallback: 'bob' }),
|
|
455
|
+
request: new Request('http://localhost/profile', {
|
|
456
|
+
headers: { 'x-user': 'bob' },
|
|
457
|
+
}),
|
|
458
|
+
routeId: '/profile',
|
|
459
|
+
}),
|
|
460
|
+
]);
|
|
461
|
+
|
|
462
|
+
expect(aliceData).toEqual({ user: 'alice' });
|
|
463
|
+
expect(bobData).toEqual({ user: 'bob' });
|
|
464
|
+
expect(fetchMock).toHaveBeenCalledTimes(2);
|
|
465
|
+
});
|
|
466
|
+
|
|
278
467
|
test('uses client loader data without requesting an RSC payload for client-loader routes', async () => {
|
|
279
468
|
const fetchMock = rstest.fn();
|
|
280
469
|
const loadClientData = rstest.fn(async () => ({ client: true }));
|