hookable 5.5.3 → 6.0.0-rc.1
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 +76 -57
- package/dist/index.d.mts +107 -0
- package/dist/index.mjs +243 -266
- package/package.json +30 -27
- package/dist/index.cjs +0 -299
- package/dist/index.d.ts +0 -103
package/README.md
CHANGED
|
@@ -10,16 +10,8 @@ Awaitable hooks system.
|
|
|
10
10
|
|
|
11
11
|
## Install
|
|
12
12
|
|
|
13
|
-
Using yarn:
|
|
14
|
-
|
|
15
|
-
```bash
|
|
16
|
-
yarn add hookable
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
Using npm:
|
|
20
|
-
|
|
21
13
|
```bash
|
|
22
|
-
|
|
14
|
+
npx nypm i hookable
|
|
23
15
|
```
|
|
24
16
|
|
|
25
17
|
## Usage
|
|
@@ -27,34 +19,39 @@ npm install hookable
|
|
|
27
19
|
**Method A: Create a hookable instance:**
|
|
28
20
|
|
|
29
21
|
```js
|
|
30
|
-
import {
|
|
22
|
+
import { Hookable } from "hookable";
|
|
31
23
|
|
|
32
24
|
// Create a hookable instance
|
|
33
|
-
const hooks =
|
|
25
|
+
const hooks = new Hookable();
|
|
34
26
|
|
|
35
27
|
// Hook on 'hello'
|
|
36
|
-
hooks.hook(
|
|
28
|
+
hooks.hook("hello", () => {
|
|
29
|
+
console.log("Hello World");
|
|
30
|
+
});
|
|
37
31
|
|
|
38
32
|
// Call 'hello' hook
|
|
39
|
-
hooks.callHook(
|
|
33
|
+
hooks.callHook("hello");
|
|
40
34
|
```
|
|
41
35
|
|
|
36
|
+
> [!TIP]
|
|
37
|
+
> You can use `HookableCore` alternatively for less bundle and runtime footprint if simple `hook`/`callHook` functionality is only needed.
|
|
38
|
+
|
|
42
39
|
**Method B: Extend your base class from Hookable:**
|
|
43
40
|
|
|
44
41
|
```js
|
|
45
|
-
import { Hookable } from
|
|
42
|
+
import { Hookable } from "hookable";
|
|
46
43
|
|
|
47
44
|
export default class FooLib extends Hookable {
|
|
48
45
|
constructor() {
|
|
49
46
|
// Call to parent to initialize
|
|
50
|
-
super()
|
|
47
|
+
super();
|
|
51
48
|
// Initialize Hookable with custom logger
|
|
52
49
|
// super(consola)
|
|
53
50
|
}
|
|
54
51
|
|
|
55
52
|
async someFunction() {
|
|
56
53
|
// Call and wait for `hook1` hooks (if any) sequential
|
|
57
|
-
await this.callHook(
|
|
54
|
+
await this.callHook("hook1");
|
|
58
55
|
}
|
|
59
56
|
}
|
|
60
57
|
```
|
|
@@ -62,56 +59,67 @@ export default class FooLib extends Hookable {
|
|
|
62
59
|
**Inside plugins, register for any hook:**
|
|
63
60
|
|
|
64
61
|
```js
|
|
65
|
-
const lib = new FooLib()
|
|
62
|
+
const lib = new FooLib();
|
|
66
63
|
|
|
67
64
|
// Register a handler for `hook2`
|
|
68
|
-
lib.hook(
|
|
65
|
+
lib.hook("hook2", async () => {
|
|
66
|
+
/* ... */
|
|
67
|
+
});
|
|
69
68
|
|
|
70
69
|
// Register multiply handlers at once
|
|
71
70
|
lib.addHooks({
|
|
72
|
-
hook1: async () => {
|
|
73
|
-
|
|
74
|
-
}
|
|
71
|
+
hook1: async () => {
|
|
72
|
+
/* ... */
|
|
73
|
+
},
|
|
74
|
+
hook2: [
|
|
75
|
+
/* can be also an array */
|
|
76
|
+
],
|
|
77
|
+
});
|
|
75
78
|
```
|
|
76
79
|
|
|
77
80
|
**Unregistering hooks:**
|
|
78
81
|
|
|
79
82
|
```js
|
|
80
|
-
const lib = new FooLib()
|
|
83
|
+
const lib = new FooLib();
|
|
81
84
|
|
|
82
|
-
const hook0 = async () => {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
+
const hook0 = async () => {
|
|
86
|
+
/* ... */
|
|
87
|
+
};
|
|
88
|
+
const hook1 = async () => {
|
|
89
|
+
/* ... */
|
|
90
|
+
};
|
|
91
|
+
const hook2 = async () => {
|
|
92
|
+
/* ... */
|
|
93
|
+
};
|
|
85
94
|
|
|
86
95
|
// The hook() method returns an "unregister" function
|
|
87
|
-
const unregisterHook0 = lib.hook(
|
|
88
|
-
const unregisterHooks1and2 = lib.addHooks({ hook1, hook2 })
|
|
96
|
+
const unregisterHook0 = lib.hook("hook0", hook0);
|
|
97
|
+
const unregisterHooks1and2 = lib.addHooks({ hook1, hook2 });
|
|
89
98
|
|
|
90
99
|
/* ... */
|
|
91
100
|
|
|
92
|
-
unregisterHook0()
|
|
93
|
-
unregisterHooks1and2()
|
|
101
|
+
unregisterHook0();
|
|
102
|
+
unregisterHooks1and2();
|
|
94
103
|
|
|
95
104
|
// or
|
|
96
105
|
|
|
97
|
-
lib.removeHooks({ hook0, hook1 })
|
|
98
|
-
lib.removeHook(
|
|
106
|
+
lib.removeHooks({ hook0, hook1 });
|
|
107
|
+
lib.removeHook("hook2", hook2);
|
|
99
108
|
```
|
|
100
109
|
|
|
101
110
|
**Triggering a hook handler once:**
|
|
102
111
|
|
|
103
112
|
```js
|
|
104
|
-
const lib = new FooLib()
|
|
113
|
+
const lib = new FooLib();
|
|
105
114
|
|
|
106
|
-
const unregister = lib.hook(
|
|
115
|
+
const unregister = lib.hook("hook0", async () => {
|
|
107
116
|
// Unregister as soon as the hook is executed
|
|
108
|
-
unregister()
|
|
117
|
+
unregister();
|
|
109
118
|
|
|
110
119
|
/* ... */
|
|
111
|
-
})
|
|
120
|
+
});
|
|
112
121
|
```
|
|
113
122
|
|
|
114
|
-
|
|
115
123
|
## Hookable class
|
|
116
124
|
|
|
117
125
|
### `constructor()`
|
|
@@ -138,10 +146,9 @@ Example:
|
|
|
138
146
|
hookable.addHooks({
|
|
139
147
|
test: {
|
|
140
148
|
before: () => {},
|
|
141
|
-
after: () => {}
|
|
142
|
-
}
|
|
143
|
-
})
|
|
144
|
-
|
|
149
|
+
after: () => {},
|
|
150
|
+
},
|
|
151
|
+
});
|
|
145
152
|
```
|
|
146
153
|
|
|
147
154
|
This registers `test:before` and `test:after` hooks at bulk.
|
|
@@ -157,6 +164,7 @@ Used by class itself to **sequentially** call handlers of a specific hook.
|
|
|
157
164
|
If you need custom control over how hooks are called, you can provide a custom function that will receive an array of handlers of a specific hook.
|
|
158
165
|
|
|
159
166
|
`callerFn` if a callback function that accepts two arguments, `hooks` and `args`:
|
|
167
|
+
|
|
160
168
|
- `hooks`: Array of user hooks to be called
|
|
161
169
|
- `args`: Array of arguments that should be passed each time calling a hook
|
|
162
170
|
|
|
@@ -179,19 +187,21 @@ Remove multiple hook handlers.
|
|
|
179
187
|
Example:
|
|
180
188
|
|
|
181
189
|
```js
|
|
182
|
-
const handler = async () => {
|
|
190
|
+
const handler = async () => {
|
|
191
|
+
/* ... */
|
|
192
|
+
};
|
|
183
193
|
|
|
184
|
-
hookable.hook(
|
|
185
|
-
hookable.addHooks({ test: { after: handler } })
|
|
194
|
+
hookable.hook("test:before", handler);
|
|
195
|
+
hookable.addHooks({ test: { after: handler } });
|
|
186
196
|
|
|
187
197
|
// ...
|
|
188
198
|
|
|
189
199
|
hookable.removeHooks({
|
|
190
200
|
test: {
|
|
191
201
|
before: handler,
|
|
192
|
-
after: handler
|
|
193
|
-
}
|
|
194
|
-
})
|
|
202
|
+
after: handler,
|
|
203
|
+
},
|
|
204
|
+
});
|
|
195
205
|
```
|
|
196
206
|
|
|
197
207
|
### `removeAllHooks`
|
|
@@ -203,12 +213,16 @@ Remove all hook handlers.
|
|
|
203
213
|
Registers a (sync) callback to be called before each hook is being called.
|
|
204
214
|
|
|
205
215
|
```js
|
|
206
|
-
hookable.beforeEach((event) => {
|
|
207
|
-
|
|
216
|
+
hookable.beforeEach((event) => {
|
|
217
|
+
console.log(`${event.name} hook is being called with ${event.args}`);
|
|
218
|
+
});
|
|
219
|
+
hookable.hook("test", () => {
|
|
220
|
+
console.log("running test hook");
|
|
221
|
+
});
|
|
208
222
|
|
|
209
223
|
// test hook is being called with []
|
|
210
224
|
// running test hook
|
|
211
|
-
await hookable.callHook(
|
|
225
|
+
await hookable.callHook("test");
|
|
212
226
|
```
|
|
213
227
|
|
|
214
228
|
### `afterEach (syncCallback)`
|
|
@@ -216,12 +230,16 @@ await hookable.callHook('test')
|
|
|
216
230
|
Registers a (sync) callback to be called after each hook is being called.
|
|
217
231
|
|
|
218
232
|
```js
|
|
219
|
-
hookable.afterEach((event) => {
|
|
220
|
-
|
|
233
|
+
hookable.afterEach((event) => {
|
|
234
|
+
console.log(`${event.name} hook called with ${event.args}`);
|
|
235
|
+
});
|
|
236
|
+
hookable.hook("test", () => {
|
|
237
|
+
console.log("running test hook");
|
|
238
|
+
});
|
|
221
239
|
|
|
222
240
|
// running test hook
|
|
223
241
|
// test hook called with []
|
|
224
|
-
await hookable.callHook(
|
|
242
|
+
await hookable.callHook("test");
|
|
225
243
|
```
|
|
226
244
|
|
|
227
245
|
### `createDebugger`
|
|
@@ -229,12 +247,12 @@ await hookable.callHook('test')
|
|
|
229
247
|
Automatically logs each hook that is called and how long it takes to run.
|
|
230
248
|
|
|
231
249
|
```js
|
|
232
|
-
const debug = hookable.createDebugger(hooks, { tag:
|
|
250
|
+
const debug = hookable.createDebugger(hooks, { tag: "something" });
|
|
233
251
|
|
|
234
|
-
hooks.callHook(
|
|
252
|
+
hooks.callHook("some-hook", "some-arg");
|
|
235
253
|
// [something] some-hook: 0.21ms
|
|
236
254
|
|
|
237
|
-
debug.close()
|
|
255
|
+
debug.close();
|
|
238
256
|
```
|
|
239
257
|
|
|
240
258
|
## Migration
|
|
@@ -244,9 +262,9 @@ debug.close()
|
|
|
244
262
|
- Type checking improved. You can use `Hookable<T>` or `createHooks<T>()` to provide types interface **([c2e1e22](https://github.com/unjs/hookable/commit/c2e1e223d16e7bf87117cd8d72ad3ba211a333d8))**
|
|
245
263
|
- We no longer provide an IE11 compatible umd build. Instead, you should use an ESM-aware bundler such as webpack or rollup to transpile if needed.
|
|
246
264
|
- Logger param is dropped. We use `console.warn` by default for deprecated hooks.
|
|
247
|
-
- Package now uses named exports. You should import `{ Hookable }` instead of
|
|
265
|
+
- Package now uses named exports. You should import `{ Hookable }` instead of `Hookable` or use new `createHooks` util
|
|
248
266
|
- `mergeHooks` util is exported standalone. You should replace `Hookable.mergeHooks` and `this.mergeHooks` with new `{ mergeHooks }` export
|
|
249
|
-
- In versions < 5.0.0 when using `callHook` if an error happened by one of the hook callbacks, we was handling errors globally and call global `error` hook + `console.error` instead and resolve `callHook` promise!
|
|
267
|
+
- In versions < 5.0.0 when using `callHook` if an error happened by one of the hook callbacks, we was handling errors globally and call global `error` hook + `console.error` instead and resolve `callHook` promise! This sometimes makes confusing behavior when we think code worked but it didn't. v5 introduced a breaking change that when a hook throws an error, `callHook` also rejects instead of a global `error` event. This means you should be careful to handle all errors when using `callHook` now.
|
|
250
268
|
|
|
251
269
|
## Credits
|
|
252
270
|
|
|
@@ -259,6 +277,7 @@ Thanks to [Joe Paice](https://github.com/RGBboy) for donating [hookable](https:/
|
|
|
259
277
|
MIT - Made with 💖
|
|
260
278
|
|
|
261
279
|
<!-- Badges -->
|
|
280
|
+
|
|
262
281
|
[npm-version-src]: https://img.shields.io/npm/v/hookable?style=flat&colorA=18181B&colorB=F0DB4F
|
|
263
282
|
[npm-version-href]: https://npmjs.com/package/hookable
|
|
264
283
|
[npm-downloads-src]: https://img.shields.io/npm/dm/hookable?style=flat&colorA=18181B&colorB=F0DB4F
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
//#region src/types.d.ts
|
|
2
|
+
type HookCallback = (...arguments_: any) => Promise<void> | void;
|
|
3
|
+
interface Hooks {
|
|
4
|
+
[key: string]: HookCallback;
|
|
5
|
+
}
|
|
6
|
+
type HookKeys<T> = keyof T & string;
|
|
7
|
+
type DeprecatedHook<T> = {
|
|
8
|
+
message?: string;
|
|
9
|
+
to: HookKeys<T>;
|
|
10
|
+
};
|
|
11
|
+
type DeprecatedHooks<T> = { [name in HookKeys<T>]: DeprecatedHook<T> };
|
|
12
|
+
type Thenable<T> = Promise<T> | T;
|
|
13
|
+
type ValueOf<C> = C extends Record<any, any> ? C[keyof C] : never;
|
|
14
|
+
type Strings<T> = Exclude<keyof T, number | symbol>;
|
|
15
|
+
type KnownKeys<T> = keyof { [K in keyof T as string extends K ? never : number extends K ? never : K]: never };
|
|
16
|
+
type StripGeneric<T> = Pick<T, KnownKeys<T> extends keyof T ? KnownKeys<T> : never>;
|
|
17
|
+
type OnlyGeneric<T> = Omit<T, KnownKeys<T> extends keyof T ? KnownKeys<T> : never>;
|
|
18
|
+
type Namespaces<T> = ValueOf<{ [key in Strings<T>]: key extends `${infer Namespace}:${string}` ? Namespace : never }>;
|
|
19
|
+
type BareHooks<T> = ValueOf<{ [key in Strings<T>]: key extends `${string}:${string}` ? never : key }>;
|
|
20
|
+
type HooksInNamespace<T, Namespace$1 extends string> = ValueOf<{ [key in Strings<T>]: key extends `${Namespace$1}:${infer HookName}` ? HookName : never }>;
|
|
21
|
+
type WithoutNamespace<T, Namespace$1 extends string> = { [key in HooksInNamespace<T, Namespace$1>]: `${Namespace$1}:${key}` extends keyof T ? T[`${Namespace$1}:${key}`] : never };
|
|
22
|
+
type NestedHooks<T> = (Partial<StripGeneric<T>> | Partial<OnlyGeneric<T>>) & Partial<{ [key in Namespaces<StripGeneric<T>>]: NestedHooks<WithoutNamespace<T, key>> }> & Partial<{ [key in BareHooks<StripGeneric<T>>]: T[key] }>;
|
|
23
|
+
//#endregion
|
|
24
|
+
//#region src/hookable.d.ts
|
|
25
|
+
type InferCallback<HT, HN extends keyof HT> = HT[HN] extends HookCallback ? HT[HN] : never;
|
|
26
|
+
type InferSpyEvent<HT extends Record<string, any>> = { [key in keyof HT]: {
|
|
27
|
+
name: key;
|
|
28
|
+
args: Parameters<HT[key]>;
|
|
29
|
+
context: Record<string, any>;
|
|
30
|
+
} }[keyof HT];
|
|
31
|
+
declare class Hookable<HooksT extends Record<string, any> = Record<string, HookCallback>, HookNameT extends HookKeys<HooksT> = HookKeys<HooksT>> {
|
|
32
|
+
private _hooks;
|
|
33
|
+
private _before?;
|
|
34
|
+
private _after?;
|
|
35
|
+
private _deprecatedHooks;
|
|
36
|
+
private _deprecatedMessages?;
|
|
37
|
+
constructor();
|
|
38
|
+
hook<NameT extends HookNameT>(name: NameT, function_: InferCallback<HooksT, NameT>, options?: {
|
|
39
|
+
allowDeprecated?: boolean;
|
|
40
|
+
}): () => void;
|
|
41
|
+
hookOnce<NameT extends HookNameT>(name: NameT, function_: InferCallback<HooksT, NameT>): () => void;
|
|
42
|
+
removeHook<NameT extends HookNameT>(name: NameT, function_: InferCallback<HooksT, NameT>): void;
|
|
43
|
+
deprecateHook<NameT extends HookNameT>(name: NameT, deprecated: HookKeys<HooksT> | DeprecatedHook<HooksT>): void;
|
|
44
|
+
deprecateHooks(deprecatedHooks: Partial<Record<HookNameT, DeprecatedHook<HooksT>>>): void;
|
|
45
|
+
addHooks(configHooks: NestedHooks<HooksT>): () => void;
|
|
46
|
+
removeHooks(configHooks: NestedHooks<HooksT>): void;
|
|
47
|
+
removeAllHooks(): void;
|
|
48
|
+
callHook<NameT extends HookNameT>(name: NameT, ...args: Parameters<InferCallback<HooksT, NameT>>): Thenable<any> | void;
|
|
49
|
+
callHookParallel<NameT extends HookNameT>(name: NameT, ...args: Parameters<InferCallback<HooksT, NameT>>): Thenable<any[]> | void;
|
|
50
|
+
callHookWith<NameT extends HookNameT, CallFunction extends (hooks: HookCallback[], args: Parameters<InferCallback<HooksT, NameT>>) => any>(caller: CallFunction, name: NameT, args: Parameters<InferCallback<HooksT, NameT>>): ReturnType<CallFunction>;
|
|
51
|
+
beforeEach(function_: (event: InferSpyEvent<HooksT>) => void): () => void;
|
|
52
|
+
afterEach(function_: (event: InferSpyEvent<HooksT>) => void): () => void;
|
|
53
|
+
}
|
|
54
|
+
declare function createHooks<T extends Record<string, any>>(): Hookable<T>;
|
|
55
|
+
declare class HookableCore<HooksT extends Record<string, any> = Record<string, HookCallback>, HookNameT extends HookKeys<HooksT> = HookKeys<HooksT>> {
|
|
56
|
+
protected _hooks: {
|
|
57
|
+
[key: string]: HookCallback[] | undefined;
|
|
58
|
+
};
|
|
59
|
+
constructor();
|
|
60
|
+
hook<NameT extends HookNameT>(name: NameT, fn: InferCallback<HooksT, NameT>): () => void;
|
|
61
|
+
removeHook<NameT extends HookNameT>(name: NameT, function_: InferCallback<HooksT, NameT>): void;
|
|
62
|
+
callHook<NameT extends HookNameT>(name: NameT, ...args: Parameters<InferCallback<HooksT, NameT>>): Thenable<any> | void;
|
|
63
|
+
}
|
|
64
|
+
//#endregion
|
|
65
|
+
//#region src/utils.d.ts
|
|
66
|
+
declare function flatHooks<T>(configHooks: NestedHooks<T>, hooks?: T, parentName?: string): T;
|
|
67
|
+
declare function mergeHooks<T>(...hooks: NestedHooks<T>[]): T;
|
|
68
|
+
declare function serial<T>(tasks: T[], function_: (task: T) => Promise<any> | any): Promise<any>;
|
|
69
|
+
type CreateTask = (name?: string) => {
|
|
70
|
+
run: (function_: () => Promise<any> | any) => Promise<any> | any;
|
|
71
|
+
};
|
|
72
|
+
declare global {
|
|
73
|
+
interface Console {
|
|
74
|
+
createTask?: CreateTask;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/** @deprecated */
|
|
78
|
+
declare function serialCaller(hooks: HookCallback[], arguments_?: any[]): Promise<any>;
|
|
79
|
+
/** @deprecated */
|
|
80
|
+
declare function parallelCaller(hooks: HookCallback[], args?: any[]): Promise<any>;
|
|
81
|
+
//#endregion
|
|
82
|
+
//#region src/debugger.d.ts
|
|
83
|
+
interface CreateDebuggerOptions {
|
|
84
|
+
/** An optional tag to prefix console logs with */
|
|
85
|
+
tag?: string;
|
|
86
|
+
/**
|
|
87
|
+
* Show hook params to the console output
|
|
88
|
+
*
|
|
89
|
+
* Enabled for browsers by default
|
|
90
|
+
*/
|
|
91
|
+
inspect?: boolean;
|
|
92
|
+
/**
|
|
93
|
+
* Use group/groupEnd wrapper around logs happening during a specific hook
|
|
94
|
+
*
|
|
95
|
+
* Enabled for browsers by default
|
|
96
|
+
*/
|
|
97
|
+
group?: boolean;
|
|
98
|
+
/** Filter which hooks to enable debugger for. Can be a string prefix or fn. */
|
|
99
|
+
filter?: string | ((event: string) => boolean);
|
|
100
|
+
}
|
|
101
|
+
/** Start debugging hook names and timing in console */
|
|
102
|
+
declare function createDebugger(hooks: Hookable<any>, _options?: CreateDebuggerOptions): {
|
|
103
|
+
/** Stop debugging and remove listeners */
|
|
104
|
+
close: () => void;
|
|
105
|
+
};
|
|
106
|
+
//#endregion
|
|
107
|
+
export { CreateDebuggerOptions, DeprecatedHook, DeprecatedHooks, HookCallback, HookKeys, Hookable, HookableCore, Hooks, NestedHooks, Thenable, createDebugger, createHooks, flatHooks, mergeHooks, parallelCaller, serial, serialCaller };
|