Skip to content

Commit

Permalink
Always use custom EventEmitter implementation to fix webpack/nextjs/w…
Browse files Browse the repository at this point in the history
…hatever being dumb

Fixes #65
  • Loading branch information
ChiriVulpes committed Oct 22, 2023
1 parent 8db0221 commit 1f86352
Showing 1 changed file with 84 additions and 96 deletions.
180 changes: 84 additions & 96 deletions src/util/EventEmitter.ts
Original file line number Diff line number Diff line change
@@ -1,121 +1,109 @@
type EventEmitter = import("node:events");
let EventEmitter: typeof import("node:events");
type NodeEventEmitter = import("node:events");

try {
EventEmitter = require("node:events");
if (!("EventEmitter" in EventEmitter))
// force an error if the module isn't as expected
throw new Error();
type Listener = (...args: any[]) => void;
const EMPTY: Listener[] = [];

} catch {
class EventEmitter implements NodeEventEmitter {

type Listener = (...args: any[]) => void;
const EMPTY: Listener[] = [];
private _maxListeners = 10;
private readonly _listeners: Record<string | symbol, Listener[]> = {};

class EventEmitterAlternative implements EventEmitter {

private _maxListeners = 10;
private readonly _listeners: Record<string | symbol, Listener[]> = {};

public addListener (eventName: string | symbol, listener: Listener): this {
const listeners = this._listeners[eventName] ??= [];
listeners.push(listener);
if (listeners.length > this._maxListeners)
console.warn(`MaxListenersExceededWarning: Possible EventEmitter memory leak detected. ${listeners.length} ${eventName.toString()} listeners added. Use emitter.setMaxListeners() to increase limit`);
return this;
}
public addListener (eventName: string | symbol, listener: Listener): this {
const listeners = this._listeners[eventName] ??= [];
listeners.push(listener);
if (listeners.length > this._maxListeners)
console.warn(`MaxListenersExceededWarning: Possible EventEmitter memory leak detected. ${listeners.length} ${eventName.toString()} listeners added. Use emitter.setMaxListeners() to increase limit`);
return this;
}

public prependListener (eventName: string | symbol, listener: Listener): this {
const listeners = this._listeners[eventName] ??= [];
listeners.unshift(listener);
if (listeners.length > this._maxListeners)
console.warn(`MaxListenersExceededWarning: Possible EventEmitter memory leak detected. ${listeners.length} ${eventName.toString()} listeners added. Use emitter.setMaxListeners() to increase limit`);
return this;
}
public prependListener (eventName: string | symbol, listener: Listener): this {
const listeners = this._listeners[eventName] ??= [];
listeners.unshift(listener);
if (listeners.length > this._maxListeners)
console.warn(`MaxListenersExceededWarning: Possible EventEmitter memory leak detected. ${listeners.length} ${eventName.toString()} listeners added. Use emitter.setMaxListeners() to increase limit`);
return this;
}

public removeListener (eventName: string | symbol, listener: Listener): this {
const listeners = this._listeners[eventName];
if (listeners) {
const index = listeners.indexOf(listener);
if (index >= 0) {
if (listeners.length === 1)
delete this._listeners[eventName];
else
listeners.splice(index, 1);
}
public removeListener (eventName: string | symbol, listener: Listener): this {
const listeners = this._listeners[eventName];
if (listeners) {
const index = listeners.indexOf(listener);
if (index >= 0) {
if (listeners.length === 1)
delete this._listeners[eventName];
else
listeners.splice(index, 1);
}

return this;
}

public on (eventName: string | symbol, listener: Listener): this {
this.addListener(eventName, listener);
return this;
}
return this;
}

public once (eventName: string | symbol, listener: Listener): this {
const realListener: Listener = (...args) => {
this.removeListener(eventName, realListener);
listener(...args);
};
this.addListener(eventName, realListener);
return this;
}
public on (eventName: string | symbol, listener: Listener): this {
this.addListener(eventName, listener);
return this;
}

public prependOnceListener (eventName: string | symbol, listener: Listener): this {
const realListener: Listener = (...args) => {
this.removeListener(eventName, realListener);
listener(...args);
};
this.prependListener(eventName, realListener);
return this;
}
public once (eventName: string | symbol, listener: Listener): this {
const realListener: Listener = (...args) => {
this.removeListener(eventName, realListener);
listener(...args);
};
this.addListener(eventName, realListener);
return this;
}

public off (eventName: string | symbol, listener: Listener): this {
this.removeListener(eventName, listener);
return this;
}
public prependOnceListener (eventName: string | symbol, listener: Listener): this {
const realListener: Listener = (...args) => {
this.removeListener(eventName, realListener);
listener(...args);
};
this.prependListener(eventName, realListener);
return this;
}

public removeAllListeners (event?: string | symbol | undefined): this {
if (event !== undefined)
delete this._listeners[event];
return this;
}
public off (eventName: string | symbol, listener: Listener): this {
this.removeListener(eventName, listener);
return this;
}

public emit (eventName: string | symbol, ...args: any[]): boolean {
for (const listener of this._listeners[eventName])
listener(...args);
return true;
}
public removeAllListeners (event?: string | symbol | undefined): this {
if (event !== undefined)
delete this._listeners[event];
return this;
}

public setMaxListeners (n: number): this {
this._maxListeners = n;
return this;
}
public emit (eventName: string | symbol, ...args: any[]): boolean {
for (const listener of this._listeners[eventName])
listener(...args);
return true;
}

public getMaxListeners (): number {
return this._maxListeners;
}
public setMaxListeners (n: number): this {
this._maxListeners = n;
return this;
}

public listeners (eventName: string | symbol): Function[] {
const listeners = this._listeners[eventName];
return listeners ? [...listeners] : EMPTY;
}
public getMaxListeners (): number {
return this._maxListeners;
}

public rawListeners (eventName: string | symbol): Function[] {
throw new Error("The rawListeners method is not available using this polyfill");
}
public listeners (eventName: string | symbol): Function[] {
const listeners = this._listeners[eventName];
return listeners ? [...listeners] : EMPTY;
}

public listenerCount (eventName: string | symbol): number {
return this._listeners[eventName]?.length ?? 0;
}
public rawListeners (eventName: string | symbol): Function[] {
throw new Error("The rawListeners method is not available using this polyfill");
}

public eventNames (): (string | symbol)[] {
return Object.keys(this._listeners);
}
public listenerCount (eventName: string | symbol): number {
return this._listeners[eventName]?.length ?? 0;
}

EventEmitter = EventEmitterAlternative as any;
public eventNames (): (string | symbol)[] {
return Object.keys(this._listeners);
}
}

export default EventEmitter;

0 comments on commit 1f86352

Please sign in to comment.