export type EventListener = (...args: unknown[]) => void; /** * List of events. */ type EventListenerCollection = Map; // Used https://gist.github.com/mudge/5830382 as a starting point. // See https://github.com/browserify/events/blob/master/events.js for // the Node.js (https://nodejs.org/api/events.html) polyfill used by webpack. export class EventManager { maxListeners = 10; warnOnce = false; events: { [eventName: string]: EventListenerCollection } = {}; // eslint-disable-next-line emit(eventName: string, ...args: any[]): void { const collection = this.events[eventName]; if (!collection) { return; } const eventListeners = Array.from(collection.keys()); for (let i = eventListeners.length - 1; i >= 0; i--) { const listener = eventListeners[i]; if (collection.has(listener)) { listener.apply(this, args); } } } on(eventName: string, listener: EventListener): void { let collection = this.events[eventName]; if (!collection) { collection = new Map(); this.events[eventName] = collection; } collection.set(listener, true); if (process.env.NODE_ENV !== 'production') { const collectionSize = collection.size; if (collectionSize > this.maxListeners && !this.warnOnce) { this.warnOnce = true; console.warn( [ `Possible EventEmitter memory leak detected. ${collectionSize} ${eventName} listeners added.`, `Use emitter.setMaxListeners() to increase limit.` ].join('\n') ); } } } once(eventName: string, listener: EventListener): void { // eslint-disable-next-line const that = this; this.on(eventName, function oneTimeListener(...args) { that.removeListener(eventName, oneTimeListener); listener.apply(that, args); }); } removeAllListeners(): void { this.events = {}; } removeListener(eventName: string, listener: EventListener): void { if (this.events[eventName]) { this.events[eventName].delete(listener); } } setMaxListeners(size: number): void { this.maxListeners = size; } }