mirror of
https://github.com/dzeiocom/libs.git
synced 2025-04-22 10:52:11 +00:00
feat(listener): Add full support for ESM & Better typing
Some checks failed
CodeQL / Analyze (javascript) (push) Failing after 2m35s
Some checks failed
CodeQL / Analyze (javascript) (push) Failing after 2m35s
Signed-off-by: Florian Bouillon <f.bouillon@aptatio.com>
This commit is contained in:
parent
f804e0f43c
commit
3d74438086
@ -6,6 +6,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
## [1.1.0] - 2023-11-09
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Full support for both CJS & ESM
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Better typing support
|
||||||
|
|
||||||
## [1.0.3] - 2022-11-17
|
## [1.0.3] - 2022-11-17
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
@ -1,129 +1,155 @@
|
|||||||
type ItemToArray<T> = {
|
type RecordToArray<T> = {
|
||||||
[P in keyof T]?: Array<T[P]>
|
[P in keyof T]?: Array<T[P]>
|
||||||
}
|
}
|
||||||
|
|
||||||
type BuiltInEvents = {
|
type EventList = Record<string, (...args: Array<any>) => void>
|
||||||
|
|
||||||
|
type BaseEvents<T extends EventList = {}> = {
|
||||||
newListener: (eventName: string, listener: (...args: Array<any>) => void) => void
|
newListener: (eventName: string, listener: (...args: Array<any>) => void) => void
|
||||||
removeListener: (eventName: string, listener: (...args: Array<any>) => void) => void
|
removeListener: (eventName: string, listener: (...args: Array<any>) => void) => void
|
||||||
all: (eventName: string, ...args: Array<any>) => void
|
all: (eventName: string, ...args: Array<any>) => void
|
||||||
}
|
} & T
|
||||||
|
|
||||||
export default abstract class Listener<
|
/**
|
||||||
T extends Record<string, (...args: Array<any>) => void> = BuiltInEvents
|
* Allows you to create a typed event handler system
|
||||||
|
*/
|
||||||
|
export default class Listener<
|
||||||
|
T extends EventList = BaseEvents
|
||||||
> {
|
> {
|
||||||
|
|
||||||
private maxListeners = 10
|
private maxListeners = 10
|
||||||
|
|
||||||
private handlers: ItemToArray<T> = {}
|
private handlers: RecordToArray<BaseEvents<T>> = {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a listener to the event
|
* Add a listener to the event
|
||||||
* @param event the event name
|
* @param event the event name
|
||||||
* @param listener the listener
|
* @param listener the listener
|
||||||
|
* @returns the class
|
||||||
*/
|
*/
|
||||||
public addListener(event: keyof T, listener: T[typeof event]) {
|
public addListener<Key extends keyof BaseEvents<T>>(event: Key, listener: BaseEvents<T>[Key]): this {
|
||||||
return this.on(event, listener)
|
return this.on(event, listener)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a listener to the event
|
* Add a listener to the event
|
||||||
|
*
|
||||||
* @param event the event name
|
* @param event the event name
|
||||||
* @param listener the listener
|
* @param listener the listener
|
||||||
|
*
|
||||||
|
* @returns the class
|
||||||
*/
|
*/
|
||||||
public on(event: keyof T, listener: T[typeof event]) {
|
public on<Key extends keyof BaseEvents<T>>(event: Key, listener: BaseEvents<T>[Key]): this {
|
||||||
return this.internalAdd(true, event, listener)
|
return this.internalAdd(true, event, listener)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a one time trigger listener to the event
|
* Add a one time trigger listener to the event
|
||||||
|
*
|
||||||
* @param event the event name
|
* @param event the event name
|
||||||
* @param listener the listener
|
* @param listener the listener
|
||||||
|
*
|
||||||
|
* @returns the class
|
||||||
*/
|
*/
|
||||||
public once(event: keyof T, listener: T[typeof event]) {
|
public once<Key extends keyof BaseEvents<T>>(event: Key, listener: BaseEvents<T>[Key]): this {
|
||||||
const fn = (...args: Array<any>) => {
|
const fn = (...args: Array<any>) => {
|
||||||
listener(...args)
|
listener(...args)
|
||||||
this.off(event, fn as any)
|
this.off(event, fn as BaseEvents<T>[Key])
|
||||||
}
|
}
|
||||||
this.on(event, fn as any)
|
return this.on(event, fn as BaseEvents<T>[Key])
|
||||||
return this
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* remove a listener from the event
|
* remove a listener from the event
|
||||||
|
*
|
||||||
* @param event the event name
|
* @param event the event name
|
||||||
* @param listener the listener
|
* @param listener the listener
|
||||||
|
*
|
||||||
|
* @returns the class
|
||||||
*/
|
*/
|
||||||
public removeListener(event: keyof T, listener: T[typeof event]) {
|
public removeListener<Key extends keyof BaseEvents<T>>(event: Key, listener: BaseEvents<T>[Key]): this {
|
||||||
return this.off(event, listener)
|
return this.off(event, listener)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* remove a listener from the event
|
* remove a listener from the event
|
||||||
|
*
|
||||||
* @param event the event name
|
* @param event the event name
|
||||||
* @param listener the listener
|
* @param listener the listener
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @returns the class
|
||||||
*/
|
*/
|
||||||
public off(event: keyof T, listener: T[typeof event]) {
|
public off<Key extends keyof BaseEvents<T>>(event: Key, listener: BaseEvents<T>[Key]): this {
|
||||||
const listeners = this.listeners(event)
|
return this.internalRemove(event, listener)
|
||||||
const index = listeners.indexOf(listener)
|
|
||||||
if (index !== -1) {
|
|
||||||
(this.handlers[event] as Array<T[typeof event]>).splice(index, 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// @ts-expect-error Meta Listener
|
|
||||||
this.emit('removeListener', event, listener)
|
|
||||||
return this
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* remove every listeners from the event
|
* remove every listeners from the event
|
||||||
|
*
|
||||||
* @param event the event name
|
* @param event the event name
|
||||||
|
*
|
||||||
|
* @returns the class
|
||||||
*/
|
*/
|
||||||
public removeAllListeners(event: keyof T) {
|
public removeAllListeners(event: keyof BaseEvents<T>): this {
|
||||||
const listeners = this.listeners(event)
|
return this.internalRemove(event)
|
||||||
listeners.forEach((ev) => this.off(event, ev))
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* set a maximum number of listeners before sending a warning
|
||||||
|
*
|
||||||
|
* @param count the number of listeners before sending the warning
|
||||||
|
*
|
||||||
|
* @returns the class
|
||||||
|
*/
|
||||||
|
public setMaxListeners(count: number): this {
|
||||||
|
this.maxListeners = count
|
||||||
|
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* set a maximum numbre of listeners before sending a warning
|
|
||||||
* @param n the number of listeners before sending the warning
|
|
||||||
*/
|
|
||||||
public setMaxListeners(n: number) {
|
|
||||||
this.maxListeners = n
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the current max number of listeners
|
* Return the current max number of listeners
|
||||||
|
*
|
||||||
|
* @returns the maximum number of listeners before it send warnings
|
||||||
*/
|
*/
|
||||||
public getMaxListeners() {
|
public getMaxListeners(): number {
|
||||||
return this.maxListeners
|
return this.maxListeners
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* return every listeners from the event
|
* return every listeners from the event
|
||||||
|
*
|
||||||
* @param event the event name
|
* @param event the event name
|
||||||
|
*
|
||||||
|
* @returns the list of events
|
||||||
*/
|
*/
|
||||||
public listeners(event: keyof T) {
|
public listeners<Key extends keyof BaseEvents<T>>(event: Key): Array<BaseEvents<T>[Key]> {
|
||||||
const item = this.handlers[event] as Array<T[typeof event]>
|
return this.handlers[event] ?? []
|
||||||
return item ?? []
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* return every listeners from the event
|
* return every listeners from the event
|
||||||
|
*
|
||||||
* @param event the event name
|
* @param event the event name
|
||||||
|
*
|
||||||
|
* @returns the list of listenersattached to the event
|
||||||
*/
|
*/
|
||||||
public rawListeners(event: keyof T) {
|
public rawListeners<Key extends keyof BaseEvents<T>>(event: Key): Array<BaseEvents<T>[Key]> {
|
||||||
return this.listenerCount(event)
|
return this.listeners(event)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Emit the event with the selected variables
|
* Emit the event with the selected variables
|
||||||
|
*
|
||||||
* @param event the event to emit
|
* @param event the event to emit
|
||||||
* @param ev the variables to send
|
* @param ev the variables to send
|
||||||
|
*
|
||||||
|
* @return if the evenet was emitted to any listeners or not
|
||||||
*/
|
*/
|
||||||
public emit(event: keyof T, ...ev: Parameters<T[typeof event]>) {
|
public emit<Key extends keyof BaseEvents<T>>(event: Key, ...ev: Parameters<BaseEvents<T>[Key]>): boolean {
|
||||||
if (event !== 'all') {
|
if (event !== 'all') {
|
||||||
// @ts-expect-error Meta Listener
|
// @ts-expect-error the all event is sent each time a new event is emitted
|
||||||
this.emit('all', event, ...ev)
|
this.emit('all', event, ...ev)
|
||||||
}
|
}
|
||||||
const listeners = this.listeners(event)
|
const listeners = this.listeners(event)
|
||||||
@ -133,27 +159,36 @@ export default abstract class Listener<
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* return the number of events in the current event
|
* return the number of events in the current event
|
||||||
|
*
|
||||||
* @param event the event
|
* @param event the event
|
||||||
|
*
|
||||||
|
* @returns the number of listeners attached to the event
|
||||||
*/
|
*/
|
||||||
public listenerCount(event: keyof T) {
|
public listenerCount(event: keyof BaseEvents<T>): number {
|
||||||
return this.listeners(event).length
|
return this.listeners(event).length
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* prepend the listener to the event list
|
* prepend the listener to the event list
|
||||||
|
*
|
||||||
* @param event the event
|
* @param event the event
|
||||||
* @param listener the listener to attach
|
* @param listener the listener to attach
|
||||||
|
*
|
||||||
|
* @returns the class
|
||||||
*/
|
*/
|
||||||
public prependListener(event: keyof T, listener: T[typeof event]) {
|
public prependListener<Key extends keyof BaseEvents<T>>(event: Key, listener: BaseEvents<T>[Key]): this {
|
||||||
return this.internalAdd(false, event, listener)
|
return this.internalAdd(false, event, listener)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* prepend the listener to the event list to triggered once
|
* prepend the listener to the event list to triggered once
|
||||||
|
*
|
||||||
* @param event the event
|
* @param event the event
|
||||||
* @param listener the listener to attach
|
* @param listener the listener to attach
|
||||||
|
*
|
||||||
|
* @returns the class
|
||||||
*/
|
*/
|
||||||
public prependOnceListener(event: keyof T, listener: T[typeof event]) {
|
public prependOnceListener<Key extends keyof BaseEvents<T>>(event: Key, listener: BaseEvents<T>[Key]): this {
|
||||||
const fn = (...args: Parameters<typeof listener>) => {
|
const fn = (...args: Parameters<typeof listener>) => {
|
||||||
listener(...args)
|
listener(...args)
|
||||||
this.off(event, fn as any)
|
this.off(event, fn as any)
|
||||||
@ -164,22 +199,39 @@ export default abstract class Listener<
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* get every event names with at least one event
|
* get every event names with at least one event
|
||||||
|
*
|
||||||
|
* @returns the list of events keys
|
||||||
*/
|
*/
|
||||||
public eventNames() {
|
public eventNames(): Array<keyof BaseEvents<T>> {
|
||||||
return Object.keys(this.handlers)
|
return Object.keys(this.handlers)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Browser Listeners
|
/**
|
||||||
public addEventListener(event: keyof T, listener: T[typeof event]) {
|
* Add a listener to the event
|
||||||
|
*
|
||||||
|
* @param event the event name
|
||||||
|
* @param listener the listener
|
||||||
|
*
|
||||||
|
* @returns the class
|
||||||
|
*/
|
||||||
|
public addEventListener<Key extends keyof BaseEvents<T>>(event: Key, listener: BaseEvents<T>[Key]): this {
|
||||||
return this.on(event, listener)
|
return this.on(event, listener)
|
||||||
}
|
}
|
||||||
|
|
||||||
public removeEventListener(event: keyof T, listener: T[typeof event]) {
|
/**
|
||||||
|
* remove a listener from the event
|
||||||
|
*
|
||||||
|
* @param event the event name
|
||||||
|
* @param listener the listener
|
||||||
|
*
|
||||||
|
* @returns the class
|
||||||
|
*/
|
||||||
|
public removeEventListener<Key extends keyof BaseEvents<T>>(event: Key, listener: BaseEvents<T>[Key]): this {
|
||||||
return this.off(event, listener)
|
return this.off(event, listener)
|
||||||
}
|
}
|
||||||
|
|
||||||
private internalAdd(push: boolean, event: keyof T, listener: T[typeof event]) {
|
private internalAdd<Key extends keyof BaseEvents<T>>(push: boolean, event: Key, listener: BaseEvents<T>[Key]): this {
|
||||||
// @ts-expect-error Meta Listener
|
// @ts-expect-error Meta Listener that emit when a new event is addrd
|
||||||
this.emit('newListener', event, listener)
|
this.emit('newListener', event, listener)
|
||||||
const item = this.handlers[event]
|
const item = this.handlers[event]
|
||||||
if (!item) {
|
if (!item) {
|
||||||
@ -193,8 +245,25 @@ export default abstract class Listener<
|
|||||||
}
|
}
|
||||||
const listenerCount = this.listenerCount(event)
|
const listenerCount = this.listenerCount(event)
|
||||||
if (listenerCount > this.getMaxListeners()) {
|
if (listenerCount > this.getMaxListeners()) {
|
||||||
console.warn(`MaxListenersExceededWarning: Possible EventEmitter memory leak detected. ${this.getMaxListeners()} listeners recommended while there is ${listenerCount} listeners. Use emitter.setMaxListeners() to increase limit`)
|
console.warn(`MaxListenersExceededWarning: Possible Listener memory leak detected. ${this.getMaxListeners()} listeners recommended while there is ${listenerCount} listeners. Use setMaxListeners() to increase the limit`)
|
||||||
}
|
}
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private internalRemove<Key extends keyof BaseEvents<T>>(event: Key, listener?: BaseEvents<T>[Key]): this {
|
||||||
|
if (!listener) {
|
||||||
|
for (const listener of (this.handlers[event] ?? [])) {
|
||||||
|
this.internalRemove(event, listener)
|
||||||
|
}
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
const listeners = this.listeners(event)
|
||||||
|
const idx = listeners.indexOf(listener)
|
||||||
|
if (idx >= 0) {
|
||||||
|
this.handlers[event]?.splice(idx, 1)
|
||||||
|
}
|
||||||
|
// @ts-expect-error Meta Listener that emit when a event is removed
|
||||||
|
this.emit('removeListener', event, listener)
|
||||||
|
return this
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
2401
packages/listener/package-lock.json
generated
2401
packages/listener/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@dzeio/listener",
|
"name": "@dzeio/listener",
|
||||||
"version": "1.0.3",
|
"version": "1.1.0",
|
||||||
"description": "A NodeJS Listener implementation",
|
"description": "A NodeJS Listener implementation",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@ -10,9 +10,28 @@
|
|||||||
"author": "Aviortheking",
|
"author": "Aviortheking",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"main": "./dist/Listener.js",
|
"main": "./dist/Listener.js",
|
||||||
|
"module": "./dist/Listener.mjs",
|
||||||
"types": "./dist/Listener.d.ts",
|
"types": "./dist/Listener.d.ts",
|
||||||
|
"browser": "./dist/Listener.global.js",
|
||||||
|
"exports": {
|
||||||
|
".": {
|
||||||
|
"require": {
|
||||||
|
"types": "./dist/Listener.d.ts",
|
||||||
|
"default": "./dist/Listener.js"
|
||||||
|
},
|
||||||
|
"import": {
|
||||||
|
"types": "./dist/Listener.d.mts",
|
||||||
|
"default": "./dist/Listener.mjs"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"files": ["dist"],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"prepublishOnly": "npm run build",
|
"prepublishOnly": "npm run build",
|
||||||
"build": "tsc"
|
"build": "tsup ./Listener.ts --format cjs,esm --dts --clean && tsup ./Listener.ts --format iife --global-name Listener --minify --sourcemap"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"tsup": "^7.2.0",
|
||||||
|
"typescript": "^5.2.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user