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

[DevTools] Add Filtering of Environment Names #30850

Merged
merged 4 commits into from
Sep 3, 2024
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
13 changes: 13 additions & 0 deletions packages/react-devtools-shared/src/__tests__/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,19 @@ export function createHOCFilter(isEnabled: boolean = true) {
};
}

export function createEnvironmentNameFilter(
env: string,
isEnabled: boolean = true,
) {
const Types = require('react-devtools-shared/src/frontend/types');
return {
type: Types.ComponentFilterEnvironmentName,
isEnabled,
isValid: true,
value: env,
};
}

export function createElementTypeFilter(
elementType: ElementType,
isEnabled: boolean = true,
Expand Down
19 changes: 19 additions & 0 deletions packages/react-devtools-shared/src/backend/agent.js
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@ export default class Agent extends EventEmitter<{
this.updateConsolePatchSettings,
);
bridge.addListener('updateComponentFilters', this.updateComponentFilters);
bridge.addListener('getEnvironmentNames', this.getEnvironmentNames);

// Temporarily support older standalone front-ends sending commands to newer embedded backends.
// We do this because React Native embeds the React DevTools backend,
Expand Down Expand Up @@ -814,6 +815,24 @@ export default class Agent extends EventEmitter<{
}
};

getEnvironmentNames: () => void = () => {
let accumulatedNames = null;
for (const rendererID in this._rendererInterfaces) {
const renderer = this._rendererInterfaces[+rendererID];
const names = renderer.getEnvironmentNames();
if (accumulatedNames === null) {
accumulatedNames = names;
} else {
for (let i = 0; i < names.length; i++) {
if (accumulatedNames.indexOf(names[i]) === -1) {
accumulatedNames.push(names[i]);
}
}
}
}
this._bridge.send('environmentNames', accumulatedNames || []);
};

onTraceUpdates: (nodes: Set<HostInstance>) => void = nodes => {
this.emit('traceUpdates', nodes);
};
Expand Down
80 changes: 68 additions & 12 deletions packages/react-devtools-shared/src/backend/fiber/renderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
ComponentFilterElementType,
ComponentFilterHOC,
ComponentFilterLocation,
ComponentFilterEnvironmentName,
ElementTypeClass,
ElementTypeContext,
ElementTypeFunction,
Expand Down Expand Up @@ -721,6 +722,11 @@ export function getInternalReactConstants(version: string): {
};
}

// All environment names we've seen so far. This lets us create a list of filters to apply.
// This should ideally include env of filtered Components too so that you can add those as
// filters at the same time as removing some other filter.
const knownEnvironmentNames: Set<string> = new Set();

