2021-03-10 15:11:04 +08:00

151 lines
4.8 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* @copy 该文件代码大部分出自 umi有需要请参考
* https://github.com/umijs/umi/blob/master/packages/runtime/src/Plugin/Plugin.ts
*/
import { assert } from '../utils';
function _compose({ fns, args }) {
if (fns.length === 1) {
return fns[0];
}
const last = fns.pop();
return fns.reduce((a, b) => () => b(a, args), last);
}
function isPromiseLike(obj) {
return !!obj && typeof obj === 'object' && typeof obj.then === 'function';
}
export const ApplyPluginsType = {
compose: 'compose',
event: 'event',
modify: 'modify'
};
export default class Plugin {
constructor(opts) {
this.validKeys = opts?.validKeys || [];
this.hooks = {};
// 共享
this.shared = {};
}
share(key, obj) {
assert(!Object.keys(this.shared).includes(key), 'share failed, key repeat');
this.shared[key] = obj;
}
getShared(key) {
return this.shared[key];
}
register(plugin) {
assert(!!plugin.apply, 'register failed, plugin.apply must supplied');
assert(!!plugin.path, 'register failed, plugin.path must supplied');
Object.keys(plugin.apply).forEach((key) => {
assert(
this.validKeys.indexOf(key) > -1,
`register failed, invalid key ${key} from plugin ${plugin.path}.`
);
if (!this.hooks[key]) this.hooks[key] = [];
this.hooks[key] = this.hooks[key].concat(plugin.apply[key]);
});
}
getHooks(keyWithDot) {
const [key, ...memberKeys] = keyWithDot.split('.');
let hooks = this.hooks[key] || [];
if (memberKeys.length) {
hooks = hooks
.map((hook) => {
try {
let ret = hook;
for (const memberKey of memberKeys) {
ret = ret[memberKey];
}
return ret;
} catch (e) {
return null;
}
})
.filter(Boolean);
}
return hooks;
}
applyPlugins({
key,
type,
initialValue,
args,
async
}) {
const hooks = this.getHooks(key) || [];
if (args) {
assert(
typeof args === 'object',
'applyPlugins failed, args must be plain object.'
);
}
switch (type) {
case ApplyPluginsType.modify:
if (async) {
return hooks.reduce(
async (memo, hook) => {
assert(typeof hook === 'function' || typeof hook === 'object' || isPromiseLike(hook),
`applyPlugins failed, all hooks for key ${key} must be function, plain object or Promise.`);
if (isPromiseLike(memo)) {
memo = await memo;
}
if (typeof hook === 'function') {
const ret = hook(memo, args);
if (isPromiseLike(ret)) {
return ret;
}
return ret;
}
if (isPromiseLike(hook)) {
hook = await hook;
}
return { ...memo, ...hook };
},
isPromiseLike(initialValue)
? initialValue
: Promise.resolve(initialValue)
);
}
return hooks.reduce((memo, hook) => {
assert(
typeof hook === 'function' || typeof hook === 'object',
`applyPlugins failed, all hooks for key ${key} must be function or plain object.`
);
if (typeof hook === 'function') {
return hook(memo, args);
}
return { ...memo, ...hook };
}, initialValue);
case ApplyPluginsType.event:
return hooks.forEach((hook) => {
assert(
typeof hook === 'function',
`applyPlugins failed, all hooks for key ${key} must be function.`
);
hook(args);
});
case ApplyPluginsType.compose:
return () => _compose({
fns: hooks.concat(initialValue),
args
})();
default:
return null;
}
}
}