Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/react-client/src/ReactFlightClient.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ import {getOwnerStackByComponentInfoInDev} from 'shared/ReactComponentInfoStack'

import {injectInternals} from './ReactFlightClientDevToolsHook';

import {OMITTED_PROP_ERROR} from './ReactFlightPropertyAccess';
import {OMITTED_PROP_ERROR} from 'shared/ReactFlightPropertyAccess';

import ReactVersion from 'shared/ReactVersion';

Expand Down
169 changes: 35 additions & 134 deletions packages/react-client/src/ReactFlightPerformanceTrack.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ import type {

import {enableProfilerTimer} from 'shared/ReactFeatureFlags';

import {OMITTED_PROP_ERROR} from './ReactFlightPropertyAccess';

import hasOwnProperty from 'shared/hasOwnProperty';
import isArray from 'shared/isArray';
import {
addValueToProperties,
addObjectToProperties,
} from 'shared/ReactPerformanceTrackProperties';

const supportsUserTiming =
enableProfilerTimer &&
Expand All @@ -33,127 +33,6 @@ const supportsUserTiming =
const IO_TRACK = 'Server Requests ⚛';
const COMPONENTS_TRACK = 'Server Components ⚛';

const EMPTY_ARRAY = 0;
const COMPLEX_ARRAY = 1;
const PRIMITIVE_ARRAY = 2; // Primitive values only
const ENTRIES_ARRAY = 3; // Tuple arrays of string and value (like Headers, Map, etc)
function getArrayKind(array: Object): 0 | 1 | 2 | 3 {
let kind = EMPTY_ARRAY;
for (let i = 0; i < array.length; i++) {
const value = array[i];
if (typeof value === 'object' && value !== null) {
if (
isArray(value) &&
value.length === 2 &&
typeof value[0] === 'string'
) {
// Key value tuple
if (kind !== EMPTY_ARRAY && kind !== ENTRIES_ARRAY) {
return COMPLEX_ARRAY;
}
kind = ENTRIES_ARRAY;
} else {
return COMPLEX_ARRAY;
}
} else if (typeof value === 'function') {
return COMPLEX_ARRAY;
} else if (typeof value === 'string' && value.length > 50) {
return COMPLEX_ARRAY;
} else if (kind !== EMPTY_ARRAY && kind !== PRIMITIVE_ARRAY) {
return COMPLEX_ARRAY;
} else {
kind = PRIMITIVE_ARRAY;
}
}
return kind;
}

function addObjectToProperties(
object: Object,
properties: Array<[string, string]>,
indent: number,
): void {
for (const key in object) {
if (hasOwnProperty.call(object, key) && key[0] !== '_') {
const value = object[key];
addValueToProperties(key, value, properties, indent);
}
}
}

function addValueToProperties(
propertyName: string,
value: mixed,
properties: Array<[string, string]>,
indent: number,
): void {
let desc;
switch (typeof value) {
case 'object':
if (value === null) {
desc = 'null';
break;
} else {
// $FlowFixMe[method-unbinding]
const objectToString = Object.prototype.toString.call(value);
let objectName = objectToString.slice(8, objectToString.length - 1);
if (objectName === 'Array') {
const array: Array<any> = (value: any);
const kind = getArrayKind(array);
if (kind === PRIMITIVE_ARRAY || kind === EMPTY_ARRAY) {
desc = JSON.stringify(array);
break;
} else if (kind === ENTRIES_ARRAY) {
properties.push(['\xa0\xa0'.repeat(indent) + propertyName, '']);
for (let i = 0; i < array.length; i++) {
const entry = array[i];
addValueToProperties(entry[0], entry[1], properties, indent + 1);
}
return;
}
}
if (objectName === 'Object') {
const proto: any = Object.getPrototypeOf(value);
if (proto && typeof proto.constructor === 'function') {
objectName = proto.constructor.name;
}
}
properties.push([
'\xa0\xa0'.repeat(indent) + propertyName,
objectName === 'Object' ? '' : objectName,
]);
if (indent < 3) {
addObjectToProperties(value, properties, indent + 1);
}
return;
}
case 'function':
if (value.name === '') {
desc = '() => {}';
} else {
desc = value.name + '() {}';
}
break;
case 'string':
if (value === OMITTED_PROP_ERROR) {
desc = '...';
} else {
desc = JSON.stringify(value);
}
break;
case 'undefined':
desc = 'undefined';
break;
case 'boolean':
desc = value ? 'true' : 'false';
break;
default:
// eslint-disable-next-line react-internal/safe-string-coercion
desc = String(value);
}
properties.push(['\xa0\xa0'.repeat(indent) + propertyName, desc]);
}

export function markAllTracksInOrder() {
if (supportsUserTiming) {
// Ensure we create the Server Component track groups earlier than the Client Scheduler
Expand Down Expand Up @@ -222,17 +101,27 @@ export function logComponentRender(
isPrimaryEnv || env === undefined ? name : name + ' [' + env + ']';
const debugTask = componentInfo.debugTask;
if (__DEV__ && debugTask) {
const properties: Array<[string, string]> = [];
if (componentInfo.key != null) {
addValueToProperties('key', componentInfo.key, properties, 0);
}
if (componentInfo.props != null) {
addObjectToProperties(componentInfo.props, properties, 0);
}
debugTask.run(
// $FlowFixMe[method-unbinding]
console.timeStamp.bind(
console,
entryName,
startTime < 0 ? 0 : startTime,
childrenEndTime,
trackNames[trackIdx],
COMPONENTS_TRACK,
color,
),
performance.measure.bind(performance, entryName, {
start: startTime < 0 ? 0 : startTime,
end: childrenEndTime,
detail: {
devtools: {
color: color,
track: trackNames[trackIdx],
trackGroup: COMPONENTS_TRACK,
properties,
},
},
}),
);
} else {
console.timeStamp(
Expand Down Expand Up @@ -268,6 +157,12 @@ export function logComponentAborted(
'The stream was aborted before this Component finished rendering.',
],
];
if (componentInfo.key != null) {
addValueToProperties('key', componentInfo.key, properties, 0);
}
if (componentInfo.props != null) {
addObjectToProperties(componentInfo.props, properties, 0);
}
performance.measure(entryName, {
start: startTime < 0 ? 0 : startTime,
end: childrenEndTime,
Expand Down Expand Up @@ -319,6 +214,12 @@ export function logComponentErrored(
: // eslint-disable-next-line react-internal/safe-string-coercion
String(error);
const properties = [['Error', message]];
if (componentInfo.key != null) {
addValueToProperties('key', componentInfo.key, properties, 0);
}
if (componentInfo.props != null) {
addObjectToProperties(componentInfo.props, properties, 0);
}
performance.measure(entryName, {
start: startTime < 0 ? 0 : startTime,
end: childrenEndTime,
Expand Down
25 changes: 21 additions & 4 deletions packages/react-reconciler/src/ReactFiberPerformanceTrack.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ import {
includesOnlyHydrationOrOffscreenLanes,
} from './ReactFiberLane';

import {
addValueToProperties,
addObjectToProperties,
} from 'shared/ReactPerformanceTrackProperties';

import {enableProfilerTimer} from 'shared/ReactFeatureFlags';

const supportsUserTiming =
Expand Down Expand Up @@ -239,7 +244,7 @@ export function logComponentErrored(
typeof performance.measure === 'function'
) {
let debugTask: ?ConsoleTask = null;
const properties = [];
const properties: Array<[string, string]> = [];
for (let i = 0; i < errors.length; i++) {
const capturedValue = errors[i];
if (debugTask == null && capturedValue.source !== null) {
Expand All @@ -261,6 +266,12 @@ export function logComponentErrored(
String(error);
properties.push(['Error', message]);
}
if (fiber.key !== null) {
addValueToProperties('key', fiber.key, properties, 0);
}
if (fiber.memoizedProps !== null) {
addObjectToProperties(fiber.memoizedProps, properties, 0);
}
if (debugTask == null) {
// If the captured values don't have a debug task, fallback to the
// error boundary itself.
Expand Down Expand Up @@ -320,7 +331,7 @@ function logComponentEffectErrored(
// $FlowFixMe[method-unbinding]
typeof performance.measure === 'function'
) {
const properties = [];
const properties: Array<[string, string]> = [];
for (let i = 0; i < errors.length; i++) {
const capturedValue = errors[i];
const error = capturedValue.value;
Expand All @@ -334,6 +345,12 @@ function logComponentEffectErrored(
String(error);
properties.push(['Error', message]);
}
if (fiber.key !== null) {
addValueToProperties('key', fiber.key, properties, 0);
}
if (fiber.memoizedProps !== null) {
addObjectToProperties(fiber.memoizedProps, properties, 0);
}
const options = {
start: startTime,
end: endTime,
Expand Down Expand Up @@ -804,7 +821,7 @@ export function logRecoveredRenderPhase(
// $FlowFixMe[method-unbinding]
typeof performance.measure === 'function'
) {
const properties = [];
const properties: Array<[string, string]> = [];
for (let i = 0; i < recoverableErrors.length; i++) {
const capturedValue = recoverableErrors[i];
const error = capturedValue.value;
Expand Down Expand Up @@ -928,7 +945,7 @@ export function logCommitErrored(
// $FlowFixMe[method-unbinding]
typeof performance.measure === 'function'
) {
const properties = [];
const properties: Array<[string, string]> = [];
for (let i = 0; i < errors.length; i++) {
const capturedValue = errors[i];
const error = capturedValue.value;
Expand Down
Loading
Loading