Skip to content

Commit

Permalink
Inlined DevTools event emitter
Browse files Browse the repository at this point in the history
DevTools previously used the NPM events package for dispatching events. This package has an unfortunate flaw though- if a listener throws during event dispatch, no subsequent listeners are called. I've replaced that event dispatcher with my own implementation that ensures all listeners are called before it re-throws an error.

This commit replaces that event emitter with a custom implementation that calls all listeners before re-throwing an error.
  • Loading branch information
Brian Vaughn committed Mar 24, 2020
1 parent a600408 commit c28283f
Show file tree
Hide file tree
Showing 7 changed files with 72 additions and 21 deletions.
1 change: 0 additions & 1 deletion packages/react-devtools-shared/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
"@reach/menu-button": "^0.1.17",
"@reach/tooltip": "^0.2.2",
"clipboard-js": "^0.3.6",
"events": "^3.0.0",
"local-storage-fallback": "^4.1.1",
"lodash.throttle": "^4.1.1",
"memoize-one": "^3.1.1",
Expand Down
2 changes: 1 addition & 1 deletion packages/react-devtools-shared/src/backend/agent.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
* @flow
*/

import EventEmitter from 'events';
import EventEmitter from '../events';
import throttle from 'lodash.throttle';
import {
SESSION_STORAGE_LAST_SELECTION_KEY,
Expand Down
2 changes: 1 addition & 1 deletion packages/react-devtools-shared/src/bridge.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
* @flow
*/

import EventEmitter from 'events';
import EventEmitter from './events';

import type {ComponentFilter, Wall} from './types';
import type {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
* @flow
*/

import EventEmitter from 'events';
import EventEmitter from '../events';
import {prepareProfilingDataFrontendFromBackendAndStore} from './views/Profiler/utils';
import ProfilingCache from './ProfilingCache';
import Store from './store';
Expand Down
2 changes: 1 addition & 1 deletion packages/react-devtools-shared/src/devtools/store.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
* @flow
*/

import EventEmitter from 'events';
import EventEmitter from '../events';
import {inspect} from 'util';
import {
TREE_OPERATION_ADD,
Expand Down
67 changes: 67 additions & 0 deletions packages/react-devtools-shared/src/events.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/

export default class EventEmitter<Events: Object> {
listenersMap: Map<string, Set<Function>> = new Map();

addListener<Event: $Keys<Events>>(
event: Event,
listener: (...$ElementType<Events, Event>) => any,
): void {
let listeners = this.listenersMap.get(event);
if (listeners === undefined) {
listeners = new Set();

this.listenersMap.set(event, listeners);
}

listeners.add(listener);
}

emit<Event: $Keys<Events>>(
event: Event,
...args: $ElementType<Events, Event>
): void {
const listeners = this.listenersMap.get(event);
if (listeners !== undefined) {
let didThrow = false;
let caughtError = null;

listeners.forEach(listener => {
try {
listener.apply(null, args);
} catch (error) {
if (caughtError === null) {
didThrow = true;
caughtError = error;
}
}
});

if (didThrow) {
throw caughtError;
}
}
}

removeAllListeners(event?: $Keys<Events>): void {
if (event != null) {
this.listenersMap.delete(event);
} else {
this.listenersMap.clear();
}
}

removeListener(event: $Keys<Events>, listener: Function): void {
const listeners = this.listenersMap.get(event);
if (listeners !== undefined) {
listeners.delete(listener);
}
}
}
17 changes: 1 addition & 16 deletions scripts/flow/react-devtools.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,4 @@
* @flow
*/

declare module 'events' {
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;
}
// No types

0 comments on commit c28283f

Please sign in to comment.