// Map of one or more Fibers in a pair to their unique id number.
// We track both Fibers to support Fast Refresh,
// which may forcefully replace one of the pair as part of hot reloading.
Expand Down Expand Up @@ -1099,6 +1105,7 @@ export function attach(
const hideElementsWithDisplayNames: Set<RegExp> = new Set();
const hideElementsWithPaths: Set<RegExp> = new Set();
const hideElementsWithTypes: Set<ElementType> = new Set();
const hideElementsWithEnvs: Set<string> = new Set();

// Highlight updates
let traceUpdatesEnabled: boolean = false;
Expand All @@ -1108,6 +1115,7 @@ export function attach(
hideElementsWithTypes.clear();
hideElementsWithDisplayNames.clear();
hideElementsWithPaths.clear();
hideElementsWithEnvs.clear();

componentFilters.forEach(componentFilter => {
if (!componentFilter.isEnabled) {
Expand All @@ -1133,6 +1141,9 @@ export function attach(
case ComponentFilterHOC:
hideElementsWithDisplayNames.add(new RegExp('\\('));
break;
case ComponentFilterEnvironmentName:
hideElementsWithEnvs.add(componentFilter.value);
break;
default:
console.warn(
`Invalid component filter type "${componentFilter.type}"`,
Expand Down Expand Up @@ -1215,7 +1226,14 @@ export function attach(
flushPendingEvents();
}

function shouldFilterVirtual(data: ReactComponentInfo): boolean {
function getEnvironmentNames(): Array<string> {
return Array.from(knownEnvironmentNames);
}

function shouldFilterVirtual(
data: ReactComponentInfo,
secondaryEnv: null | string,
): boolean {
// For purposes of filtering Server Components are always Function Components.
// Environment will be used to filter Server vs Client.
// Technically they can be forwardRef and memo too but those filters will go away
Expand All @@ -1236,6 +1254,14 @@ export function attach(
}
}

if (
(data.env == null || hideElementsWithEnvs.has(data.env)) &&
(secondaryEnv === null || hideElementsWithEnvs.has(secondaryEnv))
) {
// If a Component has two environments, you have to filter both for it not to appear.
return true;
}

return false;
}

Expand Down Expand Up @@ -1294,6 +1320,26 @@ export function attach(
}
}

if (hideElementsWithEnvs.has('Client')) {
// If we're filtering out the Client environment we should filter out all
// "Client Components". Technically that also includes the built-ins but
// since that doesn't actually include any additional code loading it's
// useful to not filter out the built-ins. Those can be filtered separately.
// There's no other way to filter out just Function components on the Client.
// Therefore, this only filters Class and Function components.
switch (tag) {
case ClassComponent:
case IncompleteClassComponent:
case IncompleteFunctionComponent:
case FunctionComponent:
case IndeterminateComponent:
case ForwardRef:
case MemoComponent:
case SimpleMemoComponent:
return true;
}
}

/* DISABLED: https://github.com/facebook/react/pull/28417
if (hideElementsWithPaths.size > 0) {
const source = getSourceForFiber(fiber);
Expand Down Expand Up @@ -2489,7 +2535,14 @@ export function attach(
}
// Scan up until the next Component to see if this component changed environment.
const componentInfo: ReactComponentInfo = (debugEntry: any);
if (shouldFilterVirtual(componentInfo)) {
const secondaryEnv = getSecondaryEnvironmentName(fiber._debugInfo, i);
if (componentInfo.env != null) {
knownEnvironmentNames.add(componentInfo.env);
}
if (secondaryEnv !== null) {
knownEnvironmentNames.add(secondaryEnv);
}
if (shouldFilterVirtual(componentInfo, secondaryEnv)) {
// Skip.
continue;
}
Expand All @@ -2511,10 +2564,6 @@ export function attach(
);
}
previousVirtualInstance = createVirtualInstance(componentInfo);
const secondaryEnv = getSecondaryEnvironmentName(
fiber._debugInfo,
i,
);
recordVirtualMount(
previousVirtualInstance,
reconcilingParent,
Expand Down Expand Up @@ -2919,7 +2968,17 @@ export function attach(
continue;
}
const componentInfo: ReactComponentInfo = (debugEntry: any);
if (shouldFilterVirtual(componentInfo)) {
const secondaryEnv = getSecondaryEnvironmentName(
nextChild._debugInfo,
i,
);
if (componentInfo.env != null) {
knownEnvironmentNames.add(componentInfo.env);
}
if (secondaryEnv !== null) {
knownEnvironmentNames.add(secondaryEnv);
}
if (shouldFilterVirtual(componentInfo, secondaryEnv)) {
continue;
}
if (level === virtualLevel) {
Expand Down Expand Up @@ -2983,10 +3042,6 @@ export function attach(
} else {
// Otherwise we create a new instance.
const newVirtualInstance = createVirtualInstance(componentInfo);
const secondaryEnv = getSecondaryEnvironmentName(
nextChild._debugInfo,
i,
);
recordVirtualMount(
newVirtualInstance,
reconcilingParent,
Expand Down Expand Up @@ -3925,7 +3980,7 @@ export function attach(
owner = ownerFiber._debugOwner;
} else {
const ownerInfo: ReactComponentInfo = (owner: any); // Refined
if (!shouldFilterVirtual(ownerInfo)) {
if (!shouldFilterVirtual(ownerInfo, null)) {
return ownerInfo;
}
owner = ownerInfo.owner;
Expand Down Expand Up @@ -5750,5 +5805,6 @@ export function attach(
storeAsGlobal,
unpatchConsoleForStrictMode,
updateComponentFilters,
getEnvironmentNames,
};
}
6 changes: 6 additions & 0 deletions packages/react-devtools-shared/src/backend/legacy/renderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -1078,6 +1078,11 @@ export function attach(
// Not implemented.
}

function getEnvironmentNames(): Array<string> {
// No RSC support.
return [];
}

function setTraceUpdatesEnabled(enabled: boolean) {
// Not implemented.
}
Expand Down Expand Up @@ -1152,5 +1157,6 @@ export function attach(
storeAsGlobal,
unpatchConsoleForStrictMode,
updateComponentFilters,
getEnvironmentNames,
};
}
1 change: 1 addition & 0 deletions packages/react-devtools-shared/src/backend/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,7 @@ export type RendererInterface = {
) => void,
unpatchConsoleForStrictMode: () => void,
updateComponentFilters: (componentFilters: Array<ComponentFilter>) => void,
getEnvironmentNames: () => Array<string>,

// Timeline profiler interface

Expand Down
2 changes: 2 additions & 0 deletions packages/react-devtools-shared/src/bridge.js
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ export type BackendEvents = {
operations: [Array<number>],
ownersList: [OwnersList],
overrideComponentFilters: [Array<ComponentFilter>],
environmentNames: [Array<string>],
profilingData: [ProfilingDataBackend],
profilingStatus: [boolean],
reloadAppForProfiling: [],
Expand Down Expand Up @@ -237,6 +238,7 @@ type FrontendEvents = {
stopProfiling: [],
storeAsGlobal: [StoreAsGlobalParams],
updateComponentFilters: [Array<ComponentFilter>],
getEnvironmentNames: [],
updateConsolePatchSettings: [ConsolePatchSettings],
viewAttributeSource: [ViewAttributeSourceParams],
viewElementSource: [ElementAndRendererID],
Expand Down
Loading
Loading