Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use react-dom/server.browser in Node.js #33950

Merged
merged 16 commits into from Feb 4, 2022
8 changes: 0 additions & 8 deletions packages/next/build/webpack-config.ts
Expand Up @@ -610,14 +610,6 @@ export default async function getBaseWebpackConfig(
}
: {}),

...(webServerRuntime
? {
'react-dom/server': dev
? 'react-dom/cjs/react-dom-server.browser.development'
: 'react-dom/cjs/react-dom-server.browser.production.min',
}
: {}),

setimmediate: 'next/dist/compiled/setimmediate',
},
...(targetWeb
Expand Down
6 changes: 6 additions & 0 deletions packages/next/server/node-polyfill-readable-stream.js
@@ -0,0 +1,6 @@
import { ReadableStream } from './web/sandbox/readable-stream'

// Polyfill ReadableStream in the Node.js environment
if (!global.ReadableStream) {
global.ReadableStream = ReadableStream
}
38 changes: 32 additions & 6 deletions packages/next/server/render-result.ts
@@ -1,15 +1,14 @@
import type { ServerResponse } from 'http'
import type { Writable } from 'stream'

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

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

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

Expand All @@ -29,8 +28,35 @@ export default class RenderResult {
)
}
const response = this._result
const flush =
typeof (res as any).flush === 'function'
? () => (res as any).flush()
: () => {}

return new Promise((resolve, reject) => {
response(res, (err) => (err ? reject(err) : resolve()))
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 {
res.end()
resolve()
}
}
)
})
}

Expand Down