@bleedingdev/modern-js-plugin-tanstack 3.5.0-ultramodern.9 → 3.5.0-ultramodern.91
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
package/src/runtime/utils.tsx
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
// @effect-diagnostics strictBooleanExpressions:off
|
|
2
2
|
import type { RouteObject } from '@modern-js/runtime-utils/router';
|
|
3
|
-
import type { NestedRoute, PageRoute, SSRMode } from '@modern-js/types';
|
|
4
3
|
import React from 'react';
|
|
4
|
+
import type { ModernRoute } from './types';
|
|
5
|
+
|
|
6
|
+
export type SSRMode = 'string' | 'stream';
|
|
5
7
|
|
|
6
8
|
type RouterConfig = {
|
|
7
9
|
routesConfig: {
|
|
8
10
|
globalApp?: React.ComponentType<any>;
|
|
9
|
-
routes?:
|
|
11
|
+
routes?: ModernRoute[];
|
|
10
12
|
};
|
|
11
13
|
};
|
|
12
14
|
|
|
@@ -19,101 +21,91 @@ type GlobalAppProps = {
|
|
|
19
21
|
[key: string]: unknown;
|
|
20
22
|
};
|
|
21
23
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
+
type ModernRouteObject = RouteObject & {
|
|
25
|
+
Component?: React.ComponentType | string;
|
|
26
|
+
action?: unknown;
|
|
27
|
+
clientData?: unknown;
|
|
28
|
+
config?: { handle?: Record<string, unknown> } | unknown;
|
|
29
|
+
handle?: Record<string, unknown>;
|
|
30
|
+
hasAction?: boolean;
|
|
24
31
|
hasClientLoader?: boolean;
|
|
25
32
|
hasLoader?: boolean;
|
|
26
|
-
hasAction?: boolean;
|
|
27
33
|
inValidSSRRoute?: boolean;
|
|
34
|
+
isClientComponent?: boolean;
|
|
28
35
|
lazyImport?: () => Promise<{ default: React.ComponentType }>;
|
|
29
36
|
};
|
|
30
37
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
38
|
-
globalApp?: React.ComponentType<GlobalAppProps>;
|
|
39
|
-
ssrMode?: SSRMode;
|
|
40
|
-
props?: Record<string, unknown>;
|
|
41
|
-
},
|
|
42
|
-
) {
|
|
43
|
-
const createLayoutElement = (
|
|
44
|
-
Component: React.ComponentType,
|
|
45
|
-
): React.ComponentType => {
|
|
46
|
-
const GlobalLayout = globalApp;
|
|
47
|
-
if (!GlobalLayout) {
|
|
48
|
-
return Component;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
const LayoutWrapper = (props: LayoutWrapperProps) => {
|
|
52
|
-
const LayoutComponent = GlobalLayout;
|
|
53
|
-
return <LayoutComponent Component={Component} {...props} />;
|
|
54
|
-
};
|
|
38
|
+
function withGlobalLayout(
|
|
39
|
+
Component: React.ComponentType,
|
|
40
|
+
globalApp?: React.ComponentType<GlobalAppProps>,
|
|
41
|
+
): React.ComponentType {
|
|
42
|
+
if (!globalApp) {
|
|
43
|
+
return Component;
|
|
44
|
+
}
|
|
55
45
|
|
|
56
|
-
|
|
46
|
+
const GlobalLayout = globalApp;
|
|
47
|
+
return function LayoutWrapper(props: LayoutWrapperProps) {
|
|
48
|
+
return <GlobalLayout Component={Component} {...props} />;
|
|
57
49
|
};
|
|
50
|
+
}
|
|
58
51
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
id: route.id,
|
|
66
|
-
loader: route.loader,
|
|
67
|
-
action: route.action,
|
|
68
|
-
shouldRevalidate: route.shouldRevalidate,
|
|
69
|
-
handle: {
|
|
70
|
-
...route.handle,
|
|
71
|
-
...(typeof route.config === 'object' ? route.config?.handle : {}),
|
|
72
|
-
},
|
|
73
|
-
index: route.index,
|
|
74
|
-
hasLoader: Boolean(route.loader),
|
|
75
|
-
hasClientLoader: Boolean(route.clientData),
|
|
76
|
-
hasAction: Boolean(route.action),
|
|
77
|
-
...(route.isClientComponent ? { isClientComponent: true } : {}),
|
|
78
|
-
...(route.inValidSSRRoute ? { inValidSSRRoute: true } : {}),
|
|
79
|
-
lazyImport: route.lazyImport,
|
|
80
|
-
Component: route.component ? route.component : undefined,
|
|
81
|
-
errorElement: route.error ? <route.error /> : undefined,
|
|
82
|
-
children: route.children
|
|
83
|
-
? route.children.map(
|
|
84
|
-
child =>
|
|
85
|
-
getRouteObjects([child], { globalApp, ssrMode, props })[0],
|
|
86
|
-
)
|
|
87
|
-
: undefined,
|
|
88
|
-
} as ModernRouteObject;
|
|
52
|
+
function getRouteHandle(route: ModernRoute) {
|
|
53
|
+
return {
|
|
54
|
+
...route.handle,
|
|
55
|
+
...(typeof route.config === 'object' ? route.config?.handle : {}),
|
|
56
|
+
};
|
|
57
|
+
}
|
|
89
58
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
59
|
+
function toTanstackRouteObject(
|
|
60
|
+
route: ModernRoute,
|
|
61
|
+
globalApp?: React.ComponentType<GlobalAppProps>,
|
|
62
|
+
): RouteObject | null {
|
|
63
|
+
if (route.type === 'nested') {
|
|
64
|
+
return {
|
|
65
|
+
path: route.path,
|
|
66
|
+
id: route.id,
|
|
67
|
+
loader: route.loader,
|
|
68
|
+
action: route.action,
|
|
69
|
+
shouldRevalidate: route.shouldRevalidate,
|
|
70
|
+
handle: getRouteHandle(route),
|
|
71
|
+
index: route.index,
|
|
72
|
+
hasLoader: route.hasLoader || Boolean(route.loader),
|
|
73
|
+
hasClientLoader: route.hasClientLoader || Boolean(route.clientData),
|
|
74
|
+
hasAction: route.hasAction || Boolean(route.action),
|
|
75
|
+
...(route.isClientComponent ? { isClientComponent: true } : {}),
|
|
76
|
+
...(route.inValidSSRRoute ? { inValidSSRRoute: true } : {}),
|
|
77
|
+
lazyImport: route.lazyImport,
|
|
78
|
+
Component: route.component ? route.component : undefined,
|
|
79
|
+
errorElement: route.error
|
|
80
|
+
? React.createElement(route.error as React.ComponentType)
|
|
81
|
+
: undefined,
|
|
82
|
+
children: route.children
|
|
83
|
+
? route.children
|
|
84
|
+
.map(child => toTanstackRouteObject(child, globalApp))
|
|
85
|
+
.filter((child): child is RouteObject => child !== null)
|
|
86
|
+
: undefined,
|
|
87
|
+
} as ModernRouteObject;
|
|
88
|
+
}
|
|
102
89
|
|
|
103
|
-
|
|
104
|
-
|
|
90
|
+
if (
|
|
91
|
+
typeof route.component !== 'function' &&
|
|
92
|
+
typeof route.component !== 'object'
|
|
93
|
+
) {
|
|
94
|
+
return null;
|
|
105
95
|
}
|
|
106
96
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
97
|
+
const LayoutComponent = withGlobalLayout(
|
|
98
|
+
route.component as React.ComponentType,
|
|
99
|
+
globalApp,
|
|
100
|
+
);
|
|
101
|
+
return {
|
|
102
|
+
path: route.path,
|
|
103
|
+
element: React.createElement(LayoutComponent),
|
|
104
|
+
};
|
|
111
105
|
}
|
|
112
106
|
|
|
113
|
-
export function
|
|
107
|
+
export function createTanstackRouteObjectsFromConfig({
|
|
114
108
|
routesConfig,
|
|
115
|
-
props,
|
|
116
|
-
ssrMode,
|
|
117
109
|
}: {
|
|
118
110
|
routesConfig: RouterConfig['routesConfig'];
|
|
119
111
|
props?: Record<string, unknown>;
|
|
@@ -126,33 +118,7 @@ export function createRouteObjectsFromConfig({
|
|
|
126
118
|
if (!routes) {
|
|
127
119
|
return null;
|
|
128
120
|
}
|
|
129
|
-
return
|
|
130
|
-
globalApp
|
|
131
|
-
|
|
132
|
-
props,
|
|
133
|
-
});
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
export const urlJoin = (...parts: string[]) => {
|
|
137
|
-
const separator = '/';
|
|
138
|
-
const replace = new RegExp(`${separator}{1,}`, 'g');
|
|
139
|
-
return standardSlash(parts.join(separator).replace(replace, separator));
|
|
140
|
-
};
|
|
141
|
-
|
|
142
|
-
export function standardSlash(str: string) {
|
|
143
|
-
let addr = str;
|
|
144
|
-
if (!addr || typeof addr !== 'string') {
|
|
145
|
-
return addr;
|
|
146
|
-
}
|
|
147
|
-
if (addr.startsWith('.')) {
|
|
148
|
-
addr = addr.slice(1);
|
|
149
|
-
}
|
|
150
|
-
if (!addr.startsWith('/')) {
|
|
151
|
-
addr = `/${addr}`;
|
|
152
|
-
}
|
|
153
|
-
if (addr.endsWith('/') && addr !== '/') {
|
|
154
|
-
addr = addr.slice(0, addr.length - 1);
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
return addr;
|
|
121
|
+
return routes
|
|
122
|
+
.map(route => toTanstackRouteObject(route, globalApp))
|
|
123
|
+
.filter((route): route is RouteObject => route !== null);
|
|
158
124
|
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
declare module 'react-server-dom-rspack/client.edge' {
|
|
2
|
+
import type { TemporaryReferenceSet } from 'react-server-dom-rspack';
|
|
3
|
+
|
|
4
|
+
export type { TemporaryReferenceSet };
|
|
5
|
+
|
|
6
|
+
export const createTemporaryReferenceSet: (
|
|
7
|
+
...args: unknown[]
|
|
8
|
+
) => TemporaryReferenceSet;
|
|
9
|
+
|
|
10
|
+
export function createServerReference(
|
|
11
|
+
id: string,
|
|
12
|
+
): (...args: unknown[]) => Promise<unknown>;
|
|
13
|
+
|
|
14
|
+
export type EncodeFormActionCallback = <A>(
|
|
15
|
+
id: unknown,
|
|
16
|
+
args: Promise<A>,
|
|
17
|
+
) => ReactCustomFormAction;
|
|
18
|
+
|
|
19
|
+
export type ReactCustomFormAction = {
|
|
20
|
+
name?: string;
|
|
21
|
+
action?: string;
|
|
22
|
+
encType?: string;
|
|
23
|
+
method?: string;
|
|
24
|
+
target?: string;
|
|
25
|
+
data?: null | FormData;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export interface Options {
|
|
29
|
+
nonce?: string;
|
|
30
|
+
encodeFormAction?: EncodeFormActionCallback;
|
|
31
|
+
temporaryReferences?: TemporaryReferenceSet;
|
|
32
|
+
replayConsoleLogs?: boolean;
|
|
33
|
+
environmentName?: string;
|
|
34
|
+
debugChannel?: {
|
|
35
|
+
readable?: ReadableStream;
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function createFromFetch<T>(
|
|
40
|
+
promiseForResponse: Promise<Response>,
|
|
41
|
+
options?: Options,
|
|
42
|
+
): Promise<T>;
|
|
43
|
+
|
|
44
|
+
export function createFromReadableStream<T>(
|
|
45
|
+
stream: ReadableStream,
|
|
46
|
+
options?: Options,
|
|
47
|
+
): Promise<T>;
|
|
48
|
+
|
|
49
|
+
export const encodeReply: (
|
|
50
|
+
value: unknown,
|
|
51
|
+
options?: {
|
|
52
|
+
temporaryReferences?: TemporaryReferenceSet;
|
|
53
|
+
signal?: AbortSignal;
|
|
54
|
+
},
|
|
55
|
+
) => Promise<string | FormData>;
|
|
56
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { readdirSync, readFileSync } from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
|
|
4
|
+
const SRC_DIR = path.join(__dirname, '../src');
|
|
5
|
+
const CAST_RATCHET_BASELINE = 40;
|
|
6
|
+
const CAST_PATTERN = /\bas\s+unknown\s+as\b|\bas\s+any\b/g;
|
|
7
|
+
const SOURCE_FILE_PATTERN = /\.(?:ts|tsx)$/u;
|
|
8
|
+
|
|
9
|
+
type CastLocation = {
|
|
10
|
+
cast: string;
|
|
11
|
+
columnNumber: number;
|
|
12
|
+
filePath: string;
|
|
13
|
+
lineNumber: number;
|
|
14
|
+
sourceLine: string;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const collectSourceFiles = (directory: string): string[] => {
|
|
18
|
+
const entries = readdirSync(directory, { withFileTypes: true }).sort((a, b) =>
|
|
19
|
+
a.name.localeCompare(b.name),
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
return entries.flatMap(entry => {
|
|
23
|
+
const entryPath = path.join(directory, entry.name);
|
|
24
|
+
|
|
25
|
+
if (entry.isDirectory()) {
|
|
26
|
+
return collectSourceFiles(entryPath);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return SOURCE_FILE_PATTERN.test(entry.name) ? [entryPath] : [];
|
|
30
|
+
});
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const collectCastLocations = (): CastLocation[] =>
|
|
34
|
+
collectSourceFiles(SRC_DIR).flatMap(filePath => {
|
|
35
|
+
const relativeFilePath = path
|
|
36
|
+
.relative(SRC_DIR, filePath)
|
|
37
|
+
.split(path.sep)
|
|
38
|
+
.join('/');
|
|
39
|
+
const lines = readFileSync(filePath, 'utf-8').split(/\r?\n/u);
|
|
40
|
+
const locations: CastLocation[] = [];
|
|
41
|
+
|
|
42
|
+
lines.forEach((sourceLine, index) => {
|
|
43
|
+
CAST_PATTERN.lastIndex = 0;
|
|
44
|
+
|
|
45
|
+
let match: RegExpExecArray | null;
|
|
46
|
+
while ((match = CAST_PATTERN.exec(sourceLine))) {
|
|
47
|
+
locations.push({
|
|
48
|
+
cast: match[0],
|
|
49
|
+
columnNumber: match.index + 1,
|
|
50
|
+
filePath: relativeFilePath,
|
|
51
|
+
lineNumber: index + 1,
|
|
52
|
+
sourceLine: sourceLine.trim(),
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
return locations;
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
const formatCastLocations = (locations: CastLocation[]): string =>
|
|
61
|
+
locations
|
|
62
|
+
.map(
|
|
63
|
+
location =>
|
|
64
|
+
` ${location.filePath}:${location.lineNumber}:${location.columnNumber} ${location.cast} :: ${location.sourceLine}`,
|
|
65
|
+
)
|
|
66
|
+
.join('\n');
|
|
67
|
+
|
|
68
|
+
describe('tanstack unsafe cast ratchet', () => {
|
|
69
|
+
test('does not add new as-any casts in src', () => {
|
|
70
|
+
const castLocations = collectCastLocations();
|
|
71
|
+
|
|
72
|
+
if (castLocations.length > CAST_RATCHET_BASELINE) {
|
|
73
|
+
throw new Error(
|
|
74
|
+
[
|
|
75
|
+
`Expected at most ${CAST_RATCHET_BASELINE} unsafe casts in plugin-tanstack src, found ${castLocations.length}.`,
|
|
76
|
+
'Remove the new cast or lower the ratchet baseline after deleting existing casts.',
|
|
77
|
+
'Current cast locations:',
|
|
78
|
+
formatCastLocations(castLocations),
|
|
79
|
+
].join('\n'),
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
expect(castLocations.length).toBeLessThanOrEqual(CAST_RATCHET_BASELINE);
|
|
84
|
+
});
|
|
85
|
+
});
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import type { RouterProviderFactory } from '@modern-js/runtime/context';
|
|
2
|
+
import { rstest } from '@rstest/core';
|
|
3
|
+
import type { RouterExtendsHooks } from '../src/runtime/hooks';
|
|
4
|
+
|
|
5
|
+
const REGISTRY_SLOT: unique symbol = Symbol.for(
|
|
6
|
+
'@modern-js/runtime:router-providers:v3',
|
|
7
|
+
);
|
|
8
|
+
const host = globalThis as { [REGISTRY_SLOT]?: unknown };
|
|
9
|
+
|
|
10
|
+
type RuntimeContextModule = typeof import('@modern-js/runtime/context');
|
|
11
|
+
type RouterModule = typeof import('../src/runtime/router');
|
|
12
|
+
|
|
13
|
+
type Vertical = {
|
|
14
|
+
context: RuntimeContextModule;
|
|
15
|
+
factory: RouterProviderFactory;
|
|
16
|
+
hooks: RouterExtendsHooks;
|
|
17
|
+
Link: unknown;
|
|
18
|
+
routerPlugin: RouterModule['routerPlugin'];
|
|
19
|
+
useMatches: unknown;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
async function loadVertical(): Promise<Vertical> {
|
|
23
|
+
rstest.resetModules();
|
|
24
|
+
const routerModule = (await import('../src/runtime/router')) as RouterModule;
|
|
25
|
+
const context = (await import(
|
|
26
|
+
'@modern-js/runtime/context'
|
|
27
|
+
)) as RuntimeContextModule;
|
|
28
|
+
const { tanstackRouterProviderFactory } = await import(
|
|
29
|
+
'../src/runtime/register'
|
|
30
|
+
);
|
|
31
|
+
const { routerProviderRegistryHooks } = await import('../src/runtime/hooks');
|
|
32
|
+
const { Link } = await import('../src/runtime/prefetchLink');
|
|
33
|
+
const { useMatches } = await import('../src/runtime/routeHooks');
|
|
34
|
+
|
|
35
|
+
return {
|
|
36
|
+
context,
|
|
37
|
+
factory: tanstackRouterProviderFactory,
|
|
38
|
+
hooks: routerProviderRegistryHooks,
|
|
39
|
+
Link,
|
|
40
|
+
routerPlugin: routerModule.routerPlugin,
|
|
41
|
+
useMatches,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function setupRouterWrapper(vertical: Vertical) {
|
|
46
|
+
let onBeforeRender:
|
|
47
|
+
| ((context: any, interrupt: (value?: unknown) => unknown) => unknown)
|
|
48
|
+
| undefined;
|
|
49
|
+
|
|
50
|
+
const plugin = vertical.routerPlugin({ framework: 'tanstack' });
|
|
51
|
+
plugin.setup?.({
|
|
52
|
+
getHooks: () => vertical.hooks,
|
|
53
|
+
getRuntimeConfig: () => ({ router: { framework: 'tanstack' } }),
|
|
54
|
+
onBeforeRender: listener => {
|
|
55
|
+
onBeforeRender = listener;
|
|
56
|
+
},
|
|
57
|
+
wrapRoot: () => undefined,
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
expect(typeof onBeforeRender).toBe('function');
|
|
61
|
+
const runtimeContext: any = {};
|
|
62
|
+
onBeforeRender?.(runtimeContext, () => undefined);
|
|
63
|
+
return runtimeContext.router as { Link: unknown; useMatches: unknown };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
describe('TanStack provider runtime-realm isolation', () => {
|
|
67
|
+
beforeEach(() => {
|
|
68
|
+
delete host[REGISTRY_SLOT];
|
|
69
|
+
rstest.resetModules();
|
|
70
|
+
(
|
|
71
|
+
globalThis as typeof globalThis & {
|
|
72
|
+
__webpack_require__?: { u: (chunkId: unknown) => string };
|
|
73
|
+
}
|
|
74
|
+
).__webpack_require__ = { u: chunkId => String(chunkId) };
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
afterEach(() => {
|
|
78
|
+
delete host[REGISTRY_SLOT];
|
|
79
|
+
rstest.resetModules();
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('binds each production router wrapper to its own real provider graph', async () => {
|
|
83
|
+
const warnSpy = rstest.spyOn(console, 'warn').mockImplementation(() => {});
|
|
84
|
+
try {
|
|
85
|
+
const verticalA = await loadVertical();
|
|
86
|
+
const routerA = setupRouterWrapper(verticalA);
|
|
87
|
+
expect(routerA.Link).toBe(verticalA.Link);
|
|
88
|
+
expect(routerA.useMatches).toBe(verticalA.useMatches);
|
|
89
|
+
|
|
90
|
+
const verticalB = await loadVertical();
|
|
91
|
+
expect(verticalB.context).not.toBe(verticalA.context);
|
|
92
|
+
expect(verticalB.factory).not.toBe(verticalA.factory);
|
|
93
|
+
expect(verticalB.Link).not.toBe(verticalA.Link);
|
|
94
|
+
expect(verticalB.useMatches).not.toBe(verticalA.useMatches);
|
|
95
|
+
|
|
96
|
+
const routerB = setupRouterWrapper(verticalB);
|
|
97
|
+
expect(routerB.Link).toBe(verticalB.Link);
|
|
98
|
+
expect(routerB.useMatches).toBe(verticalB.useMatches);
|
|
99
|
+
expect(routerB.Link).not.toBe(verticalA.Link);
|
|
100
|
+
expect(routerB.useMatches).not.toBe(verticalA.useMatches);
|
|
101
|
+
|
|
102
|
+
// Mixed-version callers that do not supply a realm still see the shared
|
|
103
|
+
// keep-first fallback, but production wrappers no longer consume it.
|
|
104
|
+
expect(verticalB.context.resolveRouterProvider('tanstack')).toBe(
|
|
105
|
+
verticalA.factory,
|
|
106
|
+
);
|
|
107
|
+
expect(warnSpy).toHaveBeenCalledWith(
|
|
108
|
+
expect.stringMatching(/keeping the first registration/),
|
|
109
|
+
);
|
|
110
|
+
} finally {
|
|
111
|
+
warnSpy.mockRestore();
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
});
|
package/tests/router/cli.test.ts
CHANGED
|
@@ -6,6 +6,7 @@ import type { Entrypoint } from '@modern-js/types';
|
|
|
6
6
|
import { fs, NESTED_ROUTE_SPEC_FILE } from '@modern-js/utils';
|
|
7
7
|
import {
|
|
8
8
|
createTanstackRsbuildRouteSplittingProfile,
|
|
9
|
+
generateTanstackRouteArtifacts,
|
|
9
10
|
tanstackRouterPlugin,
|
|
10
11
|
writeTanstackRegisterFile,
|
|
11
12
|
writeTanstackRouterTypesForEntries,
|
|
@@ -977,3 +978,9 @@ describe('tanstack router cli plugin', () => {
|
|
|
977
978
|
});
|
|
978
979
|
});
|
|
979
980
|
});
|
|
981
|
+
|
|
982
|
+
describe('generateTanstackRouteArtifacts export', () => {
|
|
983
|
+
test('is exported as a function', () => {
|
|
984
|
+
expect(typeof generateTanstackRouteArtifacts).toBe('function');
|
|
985
|
+
});
|
|
986
|
+
});
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { AnyRouter } from '@tanstack/react-router';
|
|
2
|
+
import { render, screen } from '@testing-library/react';
|
|
3
|
+
import React, { Suspense } from 'react';
|
|
4
|
+
import { ModernRouterClient } from '../../src/runtime/clientHydration';
|
|
5
|
+
|
|
6
|
+
let hydrateImpl: () => Promise<unknown>;
|
|
7
|
+
|
|
8
|
+
rstest.mock('@tanstack/react-router', () => ({
|
|
9
|
+
RouterProvider: ({ router }: { router: { label: string } }) => (
|
|
10
|
+
<div data-testid="router-provider">{router.label}</div>
|
|
11
|
+
),
|
|
12
|
+
}));
|
|
13
|
+
|
|
14
|
+
rstest.mock('@tanstack/react-router/ssr/client', () => ({
|
|
15
|
+
hydrate: () => hydrateImpl(),
|
|
16
|
+
}));
|
|
17
|
+
|
|
18
|
+
function createDeferred<T>() {
|
|
19
|
+
let resolve!: (value: T | PromiseLike<T>) => void;
|
|
20
|
+
let reject!: (reason?: unknown) => void;
|
|
21
|
+
const promise = new Promise<T>((resolvePromise, rejectPromise) => {
|
|
22
|
+
resolve = resolvePromise;
|
|
23
|
+
reject = rejectPromise;
|
|
24
|
+
});
|
|
25
|
+
return { promise, reject, resolve };
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function createRouter(label: string): AnyRouter {
|
|
29
|
+
return {
|
|
30
|
+
label,
|
|
31
|
+
routesById: {},
|
|
32
|
+
stores: {
|
|
33
|
+
matches: {
|
|
34
|
+
get: () => [],
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
} as unknown as AnyRouter;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
describe('TanStack SSR client hydration', () => {
|
|
41
|
+
it('keeps the router behind Suspense until late SSR hydration completes', async () => {
|
|
42
|
+
const hydration = createDeferred<void>();
|
|
43
|
+
hydrateImpl = () => hydration.promise;
|
|
44
|
+
|
|
45
|
+
render(
|
|
46
|
+
<Suspense fallback={<div data-testid="hydrating">hydrating</div>}>
|
|
47
|
+
<ModernRouterClient router={createRouter('inventory')} />
|
|
48
|
+
</Suspense>,
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
expect(screen.getByTestId('hydrating')).toBeTruthy();
|
|
52
|
+
expect(screen.queryByTestId('router-provider')).toBeNull();
|
|
53
|
+
|
|
54
|
+
hydration.resolve();
|
|
55
|
+
|
|
56
|
+
expect(
|
|
57
|
+
(await screen.findByTestId('router-provider')).textContent,
|
|
58
|
+
).toContain('inventory');
|
|
59
|
+
expect(screen.queryByTestId('hydrating')).toBeNull();
|
|
60
|
+
});
|
|
61
|
+
});
|
|
@@ -2,6 +2,7 @@ import { fireEvent, render, screen, waitFor } from '@testing-library/react';
|
|
|
2
2
|
import React, { act } from 'react';
|
|
3
3
|
import type { Fetcher } from '../../src/runtime/dataMutation';
|
|
4
4
|
import { Form, useFetcher } from '../../src/runtime/dataMutation';
|
|
5
|
+
import { createFormDataFromSubmit } from '../../src/runtime/formData';
|
|
5
6
|
|
|
6
7
|
type RouteHandler = (args: {
|
|
7
8
|
request: Request;
|
|
@@ -276,6 +277,28 @@ describe('tanstack data mutation fetcher', () => {
|
|
|
276
277
|
expect(states).toEqual(['idle', 'loading', 'idle']);
|
|
277
278
|
});
|
|
278
279
|
|
|
280
|
+
test('GET Form navigation replaces action query before hash', async () => {
|
|
281
|
+
currentRouter = createRouter({});
|
|
282
|
+
|
|
283
|
+
render(
|
|
284
|
+
<Form method="get" action="/products?sort=old#results">
|
|
285
|
+
<input name="q" defaultValue="boots" />
|
|
286
|
+
<button type="submit">Search</button>
|
|
287
|
+
</Form>,
|
|
288
|
+
);
|
|
289
|
+
|
|
290
|
+
const form = document.querySelector('form');
|
|
291
|
+
expect(form).toBeTruthy();
|
|
292
|
+
|
|
293
|
+
await act(async () => {
|
|
294
|
+
fireEvent.submit(form!);
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
expect(currentRouter.navigate).toHaveBeenCalledWith({
|
|
298
|
+
href: '/products?q=boots#results',
|
|
299
|
+
});
|
|
300
|
+
});
|
|
301
|
+
|
|
279
302
|
test('keeps non-idle state while overlapping mutation submits are still active', async () => {
|
|
280
303
|
const actionResults = [
|
|
281
304
|
createDeferred<Response>(),
|
|
@@ -361,6 +384,42 @@ describe('tanstack data mutation fetcher', () => {
|
|
|
361
384
|
expect(screen.getByTestId('data').textContent).toBe('{"count":3}');
|
|
362
385
|
});
|
|
363
386
|
|
|
387
|
+
test('preserves submitter name value when FormData submitter overload is unavailable', () => {
|
|
388
|
+
const NativeFormData = globalThis.FormData;
|
|
389
|
+
|
|
390
|
+
class SingleArgumentFormData extends NativeFormData {
|
|
391
|
+
constructor(form?: HTMLFormElement, submitter?: HTMLElement) {
|
|
392
|
+
if (submitter) {
|
|
393
|
+
throw new TypeError('FormData submitter overload unavailable');
|
|
394
|
+
}
|
|
395
|
+
super(form);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
globalThis.FormData = SingleArgumentFormData as typeof FormData;
|
|
400
|
+
|
|
401
|
+
try {
|
|
402
|
+
const form = document.createElement('form');
|
|
403
|
+
form.innerHTML = `
|
|
404
|
+
<input name="amount" value="2" />
|
|
405
|
+
<button type="submit" name="intent" value="save">Save</button>
|
|
406
|
+
`;
|
|
407
|
+
|
|
408
|
+
const submitter = form.querySelector('button');
|
|
409
|
+
expect(submitter).toBeInstanceOf(HTMLButtonElement);
|
|
410
|
+
|
|
411
|
+
const formData = createFormDataFromSubmit({
|
|
412
|
+
form,
|
|
413
|
+
submitter,
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
expect(formData.get('amount')).toBe('2');
|
|
417
|
+
expect(formData.get('intent')).toBe('save');
|
|
418
|
+
} finally {
|
|
419
|
+
globalThis.FormData = NativeFormData;
|
|
420
|
+
}
|
|
421
|
+
});
|
|
422
|
+
|
|
364
423
|
test('does not throw for Form submit with non-2xx response and still invalidates', async () => {
|
|
365
424
|
const action = rstest.fn(
|
|
366
425
|
async () =>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
let invocationCount = 0;
|
|
2
|
+
|
|
3
|
+
export const routerPlugin = (
|
|
4
|
+
_userConfig: Record<string, unknown> = {},
|
|
5
|
+
): { name?: string; setup?: (...args: unknown[]) => unknown } => {
|
|
6
|
+
invocationCount += 1;
|
|
7
|
+
throw new Error('The legacy global router wrapper must not be invoked.');
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export const getLegacyRouterPluginInvocationCount = () => invocationCount;
|