@@ -32,16 +32,45 @@ babelRegister({
32
32
// Ensure environment variables are read.
33
33
require ( '../config/env' ) ;
34
34
35
- const fs = require ( 'fs' ) ;
35
+ const fs = require ( 'fs' ) . promises ;
36
36
const compress = require ( 'compression' ) ;
37
37
const chalk = require ( 'chalk' ) ;
38
38
const express = require ( 'express' ) ;
39
- const app = express ( ) ;
40
-
41
39
const http = require ( 'http' ) ;
42
40
41
+ const { renderToPipeableStream} = require ( 'react-dom/server' ) ;
42
+ const { createFromNodeStream} = require ( 'react-server-dom-webpack/client' ) ;
43
+
44
+ const app = express ( ) ;
45
+
43
46
app . use ( compress ( ) ) ;
44
47
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
+
45
74
function request ( options , body ) {
46
75
return new Promise ( ( resolve , reject ) => {
47
76
const req = http . request ( options , res => {
@@ -55,12 +84,6 @@ function request(options, body) {
55
84
}
56
85
57
86
app . 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
-
64
87
// Proxy the request to the regional server.
65
88
const proxiedHeaders = {
66
89
'X-Forwarded-Host' : req . hostname ,
@@ -85,52 +108,64 @@ app.all('/', async function (req, res, next) {
85
108
req
86
109
) ;
87
110
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 initial
133
+ const mainJSChunks = JSON . parse (
134
+ await virtualFs . readFile (
135
+ path . join ( buildPath , 'entrypoint-manifest.json' ) ,
136
+ 'utf8'
137
+ )
138
+ ) . main . js ;
139
+ // For HTML, we're a "client" emulator that runs the client code,
140
+ // so we start by consuming the RSC payload. This needs a module
141
+ // map that reverse engineers the client-side path to the SSR path.
142
+ const root = await createFromNodeStream ( rscResponse , moduleMap ) ;
143
+ // Render it into HTML by resolving the client components
144
+ res . set ( 'Content-type' , 'text/html' ) ;
145
+ const { pipe} = renderToPipeableStream ( root , {
146
+ bootstrapScripts : mainJSChunks ,
147
+ } ) ;
148
+ pipe ( res ) ;
149
+ } catch ( e ) {
150
+ console . error ( `Failed to SSR: ${ e . stack } ` ) ;
151
+ res . statusCode = 500 ;
152
+ res . end ( ) ;
153
+ }
154
+ } else {
155
+ try {
156
+ const rscResponse = await promiseForData ;
157
+ // For other request, we pass-through the RSC payload.
158
+ res . set ( 'Content-type' , 'text/x-component' ) ;
159
+ rscResponse . pipe ( res ) ;
160
+ } catch ( e ) {
161
+ console . error ( `Failed to proxy request: ${ e . stack } ` ) ;
162
+ res . statusCode = 500 ;
163
+ res . end ( ) ;
164
+ }
96
165
}
97
166
} ) ;
98
167
99
168
if ( 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
- ) ;
134
169
app . use ( express . static ( 'public' ) ) ;
135
170
} else {
136
171
// In production we host the static build output.
0 commit comments