Skip to content

Commit f943abc

Browse files
fix: unset extends of serialization adapters after deduping (#5316)
1 parent 6d46aaf commit f943abc

File tree

8 files changed

+152
-72
lines changed

8 files changed

+152
-72
lines changed

e2e/react-start/serialization-adapters/src/data.tsx

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,3 +157,49 @@ export function RenderData({
157157
</div>
158158
)
159159
}
160+
161+
export function RenderNestedData({ nested }: { nested: NestedOuter }) {
162+
{
163+
const localData = makeNested()
164+
const expectedShoutState = localData.inner.shout()
165+
const expectedWhisperState = localData.whisper()
166+
const shoutState = nested.inner.shout()
167+
const whisperState = nested.whisper()
168+
169+
return (
170+
<div data-testid="data-only-container">
171+
<h2 data-testid="data-only-heading">data-only</h2>
172+
<div data-testid="shout-container">
173+
<h3>shout</h3>
174+
<div>
175+
expected:{' '}
176+
<div data-testid="shout-expected-state">
177+
{JSON.stringify(expectedShoutState)}
178+
</div>
179+
</div>
180+
<div>
181+
actual:{' '}
182+
<div data-testid="shout-actual-state">
183+
{JSON.stringify(shoutState)}
184+
</div>
185+
</div>
186+
</div>
187+
<div data-testid="whisper-container">
188+
<h3>whisper</h3>
189+
<div>
190+
expected:{' '}
191+
<div data-testid="whisper-expected-state">
192+
{JSON.stringify(expectedWhisperState)}
193+
</div>
194+
</div>
195+
<div>
196+
actual:{' '}
197+
<div data-testid="whisper-actual-state">
198+
{JSON.stringify(whisperState)}
199+
</div>
200+
</div>
201+
</div>
202+
</div>
203+
)
204+
}
205+
}

e2e/react-start/serialization-adapters/src/routeTree.gen.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { Route as IndexRouteImport } from './routes/index'
1313
import { Route as SsrStreamRouteImport } from './routes/ssr/stream'
1414
import { Route as SsrNestedRouteImport } from './routes/ssr/nested'
1515
import { Route as SsrDataOnlyRouteImport } from './routes/ssr/data-only'
16+
import { Route as ServerFunctionNestedRouteImport } from './routes/server-function/nested'
1617
import { Route as ServerFunctionCustomErrorRouteImport } from './routes/server-function/custom-error'
1718

1819
const IndexRoute = IndexRouteImport.update({
@@ -35,6 +36,11 @@ const SsrDataOnlyRoute = SsrDataOnlyRouteImport.update({
3536
path: '/ssr/data-only',
3637
getParentRoute: () => rootRouteImport,
3738
} as any)
39+
const ServerFunctionNestedRoute = ServerFunctionNestedRouteImport.update({
40+
id: '/server-function/nested',
41+
path: '/server-function/nested',
42+
getParentRoute: () => rootRouteImport,
43+
} as any)
3844
const ServerFunctionCustomErrorRoute =
3945
ServerFunctionCustomErrorRouteImport.update({
4046
id: '/server-function/custom-error',
@@ -45,13 +51,15 @@ const ServerFunctionCustomErrorRoute =
4551
export interface FileRoutesByFullPath {
4652
'/': typeof IndexRoute
4753
'/server-function/custom-error': typeof ServerFunctionCustomErrorRoute
54+
'/server-function/nested': typeof ServerFunctionNestedRoute
4855
'/ssr/data-only': typeof SsrDataOnlyRoute
4956
'/ssr/nested': typeof SsrNestedRoute
5057
'/ssr/stream': typeof SsrStreamRoute
5158
}
5259
export interface FileRoutesByTo {
5360
'/': typeof IndexRoute
5461
'/server-function/custom-error': typeof ServerFunctionCustomErrorRoute
62+
'/server-function/nested': typeof ServerFunctionNestedRoute
5563
'/ssr/data-only': typeof SsrDataOnlyRoute
5664
'/ssr/nested': typeof SsrNestedRoute
5765
'/ssr/stream': typeof SsrStreamRoute
@@ -60,6 +68,7 @@ export interface FileRoutesById {
6068
__root__: typeof rootRouteImport
6169
'/': typeof IndexRoute
6270
'/server-function/custom-error': typeof ServerFunctionCustomErrorRoute
71+
'/server-function/nested': typeof ServerFunctionNestedRoute
6372
'/ssr/data-only': typeof SsrDataOnlyRoute
6473
'/ssr/nested': typeof SsrNestedRoute
6574
'/ssr/stream': typeof SsrStreamRoute
@@ -69,20 +78,23 @@ export interface FileRouteTypes {
6978
fullPaths:
7079
| '/'
7180
| '/server-function/custom-error'
81+
| '/server-function/nested'
7282
| '/ssr/data-only'
7383
| '/ssr/nested'
7484
| '/ssr/stream'
7585
fileRoutesByTo: FileRoutesByTo
7686
to:
7787
| '/'
7888
| '/server-function/custom-error'
89+
| '/server-function/nested'
7990
| '/ssr/data-only'
8091
| '/ssr/nested'
8192
| '/ssr/stream'
8293
id:
8394
| '__root__'
8495
| '/'
8596
| '/server-function/custom-error'
97+
| '/server-function/nested'
8698
| '/ssr/data-only'
8799
| '/ssr/nested'
88100
| '/ssr/stream'
@@ -91,6 +103,7 @@ export interface FileRouteTypes {
91103
export interface RootRouteChildren {
92104
IndexRoute: typeof IndexRoute
93105
ServerFunctionCustomErrorRoute: typeof ServerFunctionCustomErrorRoute
106+
ServerFunctionNestedRoute: typeof ServerFunctionNestedRoute
94107
SsrDataOnlyRoute: typeof SsrDataOnlyRoute
95108
SsrNestedRoute: typeof SsrNestedRoute
96109
SsrStreamRoute: typeof SsrStreamRoute
@@ -126,6 +139,13 @@ declare module '@tanstack/react-router' {
126139
preLoaderRoute: typeof SsrDataOnlyRouteImport
127140
parentRoute: typeof rootRouteImport
128141
}
142+
'/server-function/nested': {
143+
id: '/server-function/nested'
144+
path: '/server-function/nested'
145+
fullPath: '/server-function/nested'
146+
preLoaderRoute: typeof ServerFunctionNestedRouteImport
147+
parentRoute: typeof rootRouteImport
148+
}
129149
'/server-function/custom-error': {
130150
id: '/server-function/custom-error'
131151
path: '/server-function/custom-error'
@@ -139,6 +159,7 @@ declare module '@tanstack/react-router' {
139159
const rootRouteChildren: RootRouteChildren = {
140160
IndexRoute: IndexRoute,
141161
ServerFunctionCustomErrorRoute: ServerFunctionCustomErrorRoute,
162+
ServerFunctionNestedRoute: ServerFunctionNestedRoute,
142163
SsrDataOnlyRoute: SsrDataOnlyRoute,
143164
SsrNestedRoute: SsrNestedRoute,
144165
SsrStreamRoute: SsrStreamRoute,

e2e/react-start/serialization-adapters/src/routes/index.tsx

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,14 @@ function Home() {
2424
>
2525
Stream
2626
</Link>
27+
<br />
28+
<Link
29+
data-testid="ssr-nested-link"
30+
to="/ssr/nested"
31+
reloadDocument={true}
32+
>
33+
Nested Classes
34+
</Link>
2735
</div>
2836
<div>
2937
<h2>Server Functions</h2>
@@ -34,6 +42,14 @@ function Home() {
3442
>
3543
Custom Error Serialization
3644
</Link>
45+
<br />
46+
<Link
47+
data-testid="server-functions-nested-link"
48+
to="/server-function/nested"
49+
reloadDocument={true}
50+
>
51+
Nested Classes returned from Server Function
52+
</Link>
3753
</div>
3854
</>
3955
)
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { createFileRoute } from '@tanstack/react-router'
2+
import { createServerFn } from '@tanstack/react-start'
3+
import { useState } from 'react'
4+
import type { NestedOuter } from '~/data'
5+
import { RenderNestedData, makeNested } from '~/data'
6+
7+
const serverFnReturningNested = createServerFn().handler(() => {
8+
return makeNested()
9+
})
10+
11+
export const Route = createFileRoute('/server-function/nested')({
12+
component: RouteComponent,
13+
})
14+
15+
function RouteComponent() {
16+
const [nestedResponse, setNestedResponse] = useState<NestedOuter>()
17+
18+
return (
19+
<div>
20+
<button
21+
data-testid="server-function-trigger"
22+
onClick={() => serverFnReturningNested().then(setNestedResponse)}
23+
>
24+
trigger
25+
</button>
26+
27+
{nestedResponse ? (
28+
<RenderNestedData nested={nestedResponse} />
29+
) : (
30+
<div data-testid="waiting-for-response">waiting for response...</div>
31+
)}
32+
</div>
33+
)
34+
}
Lines changed: 2 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { createFileRoute } from '@tanstack/react-router'
2-
import { makeNested } from '~/data'
2+
import { RenderNestedData, makeNested } from '~/data'
33

44
export const Route = createFileRoute('/ssr/nested')({
55
beforeLoad: () => {
@@ -9,48 +9,6 @@ export const Route = createFileRoute('/ssr/nested')({
99
return context
1010
},
1111
component: () => {
12-
const loaderData = Route.useLoaderData()
13-
14-
const localData = makeNested()
15-
const expectedShoutState = localData.inner.shout()
16-
const expectedWhisperState = localData.whisper()
17-
const shoutState = loaderData.nested.inner.shout()
18-
const whisperState = loaderData.nested.whisper()
19-
20-
return (
21-
<div data-testid="data-only-container">
22-
<h2 data-testid="data-only-heading">data-only</h2>
23-
<div data-testid="shout-container">
24-
<h3>shout</h3>
25-
<div>
26-
expected:{' '}
27-
<div data-testid="shout-expected-state">
28-
{JSON.stringify(expectedShoutState)}
29-
</div>
30-
</div>
31-
<div>
32-
actual:{' '}
33-
<div data-testid="shout-actual-state">
34-
{JSON.stringify(shoutState)}
35-
</div>
36-
</div>
37-
</div>
38-
<div data-testid="whisper-container">
39-
<h3>whisper</h3>
40-
<div>
41-
expected:{' '}
42-
<div data-testid="whisper-expected-state">
43-
{JSON.stringify(expectedWhisperState)}
44-
</div>
45-
</div>
46-
<div>
47-
actual:{' '}
48-
<div data-testid="whisper-actual-state">
49-
{JSON.stringify(whisperState)}
50-
</div>
51-
</div>
52-
</div>
53-
</div>
54-
)
12+
return <RenderNestedData nested={Route.useLoaderData().nested} />
5513
},
5614
})

e2e/react-start/serialization-adapters/tests/app.spec.ts

Lines changed: 30 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,24 @@ async function checkData(page: Page, id: string) {
2121
'{"value":"server"}',
2222
)
2323
}
24+
25+
async function checkNestedData(page: Page) {
26+
const expectedShout = await page
27+
.getByTestId(`shout-expected-state`)
28+
.textContent()
29+
expect(expectedShout).not.toBeNull()
30+
await expect(page.getByTestId(`shout-actual-state`)).toContainText(
31+
expectedShout!,
32+
)
33+
34+
const expectedWhisper = await page
35+
.getByTestId(`whisper-expected-state`)
36+
.textContent()
37+
expect(expectedWhisper).not.toBeNull()
38+
await expect(page.getByTestId(`whisper-actual-state`)).toContainText(
39+
expectedWhisper!,
40+
)
41+
}
2442
test.use({
2543
whitelistErrors: [
2644
/Failed to load resource: the server responded with a status of 499/,
@@ -54,21 +72,7 @@ test.describe('SSR serialization adapters', () => {
5472
await page.goto('/ssr/nested')
5573
await awaitPageLoaded(page)
5674

57-
const expectedShout = await page
58-
.getByTestId(`shout-expected-state`)
59-
.textContent()
60-
expect(expectedShout).not.toBeNull()
61-
await expect(page.getByTestId(`shout-actual-state`)).toContainText(
62-
expectedShout!,
63-
)
64-
65-
const expectedWhisper = await page
66-
.getByTestId(`whisper-expected-state`)
67-
.textContent()
68-
expect(expectedWhisper).not.toBeNull()
69-
await expect(page.getByTestId(`whisper-actual-state`)).toContainText(
70-
expectedWhisper!,
71-
)
75+
await checkNestedData(page)
7276
})
7377
})
7478

@@ -94,4 +98,15 @@ test.describe('server functions serialization adapters', () => {
9498
page.getByTestId('server-function-invalid-response'),
9599
).toContainText('{"message":"Invalid input","foo":"bar","bar":"123"}')
96100
})
101+
test('nested', async ({ page }) => {
102+
await page.goto('/server-function/nested')
103+
await awaitPageLoaded(page)
104+
105+
await expect(page.getByTestId('waiting-for-response')).toContainText(
106+
'waiting for response...',
107+
)
108+
109+
await page.getByTestId('server-function-trigger').click()
110+
await checkNestedData(page)
111+
})
97112
})

packages/router-core/src/ssr/serializer/transformer.ts

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -142,11 +142,6 @@ export function makeSsrSerovalPlugin(
142142
): Plugin<any, SerovalNode> {
143143
return createPlugin<any, SerovalNode>({
144144
tag: '$TSR/t/' + serializationAdapter.key,
145-
extends: serializationAdapter.extends
146-
? (serializationAdapter.extends as Array<AnySerializationAdapter>).map(
147-
(ext) => makeSsrSerovalPlugin(ext, options),
148-
)
149-
: undefined,
150145
test: serializationAdapter.test,
151146
parse: {
152147
stream(value, ctx) {
@@ -174,11 +169,6 @@ export function makeSerovalPlugin(
174169
): Plugin<any, SerovalNode> {
175170
return createPlugin<any, SerovalNode>({
176171
tag: '$TSR/t/' + serializationAdapter.key,
177-
extends: serializationAdapter.extends
178-
? (serializationAdapter.extends as Array<AnySerializationAdapter>).map(
179-
makeSerovalPlugin,
180-
)
181-
: undefined,
182172
test: serializationAdapter.test,
183173
parse: {
184174
sync(value, ctx) {

packages/start-client-core/src/createStart.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,10 +67,10 @@ export interface StartInstanceTypes<
6767

6868
function dedupeSerializationAdapters(
6969
deduped: Set<AnySerializationAdapter>,
70-
plugins: Array<AnySerializationAdapter>,
70+
serializationAdapters: Array<AnySerializationAdapter>,
7171
): void {
72-
for (let i = 0, len = plugins.length; i < len; i++) {
73-
const current = plugins[i]!
72+
for (let i = 0, len = serializationAdapters.length; i < len; i++) {
73+
const current = serializationAdapters[i]!
7474
if (!deduped.has(current)) {
7575
deduped.add(current)
7676
if (current.extends) {

0 commit comments

Comments
 (0)