Skip to content

Commit

Permalink
Use ReadableStream in RenderResult (vercel#34005)
Browse files Browse the repository at this point in the history
Since we're always using `ReadableStream`, we should just get rid of `ResultPiper`.

This also lets us replace things like `bufferedReadFromReadableStream` with a `TransformStream` that does the same thing, so that it's `TransformStream`s all the way down.

Finally, we can get rid of the one-off call to `renderToReadableStream` and just use `renderToStream` whenever we're rendering a concurrent tree.
  • Loading branch information
devknoll authored and natew committed Feb 16, 2022
1 parent 8eb283c commit 5e393f3
Show file tree
Hide file tree
Showing 5 changed files with 250 additions and 170 deletions.
6 changes: 0 additions & 6 deletions packages/next/server/node-polyfill-readable-stream.js

This file was deleted.

7 changes: 7 additions & 0 deletions packages/next/server/node-polyfill-web-streams.js
@@ -0,0 +1,7 @@
import { ReadableStream, TransformStream } from './web/sandbox/readable-stream'

// Polyfill Web Streams in the Node.js environment
if (!global.ReadableStream) {
global.ReadableStream = ReadableStream
global.TransformStream = TransformStream
}
50 changes: 22 additions & 28 deletions packages/next/server/render-result.ts
@@ -1,14 +1,9 @@
import type { ServerResponse } from 'http'

export type ResultPiper = (
push: (chunks: Uint8Array[]) => void,
next: (err?: Error) => void
) => void

export default class RenderResult {
_result: string | ResultPiper
_result: string | ReadableStream

constructor(response: string | ResultPiper) {
constructor(response: string | ReadableStream) {
this._result = response
}

Expand All @@ -33,31 +28,30 @@ export default class RenderResult {
? () => (res as any).flush()
: () => {}

return new Promise((resolve, reject) => {
return (async () => {
const reader = response.getReader()
let fatalError = false
response(
(chunks) => {
// The state of the stream is non-deterministic after
// writing, so any error becomes fatal.
fatalError = true
res.cork()
chunks.forEach((chunk) => res.write(chunk))
res.uncork()
flush()
},
(err) => {
if (err) {
if (fatalError) {
res.destroy(err)
}
reject(err)
} else {

try {
while (true) {
const { done, value } = await reader.read()

if (done) {
res.end()
resolve()
return
}

fatalError = true
res.write(value)
flush()
}
)
})
} catch (err) {
if (fatalError) {
res.destroy(err as any)
}
throw err
}
})()
}

isDynamic(): boolean {
Expand Down

0 comments on commit 5e393f3

Please sign in to comment.