@bleedingdev/modern-js-plugin-tanstack 3.9.0-ultramodern.4 → 3.9.0-ultramodern.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/cli/index.js +9 -0
- package/dist/cjs/runtime/lifecycle.js +7 -7
- package/dist/cjs/runtime/prefetchLink.js +2 -7
- package/dist/cjs/runtime/register.js +0 -2
- package/dist/cjs/runtime/router.js +25 -9
- package/dist/cjs/runtime/state.js +2 -2
- package/dist/esm/cli/index.mjs +9 -0
- package/dist/esm/runtime/lifecycle.mjs +1 -1
- package/dist/esm/runtime/prefetchLink.mjs +1 -6
- package/dist/esm/runtime/register.mjs +0 -2
- package/dist/esm/runtime/router.mjs +27 -11
- package/dist/esm/runtime/state.mjs +1 -1
- package/dist/esm-node/cli/index.mjs +9 -0
- package/dist/esm-node/runtime/lifecycle.mjs +1 -1
- package/dist/esm-node/runtime/prefetchLink.mjs +1 -6
- package/dist/esm-node/runtime/register.mjs +0 -2
- package/dist/esm-node/runtime/router.mjs +27 -11
- package/dist/esm-node/runtime/state.mjs +1 -1
- package/dist/types/runtime/lifecycle.d.ts +9 -2
- package/dist/types/runtime/register.d.ts +1 -9
- package/dist/types/runtime/router.d.ts +2 -13
- package/dist/types/runtime/state.d.ts +1 -1
- package/dist/types/runtime/types.d.ts +2 -1
- package/package.json +12 -10
- package/rstest.config.mts +0 -21
- package/src/cli/index.ts +10 -0
- package/src/runtime/lifecycle.ts +14 -3
- package/src/runtime/plugin.node.tsx +1 -1
- package/src/runtime/prefetchLink.tsx +1 -23
- package/src/runtime/register.ts +1 -12
- package/src/runtime/router.ts +25 -40
- package/src/runtime/state.ts +1 -1
- package/src/runtime/types.ts +2 -2
- package/tests/realm-isolation.test.ts +7 -17
- package/tests/router/cli.test.ts +105 -8
- package/tests/router/dataMutation.test.tsx +17 -121
- package/tests/router/flightSerialization.roundtrip.test.tsx +9 -6
- package/tests/router/generateRouteArtifacts.test.ts +69 -29
- package/tests/router/hooks.test.ts +1 -18
- package/tests/router/loaderBridge.test.ts +0 -77
- package/tests/router/prefetchLink.test.tsx +28 -16
- package/tests/router/prefetchLinkPreload.test.tsx +21 -28
- package/tests/router/routeHooks.test.ts +41 -105
- package/tests/router/routeTree.test.ts +0 -212
- package/tests/router/rsc.test.tsx +118 -136
- package/tests/router/rscDecoderSecurity.test.ts +24 -53
- package/tests/router/tanstackTypes.test.ts +24 -364
- package/tests/router/fastDefaults.test.ts +0 -32
- package/tests/router/fixtures/legacyRuntimeRouter.ts +0 -10
- package/tests/router/hydrationBoundary.test.tsx +0 -23
- package/tests/router/packageSurface.test.ts +0 -24
- package/tests/router/preloadRedirect.test.ts +0 -64
- package/tests/router/register.test.ts +0 -8
- package/tests/router/routerCompatibility.test.ts +0 -239
- package/tests/router/rscPayloadRouterMatrix.test.ts +0 -197
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
import { createMemoryHistory } from '@tanstack/history';
|
|
2
|
-
import {
|
|
3
|
-
createRootRoute,
|
|
4
|
-
createRoute,
|
|
5
|
-
createRouter,
|
|
6
|
-
redirect,
|
|
7
|
-
} from '@tanstack/react-router';
|
|
8
|
-
|
|
9
|
-
describe('tanstack router-core preload redirects', () => {
|
|
10
|
-
afterEach(() => {
|
|
11
|
-
rstest.restoreAllMocks();
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
test('resolves concurrent preload redirects without stale loader work', async () => {
|
|
15
|
-
const consoleErrorSpy = rstest
|
|
16
|
-
.spyOn(console, 'error')
|
|
17
|
-
.mockImplementation(() => {});
|
|
18
|
-
const rootRoute = createRootRoute();
|
|
19
|
-
const indexRoute = createRoute({
|
|
20
|
-
getParentRoute: () => rootRoute,
|
|
21
|
-
path: '/',
|
|
22
|
-
component: () => null,
|
|
23
|
-
});
|
|
24
|
-
let redirectLoaderCalls = 0;
|
|
25
|
-
const redirectRoute = createRoute({
|
|
26
|
-
getParentRoute: () => rootRoute,
|
|
27
|
-
path: '/redirect',
|
|
28
|
-
loader: async () => {
|
|
29
|
-
redirectLoaderCalls += 1;
|
|
30
|
-
await Promise.resolve();
|
|
31
|
-
throw redirect({ to: '/target' });
|
|
32
|
-
},
|
|
33
|
-
component: () => null,
|
|
34
|
-
});
|
|
35
|
-
const targetLoader = rstest.fn(async () => 'target');
|
|
36
|
-
const targetRoute = createRoute({
|
|
37
|
-
getParentRoute: () => rootRoute,
|
|
38
|
-
path: '/target',
|
|
39
|
-
loader: targetLoader,
|
|
40
|
-
component: () => null,
|
|
41
|
-
});
|
|
42
|
-
const router = createRouter({
|
|
43
|
-
routeTree: rootRoute.addChildren([
|
|
44
|
-
indexRoute,
|
|
45
|
-
redirectRoute,
|
|
46
|
-
targetRoute,
|
|
47
|
-
]),
|
|
48
|
-
history: createMemoryHistory({
|
|
49
|
-
initialEntries: ['/'],
|
|
50
|
-
}),
|
|
51
|
-
defaultStructuralSharing: true,
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
await Promise.all([
|
|
55
|
-
router.preloadRoute({ to: '/redirect' }),
|
|
56
|
-
router.preloadRoute({ to: '/redirect' }),
|
|
57
|
-
]);
|
|
58
|
-
|
|
59
|
-
expect(consoleErrorSpy).not.toHaveBeenCalled();
|
|
60
|
-
expect(router.options.defaultStructuralSharing).toBe(true);
|
|
61
|
-
expect(redirectLoaderCalls).toBe(1);
|
|
62
|
-
expect(targetLoader).toHaveBeenCalledTimes(1);
|
|
63
|
-
});
|
|
64
|
-
});
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import { resolveRouterProvider } from '@modern-js/runtime/context';
|
|
2
|
-
import '../../src/runtime/register';
|
|
3
|
-
|
|
4
|
-
describe("'@modern-js/plugin-tanstack/runtime' import side effects", () => {
|
|
5
|
-
it('registers the tanstack router provider', () => {
|
|
6
|
-
expect(typeof resolveRouterProvider('tanstack')).toBe('function');
|
|
7
|
-
});
|
|
8
|
-
});
|
|
@@ -1,239 +0,0 @@
|
|
|
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
|
-
writeFileSync(
|
|
203
|
-
path.join(tempRoot, 'legacyRuntimeRouter.ts'),
|
|
204
|
-
readFileSync(legacyFixturePath, 'utf8'),
|
|
205
|
-
);
|
|
206
|
-
writeFileSync(
|
|
207
|
-
path.join(tempRoot, 'compatibility.json'),
|
|
208
|
-
JSON.stringify(
|
|
209
|
-
{
|
|
210
|
-
compilerOptions: {
|
|
211
|
-
module: 'Preserve',
|
|
212
|
-
moduleResolution: 'Bundler',
|
|
213
|
-
noEmit: true,
|
|
214
|
-
paths: {
|
|
215
|
-
'@modern-js/runtime/router/internal': [
|
|
216
|
-
'./legacyRuntimeRouter.ts',
|
|
217
|
-
],
|
|
218
|
-
},
|
|
219
|
-
skipLibCheck: false,
|
|
220
|
-
strict: true,
|
|
221
|
-
target: 'ESNext',
|
|
222
|
-
types: [],
|
|
223
|
-
},
|
|
224
|
-
files: [
|
|
225
|
-
declarationPath,
|
|
226
|
-
path.join(tempRoot, 'legacyRuntimeRouter.ts'),
|
|
227
|
-
],
|
|
228
|
-
},
|
|
229
|
-
null,
|
|
230
|
-
2,
|
|
231
|
-
),
|
|
232
|
-
);
|
|
233
|
-
|
|
234
|
-
runTsgo(tsgoBin, path.join(tempRoot, 'compatibility.json'));
|
|
235
|
-
} finally {
|
|
236
|
-
rmSync(tempRoot, { force: true, recursive: true });
|
|
237
|
-
}
|
|
238
|
-
});
|
|
239
|
-
});
|
|
@@ -1,197 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
__setTanstackRscPayloadDecoderForTests,
|
|
3
|
-
createTanstackRscServerPayload,
|
|
4
|
-
handleTanstackRscRedirect,
|
|
5
|
-
loadTanstackRscRouteData,
|
|
6
|
-
} from '../../src/runtime/rsc/payloadRouter';
|
|
7
|
-
|
|
8
|
-
function createPayload(
|
|
9
|
-
routeId: string,
|
|
10
|
-
loaderData: unknown,
|
|
11
|
-
options: { errors?: Record<string, unknown> | null } = {},
|
|
12
|
-
) {
|
|
13
|
-
return {
|
|
14
|
-
type: 'render',
|
|
15
|
-
actionData: null,
|
|
16
|
-
errors: options.errors ?? null,
|
|
17
|
-
loaderData: loaderData === undefined ? {} : { [routeId]: loaderData },
|
|
18
|
-
location: { href: routeId },
|
|
19
|
-
routes: [{ id: routeId, hasLoader: true }],
|
|
20
|
-
};
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
async function decodeJsonPayload(stream: ReadableStream<Uint8Array>) {
|
|
24
|
-
return JSON.parse(await new Response(stream).text());
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
describe('tanstack rsc payload router matrix', () => {
|
|
28
|
-
afterEach(() => {
|
|
29
|
-
__setTanstackRscPayloadDecoderForTests();
|
|
30
|
-
rstest.restoreAllMocks();
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
test('normalizes redirect basenames while preserving status and non-location headers', () => {
|
|
34
|
-
const scenarios = [
|
|
35
|
-
{
|
|
36
|
-
location: '/base',
|
|
37
|
-
name: 'exact basename',
|
|
38
|
-
redirect: '/',
|
|
39
|
-
},
|
|
40
|
-
{
|
|
41
|
-
location: '/base/login?from=%2Fbase',
|
|
42
|
-
name: 'leading basename',
|
|
43
|
-
redirect: '/login?from=%2Fbase',
|
|
44
|
-
},
|
|
45
|
-
{
|
|
46
|
-
location: '/shop/base/login',
|
|
47
|
-
name: 'mid-path basename',
|
|
48
|
-
redirect: '/shop/base/login',
|
|
49
|
-
},
|
|
50
|
-
] as const;
|
|
51
|
-
|
|
52
|
-
for (const scenario of scenarios) {
|
|
53
|
-
const response = handleTanstackRscRedirect(
|
|
54
|
-
new Headers({
|
|
55
|
-
Location: scenario.location,
|
|
56
|
-
'X-Trace': scenario.name,
|
|
57
|
-
}),
|
|
58
|
-
'/base',
|
|
59
|
-
307,
|
|
60
|
-
);
|
|
61
|
-
|
|
62
|
-
expect(response.status).toBe(307);
|
|
63
|
-
expect(response.headers.get('Location')).toBeNull();
|
|
64
|
-
expect(response.headers.get('X-Modernjs-BaseUrl')).toBe('/base');
|
|
65
|
-
expect(response.headers.get('X-Modernjs-Redirect')).toBe(
|
|
66
|
-
scenario.redirect,
|
|
67
|
-
);
|
|
68
|
-
expect(response.headers.get('X-Trace')).toBe(scenario.name);
|
|
69
|
-
}
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
test('separates payload fetch cache entries by URL and request method', async () => {
|
|
73
|
-
const payloads = [
|
|
74
|
-
createPayload('/resource', { method: 'GET' }),
|
|
75
|
-
createPayload('/resource', { method: 'POST' }),
|
|
76
|
-
];
|
|
77
|
-
let payloadIndex = 0;
|
|
78
|
-
const fetchMock = rstest.fn(() =>
|
|
79
|
-
Promise.resolve(new Response(JSON.stringify(payloads[payloadIndex++]))),
|
|
80
|
-
);
|
|
81
|
-
rstest.stubGlobal('fetch', fetchMock);
|
|
82
|
-
__setTanstackRscPayloadDecoderForTests(decodeJsonPayload);
|
|
83
|
-
|
|
84
|
-
const [getData, postData] = await Promise.all([
|
|
85
|
-
loadTanstackRscRouteData({
|
|
86
|
-
loadClientData: async () => ({ fallback: 'get' }),
|
|
87
|
-
request: new Request('http://localhost/resource', { method: 'GET' }),
|
|
88
|
-
routeId: '/resource',
|
|
89
|
-
}),
|
|
90
|
-
loadTanstackRscRouteData({
|
|
91
|
-
loadClientData: async () => ({ fallback: 'post' }),
|
|
92
|
-
request: new Request('http://localhost/resource', { method: 'POST' }),
|
|
93
|
-
routeId: '/resource',
|
|
94
|
-
}),
|
|
95
|
-
]);
|
|
96
|
-
|
|
97
|
-
expect(getData).toEqual({ method: 'GET' });
|
|
98
|
-
expect(postData).toEqual({ method: 'POST' });
|
|
99
|
-
expect(fetchMock).toHaveBeenCalledTimes(2);
|
|
100
|
-
expect(fetchMock.mock.calls.map(call => call[0])).toEqual([
|
|
101
|
-
'http://localhost/resource',
|
|
102
|
-
'http://localhost/resource',
|
|
103
|
-
]);
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
test('rethrows matched route errors from the decoded payload', async () => {
|
|
107
|
-
const routeError = new Error('route loader failed');
|
|
108
|
-
const payload = createPayload('/broken', undefined, {
|
|
109
|
-
errors: { '/broken': routeError },
|
|
110
|
-
});
|
|
111
|
-
rstest.stubGlobal(
|
|
112
|
-
'fetch',
|
|
113
|
-
rstest.fn(() => Promise.resolve(new Response('payload'))),
|
|
114
|
-
);
|
|
115
|
-
__setTanstackRscPayloadDecoderForTests(async () => payload);
|
|
116
|
-
|
|
117
|
-
await expect(
|
|
118
|
-
loadTanstackRscRouteData({
|
|
119
|
-
loadClientData: async () => ({ fallback: true }),
|
|
120
|
-
request: new Request('http://localhost/broken'),
|
|
121
|
-
routeId: '/broken',
|
|
122
|
-
}),
|
|
123
|
-
).rejects.toBe(routeError);
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
test('returns undefined for route ids missing from the decoded payload', async () => {
|
|
127
|
-
const loadClientData = rstest.fn(async () => ({ fallback: true }));
|
|
128
|
-
const payload = createPayload('/known', { known: true });
|
|
129
|
-
rstest.stubGlobal(
|
|
130
|
-
'fetch',
|
|
131
|
-
rstest.fn(() => Promise.resolve(new Response('payload'))),
|
|
132
|
-
);
|
|
133
|
-
__setTanstackRscPayloadDecoderForTests(async () => payload);
|
|
134
|
-
|
|
135
|
-
await expect(
|
|
136
|
-
loadTanstackRscRouteData({
|
|
137
|
-
loadClientData,
|
|
138
|
-
request: new Request('http://localhost/missing'),
|
|
139
|
-
routeId: '/missing',
|
|
140
|
-
}),
|
|
141
|
-
).resolves.toBeUndefined();
|
|
142
|
-
expect(loadClientData).not.toHaveBeenCalled();
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
test('omits server payload matches with no route identity', () => {
|
|
146
|
-
const payload = createTanstackRscServerPayload({
|
|
147
|
-
state: {
|
|
148
|
-
location: { href: '/kept' },
|
|
149
|
-
matches: [
|
|
150
|
-
{
|
|
151
|
-
loaderData: { omitted: true },
|
|
152
|
-
params: {},
|
|
153
|
-
pathname: '/missing-id',
|
|
154
|
-
pathnameBase: '/missing-id',
|
|
155
|
-
route: {
|
|
156
|
-
options: {
|
|
157
|
-
path: 'missing-id',
|
|
158
|
-
staticData: { modernRouteHasLoader: true },
|
|
159
|
-
},
|
|
160
|
-
},
|
|
161
|
-
},
|
|
162
|
-
{
|
|
163
|
-
loaderData: { kept: true },
|
|
164
|
-
params: {},
|
|
165
|
-
pathname: '/kept',
|
|
166
|
-
pathnameBase: '/kept',
|
|
167
|
-
route: {
|
|
168
|
-
id: '/kept',
|
|
169
|
-
options: {
|
|
170
|
-
path: 'kept',
|
|
171
|
-
staticData: { modernRouteHasLoader: true },
|
|
172
|
-
},
|
|
173
|
-
},
|
|
174
|
-
},
|
|
175
|
-
],
|
|
176
|
-
},
|
|
177
|
-
});
|
|
178
|
-
|
|
179
|
-
expect(payload.loaderData).toEqual({ '/kept': { kept: true } });
|
|
180
|
-
expect(payload.routes).toEqual([
|
|
181
|
-
{
|
|
182
|
-
handle: undefined,
|
|
183
|
-
hasAction: false,
|
|
184
|
-
hasClientLoader: false,
|
|
185
|
-
hasErrorBoundary: false,
|
|
186
|
-
hasLoader: true,
|
|
187
|
-
id: '/kept',
|
|
188
|
-
index: undefined,
|
|
189
|
-
params: {},
|
|
190
|
-
parentId: undefined,
|
|
191
|
-
path: 'kept',
|
|
192
|
-
pathname: '/kept',
|
|
193
|
-
pathnameBase: '/kept',
|
|
194
|
-
},
|
|
195
|
-
]);
|
|
196
|
-
});
|
|
197
|
-
});
|