hooks-plugin
Version:
A plugin system built through various hooks.
294 lines (225 loc) • 6.01 kB
Markdown
<div align='center'>
<h2>hooks-plugin</h2>
[](https://www.npmjs.com/package/hooks-plugin)
</div>
Plugin system built through various hooks, inspired by [tapable](https://github.com/webpack/tapable). it is very small, but it has fully-fledged and powerful TypeScript type hinting/type checking. If your project has a microkernel architecture, then this library will be very suitable for you.
### Debugging platform
> https://imtaotao.github.io/hooks-plugin/
## Hook list
- `SyncHook`
- `SyncWaterfallHook`
- `AsyncHook`
- `AsyncWaterfallHook`
- `AsyncParallelHook`
## Apis
- `plSys.use`
- `plSys.useRefine`
- `plSys.remove`
- `plSys.create`
- `plSys.isUsed`
- `plSys.beforeEach`
- `plSys.afterEach`
- `plSys.lock`
- `plSys.unlock`
- `plSys.listenLock`
- `plSys.listenError`
- `plSys.getPluginApis`
- `plSys.pickLifyCycle`
- `plSys.clone`
- `plSys.debug`
- `plSys.performance`
- `plSys.removeAllDebug`
- `plSys.removeAllPerformance`
## Usage
### Simple example
```ts
import { SyncHook, PluginSystem } from "hooks-plugin";
// Create a plugin and declare hooks
const plSys = new PluginSystem({
a: new SyncHook<[string, number]>(),
});
// Register plugin
plSys.use({
name: "testPlugin",
hooks: {
a(a, b) {
console.log(a, b); // 'str', 1
},
},
});
// Register via function
plSys.use(function(plSys) {
return {
name: "testPlugin",
...
}
})
// If you want to simplify registering plugins
const plugin = plSys.useRefine({
a(a, b) {
console.log(a, b); // 'str', 1
},
})
console.log(plugin.name); // Plugin name is a uuid created automatically
// Trigger hook
plSys.lifecycle.a.emit("str", 1);
```
### More complex example
```ts
import { AsyncHook, PluginSystem } from "hooks-plugin";
const plSys = new PluginSystem({
// 1. The first generic is the parameter type received by the hook
// 2. The second generic is the `this`` type of the hook function
a: new AsyncHook<[number, number], string>("context"),
// The parameter type of `AsyncWaterfallHook` and `SyncWaterfallHook` must be an object
b: new AsyncWaterfallHook<{ value: number }, string>("context"),
});
plSys.use({
name: "testPlugin",
version: "1.0.0", // Optional
hooks: {
async a(a, b) {
console.log(this); // 'context'
console.log(a, b); // 1, 2
},
async b(data) {
console.log(this); // 'context'
console.log(data); // { value: 1 }
return data; // Must return values of the same type
},
},
// The order is after `hooks` and will only be called once
onceHooks: {
async a(a, b) {
console.log(this); // 'context'
console.log(a, b); // 1, 2
},
}
});
plSys.lifecycle.a.emit(1, 2);
plSys.lifecycle.b.emit({ value: 1 });
```
### Interact with other plugins
```ts
import { SyncHook, PluginSystem } from "hooks-plugin";
// Declare your plugin `api` type
declare module "hooks-plugin" {
export interface PluginApis {
testApis: (typeof plugin)["apis"];
}
}
const plSys = new PluginSystem({});
const plugin = plSys.use({
name: "testApis",
apis: {
get(key: string) {},
set(key: string, value: unknown) {},
},
});
const apis = plSys.getPluginApis("testApis");
apis.get("a");
apis.set("a", 1);
```
### `beforeEach` and `afterEach`.
```ts
import { SyncHook, PluginSystem } from "hooks-plugin";
const plSys = new PluginSystem({
a: new SyncHook(),
});
plSys.use({
name: "test",
hooks: {
a(data) {},
},
});
// Registers a (sync) callback to be called before each hook is being called.
// `id`` is an arbitrary value, used to maintain consistency with `afterEach`
const unsubscribeBefore = plSys.beforeEach((e) => {
console.log("id:", e.id);
console.log("name:", e.name);
console.log("type:", e.type);
console.log("args:", e.args);
console.log("context:", e.context);
});
// Registers a (sync) callback to be called after each hook is being called.
const unsubscribeAfter = plSys.afterEach((e) => {
console.log("id:", e.id);
console.log("name:", e.name);
console.log("type:", e.type);
console.log("args:", e.args);
console.log("context:", e.context);
console.log("pluginExecTime:", e.pluginExecTime);
});
plSys.lifecycle.a.emit(1);
unsubscribeBefore();
unsubscribeAfter();
// Listening will no longer be triggered
plSys.lifecycle.a.emit(2);
```
### Debug
```ts
import { SyncHook, PluginSystem } from "hooks-plugin";
const plSys = new PluginSystem({
a: new AsyncParallelHook("ctx"),
});
plSys.use({
name: "test",
hooks: {
a() {
return new Promise((resolve) => {
setTimeout(resolve, 300);
});
},
},
});
const close = plSys.debug({ tag: "tag" });
plSys.lifecycle.a.emit(1, 2); // [tag]: a_1(t, args, ctx, pt): 309.36ms [1, 2] 'ctx' { test: 308.51ms }
// Close debug mode
close();
// You can also pass a `receiver` to handle log data yourself.
const close = plSys.debug({
tag: "tag",
receiver(data) {
console.log(data); // { e, tag, time }
},
// filter(data) { ... },
});
plSys.lifecycle.a.emit(1, 2);
```
### Performance
```ts
import { SyncHook, PluginSystem } from "hooks-plugin";
const plSys = new PluginSystem({
a: new SyncHook(),
b: new AsyncHook(),
});
// Take the `name` prop from the `0` parameter for matching
const p = plSys.performance("[0].name");
p.monitor("a", "a").on((e) => console.log(e.time)); // 200.400
p.monitor("a", "b").on((e) => console.log(e.time)); // 0.199
plSys.lifecycle.a.emit({ name: "n" });
setTimeout(() => {
plSys.lifecycle.a.emit({ name: "n" });
plSys.lifecycle.b.emit({ name: "n" });
}, 200);
```
## CDN
```html
<!DOCTYPE html>
<html lang='en'>
<body>
<script src='https://unpkg.com/hooks-plugin/dist/hooks.umd.js'></script>
<script>
const {
PluginSystem,
SyncHook,
AsyncHook,
SyncWaterfallHook,
AsyncParallelHook,
AsyncWaterfallHook,
} = window.HooksPlugin;
// ...
</script>
</body>
</html>
```