1- // @ts -expect-error will define this module types later
2- import { renderToReadableStream } from 'react-server-dom-webpack/server.edge ' ;
3- import { PassThrough } from 'stream ' ;
1+ import { renderToPipeableStream } from 'react-server-dom-webpack/server.node' ;
2+ import { PassThrough , Readable } from 'stream ' ;
3+ import type { ReactElement } from 'react ' ;
44import fs from 'fs' ;
55
66import { RenderParams } from './types' ;
7- import ComponentRegistry from './ComponentRegistry' ;
8- import createReactOutput from './createReactOutput' ;
9- import { isPromise , isServerRenderHash } from './isServerRenderResult' ;
107import ReactOnRails from './ReactOnRails' ;
8+ import buildConsoleReplay from './buildConsoleReplay' ;
9+ import handleError from './handleError' ;
10+ import {
11+ streamServerRenderedComponent ,
12+ type StreamRenderState ,
13+ transformRenderStreamChunksToResultObject ,
14+ convertToError ,
15+ createResultObject ,
16+ } from './serverRenderReactComponent' ;
1117
1218const stringToStream = ( str : string ) => {
1319 const stream = new PassThrough ( ) ;
@@ -16,68 +22,51 @@ const stringToStream = (str: string) => {
1622 return stream ;
1723} ;
1824
19- const getBundleConfig = ( ) => {
20- const bundleConfig = JSON . parse ( fs . readFileSync ( './public/webpack/development/react-client-manifest.json' , 'utf8' ) ) ;
21- // remove file:// from keys
22- const newBundleConfig : { [ key : string ] : unknown } = { } ;
23- for ( const [ key , value ] of Object . entries ( bundleConfig ) ) {
24- newBundleConfig [ key . replace ( 'file://' , '' ) ] = value ;
25- }
26- return newBundleConfig ;
27- }
28-
29- ReactOnRails . serverRenderRSCReactComponent = ( options : RenderParams ) => {
30- const { name, domNodeId, trace, props, railsContext, throwJsErrors } = options ;
25+ const getBundleConfig = ( ) => JSON . parse ( fs . readFileSync ( './public/webpack/development/react-client-manifest.json' , 'utf8' ) )
3126
32- let renderResult : null | PassThrough = null ;
27+ const streamRenderRSCComponent = ( reactElement : ReactElement , options : RenderParams ) : Readable => {
28+ const { throwJsErrors } = options ;
29+ const renderState : StreamRenderState = {
30+ result : null ,
31+ hasErrors : false ,
32+ isShellReady : true
33+ } ;
3334
35+ const { pipeToTransform, readableStream, emitError } = transformRenderStreamChunksToResultObject ( renderState ) ;
3436 try {
35- const componentObj = ComponentRegistry . get ( name ) ;
36- if ( componentObj . isRenderer ) {
37- throw new Error ( `\
38- Detected a renderer while server rendering component '${ name } '. \
39- See https://github.com/shakacode/react_on_rails#renderer-functions` ) ;
40- }
41-
42- const reactRenderingResult = createReactOutput ( {
43- componentObj,
44- domNodeId,
45- trace,
46- props,
47- railsContext,
48- } ) ;
49-
50- if ( isServerRenderHash ( reactRenderingResult ) || isPromise ( reactRenderingResult ) ) {
51- throw new Error ( 'Server rendering of streams is not supported for server render hashes or promises.' ) ;
52- }
53-
54- renderResult = new PassThrough ( ) ;
55- let finalValue = "" ;
56- const streamReader = renderToReadableStream ( reactRenderingResult , getBundleConfig ( ) ) . getReader ( ) ;
57- const decoder = new TextDecoder ( ) ;
58- const processStream = async ( ) => {
59- const { done, value } = await streamReader . read ( ) ;
60- if ( done ) {
61- renderResult ?. push ( null ) ;
62- // @ts -expect-error value is not typed
63- debugConsole . log ( 'value' , finalValue ) ;
64- return ;
37+ const rscStream = renderToPipeableStream (
38+ reactElement ,
39+ getBundleConfig ( ) ,
40+ {
41+ onError : ( err ) => {
42+ const error = convertToError ( err ) ;
43+ console . error ( "Error in RSC stream" , error ) ;
44+ if ( throwJsErrors ) {
45+ emitError ( error ) ;
46+ }
47+ renderState . hasErrors = true ;
48+ renderState . error = error ;
49+ }
6550 }
66-
67- finalValue += decoder . decode ( value ) ;
68- renderResult ?. push ( value ) ;
69- processStream ( ) ;
70- }
71- processStream ( ) ;
72- } catch ( e : unknown ) {
73- if ( throwJsErrors ) {
74- throw e ;
75- }
76-
77- renderResult = stringToStream ( `Error: ${ e } ` ) ;
51+ ) ;
52+ pipeToTransform ( rscStream ) ;
53+ return readableStream ;
54+ } catch ( e ) {
55+ const error = convertToError ( e ) ;
56+ renderState . hasErrors = true ;
57+ renderState . error = error ;
58+ const htmlResult = handleError ( { e : error , name : options . name , serverSide : true } ) ;
59+ const jsonResult = JSON . stringify ( createResultObject ( htmlResult , buildConsoleReplay ( ) , renderState ) ) ;
60+ return stringToStream ( jsonResult ) ;
7861 }
62+ } ;
7963
80- return renderResult ;
64+ ReactOnRails . serverRenderRSCReactComponent = ( options : RenderParams ) => {
65+ try {
66+ return streamServerRenderedComponent ( options , streamRenderRSCComponent ) ;
67+ } finally {
68+ console . history = [ ] ;
69+ }
8170} ;
8271
8372export * from './types' ;
0 commit comments