"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.loadApp = loadApp; var _concat2 = _interopRequireDefault(require("lodash/concat")); var _mergeWith3 = _interopRequireDefault(require("lodash/mergeWith")); var _typeof2 = _interopRequireDefault(require("@babel/runtime/helpers/typeof")); var _forEach2 = _interopRequireDefault(require("lodash/forEach")); var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator")); var _tslib = require("tslib"); var _importHtmlEntry = require("import-html-entry"); var _addons = _interopRequireDefault(require("./addons")); var _error = require("./error"); var _globalState = require("./globalState"); var _sandbox = require("./sandbox"); var _utils = require("./utils"); /** * @author Kuitos * @since 2020-04-01 */ function assertElementExist(element, msg) { if (!element) { if (msg) { throw new _error.QiankunError(msg); } throw new _error.QiankunError('element not existed!'); } } function execHooksChain(hooks, app) { var global = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : window; if (hooks.length) { return hooks.reduce(function (chain, hook) { return chain.then(function () { return hook(app, global); }); }, Promise.resolve()); } return Promise.resolve(); } function validateSingularMode(validate, app) { return (0, _tslib.__awaiter)(this, void 0, void 0, /*#__PURE__*/_regenerator.default.mark(function _callee() { return _regenerator.default.wrap(function _callee$(_context) { while (1) { switch (_context.prev = _context.next) { case 0: return _context.abrupt("return", typeof validate === 'function' ? validate(app) : !!validate); case 1: case "end": return _context.stop(); } } }, _callee); })); } var supportShadowDOM = !!document.head.attachShadow || !!document.head.createShadowRoot; function createElement(appContent, strictStyleIsolation, scopedCSS, appInstanceId) { var containerElement = document.createElement('div'); containerElement.innerHTML = appContent; // appContent always wrapped with a singular div var appElement = containerElement.firstChild; if (strictStyleIsolation) { if (!supportShadowDOM) { console.warn('[qiankun]: As current browser not support shadow dom, your strictStyleIsolation configuration will be ignored!'); } else { var innerHTML = appElement.innerHTML; appElement.innerHTML = ''; var shadow; if (appElement.attachShadow) { shadow = appElement.attachShadow({ mode: 'open' }); } else { // createShadowRoot was proposed in initial spec, which has then been deprecated shadow = appElement.createShadowRoot(); } shadow.innerHTML = innerHTML; } } if (scopedCSS) { var attr = appElement.getAttribute(_sandbox.css.QiankunCSSRewriteAttr); if (!attr) { appElement.setAttribute(_sandbox.css.QiankunCSSRewriteAttr, appInstanceId); } var styleNodes = appElement.querySelectorAll('style') || []; (0, _forEach2.default)(styleNodes, function (stylesheetElement) { _sandbox.css.process(appElement, stylesheetElement, appInstanceId); }); } return appElement; } /** generate app wrapper dom getter */ function getAppWrapperGetter(appInstanceId, useLegacyRender, strictStyleIsolation, scopedCSS, elementGetter) { return function () { if (useLegacyRender) { if (strictStyleIsolation) throw new _error.QiankunError('strictStyleIsolation can not be used with legacy render!'); if (scopedCSS) throw new _error.QiankunError('experimentalStyleIsolation can not be used with legacy render!'); var appWrapper = document.getElementById((0, _utils.getWrapperId)(appInstanceId)); assertElementExist(appWrapper, "Wrapper element for ".concat(appInstanceId, " is not existed!")); return appWrapper; } var element = elementGetter(); assertElementExist(element, "Wrapper element for ".concat(appInstanceId, " is not existed!")); if (strictStyleIsolation && supportShadowDOM) { return element.shadowRoot; } return element; }; } var rawAppendChild = HTMLElement.prototype.appendChild; var rawRemoveChild = HTMLElement.prototype.removeChild; /** * Get the render function * If the legacy render function is provide, used as it, otherwise we will insert the app element to target container by qiankun * @param appInstanceId * @param appContent * @param legacyRender */ function getRender(appInstanceId, appContent, legacyRender) { var render = function render(_ref, phase) { var element = _ref.element, loading = _ref.loading, container = _ref.container; if (legacyRender) { if (process.env.NODE_ENV === 'development') { console.error('[qiankun] Custom rendering function is deprecated and will be removed in 3.0, you can use the container element setting instead!'); } return legacyRender({ loading: loading, appContent: element ? appContent : '' }); } var containerElement = (0, _utils.getContainer)(container); // The container might have be removed after micro app unmounted. // Such as the micro app unmount lifecycle called by a react componentWillUnmount lifecycle, after micro app unmounted, the react component might also be removed if (phase !== 'unmounted') { var errorMsg = function () { switch (phase) { case 'loading': case 'mounting': return "Target container with ".concat(container, " not existed while ").concat(appInstanceId, " ").concat(phase, "!"); case 'mounted': return "Target container with ".concat(container, " not existed after ").concat(appInstanceId, " ").concat(phase, "!"); default: return "Target container with ".concat(container, " not existed while ").concat(appInstanceId, " rendering!"); } }(); assertElementExist(containerElement, errorMsg); } if (containerElement && !containerElement.contains(element)) { // clear the container while (containerElement.firstChild) { rawRemoveChild.call(containerElement, containerElement.firstChild); } // append the element to container if it exist if (element) { rawAppendChild.call(containerElement, element); } } return undefined; }; return render; } function getLifecyclesFromExports(scriptExports, appName, global, globalLatestSetProp) { if ((0, _utils.validateExportLifecycle)(scriptExports)) { return scriptExports; } // fallback to sandbox latest set property if it had if (globalLatestSetProp) { var lifecycles = global[globalLatestSetProp]; if ((0, _utils.validateExportLifecycle)(lifecycles)) { return lifecycles; } } if (process.env.NODE_ENV === 'development') { console.warn("[qiankun] lifecycle not found from ".concat(appName, " entry exports, fallback to get from window['").concat(appName, "']")); } // fallback to global variable who named with ${appName} while module exports not found var globalVariableExports = global[appName]; if ((0, _utils.validateExportLifecycle)(globalVariableExports)) { return globalVariableExports; } throw new _error.QiankunError("You need to export lifecycle functions in ".concat(appName, " entry")); } var prevAppUnmountedDeferred; function loadApp(app) { var configuration = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; var lifeCycles = arguments.length > 2 ? arguments[2] : undefined; var _a; return (0, _tslib.__awaiter)(this, void 0, void 0, /*#__PURE__*/_regenerator.default.mark(function _callee17() { var _this = this; var entry, appName, appInstanceId, markName, _configuration$singul, singular, _configuration$sandbo, sandbox, excludeAssetFilter, _configuration$global, globalContext, importEntryOpts, _yield$importEntry, template, execScripts, assetPublicPath, appContent, strictStyleIsolation, scopedCSS, initialAppWrapperElement, initialContainer, legacyRender, render, initialAppWrapperGetter, global, mountSandbox, unmountSandbox, useLooseSandbox, sandboxContainer, _mergeWith, _mergeWith$beforeUnmo, beforeUnmount, _mergeWith$afterUnmou, afterUnmount, _mergeWith$afterMount, afterMount, _mergeWith$beforeMoun, beforeMount, _mergeWith$beforeLoad, beforeLoad, scriptExports, _getLifecyclesFromExp, bootstrap, mount, unmount, update, _getMicroAppStateActi, onGlobalStateChange, setGlobalState, offGlobalStateChange, syncAppWrapperElement2Sandbox, parcelConfigGetter; return _regenerator.default.wrap(function _callee17$(_context17) { while (1) { switch (_context17.prev = _context17.next) { case 0: entry = app.entry, appName = app.name; appInstanceId = (0, _utils.genAppInstanceIdByName)(appName); markName = "[qiankun] App ".concat(appInstanceId, " Loading"); if (process.env.NODE_ENV === 'development') { (0, _utils.performanceMark)(markName); } _configuration$singul = configuration.singular, singular = _configuration$singul === void 0 ? false : _configuration$singul, _configuration$sandbo = configuration.sandbox, sandbox = _configuration$sandbo === void 0 ? true : _configuration$sandbo, excludeAssetFilter = configuration.excludeAssetFilter, _configuration$global = configuration.globalContext, globalContext = _configuration$global === void 0 ? window : _configuration$global, importEntryOpts = (0, _tslib.__rest)(configuration, ["singular", "sandbox", "excludeAssetFilter", "globalContext"]); // get the entry html content and script executor _context17.next = 7; return (0, _importHtmlEntry.importEntry)(entry, importEntryOpts); case 7: _yield$importEntry = _context17.sent; template = _yield$importEntry.template; execScripts = _yield$importEntry.execScripts; assetPublicPath = _yield$importEntry.assetPublicPath; _context17.next = 13; return validateSingularMode(singular, app); case 13: if (!_context17.sent) { _context17.next = 16; break; } _context17.next = 16; return prevAppUnmountedDeferred && prevAppUnmountedDeferred.promise; case 16: appContent = (0, _utils.getDefaultTplWrapper)(appInstanceId)(template); strictStyleIsolation = (0, _typeof2.default)(sandbox) === 'object' && !!sandbox.strictStyleIsolation; if (process.env.NODE_ENV === 'development' && strictStyleIsolation) { console.warn("[qiankun] strictStyleIsolation configuration will be removed in 3.0, pls don't depend on it or use experimentalStyleIsolation instead!"); } scopedCSS = (0, _utils.isEnableScopedCSS)(sandbox); initialAppWrapperElement = createElement(appContent, strictStyleIsolation, scopedCSS, appInstanceId); initialContainer = 'container' in app ? app.container : undefined; legacyRender = 'render' in app ? app.render : undefined; render = getRender(appInstanceId, appContent, legacyRender); // 第一次加载设置应用可见区域 dom 结构 // 确保每次应用加载前容器 dom 结构已经设置完毕 render({ element: initialAppWrapperElement, loading: true, container: initialContainer }, 'loading'); initialAppWrapperGetter = getAppWrapperGetter(appInstanceId, !!legacyRender, strictStyleIsolation, scopedCSS, function () { return initialAppWrapperElement; }); global = globalContext; mountSandbox = function mountSandbox() { return Promise.resolve(); }; unmountSandbox = function unmountSandbox() { return Promise.resolve(); }; useLooseSandbox = (0, _typeof2.default)(sandbox) === 'object' && !!sandbox.loose; if (sandbox) { sandboxContainer = (0, _sandbox.createSandboxContainer)(appInstanceId, // FIXME should use a strict sandbox logic while remount, see https://github.com/umijs/qiankun/issues/518 initialAppWrapperGetter, scopedCSS, useLooseSandbox, excludeAssetFilter, global); // 用沙箱的代理对象作为接下来使用的全局对象 global = sandboxContainer.instance.proxy; mountSandbox = sandboxContainer.mount; unmountSandbox = sandboxContainer.unmount; } _mergeWith = (0, _mergeWith3.default)({}, (0, _addons.default)(global, assetPublicPath), lifeCycles, function (v1, v2) { return (0, _concat2.default)(v1 !== null && v1 !== void 0 ? v1 : [], v2 !== null && v2 !== void 0 ? v2 : []); }), _mergeWith$beforeUnmo = _mergeWith.beforeUnmount, beforeUnmount = _mergeWith$beforeUnmo === void 0 ? [] : _mergeWith$beforeUnmo, _mergeWith$afterUnmou = _mergeWith.afterUnmount, afterUnmount = _mergeWith$afterUnmou === void 0 ? [] : _mergeWith$afterUnmou, _mergeWith$afterMount = _mergeWith.afterMount, afterMount = _mergeWith$afterMount === void 0 ? [] : _mergeWith$afterMount, _mergeWith$beforeMoun = _mergeWith.beforeMount, beforeMount = _mergeWith$beforeMoun === void 0 ? [] : _mergeWith$beforeMoun, _mergeWith$beforeLoad = _mergeWith.beforeLoad, beforeLoad = _mergeWith$beforeLoad === void 0 ? [] : _mergeWith$beforeLoad; _context17.next = 34; return execHooksChain((0, _utils.toArray)(beforeLoad), app, global); case 34: _context17.next = 36; return execScripts(global, sandbox && !useLooseSandbox); case 36: scriptExports = _context17.sent; _getLifecyclesFromExp = getLifecyclesFromExports(scriptExports, appName, global, (_a = sandboxContainer === null || sandboxContainer === void 0 ? void 0 : sandboxContainer.instance) === null || _a === void 0 ? void 0 : _a.latestSetProp), bootstrap = _getLifecyclesFromExp.bootstrap, mount = _getLifecyclesFromExp.mount, unmount = _getLifecyclesFromExp.unmount, update = _getLifecyclesFromExp.update; _getMicroAppStateActi = (0, _globalState.getMicroAppStateActions)(appInstanceId), onGlobalStateChange = _getMicroAppStateActi.onGlobalStateChange, setGlobalState = _getMicroAppStateActi.setGlobalState, offGlobalStateChange = _getMicroAppStateActi.offGlobalStateChange; // FIXME temporary way syncAppWrapperElement2Sandbox = function syncAppWrapperElement2Sandbox(element) { return initialAppWrapperElement = element; }; parcelConfigGetter = function parcelConfigGetter() { var remountContainer = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : initialContainer; var appWrapperElement; var appWrapperGetter; var parcelConfig = { name: appInstanceId, bootstrap: bootstrap, mount: [function () { return (0, _tslib.__awaiter)(_this, void 0, void 0, /*#__PURE__*/_regenerator.default.mark(function _callee2() { var marks; return _regenerator.default.wrap(function _callee2$(_context2) { while (1) { switch (_context2.prev = _context2.next) { case 0: if (process.env.NODE_ENV === 'development') { marks = (0, _utils.performanceGetEntriesByName)(markName, 'mark'); // mark length is zero means the app is remounting if (marks && !marks.length) { (0, _utils.performanceMark)(markName); } } case 1: case "end": return _context2.stop(); } } }, _callee2); })); }, function () { return (0, _tslib.__awaiter)(_this, void 0, void 0, /*#__PURE__*/_regenerator.default.mark(function _callee3() { return _regenerator.default.wrap(function _callee3$(_context3) { while (1) { switch (_context3.prev = _context3.next) { case 0: _context3.next = 2; return validateSingularMode(singular, app); case 2: _context3.t0 = _context3.sent; if (!_context3.t0) { _context3.next = 5; break; } _context3.t0 = prevAppUnmountedDeferred; case 5: if (!_context3.t0) { _context3.next = 7; break; } return _context3.abrupt("return", prevAppUnmountedDeferred.promise); case 7: return _context3.abrupt("return", undefined); case 8: case "end": return _context3.stop(); } } }, _callee3); })); }, // initial wrapper element before app mount/remount function () { return (0, _tslib.__awaiter)(_this, void 0, void 0, /*#__PURE__*/_regenerator.default.mark(function _callee4() { return _regenerator.default.wrap(function _callee4$(_context4) { while (1) { switch (_context4.prev = _context4.next) { case 0: appWrapperElement = initialAppWrapperElement; appWrapperGetter = getAppWrapperGetter(appInstanceId, !!legacyRender, strictStyleIsolation, scopedCSS, function () { return appWrapperElement; }); case 2: case "end": return _context4.stop(); } } }, _callee4); })); }, // 添加 mount hook, 确保每次应用加载前容器 dom 结构已经设置完毕 function () { return (0, _tslib.__awaiter)(_this, void 0, void 0, /*#__PURE__*/_regenerator.default.mark(function _callee5() { var useNewContainer; return _regenerator.default.wrap(function _callee5$(_context5) { while (1) { switch (_context5.prev = _context5.next) { case 0: useNewContainer = remountContainer !== initialContainer; if (useNewContainer || !appWrapperElement) { // element will be destroyed after unmounted, we need to recreate it if it not exist // or we try to remount into a new container appWrapperElement = createElement(appContent, strictStyleIsolation, scopedCSS, appInstanceId); syncAppWrapperElement2Sandbox(appWrapperElement); } render({ element: appWrapperElement, loading: true, container: remountContainer }, 'mounting'); case 3: case "end": return _context5.stop(); } } }, _callee5); })); }, mountSandbox, // exec the chain after rendering to keep the behavior with beforeLoad function () { return (0, _tslib.__awaiter)(_this, void 0, void 0, /*#__PURE__*/_regenerator.default.mark(function _callee6() { return _regenerator.default.wrap(function _callee6$(_context6) { while (1) { switch (_context6.prev = _context6.next) { case 0: return _context6.abrupt("return", execHooksChain((0, _utils.toArray)(beforeMount), app, global)); case 1: case "end": return _context6.stop(); } } }, _callee6); })); }, function (props) { return (0, _tslib.__awaiter)(_this, void 0, void 0, /*#__PURE__*/_regenerator.default.mark(function _callee7() { return _regenerator.default.wrap(function _callee7$(_context7) { while (1) { switch (_context7.prev = _context7.next) { case 0: return _context7.abrupt("return", mount(Object.assign(Object.assign({}, props), { container: appWrapperGetter(), setGlobalState: setGlobalState, onGlobalStateChange: onGlobalStateChange }))); case 1: case "end": return _context7.stop(); } } }, _callee7); })); }, // finish loading after app mounted function () { return (0, _tslib.__awaiter)(_this, void 0, void 0, /*#__PURE__*/_regenerator.default.mark(function _callee8() { return _regenerator.default.wrap(function _callee8$(_context8) { while (1) { switch (_context8.prev = _context8.next) { case 0: return _context8.abrupt("return", render({ element: appWrapperElement, loading: false, container: remountContainer }, 'mounted')); case 1: case "end": return _context8.stop(); } } }, _callee8); })); }, function () { return (0, _tslib.__awaiter)(_this, void 0, void 0, /*#__PURE__*/_regenerator.default.mark(function _callee9() { return _regenerator.default.wrap(function _callee9$(_context9) { while (1) { switch (_context9.prev = _context9.next) { case 0: return _context9.abrupt("return", execHooksChain((0, _utils.toArray)(afterMount), app, global)); case 1: case "end": return _context9.stop(); } } }, _callee9); })); }, // initialize the unmount defer after app mounted and resolve the defer after it unmounted function () { return (0, _tslib.__awaiter)(_this, void 0, void 0, /*#__PURE__*/_regenerator.default.mark(function _callee10() { return _regenerator.default.wrap(function _callee10$(_context10) { while (1) { switch (_context10.prev = _context10.next) { case 0: _context10.next = 2; return validateSingularMode(singular, app); case 2: if (!_context10.sent) { _context10.next = 4; break; } prevAppUnmountedDeferred = new _utils.Deferred(); case 4: case "end": return _context10.stop(); } } }, _callee10); })); }, function () { return (0, _tslib.__awaiter)(_this, void 0, void 0, /*#__PURE__*/_regenerator.default.mark(function _callee11() { var measureName; return _regenerator.default.wrap(function _callee11$(_context11) { while (1) { switch (_context11.prev = _context11.next) { case 0: if (process.env.NODE_ENV === 'development') { measureName = "[qiankun] App ".concat(appInstanceId, " Loading Consuming"); (0, _utils.performanceMeasure)(measureName, markName); } case 1: case "end": return _context11.stop(); } } }, _callee11); })); }], unmount: [function () { return (0, _tslib.__awaiter)(_this, void 0, void 0, /*#__PURE__*/_regenerator.default.mark(function _callee12() { return _regenerator.default.wrap(function _callee12$(_context12) { while (1) { switch (_context12.prev = _context12.next) { case 0: return _context12.abrupt("return", execHooksChain((0, _utils.toArray)(beforeUnmount), app, global)); case 1: case "end": return _context12.stop(); } } }, _callee12); })); }, function (props) { return (0, _tslib.__awaiter)(_this, void 0, void 0, /*#__PURE__*/_regenerator.default.mark(function _callee13() { return _regenerator.default.wrap(function _callee13$(_context13) { while (1) { switch (_context13.prev = _context13.next) { case 0: return _context13.abrupt("return", unmount(Object.assign(Object.assign({}, props), { container: appWrapperGetter() }))); case 1: case "end": return _context13.stop(); } } }, _callee13); })); }, unmountSandbox, function () { return (0, _tslib.__awaiter)(_this, void 0, void 0, /*#__PURE__*/_regenerator.default.mark(function _callee14() { return _regenerator.default.wrap(function _callee14$(_context14) { while (1) { switch (_context14.prev = _context14.next) { case 0: return _context14.abrupt("return", execHooksChain((0, _utils.toArray)(afterUnmount), app, global)); case 1: case "end": return _context14.stop(); } } }, _callee14); })); }, function () { return (0, _tslib.__awaiter)(_this, void 0, void 0, /*#__PURE__*/_regenerator.default.mark(function _callee15() { return _regenerator.default.wrap(function _callee15$(_context15) { while (1) { switch (_context15.prev = _context15.next) { case 0: render({ element: null, loading: false, container: remountContainer }, 'unmounted'); offGlobalStateChange(appInstanceId); // for gc appWrapperElement = null; syncAppWrapperElement2Sandbox(appWrapperElement); case 4: case "end": return _context15.stop(); } } }, _callee15); })); }, function () { return (0, _tslib.__awaiter)(_this, void 0, void 0, /*#__PURE__*/_regenerator.default.mark(function _callee16() { return _regenerator.default.wrap(function _callee16$(_context16) { while (1) { switch (_context16.prev = _context16.next) { case 0: _context16.next = 2; return validateSingularMode(singular, app); case 2: _context16.t0 = _context16.sent; if (!_context16.t0) { _context16.next = 5; break; } _context16.t0 = prevAppUnmountedDeferred; case 5: if (!_context16.t0) { _context16.next = 7; break; } prevAppUnmountedDeferred.resolve(); case 7: case "end": return _context16.stop(); } } }, _callee16); })); }] }; if (typeof update === 'function') { parcelConfig.update = update; } return parcelConfig; }; return _context17.abrupt("return", parcelConfigGetter); case 42: case "end": return _context17.stop(); } } }, _callee17); })); }