File size: 13,415 Bytes
d4b85c0
1
{"version":3,"sources":["../src/MemoryLeakError.ts","../src/Emitter.ts"],"sourcesContent":["import type { Emitter } from './Emitter'\n\nexport class MemoryLeakError extends Error {\n  constructor(\n    public readonly emitter: Emitter<any>,\n    public readonly type: string | number | symbol,\n    public readonly count: number\n  ) {\n    super(\n      `Possible EventEmitter memory leak detected. ${count} ${type.toString()} listeners added. Use emitter.setMaxListeners() to increase limit`\n    )\n    this.name = 'MaxListenersExceededWarning'\n  }\n}\n","import { MemoryLeakError } from './MemoryLeakError'\n\nexport type EventMap = {\n  [eventName: string]: Array<unknown>\n}\n\nexport type InternalEventNames = 'newListener' | 'removeListener'\n\nexport type InternalListener<Events extends EventMap> = Listener<\n  [eventName: keyof Events, listener: Listener<Array<unknown>>]\n>\n\nexport type Listener<Data extends Array<unknown>> = (...data: Data) => void\n\n/**\n * Node.js-compatible implementation of `EventEmitter`.\n *\n * @example\n * const emitter = new Emitter<{ hello: [string] }>()\n * emitter.on('hello', (name) => console.log(name))\n * emitter.emit('hello', 'John')\n */\nexport class Emitter<Events extends EventMap> {\n  private events: Map<keyof Events, Array<Listener<any>>>\n  private maxListeners: number\n  private hasWarnedAboutPotentialMemoryLeak: boolean\n\n  static defaultMaxListeners = 10\n\n  static listenerCount<Events extends EventMap>(\n    emitter: Emitter<EventMap>,\n    eventName: keyof Events\n  ): number {\n    return emitter.listenerCount<any>(eventName)\n  }\n\n  constructor() {\n    this.events = new Map()\n    this.maxListeners = Emitter.defaultMaxListeners\n    this.hasWarnedAboutPotentialMemoryLeak = false\n  }\n\n  private _emitInternalEvent(\n    internalEventName: InternalEventNames,\n    eventName: keyof Events,\n    listener: Listener<Array<unknown>>\n  ): void {\n    this.emit(\n      internalEventName,\n      // Anything to make TypeScript happy.\n      ...([eventName, listener] as Events['newListener'] &\n        Events['removeListener'])\n    )\n  }\n\n  private _getListeners<EventName extends keyof Events>(\n    eventName: EventName\n  ): Array<Listener<Array<unknown>>> {\n    // Always return a copy of the listeners array\n    // so they are fixed at the time of the \"_getListeners\" call.\n    return Array.prototype.concat.apply([], this.events.get(eventName)) || []\n  }\n\n  private _removeListener<EventName extends keyof Events>(\n    listeners: Array<Listener<Events[EventName]>>,\n    listener: Listener<Events[EventName]>\n  ): Array<Listener<Events[EventName]>> {\n    const index = listeners.indexOf(listener)\n\n    if (index > -1) {\n      listeners.splice(index, 1)\n    }\n\n    return []\n  }\n\n  private _wrapOnceListener<EventName extends keyof Events>(\n    eventName: EventName,\n    listener: Listener<Events[EventName]>\n  ): Listener<Events[EventName]> {\n    const onceListener = (...data: Events[keyof Events]) => {\n      this.removeListener(eventName, onceListener)\n\n      /**\n       * @note Return the result of the original listener.\n       * This way this wrapped preserves listeners that are async.\n       */\n      return listener.apply(this, data)\n    }\n\n    // Inherit the name of the original listener.\n    Object.defineProperty(onceListener, 'name', { value: listener.name })\n\n    return onceListener\n  }\n\n  public setMaxListeners(maxListeners: number): this {\n    this.maxListeners = maxListeners\n    return this\n  }\n\n  /**\n   * Returns the current max listener value for the `Emitter` which is\n   * either set by `emitter.setMaxListeners(n)` or defaults to\n   * `Emitter.defaultMaxListeners`.\n   */\n  public getMaxListeners(): number {\n    return this.maxListeners\n  }\n\n  /**\n   * Returns an array listing the events for which the emitter has registered listeners.\n   * The values in the array will be strings or Symbols.\n   */\n  public eventNames(): Array<keyof Events> {\n    return Array.from(this.events.keys())\n  }\n\n  /**\n   * Synchronously calls each of the listeners registered for the event named `eventName`,\n   * in the order they were registered, passing the supplied arguments to each.\n   * Returns `true` if the event has listeners, `false` otherwise.\n   *\n   * @example\n   * const emitter = new Emitter<{ hello: [string] }>()\n   * emitter.emit('hello', 'John')\n   */\n  public emit<EventName extends keyof Events>(\n    eventName: EventName,\n    ...data: Events[EventName]\n  ): boolean {\n    const listeners = this._getListeners(eventName)\n    listeners.forEach((listener) => {\n      listener.apply(this, data)\n    })\n\n    return listeners.length > 0\n  }\n\n  public addListener(\n    eventName: InternalEventNames,\n    listener: InternalListener<Events>\n  ): this\n  public addListener<EventName extends keyof Events>(\n    eventName: EventName,\n    listener: Listener<Events[EventName]>\n  ): this\n  public addListener(\n    eventName: InternalEventNames | keyof Events,\n    listener: InternalListener<Events> | Listener<Events[any]>\n  ): this {\n    // Emit the `newListener` event before adding the listener.\n    this._emitInternalEvent('newListener', eventName, listener)\n\n    const nextListeners = this._getListeners(eventName).concat(listener)\n    this.events.set(eventName, nextListeners)\n\n    if (\n      this.maxListeners > 0 &&\n      this.listenerCount(eventName) > this.maxListeners &&\n      !this.hasWarnedAboutPotentialMemoryLeak\n    ) {\n      this.hasWarnedAboutPotentialMemoryLeak = true\n\n      const memoryLeakWarning = new MemoryLeakError(\n        this,\n        eventName,\n        this.listenerCount(eventName)\n      )\n      console.warn(memoryLeakWarning)\n    }\n\n    return this\n  }\n\n  public on(\n    eventName: InternalEventNames,\n    listener: InternalListener<Events>\n  ): this\n  public on<EventName extends keyof Events>(\n    eventName: EventName,\n    listener: Listener<Events[EventName]>\n  ): this\n  public on<EventName extends keyof Events>(\n    eventName: 'removeListener' | EventName,\n    listener: Listener<any>\n  ): this {\n    return this.addListener(eventName, listener)\n  }\n\n  public once(\n    eventName: InternalEventNames,\n    listener: InternalListener<Events>\n  ): this\n  public once<EventName extends keyof Events>(\n    eventName: EventName,\n    listener: Listener<Events[EventName]>\n  ): this\n  public once<EventName extends keyof Events>(\n    eventName: InternalEventNames | EventName,\n    listener: Listener<any>\n  ): this {\n    return this.addListener(\n      eventName,\n      this._wrapOnceListener(eventName, listener)\n    )\n  }\n\n  public prependListener(\n    eventName: InternalEventNames,\n    listener: InternalListener<Events>\n  ): this\n  public prependListener<EventName extends keyof Events>(\n    eventName: EventName,\n    listener: Listener<Events[EventName]>\n  ): this\n  public prependListener(\n    eventName: InternalEventNames | keyof Events,\n    listener: Listener<any>\n  ): this {\n    const listeners = this._getListeners(eventName)\n\n    if (listeners.length > 0) {\n      const nextListeners = [listener].concat(listeners)\n      this.events.set(eventName, nextListeners)\n    } else {\n      this.events.set(eventName, listeners.concat(listener))\n    }\n\n    return this\n  }\n\n  public prependOnceListener(\n    eventName: InternalEventNames,\n    listener: InternalListener<Events>\n  ): this\n  public prependOnceListener<EventName extends keyof Events>(\n    eventName: EventName,\n    listener: Listener<Events[EventName]>\n  ): this\n  public prependOnceListener(\n    eventName: InternalEventNames | keyof Events,\n    listener: Listener<any>\n  ): this {\n    return this.prependListener(\n      eventName,\n      this._wrapOnceListener(eventName, listener)\n    )\n  }\n\n  public removeListener(\n    eventName: InternalEventNames,\n    listener: InternalListener<Events>\n  ): this\n  public removeListener<EventName extends keyof Events>(\n    eventName: EventName,\n    listener: Listener<Events[EventName]>\n  ): this\n  public removeListener(\n    eventName: InternalEventNames | keyof Events,\n    listener: Listener<any>\n  ): this {\n    const listeners = this._getListeners(eventName)\n\n    if (listeners.length > 0) {\n      this._removeListener(listeners, listener)\n      this.events.set(eventName, listeners)\n\n      // Emit the `removeListener` event after removing the listener.\n      this._emitInternalEvent('removeListener', eventName, listener)\n    }\n\n    return this\n  }\n\n  public off(\n    eventName: InternalEventNames,\n    listener: InternalListener<Events>\n  ): this\n  public off<EventName extends keyof Events>(\n    eventName: EventName,\n    listener: Listener<Events[EventName]>\n  ): this\n  /**\n   * Alias for `emitter.removeListener()`.\n   *\n   * @example\n   * emitter.off('hello', listener)\n   */\n  public off(\n    eventName: InternalEventNames | keyof Events,\n    listener: Listener<any>\n  ): this {\n    return this.removeListener(eventName, listener)\n  }\n\n  public removeAllListeners(eventName?: InternalEventNames): this\n  public removeAllListeners<EventName extends keyof Events>(\n    eventName?: EventName\n  ): this\n  public removeAllListeners(\n    eventName?: InternalEventNames | keyof Events\n  ): this {\n    if (eventName) {\n      this.events.delete(eventName)\n    } else {\n      this.events.clear()\n    }\n\n    return this\n  }\n\n  public listeners(eventName: InternalEventNames): Array<Listener<any>>\n  public listeners<EventName extends keyof Events>(\n    eventName: EventName\n  ): Array<Listener<Events[EventName]>>\n  /**\n   * Returns a copy of the array of listeners for the event named `eventName`.\n   */\n  public listeners(eventName: InternalEventNames | keyof Events) {\n    return Array.from(this._getListeners(eventName))\n  }\n\n  public listenerCount(eventName: InternalEventNames): number\n  public listenerCount<EventName extends keyof Events>(\n    eventName: EventName\n  ): number\n  /**\n   * Returns the number of listeners listening to the event named `eventName`.\n   */\n  public listenerCount(eventName: InternalEventNames | keyof Events): number {\n    return this._getListeners(eventName).length\n  }\n\n  public rawListeners<EventName extends keyof Events>(\n    eventName: EventName\n  ): Array<Listener<Events[EventName]>> {\n    return this.listeners(eventName)\n  }\n}\n"],"mappings":"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","names":[]}