hookified 1.12.0 → 1.12.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -16,6 +16,9 @@
16
16
  - Browser Support and Delivered via CDN
17
17
  - Ability to throw errors in hooks
18
18
  - Ability to pass in a logger (such as Pino) for errors
19
+ - Enforce consistent hook naming conventions with `enforceBeforeAfter`
20
+ - Deprecation warnings for hooks with `deprecatedHooks`
21
+ - Control deprecated hook execution with `allowDeprecated`
19
22
  - No package dependencies and only 100KB in size
20
23
  - Fast and Efficient with [Benchmarks](#benchmarks)
21
24
  - Maintained on a regular basis!
@@ -27,6 +30,9 @@
27
30
  - [API - Hooks](#api---hooks)
28
31
  - [.throwHookErrors](#throwhookerrors)
29
32
  - [.logger](#logger)
33
+ - [.enforceBeforeAfter](#enforcebeforeafter)
34
+ - [.deprecatedHooks](#deprecatedhooks)
35
+ - [.allowDeprecated](#allowdeprecated)
30
36
  - [.onHook(eventName, handler)](#onhookeventname-handler)
31
37
  - [.onHookEntry(hookEntry)](#onhookentryhookentry)
32
38
  - [.addHook(eventName, handler)](#addhookeventname-handler)
@@ -57,9 +63,9 @@
57
63
  - [.eventNames()](#eventnames)
58
64
  - [.listenerCount(eventName?)](#listenercounteventname)
59
65
  - [.rawListeners(eventName?)](#rawlistenerseventname)
60
- - [Development and Contribution](#development-and-contribution)
61
66
  - [Benchmarks](#benchmarks)
62
- - [License](#license)
67
+ - [How to Contribute](#how-to-contribute)
68
+ - [License and Copyright](#license-and-copyright)
63
69
 
64
70
  # Installation
65
71
  ```bash
@@ -231,6 +237,197 @@ myClass.onHook('before:myMethod2', async () => {
231
237
  await myClass.hook('before:myMethod2');
232
238
  ```
233
239
 
240
+ ## .enforceBeforeAfter
241
+
242
+ If set to true, enforces that all hook names must start with 'before' or 'after'. This is useful for maintaining consistent hook naming conventions in your application. Default is false.
243
+
244
+ ```javascript
245
+ import { Hookified } from 'hookified';
246
+
247
+ class MyClass extends Hookified {
248
+ constructor() {
249
+ super({ enforceBeforeAfter: true });
250
+ }
251
+ }
252
+
253
+ const myClass = new MyClass();
254
+
255
+ console.log(myClass.enforceBeforeAfter); // true
256
+
257
+ // These will work fine
258
+ myClass.onHook('beforeSave', async () => {
259
+ console.log('Before save hook');
260
+ });
261
+
262
+ myClass.onHook('afterSave', async () => {
263
+ console.log('After save hook');
264
+ });
265
+
266
+ myClass.onHook('before:validation', async () => {
267
+ console.log('Before validation hook');
268
+ });
269
+
270
+ // This will throw an error
271
+ try {
272
+ myClass.onHook('customEvent', async () => {
273
+ console.log('This will not work');
274
+ });
275
+ } catch (error) {
276
+ console.log(error.message); // Hook event "customEvent" must start with "before" or "after" when enforceBeforeAfter is enabled
277
+ }
278
+
279
+ // You can also change it dynamically
280
+ myClass.enforceBeforeAfter = false;
281
+ myClass.onHook('customEvent', async () => {
282
+ console.log('This will work now');
283
+ });
284
+ ```
285
+
286
+ The validation applies to all hook-related methods:
287
+ - `onHook()`, `addHook()`, `onHookEntry()`, `onHooks()`
288
+ - `prependHook()`, `onceHook()`, `prependOnceHook()`
289
+ - `hook()`, `callHook()`
290
+ - `getHooks()`, `removeHook()`, `removeHooks()`
291
+
292
+ Note: The `beforeHook()` and `afterHook()` helper methods automatically generate proper hook names and work regardless of the `enforceBeforeAfter` setting.
293
+
294
+ ## .deprecatedHooks
295
+
296
+ A Map of deprecated hook names to deprecation messages. When a deprecated hook is used, a warning will be emitted via the 'warn' event and logged to the logger (if available). Default is an empty Map.
297
+
298
+ ```javascript
299
+ import { Hookified } from 'hookified';
300
+
301
+ // Define deprecated hooks with custom messages
302
+ const deprecatedHooks = new Map([
303
+ ['oldHook', 'Use newHook instead'],
304
+ ['legacyMethod', 'This hook will be removed in v2.0'],
305
+ ['deprecatedFeature', ''] // Empty message - will just say "deprecated"
306
+ ]);
307
+
308
+ class MyClass extends Hookified {
309
+ constructor() {
310
+ super({ deprecatedHooks });
311
+ }
312
+ }
313
+
314
+ const myClass = new MyClass();
315
+
316
+ console.log(myClass.deprecatedHooks); // Map with deprecated hooks
317
+
318
+ // Listen for deprecation warnings
319
+ myClass.on('warn', (event) => {
320
+ console.log(`Deprecation warning: ${event.message}`);
321
+ // event.hook contains the hook name
322
+ // event.message contains the full warning message
323
+ });
324
+
325
+ // Using a deprecated hook will emit warnings
326
+ myClass.onHook('oldHook', () => {
327
+ console.log('This hook is deprecated');
328
+ });
329
+ // Output: Hook "oldHook" is deprecated: Use newHook instead
330
+
331
+ // Using a deprecated hook with empty message
332
+ myClass.onHook('deprecatedFeature', () => {
333
+ console.log('This hook is deprecated');
334
+ });
335
+ // Output: Hook "deprecatedFeature" is deprecated
336
+
337
+ // You can also set deprecated hooks dynamically
338
+ myClass.deprecatedHooks.set('anotherOldHook', 'Please migrate to the new API');
339
+
340
+ // Works with logger if provided
341
+ import pino from 'pino';
342
+ const logger = pino();
343
+
344
+ const myClassWithLogger = new Hookified({
345
+ deprecatedHooks,
346
+ logger
347
+ });
348
+
349
+ // Deprecation warnings will be logged to logger.warn
350
+ ```
351
+
352
+ The deprecation warning system applies to all hook-related methods:
353
+ - Registration: `onHook()`, `addHook()`, `onHookEntry()`, `onHooks()`, `prependHook()`, `onceHook()`, `prependOnceHook()`
354
+ - Execution: `hook()`, `callHook()`
355
+ - Management: `getHooks()`, `removeHook()`, `removeHooks()`
356
+
357
+ Deprecation warnings are emitted in two ways:
358
+ 1. **Event**: A 'warn' event is emitted with `{ hook: string, message: string }`
359
+ 2. **Logger**: Logged to `logger.warn()` if a logger is configured and has a `warn` method
360
+
361
+ ## .allowDeprecated
362
+
363
+ Controls whether deprecated hooks are allowed to be registered and executed. Default is true. When set to false, deprecated hooks will still emit warnings but will be prevented from registration and execution.
364
+
365
+ ```javascript
366
+ import { Hookified } from 'hookified';
367
+
368
+ const deprecatedHooks = new Map([
369
+ ['oldHook', 'Use newHook instead']
370
+ ]);
371
+
372
+ class MyClass extends Hookified {
373
+ constructor() {
374
+ super({ deprecatedHooks, allowDeprecated: false });
375
+ }
376
+ }
377
+
378
+ const myClass = new MyClass();
379
+
380
+ console.log(myClass.allowDeprecated); // false
381
+
382
+ // Listen for deprecation warnings (still emitted even when blocked)
383
+ myClass.on('warn', (event) => {
384
+ console.log(`Warning: ${event.message}`);
385
+ });
386
+
387
+ // Try to register a deprecated hook - will emit warning but not register
388
+ myClass.onHook('oldHook', () => {
389
+ console.log('This will never execute');
390
+ });
391
+ // Output: Warning: Hook "oldHook" is deprecated: Use newHook instead
392
+
393
+ // Verify hook was not registered
394
+ console.log(myClass.getHooks('oldHook')); // undefined
395
+
396
+ // Try to execute a deprecated hook - will emit warning but not execute
397
+ await myClass.hook('oldHook');
398
+ // Output: Warning: Hook "oldHook" is deprecated: Use newHook instead
399
+ // (but no handlers execute)
400
+
401
+ // Non-deprecated hooks work normally
402
+ myClass.onHook('validHook', () => {
403
+ console.log('This works fine');
404
+ });
405
+
406
+ console.log(myClass.getHooks('validHook')); // [handler function]
407
+
408
+ // You can dynamically change the setting
409
+ myClass.allowDeprecated = true;
410
+
411
+ // Now deprecated hooks can be registered and executed
412
+ myClass.onHook('oldHook', () => {
413
+ console.log('Now this works');
414
+ });
415
+
416
+ console.log(myClass.getHooks('oldHook')); // [handler function]
417
+ ```
418
+
419
+ **Behavior when `allowDeprecated` is false:**
420
+ - **Registration**: All hook registration methods (`onHook`, `addHook`, `prependHook`, etc.) will emit warnings but skip registration
421
+ - **Execution**: Hook execution methods (`hook`, `callHook`) will emit warnings but skip execution
422
+ - **Management**: Hook management methods (`getHooks`, `removeHook`) will emit warnings and return undefined/skip operations
423
+ - **Warnings**: Deprecation warnings are always emitted regardless of `allowDeprecated` setting
424
+
425
+ **Use cases:**
426
+ - **Development**: Keep `allowDeprecated: true` to maintain functionality while seeing warnings
427
+ - **Testing**: Set `allowDeprecated: false` to ensure no deprecated hooks are accidentally used
428
+ - **Migration**: Gradually disable deprecated hooks during API transitions
429
+ - **Production**: Disable deprecated hooks to prevent legacy code execution
430
+
234
431
  ## .onHook(eventName, handler)
235
432
 
236
433
  Subscribe to a hook event.
@@ -999,7 +1196,31 @@ myClass.on('message', (message) => {
999
1196
  console.log(myClass.rawListeners('message'));
1000
1197
  ```
1001
1198
 
1002
- # Development and Contribution
1199
+ # Benchmarks
1200
+
1201
+ 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).
1202
+
1203
+ ## Hooks
1204
+
1205
+ | name | summary | ops/sec | time/op | margin | samples |
1206
+ |-----------------------|:---------:|----------:|----------:|:--------:|----------:|
1207
+ | Hookified (v1.12.1) | 🥇 | 5M | 243ns | ±0.89% | 4M |
1208
+ | Hookable (v5.5.3) | -69% | 1M | 835ns | ±2.23% | 1M |
1209
+
1210
+ ## Emits
1211
+
1212
+ This shows how on par `hookified` is to the native `EventEmitter` and popular `eventemitter3`. These are simple emitting benchmarks to see how it performs.
1213
+
1214
+ | name | summary | ops/sec | time/op | margin | samples |
1215
+ |---------------------------|:---------:|----------:|----------:|:--------:|----------:|
1216
+ | Hookified (v1.12.1) | 🥇 | 12M | 89ns | ±2.56% | 11M |
1217
+ | EventEmitter3 (v5.0.1) | -1.7% | 12M | 91ns | ±3.31% | 11M |
1218
+ | EventEmitter (v20.17.0) | -4% | 11M | 92ns | ±0.38% | 11M |
1219
+ | Emittery (v1.2.0) | -91% | 1M | 1µs | ±1.59% | 993K |
1220
+
1221
+ _Note: the `EventEmitter` version is Nodejs versioning._
1222
+
1223
+ # How to Contribute
1003
1224
 
1004
1225
  Hookified is written in TypeScript and tests are written in `vitest`. To run the tests, use the following command:
1005
1226
 
@@ -1017,31 +1238,19 @@ npm install -g pnpm
1017
1238
 
1018
1239
  To contribute follow the [Contributing Guidelines](CONTRIBUTING.md) and [Code of Conduct](CODE_OF_CONDUCT.md).
1019
1240
 
1020
- # Benchmarks
1021
-
1022
- 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).
1023
-
1024
- ## Hooks
1025
-
1026
- | name | summary | ops/sec | time/op | margin | samples |
1027
- |-------------------|:---------:|----------:|----------:|:--------:|----------:|
1028
- | Hookified 1.8.0 | 🥇 | 4M | 299ns | ±2.42% | 3M |
1029
- | Hookable 5.5.3 | -73% | 982K | 1µs | ±2.92% | 812K |
1030
-
1031
- ## Emits
1241
+ ```bash
1242
+ pnpm i && pnpm test
1243
+ ```
1032
1244
 
1033
- This shows how on par `hookified` is to the native `EventEmitter` and popular `eventemitter3`. These are simple emitting benchmarks to see how it performs.
1245
+ Note that we are using `pnpm` as our package manager. If you don't have it installed, you can install it globally with:
1034
1246
 
1035
- | name | summary | ops/sec | time/op | margin | samples |
1036
- |-------------------------|:---------:|----------:|----------:|:--------:|----------:|
1037
- | Hookified 1.8.0 | 🥇 | 10M | 112ns | ±1.13% | 9M |
1038
- | EventEmitter3 5.0.1 | -1.3% | 10M | 114ns | ±1.84% | 9M |
1039
- | EventEmitter v22.12.0 | -1.5% | 9M | 114ns | ±1.18% | 9M |
1040
- | Emittery 1.1.0 | -92% | 785K | 1µs | ±0.45% | 761K |
1247
+ ```bash
1248
+ npm install -g pnpm
1249
+ ```
1041
1250
 
1042
- _Note: the `EventEmitter` version is Nodejs versioning._
1251
+ To contribute follow the [Contributing Guidelines](CONTRIBUTING.md) and [Code of Conduct](CODE_OF_CONDUCT.md).
1043
1252
 
1044
- # License
1253
+ # License and Copyright
1045
1254
 
1046
1255
  [MIT & © Jared Wray](LICENSE)
1047
1256