@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 ADDED
@@ -0,0 +1 @@
1
+ # @shuvi/hook
@@ -0,0 +1,3 @@
1
+ export declare const executeAsyncParallelHook: (tapFns: (((...args: any[]) => void | Promise<void>) & {
2
+ hookName?: string | undefined;
3
+ })[], ...args: any[]) => Promise<void[]>;
@@ -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,3 @@
1
+ export declare const executeAsyncSeriesBailHook: (tapFns: (((...args: any[]) => void | Promise<void>) & {
2
+ hookName?: string | undefined;
3
+ })[], ...args: any[]) => Promise<unknown>;
@@ -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,3 @@
1
+ export declare const executeAsyncSeriesHook: (tapFns: (((...args: any[]) => void | Promise<void>) & {
2
+ hookName?: string | undefined;
3
+ })[], ...args: any[]) => Promise<unknown[]>;
@@ -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,3 @@
1
+ export declare const executeAsyncSeriesWaterfallHook: (tapFns: (((...args: any[]) => void | Promise<void>) & {
2
+ hookName?: string | undefined;
3
+ })[], ...args: any[]) => Promise<any>;
@@ -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
+ }
@@ -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
@@ -0,0 +1,3 @@
1
+ import { Hookable } from './Hookable';
2
+ export { Hookable };
3
+ export * from './types';
package/esm/index.js ADDED
@@ -0,0 +1,2 @@
1
+ import { Hookable } from './Hookable';
2
+ export { Hookable };
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,3 @@
1
+ export declare const executeAsyncParallelHook: (tapFns: (((...args: any[]) => void | Promise<void>) & {
2
+ hookName?: string | undefined;
3
+ })[], ...args: any[]) => Promise<void[]>;
@@ -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,3 @@
1
+ export declare const executeAsyncSeriesBailHook: (tapFns: (((...args: any[]) => void | Promise<void>) & {
2
+ hookName?: string | undefined;
3
+ })[], ...args: any[]) => Promise<unknown>;
@@ -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,3 @@
1
+ export declare const executeAsyncSeriesHook: (tapFns: (((...args: any[]) => void | Promise<void>) & {
2
+ hookName?: string | undefined;
3
+ })[], ...args: any[]) => Promise<unknown[]>;
@@ -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,3 @@
1
+ export declare const executeAsyncSeriesWaterfallHook: (tapFns: (((...args: any[]) => void | Promise<void>) & {
2
+ hookName?: string | undefined;
3
+ })[], ...args: any[]) => Promise<any>;
@@ -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
+ }
@@ -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
@@ -0,0 +1,3 @@
1
+ import { Hookable } from './Hookable';
2
+ export { Hookable };
3
+ export * from './types';
package/lib/index.js ADDED
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const Hookable_1 = require("./Hookable");
4
+ exports.Hookable = Hookable_1.Hookable;
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
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
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
+ }