@bleedingdev/modern-js-plugin-tanstack 3.9.0-ultramodern.3 → 3.9.0-ultramodern.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/dist/cjs/cli/index.js +9 -0
  2. package/dist/cjs/cli/tanstackTypes/source.js +3 -1
  3. package/dist/cjs/runtime/basepathRewrite.js +13 -6
  4. package/dist/cjs/runtime/lifecycle.js +7 -7
  5. package/dist/cjs/runtime/plugin.js +1 -1
  6. package/dist/cjs/runtime/plugin.node.js +5 -1
  7. package/dist/cjs/runtime/prefetchLink.js +2 -7
  8. package/dist/cjs/runtime/register.js +2 -2
  9. package/dist/cjs/runtime/routeTree/construction.js +2 -0
  10. package/dist/cjs/runtime/router.js +25 -9
  11. package/dist/cjs/runtime/state.js +2 -2
  12. package/dist/cjs/runtime/utils.js +2 -0
  13. package/dist/esm/cli/index.mjs +9 -0
  14. package/dist/esm/cli/tanstackTypes/source.mjs +3 -1
  15. package/dist/esm/runtime/basepathRewrite.mjs +10 -3
  16. package/dist/esm/runtime/lifecycle.mjs +1 -1
  17. package/dist/esm/runtime/plugin.mjs +1 -1
  18. package/dist/esm/runtime/plugin.node.mjs +5 -1
  19. package/dist/esm/runtime/prefetchLink.mjs +1 -6
  20. package/dist/esm/runtime/register.mjs +1 -1
  21. package/dist/esm/runtime/routeTree/construction.mjs +2 -0
  22. package/dist/esm/runtime/router.mjs +27 -11
  23. package/dist/esm/runtime/state.mjs +1 -1
  24. package/dist/esm/runtime/utils.mjs +2 -0
  25. package/dist/esm-node/cli/index.mjs +9 -0
  26. package/dist/esm-node/cli/tanstackTypes/source.mjs +3 -1
  27. package/dist/esm-node/runtime/basepathRewrite.mjs +10 -3
  28. package/dist/esm-node/runtime/lifecycle.mjs +1 -1
  29. package/dist/esm-node/runtime/plugin.mjs +1 -1
  30. package/dist/esm-node/runtime/plugin.node.mjs +5 -1
  31. package/dist/esm-node/runtime/prefetchLink.mjs +1 -6
  32. package/dist/esm-node/runtime/register.mjs +1 -1
  33. package/dist/esm-node/runtime/routeTree/construction.mjs +2 -0
  34. package/dist/esm-node/runtime/router.mjs +27 -11
  35. package/dist/esm-node/runtime/state.mjs +1 -1
  36. package/dist/esm-node/runtime/utils.mjs +2 -0
  37. package/dist/types/runtime/basepathRewrite.d.ts +2 -1
  38. package/dist/types/runtime/lifecycle.d.ts +9 -2
  39. package/dist/types/runtime/register.d.ts +1 -1
  40. package/dist/types/runtime/router.d.ts +2 -13
  41. package/dist/types/runtime/state.d.ts +1 -1
  42. package/dist/types/runtime/types.d.ts +2 -1
  43. package/package.json +12 -9
  44. package/rstest.config.mts +1 -6
  45. package/src/cli/index.ts +10 -0
  46. package/src/cli/tanstackTypes/source.ts +3 -1
  47. package/src/runtime/basepathRewrite.ts +11 -2
  48. package/src/runtime/lifecycle.ts +14 -3
  49. package/src/runtime/plugin.node.tsx +9 -2
  50. package/src/runtime/plugin.tsx +5 -1
  51. package/src/runtime/prefetchLink.tsx +1 -23
  52. package/src/runtime/register.ts +1 -1
  53. package/src/runtime/routeTree/construction.ts +2 -0
  54. package/src/runtime/router.ts +25 -40
  55. package/src/runtime/state.ts +1 -1
  56. package/src/runtime/types.ts +2 -2
  57. package/src/runtime/utils.tsx +3 -1
  58. package/tests/realm-isolation.test.ts +8 -2
  59. package/tests/router/cli.test.ts +108 -9
  60. package/tests/router/dataMutation.test.tsx +17 -121
  61. package/tests/router/flightSerialization.roundtrip.test.tsx +9 -6
  62. package/tests/router/generateRouteArtifacts.test.ts +69 -29
  63. package/tests/router/hooks.test.ts +1 -18
  64. package/tests/router/loaderBridge.test.ts +0 -77
  65. package/tests/router/localisedIdentity.test.tsx +95 -0
  66. package/tests/router/prefetchLink.test.tsx +28 -16
  67. package/tests/router/prefetchLinkPreload.test.tsx +21 -28
  68. package/tests/router/register.test.ts +1 -1
  69. package/tests/router/routeHooks.test.ts +41 -105
  70. package/tests/router/routeTree.test.ts +0 -212
  71. package/tests/router/routerCompatibility.test.ts +32 -9
  72. package/tests/router/rsc.test.tsx +118 -136
  73. package/tests/router/rscDecoderSecurity.test.ts +24 -53
  74. package/tests/router/serverPlugin.test.ts +43 -1
  75. package/tests/router/tanstackTypes.test.ts +24 -364
  76. package/tests/router/fastDefaults.test.ts +0 -32
  77. package/tests/router/hydrationBoundary.test.tsx +0 -23
  78. package/tests/router/packageSurface.test.ts +0 -24
  79. package/tests/router/preloadRedirect.test.ts +0 -64
  80. package/tests/router/rscPayloadRouterMatrix.test.ts +0 -197
