Skip to content

Commit 486ebb0

Browse files
Copilothi-ogawa
andauthored
fix(rsc): fix deserializing client reference in server environment with React 19.2.1+ (#1000)
Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: hi-ogawa <[email protected]> Co-authored-by: Hiroshi Ogawa <[email protected]>
1 parent 92bba57 commit 486ebb0

File tree

4 files changed

+29
-23
lines changed

4 files changed

+29
-23
lines changed

packages/plugin-rsc/e2e/basic.test.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1252,8 +1252,7 @@ function defineTest(f: Fixture) {
12521252
.click()
12531253
}
12541254

1255-
// TODO: skipped since 19.2.1
1256-
test.skip('action bind client @js', async ({ page }) => {
1255+
test('action bind client @js', async ({ page }) => {
12571256
await page.goto(f.url())
12581257
await waitForHydration(page)
12591258
await using _ = await expectNoReload(page)
@@ -1308,10 +1307,7 @@ function defineTest(f: Fixture) {
13081307
.click()
13091308
}
13101309

1311-
// TODO: skipped since 19.2.1 as round-tripping server action is broken
1312-
// import { createFromReadableStream, renderToReadableStream } from '@vitejs/plugin-rsc/rsc'
1313-
// renderToReadableStream -> createFromReadableStream
1314-
test.skip('test serialization @js', async ({ page }) => {
1310+
test('test serialization @js', async ({ page }) => {
13151311
await page.goto(f.url())
13161312
await waitForHydration(page)
13171313
await expect(page.getByTestId('serialization')).toHaveText('?')

packages/plugin-rsc/e2e/browser-mode.test.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,7 @@ function defineBrowserModeTest(f: ReturnType<typeof useFixture>) {
4141
.click()
4242
}
4343

44-
// TODO: skipped since 19.2.1
45-
test.skip('action bind client', async ({ page }) => {
44+
test('action bind client', async ({ page }) => {
4645
await page.goto(f.url())
4746
await testActionBindClient(page)
4847
})

packages/plugin-rsc/examples/basic/src/routes/serialization/server.tsx

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,6 @@ import { testSerializationAction } from './action'
66
import { TestSerializationClient } from './client'
77

88
export function TestSerializationServer() {
9-
if (1) {
10-
return <div>test-serialization:todo</div>
11-
}
129
const original = <TestSerializationClient action={testSerializationAction} />
1310
let serialized = renderToReadableStream(original)
1411
// debug serialization

packages/plugin-rsc/src/core/rsc.ts

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -28,24 +28,38 @@ export function setRequireModule(options: {
2828
;(globalThis as any).__vite_rsc_server_require__ = memoize(
2929
async (id: string) => {
3030
if (id.startsWith(SERVER_DECODE_CLIENT_PREFIX)) {
31+
// decode client reference on the server
3132
id = id.slice(SERVER_DECODE_CLIENT_PREFIX.length)
3233
id = removeReferenceCacheTag(id)
3334
// create `registerClientReference` on the fly since there's no way to
3435
// grab the original client reference module on ther server.
3536
// cf. https://github.com/lazarv/react-server/blob/79e7acebc6f4a8c930ad8422e2a4a9fdacfcce9b/packages/react-server/server/module-loader.mjs#L19
36-
// decode client reference on the server
37-
return new Proxy({} as any, {
38-
get(target, name, _receiver) {
37+
const target = {} as any
38+
const getOrCreateClientReference = (name: string) => {
39+
return (target[name] ??= ReactServer.registerClientReference(
40+
() => {
41+
throw new Error(
42+
`Unexpectedly client reference export '${name}' is called on server`,
43+
)
44+
},
45+
id,
46+
name,
47+
))
48+
}
49+
return new Proxy(target, {
50+
get(_target, name, _receiver) {
51+
// not thennable
3952
if (typeof name !== 'string' || name === 'then') return
40-
return (target[name] ??= ReactServer.registerClientReference(
41-
() => {
42-
throw new Error(
43-
`Unexpectedly client reference export '${name}' is called on server`,
44-
)
45-
},
46-
id,
47-
name,
48-
))
53+
return getOrCreateClientReference(name)
54+
},
55+
// React 19.2.1+ uses hasOwnProperty.call() to check for exports
56+
// https://github.com/facebook/react/pull/35277
57+
getOwnPropertyDescriptor(_target, name) {
58+
if (typeof name !== 'string' || name === 'then') {
59+
return Reflect.getOwnPropertyDescriptor(target, name)
60+
}
61+
getOrCreateClientReference(name)
62+
return Reflect.getOwnPropertyDescriptor(target, name)
4963
},
5064
})
5165
}

0 commit comments

Comments
 (0)