eventEmitter

export default class EventEmitter {
  listeners = new Map<string, EventListener[]>()

  emit(eventName: string, ...args: unknown[]) {
    const listeners = this.listeners.get(eventName)
    if (!listeners || listeners.length === 0) {
      console.warn(`the ${eventName} listener does not exist`)
      return
    }
    listeners.forEach((fn) => {
      fn(...args)
    })
  }

  on(eventName: string, listener: EventListener) {
    if (typeof listener !== 'function')
      throw new Error('listener must be a function')
    const listeners = this.listeners.get(eventName) || []
    listeners.push(listener)
    this.listeners.set(eventName, listeners)
  }

  off(eventName: string, listener?: EventListener) {
    if (!this.listeners.has(eventName)) return
    if (!listener) {
      this.listeners.delete(eventName)
    } else {
      const listeners = this.listeners
        .get(eventName)!
        .filter((fn) => fn !== listener)
      if (listeners.length === 0) {
        this.listeners.delete(eventName)
      } else {
        this.listeners.set(eventName, listeners)
      }
    }
  }

  once(eventName: string, listener: EventListener) {
    if (typeof listener !== 'function')
      throw new Error('listener must be a function')
    const fn = (...args: unknown[]) => {
      listener(...args)
      this.off(eventName, fn)
    }
    this.on(eventName, fn)
  }
}