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
1218( async ( ) => {
1319 try {
@@ -25,68 +31,51 @@ const stringToStream = (str: string) => {
2531 return stream ;
2632} ;
2733
28- const getBundleConfig = ( ) => {
29- const bundleConfig = JSON . parse ( fs . readFileSync ( './public/webpack/development/react-client-manifest.json' , 'utf8' ) ) ;
30- // remove file:// from keys
31- const newBundleConfig : { [ key : string ] : unknown } = { } ;
32- for ( const [ key , value ] of Object . entries ( bundleConfig ) ) {
33- newBundleConfig [ key . replace ( 'file://' , '' ) ] = value ;
34- }
35- return newBundleConfig ;
36- }
37-
38- ReactOnRails . serverRenderRSCReactComponent = ( options : RenderParams ) => {
39- const { name, domNodeId, trace, props, railsContext, throwJsErrors } = options ;
34+ const getBundleConfig = ( ) => JSON . parse ( fs . readFileSync ( './public/webpack/development/react-client-manifest.json' , 'utf8' ) )
4035
41- let renderResult : null | PassThrough = null ;
36+ const streamRenderRSCComponent = ( reactElement : ReactElement , options : RenderParams ) : Readable => {
37+ const { throwJsErrors } = options ;
38+ const renderState : StreamRenderState = {
39+ result : null ,
40+ hasErrors : false ,
41+ isShellReady : true
42+ } ;
4243
44+ const { pipeToTransform, readableStream, emitError } = transformRenderStreamChunksToResultObject ( renderState ) ;
4345 try {
44- const componentObj = ComponentRegistry . get ( name ) ;
45- if ( componentObj . isRenderer ) {
46- throw new Error ( `\
47- Detected a renderer while server rendering component '${ name } '. \
48- See https://github.com/shakacode/react_on_rails#renderer-functions` ) ;
49- }
50-
51- const reactRenderingResult = createReactOutput ( {
52- componentObj,
53- domNodeId,
54- trace,
55- props,
56- railsContext,
57- } ) ;
58-
59- if ( isServerRenderHash ( reactRenderingResult ) || isPromise ( reactRenderingResult ) ) {
60- throw new Error ( 'Server rendering of streams is not supported for server render hashes or promises.' ) ;
61- }
62-
63- renderResult = new PassThrough ( ) ;
64- let finalValue = "" ;
65- const streamReader = renderToReadableStream ( reactRenderingResult , getBundleConfig ( ) ) . getReader ( ) ;
66- const decoder = new TextDecoder ( ) ;
67- const processStream = async ( ) => {
68- const { done, value } = await streamReader . read ( ) ;
69- if ( done ) {
70- renderResult ?. push ( null ) ;
71- // @ts -expect-error value is not typed
72- debugConsole . log ( 'value' , finalValue ) ;
73- return ;
46+ const rscStream = renderToPipeableStream (
47+ reactElement ,
48+ getBundleConfig ( ) ,
49+ {
50+ onError : ( err ) => {
51+ const error = convertToError ( err ) ;
52+ console . error ( "Error in RSC stream" , error ) ;
53+ if ( throwJsErrors ) {
54+ emitError ( error ) ;
55+ }
56+ renderState . hasErrors = true ;
57+ renderState . error = error ;
58+ }
7459 }
75-
76- finalValue += decoder . decode ( value ) ;
77- renderResult ?. push ( value ) ;
78- processStream ( ) ;
79- }
80- processStream ( ) ;
81- } catch ( e : unknown ) {
82- if ( throwJsErrors ) {
83- throw e ;
84- }
85-
86- renderResult = stringToStream ( `Error: ${ e } ` ) ;
60+ ) ;
61+ pipeToTransform ( rscStream ) ;
62+ return readableStream ;
63+ } catch ( e ) {
64+ const error = convertToError ( e ) ;
65+ renderState . hasErrors = true ;
66+ renderState . error = error ;
67+ const htmlResult = handleError ( { e : error , name : options . name , serverSide : true } ) ;
68+ const jsonResult = JSON . stringify ( createResultObject ( htmlResult , buildConsoleReplay ( ) , renderState ) ) ;
69+ return stringToStream ( jsonResult ) ;
8770 }
71+ } ;
8872
89- return renderResult ;
73+ ReactOnRails . serverRenderRSCReactComponent = ( options : RenderParams ) => {
74+ try {
75+ return streamServerRenderedComponent ( options , streamRenderRSCComponent ) ;
76+ } finally {
77+ console . history = [ ] ;
78+ }
9079} ;
9180
9281export * from './types' ;
0 commit comments