diff --git a/packages/react-devtools-core/README.md b/packages/react-devtools-core/README.md index 21f4f697cf3..a42e569697f 100644 --- a/packages/react-devtools-core/README.md +++ b/packages/react-devtools-core/README.md @@ -25,15 +25,30 @@ if (process.env.NODE_ENV !== 'production') { > **NOTE** that this API (`connectToDevTools`) must be (1) run in the same context as React and (2) must be called before React packages are imported (e.g. `react`, `react-dom`, `react-native`). ### `initialize` arguments -| Argument | Description | -|------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `settings` | Optional. If not specified, or received as null, then default settings are used. Can be plain object or a Promise that resolves with the [plain settings object](#Settings). If Promise rejects, the console will not be patched and some console features from React DevTools will not work. | +| Argument | Description | +|---------------------------|-------------| +| `settings` | Optional. If not specified, or received as null, then default settings are used. Can be plain object or a Promise that resolves with the [plain settings object](#Settings). If Promise rejects, the console will not be patched and some console features from React DevTools will not work. | +| `shouldStartProfilingNow` | Optional. Whether to start profiling immediately after installing the hook. Defaults to `false`. | +| `profilingSettings` | Optional. Profiling settings used when `shouldStartProfilingNow` is `true`. Defaults to `{ recordChangeDescriptions: false, recordTimeline: false }`. | +| `componentFilters` | Optional. Array or Promise that resolves to an array of component filters to apply before DevTools connects. Defaults to the built-in host component filter. See [Component filters](#component-filters) for the full spec. | #### `Settings` | Spec | Default value | |--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------| |
{
appendComponentStack: boolean,
breakOnConsoleErrors: boolean,
showInlineWarningsAndErrors: boolean,
hideConsoleLogsInStrictMode: boolean,
disableSecondConsoleLogDimmingInStrictMode: boolean
}
|
{
appendComponentStack: true,
breakOnConsoleErrors: false,
showInlineWarningsAndErrors: true,
hideConsoleLogsInStrictMode: false,
disableSecondConsoleLogDimmingInStrictMode: false
}
| +#### Component filters +Each filter object must include `type` and `isEnabled`. Some filters also require `value` or `isValid`. + +| Type | Required fields | Description | +|------|-----------------|-------------| +| `ComponentFilterElementType` (`1`) | `type`, `isEnabled`, `value: ElementType` | Hides elements of the given element type. DevTools defaults to hiding host components. | +| `ComponentFilterDisplayName` (`2`) | `type`, `isEnabled`, `isValid`, `value: string` | Hides components whose display name matches the provided RegExp string. | +| `ComponentFilterLocation` (`3`) | `type`, `isEnabled`, `isValid`, `value: string` | Hides components whose source location matches the provided RegExp string. | +| `ComponentFilterHOC` (`4`) | `type`, `isEnabled`, `isValid` | Hides higher-order components. | +| `ComponentFilterEnvironmentName` (`5`) | `type`, `isEnabled`, `isValid`, `value: string` | Hides components whose environment name matches the provided string. | +| `ComponentFilterActivitySlice` (`6`) | `type`, `isEnabled`, `isValid`, `activityID`, `rendererID` | Filters activity slices; usually managed by DevTools rather than user code. | + ### `connectToDevTools` options | Prop | Default | Description | |------------------------|---------------|---------------------------------------------------------------------------------------------------------------------------| diff --git a/packages/react-devtools-core/src/backend.js b/packages/react-devtools-core/src/backend.js index 4cca0887c57..262b3bc0412 100644 --- a/packages/react-devtools-core/src/backend.js +++ b/packages/react-devtools-core/src/backend.js @@ -66,9 +66,17 @@ export function initialize( | Promise, shouldStartProfilingNow: boolean = false, profilingSettings?: ProfilingSettings, + maybeComponentFiltersOrComponentFiltersPromise?: + | Array + | Promise>, ) { + const componentFiltersOrComponentFiltersPromise = + maybeComponentFiltersOrComponentFiltersPromise + ? maybeComponentFiltersOrComponentFiltersPromise + : savedComponentFilters; installHook( window, + componentFiltersOrComponentFiltersPromise, maybeSettingsOrSettingsPromise, shouldStartProfilingNow, profilingSettings, @@ -174,19 +182,6 @@ export function connectToDevTools(options: ?ConnectOptions) { }, ); - // The renderer interface doesn't read saved component filters directly, - // because they are generally stored in localStorage within the context of the extension. - // Because of this it relies on the extension to pass filters. - // In the case of the standalone DevTools being used with a website, - // saved filters are injected along with the backend script tag so we shouldn't override them here. - // This injection strategy doesn't work for React Native though. - // Ideally the backend would save the filters itself, but RN doesn't provide a sync storage solution. - // So for now we just fall back to using the default filters... - if (window.__REACT_DEVTOOLS_COMPONENT_FILTERS__ == null) { - // $FlowFixMe[incompatible-use] found when upgrading Flow - bridge.send('overrideComponentFilters', savedComponentFilters); - } - // TODO (npm-packages) Warn if "isBackendStorageAPISupported" // $FlowFixMe[incompatible-call] found when upgrading Flow const agent = new Agent(bridge, isProfiling, onReloadAndProfile); @@ -381,10 +376,6 @@ export function connectWithCustomMessagingProtocol({ }, ); - if (window.__REACT_DEVTOOLS_COMPONENT_FILTERS__ == null) { - bridge.send('overrideComponentFilters', savedComponentFilters); - } - const agent = new Agent(bridge, isProfiling, onReloadAndProfile); if (typeof onReloadAndProfileFlagsReset === 'function') { onReloadAndProfileFlagsReset(); diff --git a/packages/react-devtools-core/src/standalone.js b/packages/react-devtools-core/src/standalone.js index f8286783a9b..df89a728c30 100644 --- a/packages/react-devtools-core/src/standalone.js +++ b/packages/react-devtools-core/src/standalone.js @@ -356,17 +356,12 @@ function startServer( // because they are generally stored in localStorage within the context of the extension. // Because of this it relies on the extension to pass filters, so include them wth the response here. // This will ensure that saved filters are shared across different web pages. - const savedPreferencesString = ` - window.__REACT_DEVTOOLS_COMPONENT_FILTERS__ = ${JSON.stringify( - getSavedComponentFilters(), - )};`; + const componentFiltersString = JSON.stringify(getSavedComponentFilters()); response.end( - savedPreferencesString + + backendFile.toString() + '\n;' + - backendFile.toString() + - '\n;' + - 'ReactDevToolsBackend.initialize();' + + `ReactDevToolsBackend.initialize(undefined, undefined, undefined, ${componentFiltersString});` + '\n' + `ReactDevToolsBackend.connectToDevTools({port: ${port}, host: '${host}', useHttps: ${ useHttps ? 'true' : 'false' diff --git a/packages/react-devtools-extensions/src/contentScripts/hookSettingsInjector.js b/packages/react-devtools-extensions/src/contentScripts/hookSettingsInjector.js index 196369e71d9..00c9b905a66 100644 --- a/packages/react-devtools-extensions/src/contentScripts/hookSettingsInjector.js +++ b/packages/react-devtools-extensions/src/contentScripts/hookSettingsInjector.js @@ -5,9 +5,15 @@ // This is the only purpose of this script - to send persisted settings to installHook.js content script import type {UnknownMessageEvent} from './messages'; -import type {DevToolsHookSettings} from 'react-devtools-shared/src/backend/types'; +import type { + DevToolsHookSettings, + DevToolsSettings, +} from 'react-devtools-shared/src/backend/types'; +import type {ComponentFilter} from 'react-devtools-shared/src/frontend/types'; import {postMessage} from './messages'; +import {getDefaultComponentFilters} from 'react-devtools-shared/src/utils'; + async function messageListener(event: UnknownMessageEvent) { if (event.source !== window) { return; @@ -15,7 +21,7 @@ async function messageListener(event: UnknownMessageEvent) { if (event.data.source === 'react-devtools-hook-installer') { if (event.data.payload.handshake) { - const settings: Partial = + const settings: Partial = await chrome.storage.local.get(); // If storage was empty (first installation), define default settings const hookSettings: DevToolsHookSettings = { @@ -41,10 +47,15 @@ async function messageListener(event: UnknownMessageEvent) { ? settings.disableSecondConsoleLogDimmingInStrictMode : false, }; + const componentFilters: Array = Array.isArray( + settings.componentFilters, + ) + ? settings.componentFilters + : getDefaultComponentFilters(); postMessage({ - source: 'react-devtools-hook-settings-injector', - payload: {settings: hookSettings}, + source: 'react-devtools-settings-injector', + payload: {hookSettings, componentFilters}, }); window.removeEventListener('message', messageListener); @@ -54,6 +65,6 @@ async function messageListener(event: UnknownMessageEvent) { window.addEventListener('message', messageListener); postMessage({ - source: 'react-devtools-hook-settings-injector', + source: 'react-devtools-settings-injector', payload: {handshake: true}, }); diff --git a/packages/react-devtools-extensions/src/contentScripts/installHook.js b/packages/react-devtools-extensions/src/contentScripts/installHook.js index 8f30d6f0892..490232baf86 100644 --- a/packages/react-devtools-extensions/src/contentScripts/installHook.js +++ b/packages/react-devtools-extensions/src/contentScripts/installHook.js @@ -2,6 +2,7 @@ import type {UnknownMessageEvent} from './messages'; import type {DevToolsHookSettings} from 'react-devtools-shared/src/backend/types'; +import type {ComponentFilter} from 'react-devtools-shared/src/frontend/types'; import {installHook} from 'react-devtools-shared/src/hook'; import { @@ -11,13 +12,14 @@ import { import {postMessage} from './messages'; let resolveHookSettingsInjection: (settings: DevToolsHookSettings) => void; +let resolveComponentFiltersInjection: (filters: Array) => void; function messageListener(event: UnknownMessageEvent) { if (event.source !== window) { return; } - if (event.data.source === 'react-devtools-hook-settings-injector') { + if (event.data.source === 'react-devtools-settings-injector') { const payload = event.data.payload; // In case handshake message was sent prior to hookSettingsInjector execution // We can't guarantee order @@ -26,9 +28,10 @@ function messageListener(event: UnknownMessageEvent) { source: 'react-devtools-hook-installer', payload: {handshake: true}, }); - } else if (payload.settings) { + } else if (payload.hookSettings) { window.removeEventListener('message', messageListener); - resolveHookSettingsInjection(payload.settings); + resolveHookSettingsInjection(payload.hookSettings); + resolveComponentFiltersInjection(payload.componentFilters); } } } @@ -38,6 +41,11 @@ if (!window.hasOwnProperty('__REACT_DEVTOOLS_GLOBAL_HOOK__')) { const hookSettingsPromise = new Promise(resolve => { resolveHookSettingsInjection = resolve; }); + const componentFiltersPromise = new Promise>( + resolve => { + resolveComponentFiltersInjection = resolve; + }, + ); window.addEventListener('message', messageListener); postMessage({ @@ -50,6 +58,7 @@ if (!window.hasOwnProperty('__REACT_DEVTOOLS_GLOBAL_HOOK__')) { // Can't delay hook installation, inject settings lazily installHook( window, + componentFiltersPromise, hookSettingsPromise, shouldStartProfiling, profilingSettings, diff --git a/packages/react-devtools-extensions/src/contentScripts/messages.js b/packages/react-devtools-extensions/src/contentScripts/messages.js index e65d46b4b26..b93a3f0df86 100644 --- a/packages/react-devtools-extensions/src/contentScripts/messages.js +++ b/packages/react-devtools-extensions/src/contentScripts/messages.js @@ -1,6 +1,7 @@ /** @flow */ import type {DevToolsHookSettings} from 'react-devtools-shared/src/backend/types'; +import type {ComponentFilter} from 'react-devtools-shared/src/frontend/types'; export function postMessage(event: UnknownMessageEventData): void { window.postMessage(event); @@ -10,7 +11,7 @@ export interface UnknownMessageEvent extends MessageEvent {} export type UnknownMessageEventData = - | HookSettingsInjectorEventData + | SettingsInjectorEventData | HookInstallerEventData; export type HookInstallerEventData = { @@ -24,19 +25,20 @@ export type HookInstallerEventPayloadHandshake = { handshake: true, }; -export type HookSettingsInjectorEventData = { - source: 'react-devtools-hook-settings-injector', - payload: HookSettingsInjectorEventPayload, +export type SettingsInjectorEventData = { + source: 'react-devtools-settings-injector', + payload: SettingsInjectorEventPayload, }; -export type HookSettingsInjectorEventPayload = - | HookSettingsInjectorEventPayloadHandshake - | HookSettingsInjectorEventPayloadSettings; +export type SettingsInjectorEventPayload = + | SettingsInjectorEventPayloadHandshake + | SettingsInjectorEventPayloadSettings; -export type HookSettingsInjectorEventPayloadHandshake = { +export type SettingsInjectorEventPayloadHandshake = { handshake: true, }; -export type HookSettingsInjectorEventPayloadSettings = { - settings: DevToolsHookSettings, +export type SettingsInjectorEventPayloadSettings = { + hookSettings: DevToolsHookSettings, + componentFilters: Array, }; diff --git a/packages/react-devtools-extensions/src/main/index.js b/packages/react-devtools-extensions/src/main/index.js index c2616cf04a1..75a81c92ddc 100644 --- a/packages/react-devtools-extensions/src/main/index.js +++ b/packages/react-devtools-extensions/src/main/index.js @@ -171,8 +171,8 @@ function createBridgeAndStore() { createSuspensePanel(); }); - store.addListener('settingsUpdated', settings => { - chrome.storage.local.set(settings); + store.addListener('settingsUpdated', (hookSettings, componentFilters) => { + chrome.storage.local.set({...hookSettings, componentFilters}); }); if (!isProfiling) { diff --git a/packages/react-devtools-inline/src/backend.js b/packages/react-devtools-inline/src/backend.js index 2dd03417121..9810d5b39cc 100644 --- a/packages/react-devtools-inline/src/backend.js +++ b/packages/react-devtools-inline/src/backend.js @@ -10,7 +10,10 @@ import type { BackendBridge, SavedPreferencesParams, } from 'react-devtools-shared/src/bridge'; -import type {Wall} from 'react-devtools-shared/src/frontend/types'; +import type { + ComponentFilter, + Wall, +} from 'react-devtools-shared/src/frontend/types'; import { getIfReloadedAndProfiling, getIsReloadAndProfileSupported, @@ -18,6 +21,11 @@ import { onReloadAndProfileFlagsReset, } from 'react-devtools-shared/src/utils'; +let resolveComponentFiltersInjection: (filters: Array) => void; +const componentFiltersPromise = new Promise>(resolve => { + resolveComponentFiltersInjection = resolve; +}); + function startActivation(contentWindow: any, bridge: BackendBridge) { const onSavedPreferences = (data: SavedPreferencesParams) => { // This is the only message we're listening for, @@ -26,21 +34,13 @@ function startActivation(contentWindow: any, bridge: BackendBridge) { const {componentFilters} = data; - contentWindow.__REACT_DEVTOOLS_COMPONENT_FILTERS__ = componentFilters; - - // TRICKY - // The backend entry point may be required in the context of an iframe or the parent window. - // If it's required within the parent window, store the saved values on it as well, - // since the injected renderer interface will read from window. - // Technically we don't need to store them on the contentWindow in this case, - // but it doesn't really hurt anything to store them there too. - if (contentWindow !== window) { - window.__REACT_DEVTOOLS_COMPONENT_FILTERS__ = componentFilters; - } - - finishActivation(contentWindow, bridge); + resolveComponentFiltersInjection(componentFilters); }; + componentFiltersPromise.then( + finishActivation.bind(null, contentWindow, bridge), + ); + bridge.addListener('savedPreferences', onSavedPreferences); // The backend may be unable to read saved preferences directly, @@ -113,5 +113,5 @@ export function createBridge(contentWindow: any, wall?: Wall): BackendBridge { } export function initialize(contentWindow: any): void { - installHook(contentWindow); + installHook(contentWindow, componentFiltersPromise); } diff --git a/packages/react-devtools-shared/src/__tests__/setupTests.js b/packages/react-devtools-shared/src/__tests__/setupTests.js index 5774a573b31..37b07ea7c35 100644 --- a/packages/react-devtools-shared/src/__tests__/setupTests.js +++ b/packages/react-devtools-shared/src/__tests__/setupTests.js @@ -238,9 +238,8 @@ beforeEach(() => { // Initialize filters to a known good state. setSavedComponentFilters(getDefaultComponentFilters()); - global.__REACT_DEVTOOLS_COMPONENT_FILTERS__ = getDefaultComponentFilters(); - installHook(global, { + installHook(global, getDefaultComponentFilters(), { appendComponentStack: true, breakOnConsoleErrors: false, showInlineWarningsAndErrors: true, diff --git a/packages/react-devtools-shared/src/attachRenderer.js b/packages/react-devtools-shared/src/attachRenderer.js index fedf76293cb..fa230fe7eff 100644 --- a/packages/react-devtools-shared/src/attachRenderer.js +++ b/packages/react-devtools-shared/src/attachRenderer.js @@ -14,6 +14,7 @@ import type { RendererID, ProfilingSettings, } from 'react-devtools-shared/src/backend/types'; +import type {ComponentFilter} from 'react-devtools-shared/src/frontend/types'; import {attach as attachFlight} from 'react-devtools-shared/src/backend/flight/renderer'; import {attach as attachFiber} from 'react-devtools-shared/src/backend/fiber/renderer'; @@ -32,6 +33,9 @@ export default function attachRenderer( global: Object, shouldStartProfilingNow: boolean, profilingSettings: ProfilingSettings, + componentFiltersOrComponentFiltersPromise: + | Array + | Promise>, ): RendererInterface | void { // only attach if the renderer is compatible with the current version of the backend if (!isMatchingRender(renderer.reconcilerVersion || renderer.version)) { @@ -58,6 +62,7 @@ export default function attachRenderer( global, shouldStartProfilingNow, profilingSettings, + componentFiltersOrComponentFiltersPromise, ); } else if (renderer.ComponentTree) { // react-dom v15 diff --git a/packages/react-devtools-shared/src/backend/fiber/renderer.js b/packages/react-devtools-shared/src/backend/fiber/renderer.js index 892a3c6fc77..289bad6f329 100644 --- a/packages/react-devtools-shared/src/backend/fiber/renderer.js +++ b/packages/react-devtools-shared/src/backend/fiber/renderer.js @@ -48,13 +48,11 @@ import { deletePathInObject, getDisplayName, getWrappedDisplayName, - getDefaultComponentFilters, getInObject, getUID, renamePathInObject, setInObject, utfEncodeString, - persistableComponentFilters, } from 'react-devtools-shared/src/utils'; import { formatConsoleArgumentsToSingleString, @@ -1010,6 +1008,9 @@ export function attach( global: Object, shouldStartProfilingNow: boolean, profilingSettings: ProfilingSettings, + componentFiltersOrComponentFiltersPromise: + | Array + | Promise>, ): RendererInterface { // Newer versions of the reconciler package also specific reconciler version. // If that version number is present, use it. @@ -1516,21 +1517,12 @@ export function attach( }); } - // The renderer interface can't read saved component filters directly, - // because they are stored in localStorage within the context of the extension. - // Instead it relies on the extension to pass filters through. - if (window.__REACT_DEVTOOLS_COMPONENT_FILTERS__ != null) { - const restoredComponentFilters: Array = - persistableComponentFilters(window.__REACT_DEVTOOLS_COMPONENT_FILTERS__); - applyComponentFilters(restoredComponentFilters, null); + if (Array.isArray(componentFiltersOrComponentFiltersPromise)) { + applyComponentFilters(componentFiltersOrComponentFiltersPromise, null); } else { - // Unfortunately this feature is not expected to work for React Native for now. - // It would be annoying for us to spam YellowBox warnings with unactionable stuff, - // so for now just skip this message... - //console.warn('⚛ DevTools: Could not locate saved component filters'); - - // Fallback to assuming the default filters in this case. - applyComponentFilters(getDefaultComponentFilters(), null); + componentFiltersOrComponentFiltersPromise.then(componentFilters => { + applyComponentFilters(componentFilters, null); + }); } // If necessary, we can revisit optimizing this operation. diff --git a/packages/react-devtools-shared/src/backend/types.js b/packages/react-devtools-shared/src/backend/types.js index 00c66355b2d..a97439b7cf9 100644 --- a/packages/react-devtools-shared/src/backend/types.js +++ b/packages/react-devtools-shared/src/backend/types.js @@ -599,3 +599,7 @@ export type DevToolsHookSettings = { hideConsoleLogsInStrictMode: boolean, disableSecondConsoleLogDimmingInStrictMode: boolean, }; + +export type DevToolsSettings = DevToolsHookSettings & { + componentFilters: Array, +}; diff --git a/packages/react-devtools-shared/src/bridge.js b/packages/react-devtools-shared/src/bridge.js index af3a87b5968..bc752ec4b8c 100644 --- a/packages/react-devtools-shared/src/bridge.js +++ b/packages/react-devtools-shared/src/bridge.js @@ -208,7 +208,6 @@ export type BackendEvents = { isReloadAndProfileSupportedByBackend: [boolean], operations: [Array], ownersList: [OwnersList], - overrideComponentFilters: [Array], environmentNames: [Array], profilingData: [ProfilingDataBackend], profilingStatus: [boolean], diff --git a/packages/react-devtools-shared/src/devtools/store.js b/packages/react-devtools-shared/src/devtools/store.js index c48d5b42fb6..3e2dde5fea1 100644 --- a/packages/react-devtools-shared/src/devtools/store.js +++ b/packages/react-devtools-shared/src/devtools/store.js @@ -148,7 +148,7 @@ export default class Store extends EventEmitter<{ error: [Error], hookSettings: [$ReadOnly], hostInstanceSelected: [Element['id'] | null], - settingsUpdated: [$ReadOnly], + settingsUpdated: [$ReadOnly, Array], mutated: [ [ Array, @@ -321,10 +321,6 @@ export default class Store extends EventEmitter<{ this._bridge = bridge; bridge.addListener('operations', this.onBridgeOperations); - bridge.addListener( - 'overrideComponentFilters', - this.onBridgeOverrideComponentFilters, - ); bridge.addListener('shutdown', this.onBridgeShutdown); bridge.addListener( 'isReloadAndProfileSupportedByBackend', @@ -437,8 +433,23 @@ export default class Store extends EventEmitter<{ this._componentFilters = value; - // Update persisted filter preferences stored in localStorage. + // Update persisted filter preferences setSavedComponentFilters(value); + if (this._hookSettings === null) { + // We changed filters before we got the hook settings. + // Wait for hook settings before persisting component filters to not overwrite + // persisted hook settings with defaults. + // This exists purely as a type safety check; in practice the hook settings + // should have arrived before any filter changes could be made. + const onHookSettings = (settings: $ReadOnly) => { + this._bridge.removeListener('hookSettings', onHookSettings); + this.emit('settingsUpdated', settings, value); + }; + this._bridge.addListener('hookSettings', onHookSettings); + this._bridge.send('getHookSettings'); + } else { + this.emit('settingsUpdated', this._hookSettings, value); + } // Notify the renderer that filter preferences have changed. // This is an expensive operation; it unmounts and remounts the entire tree, @@ -2264,19 +2275,6 @@ export default class Store extends EventEmitter<{ return didMutate; } - // Certain backends save filters on a per-domain basis. - // In order to prevent filter preferences and applied filters from being out of sync, - // this message enables the backend to override the frontend's current ("saved") filters. - // This action should also override the saved filters too, - // else reloading the frontend without reloading the backend would leave things out of sync. - onBridgeOverrideComponentFilters: ( - componentFilters: Array, - ) => void = componentFilters => { - this._componentFilters = componentFilters; - - setSavedComponentFilters(componentFilters); - }; - onBridgeShutdown: () => void = () => { if (__DEBUG__) { debug('onBridgeShutdown', 'unsubscribing from Bridge'); @@ -2284,10 +2282,6 @@ export default class Store extends EventEmitter<{ const bridge = this._bridge; bridge.removeListener('operations', this.onBridgeOperations); - bridge.removeListener( - 'overrideComponentFilters', - this.onBridgeOverrideComponentFilters, - ); bridge.removeListener('shutdown', this.onBridgeShutdown); bridge.removeListener( 'isReloadAndProfileSupportedByBackend', @@ -2419,7 +2413,7 @@ export default class Store extends EventEmitter<{ this._hookSettings = settings; this._bridge.send('updateHookSettings', settings); - this.emit('settingsUpdated', settings); + this.emit('settingsUpdated', settings, this._componentFilters); }; onHookSettings: (settings: $ReadOnly) => void = diff --git a/packages/react-devtools-shared/src/hook.js b/packages/react-devtools-shared/src/hook.js index 97f8230b6a2..5764a8bee4b 100644 --- a/packages/react-devtools-shared/src/hook.js +++ b/packages/react-devtools-shared/src/hook.js @@ -18,6 +18,7 @@ import type { DevToolsHookSettings, ProfilingSettings, } from './backend/types'; +import type {ComponentFilter} from './frontend/types'; import { FIREFOX_CONSOLE_DIMMING_COLOR, @@ -57,6 +58,9 @@ const defaultProfilingSettings: ProfilingSettings = { export function installHook( target: any, + componentFiltersOrComponentFiltersPromise: + | Array + | Promise>, maybeSettingsOrSettingsPromise?: | DevToolsHookSettings | Promise, @@ -224,6 +228,7 @@ export function installHook( target, isProfiling, profilingSettings, + componentFiltersOrComponentFiltersPromise, ); if (rendererInterface != null) { hook.rendererInterfaces.set(id, rendererInterface);