Skip to content

Commit c054691

Browse files
committed
Serialize console arguments using a special model serializer
This allows complex objects to be logged and inspected on the client. We limit the depth of objects after 20 objects are written. This deals with cycles too. Because these objects aren't the complete model we can't cache them but if the full thing is already written, we can reuse that. This might not be fully ok since if there's an error:ed reference inside of it, it would error the log. This should never error nor suspend. Functions are emitted as eval of their source string to produce a similar looking function on the client. Unresolved Promises are serialized as infinite promises.
1 parent b73f0ab commit c054691

File tree

2 files changed

+342
-9
lines changed

2 files changed

+342
-9
lines changed

packages/react-client/src/ReactFlightClient.js

+23-3
Original file line numberDiff line numberDiff line change
@@ -644,6 +644,10 @@ function parseModelString(
644644
}
645645
case '@': {
646646
// Promise
647+
if (value.length === 2) {
648+
// Infinite promise that never resolves.
649+
return new Promise(() => {});
650+
}
647651
const id = parseInt(value.slice(2), 16);
648652
const chunk = getChunk(response, id);
649653
return chunk;
@@ -699,6 +703,21 @@ function parseModelString(
699703
// BigInt
700704
return BigInt(value.slice(2));
701705
}
706+
case 'E': {
707+
if (__DEV__) {
708+
// In DEV mode we allow indirect eval to produce functions for logging.
709+
// This should not compile to eval() because then it has local scope access.
710+
try {
711+
// eslint-disable-next-line no-eval
712+
return (0, eval)(value.slice(2));
713+
} catch (x) {
714+
// We currently use this to express functions so we fail parsing it,
715+
// let's just return a blank function as a place holder.
716+
return function () {};
717+
}
718+
}
719+
// Fallthrough
720+
}
702721
default: {
703722
// We assume that anything else is a reference ID.
704723
const id = parseInt(value.slice(1), 16);
@@ -1039,7 +1058,7 @@ function resolveDebugInfo(
10391058

10401059
function resolveConsoleEntry(
10411060
response: Response,
1042-
payload: [string /*methodName */, string /* stackTrace */, ...any],
1061+
value: UninitializedModel,
10431062
): void {
10441063
if (!__DEV__) {
10451064
// These errors should never make it into a build so we don't need to encode them in codes.json
@@ -1048,6 +1067,8 @@ function resolveConsoleEntry(
10481067
'resolveConsoleEntry should never be called in production mode. This is a bug in React.',
10491068
);
10501069
}
1070+
1071+
const payload: [string, string, mixed] = parseModel(response, value);
10511072
const methodName = payload[0];
10521073
// TODO: Restore the fake stack before logging.
10531074
// const stackTrace = payload[1];
@@ -1209,8 +1230,7 @@ function processFullRow(
12091230
}
12101231
case 87 /* "W" */: {
12111232
if (__DEV__) {
1212-
const payload = JSON.parse(row);
1213-
resolveConsoleEntry(response, payload);
1233+
resolveConsoleEntry(response, row);
12141234
return;
12151235
}
12161236
throw new Error(

0 commit comments

Comments
 (0)