11import { IncomingMessage , ServerResponse } from 'http'
22import { ParsedUrlQuery } from 'querystring'
3+ import { PassThrough } from 'stream'
34import React from 'react'
4- import { renderToStaticMarkup , renderToString } from 'react-dom/server'
5+ import * as ReactDOMServer from 'react-dom/server'
56import { warn } from '../build/output/log'
67import { UnwrapPromise } from '../lib/coalesced-function'
78import {
@@ -43,6 +44,7 @@ import {
4344 loadGetInitialProps ,
4445 NextComponentType ,
4546 RenderPage ,
47+ RenderPageResult ,
4648} from '../shared/lib/utils'
4749import {
4850 tryGetPreviewData ,
@@ -190,6 +192,7 @@ export type RenderOptsPartial = {
190192 domainLocales ?: DomainLocale [ ]
191193 disableOptimizedLoading ?: boolean
192194 requireStaticHTML ?: boolean
195+ concurrentFeatures ?: boolean
193196}
194197
195198export type RenderOpts = LoadComponentsReturnType & RenderOptsPartial
@@ -263,7 +266,7 @@ function renderDocument(
263266) : string {
264267 return (
265268 '<!DOCTYPE html>' +
266- renderToStaticMarkup (
269+ ReactDOMServer . renderToStaticMarkup (
267270 < AmpStateContext . Provider value = { ampState } >
268271 { Document . renderDocument ( Document , {
269272 __NEXT_DATA__ : {
@@ -408,6 +411,7 @@ export async function renderToHTML(
408411 previewProps,
409412 basePath,
410413 devOnlyCacheBusterQueryString,
414+ concurrentFeatures,
411415 } = renderOpts
412416
413417 const getFontDefinition = ( url : string ) : string => {
@@ -626,6 +630,8 @@ export async function renderToHTML(
626630 let head : JSX . Element [ ] = defaultHead ( inAmpMode )
627631
628632 let scriptLoader : any = { }
633+ const nextExport =
634+ ! isSSG && ( renderOpts . nextExport || ( dev && ( isAutoExport || isFallback ) ) )
629635
630636 const AppContainer = ( { children } : any ) => (
631637 < RouterContext . Provider value = { router } >
@@ -991,11 +997,45 @@ export async function renderToHTML(
991997 }
992998 }
993999
1000+ // TODO: Support SSR streaming of Suspense.
1001+ const renderToString = concurrentFeatures
1002+ ? ( element : React . ReactElement ) =>
1003+ new Promise < string > ( ( resolve , reject ) => {
1004+ const stream = new PassThrough ( )
1005+ const buffers : Buffer [ ] = [ ]
1006+ stream . on ( 'data' , ( chunk ) => {
1007+ buffers . push ( chunk )
1008+ } )
1009+ stream . once ( 'end' , ( ) => {
1010+ resolve ( Buffer . concat ( buffers ) . toString ( 'utf-8' ) )
1011+ } )
1012+
1013+ const {
1014+ abort,
1015+ startWriting,
1016+ } = ( ReactDOMServer as any ) . pipeToNodeWritable ( element , stream , {
1017+ onError ( error : Error ) {
1018+ abort ( )
1019+ reject ( error )
1020+ } ,
1021+ onCompleteAll ( ) {
1022+ startWriting ( )
1023+ } ,
1024+ } )
1025+ } )
1026+ : ReactDOMServer . renderToString
1027+
9941028 const renderPage : RenderPage = (
9951029 options : ComponentsEnhancer = { }
996- ) : { html : string ; head : any } => {
1030+ ) : RenderPageResult | Promise < RenderPageResult > => {
9971031 if ( ctx . err && ErrorDebug ) {
998- return { html : renderToString ( < ErrorDebug error = { ctx . err } /> ) , head }
1032+ const htmlOrPromise = renderToString ( < ErrorDebug error = { ctx . err } /> )
1033+ return typeof htmlOrPromise === 'string'
1034+ ? { html : htmlOrPromise , head }
1035+ : htmlOrPromise . then ( ( html ) => ( {
1036+ html,
1037+ head,
1038+ } ) )
9991039 }
10001040
10011041 if ( dev && ( props . router || props . Component ) ) {
@@ -1009,13 +1049,17 @@ export async function renderToHTML(
10091049 Component : EnhancedComponent ,
10101050 } = enhanceComponents ( options , App , Component )
10111051
1012- const html = renderToString (
1052+ const htmlOrPromise = renderToString (
10131053 < AppContainer >
10141054 < EnhancedApp Component = { EnhancedComponent } router = { router } { ...props } />
10151055 </ AppContainer >
10161056 )
1017-
1018- return { html, head }
1057+ return typeof htmlOrPromise === 'string'
1058+ ? { html : htmlOrPromise , head }
1059+ : htmlOrPromise . then ( ( html ) => ( {
1060+ html,
1061+ head,
1062+ } ) )
10191063 }
10201064 const documentCtx = { ...ctx , renderPage }
10211065 const docProps : DocumentInitialProps = await loadGetInitialProps (
@@ -1049,8 +1093,6 @@ export async function renderToHTML(
10491093 const hybridAmp = ampState . hybrid
10501094
10511095 const docComponentsRendered : DocumentProps [ 'docComponentsRendered' ] = { }
1052- const nextExport =
1053- ! isSSG && ( renderOpts . nextExport || ( dev && ( isAutoExport || isFallback ) ) )
10541096
10551097 let html = renderDocument ( Document , {
10561098 ...renderOpts ,
0 commit comments