@@ -11,39 +11,42 @@ import type { CreateReactOutputResult, RegisteredComponent, RenderParams, Render
1111type RenderState = {
1212 result : null | string | Promise < string > ;
1313 hasErrors : boolean ;
14- error : null | RenderingError ;
14+ error ?: RenderingError ;
1515} ;
1616
1717type RenderOptions = {
18- name : string ;
18+ componentName : string ;
1919 domNodeId ?: string ;
2020 trace ?: boolean ;
2121 renderingReturnsPromises : boolean ;
2222} ;
2323
24- function validateComponent ( componentObj : RegisteredComponent , name : string ) {
24+ function validateComponent ( componentObj : RegisteredComponent , componentName : string ) {
2525 if ( componentObj . isRenderer ) {
26- throw new Error ( `Detected a renderer while server rendering component '${ name } '. See https://github.com/shakacode/react_on_rails#renderer-functions` ) ;
26+ throw new Error ( `Detected a renderer while server rendering component '${ componentName } '. See https://github.com/shakacode/react_on_rails#renderer-functions` ) ;
2727 }
2828}
2929
30- function processServerRenderHash ( result : ServerRenderResult , options : RenderOptions ) : string {
30+ function processServerRenderHash ( result : ServerRenderResult , options : RenderOptions ) : RenderState {
3131 const { redirectLocation, routeError } = result ;
3232 const hasErrors = ! ! routeError ;
3333
3434 if ( hasErrors ) {
3535 console . error ( `React Router ERROR: ${ JSON . stringify ( routeError ) } ` ) ;
3636 }
3737
38+ let htmlResult : string ;
3839 if ( redirectLocation ) {
3940 if ( options . trace ) {
4041 const redirectPath = redirectLocation . pathname + redirectLocation . search ;
41- console . log ( `ROUTER REDIRECT: ${ options . name } to dom node with id: ${ options . domNodeId } , redirect to ${ redirectPath } ` ) ;
42+ console . log ( `ROUTER REDIRECT: ${ options . componentName } to dom node with id: ${ options . domNodeId } , redirect to ${ redirectPath } ` ) ;
4243 }
43- return '' ;
44+ htmlResult = '' ;
45+ } else {
46+ htmlResult = result . renderedHtml as string ;
4447 }
4548
46- return result . renderedHtml as string ;
49+ return { result : htmlResult , hasErrors } ;
4750}
4851
4952function processPromise ( result : Promise < unknown > , renderingReturnsPromises : boolean ) : Promise < string > | string {
@@ -67,79 +70,78 @@ as a renderFunction and not a simple React Function Component.`);
6770 }
6871}
6972
70- function processRenderingResult ( result : CreateReactOutputResult , options : RenderOptions ) : string | Promise < string > {
73+ function processRenderingResult ( result : CreateReactOutputResult , options : RenderOptions ) : RenderState {
7174 if ( isServerRenderHash ( result ) ) {
7275 return processServerRenderHash ( result , options ) ;
7376 }
7477 if ( isPromise ( result ) ) {
75- return processPromise ( result , options . renderingReturnsPromises ) ;
78+ return { result : processPromise ( result , options . renderingReturnsPromises ) , hasErrors : false } ;
7679 }
77- return processReactElement ( result ) ;
80+ return { result : processReactElement ( result ) , hasErrors : false } ;
7881}
7982
80- function handleRenderingError ( e : Error , renderState : RenderState , options : { name : string , throwJsErrors : boolean } ) {
83+ function handleRenderingError ( e : unknown , options : { componentName : string , throwJsErrors : boolean } ) {
8184 if ( options . throwJsErrors ) {
8285 throw e ;
8386 }
87+ const error = e instanceof Error ? e : new Error ( String ( e ) ) ;
8488 return {
85- ...renderState ,
8689 hasErrors : true ,
87- result : handleError ( { e, name : options . name , serverSide : true } ) ,
88- error : e ,
90+ result : handleError ( { e : error , name : options . componentName , serverSide : true } ) ,
91+ error,
8992 } ;
9093}
9194
92- function createResultObject ( html : string | null , consoleReplayScript : string , hasErrors : boolean , error : RenderingError | null ) : RenderResult {
93- const result : RenderResult = { html, consoleReplayScript, hasErrors } ;
94- if ( error ) {
95- result . renderingError = {
96- message : error . message ,
97- stack : error . stack ,
98- } ;
99- }
100- return result ;
101- }
102-
103- function createSyncResult ( renderState : RenderState & { result : string | null } , consoleReplayScript : string ) : RenderResult {
104- return createResultObject ( renderState . result , consoleReplayScript , renderState . hasErrors , renderState . error ) ;
95+ function createResultObject ( html : string | null , consoleReplayScript : string , hasErrors : boolean , error ?: RenderingError ) : RenderResult {
96+ return {
97+ html,
98+ consoleReplayScript,
99+ hasErrors,
100+ renderingError : error && { message : error . message , stack : error . stack } ,
101+ } ;
105102}
106103
107- function createPromiseResult ( renderState : RenderState & { result : Promise < string > } , consoleReplayScript : string ) : Promise < RenderResult > {
108- return ( async ( ) => {
109- try {
110- const html = await renderState . result ;
111- return createResultObject ( html , consoleReplayScript , renderState . hasErrors , renderState . error ) ;
112- } catch ( e : unknown ) {
113- const error = e instanceof Error ? e : new Error ( String ( e ) ) ;
114- const html = handleError ( { e : error , name : 'Unknown' , serverSide : true } ) ;
115- return createResultObject ( html , consoleReplayScript , true , error ) ;
116- }
117- } ) ( ) ;
104+ async function createPromiseResult (
105+ renderState : RenderState & { result : Promise < string > } ,
106+ consoleReplayScript : string ,
107+ componentName : string ,
108+ throwJsErrors : boolean
109+ ) : Promise < RenderResult > {
110+ try {
111+ const html = await renderState . result ;
112+ return createResultObject ( html , consoleReplayScript , renderState . hasErrors , renderState . error ) ;
113+ } catch ( e : unknown ) {
114+ const errorRenderState = handleRenderingError ( e , { componentName, throwJsErrors } ) ;
115+ return createResultObject ( errorRenderState . result , consoleReplayScript , errorRenderState . hasErrors , errorRenderState . error ) ;
116+ }
118117}
119118
120- function createFinalResult ( renderState : RenderState ) : null | string | Promise < RenderResult > {
119+ function createFinalResult (
120+ renderState : RenderState ,
121+ componentName : string ,
122+ throwJsErrors : boolean
123+ ) : null | string | Promise < RenderResult > {
121124 const consoleReplayScript = buildConsoleReplay ( ) ;
122125
123126 const { result } = renderState ;
124127 if ( isPromise ( result ) ) {
125- return createPromiseResult ( { ...renderState , result } , consoleReplayScript ) ;
128+ return createPromiseResult ( { ...renderState , result } , consoleReplayScript , componentName , throwJsErrors ) ;
126129 }
127130
128- return JSON . stringify ( createSyncResult ( { ... renderState , result } , consoleReplayScript ) ) ;
131+ return JSON . stringify ( createResultObject ( result , consoleReplayScript , renderState . hasErrors , renderState . error ) ) ;
129132}
130133
131134function serverRenderReactComponentInternal ( options : RenderParams ) : null | string | Promise < RenderResult > {
132- const { name, domNodeId, trace, props, railsContext, renderingReturnsPromises, throwJsErrors } = options ;
135+ const { name : componentName , domNodeId, trace, props, railsContext, renderingReturnsPromises, throwJsErrors } = options ;
133136
134137 let renderState : RenderState = {
135138 result : null ,
136139 hasErrors : false ,
137- error : null ,
138140 } ;
139141
140142 try {
141- const componentObj = ComponentRegistry . get ( name ) ;
142- validateComponent ( componentObj , name ) ;
143+ const componentObj = ComponentRegistry . get ( componentName ) ;
144+ validateComponent ( componentObj , componentName ) ;
143145
144146 // Renders the component or executes the render function
145147 // - If the registered component is a React element or component, it renders it
@@ -149,13 +151,14 @@ function serverRenderReactComponentInternal(options: RenderParams): null | strin
149151 // - For other values (e.g., strings), it returns them directly
150152 // Note: Only synchronous operations are performed at this stage
151153 const reactRenderingResult = createReactOutput ( { componentObj, domNodeId, trace, props, railsContext } ) ;
154+
152155 // Processes the result from createReactOutput:
153156 // 1. Converts React elements to HTML strings
154157 // 2. Returns rendered HTML from serverRenderHash
155158 // 3. Handles promises for async rendering
156- renderState . result = processRenderingResult ( reactRenderingResult , { name , domNodeId, trace, renderingReturnsPromises } ) ;
157- } catch ( e ) {
158- renderState = handleRenderingError ( e as Error , renderState , { name , throwJsErrors } ) ;
159+ renderState = processRenderingResult ( reactRenderingResult , { componentName , domNodeId, trace, renderingReturnsPromises } ) ;
160+ } catch ( e : unknown ) {
161+ renderState = handleRenderingError ( e , { componentName , throwJsErrors } ) ;
159162 }
160163
161164 // Finalize the rendering result and prepare it for server response
@@ -167,7 +170,7 @@ function serverRenderReactComponentInternal(options: RenderParams): null | strin
167170 // - hasErrors: boolean (Indicates if any errors occurred during rendering)
168171 // - renderingError: Error | null (The error object if an error occurred, null otherwise)
169172 // 4. For Promise results, it awaits resolution before creating the final JSON
170- return createFinalResult ( renderState ) ;
173+ return createFinalResult ( renderState , componentName , throwJsErrors ) ;
171174}
172175
173176const serverRenderReactComponent : typeof serverRenderReactComponentInternal = ( options ) => {
0 commit comments