mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Added Flow type coverage for all EventEmitter subclasses
This commit is contained in:
@@ -1,11 +1,17 @@
|
||||
// @flow
|
||||
|
||||
declare module 'events' {
|
||||
declare class EventEmitter {
|
||||
addListener: (type: string, fn: Function) => void;
|
||||
emit: (type: string, data: any) => void;
|
||||
removeListener: (type: string, fn: Function) => void;
|
||||
removeAllListeners: (type?: string) => void;
|
||||
declare class EventEmitter<Events: Object> {
|
||||
addListener<Event: $Keys<Events>>(
|
||||
event: Event,
|
||||
listener: (...$ElementType<Events, Event>) => any
|
||||
): void;
|
||||
emit: <Event: $Keys<Events>>(
|
||||
event: Event,
|
||||
...$ElementType<Events, Event>
|
||||
) => void;
|
||||
removeListener(event: $Keys<Events>, listener: Function): void;
|
||||
removeAllListeners(event?: $Keys<Events>): void;
|
||||
}
|
||||
|
||||
declare export default typeof EventEmitter;
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import EventEmitter from 'events';
|
||||
import memoize from 'memoize-one';
|
||||
import throttle from 'lodash.throttle';
|
||||
import Bridge from 'src/bridge';
|
||||
import {
|
||||
SESSION_STORAGE_LAST_SELECTION_KEY,
|
||||
SESSION_STORAGE_RELOAD_AND_PROFILE_KEY,
|
||||
@@ -23,7 +24,7 @@ import type {
|
||||
RendererID,
|
||||
RendererInterface,
|
||||
} from './types';
|
||||
import type { Bridge, ComponentFilter } from '../types';
|
||||
import type { ComponentFilter } from '../types';
|
||||
|
||||
const debug = (methodName, ...args) => {
|
||||
if (__DEBUG__) {
|
||||
@@ -67,7 +68,9 @@ type PersistedSelection = {|
|
||||
path: Array<PathFrame>,
|
||||
|};
|
||||
|
||||
export default class Agent extends EventEmitter {
|
||||
export default class Agent extends EventEmitter<{|
|
||||
shutdown: [],
|
||||
|}> {
|
||||
_bridge: Bridge;
|
||||
_isProfiling: boolean = false;
|
||||
_recordChangeDescriptions: boolean = false;
|
||||
@@ -273,9 +276,11 @@ export default class Agent extends EventEmitter {
|
||||
screenshotCaptured = ({
|
||||
commitIndex,
|
||||
dataURL,
|
||||
rootID,
|
||||
}: {|
|
||||
commitIndex: number,
|
||||
dataURL: string,
|
||||
rootID: number,
|
||||
|}) => {
|
||||
this._bridge.send('screenshotCaptured', { commitIndex, dataURL });
|
||||
};
|
||||
|
||||
@@ -13,10 +13,10 @@ export type ExpirationTime = number;
|
||||
export type RefObject = {|
|
||||
current: any,
|
||||
|};
|
||||
export type Source = {
|
||||
export type Source = {|
|
||||
fileName: string,
|
||||
lineNumber: number,
|
||||
};
|
||||
|};
|
||||
export type HookType =
|
||||
| 'useState'
|
||||
| 'useReducer'
|
||||
|
||||
+74
-3
@@ -2,16 +2,85 @@
|
||||
|
||||
import EventEmitter from 'events';
|
||||
|
||||
import type { Wall } from './types';
|
||||
import type { ComponentFilter, Wall } from './types';
|
||||
import type {
|
||||
InspectedElement,
|
||||
OwnersList,
|
||||
ProfilingDataBackend,
|
||||
RendererID,
|
||||
} from 'src/backend/types';
|
||||
|
||||
const BATCH_DURATION = 100;
|
||||
|
||||
type ElementAndRendererID = {| id: number, rendererID: RendererID |};
|
||||
|
||||
type Message = {|
|
||||
event: string,
|
||||
payload: any,
|
||||
|};
|
||||
|
||||
export default class Bridge extends EventEmitter {
|
||||
type HighlightElementInDOM = {|
|
||||
...ElementAndRendererID,
|
||||
displayName: string,
|
||||
hideAfterTimeout: boolean,
|
||||
openNativeElementsPanel: boolean,
|
||||
scrollIntoView: boolean,
|
||||
|};
|
||||
|
||||
type OverrideValue = {|
|
||||
...ElementAndRendererID,
|
||||
path: Array<string | number>,
|
||||
value: any,
|
||||
|};
|
||||
|
||||
type OverrideHookState = {|
|
||||
...OverrideValue,
|
||||
hookID: number,
|
||||
|};
|
||||
|
||||
type OverrideSuspense = {|
|
||||
...ElementAndRendererID,
|
||||
forceFallback: boolean,
|
||||
|};
|
||||
|
||||
export default class Bridge extends EventEmitter<{|
|
||||
captureScreenshot: [{| commitIndex: number, rootID: number |}],
|
||||
clearHighlightedElementInDOM: [],
|
||||
getOwnersList: [ElementAndRendererID],
|
||||
getProfilingData: [{| rendererID: RendererID |}],
|
||||
getProfilingStatus: [],
|
||||
highlightElementInDOM: [HighlightElementInDOM],
|
||||
init: [],
|
||||
inspectElement: [ElementAndRendererID],
|
||||
inspectedElement: [InspectedElement | number | null],
|
||||
isBackendStorageAPISupported: [boolean],
|
||||
logElementToConsole: [ElementAndRendererID],
|
||||
operations: [Uint32Array],
|
||||
ownersList: [OwnersList],
|
||||
overrideContext: [OverrideValue],
|
||||
overrideHookState: [OverrideHookState],
|
||||
overrideProps: [OverrideValue],
|
||||
overrideState: [OverrideValue],
|
||||
overrideSuspense: [OverrideSuspense],
|
||||
profilingData: [ProfilingDataBackend],
|
||||
profilingStatus: [boolean],
|
||||
reloadAndProfile: [boolean],
|
||||
reloadAppForProfiling: [],
|
||||
screenshotCaptured: [
|
||||
{| commitIndex: number, dataURL: string, rootID: number |},
|
||||
],
|
||||
selectElement: [ElementAndRendererID],
|
||||
selectFiber: [number],
|
||||
shutdown: [],
|
||||
startInspectingDOM: [],
|
||||
startProfiling: [boolean],
|
||||
stopInspectingDOM: [boolean],
|
||||
stopProfiling: [],
|
||||
syncSelectionFromNativeElementsPanel: [],
|
||||
syncSelectionToNativeElementsPanel: [],
|
||||
updateComponentFilters: [Array<ComponentFilter>],
|
||||
viewElementSource: [ElementAndRendererID],
|
||||
|}> {
|
||||
_isShutdown: boolean = false;
|
||||
_messageQueue: Array<any> = [];
|
||||
_timeoutID: TimeoutID | null = null;
|
||||
@@ -25,7 +94,7 @@ export default class Bridge extends EventEmitter {
|
||||
|
||||
this._wallUnlisten =
|
||||
wall.listen((message: Message) => {
|
||||
this.emit(message.event, message.payload);
|
||||
(this: any).emit(message.event, message.payload);
|
||||
}) || null;
|
||||
}
|
||||
|
||||
@@ -64,7 +133,9 @@ export default class Bridge extends EventEmitter {
|
||||
this._isShutdown = true;
|
||||
|
||||
// Disable the API inherited from EventEmitter that can add more listeners and send more messages.
|
||||
// $FlowFixMe This property is not writable.
|
||||
this.addListener = function() {};
|
||||
// $FlowFixMe This property is not writable.
|
||||
this.emit = function() {};
|
||||
// NOTE: There's also EventEmitter API like `on` and `prependListener` that we didn't add to our Flow type of EventEmitter.
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import EventEmitter from 'events';
|
||||
import memoize from 'memoize-one';
|
||||
import throttle from 'lodash.throttle';
|
||||
import Bridge from 'src/bridge';
|
||||
import { prepareProfilingDataFrontendFromBackendAndStore } from './views/Profiler/utils';
|
||||
import ProfilingCache from './ProfilingCache';
|
||||
import Store from './store';
|
||||
@@ -14,11 +15,14 @@ import type {
|
||||
ProfilingDataFrontend,
|
||||
SnapshotNode,
|
||||
} from './views/Profiler/types';
|
||||
import type { Bridge } from '../types';
|
||||
|
||||
const THROTTLE_CAPTURE_SCREENSHOT_DURATION = 500;
|
||||
|
||||
export default class ProfilerStore extends EventEmitter {
|
||||
export default class ProfilerStore extends EventEmitter<{|
|
||||
isProcessingData: [],
|
||||
isProfiling: [],
|
||||
profilingData: [],
|
||||
|}> {
|
||||
_bridge: Bridge;
|
||||
|
||||
// Suspense cache for lazily calculating derived profiling data.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// @flow
|
||||
|
||||
import type { Bridge } from '../types';
|
||||
import Bridge from 'src/bridge';
|
||||
|
||||
type Shell = {|
|
||||
connect: (callback: Function) => void,
|
||||
|
||||
+12
-2
@@ -2,6 +2,7 @@
|
||||
|
||||
import EventEmitter from 'events';
|
||||
import { inspect } from 'util';
|
||||
import Bridge from 'src/bridge';
|
||||
import {
|
||||
TREE_OPERATION_ADD,
|
||||
TREE_OPERATION_REMOVE,
|
||||
@@ -21,7 +22,7 @@ import { printStore } from 'src/__tests__/storeSerializer';
|
||||
import ProfilerStore from './ProfilerStore';
|
||||
|
||||
import type { Element } from './views/Components/types';
|
||||
import type { Bridge, ComponentFilter, ElementType } from '../types';
|
||||
import type { ComponentFilter, ElementType } from '../types';
|
||||
|
||||
const debug = (methodName, ...args) => {
|
||||
if (__DEBUG__) {
|
||||
@@ -57,7 +58,16 @@ export type Capabilities = {|
|
||||
* The store is the single source of truth for updates from the backend.
|
||||
* ContextProviders can subscribe to the Store for specific things they want to provide.
|
||||
*/
|
||||
export default class Store extends EventEmitter {
|
||||
export default class Store extends EventEmitter<{|
|
||||
captureScreenshots: [],
|
||||
collapseNodesByDefault: [],
|
||||
componentFilters: [],
|
||||
mutated: [[Array<number>, Map<number, number>]],
|
||||
recordChangeDescriptions: [],
|
||||
roots: [],
|
||||
supportsProfiling: [],
|
||||
supportsReloadAndProfile: [],
|
||||
|}> {
|
||||
_bridge: Bridge;
|
||||
|
||||
_captureScreenshots: boolean = false;
|
||||
|
||||
@@ -14,28 +14,33 @@ import { hydrate } from 'src/hydration';
|
||||
import { TreeStateContext } from './TreeContext';
|
||||
import { separateDisplayNameAndHOCs } from 'src/utils';
|
||||
|
||||
import type { InspectedElement as InspectedElementBackend } from 'src/backend/types';
|
||||
import type {
|
||||
DehydratedData,
|
||||
Element,
|
||||
InspectedElement,
|
||||
InspectedElement as InspectedElementFrontend,
|
||||
} from 'src/devtools/views/Components/types';
|
||||
import type { Resource, Thenable } from '../../cache';
|
||||
|
||||
type Context = {|
|
||||
read(id: number): InspectedElement | null,
|
||||
read(id: number): InspectedElementFrontend | null,
|
||||
|};
|
||||
|
||||
const InspectedElementContext = createContext<Context>(((null: any): Context));
|
||||
InspectedElementContext.displayName = 'InspectedElementContext';
|
||||
|
||||
type ResolveFn = (inspectedElement: InspectedElement) => void;
|
||||
type ResolveFn = (inspectedElement: InspectedElementFrontend) => void;
|
||||
type InProgressRequest = {|
|
||||
promise: Thenable<InspectedElement>,
|
||||
promise: Thenable<InspectedElementFrontend>,
|
||||
resolveFn: ResolveFn,
|
||||
|};
|
||||
|
||||
const inProgressRequests: WeakMap<Element, InProgressRequest> = new WeakMap();
|
||||
const resource: Resource<Element, Element, InspectedElement> = createResource(
|
||||
const resource: Resource<
|
||||
Element,
|
||||
Element,
|
||||
InspectedElementFrontend
|
||||
> = createResource(
|
||||
(element: Element) => {
|
||||
let request = inProgressRequests.get(element);
|
||||
if (request != null) {
|
||||
@@ -85,7 +90,7 @@ function InspectedElementContextController({ children }: Props) {
|
||||
// This effect handler invalidates the suspense cache and schedules rendering updates with React.
|
||||
useEffect(() => {
|
||||
const onInspectedElement = (
|
||||
inspectedElement: InspectedElement | number | null
|
||||
data: InspectedElementBackend | number | null
|
||||
) => {
|
||||
// A null value means that the element no longer exists in the backend.
|
||||
// If it's the same element that's currently selected, that selection will be removed once the Store updates.
|
||||
@@ -93,23 +98,21 @@ function InspectedElementContextController({ children }: Props) {
|
||||
// Either way there is nothing we need to do in this case.
|
||||
// A numeric value indicates that the element hasn't changed since we last requested its data,
|
||||
// in which case we don't need to invalidate the cache and re-render anything in the DevTools.
|
||||
if (inspectedElement !== null && typeof inspectedElement === 'object') {
|
||||
const id = inspectedElement.id;
|
||||
if (data !== null && typeof data === 'object') {
|
||||
const id = data.id;
|
||||
|
||||
const { displayName, type } = inspectedElement;
|
||||
|
||||
const [
|
||||
displayNameWithoutHOCs,
|
||||
hocDisplayNames,
|
||||
] = separateDisplayNameAndHOCs(displayName, type);
|
||||
|
||||
inspectedElement = (({
|
||||
...inspectedElement,
|
||||
displayName: displayNameWithoutHOCs,
|
||||
const inspectedElement: InspectedElementFrontend = {
|
||||
canEditFunctionProps: data.canEditFunctionProps,
|
||||
canEditHooks: data.canEditHooks,
|
||||
canToggleSuspense: data.canToggleSuspense,
|
||||
canViewSource: data.canViewSource,
|
||||
id: data.id,
|
||||
source: data.source,
|
||||
type: data.type,
|
||||
owners:
|
||||
inspectedElement.owners === null
|
||||
data.owners === null
|
||||
? null
|
||||
: inspectedElement.owners.map(owner => {
|
||||
: data.owners.map(owner => {
|
||||
const [
|
||||
displayName,
|
||||
hocDisplayNames,
|
||||
@@ -120,13 +123,12 @@ function InspectedElementContextController({ children }: Props) {
|
||||
hocDisplayNames,
|
||||
};
|
||||
}),
|
||||
hocDisplayNames,
|
||||
context: hydrateHelper(inspectedElement.context),
|
||||
events: hydrateHelper(inspectedElement.events),
|
||||
hooks: hydrateHelper(inspectedElement.hooks),
|
||||
props: hydrateHelper(inspectedElement.props),
|
||||
state: hydrateHelper(inspectedElement.state),
|
||||
}: any): InspectedElement);
|
||||
context: hydrateHelper(data.context),
|
||||
events: hydrateHelper(data.events),
|
||||
hooks: hydrateHelper(data.hooks),
|
||||
props: hydrateHelper(data.props),
|
||||
state: hydrateHelper(data.state),
|
||||
};
|
||||
|
||||
const element = store.getElementByID(id);
|
||||
if (element !== null) {
|
||||
@@ -174,16 +176,13 @@ function InspectedElementContextController({ children }: Props) {
|
||||
bridge.send('selectElement', { id: selectedElementID, rendererID });
|
||||
|
||||
const onInspectedElement = (
|
||||
inspectedElement: InspectedElement | number | null
|
||||
data: InspectedElementBackend | number | null
|
||||
) => {
|
||||
if (inspectedElement !== null) {
|
||||
if (data !== null) {
|
||||
// If this is the element we requested, wait a little bit and then ask for an update.
|
||||
if (inspectedElement === selectedElementID) {
|
||||
if (data === selectedElementID) {
|
||||
timeoutID = setTimeout(sendRequest, 1000);
|
||||
} else if (
|
||||
typeof inspectedElement === 'object' &&
|
||||
inspectedElement.id === selectedElementID
|
||||
) {
|
||||
} else if (typeof data === 'object' && data.id === selectedElementID) {
|
||||
timeoutID = setTimeout(sendRequest, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,11 +11,8 @@ import { BridgeContext, StoreContext } from '../context';
|
||||
import { TreeStateContext } from './TreeContext';
|
||||
import { separateDisplayNameAndHOCs } from 'src/utils';
|
||||
|
||||
import type {
|
||||
Element,
|
||||
Owner,
|
||||
OwnersList,
|
||||
} from 'src/devtools/views/Components/types';
|
||||
import type { OwnersList } from 'src/backend/types';
|
||||
import type { Element, Owner } from 'src/devtools/views/Components/types';
|
||||
import type { Resource, Thenable } from '../../cache';
|
||||
|
||||
type Context = (id: number) => Array<Owner> | null;
|
||||
|
||||
@@ -52,8 +52,6 @@ export type Source = {|
|
||||
export type InspectedElement = {|
|
||||
id: number,
|
||||
|
||||
displayName: string | null,
|
||||
|
||||
// Does the current renderer support editable hooks?
|
||||
canEditHooks: boolean,
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import '@reach/menu-button/styles.css';
|
||||
import '@reach/tooltip/styles.css';
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import Bridge from 'src/bridge';
|
||||
import Store from '../store';
|
||||
import { BridgeContext, StoreContext } from './context';
|
||||
import Components from './Components/Components';
|
||||
@@ -23,8 +24,6 @@ import styles from './DevTools.css';
|
||||
|
||||
import './root.css';
|
||||
|
||||
import type { Bridge } from '../../types';
|
||||
|
||||
export type BrowserName = 'Chrome' | 'Firefox';
|
||||
export type BrowserTheme = 'dark' | 'light';
|
||||
export type TabID = 'components' | 'profiler' | 'settings';
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
// @flow
|
||||
|
||||
import { createContext } from 'react';
|
||||
|
||||
import type { Bridge } from '../../types';
|
||||
import Bridge from 'src/bridge';
|
||||
|
||||
import Store from '../store';
|
||||
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
// @flow
|
||||
|
||||
export type Bridge = {
|
||||
addListener(type: string, callback: Function): void,
|
||||
removeListener(type: string, callback: Function): void,
|
||||
send(event: string, payload: any, transferable?: Array<any>): void,
|
||||
};
|
||||
|
||||
export type Wall = {|
|
||||
// `listen` returns the "unlisten" function.
|
||||
listen: (fn: Function) => Function,
|
||||
|
||||
Reference in New Issue
Block a user