1
1
import ReactDOMServer from 'react-dom/server' ;
2
- import { PassThrough , Readable , Transform } from 'stream' ;
2
+ import { PassThrough , Readable } from 'stream' ;
3
3
import type { ReactElement } from 'react' ;
4
4
5
5
import ComponentRegistry from './ComponentRegistry' ;
@@ -195,16 +195,19 @@ const serverRenderReactComponent: typeof serverRenderReactComponentInternal = (o
195
195
196
196
const stringToStream = ( str : string ) : Readable => {
197
197
const stream = new PassThrough ( ) ;
198
- stream . push ( str ) ;
199
- stream . push ( null ) ;
198
+ stream . write ( str ) ;
199
+ stream . end ( ) ;
200
200
return stream ;
201
201
} ;
202
202
203
203
export const streamServerRenderedReactComponent = ( options : RenderParams ) : Readable => {
204
204
const { name : componentName , domNodeId, trace, props, railsContext, throwJsErrors } = options ;
205
205
206
206
let renderResult : null | Readable = null ;
207
+ let hasErrors = false ;
208
+ let isShellReady = false ;
207
209
let previouslyReplayedConsoleMessages : number = 0 ;
210
+ let consoleHistory : typeof console [ 'history' ] | undefined ;
208
211
209
212
try {
210
213
const componentObj = ComponentRegistry . get ( componentName ) ;
@@ -222,24 +225,49 @@ export const streamServerRenderedReactComponent = (options: RenderParams): Reada
222
225
throw new Error ( 'Server rendering of streams is not supported for server render hashes or promises.' ) ;
223
226
}
224
227
225
- const consoleHistory = console . history ;
226
- const transformStream = new Transform ( {
228
+ consoleHistory = console . history ;
229
+ const transformStream = new PassThrough ( {
227
230
transform ( chunk , _ , callback ) {
228
231
const htmlChunk = chunk . toString ( ) ;
232
+ console . log ( 'htmlChunk' , htmlChunk ) ;
229
233
const consoleReplayScript = buildConsoleReplay ( consoleHistory , previouslyReplayedConsoleMessages ) ;
230
234
previouslyReplayedConsoleMessages = consoleHistory ?. length || 0 ;
231
235
232
236
const jsonChunk = JSON . stringify ( {
233
237
html : htmlChunk ,
234
238
consoleReplayScript,
239
+ hasErrors,
240
+ isShellReady,
235
241
} ) ;
236
-
242
+
237
243
this . push ( jsonChunk ) ;
238
244
callback ( ) ;
239
245
}
240
246
} ) ;
241
247
242
- ReactDOMServer . renderToPipeableStream ( reactRenderingResult ) . pipe ( transformStream ) ;
248
+ const renderingStream = ReactDOMServer . renderToPipeableStream ( reactRenderingResult , {
249
+ onShellError ( e ) {
250
+ // Can't through error here if throwJsErrors is true because the error will happen inside the stream
251
+ // And will not be handled by any catch clause
252
+ hasErrors = true ;
253
+ const error = e instanceof Error ? e : new Error ( String ( e ) ) ;
254
+ transformStream . write ( handleError ( {
255
+ e : error ,
256
+ name : componentName ,
257
+ serverSide : true ,
258
+ } ) ) ;
259
+ transformStream . end ( ) ;
260
+ } ,
261
+ onShellReady ( ) {
262
+ isShellReady = true ;
263
+ renderingStream . pipe ( transformStream ) ;
264
+ } ,
265
+ onError ( ) {
266
+ // Can't through error here if throwJsErrors is true because the error will happen inside the stream
267
+ // And will not be handled by any catch clause
268
+ hasErrors = true ;
269
+ } ,
270
+ } ) ;
243
271
244
272
renderResult = transformStream ;
245
273
} catch ( e ) {
@@ -248,10 +276,15 @@ export const streamServerRenderedReactComponent = (options: RenderParams): Reada
248
276
}
249
277
250
278
const error = e instanceof Error ? e : new Error ( String ( e ) ) ;
251
- renderResult = stringToStream ( handleError ( {
252
- e : error ,
253
- name : componentName ,
254
- serverSide : true ,
279
+ renderResult = stringToStream ( JSON . stringify ( {
280
+ html : handleError ( {
281
+ e : error ,
282
+ name : componentName ,
283
+ serverSide : true ,
284
+ } ) ,
285
+ consoleReplayScript : buildConsoleReplay ( consoleHistory , previouslyReplayedConsoleMessages ) ,
286
+ hasErrors : true ,
287
+ isShellReady,
255
288
} ) ) ;
256
289
}
257
290
0 commit comments