packages/core/loader/src/requireEnsure.ts (105 lines of code) (raw):

import { v4 as uuidv4 } from 'uuid'; import { globalModule } from './module'; import { Module, Record } from './module/Module'; import { IBundleOption } from './type'; /** * 更新模块的加载状态,如果加载执行成功,loaded 为 true,则设置 promise 状态 done * @param id */ function resolveRecord(id: string) { const record = Module.record.get(id); if (record) { // 加载完成时会通过全局 hook 注册,成功后设置 loaded 为 true if (record.loaded) { record.resolve(); if (id.endsWith('_scripts_')) { Module.record.delete(id); } } else { // 当记录中,该模块加载状态为 false,清除该记录 record.reject(record.error); Module.record.delete(id); } } } /** * handle script loaded * @param id * @param script * @param timeout */ function onScriptComplete(id: string, script: HTMLScriptElement, timeout: number) { script.onerror = null; script.onload = null; clearTimeout(timeout); resolveRecord(id); } /** * handle script load error * @param id * @param script * @param timeout */ function onScriptError(id: string, script: HTMLScriptElement, timeout: number) { const record = Module.record.get(id); onScriptComplete(id, script, timeout); if (record) { record.reject(new Error('script load fail')); } } /** * append script * @param {IBundleOption} bundle */ function jsonpRequire(id: string, url: string, uuid: string) { const script = document.createElement('script'); script.charset = 'utf-8'; script.src = url; script.crossOrigin = 'anonymous'; script.setAttribute('nonce', ''); // 用于加载的脚本判断来源 script.setAttribute('data-from', 'alfa'); script.setAttribute('data-uuid', uuid); const timeout = window.setTimeout(() => { onScriptError(id, script, timeout); }, 120000); script.onerror = () => { onScriptError(id, script, timeout); }; script.onload = () => { onScriptComplete(id, script, timeout); }; document.head.appendChild(script); } export async function xmlRequire(id: string, url: string, transform: (source: string) => string) { const resp = await fetch(url); const code = await resp.text(); // eslint-disable-next-line no-eval window.eval(`__CONSOLE_OS_GLOBAL_HOOK__('${id.replace('_scripts_', '')}', function(require, module, exports, {window, location, history, document}){ with(window.__CONSOLE_OS_GLOBAL_VARS_){ ${transform(code)} } })`); resolveRecord(id); } // if module has been resolved function isModuleResolved(bundle: IBundleOption) { return !bundle.noCache && globalModule.resolved(bundle.id); } /** * async require the bundle from url * @param bundle {IBundleOption} */ export async function requireEnsure<T>(bundle: IBundleOption) { const transform = bundle.transform || ((source: string) => source); if (isModuleResolved(bundle)) { // if loader contains the context(window, location) // then get the new export using new context if (bundle.context) { return globalModule.requireIsolateWithContext(bundle.id, bundle.context); } // return the cached module return globalModule.require(bundle.id); } const promises: Array<Promise<T>> = []; let chunkRecord: Record<T> = Module.record.get(bundle.id); let pending = false; if (!chunkRecord || !chunkRecord.loaded) { if (chunkRecord) { promises.push(chunkRecord.promise); pending = true; } else { const promise = new Promise<T>((resolve, reject) => { chunkRecord = new Record(); chunkRecord.resolve = resolve; chunkRecord.reject = reject; chunkRecord.context = bundle.context; chunkRecord.deps = bundle.deps; chunkRecord.uuid = uuidv4(); Module.record.set(bundle.id, chunkRecord); }); chunkRecord.promise = promise; promises.push(promise); if (bundle.xmlrequest) { xmlRequire(bundle.id, bundle.url, transform); } else { jsonpRequire(bundle.id, bundle.url, chunkRecord.uuid); } } } await Promise.all(promises); // pending 的 chunk 需要重新执行 if (isModuleResolved(bundle) && pending) { // if loader contains the context(window, location) // then get the new export using new context if (bundle.context) { return globalModule.requireIsolateWithContext(bundle.id, bundle.context); } } return globalModule.require(bundle.id); }