diff --git a/README.md b/README.md index c2e52ad..4a856f1 100644 --- a/README.md +++ b/README.md @@ -182,6 +182,66 @@ myClass.myMethodWithHooks(); console.log(myClass.hooks.length); // 0 ``` +## .prependHook(eventName, handler) + +Subscribe to a hook event before all other hooks. + +```javascript +import { Hookified } from 'hookified'; + +class MyClass extends Hookified { + constructor() { + super(); + } + + async myMethodWithHooks() Promise { + let data = { some: 'data' }; + // do something + await this.hook('before:myMethod2', data); + + return data; + } +} + +const myClass = new MyClass(); +myClass.onHook('before:myMethod2', async (data) => { + data.some = 'new data'; +}); +myClass.preHook('before:myMethod2', async (data) => { + data.some = 'will run before new data'; +}); +``` + +## .prependOnceHook(eventName, handler) + +Subscribe to a hook event before all other hooks. After it is used once it will be removed. + +```javascript +import { Hookified } from 'hookified'; + +class MyClass extends Hookified { + constructor() { + super(); + } + + async myMethodWithHooks() Promise { + let data = { some: 'data' }; + // do something + await this.hook('before:myMethod2', data); + + return data; + } +} + +const myClass = new MyClass(); +myClass.onHook('before:myMethod2', async (data) => { + data.some = 'new data'; +}); +myClass.preHook('before:myMethod2', async (data) => { + data.some = 'will run before new data'; +}); +``` + ## .removeHook(eventName) Unsubscribe from a hook event. diff --git a/package.json b/package.json index 2fa26d5..c3bc26a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "hookified", - "version": "1.5.1", + "version": "1.6.0", "description": "Event and Middleware Hooks", "type": "module", "main": "dist/node/index.cjs", diff --git a/src/index.ts b/src/index.ts index 53bd7b7..8d74c05 100644 --- a/src/index.ts +++ b/src/index.ts @@ -25,6 +25,36 @@ export class Hookified extends Eventified { } } + /** + * Adds a handler function for a specific event that runs before all other handlers + * @param {string} event + * @param {Hook} handler - this can be async or sync + * @returns {void} + */ + prependHook(event: string, handler: Hook) { + const eventHandlers = this._hooks.get(event); + if (eventHandlers) { + eventHandlers.unshift(handler); + } else { + this._hooks.set(event, [handler]); + } + } + + /** + * Adds a handler that only executes once for a specific event before all other handlers + * @param event + * @param handler + */ + prependOnceHook(event: string, handler: Hook) { + const hook = async (...arguments_: any[]) => { + this.removeHook(event, hook); + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument + return handler(...arguments_); + }; + + this.prependHook(event, hook); + } + /** * Adds a handler that only executes once for a specific event * @param event diff --git a/test/index.test.ts b/test/index.test.ts index b7ef3e8..5b98e7d 100644 --- a/test/index.test.ts +++ b/test/index.test.ts @@ -138,4 +138,49 @@ describe('Hookified', () => { await hookified.hook('event', data); expect(data.key).toBe('modified'); }); + + test('prepends hook to the beginning of the array', async () => { + const hookified = new Hookified(); + const handlerData: string[] = []; + + const handler = () => { + handlerData.push('modified2'); + }; + + const handler2 = () => { + handlerData.push('modified1'); + }; + + hookified.onHook('event', handler); + hookified.prependHook('event', handler2); + await hookified.hook('event'); + expect(handlerData[0]).toBe('modified1'); + expect(handlerData[1]).toBe('modified2'); + }); + + test('prepends hook and creates new array', async () => { + const hookified = new Hookified(); + const handlerData: string[] = []; + + const handler = () => { + handlerData.push('modified1'); + }; + + hookified.prependHook('event', handler); + await hookified.hook('event'); + expect(handlerData[0]).toBe('modified1'); + }); + + test('prepends a hook and removes it', async () => { + const hookified = new Hookified(); + const handlerData: string[] = []; + + const handler = () => { + handlerData.push('modified1'); + }; + + hookified.prependOnceHook('event20', handler); + await hookified.hook('event20'); + expect(hookified.hooks.get('event20')?.length).toBe(0); + }); });