@@ -105,16 +105,22 @@ function createResultObject(html: string | null, consoleReplayScript: string, re
105105
106106async function createPromiseResult (
107107 renderState : RenderState & { result : Promise < string > } ,
108- consoleReplayScript : string ,
109108 componentName : string ,
110109 throwJsErrors : boolean
111110) : 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 ;
112116 try {
113117 const html = await renderState . result ;
118+ const consoleReplayScript = buildConsoleReplay ( consoleHistory ) ;
114119 return createResultObject ( html , consoleReplayScript , renderState ) ;
115120 } catch ( e : unknown ) {
116121 const errorRenderState = handleRenderingError ( e , { componentName, throwJsErrors } ) ;
117- return createResultObject ( errorRenderState . result , consoleReplayScript , errorRenderState ) ;
122+ const consoleReplayScript = buildConsoleReplay ( consoleHistory ) ;
123+ return createResultObject ( errorRenderState . result , consoleReplayScript , renderState ) ;
118124 }
119125}
120126
@@ -123,20 +129,12 @@ function createFinalResult(
123129 componentName : string ,
124130 throwJsErrors : boolean
125131) : 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-
135132 const { result } = renderState ;
136133 if ( isPromise ( result ) ) {
137- return createPromiseResult ( { ...renderState , result } , consoleReplayScript , componentName , throwJsErrors ) ;
134+ return createPromiseResult ( { ...renderState , result } , componentName , throwJsErrors ) ;
138135 }
139136
137+ const consoleReplayScript = buildConsoleReplay ( ) ;
140138 return JSON . stringify ( createResultObject ( result , consoleReplayScript , renderState ) ) ;
141139}
142140
@@ -183,13 +181,19 @@ function serverRenderReactComponentInternal(options: RenderParams): null | strin
183181}
184182
185183const serverRenderReactComponent : typeof serverRenderReactComponentInternal = ( options ) => {
184+ let result : string | Promise < RenderResult > | null = null ;
186185 try {
187- return serverRenderReactComponentInternal ( options ) ;
186+ result = serverRenderReactComponentInternal ( options ) ;
188187 } finally {
189188 // Reset console history after each render.
190189 // 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+ }
192195 }
196+ return result ;
193197} ;
194198
195199export default serverRenderReactComponent ;
0 commit comments