@shuvi/hook 0.0.1-pre.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 +1 -0
- package/esm/AsyncParallelHook.d.ts +3 -0
- package/esm/AsyncParallelHook.js +13 -0
- package/esm/AsyncSeriesBailHook.d.ts +3 -0
- package/esm/AsyncSeriesBailHook.js +22 -0
- package/esm/AsyncSeriesHook.d.ts +3 -0
- package/esm/AsyncSeriesHook.js +16 -0
- package/esm/AsyncSeriesWaterfallHook.d.ts +3 -0
- package/esm/AsyncSeriesWaterfallHook.js +24 -0
- package/esm/Hookable.d.ts +9 -0
- package/esm/Hookable.js +87 -0
- package/esm/index.d.ts +3 -0
- package/esm/index.js +2 -0
- package/esm/types.d.ts +44 -0
- package/esm/types.js +0 -0
- package/esm/utils.d.ts +6 -0
- package/esm/utils.js +45 -0
- package/lib/AsyncParallelHook.d.ts +3 -0
- package/lib/AsyncParallelHook.js +15 -0
- package/lib/AsyncSeriesBailHook.d.ts +3 -0
- package/lib/AsyncSeriesBailHook.js +24 -0
- package/lib/AsyncSeriesHook.d.ts +3 -0
- package/lib/AsyncSeriesHook.js +18 -0
- package/lib/AsyncSeriesWaterfallHook.d.ts +3 -0
- package/lib/AsyncSeriesWaterfallHook.js +26 -0
- package/lib/Hookable.d.ts +9 -0
- package/lib/Hookable.js +90 -0
- package/lib/index.d.ts +3 -0
- package/lib/index.js +4 -0
- package/lib/types.d.ts +44 -0
- package/lib/types.js +2 -0
- package/lib/utils.d.ts +6 -0
- package/lib/utils.js +47 -0
- package/package.json +26 -0
package/README.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# @shuvi/hook
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
export const executeAsyncParallelHook = (tapFns, ...args) => __awaiter(void 0, void 0, void 0, function* () {
|
|
11
|
+
const results = yield Promise.all(tapFns.map(fn => fn(...args)));
|
|
12
|
+
return results;
|
|
13
|
+
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
export const executeAsyncSeriesBailHook = (tapFns, ...args) => __awaiter(void 0, void 0, void 0, function* () {
|
|
11
|
+
let result = [];
|
|
12
|
+
for (let i = 0; i < tapFns.length; i++) {
|
|
13
|
+
result = tapFns[i](...args);
|
|
14
|
+
if (Promise.resolve(result) === result) {
|
|
15
|
+
result = yield result;
|
|
16
|
+
}
|
|
17
|
+
if (result) {
|
|
18
|
+
break;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return result;
|
|
22
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
export const executeAsyncSeriesHook = (tapFns, ...args) => __awaiter(void 0, void 0, void 0, function* () {
|
|
11
|
+
let results = [];
|
|
12
|
+
for (let i = 0; i < tapFns.length; i++) {
|
|
13
|
+
results.push(yield tapFns[i](...args));
|
|
14
|
+
}
|
|
15
|
+
return results;
|
|
16
|
+
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
export const executeAsyncSeriesWaterfallHook = (tapFns, ...args) => __awaiter(void 0, void 0, void 0, function* () {
|
|
11
|
+
for (let i = 0; i < tapFns.length; i++) {
|
|
12
|
+
let fn = tapFns[i];
|
|
13
|
+
let promiseResult = yield fn(...args);
|
|
14
|
+
if (typeof args[0] !== 'undefined') {
|
|
15
|
+
if (typeof promiseResult !== 'undefined') {
|
|
16
|
+
args[0] = promiseResult;
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
console.warn(`Expected return value from hook "${fn.hookName}" but is undefined`);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return args[0];
|
|
24
|
+
});
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { IHookOpts, ICallHookOpts, IHookable, IHookConfig } from './types';
|
|
2
|
+
export declare class Hookable implements IHookable {
|
|
3
|
+
private _hooks;
|
|
4
|
+
tap<Config extends IHookConfig = IHookConfig>(name: Config['name'], hook: IHookOpts<Config['initialValue'], Config['args']>): () => void;
|
|
5
|
+
callHook<Config extends IHookConfig = IHookConfig>(name: Config['name'], ...args: Config['args']): Promise<unknown[]>;
|
|
6
|
+
callHook<Config extends IHookConfig = IHookConfig>(options: ICallHookOpts<Config['name'], Config['initialValue']>, ...args: Config['args']): Promise<Config['initialValue']>;
|
|
7
|
+
on<Config extends IHookConfig = IHookConfig>(event: Config['name'], listener: (...args: Config['args']) => void): () => void;
|
|
8
|
+
emitEvent<Config extends IHookConfig = IHookConfig>(name: Config['name'], ...args: Config['args']): void;
|
|
9
|
+
}
|
package/esm/Hookable.js
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import { executeAsyncParallelHook } from './AsyncParallelHook';
|
|
11
|
+
import { executeAsyncSeriesHook } from './AsyncSeriesHook';
|
|
12
|
+
import { executeAsyncSeriesBailHook } from './AsyncSeriesBailHook';
|
|
13
|
+
import { executeAsyncSeriesWaterfallHook } from './AsyncSeriesWaterfallHook';
|
|
14
|
+
import { insertHook, getHooksFunctions, removeHook } from './utils';
|
|
15
|
+
function callSerailWithInitialValue(hooks, args, initialValue) {
|
|
16
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
17
|
+
const fns = getHooksFunctions(hooks);
|
|
18
|
+
return executeAsyncSeriesWaterfallHook(fns, initialValue, ...args);
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
function callSerail(hooks, args, bail) {
|
|
22
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
23
|
+
const thookFn = bail ? executeAsyncSeriesBailHook : executeAsyncSeriesHook;
|
|
24
|
+
const fns = getHooksFunctions(hooks);
|
|
25
|
+
return thookFn(fns, ...args);
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
function callParallel(hooks, args) {
|
|
29
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
30
|
+
const fns = getHooksFunctions(hooks);
|
|
31
|
+
return (yield executeAsyncParallelHook(fns, ...args));
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
export class Hookable {
|
|
35
|
+
constructor() {
|
|
36
|
+
this._hooks = new Map();
|
|
37
|
+
}
|
|
38
|
+
tap(name, hook) {
|
|
39
|
+
let hooks = this._hooks.get(name);
|
|
40
|
+
if (!hooks) {
|
|
41
|
+
hooks = [];
|
|
42
|
+
this._hooks.set(name, hooks);
|
|
43
|
+
}
|
|
44
|
+
insertHook(hooks, hook);
|
|
45
|
+
return () => {
|
|
46
|
+
removeHook(hooks, hook);
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
// implement
|
|
50
|
+
callHook(options, ...args) {
|
|
51
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
52
|
+
const defaultOpts = {
|
|
53
|
+
bail: false,
|
|
54
|
+
parallel: false,
|
|
55
|
+
initialValue: undefined
|
|
56
|
+
};
|
|
57
|
+
let opts;
|
|
58
|
+
if (typeof options === 'object') {
|
|
59
|
+
opts = Object.assign(Object.assign({}, defaultOpts), options);
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
opts = Object.assign(Object.assign({}, defaultOpts), { name: options });
|
|
63
|
+
}
|
|
64
|
+
const hasInitialValue = typeof opts.initialValue !== 'undefined';
|
|
65
|
+
const hooks = this._hooks.get(opts.name);
|
|
66
|
+
if (!hooks || hooks.length <= 0) {
|
|
67
|
+
// @ts-ignore no return value
|
|
68
|
+
return hasInitialValue ? opts.initialValue : [];
|
|
69
|
+
}
|
|
70
|
+
if (opts.parallel) {
|
|
71
|
+
return yield callParallel(hooks, args);
|
|
72
|
+
}
|
|
73
|
+
else if (hasInitialValue) {
|
|
74
|
+
return yield callSerailWithInitialValue(hooks, args, opts.initialValue);
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
return yield callSerail(hooks, args, opts.bail);
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
on(event, listener) {
|
|
82
|
+
return this.tap(event, { name: 'listener', fn: listener });
|
|
83
|
+
}
|
|
84
|
+
emitEvent(name, ...args) {
|
|
85
|
+
this.callHook({ name, parallel: true }, ...args);
|
|
86
|
+
}
|
|
87
|
+
}
|
package/esm/index.d.ts
ADDED
package/esm/index.js
ADDED
package/esm/types.d.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
export declare type NoInitValue = '$$no-initial-value';
|
|
2
|
+
export interface ICallHookOpts<Name extends string = string, InitV = unknown> {
|
|
3
|
+
name: Name;
|
|
4
|
+
bail?: boolean;
|
|
5
|
+
parallel?: boolean;
|
|
6
|
+
initialValue?: InitV;
|
|
7
|
+
}
|
|
8
|
+
export interface IHookOpts<InitValue = NoInitValue, Args extends any[] = any[]> {
|
|
9
|
+
name: string;
|
|
10
|
+
fn: (InitValue extends NoInitValue ? (...args: Args) => void | Promise<void> : (init: InitValue, ...args: Args) => InitValue | Promise<InitValue>) & {
|
|
11
|
+
hookName?: string;
|
|
12
|
+
};
|
|
13
|
+
before?: string;
|
|
14
|
+
stage?: number;
|
|
15
|
+
}
|
|
16
|
+
export interface IHookConfig {
|
|
17
|
+
name: string;
|
|
18
|
+
args: any[];
|
|
19
|
+
initialValue: any;
|
|
20
|
+
}
|
|
21
|
+
export interface IHookable {
|
|
22
|
+
tap<Config extends IHookConfig>(hook: Config['name'], opts: IHookOpts<Config['initialValue'], Config['args']>): void;
|
|
23
|
+
callHook<Config extends IHookConfig>(name: Config['name'], ...args: Config['args']): Promise<unknown[]>;
|
|
24
|
+
callHook<Config extends IHookConfig>(options: ICallHookOpts<Config['name'], Config['initialValue']>, ...args: Config['args']): Promise<Config['initialValue']>;
|
|
25
|
+
on<Config extends IHookConfig>(event: Config['name'], listener: (...args: Config['args']) => void): void;
|
|
26
|
+
emitEvent<Config extends IHookConfig>(name: Config['name'], ...args: Config['args']): void;
|
|
27
|
+
}
|
|
28
|
+
declare type IDefaultHookConfig = {
|
|
29
|
+
args: [];
|
|
30
|
+
initialValue: NoInitValue;
|
|
31
|
+
};
|
|
32
|
+
export declare type defineHook<Name extends string, Config extends Partial<IHookConfig> = {}> = {
|
|
33
|
+
name: Name;
|
|
34
|
+
} & {
|
|
35
|
+
[K in keyof Config]: Config[K];
|
|
36
|
+
} & {
|
|
37
|
+
[K in Exclude<keyof IDefaultHookConfig, keyof Config>]: IDefaultHookConfig[K];
|
|
38
|
+
};
|
|
39
|
+
export declare type defineEvent<Name extends string, Args = []> = {
|
|
40
|
+
name: Name;
|
|
41
|
+
args: Args;
|
|
42
|
+
initialValue: NoInitValue;
|
|
43
|
+
};
|
|
44
|
+
export {};
|
package/esm/types.js
ADDED
|
File without changes
|
package/esm/utils.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { IHookOpts } from './types';
|
|
2
|
+
export declare const getHooksFunctions: (hooks: IHookOpts<"$$no-initial-value", any[]>[]) => (((...args: any[]) => void | Promise<void>) & {
|
|
3
|
+
hookName?: string | undefined;
|
|
4
|
+
})[];
|
|
5
|
+
export declare const insertHook: (hooks: IHookOpts<"$$no-initial-value", any[]>[], hook: IHookOpts<"$$no-initial-value", any[]>) => IHookOpts<"$$no-initial-value", any[]>[];
|
|
6
|
+
export declare const removeHook: (hooks: IHookOpts<"$$no-initial-value", any[]>[], hookToRemove: IHookOpts<"$$no-initial-value", any[]>) => void;
|
package/esm/utils.js
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
export const getHooksFunctions = (hooks) => {
|
|
2
|
+
return hooks.map(({ fn, name }) => {
|
|
3
|
+
fn.hookName = name;
|
|
4
|
+
return fn;
|
|
5
|
+
});
|
|
6
|
+
};
|
|
7
|
+
// mutable sort
|
|
8
|
+
export const insertHook = (hooks, hook) => {
|
|
9
|
+
let before;
|
|
10
|
+
if (typeof hook.before === 'string') {
|
|
11
|
+
before = new Set([hook.before]);
|
|
12
|
+
}
|
|
13
|
+
let stage = 0;
|
|
14
|
+
if (typeof hook.stage === 'number') {
|
|
15
|
+
stage = hook.stage;
|
|
16
|
+
}
|
|
17
|
+
const originalHooksLength = hooks.length;
|
|
18
|
+
if (hooks.length > 1) {
|
|
19
|
+
for (let i = 1; i < originalHooksLength; i++) {
|
|
20
|
+
const tap = hooks[i];
|
|
21
|
+
const tapStage = tap.stage || 0;
|
|
22
|
+
if (before) {
|
|
23
|
+
if (before.has(tap.name)) {
|
|
24
|
+
hooks.splice(i, 0, hook);
|
|
25
|
+
break;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
if (tapStage > stage) {
|
|
29
|
+
hooks.splice(i, 0, hook);
|
|
30
|
+
break;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
if (hooks.length === originalHooksLength) {
|
|
35
|
+
hooks.push(hook);
|
|
36
|
+
}
|
|
37
|
+
return hooks;
|
|
38
|
+
};
|
|
39
|
+
// mutable way
|
|
40
|
+
export const removeHook = (hooks, hookToRemove) => {
|
|
41
|
+
const indexToRemove = hooks.findIndex(hook => hook === hookToRemove);
|
|
42
|
+
if (indexToRemove >= 0) {
|
|
43
|
+
hooks.splice(indexToRemove, 1);
|
|
44
|
+
}
|
|
45
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.executeAsyncParallelHook = (tapFns, ...args) => __awaiter(void 0, void 0, void 0, function* () {
|
|
13
|
+
const results = yield Promise.all(tapFns.map(fn => fn(...args)));
|
|
14
|
+
return results;
|
|
15
|
+
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.executeAsyncSeriesBailHook = (tapFns, ...args) => __awaiter(void 0, void 0, void 0, function* () {
|
|
13
|
+
let result = [];
|
|
14
|
+
for (let i = 0; i < tapFns.length; i++) {
|
|
15
|
+
result = tapFns[i](...args);
|
|
16
|
+
if (Promise.resolve(result) === result) {
|
|
17
|
+
result = yield result;
|
|
18
|
+
}
|
|
19
|
+
if (result) {
|
|
20
|
+
break;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return result;
|
|
24
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.executeAsyncSeriesHook = (tapFns, ...args) => __awaiter(void 0, void 0, void 0, function* () {
|
|
13
|
+
let results = [];
|
|
14
|
+
for (let i = 0; i < tapFns.length; i++) {
|
|
15
|
+
results.push(yield tapFns[i](...args));
|
|
16
|
+
}
|
|
17
|
+
return results;
|
|
18
|
+
});
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.executeAsyncSeriesWaterfallHook = (tapFns, ...args) => __awaiter(void 0, void 0, void 0, function* () {
|
|
13
|
+
for (let i = 0; i < tapFns.length; i++) {
|
|
14
|
+
let fn = tapFns[i];
|
|
15
|
+
let promiseResult = yield fn(...args);
|
|
16
|
+
if (typeof args[0] !== 'undefined') {
|
|
17
|
+
if (typeof promiseResult !== 'undefined') {
|
|
18
|
+
args[0] = promiseResult;
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
console.warn(`Expected return value from hook "${fn.hookName}" but is undefined`);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return args[0];
|
|
26
|
+
});
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { IHookOpts, ICallHookOpts, IHookable, IHookConfig } from './types';
|
|
2
|
+
export declare class Hookable implements IHookable {
|
|
3
|
+
private _hooks;
|
|
4
|
+
tap<Config extends IHookConfig = IHookConfig>(name: Config['name'], hook: IHookOpts<Config['initialValue'], Config['args']>): () => void;
|
|
5
|
+
callHook<Config extends IHookConfig = IHookConfig>(name: Config['name'], ...args: Config['args']): Promise<unknown[]>;
|
|
6
|
+
callHook<Config extends IHookConfig = IHookConfig>(options: ICallHookOpts<Config['name'], Config['initialValue']>, ...args: Config['args']): Promise<Config['initialValue']>;
|
|
7
|
+
on<Config extends IHookConfig = IHookConfig>(event: Config['name'], listener: (...args: Config['args']) => void): () => void;
|
|
8
|
+
emitEvent<Config extends IHookConfig = IHookConfig>(name: Config['name'], ...args: Config['args']): void;
|
|
9
|
+
}
|
package/lib/Hookable.js
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
const AsyncParallelHook_1 = require("./AsyncParallelHook");
|
|
13
|
+
const AsyncSeriesHook_1 = require("./AsyncSeriesHook");
|
|
14
|
+
const AsyncSeriesBailHook_1 = require("./AsyncSeriesBailHook");
|
|
15
|
+
const AsyncSeriesWaterfallHook_1 = require("./AsyncSeriesWaterfallHook");
|
|
16
|
+
const utils_1 = require("./utils");
|
|
17
|
+
function callSerailWithInitialValue(hooks, args, initialValue) {
|
|
18
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
19
|
+
const fns = utils_1.getHooksFunctions(hooks);
|
|
20
|
+
return AsyncSeriesWaterfallHook_1.executeAsyncSeriesWaterfallHook(fns, initialValue, ...args);
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
function callSerail(hooks, args, bail) {
|
|
24
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
25
|
+
const thookFn = bail ? AsyncSeriesBailHook_1.executeAsyncSeriesBailHook : AsyncSeriesHook_1.executeAsyncSeriesHook;
|
|
26
|
+
const fns = utils_1.getHooksFunctions(hooks);
|
|
27
|
+
return thookFn(fns, ...args);
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
function callParallel(hooks, args) {
|
|
31
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
32
|
+
const fns = utils_1.getHooksFunctions(hooks);
|
|
33
|
+
return (yield AsyncParallelHook_1.executeAsyncParallelHook(fns, ...args));
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
class Hookable {
|
|
37
|
+
constructor() {
|
|
38
|
+
this._hooks = new Map();
|
|
39
|
+
}
|
|
40
|
+
tap(name, hook) {
|
|
41
|
+
let hooks = this._hooks.get(name);
|
|
42
|
+
if (!hooks) {
|
|
43
|
+
hooks = [];
|
|
44
|
+
this._hooks.set(name, hooks);
|
|
45
|
+
}
|
|
46
|
+
utils_1.insertHook(hooks, hook);
|
|
47
|
+
return () => {
|
|
48
|
+
utils_1.removeHook(hooks, hook);
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
// implement
|
|
52
|
+
callHook(options, ...args) {
|
|
53
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
54
|
+
const defaultOpts = {
|
|
55
|
+
bail: false,
|
|
56
|
+
parallel: false,
|
|
57
|
+
initialValue: undefined
|
|
58
|
+
};
|
|
59
|
+
let opts;
|
|
60
|
+
if (typeof options === 'object') {
|
|
61
|
+
opts = Object.assign(Object.assign({}, defaultOpts), options);
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
opts = Object.assign(Object.assign({}, defaultOpts), { name: options });
|
|
65
|
+
}
|
|
66
|
+
const hasInitialValue = typeof opts.initialValue !== 'undefined';
|
|
67
|
+
const hooks = this._hooks.get(opts.name);
|
|
68
|
+
if (!hooks || hooks.length <= 0) {
|
|
69
|
+
// @ts-ignore no return value
|
|
70
|
+
return hasInitialValue ? opts.initialValue : [];
|
|
71
|
+
}
|
|
72
|
+
if (opts.parallel) {
|
|
73
|
+
return yield callParallel(hooks, args);
|
|
74
|
+
}
|
|
75
|
+
else if (hasInitialValue) {
|
|
76
|
+
return yield callSerailWithInitialValue(hooks, args, opts.initialValue);
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
return yield callSerail(hooks, args, opts.bail);
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
on(event, listener) {
|
|
84
|
+
return this.tap(event, { name: 'listener', fn: listener });
|
|
85
|
+
}
|
|
86
|
+
emitEvent(name, ...args) {
|
|
87
|
+
this.callHook({ name, parallel: true }, ...args);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
exports.Hookable = Hookable;
|
package/lib/index.d.ts
ADDED
package/lib/index.js
ADDED
package/lib/types.d.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
export declare type NoInitValue = '$$no-initial-value';
|
|
2
|
+
export interface ICallHookOpts<Name extends string = string, InitV = unknown> {
|
|
3
|
+
name: Name;
|
|
4
|
+
bail?: boolean;
|
|
5
|
+
parallel?: boolean;
|
|
6
|
+
initialValue?: InitV;
|
|
7
|
+
}
|
|
8
|
+
export interface IHookOpts<InitValue = NoInitValue, Args extends any[] = any[]> {
|
|
9
|
+
name: string;
|
|
10
|
+
fn: (InitValue extends NoInitValue ? (...args: Args) => void | Promise<void> : (init: InitValue, ...args: Args) => InitValue | Promise<InitValue>) & {
|
|
11
|
+
hookName?: string;
|
|
12
|
+
};
|
|
13
|
+
before?: string;
|
|
14
|
+
stage?: number;
|
|
15
|
+
}
|
|
16
|
+
export interface IHookConfig {
|
|
17
|
+
name: string;
|
|
18
|
+
args: any[];
|
|
19
|
+
initialValue: any;
|
|
20
|
+
}
|
|
21
|
+
export interface IHookable {
|
|
22
|
+
tap<Config extends IHookConfig>(hook: Config['name'], opts: IHookOpts<Config['initialValue'], Config['args']>): void;
|
|
23
|
+
callHook<Config extends IHookConfig>(name: Config['name'], ...args: Config['args']): Promise<unknown[]>;
|
|
24
|
+
callHook<Config extends IHookConfig>(options: ICallHookOpts<Config['name'], Config['initialValue']>, ...args: Config['args']): Promise<Config['initialValue']>;
|
|
25
|
+
on<Config extends IHookConfig>(event: Config['name'], listener: (...args: Config['args']) => void): void;
|
|
26
|
+
emitEvent<Config extends IHookConfig>(name: Config['name'], ...args: Config['args']): void;
|
|
27
|
+
}
|
|
28
|
+
declare type IDefaultHookConfig = {
|
|
29
|
+
args: [];
|
|
30
|
+
initialValue: NoInitValue;
|
|
31
|
+
};
|
|
32
|
+
export declare type defineHook<Name extends string, Config extends Partial<IHookConfig> = {}> = {
|
|
33
|
+
name: Name;
|
|
34
|
+
} & {
|
|
35
|
+
[K in keyof Config]: Config[K];
|
|
36
|
+
} & {
|
|
37
|
+
[K in Exclude<keyof IDefaultHookConfig, keyof Config>]: IDefaultHookConfig[K];
|
|
38
|
+
};
|
|
39
|
+
export declare type defineEvent<Name extends string, Args = []> = {
|
|
40
|
+
name: Name;
|
|
41
|
+
args: Args;
|
|
42
|
+
initialValue: NoInitValue;
|
|
43
|
+
};
|
|
44
|
+
export {};
|
package/lib/types.js
ADDED
package/lib/utils.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { IHookOpts } from './types';
|
|
2
|
+
export declare const getHooksFunctions: (hooks: IHookOpts<"$$no-initial-value", any[]>[]) => (((...args: any[]) => void | Promise<void>) & {
|
|
3
|
+
hookName?: string | undefined;
|
|
4
|
+
})[];
|
|
5
|
+
export declare const insertHook: (hooks: IHookOpts<"$$no-initial-value", any[]>[], hook: IHookOpts<"$$no-initial-value", any[]>) => IHookOpts<"$$no-initial-value", any[]>[];
|
|
6
|
+
export declare const removeHook: (hooks: IHookOpts<"$$no-initial-value", any[]>[], hookToRemove: IHookOpts<"$$no-initial-value", any[]>) => void;
|
package/lib/utils.js
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getHooksFunctions = (hooks) => {
|
|
4
|
+
return hooks.map(({ fn, name }) => {
|
|
5
|
+
fn.hookName = name;
|
|
6
|
+
return fn;
|
|
7
|
+
});
|
|
8
|
+
};
|
|
9
|
+
// mutable sort
|
|
10
|
+
exports.insertHook = (hooks, hook) => {
|
|
11
|
+
let before;
|
|
12
|
+
if (typeof hook.before === 'string') {
|
|
13
|
+
before = new Set([hook.before]);
|
|
14
|
+
}
|
|
15
|
+
let stage = 0;
|
|
16
|
+
if (typeof hook.stage === 'number') {
|
|
17
|
+
stage = hook.stage;
|
|
18
|
+
}
|
|
19
|
+
const originalHooksLength = hooks.length;
|
|
20
|
+
if (hooks.length > 1) {
|
|
21
|
+
for (let i = 1; i < originalHooksLength; i++) {
|
|
22
|
+
const tap = hooks[i];
|
|
23
|
+
const tapStage = tap.stage || 0;
|
|
24
|
+
if (before) {
|
|
25
|
+
if (before.has(tap.name)) {
|
|
26
|
+
hooks.splice(i, 0, hook);
|
|
27
|
+
break;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
if (tapStage > stage) {
|
|
31
|
+
hooks.splice(i, 0, hook);
|
|
32
|
+
break;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
if (hooks.length === originalHooksLength) {
|
|
37
|
+
hooks.push(hook);
|
|
38
|
+
}
|
|
39
|
+
return hooks;
|
|
40
|
+
};
|
|
41
|
+
// mutable way
|
|
42
|
+
exports.removeHook = (hooks, hookToRemove) => {
|
|
43
|
+
const indexToRemove = hooks.findIndex(hook => hook === hookToRemove);
|
|
44
|
+
if (indexToRemove >= 0) {
|
|
45
|
+
hooks.splice(indexToRemove, 1);
|
|
46
|
+
}
|
|
47
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@shuvi/hook",
|
|
3
|
+
"version": "0.0.1-pre.1",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"main": "lib/index.js",
|
|
6
|
+
"module": "esm/index.js",
|
|
7
|
+
"types": "lib/index.d.ts",
|
|
8
|
+
"files": [
|
|
9
|
+
"esm",
|
|
10
|
+
"lib"
|
|
11
|
+
],
|
|
12
|
+
"engines": {
|
|
13
|
+
"node": ">= 12.0.0"
|
|
14
|
+
},
|
|
15
|
+
"scripts": {
|
|
16
|
+
"dev": "run-p watch:*",
|
|
17
|
+
"watch:esm": "tsc -p tsconfig.build.esm.json -w",
|
|
18
|
+
"watch:cjs": "tsc -p tsconfig.build.cjs.json -w",
|
|
19
|
+
"prebuild": "rimraf lib esm",
|
|
20
|
+
"build": "run-p build:*",
|
|
21
|
+
"build:esm": "tsc -p tsconfig.build.esm.json",
|
|
22
|
+
"build:cjs": "tsc -p tsconfig.build.cjs.json"
|
|
23
|
+
},
|
|
24
|
+
"author": "Zheng Yu Tay",
|
|
25
|
+
"gitHead": "0bc0bc76e72ca5a339341e8b71d6bb6bb41409e8"
|
|
26
|
+
}
|