@samitouri / QOS-React / commits / cdbd55f440

Type `react-devtools-hook-installer` and `react-devtools-hook-settings-injector` messages (#35586)

Sebastian "Sebbie" Silbermann committed Jan 21, 2026 at 19:13 UTC cdbd55f44004cf77a61c178952e34d4514414519
8 files changed +106 -44
.eslintrc.js
+1
@@ -593,6 +593,7 @@ module.exports = {
593 mixin$Animatable: 'readonly',
594 MouseEventHandler: 'readonly',
595 NavigateEvent: 'readonly',
596 + Partial: 'readonly',
597 PerformanceMeasureOptions: 'readonly',
598 PropagationPhases: 'readonly',
599 PropertyDescriptor: 'readonly',
flow-typed/environments/bom.js
+8 -8
@@ -826,7 +826,7 @@ declare class WebSocket extends EventTarget {
826 bufferedAmount: number;
827 extensions: string;
828 onopen: (ev: any) => mixed;
829 - onmessage: (ev: MessageEvent) => mixed;
829 + onmessage: (ev: MessageEvent<>) => mixed;
830 onclose: (ev: CloseEvent) => mixed;
831 onerror: (ev: any) => mixed;
832 binaryType: 'blob' | 'arraybuffer';
@@ -855,8 +855,8 @@ declare class Worker extends EventTarget {
855 workerOptions?: WorkerOptions
856 ): void;
857 onerror: null | ((ev: any) => mixed);
858 - onmessage: null | ((ev: MessageEvent) => mixed);
859 - onmessageerror: null | ((ev: MessageEvent) => mixed);
858 + onmessage: null | ((ev: MessageEvent<>) => mixed);
859 + onmessageerror: null | ((ev: MessageEvent<>) => mixed);
860 postMessage(message: any, ports?: any): void;
861 terminate(): void;
862 }
@@ -888,14 +888,14 @@ declare class WorkerGlobalScope extends EventTarget {
888 }
889
890 declare class DedicatedWorkerGlobalScope extends WorkerGlobalScope {
891 - onmessage: (ev: MessageEvent) => mixed;
892 - onmessageerror: (ev: MessageEvent) => mixed;
891 + onmessage: (ev: MessageEvent<>) => mixed;
892 + onmessageerror: (ev: MessageEvent<>) => mixed;
893 postMessage(message: any, transfer?: Iterable<any>): void;
894 }
895
896 declare class SharedWorkerGlobalScope extends WorkerGlobalScope {
897 name: string;
898 - onconnect: (ev: MessageEvent) => mixed;
898 + onconnect: (ev: MessageEvent<>) => mixed;
899 }
900
901 declare class WorkerLocation {
@@ -2056,8 +2056,8 @@ declare class MessagePort extends EventTarget {
2056 start(): void;
2057 close(): void;
2058
2059 - onmessage: null | ((ev: MessageEvent) => mixed);
2060 - onmessageerror: null | ((ev: MessageEvent) => mixed);
2059 + onmessage: null | ((ev: MessageEvent<>) => mixed);
2060 + onmessageerror: null | ((ev: MessageEvent<>) => mixed);
2061 }
2062
2063 declare class MessageChannel {
flow-typed/environments/dom.js
+3 -3
@@ -151,7 +151,7 @@ type TransitionEventHandler = (event: TransitionEvent) => mixed;
151 type TransitionEventListener =
152 | {handleEvent: TransitionEventHandler, ...}
153 | TransitionEventHandler;
154 -type MessageEventHandler = (event: MessageEvent) => mixed;
154 +type MessageEventHandler = (event: MessageEvent<>) => mixed;
155 type MessageEventListener =
156 | {handleEvent: MessageEventHandler, ...}
157 | MessageEventHandler;
@@ -845,8 +845,8 @@ declare class PageTransitionEvent extends Event {
845 // https://www.w3.org/TR/2008/WD-html5-20080610/comms.html
846 // and
847 // https://html.spec.whatwg.org/multipage/comms.html#the-messageevent-interfaces
848 -declare class MessageEvent extends Event {
849 - data: mixed;
848 +declare class MessageEvent<Data = mixed> extends Event {
849 + data: Data;
850 origin: string;
851 lastEventId: string;
852 source: WindowProxy;
flow-typed/environments/html.js
+2 -2
@@ -109,8 +109,8 @@ declare class ErrorEvent extends Event {
109 // https://html.spec.whatwg.org/multipage/web-messaging.html#broadcasting-to-other-browsing-contexts
110 declare class BroadcastChannel extends EventTarget {
111 name: string;
112 - onmessage: ?(event: MessageEvent) => void;
113 - onmessageerror: ?(event: MessageEvent) => void;
112 + onmessage: ?(event: MessageEvent<>) => void;
113 + onmessageerror: ?(event: MessageEvent<>) => void;
114
115 constructor(name: string): void;
116 postMessage(msg: mixed): void;
packages/react-devtools-core/src/backend.js
+1 -1
@@ -293,7 +293,7 @@ export function connectToDevTools(options: ?ConnectOptions) {
293 scheduleRetry();
294 }
295
296 - function handleMessage(event: MessageEvent) {
296 + function handleMessage(event: MessageEvent<>) {
297 let data;
298 try {
299 if (typeof event.data === 'string') {
packages/react-devtools-extensions/src/contentScripts/hookSettingsInjector.js
+34 -22
@@ -1,38 +1,50 @@
1 /* global chrome */
2 +/** @flow */
3
4 // We can't use chrome.storage domain from scripts which are injected in ExecutionWorld.MAIN
5 // This is the only purpose of this script - to send persisted settings to installHook.js content script
6
6 -async function messageListener(event: MessageEvent) {
7 +import type {UnknownMessageEvent} from './messages';
8 +import type {DevToolsHookSettings} from 'react-devtools-shared/src/backend/types';
9 +import {postMessage} from './messages';
10 +
11 +async function messageListener(event: UnknownMessageEvent) {
12 if (event.source !== window) {
13 return;
14 }
15
16 if (event.data.source === 'react-devtools-hook-installer') {
17 if (event.data.payload.handshake) {
13 - const settings = await chrome.storage.local.get();
18 + const settings: Partial<DevToolsHookSettings> =
19 + await chrome.storage.local.get();
20 // If storage was empty (first installation), define default settings
15 - if (typeof settings.appendComponentStack !== 'boolean') {
16 - settings.appendComponentStack = true;
17 - }
18 - if (typeof settings.breakOnConsoleErrors !== 'boolean') {
19 - settings.breakOnConsoleErrors = false;
20 - }
21 - if (typeof settings.showInlineWarningsAndErrors !== 'boolean') {
22 - settings.showInlineWarningsAndErrors = true;
23 - }
24 - if (typeof settings.hideConsoleLogsInStrictMode !== 'boolean') {
25 - settings.hideConsoleLogsInStrictMode = false;
26 - }
27 - if (
28 - typeof settings.disableSecondConsoleLogDimmingInStrictMode !== 'boolean'
29 - ) {
30 - settings.disableSecondConsoleLogDimmingInStrictMode = false;
31 - }
21 + const hookSettings: DevToolsHookSettings = {
22 + appendComponentStack:
23 + typeof settings.appendComponentStack === 'boolean'
24 + ? settings.appendComponentStack
25 + : true,
26 + breakOnConsoleErrors:
27 + typeof settings.breakOnConsoleErrors === 'boolean'
28 + ? settings.breakOnConsoleErrors
29 + : false,
30 + showInlineWarningsAndErrors:
31 + typeof settings.showInlineWarningsAndErrors === 'boolean'
32 + ? settings.showInlineWarningsAndErrors
33 + : true,
34 + hideConsoleLogsInStrictMode:
35 + typeof settings.hideConsoleLogsInStrictMode === 'boolean'
36 + ? settings.hideConsoleLogsInStrictMode
37 + : false,
38 + disableSecondConsoleLogDimmingInStrictMode:
39 + typeof settings.disableSecondConsoleLogDimmingInStrictMode ===
40 + 'boolean'
41 + ? settings.disableSecondConsoleLogDimmingInStrictMode
42 + : false,
43 + };
44
33 - window.postMessage({
45 + postMessage({
46 source: 'react-devtools-hook-settings-injector',
35 - payload: {settings},
47 + payload: {settings: hookSettings},
48 });
49
50 window.removeEventListener('message', messageListener);
@@ -41,7 +53,7 @@ async function messageListener(event: MessageEvent) {
53 }
54
55 window.addEventListener('message', messageListener);
44 -window.postMessage({
56 +postMessage({
57 source: 'react-devtools-hook-settings-injector',
58 payload: {handshake: true},
59 });
packages/react-devtools-extensions/src/contentScripts/installHook.js
+15 -8
@@ -1,39 +1,46 @@
1 +/** @flow */
2 +
3 +import type {UnknownMessageEvent} from './messages';
4 +import type {DevToolsHookSettings} from 'react-devtools-shared/src/backend/types';
5 +
6 import {installHook} from 'react-devtools-shared/src/hook';
7 import {
8 getIfReloadedAndProfiling,
9 getProfilingSettings,
10 } from 'react-devtools-shared/src/utils';
11 +import {postMessage} from './messages';
12
7 -let resolveHookSettingsInjection;
13 +let resolveHookSettingsInjection: (settings: DevToolsHookSettings) => void;
14
9 -function messageListener(event: MessageEvent) {
15 +function messageListener(event: UnknownMessageEvent) {
16 if (event.source !== window) {
17 return;
18 }
19
20 if (event.data.source === 'react-devtools-hook-settings-injector') {
21 + const payload = event.data.payload;
22 // In case handshake message was sent prior to hookSettingsInjector execution
23 // We can't guarantee order
17 - if (event.data.payload.handshake) {
18 - window.postMessage({
24 + if (payload.handshake) {
25 + postMessage({
26 source: 'react-devtools-hook-installer',
27 payload: {handshake: true},
28 });
22 - } else if (event.data.payload.settings) {
29 + } else if (payload.settings) {
30 window.removeEventListener('message', messageListener);
24 - resolveHookSettingsInjection(event.data.payload.settings);
31 + resolveHookSettingsInjection(payload.settings);
32 }
33 }
34 }
35
36 // Avoid double execution
37 if (!window.hasOwnProperty('__REACT_DEVTOOLS_GLOBAL_HOOK__')) {
31 - const hookSettingsPromise = new Promise(resolve => {
38 + const hookSettingsPromise = new Promise<DevToolsHookSettings>(resolve => {
39 resolveHookSettingsInjection = resolve;
40 });
41
42 window.addEventListener('message', messageListener);
36 - window.postMessage({
43 + postMessage({
44 source: 'react-devtools-hook-installer',
45 payload: {handshake: true},
46 });
packages/react-devtools-extensions/src/contentScripts/messages.js new
+42
@@ -0,0 +1,42 @@
1 +/** @flow */
2 +
3 +import type {DevToolsHookSettings} from 'react-devtools-shared/src/backend/types';
4 +
5 +export function postMessage(event: UnknownMessageEventData): void {
6 + window.postMessage(event);
7 +}
8 +
9 +export interface UnknownMessageEvent
10 + extends MessageEvent<UnknownMessageEventData> {}
11 +
12 +export type UnknownMessageEventData =
13 + | HookSettingsInjectorEventData
14 + | HookInstallerEventData;
15 +
16 +export type HookInstallerEventData = {
17 + source: 'react-devtools-hook-installer',
18 + payload: HookInstallerEventPayload,
19 +};
20 +
21 +export type HookInstallerEventPayload = HookInstallerEventPayloadHandshake;
22 +
23 +export type HookInstallerEventPayloadHandshake = {
24 + handshake: true,
25 +};
26 +
27 +export type HookSettingsInjectorEventData = {
28 + source: 'react-devtools-hook-settings-injector',
29 + payload: HookSettingsInjectorEventPayload,
30 +};
31 +
32 +export type HookSettingsInjectorEventPayload =
33 + | HookSettingsInjectorEventPayloadHandshake
34 + | HookSettingsInjectorEventPayloadSettings;
35 +
36 +export type HookSettingsInjectorEventPayloadHandshake = {
37 + handshake: true,
38 +};
39 +
40 +export type HookSettingsInjectorEventPayloadSettings = {
41 + settings: DevToolsHookSettings,
42 +};