@@ -105,15 +105,21 @@ function createResultObject(html: string | null, consoleReplayScript: string, ha
105
105
106
106
async function createPromiseResult (
107
107
renderState : RenderState & { result : Promise < string > } ,
108
- consoleReplayScript : string ,
109
108
componentName : string ,
110
109
throwJsErrors : boolean
111
110
) : Promise < RenderResult > {
111
+ // Capture console history before awaiting the promise
112
+ // Node renderer will reset the global console.history after executing the synchronous part of the request.
113
+ // It resets it only if replayServerAsyncOperationLogs renderer config is set to false.
114
+ // In both cases, we need to keep a reference to console.history to avoid losing console logs in case of reset.
115
+ const consoleHistory = console . history ;
112
116
try {
113
117
const html = await renderState . result ;
118
+ const consoleReplayScript = buildConsoleReplay ( consoleHistory ) ;
114
119
return createResultObject ( html , consoleReplayScript , renderState . hasErrors , renderState . error ) ;
115
120
} catch ( e : unknown ) {
116
121
const errorRenderState = handleRenderingError ( e , { componentName, throwJsErrors } ) ;
122
+ const consoleReplayScript = buildConsoleReplay ( consoleHistory ) ;
117
123
return createResultObject ( errorRenderState . result , consoleReplayScript , errorRenderState . hasErrors , errorRenderState . error ) ;
118
124
}
119
125
}
@@ -123,20 +129,12 @@ function createFinalResult(
123
129
componentName : string ,
124
130
throwJsErrors : boolean
125
131
) : null | string | Promise < RenderResult > {
126
- // Console history is stored globally in `console.history`.
127
- // If node renderer is handling a render request that returns a promise,
128
- // It can handle another request while awaiting the promise.
129
- // To prevent cross-request console logs leakage between these requests,
130
- // we build the consoleReplayScript before awaiting any promises.
131
- // The console history is reset after the synchronous part of each request.
132
- // This causes console logs happening during async operations to not be captured.
133
- const consoleReplayScript = buildConsoleReplay ( ) ;
134
-
135
132
const { result } = renderState ;
136
133
if ( isPromise ( result ) ) {
137
- return createPromiseResult ( { ...renderState , result } , consoleReplayScript , componentName , throwJsErrors ) ;
134
+ return createPromiseResult ( { ...renderState , result } , componentName , throwJsErrors ) ;
138
135
}
139
136
137
+ const consoleReplayScript = buildConsoleReplay ( ) ;
140
138
return JSON . stringify ( createResultObject ( result , consoleReplayScript , renderState . hasErrors , renderState . error ) ) ;
141
139
}
142
140
@@ -183,13 +181,19 @@ function serverRenderReactComponentInternal(options: RenderParams): null | strin
183
181
}
184
182
185
183
const serverRenderReactComponent : typeof serverRenderReactComponentInternal = ( options ) => {
184
+ let result : string | Promise < RenderResult > | null = null ;
186
185
try {
187
- return serverRenderReactComponentInternal ( options ) ;
186
+ result = serverRenderReactComponentInternal ( options ) ;
188
187
} finally {
189
188
// Reset console history after each render.
190
189
// See `RubyEmbeddedJavaScript.console_polyfill` for initialization.
191
- console . history = [ ] ;
190
+ // We don't need to clear the console history if the result is a promise
191
+ // Promises only supported in node renderer and node renderer takes care of cleanining console history
192
+ if ( typeof result === 'string' ) {
193
+ console . history = [ ] ;
194
+ }
192
195
}
196
+ return result ;
193
197
} ;
194
198
195
199
export default serverRenderReactComponent ;
0 commit comments