From c3c8bd989bdadabdb680008a1c46a09c7130c501 Mon Sep 17 00:00:00 2001 From: Sebastian Markbage Date: Wed, 28 Aug 2024 22:57:23 -0400 Subject: [PATCH 1/4] Add Environment Name Component Filter in the Backend --- .../src/backend/fiber/renderer.js | 58 +++++++++++++++---- .../src/frontend/types.js | 13 ++++- 2 files changed, 57 insertions(+), 14 deletions(-) diff --git a/packages/react-devtools-shared/src/backend/fiber/renderer.js b/packages/react-devtools-shared/src/backend/fiber/renderer.js index 47cb12bf17a..caa83c30ad6 100644 --- a/packages/react-devtools-shared/src/backend/fiber/renderer.js +++ b/packages/react-devtools-shared/src/backend/fiber/renderer.js @@ -14,6 +14,7 @@ import { ComponentFilterElementType, ComponentFilterHOC, ComponentFilterLocation, + ComponentFilterEnvironmentName, ElementTypeClass, ElementTypeContext, ElementTypeFunction, @@ -1099,6 +1100,7 @@ export function attach( const hideElementsWithDisplayNames: Set = new Set(); const hideElementsWithPaths: Set = new Set(); const hideElementsWithTypes: Set = new Set(); + const hideElementsWithEnvs: Set = new Set(); // Highlight updates let traceUpdatesEnabled: boolean = false; @@ -1108,6 +1110,7 @@ export function attach( hideElementsWithTypes.clear(); hideElementsWithDisplayNames.clear(); hideElementsWithPaths.clear(); + hideElementsWithEnvs.clear(); componentFilters.forEach(componentFilter => { if (!componentFilter.isEnabled) { @@ -1133,6 +1136,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}"`, @@ -1215,7 +1221,10 @@ export function attach( flushPendingEvents(); } - function shouldFilterVirtual(data: ReactComponentInfo): boolean { + 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 @@ -1236,6 +1245,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; } @@ -1294,6 +1311,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); @@ -2489,7 +2526,8 @@ 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 (shouldFilterVirtual(componentInfo, secondaryEnv)) { // Skip. continue; } @@ -2511,10 +2549,6 @@ export function attach( ); } previousVirtualInstance = createVirtualInstance(componentInfo); - const secondaryEnv = getSecondaryEnvironmentName( - fiber._debugInfo, - i, - ); recordVirtualMount( previousVirtualInstance, reconcilingParent, @@ -2919,7 +2953,11 @@ export function attach( continue; } const componentInfo: ReactComponentInfo = (debugEntry: any); - if (shouldFilterVirtual(componentInfo)) { + const secondaryEnv = getSecondaryEnvironmentName( + nextChild._debugInfo, + i, + ); + if (shouldFilterVirtual(componentInfo, secondaryEnv)) { continue; } if (level === virtualLevel) { @@ -2983,10 +3021,6 @@ export function attach( } else { // Otherwise we create a new instance. const newVirtualInstance = createVirtualInstance(componentInfo); - const secondaryEnv = getSecondaryEnvironmentName( - nextChild._debugInfo, - i, - ); recordVirtualMount( newVirtualInstance, reconcilingParent, @@ -3925,7 +3959,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; diff --git a/packages/react-devtools-shared/src/frontend/types.js b/packages/react-devtools-shared/src/frontend/types.js index 05fa9e93333..459015d0500 100644 --- a/packages/react-devtools-shared/src/frontend/types.js +++ b/packages/react-devtools-shared/src/frontend/types.js @@ -76,8 +76,9 @@ export const ComponentFilterElementType = 1; export const ComponentFilterDisplayName = 2; export const ComponentFilterLocation = 3; export const ComponentFilterHOC = 4; +export const ComponentFilterEnvironmentName = 5; -export type ComponentFilterType = 1 | 2 | 3 | 4; +export type ComponentFilterType = 1 | 2 | 3 | 4 | 5; // Hide all elements of types in this Set. // We hide host components only by default. @@ -102,10 +103,18 @@ export type BooleanComponentFilter = { type: 4, }; +export type EnvironmentNameComponentFilter = { + isEnabled: boolean, + isValid: boolean, + type: 5, + value: string, +}; + export type ComponentFilter = | BooleanComponentFilter | ElementTypeComponentFilter - | RegExpComponentFilter; + | RegExpComponentFilter + | EnvironmentNameComponentFilter; export type HookName = string | null; // Map of hook source ("::") to name. From eadedeb0c799d7d7c6751656e72ec5aacfc29b8b Mon Sep 17 00:00:00 2001 From: Sebastian Markbage Date: Wed, 28 Aug 2024 23:12:54 -0400 Subject: [PATCH 2/4] Add Environment Name Filter to the Settings UI --- .../src/__tests__/utils.js | 13 +++++ .../views/Settings/ComponentsSettings.js | 57 ++++++++++++++++++- 2 files changed, 69 insertions(+), 1 deletion(-) diff --git a/packages/react-devtools-shared/src/__tests__/utils.js b/packages/react-devtools-shared/src/__tests__/utils.js index 4a42cf2703f..c22ac6e05dc 100644 --- a/packages/react-devtools-shared/src/__tests__/utils.js +++ b/packages/react-devtools-shared/src/__tests__/utils.js @@ -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, diff --git a/packages/react-devtools-shared/src/devtools/views/Settings/ComponentsSettings.js b/packages/react-devtools-shared/src/devtools/views/Settings/ComponentsSettings.js index a2a6a1d6818..d6ad52b60d0 100644 --- a/packages/react-devtools-shared/src/devtools/views/Settings/ComponentsSettings.js +++ b/packages/react-devtools-shared/src/devtools/views/Settings/ComponentsSettings.js @@ -31,6 +31,7 @@ import { ComponentFilterElementType, ComponentFilterHOC, ComponentFilterLocation, + ComponentFilterEnvironmentName, ElementTypeClass, ElementTypeContext, ElementTypeFunction, @@ -52,6 +53,7 @@ import type { ElementType, ElementTypeComponentFilter, RegExpComponentFilter, + EnvironmentNameComponentFilter, } from 'react-devtools-shared/src/frontend/types'; const vscodeFilepath = 'vscode://file/{path}:{line}'; @@ -146,6 +148,13 @@ export default function ComponentsSettings(_: {}): React.Node { isEnabled: componentFilter.isEnabled, isValid: true, }; + } else if (type === ComponentFilterEnvironmentName) { + cloned[index] = { + type: ComponentFilterEnvironmentName, + isEnabled: componentFilter.isEnabled, + isValid: true, + value: 'Client', + }; } } return cloned; @@ -210,6 +219,29 @@ export default function ComponentsSettings(_: {}): React.Node { [], ); + const updateFilterValueEnvironmentName = useCallback( + (componentFilter: ComponentFilter, value: string) => { + if (componentFilter.type !== ComponentFilterEnvironmentName) { + throw Error('Invalid value for environment name filter'); + } + + setComponentFilters(prevComponentFilters => { + const cloned: Array = [...prevComponentFilters]; + if (componentFilter.type === ComponentFilterEnvironmentName) { + const index = prevComponentFilters.indexOf(componentFilter); + if (index >= 0) { + cloned[index] = { + ...componentFilter, + value, + }; + } + } + return cloned; + }); + }, + [], + ); + const removeFilter = useCallback((index: number) => { setComponentFilters(prevComponentFilters => { const cloned: Array = [...prevComponentFilters]; @@ -246,6 +278,11 @@ export default function ComponentsSettings(_: {}): React.Node { ...((cloned[index]: any): BooleanComponentFilter), isEnabled, }; + } else if (componentFilter.type === ComponentFilterEnvironmentName) { + cloned[index] = { + ...((cloned[index]: any): EnvironmentNameComponentFilter), + isEnabled, + }; } } return cloned; @@ -380,10 +417,14 @@ export default function ComponentsSettings(_: {}): React.Node { + - {componentFilter.type === ComponentFilterElementType && + {(componentFilter.type === ComponentFilterElementType || + componentFilter.type === ComponentFilterEnvironmentName) && 'equals'} {(componentFilter.type === ComponentFilterLocation || componentFilter.type === ComponentFilterDisplayName) && @@ -428,6 +469,20 @@ export default function ComponentsSettings(_: {}): React.Node { value={componentFilter.value} /> )} + {componentFilter.type === ComponentFilterEnvironmentName && ( + + )}