module-federation-import-remote
Version:
Allow Dynamic Imports of Remotely Exposed Modules Using Webpack Module Federation
113 lines (109 loc) • 3.86 kB
JavaScript
const REMOTE_ENTRY_FILE = "remoteEntry.js";
const inProgress = {};
const dataPrefix = "host:";
// Ported from __webpack_require__.l
const injectScriptInHtml = (url, onDone, key) => {
if (inProgress[url]) {
inProgress[url].push(onDone);
return;
}
const allScripts = document.getElementsByTagName("script");
let script;
for (let i = 0; i < allScripts.length; i++) {
let currentScript = allScripts[i];
if (currentScript.getAttribute("src") == url || currentScript.getAttribute("data-mfir") == dataPrefix + key) {
script = currentScript;
break;
}
}
let shouldAttach = false;
if (!script) {
shouldAttach = true;
script = document.createElement("script");
script.setAttribute("data-mfir", dataPrefix + key);
script.src = url;
}
inProgress[url] = [onDone];
const onScriptComplete = (prev, event) => {
// avoid mem leaks in IE.
script.onerror = script.onload = null;
clearTimeout(timeout);
const doneFns = inProgress[url];
delete inProgress[url];
script.parentNode?.removeChild(script);
doneFns?.forEach(fn => fn(event));
if (prev) {
return prev(event);
}
};
const timeout = setTimeout(onScriptComplete.bind(null, undefined, {
type: "timeout",
target: script
}), 120000);
script.onerror = onScriptComplete.bind(null, script.onerror);
script.onload = onScriptComplete.bind(null, script.onload);
if (shouldAttach) {
document.head.appendChild(script);
}
};
const loadRemote = (url, scope, bustRemoteEntryCache) => new Promise((resolve, reject) => {
const timestamp = bustRemoteEntryCache ? `?t=${new Date().getTime()}` : "";
injectScriptInHtml(`${url}${timestamp}`, event => {
if (event?.type === "load") {
// Script loaded successfully:
return resolve();
}
const realSrc = event?.target?.src;
const eventType = event?.type;
const error = new Error();
error.message = `Loading script failed.\nMissing: ${realSrc}\nEvent type: ${eventType}`;
error.name = "ScriptExternalLoadError";
reject(error);
}, scope);
});
const initSharing = async () => {
if (!__webpack_share_scopes__?.default) {
await __webpack_init_sharing__("default");
}
};
// __initialized and __initializing flags prevent some concurrent re-initialization corner cases
const initContainer = async containerScope => {
try {
if (!containerScope.__initialized && !containerScope.__initializing) {
containerScope.__initializing = true;
await containerScope.init(__webpack_share_scopes__.default);
containerScope.__initialized = true;
delete containerScope.__initializing;
}
} catch (error) {
console.error(error);
}
};
/*
Dynamically import a remote module using Webpack's loading mechanism:
https://webpack.js.org/concepts/module-federation/
*/
const importRemote = async _ref => {
let {
url,
scope,
module,
remoteEntryFileName = REMOTE_ENTRY_FILE,
bustRemoteEntryCache = true
} = _ref;
if (!window[scope]) {
// Load the remote and initialize the share scope if it's empty
await Promise.all([loadRemote(`${url}/${remoteEntryFileName}`, scope, bustRemoteEntryCache), initSharing()]);
if (!window[scope]) {
throw new Error(`Remote loaded successfully but ${scope} could not be found! Verify that the name is correct in the Webpack configuration!`);
}
// Initialize the container to get shared modules and get the module factory:
const [, moduleFactory] = await Promise.all([initContainer(window[scope]), window[scope].get(module.startsWith("./") ? module : `./${module}`)]);
return moduleFactory();
} else {
const moduleFactory = await window[scope].get(module.startsWith("./") ? module : `./${module}`);
return moduleFactory();
}
};
export { importRemote };
//# sourceMappingURL=index.js.map