@@ -32,16 +32,45 @@ babelRegister({
3232// Ensure environment variables are read.
3333require ( '../config/env' ) ;
3434
35- const fs = require ( 'fs' ) ;
35+ const fs = require ( 'fs' ) . promises ;
3636const compress = require ( 'compression' ) ;
3737const chalk = require ( 'chalk' ) ;
3838const express = require ( 'express' ) ;
39- const app = express ( ) ;
40-
4139const http = require ( 'http' ) ;
4240
41+ const { renderToPipeableStream} = require ( 'react-dom/server' ) ;
42+ const { createFromNodeStream} = require ( 'react-server-dom-webpack/client' ) ;
43+
44+ const app = express ( ) ;
45+
4346app . use ( compress ( ) ) ;
4447
48+ if ( process . env . NODE_ENV === 'development' ) {
49+ // In development we host the Webpack server for live bundling.
50+ const webpack = require ( 'webpack' ) ;
51+ const webpackMiddleware = require ( 'webpack-dev-middleware' ) ;
52+ const webpackHotMiddleware = require ( 'webpack-hot-middleware' ) ;
53+ const paths = require ( '../config/paths' ) ;
54+ const configFactory = require ( '../config/webpack.config' ) ;
55+ const getClientEnvironment = require ( '../config/env' ) ;
56+
57+ const env = getClientEnvironment ( paths . publicUrlOrPath . slice ( 0 , - 1 ) ) ;
58+
59+ const config = configFactory ( 'development' ) ;
60+ const protocol = process . env . HTTPS === 'true' ? 'https' : 'http' ;
61+ const appName = require ( paths . appPackageJson ) . name ;
62+
63+ // Create a webpack compiler that is configured with custom messages.
64+ const compiler = webpack ( config ) ;
65+ app . use (
66+ webpackMiddleware ( compiler , {
67+ publicPath : paths . publicUrlOrPath . slice ( 0 , - 1 ) ,
68+ serverSideRender : true ,
69+ } )
70+ ) ;
71+ app . use ( webpackHotMiddleware ( compiler ) ) ;
72+ }
73+
4574function request ( options , body ) {
4675 return new Promise ( ( resolve , reject ) => {
4776 const req = http . request ( options , res => {
@@ -55,12 +84,6 @@ function request(options, body) {
5584}
5685
5786app . all ( '/' , async function ( req , res , next ) {
58- if ( req . accepts ( 'text/html' ) ) {
59- // Pass-through to the html file
60- next ( ) ;
61- return ;
62- }
63-
6487 // Proxy the request to the regional server.
6588 const proxiedHeaders = {
6689 'X-Forwarded-Host' : req . hostname ,
@@ -85,52 +108,65 @@ app.all('/', async function (req, res, next) {
85108 req
86109 ) ;
87110
88- try {
89- const rscResponse = await promiseForData ;
90- res . set ( 'Content-type' , 'text/x-component' ) ;
91- rscResponse . pipe ( res ) ;
92- } catch ( e ) {
93- console . error ( `Failed to proxy request: ${ e . stack } ` ) ;
94- res . statusCode = 500 ;
95- res . end ( ) ;
111+ if ( req . accepts ( 'text/html' ) ) {
112+ try {
113+ const rscResponse = await promiseForData ;
114+
115+ let virtualFs ;
116+ let buildPath ;
117+ if ( process . env . NODE_ENV === 'development' ) {
118+ const { devMiddleware} = res . locals . webpack ;
119+ virtualFs = devMiddleware . outputFileSystem . promises ;
120+ buildPath = devMiddleware . stats . toJson ( ) . outputPath ;
121+ } else {
122+ virtualFs = fs ;
123+ buildPath = path . join ( __dirname , '../build/' ) ;
124+ }
125+ // Read the module map from the virtual file system.
126+ const moduleMap = JSON . parse (
127+ await virtualFs . readFile (
128+ path . join ( buildPath , 'react-ssr-manifest.json' ) ,
129+ 'utf8'
130+ )
131+ ) ;
132+ // Read the entrypoints containing the initial JS to bootstrap everything.
133+ // For other pages, the chunks in the RSC payload are enough.
134+ const mainJSChunks = JSON . parse (
135+ await virtualFs . readFile (
136+ path . join ( buildPath , 'entrypoint-manifest.json' ) ,
137+ 'utf8'
138+ )
139+ ) . main . js ;
140+ // For HTML, we're a "client" emulator that runs the client code,
141+ // so we start by consuming the RSC payload. This needs a module
142+ // map that reverse engineers the client-side path to the SSR path.
143+ const root = await createFromNodeStream ( rscResponse , moduleMap ) ;
144+ // Render it into HTML by resolving the client components
145+ res . set ( 'Content-type' , 'text/html' ) ;
146+ const { pipe} = renderToPipeableStream ( root , {
147+ bootstrapScripts : mainJSChunks ,
148+ } ) ;
149+ pipe ( res ) ;
150+ } catch ( e ) {
151+ console . error ( `Failed to SSR: ${ e . stack } ` ) ;
152+ res . statusCode = 500 ;
153+ res . end ( ) ;
154+ }
155+ } else {
156+ try {
157+ const rscResponse = await promiseForData ;
158+ // For other request, we pass-through the RSC payload.
159+ res . set ( 'Content-type' , 'text/x-component' ) ;
160+ rscResponse . pipe ( res ) ;
161+ } catch ( e ) {
162+ console . error ( `Failed to proxy request: ${ e . stack } ` ) ;
163+ res . statusCode = 500 ;
164+ res . end ( ) ;
165+ }
96166 }
97167} ) ;
98168
99169if ( process . env . NODE_ENV === 'development' ) {
100- // In development we host the Webpack server for live bundling.
101- const webpack = require ( 'webpack' ) ;
102- const webpackMiddleware = require ( 'webpack-dev-middleware' ) ;
103- const webpackHotMiddleware = require ( 'webpack-hot-middleware' ) ;
104- const paths = require ( '../config/paths' ) ;
105- const configFactory = require ( '../config/webpack.config' ) ;
106- const getClientEnvironment = require ( '../config/env' ) ;
107-
108- const env = getClientEnvironment ( paths . publicUrlOrPath . slice ( 0 , - 1 ) ) ;
109-
110- const HOST = '0.0.0.0' ;
111- const PORT = 3000 ;
112-
113- const config = configFactory ( 'development' ) ;
114- const protocol = process . env . HTTPS === 'true' ? 'https' : 'http' ;
115- const appName = require ( paths . appPackageJson ) . name ;
116-
117- // Create a webpack compiler that is configured with custom messages.
118- const compiler = webpack ( config ) ;
119- app . use (
120- webpackMiddleware ( compiler , {
121- writeToDisk : filePath => {
122- return / ( r e a c t - c l i e n t - m a n i f e s t | r e a c t - s s r - m a n i f e s t ) \. j s o n $ / . test (
123- filePath
124- ) ;
125- } ,
126- publicPath : paths . publicUrlOrPath . slice ( 0 , - 1 ) ,
127- } )
128- ) ;
129- app . use (
130- webpackHotMiddleware ( compiler , {
131- /* Options */
132- } )
133- ) ;
134170 app . use ( express . static ( 'public' ) ) ;
135171} else {
136172 // In production we host the static build output.
0 commit comments