@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
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
// This file is auto-generated by Modern.js. Do not edit manually.
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
createMemoryHistory,
|
|
5
|
+
createRootRouteWithContext,
|
|
6
|
+
createRoute,
|
|
7
|
+
createRouter,
|
|
8
|
+
createRouteStaticData,
|
|
9
|
+
type ModernRouterContext,
|
|
10
|
+
modernLoaderToTanstack,
|
|
11
|
+
modernTanstackRouterFastDefaults,
|
|
12
|
+
} from '@modern-js/plugin-tanstack/runtime';
|
|
13
|
+
|
|
14
|
+
import component_0 from "../../routes/(app)/layout";
|
|
15
|
+
import { loader as loader_0, action as action_0 } from "../../routes/(app)/users/(userId)/page.data";
|
|
16
|
+
import { validateSearch as validateSearch_0 } from "../../routes/search.contract";
|
|
17
|
+
import { loaderDeps as loaderDeps_0 } from "../../routes/search.contract";
|
|
18
|
+
import component_1 from "../../routes/(app)/users/(userId)/page";
|
|
19
|
+
import component_2 from "../../routes/(app)/docs/splat";
|
|
20
|
+
|
|
21
|
+
export const rootRoute = createRootRouteWithContext<ModernRouterContext>()({
|
|
22
|
+
|
|
23
|
+
staticData: createRouteStaticData({
|
|
24
|
+
modernRouteId: "layout",
|
|
25
|
+
}),
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
const route__app__layout__base = createRoute({
|
|
29
|
+
getParentRoute: () => rootRoute,
|
|
30
|
+
component: component_0,
|
|
31
|
+
id: "(app)/layout",
|
|
32
|
+
staticData: createRouteStaticData({
|
|
33
|
+
modernRouteId: "(app)/layout",
|
|
34
|
+
}),
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const route__app__users__userId__page = createRoute({
|
|
38
|
+
getParentRoute: () => route__app__layout__base,
|
|
39
|
+
component: component_1,
|
|
40
|
+
path: "users/$userId",
|
|
41
|
+
loader: modernLoaderToTanstack({ hasSplat: false }, loader_0),
|
|
42
|
+
validateSearch: validateSearch_0,
|
|
43
|
+
loaderDeps: loaderDeps_0,
|
|
44
|
+
staticData: createRouteStaticData({
|
|
45
|
+
modernRouteId: "(app)/users/(userId)/page",
|
|
46
|
+
modernRouteLoader: loader_0,
|
|
47
|
+
modernRouteAction: action_0,
|
|
48
|
+
}),
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
const route__app__docs_splat_page = createRoute({
|
|
52
|
+
getParentRoute: () => route__app__layout__base,
|
|
53
|
+
component: component_2,
|
|
54
|
+
path: "docs/$",
|
|
55
|
+
staticData: createRouteStaticData({
|
|
56
|
+
modernRouteId: "(app)/docs/splat/page",
|
|
57
|
+
}),
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
const route__app__layout = route__app__layout__base.addChildren([route__app__users__userId__page, route__app__docs_splat_page]);
|
|
61
|
+
|
|
62
|
+
export const routeTree = rootRoute.addChildren([route__app__layout]);
|
|
63
|
+
|
|
64
|
+
export const router = createRouter({
|
|
65
|
+
...modernTanstackRouterFastDefaults,
|
|
66
|
+
routeTree,
|
|
67
|
+
history: createMemoryHistory({
|
|
68
|
+
initialEntries: ['/'],
|
|
69
|
+
}),
|
|
70
|
+
context: {} as ModernRouterContext,
|
|
71
|
+
});
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import type React from 'react';
|
|
2
|
+
import { isValidElement } from 'react';
|
|
3
|
+
import { createRscProxy } from '../../src/runtime/rsc/createRscProxy';
|
|
4
|
+
import {
|
|
5
|
+
reviveTanstackRscFlightValues,
|
|
6
|
+
serializeTanstackRscFlightValues,
|
|
7
|
+
} from '../../src/runtime/rsc/flightSerialization';
|
|
8
|
+
import {
|
|
9
|
+
RENDERABLE_RSC,
|
|
10
|
+
SERVER_COMPONENT_STREAM,
|
|
11
|
+
type ServerComponentStream,
|
|
12
|
+
} from '../../src/runtime/rsc/symbols';
|
|
13
|
+
|
|
14
|
+
type RoundTripFixture = {
|
|
15
|
+
bigintValue: bigint;
|
|
16
|
+
booleanValue: boolean;
|
|
17
|
+
circular: Record<string, unknown>;
|
|
18
|
+
dateValue: Date;
|
|
19
|
+
falseValue: boolean;
|
|
20
|
+
mapValue: Map<unknown, unknown>;
|
|
21
|
+
nested: {
|
|
22
|
+
array: unknown[];
|
|
23
|
+
object: {
|
|
24
|
+
child: unknown;
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
nullValue: null;
|
|
28
|
+
numberValue: number;
|
|
29
|
+
rscProxy: React.ReactElement & Record<PropertyKey, unknown>;
|
|
30
|
+
setValue: Set<unknown>;
|
|
31
|
+
sharedA: Record<string, unknown>;
|
|
32
|
+
sharedB: Record<string, unknown>;
|
|
33
|
+
stringValue: string;
|
|
34
|
+
undefinedValue: undefined;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
function createStream(): ServerComponentStream {
|
|
38
|
+
return {
|
|
39
|
+
createReplayStream: () => new ReadableStream<Uint8Array>(),
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
test('round-trips TanStack RSC flight values across supported value types', async () => {
|
|
44
|
+
const shared = { marker: 'shared' };
|
|
45
|
+
const circular: Record<string, unknown> = { marker: 'circular' };
|
|
46
|
+
circular.self = circular;
|
|
47
|
+
|
|
48
|
+
const mapValue = new Map<unknown, unknown>([
|
|
49
|
+
['primitive', 1],
|
|
50
|
+
['shared', shared],
|
|
51
|
+
[shared, circular],
|
|
52
|
+
]);
|
|
53
|
+
mapValue.set('self', mapValue);
|
|
54
|
+
|
|
55
|
+
const setValue = new Set<unknown>(['set-item', shared, circular]);
|
|
56
|
+
setValue.add(setValue);
|
|
57
|
+
|
|
58
|
+
const rscProxy = createRscProxy(() => ({ slot: 'server' }), {
|
|
59
|
+
renderable: true,
|
|
60
|
+
stream: createStream(),
|
|
61
|
+
}) as React.ReactElement & Record<PropertyKey, unknown>;
|
|
62
|
+
|
|
63
|
+
const fixture: RoundTripFixture = {
|
|
64
|
+
bigintValue: 9007199254740993n,
|
|
65
|
+
booleanValue: true,
|
|
66
|
+
circular,
|
|
67
|
+
dateValue: new Date('2026-07-08T12:34:56.789Z'),
|
|
68
|
+
falseValue: false,
|
|
69
|
+
mapValue,
|
|
70
|
+
nested: {
|
|
71
|
+
array: [shared, [null, undefined, 7n]],
|
|
72
|
+
object: {
|
|
73
|
+
child: shared,
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
nullValue: null,
|
|
77
|
+
numberValue: 42,
|
|
78
|
+
rscProxy,
|
|
79
|
+
setValue,
|
|
80
|
+
sharedA: shared,
|
|
81
|
+
sharedB: shared,
|
|
82
|
+
stringValue: 'flight',
|
|
83
|
+
undefinedValue: undefined,
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const serialized = serializeTanstackRscFlightValues(
|
|
87
|
+
fixture,
|
|
88
|
+
) as RoundTripFixture;
|
|
89
|
+
const revived = (await reviveTanstackRscFlightValues(
|
|
90
|
+
serialized,
|
|
91
|
+
)) as RoundTripFixture;
|
|
92
|
+
|
|
93
|
+
expect(revived).not.toBe(fixture);
|
|
94
|
+
expect(revived.stringValue).toBe('flight');
|
|
95
|
+
expect(revived.numberValue).toBe(42);
|
|
96
|
+
expect(revived.booleanValue).toBe(true);
|
|
97
|
+
expect(revived.falseValue).toBe(false);
|
|
98
|
+
expect(revived.nullValue).toBeNull();
|
|
99
|
+
expect(Object.hasOwn(revived, 'undefinedValue')).toBe(true);
|
|
100
|
+
expect(revived.undefinedValue).toBeUndefined();
|
|
101
|
+
expect(revived.bigintValue).toBe(9007199254740993n);
|
|
102
|
+
expect(revived.dateValue).toBeInstanceOf(Date);
|
|
103
|
+
expect(revived.dateValue.toISOString()).toBe('2026-07-08T12:34:56.789Z');
|
|
104
|
+
|
|
105
|
+
expect(revived.nested.array[0]).toBe(revived.sharedA);
|
|
106
|
+
expect(revived.nested.object.child).toBe(revived.sharedA);
|
|
107
|
+
expect(revived.sharedB).toBe(revived.sharedA);
|
|
108
|
+
|
|
109
|
+
expect(revived.circular).not.toBe(circular);
|
|
110
|
+
expect(revived.circular.self).toBe(revived.circular);
|
|
111
|
+
|
|
112
|
+
expect(revived.mapValue).toBeInstanceOf(Map);
|
|
113
|
+
expect(revived.mapValue).not.toBe(mapValue);
|
|
114
|
+
expect(revived.mapValue.get('primitive')).toBe(1);
|
|
115
|
+
expect(revived.mapValue.get('shared')).toBe(revived.sharedA);
|
|
116
|
+
expect(revived.mapValue.get(revived.sharedA)).toBe(revived.circular);
|
|
117
|
+
expect(revived.mapValue.get('self')).toBe(revived.mapValue);
|
|
118
|
+
|
|
119
|
+
expect(revived.setValue).toBeInstanceOf(Set);
|
|
120
|
+
expect(revived.setValue).not.toBe(setValue);
|
|
121
|
+
expect(revived.setValue.has('set-item')).toBe(true);
|
|
122
|
+
expect(revived.setValue.has(revived.sharedA)).toBe(true);
|
|
123
|
+
expect(revived.setValue.has(revived.circular)).toBe(true);
|
|
124
|
+
expect(revived.setValue.has(revived.setValue)).toBe(true);
|
|
125
|
+
|
|
126
|
+
expect(serialized.rscProxy).not.toBe(rscProxy);
|
|
127
|
+
expect(serialized.rscProxy).toMatchObject({
|
|
128
|
+
__modernTanstackRsc: true,
|
|
129
|
+
kind: 'renderable',
|
|
130
|
+
});
|
|
131
|
+
expect(serialized.rscProxy.stream).toBeInstanceOf(ReadableStream);
|
|
132
|
+
|
|
133
|
+
expect(revived.rscProxy).not.toBe(rscProxy);
|
|
134
|
+
expect(isValidElement(revived.rscProxy)).toBe(true);
|
|
135
|
+
expect(revived.rscProxy[RENDERABLE_RSC]).toBe(true);
|
|
136
|
+
expect(revived.rscProxy[SERVER_COMPONENT_STREAM]).toBeDefined();
|
|
137
|
+
});
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
// Behavioral coverage for the headless routes-generate entry point: the
|
|
2
|
+
// export-shape test in cli.test.ts would pass even if the body were
|
|
3
|
+
// reverted, so this file pins the actual prepare-only drive contract —
|
|
4
|
+
// createRunOptions against the app dir, cli.init with build-command
|
|
5
|
+
// semantics, the MODERN_ARGV gate that makes analyze.onPrepare fire
|
|
6
|
+
// generateEntryCode, and the absolute config-file resolution that keeps
|
|
7
|
+
// headless runs (cwd != appDirectory) from feeding `false` into node's
|
|
8
|
+
// path APIs.
|
|
9
|
+
const createRunOptionsMock = rstest.fn(async (options: unknown) => ({
|
|
10
|
+
mockRunOptions: true,
|
|
11
|
+
received: options,
|
|
12
|
+
}));
|
|
13
|
+
const cliInitMock = rstest.fn(async (options: unknown) => ({
|
|
14
|
+
appContext: {},
|
|
15
|
+
received: options,
|
|
16
|
+
}));
|
|
17
|
+
|
|
18
|
+
rstest.mock('@modern-js/app-tools/cli/run', () => ({
|
|
19
|
+
createRunOptions: createRunOptionsMock,
|
|
20
|
+
}));
|
|
21
|
+
rstest.mock('@modern-js/plugin/cli', () => ({
|
|
22
|
+
cli: { init: cliInitMock },
|
|
23
|
+
}));
|
|
24
|
+
|
|
25
|
+
import fs from 'node:fs';
|
|
26
|
+
import os from 'node:os';
|
|
27
|
+
import path from 'node:path';
|
|
28
|
+
import { INTERNAL_RUNTIME_PLUGINS } from '@modern-js/utils';
|
|
29
|
+
import { generateTanstackRouteArtifacts } from '../../src/cli';
|
|
30
|
+
|
|
31
|
+
function makeAppDir(withConfig: boolean): string {
|
|
32
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'tanstack-routes-gen-'));
|
|
33
|
+
if (withConfig) {
|
|
34
|
+
fs.writeFileSync(
|
|
35
|
+
path.join(dir, 'modern.config.ts'),
|
|
36
|
+
'export default {};\n',
|
|
37
|
+
'utf-8',
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
return dir;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
describe('generateTanstackRouteArtifacts (headless routes-generate)', () => {
|
|
44
|
+
beforeEach(() => {
|
|
45
|
+
createRunOptionsMock.mockClear();
|
|
46
|
+
cliInitMock.mockClear();
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('drives a prepare-only cli.init with build-command semantics', async () => {
|
|
50
|
+
const previousArgv = process.env.MODERN_ARGV;
|
|
51
|
+
const appDirectory = makeAppDir(true);
|
|
52
|
+
|
|
53
|
+
try {
|
|
54
|
+
await generateTanstackRouteArtifacts({
|
|
55
|
+
appDirectory,
|
|
56
|
+
version: '9.9.9-test',
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
expect(createRunOptionsMock).toHaveBeenCalledTimes(1);
|
|
60
|
+
const runOptionsArg = createRunOptionsMock.mock.calls[0][0] as Record<
|
|
61
|
+
string,
|
|
62
|
+
unknown
|
|
63
|
+
>;
|
|
64
|
+
expect(runOptionsArg.cwd).toBe(appDirectory);
|
|
65
|
+
expect(runOptionsArg.version).toBe('9.9.9-test');
|
|
66
|
+
expect(runOptionsArg.internalPlugins).toBe(INTERNAL_RUNTIME_PLUGINS);
|
|
67
|
+
|
|
68
|
+
// The config file is resolved to an absolute base path against the app
|
|
69
|
+
// directory (not process.cwd()). Without this, a headless run from a
|
|
70
|
+
// different cwd finds no config, `findExists` returns `false`, and that
|
|
71
|
+
// boolean reaches `path.isAbsolute` deep inside plugin/cli.
|
|
72
|
+
expect(runOptionsArg.configFile).toBe(
|
|
73
|
+
path.resolve(appDirectory, 'modern.config'),
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
expect(cliInitMock).toHaveBeenCalledTimes(1);
|
|
77
|
+
const initArg = cliInitMock.mock.calls[0][0] as Record<string, unknown>;
|
|
78
|
+
expect(initArg.command).toBe('build');
|
|
79
|
+
expect(initArg.mockRunOptions).toBe(true);
|
|
80
|
+
|
|
81
|
+
// checkIsBuildCommands() resolves the command from MODERN_ARGV — this
|
|
82
|
+
// is what lets analyze.onPrepare fire generateEntryCode headlessly.
|
|
83
|
+
expect(process.env.MODERN_ARGV).toBe('node modern build');
|
|
84
|
+
} finally {
|
|
85
|
+
fs.rmSync(appDirectory, { force: true, recursive: true });
|
|
86
|
+
if (previousArgv === undefined) {
|
|
87
|
+
delete process.env.MODERN_ARGV;
|
|
88
|
+
} else {
|
|
89
|
+
process.env.MODERN_ARGV = previousArgv;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('throws a clear error (never a boolean path crash) when no config file exists', async () => {
|
|
95
|
+
const appDirectory = makeAppDir(false);
|
|
96
|
+
|
|
97
|
+
try {
|
|
98
|
+
await expect(
|
|
99
|
+
generateTanstackRouteArtifacts({ appDirectory }),
|
|
100
|
+
).rejects.toThrow(/Unable to locate a Modern\.js config file/u);
|
|
101
|
+
expect(createRunOptionsMock).not.toHaveBeenCalled();
|
|
102
|
+
} finally {
|
|
103
|
+
fs.rmSync(appDirectory, { force: true, recursive: true });
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
});
|
|
@@ -3,8 +3,10 @@ import {
|
|
|
3
3
|
routerProviderRegistryHooks,
|
|
4
4
|
} from '@modern-js/runtime/context';
|
|
5
5
|
import * as tanstackHooks from '../../src/runtime/hooks';
|
|
6
|
+
import { applyRouterRuntimeState } from '../../src/runtime/lifecycle';
|
|
6
7
|
import { tanstackRouterPlugin as browserPlugin } from '../../src/runtime/plugin';
|
|
7
8
|
import { tanstackRouterPlugin as nodePlugin } from '../../src/runtime/plugin.node';
|
|
9
|
+
import { getTanstackRouterState } from '../../src/runtime/state';
|
|
8
10
|
|
|
9
11
|
describe('tanstack router hooks single declaration source', () => {
|
|
10
12
|
test('re-exports the canonical hook instances owned by @modern-js/runtime', () => {
|
|
@@ -24,3 +26,15 @@ describe('tanstack router hooks single declaration source', () => {
|
|
|
24
26
|
expect(nodePlugin().registryHooks).toBe(routerProviderRegistryHooks);
|
|
25
27
|
});
|
|
26
28
|
});
|
|
29
|
+
|
|
30
|
+
describe('tanstack router runtime state accessor', () => {
|
|
31
|
+
test('returns undefined before a tanstack router instance is created', () => {
|
|
32
|
+
const context = {};
|
|
33
|
+
|
|
34
|
+
applyRouterRuntimeState(context as never, {
|
|
35
|
+
framework: 'tanstack',
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
expect(getTanstackRouterState(context)).toBeUndefined();
|
|
39
|
+
});
|
|
40
|
+
});
|
|
@@ -117,6 +117,27 @@ describe('modernLoaderToTanstack', () => {
|
|
|
117
117
|
expect(seen.context).toEqual({ user: 'u1' });
|
|
118
118
|
});
|
|
119
119
|
|
|
120
|
+
test('uses current location when context carries a previous request', async () => {
|
|
121
|
+
const seen: { request?: Request } = {};
|
|
122
|
+
const loader = modernLoaderToTanstack({ hasSplat: false }, (args: any) => {
|
|
123
|
+
seen.request = args.request;
|
|
124
|
+
return { ok: true };
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
await expect(
|
|
128
|
+
loader({
|
|
129
|
+
...baseCtx,
|
|
130
|
+
location: { href: 'http://localhost/products/2?sort=price' },
|
|
131
|
+
context: {
|
|
132
|
+
request: new Request('http://localhost/products/1?sort=name'),
|
|
133
|
+
},
|
|
134
|
+
}),
|
|
135
|
+
).resolves.toEqual({ ok: true });
|
|
136
|
+
|
|
137
|
+
expect(seen.request).toBeInstanceOf(Request);
|
|
138
|
+
expect(seen.request?.url).toBe('http://localhost/products/2?sort=price');
|
|
139
|
+
});
|
|
140
|
+
|
|
120
141
|
test('translates an absolute-URL redirect Response into redirect({ href })', async () => {
|
|
121
142
|
const loader = modernLoaderToTanstack({ hasSplat: false }, () =>
|
|
122
143
|
Response.redirect('https://example.com/away', 302),
|
|
@@ -1,79 +1,126 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createMemoryHistory } from '@tanstack/history';
|
|
2
|
+
import {
|
|
3
|
+
createRootRoute,
|
|
4
|
+
createRoute,
|
|
5
|
+
createRouter,
|
|
6
|
+
RouterProvider,
|
|
7
|
+
} from '@tanstack/react-router';
|
|
8
|
+
import { render, waitFor } from '@testing-library/react';
|
|
2
9
|
import React from 'react';
|
|
3
10
|
import { Link, NavLink } from '../../src/runtime/prefetchLink';
|
|
4
11
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
12
|
+
// Real-router harness (no module mocking): STATIC_ACTIVE_PROPS (the
|
|
13
|
+
// TanStack behavior this adapter works around) is injected by
|
|
14
|
+
// useLinkProps() based on live route match state, which a mocked
|
|
15
|
+
// useLinkProps() cannot reproduce faithfully. We tried spying on
|
|
16
|
+
// useLinkProps via rstest.mock()+importActual while re-exporting the rest
|
|
17
|
+
// of the module untouched, but rstest's module-mock hoisting resolves the
|
|
18
|
+
// "actual" import for a module through its own in-progress mock
|
|
19
|
+
// registration, so every named export (createRootRoute included) comes
|
|
20
|
+
// back undefined -- see the sibling prefetchLinkPreload.test.tsx file for
|
|
21
|
+
// the fully-mocked preload-mapping coverage instead. This file renders
|
|
22
|
+
// through an actual createRouter()/RouterProvider tree with in-memory
|
|
23
|
+
// history so the active/inactive aria-current behavior is exercised for
|
|
24
|
+
// real.
|
|
25
|
+
function buildRouter(options: {
|
|
26
|
+
initialPath: string;
|
|
27
|
+
linkProps?: Record<string, unknown>;
|
|
28
|
+
useNavLink?: boolean;
|
|
29
|
+
children?:
|
|
30
|
+
| React.ReactNode
|
|
31
|
+
| ((state: { isActive: boolean }) => React.ReactNode);
|
|
32
|
+
}) {
|
|
33
|
+
// The Link lives on the always-rendered root component (not a route
|
|
34
|
+
// that's only matched at "/settings") so the "inactive link" case -
|
|
35
|
+
// sitting at "/" while linking to "/settings" - actually mounts it.
|
|
36
|
+
const rootRoute = createRootRoute({
|
|
37
|
+
component: () => {
|
|
38
|
+
const Component = options.useNavLink ? NavLink : Link;
|
|
39
|
+
return (
|
|
40
|
+
<Component to="/settings" {...(options.linkProps ?? {})}>
|
|
41
|
+
{options.children ?? 'Settings'}
|
|
42
|
+
</Component>
|
|
43
|
+
);
|
|
44
|
+
},
|
|
22
45
|
});
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
46
|
+
const homeRoute = createRoute({
|
|
47
|
+
getParentRoute: () => rootRoute,
|
|
48
|
+
path: '/',
|
|
49
|
+
component: () => null,
|
|
50
|
+
});
|
|
51
|
+
const settingsRoute = createRoute({
|
|
52
|
+
getParentRoute: () => rootRoute,
|
|
53
|
+
path: '/settings',
|
|
54
|
+
component: () => null,
|
|
28
55
|
});
|
|
29
56
|
|
|
30
|
-
|
|
31
|
-
render(
|
|
32
|
-
<Link to="/settings" prefetch="render" preload="intent">
|
|
33
|
-
Settings
|
|
34
|
-
</Link>,
|
|
35
|
-
);
|
|
57
|
+
const routeTree = rootRoute.addChildren([homeRoute, settingsRoute]);
|
|
36
58
|
|
|
37
|
-
|
|
59
|
+
return createRouter({
|
|
60
|
+
routeTree,
|
|
61
|
+
history: createMemoryHistory({ initialEntries: [options.initialPath] }),
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async function renderLink(options: Parameters<typeof buildRouter>[0]) {
|
|
66
|
+
const router = buildRouter(options);
|
|
67
|
+
const utils = render(<RouterProvider router={router} />);
|
|
68
|
+
|
|
69
|
+
const anchor = await waitFor(() => {
|
|
70
|
+
const found = utils.container.querySelector('a[href="/settings"]');
|
|
71
|
+
if (!found) {
|
|
72
|
+
throw new Error('anchor not rendered yet');
|
|
73
|
+
}
|
|
74
|
+
return found as HTMLAnchorElement;
|
|
38
75
|
});
|
|
39
76
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
<Link to="/settings" prefetch="render" preload={false}>
|
|
43
|
-
Settings
|
|
44
|
-
</Link>,
|
|
45
|
-
);
|
|
77
|
+
return { anchor, ...utils };
|
|
78
|
+
}
|
|
46
79
|
|
|
47
|
-
|
|
80
|
+
describe('tanstack prefetch link adapter - aria-current override', () => {
|
|
81
|
+
it('defaults active link to aria-current="page" when caller passes nothing', async () => {
|
|
82
|
+
const { anchor } = await renderLink({ initialPath: '/settings' });
|
|
83
|
+
expect(anchor.getAttribute('aria-current')).toBe('page');
|
|
48
84
|
});
|
|
49
85
|
|
|
50
|
-
it('
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
);
|
|
56
|
-
|
|
57
|
-
expect(capturedPreloads).toEqual([false]);
|
|
86
|
+
it('lets caller aria-current="true" win over the TanStack-forced value', async () => {
|
|
87
|
+
const { anchor } = await renderLink({
|
|
88
|
+
initialPath: '/settings',
|
|
89
|
+
linkProps: { 'aria-current': 'true' },
|
|
90
|
+
});
|
|
91
|
+
expect(anchor.getAttribute('aria-current')).toBe('true');
|
|
92
|
+
expect(anchor.outerHTML.match(/aria-current=/g)).toHaveLength(1);
|
|
58
93
|
});
|
|
59
94
|
|
|
60
|
-
it
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
Settings
|
|
68
|
-
</Link>,
|
|
69
|
-
);
|
|
95
|
+
it('suppresses aria-current entirely when caller passes false', async () => {
|
|
96
|
+
const { anchor } = await renderLink({
|
|
97
|
+
initialPath: '/settings',
|
|
98
|
+
linkProps: { 'aria-current': false },
|
|
99
|
+
});
|
|
100
|
+
expect(anchor.hasAttribute('aria-current')).toBe(false);
|
|
101
|
+
});
|
|
70
102
|
|
|
71
|
-
|
|
103
|
+
it('leaves an inactive link untouched when caller passes nothing', async () => {
|
|
104
|
+
const { anchor } = await renderLink({ initialPath: '/' });
|
|
105
|
+
expect(anchor.hasAttribute('aria-current')).toBe(false);
|
|
106
|
+
expect(anchor.getAttribute('data-status')).not.toBe('active');
|
|
72
107
|
});
|
|
73
108
|
|
|
74
|
-
it('
|
|
75
|
-
|
|
109
|
+
it('resolves render-prop children with the active state like TanStack Link', async () => {
|
|
110
|
+
const { anchor } = await renderLink({
|
|
111
|
+
initialPath: '/settings',
|
|
112
|
+
children: ({ isActive }: { isActive: boolean }) =>
|
|
113
|
+
isActive ? 'Active settings' : 'Settings',
|
|
114
|
+
});
|
|
115
|
+
expect(anchor.textContent).toBe('Active settings');
|
|
116
|
+
});
|
|
76
117
|
|
|
77
|
-
|
|
118
|
+
it('resolves render-prop children as inactive on a non-matching route', async () => {
|
|
119
|
+
const { anchor } = await renderLink({
|
|
120
|
+
initialPath: '/',
|
|
121
|
+
children: ({ isActive }: { isActive: boolean }) =>
|
|
122
|
+
isActive ? 'Active settings' : 'Settings',
|
|
123
|
+
});
|
|
124
|
+
expect(anchor.textContent).toBe('Settings');
|
|
78
125
|
});
|
|
79
126
|
});
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { render } from '@testing-library/react';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { Link, NavLink } from '../../src/runtime/prefetchLink';
|
|
4
|
+
|
|
5
|
+
type CapturedOptions = {
|
|
6
|
+
preload?: unknown;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
type MockLinkAnchorProps = Record<string, unknown> & {
|
|
10
|
+
href?: string;
|
|
11
|
+
ref?: unknown;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
let capturedOptions: CapturedOptions[] = [];
|
|
15
|
+
let mockReturnProps: MockLinkAnchorProps = { href: '/settings' };
|
|
16
|
+
|
|
17
|
+
rstest.mock('@tanstack/react-router', () => ({
|
|
18
|
+
useLinkProps: (options: CapturedOptions) => {
|
|
19
|
+
capturedOptions.push(options);
|
|
20
|
+
return mockReturnProps;
|
|
21
|
+
},
|
|
22
|
+
}));
|
|
23
|
+
|
|
24
|
+
describe('tanstack prefetch link adapter - preload mapping', () => {
|
|
25
|
+
beforeEach(() => {
|
|
26
|
+
capturedOptions = [];
|
|
27
|
+
mockReturnProps = { href: '/settings' };
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('defaults TanStack preload to viewport', () => {
|
|
31
|
+
render(<Link to="/settings">Settings</Link>);
|
|
32
|
+
|
|
33
|
+
expect(capturedOptions.map(o => o.preload)).toEqual(['viewport']);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('preserves explicit preload', () => {
|
|
37
|
+
render(
|
|
38
|
+
<Link to="/settings" prefetch="render" preload="intent">
|
|
39
|
+
Settings
|
|
40
|
+
</Link>,
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
expect(capturedOptions.map(o => o.preload)).toEqual(['intent']);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('preserves explicit disabled preload', () => {
|
|
47
|
+
render(
|
|
48
|
+
<Link to="/settings" prefetch="render" preload={false}>
|
|
49
|
+
Settings
|
|
50
|
+
</Link>,
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
expect(capturedOptions.map(o => o.preload)).toEqual([false]);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('maps none prefetch to disabled TanStack preload', () => {
|
|
57
|
+
render(
|
|
58
|
+
<Link to="/settings" prefetch="none">
|
|
59
|
+
Settings
|
|
60
|
+
</Link>,
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
expect(capturedOptions.map(o => o.preload)).toEqual([false]);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it.each([
|
|
67
|
+
'intent',
|
|
68
|
+
'render',
|
|
69
|
+
'viewport',
|
|
70
|
+
] as const)('maps %s prefetch to TanStack preload', prefetch => {
|
|
71
|
+
render(
|
|
72
|
+
<Link to="/settings" prefetch={prefetch}>
|
|
73
|
+
Settings
|
|
74
|
+
</Link>,
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
expect(capturedOptions.map(o => o.preload)).toEqual([prefetch]);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('defaults NavLink preload to viewport', () => {
|
|
81
|
+
render(<NavLink to="/settings">Settings</NavLink>);
|
|
82
|
+
|
|
83
|
+
expect(capturedOptions.map(o => o.preload)).toEqual(['viewport']);
|
|
84
|
+
});
|
|
85
|
+
});
|