diff --git a/packages/listener/.eslintrc.js b/packages/listener/.eslintrc.js new file mode 100644 index 0000000..7a0472e --- /dev/null +++ b/packages/listener/.eslintrc.js @@ -0,0 +1,5 @@ +module.exports = { + "parserOptions": { + "project": __dirname + "/tsconfig.json" + } +} \ No newline at end of file diff --git a/packages/listener/.npmignore b/packages/listener/.npmignore new file mode 100644 index 0000000..5270492 --- /dev/null +++ b/packages/listener/.npmignore @@ -0,0 +1,6 @@ +node_modules +.eslintrc.js +.gitignore +.npmignore +tsconfig.json +Listener.ts diff --git a/packages/listener/Listener.ts b/packages/listener/Listener.ts new file mode 100644 index 0000000..d7be0a1 --- /dev/null +++ b/packages/listener/Listener.ts @@ -0,0 +1,193 @@ +type ItemToArray = { + [P in keyof T]?: Array +} + +export default abstract class Listener< + T extends Record) => void> = { + newListener: (eventName: string, listener: (...args: Array) => void) => void + removeListener: (eventName: string, listener: (...args: Array) => void) => void + } +> { + + private maxListeners = 10 + + private handlers: ItemToArray = {} + + /** + * Add a listener to the event + * @param event the event name + * @param listener the listener + */ + public addListener(event: keyof T, listener: T[typeof event]) { + return this.on(event, listener) + } + + /** + * Add a listener to the event + * @param event the event name + * @param listener the listener + */ + public on(event: keyof T, listener: T[typeof event]) { + return this.internalAdd(true, event, listener) + } + + /** + * Add a one time trigger listener to the event + * @param event the event name + * @param listener the listener + */ + public once(event: keyof T, listener: T[typeof event]) { + const fn = (...args: Array) => { + listener(...args) + this.off(event, fn as any) + } + this.on(event, fn as any) + return this + } + + /** + * remove a listener from the event + * @param event the event name + * @param listener the listener + */ + public removeListener(event: keyof T, listener: T[typeof event]) { + return this.off(event, listener) + } + + /** + * remove a listener from the event + * @param event the event name + * @param listener the listener + */ + public off(event: keyof T, listener: T[typeof event]) { + const listeners = this.listeners(event) + const index = listeners.indexOf(listener) + if (index !== -1) { + (this.handlers[event] as Array).splice(index, 1) + } + + // @ts-ignore + this.emit('removeListener', event, listener) + return this + } + + /** + * remove every listeners from the event + * @param event the event name + */ + public removeAllListeners(event: keyof T) { + const listeners = this.listeners(event) + listeners.forEach((ev) => this.off(event, ev)) + 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 + */ + public getMaxListeners() { + return this.maxListeners + } + + /** + * return every listeners from the event + * @param event the event name + */ + public listeners(event: keyof T) { + const item = this.handlers[event] as Array + return item ?? [] + } + + /** + * return every listeners from the event + * @param event the event name + */ + public rawListeners(event: keyof T) { + return this.listenerCount(event) + } + + /** + * Emit the event with the selected variables + * @param event the event to emit + * @param ev the variables to send + */ + public emit(event: keyof T, ...ev: Parameters) { + const listeners = this.listeners(event) + listeners.forEach((fn) => fn(...ev)) + return listeners.length > 0 + } + + /** + * return the number of events in the current event + * @param event the event + */ + public listenerCount(event: keyof T) { + return this.listeners(event).length + } + + /** + * prepend the listener to the event list + * @param event the event + * @param listener the listener to attach + */ + public prependListener(event: keyof T, listener: T[typeof event]) { + return this.internalAdd(false, event, listener) + } + + /** + * prepend the listener to the event list to triggered once + * @param event the event + * @param listener the listener to attach + */ + public prependOnceListener(event: keyof T, listener: T[typeof event]) { + const fn = (...args: Parameters) => { + listener(...args) + this.off(event, fn as any) + } + this.prependListener(event, fn as any) + return this + } + + /** + * get every event names with at least one event + */ + public eventNames() { + return Object.keys(this.handlers) + } + + // Browser Listeners + public addEventListener(event: keyof T, listener: T[typeof event]) { + return this.on(event, listener) + } + + public removeEventListener(event: keyof T, listener: T[typeof event]) { + return this.off(event, listener) + } + + private internalAdd(push: boolean, event: keyof T, listener: T[typeof event]) { + // @ts-ignore + this.emit('newListener', event, listener) + const item = this.handlers[event] + if (!item) { + this.handlers[event] = [listener] + } else { + if (push) { + item.push(listener) + } else { + item.unshift(listener) + } + } + const listenerCount = this.listenerCount(event) + if (listenerCount > this.getMaxListeners()) { + console.warn(`MaxListenersExceededWarning: Possible EventEmitter memory leak detected. ${this.getMaxListeners()} userStateChanged listeners added to [FireAuth]. Use emitter.setMaxListeners() to increase limitWarning: more than are in the event ${event}! (${listenerCount})`) + } + return this + } +} diff --git a/packages/listener/README.md b/packages/listener/README.md new file mode 100644 index 0000000..d57b8f0 --- /dev/null +++ b/packages/listener/README.md @@ -0,0 +1,53 @@ +# Listener + +Export an Abstract class o quicly add an isomorphic listener to your own classes + +## how to use it + +### Javascript + +```javascript +const Listener = require('@dzeio/listener') + +class Test extends Listener { + public pouet() { + this.emit('eventName') + } +} + +exports.default = Test + +// Another file + +const Test = require('./Test') + +const test = new Test() +test.on('eventName', () => { + console.log('Event Ran') +}) +test.pouet() +``` + +### TS + +```typescript +import Listener from '@dzeio/listener' + +export default class Test extends Listener< + eventName: () => void +> { + public pouet() { + this.emit('eventName') + } +} + +// Another file + +import Test from './Test' + +const test = new Test() +test.on('eventName', () => { + console.log('Event Ran') +}) +test.pouet() +``` diff --git a/packages/listener/package.json b/packages/listener/package.json new file mode 100644 index 0000000..2f7114f --- /dev/null +++ b/packages/listener/package.json @@ -0,0 +1,22 @@ +{ + "name": "@dzeio/listener", + "version": "0.0.1", + "description": "A NodeJS Listener implementation", + "repository": { + "type": "git", + "url": "https://github.com/dzeiocom/libs.git", + "directory": "packages/listener" + }, + "author": "Aviortheking", + "license": "MIT", + "main": "./dist/Listener.js", + "types": "./dist/Listener.d.ts", + "devDependencies": { + "typescript": "^4.0.3" + }, + "scripts": { + "prepublishOnly": "yarn build", + "build": "tsc", + "test": "tsc --noEmit" + } +} diff --git a/packages/listener/tsconfig.json b/packages/listener/tsconfig.json new file mode 100644 index 0000000..6047180 --- /dev/null +++ b/packages/listener/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "dist" + }, + "files": [ + "Listener.ts" + ] +}