Skip to content

Commit

Permalink
Track component logs
Browse files Browse the repository at this point in the history
  • Loading branch information
sebmarkbage committed Sep 9, 2024
1 parent e07235b commit fe37736
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 23 deletions.
70 changes: 47 additions & 23 deletions packages/react-devtools-shared/src/backend/fiber/renderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,9 @@ import {
SERVER_CONTEXT_SYMBOL_STRING,
} from '../shared/ReactSymbols';
import {enableStyleXFeatures} from 'react-devtools-feature-flags';

import {componentInfoToComponentLogsMap} from '../shared/DevToolsServerComponentLogs';

import is from 'shared/objectIs';
import hasOwnProperty from 'shared/hasOwnProperty';

Expand Down Expand Up @@ -1001,7 +1004,8 @@ export function attach(
// Note, this only clears logs for Fibers that have instances. If they're filtered
// and then mount, the logs are there. Ensuring we only clear what you've seen.
// If we wanted to clear the whole set, we'd replace fiberToComponentLogsMap with a
// new WeakMap.
// new WeakMap. It's unclear whether we should clear componentInfoToComponentLogsMap
// since it's shared by other renderers but presumably it would.

// eslint-disable-next-line no-for-of-loops/no-for-of-loops
for (const devtoolsInstance of idToDevToolsInstanceMap.values()) {
Expand All @@ -1012,7 +1016,7 @@ export function attach(
fiberToComponentLogsMap.delete(fiber.alternate);
}
} else {
// TODO: Handle VirtualInstance.
componentInfoToComponentLogsMap.delete(devtoolsInstance.data);
}
const changed = recordConsoleLogs(devtoolsInstance, undefined);
if (changed) {
Expand All @@ -1025,28 +1029,27 @@ export function attach(
function clearConsoleLogsHelper(instanceID: number, type: 'error' | 'warn') {
const devtoolsInstance = idToDevToolsInstanceMap.get(instanceID);
if (devtoolsInstance !== undefined) {
let componentLogsEntry;
if (devtoolsInstance.kind === FIBER_INSTANCE) {
const fiber = devtoolsInstance.data;
const componentLogsEntry = fiberToComponentLogsMap.get(fiber);
if (componentLogsEntry !== undefined) {
if (type === 'error') {
componentLogsEntry.errors.clear();
componentLogsEntry.errorsCount = 0;
} else {
componentLogsEntry.warnings.clear();
componentLogsEntry.warningsCount = 0;
}
const changed = recordConsoleLogs(
devtoolsInstance,
componentLogsEntry,
);
if (changed) {
flushPendingEvents();
updateMostRecentlyInspectedElementIfNecessary(devtoolsInstance.id);
}
}
componentLogsEntry = fiberToComponentLogsMap.get(fiber);
} else {
// TODO: Handle VirtualInstance.
const componentInfo = devtoolsInstance.data;
componentLogsEntry = componentInfoToComponentLogsMap.get(componentInfo);
}
if (componentLogsEntry !== undefined) {
if (type === 'error') {
componentLogsEntry.errors.clear();
componentLogsEntry.errorsCount = 0;
} else {
componentLogsEntry.warnings.clear();
componentLogsEntry.warningsCount = 0;
}
const changed = recordConsoleLogs(devtoolsInstance, componentLogsEntry);
if (changed) {
flushPendingEvents();
updateMostRecentlyInspectedElementIfNecessary(devtoolsInstance.id);
}
}
}
}
Expand Down Expand Up @@ -2207,6 +2210,10 @@ export function attach(
pushOperation(ownerID);
pushOperation(displayNameStringID);
pushOperation(keyStringID);

const componentLogsEntry =
componentInfoToComponentLogsMap.get(componentInfo);
recordConsoleLogs(instance, componentLogsEntry);
}

function recordUnmount(fiberInstance: FiberInstance): void {
Expand Down Expand Up @@ -2886,6 +2893,14 @@ export function attach(
) {
recordResetChildren(virtualInstance);
}
// Update the errors/warnings count. If this Instance has switched to a different
// ReactComponentInfo instance, such as when refreshing Server Components, then
// we replace all the previous logs with the ones associated with the new ones rather
// than merging. Because deduping is expected to happen at the request level.
const componentLogsEntry = componentInfoToComponentLogsMap.get(
virtualInstance.data,
);
recordConsoleLogs(virtualInstance, componentLogsEntry);
// Must be called after all children have been appended.
recordVirtualProfilingDurations(virtualInstance);
} finally {
Expand Down Expand Up @@ -4335,6 +4350,9 @@ export function attach(
stylex: null,
};

const componentLogsEntry =
componentInfoToComponentLogsMap.get(componentInfo);

return {
id: virtualInstance.id,

Expand Down Expand Up @@ -4368,8 +4386,14 @@ export function attach(
hooks: null,
props: props,
state: null,
errors: [], // TODO: Handle errors on Virtual Instances.
warnings: [], // TODO: Handle warnings on Virtual Instances.
errors:
componentLogsEntry === undefined
? []
: Array.from(componentLogsEntry.errors.entries()),
warnings:
componentLogsEntry === undefined
? []
: Array.from(componentLogsEntry.warnings.entries()),
// List of owners
owners,

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/

// This keeps track of Server Component logs which may come from.
// This is in a shared module because Server Component logs don't come from a specific renderer
// but can become associated with a Virtual Instance of any renderer.

import type {ReactComponentInfo} from 'shared/ReactTypes';

type ComponentLogs = {
errors: Map<string, number>,
errorsCount: number,
warnings: Map<string, number>,
warningsCount: number,
};

// This keeps it around as long as the ComponentInfo is alive which
// lets the Fiber get reparented/remounted and still observe the previous errors/warnings.
// Unless we explicitly clear the logs from a Fiber.
export const componentInfoToComponentLogsMap: WeakMap<
ReactComponentInfo,
ComponentLogs,
> = new WeakMap();

0 comments on commit fe37736

Please sign in to comment.