@@ -11,39 +11,42 @@ import type { CreateReactOutputResult, RegisteredComponent, RenderParams, Render
11
11
type RenderState = {
12
12
result : null | string | Promise < string > ;
13
13
hasErrors : boolean ;
14
- error : null | RenderingError ;
14
+ error ?: RenderingError ;
15
15
} ;
16
16
17
17
type RenderOptions = {
18
- name : string ;
18
+ componentName : string ;
19
19
domNodeId ?: string ;
20
20
trace ?: boolean ;
21
21
renderingReturnsPromises : boolean ;
22
22
} ;
23
23
24
- function validateComponent ( componentObj : RegisteredComponent , name : string ) {
24
+ function validateComponent ( componentObj : RegisteredComponent , componentName : string ) {
25
25
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` ) ;
27
27
}
28
28
}
29
29
30
- function processServerRenderHash ( result : ServerRenderResult , options : RenderOptions ) : string {
30
+ function processServerRenderHash ( result : ServerRenderResult , options : RenderOptions ) : RenderState {
31
31
const { redirectLocation, routeError } = result ;
32
32
const hasErrors = ! ! routeError ;
33
33
34
34
if ( hasErrors ) {
35
35
console . error ( `React Router ERROR: ${ JSON . stringify ( routeError ) } ` ) ;
36
36
}
37
37
38
+ let htmlResult : string ;
38
39
if ( redirectLocation ) {
39
40
if ( options . trace ) {
40
41
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 } ` ) ;
42
43
}
43
- return '' ;
44
+ htmlResult = '' ;
45
+ } else {
46
+ htmlResult = result . renderedHtml as string ;
44
47
}
45
48
46
- return result . renderedHtml as string ;
49
+ return { result : htmlResult , hasErrors } ;
47
50
}
48
51
49
52
function processPromise ( result : Promise < unknown > , renderingReturnsPromises : boolean ) : Promise < string > | string {
@@ -67,79 +70,78 @@ as a renderFunction and not a simple React Function Component.`);
67
70
}
68
71
}
69
72
70
- function processRenderingResult ( result : CreateReactOutputResult , options : RenderOptions ) : string | Promise < string > {
73
+ function processRenderingResult ( result : CreateReactOutputResult , options : RenderOptions ) : RenderState {
71
74
if ( isServerRenderHash ( result ) ) {
72
75
return processServerRenderHash ( result , options ) ;
73
76
}
74
77
if ( isPromise ( result ) ) {
75
- return processPromise ( result , options . renderingReturnsPromises ) ;
78
+ return { result : processPromise ( result , options . renderingReturnsPromises ) , hasErrors : false } ;
76
79
}
77
- return processReactElement ( result ) ;
80
+ return { result : processReactElement ( result ) , hasErrors : false } ;
78
81
}
79
82
80
- function handleRenderingError ( e : Error , renderState : RenderState , options : { name : string , throwJsErrors : boolean } ) {
83
+ function handleRenderingError ( e : unknown , options : { componentName : string , throwJsErrors : boolean } ) {
81
84
if ( options . throwJsErrors ) {
82
85
throw e ;
83
86
}
87
+ const error = e instanceof Error ? e : new Error ( String ( e ) ) ;
84
88
return {
85
- ...renderState ,
86
89
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,
89
92
} ;
90
93
}
91
94
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
+ } ;
105
102
}
106
103
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
+ }
118
117
}
119
118
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 > {
121
124
const consoleReplayScript = buildConsoleReplay ( ) ;
122
125
123
126
const { result } = renderState ;
124
127
if ( isPromise ( result ) ) {
125
- return createPromiseResult ( { ...renderState , result } , consoleReplayScript ) ;
128
+ return createPromiseResult ( { ...renderState , result } , consoleReplayScript , componentName , throwJsErrors ) ;
126
129
}
127
130
128
- return JSON . stringify ( createSyncResult ( { ... renderState , result } , consoleReplayScript ) ) ;
131
+ return JSON . stringify ( createResultObject ( result , consoleReplayScript , renderState . hasErrors , renderState . error ) ) ;
129
132
}
130
133
131
134
function 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 ;
133
136
134
137
let renderState : RenderState = {
135
138
result : null ,
136
139
hasErrors : false ,
137
- error : null ,
138
140
} ;
139
141
140
142
try {
141
- const componentObj = ComponentRegistry . get ( name ) ;
142
- validateComponent ( componentObj , name ) ;
143
+ const componentObj = ComponentRegistry . get ( componentName ) ;
144
+ validateComponent ( componentObj , componentName ) ;
143
145
144
146
// Renders the component or executes the render function
145
147
// - If the registered component is a React element or component, it renders it
@@ -149,13 +151,14 @@ function serverRenderReactComponentInternal(options: RenderParams): null | strin
149
151
// - For other values (e.g., strings), it returns them directly
150
152
// Note: Only synchronous operations are performed at this stage
151
153
const reactRenderingResult = createReactOutput ( { componentObj, domNodeId, trace, props, railsContext } ) ;
154
+
152
155
// Processes the result from createReactOutput:
153
156
// 1. Converts React elements to HTML strings
154
157
// 2. Returns rendered HTML from serverRenderHash
155
158
// 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 } ) ;
159
162
}
160
163
161
164
// Finalize the rendering result and prepare it for server response
@@ -167,7 +170,7 @@ function serverRenderReactComponentInternal(options: RenderParams): null | strin
167
170
// - hasErrors: boolean (Indicates if any errors occurred during rendering)
168
171
// - renderingError: Error | null (The error object if an error occurred, null otherwise)
169
172
// 4. For Promise results, it awaits resolution before creating the final JSON
170
- return createFinalResult ( renderState ) ;
173
+ return createFinalResult ( renderState , componentName , throwJsErrors ) ;
171
174
}
172
175
173
176
const serverRenderReactComponent : typeof serverRenderReactComponentInternal = ( options ) => {
0 commit comments