hookable 5.1.0 → 5.2.0

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
@@ -44,7 +44,7 @@ hooks.callHook('hello')
44
44
  ```js
45
45
  import { Hookable } from 'hookable'
46
46
 
47
- export default class Foo extends Hookable {
47
+ export default class FooLib extends Hookable {
48
48
  constructor() {
49
49
  // Call to parent to initialize
50
50
  super()
@@ -62,7 +62,7 @@ export default class Foo extends Hookable {
62
62
  **Inside plugins, register for any hook:**
63
63
 
64
64
  ```js
65
- const lib = newFooLib()
65
+ const lib = new FooLib()
66
66
 
67
67
  // Register a handler for `hook2`
68
68
  lib.hook('hook2', async () => { /* ... */ })
@@ -77,7 +77,7 @@ lib.addHooks({
77
77
  **Unregistering hooks:**
78
78
 
79
79
  ```js
80
- const lib = newFooLib()
80
+ const lib = new FooLib()
81
81
 
82
82
  const hook0 = async () => { /* ... */ }
83
83
  const hook1 = async () => { /* ... */ }
@@ -101,7 +101,7 @@ lib.removeHook('hook2', hook2)
101
101
  **Triggering a hook handler once:**
102
102
 
103
103
  ```js
104
- const lib = newFooLib()
104
+ const lib = new FooLib()
105
105
 
106
106
  const unregister = lib.hook('hook0', async () => {
107
107
  // Unregister as soon as the hook is executed
@@ -194,6 +194,32 @@ hookable.removeHooks({
194
194
  })
195
195
  ```
196
196
 
197
+ ### `beforeEach (syncCallback)`
198
+
199
+ Registers a (sync) callback to be called before each hook is being called.
200
+
201
+ ```js
202
+ hookable.beforeEach((event) => { console.log(`${event.name} hook is being called with ${event.args}`)})
203
+ hookable.hook('test', () => { console.log('running test hook') })
204
+
205
+ // test hook is being called with []
206
+ // running test hook
207
+ await hookable.callHook('test')
208
+ ```
209
+
210
+ ### `afterEach (syncCallback)`
211
+
212
+ Registers a (sync) callback to be called after each hook is being called.
213
+
214
+ ```js
215
+ hookable.afterEach((event) => { console.log(`${event.name} hook called with ${event.args}`)})
216
+ hookable.hook('test', () => { console.log('running test hook') })
217
+
218
+ // running test hook
219
+ // test hook called with []
220
+ await hookable.callHook('test')
221
+ ```
222
+
197
223
  ## Migration
198
224
 
199
225
  ### From `4.x` to `5.x`
package/dist/index.cjs CHANGED
@@ -45,10 +45,17 @@ function serialCaller(hooks, args) {
45
45
  function parallelCaller(hooks, args) {
46
46
  return Promise.all(hooks.map((hook) => hook.apply(void 0, args)));
47
47
  }
48
+ function callEachWith(callbacks, arg0) {
49
+ for (const cb of callbacks) {
50
+ cb(arg0);
51
+ }
52
+ }
48
53
 
49
54
  class Hookable {
50
55
  constructor() {
51
56
  this._hooks = {};
57
+ this._before = null;
58
+ this._after = null;
52
59
  this._deprecatedHooks = {};
53
60
  this.hook = this.hook.bind(this);
54
61
  this.callHook = this.callHook.bind(this);
@@ -72,7 +79,9 @@ class Hookable {
72
79
  }
73
80
  if (deprecatedHookObj) {
74
81
  if (!deprecatedHookObj.message) {
75
- console.warn(`${originalName} hook has been deprecated` + (deprecatedHookObj.to ? `, please use ${deprecatedHookObj.to}` : ""));
82
+ console.warn(
83
+ `${originalName} hook has been deprecated` + (deprecatedHookObj.to ? `, please use ${deprecatedHookObj.to}` : "")
84
+ );
76
85
  } else {
77
86
  console.warn(deprecatedHookObj.message);
78
87
  }
@@ -110,9 +119,17 @@ class Hookable {
110
119
  }
111
120
  deprecateHook(name, deprecated) {
112
121
  this._deprecatedHooks[name] = deprecated;
122
+ const _hooks = this._hooks[name] || [];
123
+ this._hooks[name] = void 0;
124
+ for (const hook of _hooks) {
125
+ this.hook(name, hook);
126
+ }
113
127
  }
114
128
  deprecateHooks(deprecatedHooks) {
115
129
  Object.assign(this._deprecatedHooks, deprecatedHooks);
130
+ for (const name in deprecatedHooks) {
131
+ this.deprecateHook(name, deprecatedHooks[name]);
132
+ }
116
133
  }
117
134
  addHooks(configHooks) {
118
135
  const hooks = flatHooks(configHooks);
@@ -128,19 +145,36 @@ class Hookable {
128
145
  }
129
146
  }
130
147
  callHook(name, ...args) {
131
- if (this._hooks[name]) {
132
- return serialCaller(this._hooks[name], args);
133
- }
148
+ return this.callHookWith(serialCaller, name, ...args);
134
149
  }
135
150
  callHookParallel(name, ...args) {
136
- if (this._hooks[name]) {
137
- return parallelCaller(this._hooks[name], args);
138
- }
151
+ return this.callHookWith(parallelCaller, name, ...args);
139
152
  }
140
153
  callHookWith(caller, name, ...args) {
141
- if (this._hooks[name]) {
142
- return caller(this._hooks[name], args);
154
+ const event = this._before || this._after ? { name, args, context: {} } : void 0;
155
+ if (this._before) {
156
+ callEachWith(this._before, event);
157
+ }
158
+ const result = caller(this._hooks[name] || [], args);
159
+ if (result instanceof Promise) {
160
+ return result.finally(() => {
161
+ if (this._after) {
162
+ callEachWith(this._after, event);
163
+ }
164
+ });
143
165
  }
166
+ if (this._after) {
167
+ callEachWith(this._after, event);
168
+ }
169
+ return result;
170
+ }
171
+ beforeEach(fn) {
172
+ this._before = this._before || [];
173
+ this._before.push(fn);
174
+ }
175
+ afterEach(fn) {
176
+ this._after = this._after || [];
177
+ this._after.push(fn);
144
178
  }
145
179
  }
146
180
  function createHooks() {
package/dist/index.d.ts CHANGED
@@ -36,20 +36,31 @@ declare type NestedHooks<T> = (Partial<StripGeneric<T>> | Partial<OnlyGeneric<T>
36
36
  }>;
37
37
 
38
38
  declare type InferCallback<HT, HN extends keyof HT> = HT[HN] extends HookCallback ? HT[HN] : never;
39
+ declare type InferSpyEvent<HT extends Record<string, any>> = {
40
+ [key in keyof HT]: {
41
+ name: key;
42
+ args: Parameters<HT[key]>;
43
+ context: Record<string, any>;
44
+ };
45
+ }[keyof HT];
39
46
  declare class Hookable<HooksT = Record<string, HookCallback>, HookNameT extends HookKeys<HooksT> = HookKeys<HooksT>> {
40
47
  private _hooks;
48
+ private _before;
49
+ private _after;
41
50
  private _deprecatedHooks;
42
51
  constructor();
43
52
  hook<NameT extends HookNameT>(name: NameT, fn: InferCallback<HooksT, NameT>): () => void;
44
53
  hookOnce<NameT extends HookNameT>(name: NameT, fn: InferCallback<HooksT, NameT>): () => void;
45
54
  removeHook<NameT extends HookNameT>(name: NameT, fn: InferCallback<HooksT, NameT>): void;
46
55
  deprecateHook<NameT extends HookNameT>(name: NameT, deprecated: DeprecatedHook<HooksT>): void;
47
- deprecateHooks(deprecatedHooks: Record<HookNameT, DeprecatedHook<HooksT>>): void;
56
+ deprecateHooks(deprecatedHooks: Partial<Record<HookNameT, DeprecatedHook<HooksT>>>): void;
48
57
  addHooks(configHooks: NestedHooks<HooksT>): () => void;
49
58
  removeHooks(configHooks: NestedHooks<HooksT>): void;
50
- callHook<NameT extends HookNameT>(name: NameT, ...args: Parameters<InferCallback<HooksT, NameT>>): Promise<any>;
51
- callHookParallel<NameT extends HookNameT>(name: NameT, ...args: Parameters<InferCallback<HooksT, NameT>>): Promise<any[]>;
59
+ callHook<NameT extends HookNameT>(name: NameT, ...args: Parameters<InferCallback<HooksT, NameT>>): void | Promise<any>;
60
+ callHookParallel<NameT extends HookNameT>(name: NameT, ...args: Parameters<InferCallback<HooksT, NameT>>): void | Promise<any[]>;
52
61
  callHookWith<NameT extends HookNameT, CallFunction extends (hooks: HookCallback[], args: Parameters<InferCallback<HooksT, NameT>>) => any>(caller: CallFunction, name: NameT, ...args: Parameters<InferCallback<HooksT, NameT>>): void | ReturnType<CallFunction>;
62
+ beforeEach(fn: (event: InferSpyEvent<HooksT>) => void): void;
63
+ afterEach(fn: (event: InferSpyEvent<HooksT>) => void): void;
53
64
  }
54
65
  declare function createHooks<T>(): Hookable<T>;
55
66
 
package/dist/index.mjs CHANGED
@@ -41,10 +41,17 @@ function serialCaller(hooks, args) {
41
41
  function parallelCaller(hooks, args) {
42
42
  return Promise.all(hooks.map((hook) => hook.apply(void 0, args)));
43
43
  }
44
+ function callEachWith(callbacks, arg0) {
45
+ for (const cb of callbacks) {
46
+ cb(arg0);
47
+ }
48
+ }
44
49
 
45
50
  class Hookable {
46
51
  constructor() {
47
52
  this._hooks = {};
53
+ this._before = null;
54
+ this._after = null;
48
55
  this._deprecatedHooks = {};
49
56
  this.hook = this.hook.bind(this);
50
57
  this.callHook = this.callHook.bind(this);
@@ -68,7 +75,9 @@ class Hookable {
68
75
  }
69
76
  if (deprecatedHookObj) {
70
77
  if (!deprecatedHookObj.message) {
71
- console.warn(`${originalName} hook has been deprecated` + (deprecatedHookObj.to ? `, please use ${deprecatedHookObj.to}` : ""));
78
+ console.warn(
79
+ `${originalName} hook has been deprecated` + (deprecatedHookObj.to ? `, please use ${deprecatedHookObj.to}` : "")
80
+ );
72
81
  } else {
73
82
  console.warn(deprecatedHookObj.message);
74
83
  }
@@ -106,9 +115,17 @@ class Hookable {
106
115
  }
107
116
  deprecateHook(name, deprecated) {
108
117
  this._deprecatedHooks[name] = deprecated;
118
+ const _hooks = this._hooks[name] || [];
119
+ this._hooks[name] = void 0;
120
+ for (const hook of _hooks) {
121
+ this.hook(name, hook);
122
+ }
109
123
  }
110
124
  deprecateHooks(deprecatedHooks) {
111
125
  Object.assign(this._deprecatedHooks, deprecatedHooks);
126
+ for (const name in deprecatedHooks) {
127
+ this.deprecateHook(name, deprecatedHooks[name]);
128
+ }
112
129
  }
113
130
  addHooks(configHooks) {
114
131
  const hooks = flatHooks(configHooks);
@@ -124,19 +141,36 @@ class Hookable {
124
141
  }
125
142
  }
126
143
  callHook(name, ...args) {
127
- if (this._hooks[name]) {
128
- return serialCaller(this._hooks[name], args);
129
- }
144
+ return this.callHookWith(serialCaller, name, ...args);
130
145
  }
131
146
  callHookParallel(name, ...args) {
132
- if (this._hooks[name]) {
133
- return parallelCaller(this._hooks[name], args);
134
- }
147
+ return this.callHookWith(parallelCaller, name, ...args);
135
148
  }
136
149
  callHookWith(caller, name, ...args) {
137
- if (this._hooks[name]) {
138
- return caller(this._hooks[name], args);
150
+ const event = this._before || this._after ? { name, args, context: {} } : void 0;
151
+ if (this._before) {
152
+ callEachWith(this._before, event);
153
+ }
154
+ const result = caller(this._hooks[name] || [], args);
155
+ if (result instanceof Promise) {
156
+ return result.finally(() => {
157
+ if (this._after) {
158
+ callEachWith(this._after, event);
159
+ }
160
+ });
139
161
  }
162
+ if (this._after) {
163
+ callEachWith(this._after, event);
164
+ }
165
+ return result;
166
+ }
167
+ beforeEach(fn) {
168
+ this._before = this._before || [];
169
+ this._before.push(fn);
170
+ }
171
+ afterEach(fn) {
172
+ this._after = this._after || [];
173
+ this._after.push(fn);
140
174
  }
141
175
  }
142
176
  function createHooks() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hookable",
3
- "version": "5.1.0",
3
+ "version": "5.2.0",
4
4
  "description": "Awaitable hook system",
5
5
  "keywords": [
6
6
  "hook",
@@ -21,24 +21,24 @@
21
21
  "files": [
22
22
  "dist"
23
23
  ],
24
- "scripts": {
25
- "build": "unbuild",
26
- "lint": "eslint --ext .ts src",
27
- "prepublish": "yarn build",
28
- "release": "yarn test && yarn build && standard-version && git push --follow-tags && npm publish",
29
- "test": "yarn lint && yarn jest"
30
- },
31
24
  "devDependencies": {
32
25
  "@nuxtjs/eslint-config-typescript": "latest",
33
- "@types/jest": "latest",
34
- "babel-jest": "latest",
35
- "codecov": "latest",
26
+ "@vitest/coverage-c8": "^0.22.1",
36
27
  "eslint": "latest",
37
- "expect-type": "^0.12.0",
38
- "jest": "latest",
28
+ "expect-type": "^0.13.0",
39
29
  "standard-version": "latest",
40
- "ts-jest": "latest",
41
30
  "typescript": "latest",
42
- "unbuild": "latest"
31
+ "unbuild": "latest",
32
+ "vitest": "latest"
33
+ },
34
+ "packageManager": "pnpm@7.9.4",
35
+ "scripts": {
36
+ "build": "unbuild",
37
+ "dev": "vitest",
38
+ "lint": "eslint --ext .ts src",
39
+ "prepublish": "pnpm build",
40
+ "release": "pnpm test && pnpm build && standard-version && git push --follow-tags && pnpm publish",
41
+ "test": "pnpm lint && vitest run --coverage",
42
+ "test:types": "tsc --noEmit"
43
43
  }
44
- }
44
+ }