Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for replaying logs happen on async server operations #1649

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
2 changes: 1 addition & 1 deletion node_package/src/ReactOnRails.ts
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ ctx.ReactOnRails = {
* Used by Rails server rendering to replay console messages.
*/
buildConsoleReplay(): string {
return buildConsoleReplay();
return buildConsoleReplay(console.history);
},

/**
Expand Down
12 changes: 7 additions & 5 deletions node_package/src/buildConsoleReplay.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,16 @@ declare global {
}
}

export function consoleReplay(): string {
export function consoleReplay(consoleHistory: typeof console['history']): string {
// console.history is a global polyfill used in server rendering.
// Must use Array.isArray instead of instanceof Array the history array is defined outside the vm if node renderer is used.
// In this case, the Array prototype used to define the array is not the same as the one in the global scope inside the vm.
// $FlowFixMe
AbanoubGhadban marked this conversation as resolved.
Show resolved Hide resolved
if (!(console.history instanceof Array)) {
if (!(Array.isArray(consoleHistory))) {
return '';
}

const lines = console.history.map(msg => {
const lines = consoleHistory.map(msg => {
const stringifiedList = msg.arguments.map(arg => {
let val;
try {
Expand All @@ -39,6 +41,6 @@ export function consoleReplay(): string {
return lines.join('\n');
}

export default function buildConsoleReplay(): string {
return RenderUtils.wrapInScriptTags('consoleReplayLog', consoleReplay());
export default function buildConsoleReplay(consoleHistory: typeof console['history']): string {
return RenderUtils.wrapInScriptTags('consoleReplayLog', consoleReplay(consoleHistory));
}
6 changes: 3 additions & 3 deletions node_package/src/isServerRenderResult.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export function isServerRenderHash(testValue: CreateReactOutputResult):
(testValue as ServerRenderResult).error);
}

export function isPromise(testValue: CreateReactOutputResult):
testValue is Promise<string> {
return !!((testValue as Promise<string>).then);
export function isPromise<T>(testValue: CreateReactOutputResult | Promise<T> | string):
alexeyr-ci marked this conversation as resolved.
Show resolved Hide resolved
testValue is Promise<T> {
return !!((testValue as Promise<T>).then);
}
29 changes: 21 additions & 8 deletions node_package/src/serverRenderReactComponent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@ import type { ReactElement } from 'react';

import ComponentRegistry from './ComponentRegistry';
import createReactOutput from './createReactOutput';
import {isServerRenderHash, isPromise} from
'./isServerRenderResult';
import { isServerRenderHash, isPromise } from './isServerRenderResult';
import buildConsoleReplay from './buildConsoleReplay';
import handleError from './handleError';
import type { RenderParams, RenderResult, RenderingError } from './types/index';
Expand Down Expand Up @@ -99,7 +98,7 @@ as a renderFunction and not a simple React Function Component.`);
renderingError = e;
}

const consoleReplayScript = buildConsoleReplay();
alexeyr-ci marked this conversation as resolved.
Show resolved Hide resolved
const consoleHistoryAfterSyncExecution = console.history;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Too confusing to use this getter...timing and globals

Could this be updated only for the pro version node renderer to return a object for the console history?

https://github.com/shakacode/react_on_rails/pull/1649/files#diff-cbb0f0518343f42b78a0ec373eddd1187ffb9275651ddb9758a1160c52176eb6R28-R34

    const reactRenderingResult = createReactOutput({
      componentObj,
      domNodeId,
      trace,
      props,
      railsContext,
    });

const addRenderingErrors = (resultObject: RenderResult, renderError: RenderingError) => {
resultObject.renderingError = { // eslint-disable-line no-param-reassign
message: renderError.message,
Expand All @@ -112,8 +111,16 @@ as a renderFunction and not a simple React Function Component.`);
let promiseResult;

try {
const awaitedRenderResult = await renderResult;
const consoleHistoryAfterAsyncExecution = console.history;
let consoleReplayScript = '';
if ((consoleHistoryAfterAsyncExecution?.length ?? 0) > (consoleHistoryAfterSyncExecution?.length ?? 0)) {
consoleReplayScript = buildConsoleReplay(consoleHistoryAfterAsyncExecution);
} else {
consoleReplayScript = buildConsoleReplay(consoleHistoryAfterSyncExecution);
}
AbanoubGhadban marked this conversation as resolved.
Show resolved Hide resolved
promiseResult = {
html: await renderResult,
html: awaitedRenderResult,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are we basically just moving the await to line 114?

consoleReplayScript,
hasErrors,
};
Expand All @@ -127,7 +134,7 @@ as a renderFunction and not a simple React Function Component.`);
name,
serverSide: true,
}),
consoleReplayScript,
consoleReplayScript: buildConsoleReplay(consoleHistoryAfterSyncExecution),
hasErrors: true,
}
renderingError = e;
Expand All @@ -145,7 +152,7 @@ as a renderFunction and not a simple React Function Component.`);

const result = {
html: renderResult,
consoleReplayScript,
consoleReplayScript: buildConsoleReplay(consoleHistoryAfterSyncExecution),
hasErrors,
} as RenderResult;

Expand All @@ -157,12 +164,18 @@ as a renderFunction and not a simple React Function Component.`);
}

const serverRenderReactComponent: typeof serverRenderReactComponentInternal = (options) => {
let result: string | Promise<RenderResult> | null = null;
try {
return serverRenderReactComponentInternal(options);
result = serverRenderReactComponentInternal(options);
} finally {
// Reset console history after each render.
// See `RubyEmbeddedJavaScript.console_polyfill` for initialization.
console.history = [];
// We don't need to clear the console history if the result is a promise
// Promises only supported in node renderer and node renderer takes care of cleanining console history
AbanoubGhadban marked this conversation as resolved.
Show resolved Hide resolved
if (typeof result === 'string') {
console.history = [];
}
}
return result;
};
export default serverRenderReactComponent;
Loading