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

Refactor server/render for SSR streaming #31231

Merged
merged 12 commits into from Nov 15, 2021
Expand Up @@ -28,16 +28,6 @@ export default async function middlewareRSCLoader(this: any) {
import { RouterContext } from 'next/dist/shared/lib/router-context'
import { renderToHTML } from 'next/dist/server/web/render'

import React, { createElement } from 'react'

${
isServerComponent
? `
import { renderToReadableStream } from 'next/dist/compiled/react-server-dom-webpack/writer.browser.server'
import { createFromReadableStream } from 'next/dist/compiled/react-server-dom-webpack'`
: ''
}

${appDefinition}
${documentDefinition}

Expand All @@ -57,47 +47,7 @@ export default async function middlewareRSCLoader(this: any) {
throw new Error('Your page must export a \`default\` component')
}

function wrapReadable(readable) {
const encoder = new TextEncoder()
const transformStream = new TransformStream()
const writer = transformStream.writable.getWriter()
const reader = readable.getReader()
const process = () => {
reader.read().then(({ done, value }) => {
if (!done) {
writer.write(typeof value === 'string' ? encoder.encode(value) : value)
process()
} else {
writer.close()
}
})
}
process()
return transformStream.readable
}

${
isServerComponent
? `
const renderFlight = props => renderToReadableStream(createElement(Page, props), rscManifest)

let responseCache
const FlightWrapper = props => {
let response = responseCache
if (!response) {
responseCache = response = createFromReadableStream(renderFlight(props))
}
return response.readRoot()
}
const Component = props => {
return createElement(
React.Suspense,
{ fallback: null },
createElement(FlightWrapper, props)
)
}`
: `const Component = Page`
}
const Component = Page

async function render(request) {
const url = request.nextUrl
Expand All @@ -111,28 +61,10 @@ export default async function middlewareRSCLoader(this: any) {
})
}

${
isServerComponent
? `
// Flight data request
const isFlightDataRequest = query.__flight__ !== undefined
if (isFlightDataRequest) {
delete query.__flight__
return new Response(
wrapReadable(
renderFlight({
router: {
route: pathname,
asPath: pathname,
pathname: pathname,
query,
}
})
)
)
}`
: ''
const renderServerComponentData = ${
isServerComponent ? `query.__flight__ !== undefined` : 'false'
}
delete query.__flight__

const renderOpts = {
Component,
Expand All @@ -156,7 +88,10 @@ export default async function middlewareRSCLoader(this: any) {
basePath: ${JSON.stringify(basePath || '')},
supportsDynamicHTML: true,
concurrentFeatures: true,
renderServerComponent: ${isServerComponent ? 'true' : 'false'},
renderServerComponentData,
serverComponentManifest: ${
isServerComponent ? 'rscManifest' : 'null'
},
}

const transformStream = new TransformStream()
Expand All @@ -173,7 +108,8 @@ export default async function middlewareRSCLoader(this: any) {
)
result.pipe({
write: str => writer.write(encoder.encode(str)),
end: () => writer.close()
end: () => writer.close(),
// Not implemented: cork/uncork/on/removeListener
})
} catch (err) {
return new Response(
Expand Down