Skip to content

Commit a3e6e6e

Browse files
authored
chore(rsc): Switch to web streams instead of using node streams (redwoodjs#11445)
1 parent 69c5a23 commit a3e6e6e

File tree

2 files changed

+26
-31
lines changed

2 files changed

+26
-31
lines changed

packages/vite/modules.d.ts

+9-1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,12 @@ type ClientManifest = {
1515
[id: string]: ClientReferenceManifestEntry
1616
}
1717

18+
/**
19+
* ReactClientValue is complicated. But basically it's anything that's serializable.
20+
* @see {@link https://github.com/facebook/react/blob/4c58fce7777f2760f4a93091ca4fca0e3fc2f48c/packages/react-server/src/ReactFlightServer.js#L295}
21+
*/
22+
type ReactClientValue = any
23+
1824
declare module 'react-server-dom-webpack/server.edge' {
1925
type Options = {
2026
environmentName?: string
@@ -24,7 +30,9 @@ declare module 'react-server-dom-webpack/server.edge' {
2430
onPostpone?: (reason: string) => void
2531
}
2632

27-
// https://github.com/facebook/react/blob/0711ff17638ed41f9cdea712a19b92f01aeda38f/packages/react-server-dom-webpack/src/ReactFlightDOMServerEdge.js#L48
33+
/**
34+
* @see {@link https://github.com/facebook/react/blob/4c58fce7777f2760f4a93091ca4fca0e3fc2f48c/packages/react-server-dom-webpack/src/server/ReactFlightDOMServerEdge.js#L57}
35+
*/
2836
export function renderToReadableStream(
2937
model: ReactClientValue,
3038
webpackMap: ClientManifest,

packages/vite/src/rsc/rscWorker.ts

+17-30
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,13 @@
33
// `--condition react-server`. If we did try to do that the main process
44
// couldn't do SSR because it would be missing client-side React functions
55
// like `useState` and `createContext`.
6-
import type { Buffer } from 'node:buffer'
6+
import { Buffer } from 'node:buffer'
77
import path from 'node:path'
8-
import { Writable } from 'node:stream'
98
import { parentPort } from 'node:worker_threads'
109

1110
import { createElement } from 'react'
1211

13-
import RSDWServer from 'react-server-dom-webpack/server'
12+
import { renderToReadableStream } from 'react-server-dom-webpack/server.edge'
1413

1514
import { getPaths } from '@redwoodjs/project-config'
1615
import {
@@ -28,16 +27,9 @@ import type {
2827
RenderInput,
2928
} from './rscWorkerCommunication.js'
3029

31-
// TODO (RSC): We should look into importing renderToReadableStream from
32-
// 'react-server-dom-webpack/server.browser' so that we can respond with web
33-
// streams
34-
const { renderToPipeableStream } = RSDWServer
35-
3630
let absoluteClientEntries: Record<string, string> = {}
3731
const serverStorage = createServerStorage()
3832

39-
type PipeableStream = { pipe<T extends Writable>(destination: T): T }
40-
4133
const handleSetClientEntries = async ({
4234
id,
4335
}: MessageReq & { type: 'setClientEntries' }) => {
@@ -73,21 +65,17 @@ const handleRender = async ({ id, input }: MessageReq & { type: 'render' }) => {
7365
serverStorage.run(reqMap, async () => {
7466
try {
7567
// @MARK run render with map initialised
76-
const pipeable = input.rscId
68+
const readable = input.rscId
7769
? await renderRsc(input)
7870
: await handleRsa(input)
7971

80-
const writable = new Writable({
81-
write(chunk, encoding, callback) {
82-
if (encoding !== ('buffer' as any)) {
83-
throw new Error('Unknown encoding')
84-
}
85-
72+
const writable = new WritableStream({
73+
write(chunk) {
8674
if (!parentPort) {
8775
throw new Error('parentPort is undefined')
8876
}
8977

90-
const buffer: Buffer = chunk
78+
const buffer = Buffer.from(chunk)
9179
const message: MessageRes = {
9280
id,
9381
type: 'buf',
@@ -96,20 +84,18 @@ const handleRender = async ({ id, input }: MessageReq & { type: 'render' }) => {
9684
len: buffer.length,
9785
}
9886
parentPort.postMessage(message, [message.buf])
99-
callback()
10087
},
101-
final(callback) {
88+
close() {
10289
if (!parentPort) {
10390
throw new Error('parentPort is undefined')
10491
}
10592

10693
const message: MessageRes = { id, type: 'end' }
10794
parentPort.postMessage(message)
108-
callback()
10995
},
11096
})
11197

112-
pipeable.pipe(writable)
98+
readable.pipeTo(writable)
11399
} catch (err) {
114100
if (!parentPort) {
115101
throw new Error('parentPort is undefined')
@@ -223,7 +209,7 @@ function getBundlerConfig() {
223209
return bundlerConfig
224210
}
225211

226-
async function renderRsc(input: RenderInput): Promise<PipeableStream> {
212+
async function renderRsc(input: RenderInput): Promise<ReadableStream> {
227213
if (input.rsaId || !input.args) {
228214
throw new Error(
229215
"Unexpected input. Can't request both RSCs and execute RSAs at the same time.",
@@ -237,12 +223,13 @@ async function renderRsc(input: RenderInput): Promise<PipeableStream> {
237223
console.log('renderRsc input', input)
238224

239225
const serverRoutes = await getRoutesComponent()
240-
const element = createElement(serverRoutes, input.props)
226+
// TODO (RSC): Should this have the same shape as for handleRsa?
227+
const model = createElement(serverRoutes, input.props)
241228

242229
console.log('rscWorker.ts renderRsc renderRsc props', input.props)
243-
console.log('rscWorker.ts renderRsc element', element)
230+
console.log('rscWorker.ts renderRsc model', model)
244231

245-
return renderToPipeableStream(element, getBundlerConfig())
232+
return renderToReadableStream(model, getBundlerConfig())
246233
// TODO (RSC): We used to transform() the stream here to remove
247234
// "prefixToRemove", which was the common base path to all filenames. We
248235
// then added it back in handleRsa with a simple
@@ -260,7 +247,7 @@ function isSerializedFormData(data?: unknown): data is SerializedFormData {
260247
return !!data && (data as SerializedFormData)?.__formData__
261248
}
262249

263-
async function handleRsa(input: RenderInput): Promise<PipeableStream> {
250+
async function handleRsa(input: RenderInput): Promise<ReadableStream> {
264251
console.log('handleRsa input', input)
265252

266253
if (!input.rsaId || !input.args) {
@@ -296,13 +283,13 @@ async function handleRsa(input: RenderInput): Promise<PipeableStream> {
296283

297284
const serverRoutes = await getRoutesComponent()
298285
console.log('rscWorker.ts handleRsa serverRoutes', serverRoutes)
299-
const elements = {
286+
const model = {
300287
Routes: createElement(serverRoutes, {
301288
location: { pathname: '/', search: '' },
302289
}),
303290
__rwjs__rsa_data: data,
304291
}
305-
console.log('rscWorker.ts handleRsa elements', elements)
292+
console.log('rscWorker.ts handleRsa model', model)
306293

307-
return renderToPipeableStream(elements, getBundlerConfig())
294+
return renderToReadableStream(model, getBundlerConfig())
308295
}

0 commit comments

Comments
 (0)