@@ -1,23 +0,0 @@
1
- import { isValidElement, Suspense } from 'react';
2
- import { wrapTanstackSsrHydrationBoundary } from '../../src/runtime/hydrationBoundary';
3
-
4
- describe('tanstack SSR hydration boundary', () => {
5
- it('wraps SSR hydration content in a Suspense boundary', () => {
6
- const routerContent = <main data-testid="router" />;
7
-
8
- const wrapped = wrapTanstackSsrHydrationBoundary(routerContent, true);
9
-
10
- expect(isValidElement(wrapped)).toBe(true);
11
- expect(wrapped.type).toBe(Suspense);
12
- expect(wrapped.props.fallback).toBe(null);
13
- expect(wrapped.props.children).toBe(routerContent);
14
- });
15
-
16
- it('keeps non-SSR router content unwrapped', () => {
17
- const routerContent = <main data-testid="router" />;
18
-
19
- expect(wrapTanstackSsrHydrationBoundary(routerContent, false)).toBe(
20
- routerContent,
21
- );
22
- });
23
- });
@@ -1,24 +0,0 @@
1
- import { readFileSync } from 'node:fs';
2
- import path from 'node:path';
3
-
4
- describe('tanstack package public surface', () => {
5
- test('package manifest exposes the runtime subpath used by app fixtures', () => {
6
- const packageJson = JSON.parse(
7
- readFileSync(path.join(__dirname, '../../package.json'), 'utf-8'),
8
- ) as {
9
- exports: Record<string, unknown>;
10
- typesVersions?: Record<string, Record<string, string[]>>;
11
- };
12
-
13
- expect(packageJson.exports['./runtime']).toEqual({
14
- types: './dist/types/runtime/index.d.ts',
15
- node: {
16
- module: './dist/esm/runtime/index.mjs',
17
- },
18
- default: './dist/esm/runtime/index.mjs',
19
- });
20
- expect(packageJson.typesVersions?.['*']?.runtime).toEqual([
21
- './dist/types/runtime/index.d.ts',
22
- ]);
23
- });
24
- });
@@ -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,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
- });