|
|
10 месяцев назад | |
|---|---|---|
| .. | ||
| LICENSE | 10 месяцев назад | |
| README.md | 10 месяцев назад | |
| package.json | 10 месяцев назад | |
npm install hookified --save
This was built because we constantly wanted hooks and events extended on libraires we are building such as Keyv and Cacheable. This is a simple way to add hooks and events to your classes.
import { Hookified } from 'hookified';
class MyClass extends Hookified {
constructor() {
super();
}
async myMethodEmittingEvent() {
this.emit('message', 'Hello World'); //using Emittery
}
//with hooks you can pass data in and if they are subscribed via onHook they can modify the data
async myMethodWithHooks() Promise<any> {
let data = { some: 'data' };
// do something
await this.hook('before:myMethod2', data);
return data;
}
}
You can even pass in multiple arguments to the hooks:
import { Hookified } from 'hookified';
class MyClass extends Hookified {
constructor() {
super();
}
async myMethodWithHooks() Promise<any> {
let data = { some: 'data' };
let data2 = { some: 'data2' };
// do something
await this.hook('before:myMethod2', data, data2);
return data;
}
}
<script type="module">
import { Hookified } from 'https://cdn.jsdelivr.net/npm/hookified/dist/browser/index.js';
class MyClass extends Hookified {
constructor() {
super();
}
async myMethodEmittingEvent() {
this.emit('message', 'Hello World'); //using Emittery
}
//with hooks you can pass data in and if they are subscribed via onHook they can modify the data
async myMethodWithHooks() Promise<any> {
let data = { some: 'data' };
// do something
await this.hook('before:myMethod2', data);
return data;
}
}
</script>
if you are not using ESM modules, you can use the following:
<script src="https://cdn.jsdelivr.net/npm/hookified/dist/browser/index.global.js"></script>
<script>
class MyClass extends Hookified {
constructor() {
super();
}
async myMethodEmittingEvent() {
this.emit('message', 'Hello World'); //using Emittery
}
//with hooks you can pass data in and if they are subscribed via onHook they can modify the data
async myMethodWithHooks() Promise<any> {
let data = { some: 'data' };
// do something
await this.hook('before:myMethod2', data);
return data;
}
}
</script>
If set to true, errors thrown in hooks will be thrown. If set to false, errors will be only emitted.
import { Hookified } from 'hookified';
class MyClass extends Hookified {
constructor() {
super({ throwHookErrors: true });
}
}
const myClass = new MyClass();
console.log(myClass.throwHookErrors); // true. because it is set in super
try {
myClass.onHook('error-event', async () => {
throw new Error('error');
});
await myClass.hook('error-event');
} catch (error) {
console.log(error.message); // error
}
myClass.throwHookErrors = false;
console.log(myClass.throwHookErrors); // false
If set, errors thrown in hooks will be logged to the logger. If not set, errors will be only emitted.
import { Hookified } from 'hookified';
import pino from 'pino';
const logger = pino(); // create a logger instance that is compatible with Logger type
class MyClass extends Hookified {
constructor() {
super({ logger });
}
async myMethodWithHooks() Promise<any> {
let data = { some: 'data' };
// do something
await this.hook('before:myMethod2', data);
return data;
}
}
const myClass = new MyClass();
myClass.onHook('before:myMethod2', async () => {
throw new Error('error');
});
// when you call before:myMethod2 it will log the error to the logger
await myClass.hook('before:myMethod2');
Subscribe to a hook event.
import { Hookified } from 'hookified';
class MyClass extends Hookified {
constructor() {
super();
}
async myMethodWithHooks() Promise<any> {
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';
});
This allows you to create a hook with the HookEntry type which includes the event and handler. This is useful for creating hooks with a single object.
import { Hookified, HookEntry } from 'hookified';
class MyClass extends Hookified {
constructor() {
super();
}
async myMethodWithHooks() Promise<any> {
let data = { some: 'data' };
// do something
await this.hook('before:myMethod2', data);
return data;
}
}
const myClass = new MyClass();
myClass.onHookEntry({
event: 'before:myMethod2',
handler: async (data) => {
data.some = 'new data';
},
});
This is an alias for .onHook(eventName, handler) for backwards compatibility.
Subscribe to multiple hook events at once
import { Hookified } from 'hookified';
class MyClass extends Hookified {
constructor() {
super();
}
async myMethodWithHooks() Promise<any> {
let data = { some: 'data' };
await this.hook('before:myMethodWithHooks', data);
// do something here with the data
data.some = 'new data';
await this.hook('after:myMethodWithHooks', data);
return data;
}
}
const myClass = new MyClass();
const hooks = [
{
event: 'before:myMethodWithHooks',
handler: async (data) => {
data.some = 'new data1';
},
},
{
event: 'after:myMethodWithHooks',
handler: async (data) => {
data.some = 'new data2';
},
},
];
Subscribe to a hook event once.
import { Hookified } from 'hookified';
class MyClass extends Hookified {
constructor() {
super();
}
async myMethodWithHooks() Promise<any> {
let data = { some: 'data' };
// do something
await this.hook('before:myMethod2', data);
return data;
}
}
const myClass = new MyClass();
myClass.onHookOnce('before:myMethod2', async (data) => {
data.some = 'new data';
});
myClass.myMethodWithHooks();
console.log(myClass.hooks.length); // 0
Subscribe to a hook event before all other hooks.
import { Hookified } from 'hookified';
class MyClass extends Hookified {
constructor() {
super();
}
async myMethodWithHooks() Promise<any> {
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';
});
Subscribe to a hook event before all other hooks. After it is used once it will be removed.
import { Hookified } from 'hookified';
class MyClass extends Hookified {
constructor() {
super();
}
async myMethodWithHooks() Promise<any> {
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';
});
Unsubscribe from a hook event.
import { Hookified } from 'hookified';
class MyClass extends Hookified {
constructor() {
super();
}
async myMethodWithHooks() Promise<any> {
let data = { some: 'data' };
// do something
await this.hook('before:myMethod2', data);
return data;
}
}
const myClass = new MyClass();
const handler = async (data) => {
data.some = 'new data';
};
myClass.onHook('before:myMethod2', handler);
myClass.removeHook('before:myMethod2', handler);
Unsubscribe from multiple hooks.
import { Hookified } from 'hookified';
class MyClass extends Hookified {
constructor() {
super();
}
async myMethodWithHooks() Promise<any> {
let data = { some: 'data' };
await this.hook('before:myMethodWithHooks', data);
// do something
data.some = 'new data';
await this.hook('after:myMethodWithHooks', data);
return data;
}
}
const myClass = new MyClass();
const hooks = [
{
event: 'before:myMethodWithHooks',
handler: async (data) => {
data.some = 'new data1';
},
},
{
event: 'after:myMethodWithHooks',
handler: async (data) => {
data.some = 'new data2';
},
},
];
myClass.onHooks(hooks);
// remove all hooks
myClass.removeHook(hooks);
Run a hook event.
import { Hookified } from 'hookified';
class MyClass extends Hookified {
constructor() {
super();
}
async myMethodWithHooks() Promise<any> {
let data = { some: 'data' };
// do something
await this.hook('before:myMethod2', data);
return data;
}
}
in this example we are passing multiple arguments to the hook:
import { Hookified } from 'hookified';
class MyClass extends Hookified {
constructor() {
super();
}
async myMethodWithHooks() Promise<any> {
let data = { some: 'data' };
let data2 = { some: 'data2' };
// do something
await this.hook('before:myMethod2', data, data2);
return data;
}
}
const myClass = new MyClass();
myClass.onHook('before:myMethod2', async (data, data2) => {
data.some = 'new data';
data2.some = 'new data2';
});
await myClass.myMethodWithHooks();
This is an alias for .hook(eventName, ...args) for backwards compatibility.
This is a helper function that will prepend a hook name with before:.
import { Hookified } from 'hookified';
class MyClass extends Hookified {
constructor() {
super();
}
async myMethodWithHooks() Promise<any> {
let data = { some: 'data' };
// the event name will be `before:myMethod2`
await this.beforeHook('myMethod2', data);
return data;
}
}
This is a helper function that will prepend a hook name with after:.
import { Hookified } from 'hookified';
class MyClass extends Hookified {
constructor() {
super();
}
async myMethodWithHooks() Promise<any> {
let data = { some: 'data' };
// the event name will be `after:myMethod2`
await this.afterHook('myMethod2', data);
return data;
}
}
Get all hooks.
import { Hookified } from 'hookified';
class MyClass extends Hookified {
constructor() {
super();
}
async myMethodWithHooks() Promise<any> {
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';
});
console.log(myClass.hooks);
Get all hooks for an event.
import { Hookified } from 'hookified';
class MyClass extends Hookified {
constructor() {
super();
}
async myMethodWithHooks() Promise<any> {
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';
});
console.log(myClass.getHooks('before:myMethod2'));
Clear all hooks for an event.
import { Hookified } from 'hookified';
class MyClass extends Hookified {
constructor() {
super();
}
async myMethodWithHooks() Promise<any> {
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.clearHooks('before:myMethod2');
If set to true, errors emitted as error will be thrown if there are no listeners. If set to false, errors will be only emitted.
import { Hookified } from 'hookified';
class MyClass extends Hookified {
constructor() {
super();
}
async myMethodWithHooks() Promise<any> {
let data = { some: 'data' };
// do something
await this.hook('before:myMethod2', data);
return data;
}
}
Subscribe to an event.
import { Hookified } from 'hookified';
class MyClass extends Hookified {
constructor() {
super();
}
async myMethodEmittingEvent() {
this.emit('message', 'Hello World');
}
}
const myClass = new MyClass();
myClass.on('message', (message) => {
console.log(message);
});
Unsubscribe from an event.
import { Hookified } from 'hookified';
class MyClass extends Hookified {
constructor() {
super();
}
async myMethodEmittingEvent() {
this.emit('message', 'Hello World');
}
}
const myClass = new MyClass();
myClass.on('message', (message) => {
console.log(message);
});
myClass.off('message', (message) => {
console.log(message);
});
Emit an event.
import { Hookified } from 'hookified';
class MyClass extends Hookified {
constructor() {
super();
}
async myMethodEmittingEvent() {
this.emit('message', 'Hello World');
}
}
Get all listeners for an event.
import { Hookified } from 'hookified';
class MyClass extends Hookified {
constructor() {
super();
}
async myMethodEmittingEvent() {
this.emit('message', 'Hello World');
}
}
const myClass = new MyClass();
myClass.on('message', (message) => {
console.log(message);
});
console.log(myClass.listeners('message'));
Remove all listeners for an event.
import { Hookified } from 'hookified';
class MyClass extends Hookified {
constructor() {
super();
}
async myMethodEmittingEvent() {
this.emit('message', 'Hello World');
}
}
const myClass = new MyClass();
myClass.on('message', (message) => {
console.log(message);
});
myClass.removeAllListeners('message');
Set the maximum number of listeners and will truncate if there are already too many.
import { Hookified } from 'hookified';
class MyClass extends Hookified {
constructor() {
super();
}
async myMethodEmittingEvent() {
this.emit('message', 'Hello World');
}
}
const myClass = new MyClass();
myClass.setMaxListeners(1);
myClass.on('message', (message) => {
console.log(message);
});
myClass.on('message', (message) => {
console.log(message);
}); // this will not be added and console warning
console.log(myClass.listenerCount('message')); // 1
Subscribe to an event once.
import { Hookified } from 'hookified';
class MyClass extends Hookified {
constructor() {
super();
}
}
const myClass = new MyClass();
myClass.once('message', (message) => {
console.log(message);
});
myClass.emit('message', 'Hello World');
myClass.emit('message', 'Hello World'); // this will not be called
Prepend a listener to an event. This will be called before any other listeners.
import { Hookified } from 'hookified';
class MyClass extends Hookified {
constructor() {
super();
}
}
const myClass = new MyClass();
myClass.prependListener('message', (message) => {
console.log(message);
});
Prepend a listener to an event once. This will be called before any other listeners.
import { Hookified } from 'hookified';
class MyClass extends Hookified {
constructor() {
super();
}
}
const myClass = new MyClass();
myClass.prependOnceListener('message', (message) => {
console.log(message);
});
myClass.emit('message', 'Hello World');
Get all event names.
import { Hookified } from 'hookified';
class MyClass extends Hookified {
constructor() {
super();
}
}
const myClass = new MyClass();
myClass.on('message', (message) => {
console.log(message);
});
console.log(myClass.eventNames());
Get the count of listeners for an event or all events if evenName not provided.
import { Hookified } from 'hookified';
class MyClass extends Hookified {
constructor() {
super();
}
}
const myClass = new MyClass();
myClass.on('message', (message) => {
console.log(message);
});
console.log(myClass.listenerCount('message')); // 1
Get all listeners for an event or all events if evenName not provided.
import { Hookified } from 'hookified';
class MyClass extends Hookified {
constructor() {
super();
}
}
const myClass = new MyClass();
myClass.on('message', (message) => {
console.log(message);
});
console.log(myClass.rawListeners('message'));
Hookified is written in TypeScript and tests are written in vitest. To run the tests, use the following command:
To setup the environment and run the tests:
pnpm i && pnpm test
Note that we are using pnpm as our package manager. If you don't have it installed, you can install it globally with:
npm install -g pnpm
To contribute follow the Contributing Guidelines and Code of Conduct.
We are doing very simple benchmarking to see how this compares to other libraries using tinybench. This is not a full benchmark but just a simple way to see how it performs. Our goal is to be as close or better than the other libraries including native (EventEmitter).
| name | summary | ops/sec | time/op | margin | samples |
|---|---|---|---|---|---|
| Hookified 1.8.0 | 🥇 | 4M | 299ns | ±2.42% | 3M |
| Hookable 5.5.3 | -73% | 982K | 1µs | ±2.92% | 812K |
This shows how on par hookified is to the native EventEmitter and popular eventemitter3. These are simple emitting benchmarks to see how it performs.
| name | summary | ops/sec | time/op | margin | samples |
|---|---|---|---|---|---|
| Hookified 1.8.0 | 🥇 | 10M | 112ns | ±1.13% | 9M |
| EventEmitter3 5.0.1 | -1.3% | 10M | 114ns | ±1.84% | 9M |
| EventEmitter v22.12.0 | -1.5% | 9M | 114ns | ±1.18% | 9M |
| Emittery 1.1.0 | -92% | 785K | 1µs | ±0.45% | 761K |
Note: the EventEmitter version is Nodejs versioning.