diff --git a/src/js/builtins/ReadableStreamInternals.ts b/src/js/builtins/ReadableStreamInternals.ts index 44651b98aa9581..524c5d94d9db51 100644 --- a/src/js/builtins/ReadableStreamInternals.ts +++ b/src/js/builtins/ReadableStreamInternals.ts @@ -889,10 +889,8 @@ export function onPullDirectStream(controller) { return promiseToReturn; } - // not done, but they called flush() - if (deferFlush === 1) { - $onFlushDirectStream.$call(controller); - } + // coalesce all enqueued entries during $pull() and flush them + $onFlushDirectStream.$call(controller); return promiseToReturn; } diff --git a/test/js/bun/stream/direct-readable-stream-suspense.test.tsx b/test/js/bun/stream/direct-readable-stream-suspense.test.tsx new file mode 100644 index 00000000000000..a11cec09ed3b84 --- /dev/null +++ b/test/js/bun/stream/direct-readable-stream-suspense.test.tsx @@ -0,0 +1,54 @@ +import { Suspense } from "react"; +import { renderToReadableStream } from "react-dom/server"; +import { describe, expect, it } from "bun:test"; + +if (!import.meta.resolveSync("react-dom/server").endsWith("server.bun.js")) { + throw new Error("react-dom/server is not the correct version:\n " + import.meta.resolveSync("react-dom/server")); +} + +describe("ReactDOM", () => { + it("should properly chunk Suspense boundaries", async () => { + const A = async () => { + await new Promise(resolve => setImmediate(resolve)); + return
hi
; + }; + + const B = async () => { + return ( + // @ts-ignore + loading}> + {/* @ts-ignore */} + + + ); + }; + // @ts-ignore + const stream = await renderToReadableStream(); + + let text = ""; + let numChunks = 0; + for await (const chunk of stream) { + text += new TextDecoder().decode(chunk); + numChunks++; + } + + expect(text).toBe( + `
loading
`, + ); + expect(numChunks).toBeGreaterThan(1); + }); +}); +const A = async () => { + await new Promise(resolve => setImmediate(resolve)); + return
hi
; +}; + +const B = async () => { + return ( + // @ts-ignore + loading}> + {/* @ts-ignore */} +
+ + ); +};