@morjs/runtime-base
Version:
mor runtime base
251 lines • 9.27 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.applySolutions = exports.applyPlugins = exports.hooks = exports.getAllHooks = exports.createHooks = exports.SyncHook = void 0;
var tslib_1 = require("tslib");
var logger_1 = require("./logger");
var asArray_1 = require("./utils/asArray");
var HookInvokeState;
(function (HookInvokeState) {
HookInvokeState["pausing"] = "pausing";
HookInvokeState["resuming"] = "resuming";
})(HookInvokeState || (HookInvokeState = {}));
/**
* 同步 Hook
*/
var SyncHook = /** @class */ (function () {
/**
* @constructor
* @param name - Hook 名称
*/
function SyncHook(name, sharedState) {
this.name = name || '';
this.taps = [];
this.sharedState = sharedState;
}
/**
* 返回 hook 是否已被使用
*/
SyncHook.prototype.isUsed = function () {
return this.taps.length > 0;
};
/**
* 创建 hook alias
* @param name Hook 名称
*/
SyncHook.prototype.alias = function (name) {
var aliasHook = new SyncHook(name, this.sharedState);
// 这里直接使用 taps 数组, 方便 alias Hook 共用
aliasHook.taps = this.taps;
return aliasHook;
};
/**
* 添加 hook 插件
* @param nameOrOptions 名称或选项
* @param fn 函数
*/
SyncHook.prototype.tap = function (nameOrOptions, fn) {
var _a;
var name;
var stage;
if (typeof nameOrOptions === 'string') {
name = nameOrOptions;
stage = 0;
}
else {
name = nameOrOptions.name;
stage = (_a = nameOrOptions.stage) !== null && _a !== void 0 ? _a : 0;
}
if (name == null) {
logger_1.logger.error("$hooks.".concat(this.name, ".tap \u7F3A\u5C11 name"));
}
this.taps.push({
type: 'sync',
name: name,
stage: stage,
fn: fn
});
};
/**
* 执行 hook
* @param context 上下文
* @param args 参数列表
*/
SyncHook.prototype.call = function (context) {
var _a;
var args = [];
for (var _i = 1; _i < arguments.length; _i++) {
args[_i - 1] = arguments[_i];
}
// 按照 stage 排序
var taps = this.taps.sort(function (a, b) {
return a.stage - b.stage;
});
for (var _b = 0, taps_1 = taps; _b < taps_1.length; _b++) {
var tap = taps_1[_b];
// 暂停中的 hook 将所有调用保存到堆栈中,等待后续恢复
if (this.isPausing()) {
this.sharedState.stack.push([this.name, tap, context, args]);
}
else {
try {
(_a = tap.fn).call.apply(_a, tslib_1.__spreadArray([context], args, false));
}
catch (err) {
logger_1.logger.error(this.name, tap.name, err);
}
}
}
};
SyncHook.prototype.isPausing = function () {
var _a;
var state = this.sharedState;
// 当触发了 $hooks.pause 暂停,若未传入需要指定暂停的 hooks 则暂停所有生命周期触发
// 若传入了某些指定的 hooks 数组,则只暂停这些传入 hooks
if ((state === null || state === void 0 ? void 0 : state.state) !== HookInvokeState.pausing)
return false;
if (((_a = state.hooksNameList) === null || _a === void 0 ? void 0 : _a.length) === 0)
return true;
if (state.hooksNameList.indexOf(this.name) !== -1)
return true;
return false;
};
return SyncHook;
}());
exports.SyncHook = SyncHook;
// 搜集所有创建的 hooks 实例
// 主要用于 调试或检查
var HOOKS_INSTANCES = {};
/**
* 创建 hooks 对象
* @param reason Hooks 创建原因
* @returns hooks 对象
*/
function createHooks(reason) {
var sharedState = {
state: HookInvokeState.resuming,
stack: [],
hooksNameList: []
};
var appOnConstruct = new SyncHook('appOnConstruct', sharedState);
var pageOnConstructHook = new SyncHook('pageOnConstruct', sharedState);
var componentOnInitHook = new SyncHook('componentOnInit', sharedState);
var componentDidMountHook = new SyncHook('componentDidMount', sharedState);
var componentDidUnmountHook = new SyncHook('componentDidUnmount', sharedState);
var componentOnError = new SyncHook('componentOnError', sharedState);
var hooks = {
/* App 相关 hooks */
appOnConstruct: appOnConstruct,
// appOnInit 已废弃, 这里出于兼容性暂不移除
appOnInit: appOnConstruct.alias('appOnInit'),
appOnLaunch: new SyncHook('appOnLaunch', sharedState),
appOnError: new SyncHook('appOnError', sharedState),
appOnShow: new SyncHook('appOnShow', sharedState),
appOnHide: new SyncHook('appOnHide', sharedState),
appOnPageNotFound: new SyncHook('appOnPageNotFound', sharedState),
appOnUnhandledRejection: new SyncHook('appOnUnhandledRejection', sharedState),
/* Page 相关 hooks */
pageOnConstruct: pageOnConstructHook,
// pageOnInit 已废弃, 这里出于兼容性暂不移除
pageOnInit: pageOnConstructHook.alias('pageOnInit'),
pageOnLoad: new SyncHook('pageOnLoad', sharedState),
pageOnReady: new SyncHook('pageOnReady', sharedState),
pageOnShow: new SyncHook('pageOnShow', sharedState),
pageOnHide: new SyncHook('pageOnHide', sharedState),
pageOnUnload: new SyncHook('pageOnUnload', sharedState),
/* Component 相关 hooks */
componentOnConstruct: new SyncHook('componentOnConstruct', sharedState),
componentOnInit: componentOnInitHook,
componentOnCreated: componentOnInitHook.alias('componentOnCreated'),
componentDidMount: componentDidMountHook,
componentOnAttached: componentDidMountHook.alias('componentOnAttached'),
componentDidUnmount: componentDidUnmountHook,
componentOnDetached: componentDidUnmountHook.alias('componentOnDetached'),
componentOnError: componentOnError,
pause: function (hooksNameList) {
sharedState.state = HookInvokeState.pausing;
sharedState.hooksNameList = hooksNameList || [];
},
resume: function () {
var _a;
sharedState.state = HookInvokeState.resuming;
var stackItem = sharedState.stack.shift();
while (stackItem) {
var name_1 = stackItem[0], tap = stackItem[1], context = stackItem[2], args = stackItem[3];
try {
tap === null || tap === void 0 ? void 0 : (_a = tap.fn).call.apply(_a, tslib_1.__spreadArray([context], args, false));
}
catch (error) {
logger_1.logger.error(name_1, tap.name, error);
}
stackItem = sharedState.stack.shift();
}
}
};
// 记录创建的所有 hooks
HOOKS_INSTANCES[reason] = HOOKS_INSTANCES[reason] || [];
HOOKS_INSTANCES[reason].push({
createdAt: +new Date(),
hooks: hooks
});
return hooks;
}
exports.createHooks = createHooks;
/**
* 获取所有 hooks
*/
function getAllHooks() {
return HOOKS_INSTANCES;
}
exports.getAllHooks = getAllHooks;
/**
* 获取全局共享属性,用于作为原子化的兜底实现
* 1. 首先查找上下文中的属性
* 2. 如果不存在,则查找 getApp 中的
* 3. 如果不存在,则查找 小程序环境的 globalObject, 如 my 中是否存在
*/
exports.hooks = createHooks('default');
/**
* 应用插件
* @param hooks Hooks
* @param plugins 插件列表
*/
function applyPlugins(hooks, plugins) {
var pluginsNames = [];
plugins.forEach(function (plugin) {
try {
plugin.apply(hooks);
pluginsNames.push(plugin.pluginName);
}
catch (err) {
logger_1.logger.error("[plugin ".concat(plugin.pluginName, "]: \u521D\u59CB\u5316\u62A5\u9519"), err);
}
});
return pluginsNames;
}
exports.applyPlugins = applyPlugins;
/**
* 应用 Solutions
* @param hooks Hooks
* @param solutions 插件集列表
*/
function applySolutions(hooks, solution) {
var solutions = (0, asArray_1.asArray)(solution);
var plugins = [];
try {
solutions.forEach(function (solution) {
var _a;
if (typeof solution === 'function') {
plugins = plugins.concat(((_a = solution()) === null || _a === void 0 ? void 0 : _a.plugins) || []);
}
else {
logger_1.logger.error("\u521D\u59CB\u5316\u8FD0\u884C\u65F6\u63D2\u4EF6\u5931\u8D25, \u539F\u56E0: ".concat(solution, " \u4E0D\u662F\u4E00\u4E2A\u6709\u6548\u7684 solution"));
}
});
}
catch (err) {
logger_1.logger.error("\u521D\u59CB\u5316\u8FD0\u884C\u65F6\u63D2\u4EF6\u5931\u8D25, \u539F\u56E0: ".concat(err));
}
return applyPlugins(hooks, plugins);
}
exports.applySolutions = applySolutions;
//# sourceMappingURL=hooks.js.map