From 42047f650a51e93a78a5f27c21d0f0299159eb76 Mon Sep 17 00:00:00 2001 From: pqcqaq <905739777@qq.com> Date: Sun, 27 Oct 2024 12:50:57 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=A0=E9=99=A4attrUpdateMatrix=E4=B8=AD?= =?UTF-8?q?=E5=A4=9A=E4=BD=99=E7=9A=84=E6=95=B0=E6=8D=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- plugins/LoadFromCdn.js | 131 ++++++++++++++++++ src/CdnConfig.ts | 17 +++ .../src/configuration/attrUpdateMatrix.ts | 14 +- 3 files changed, 155 insertions(+), 7 deletions(-) create mode 100644 plugins/LoadFromCdn.js create mode 100644 src/CdnConfig.ts diff --git a/plugins/LoadFromCdn.js b/plugins/LoadFromCdn.js new file mode 100644 index 0000000..13675d5 --- /dev/null +++ b/plugins/LoadFromCdn.js @@ -0,0 +1,131 @@ +const fs = require('fs'); +const path = require('path'); + +class CdnLoaderPlugin { + constructor(options) { + this.options = options; + } + + apply(compiler) { + compiler.hooks.emit.tapAsync('CdnLoaderPlugin', (compilation, callback) => { + // 生成 loadFromCdn.js 内容 + const cdnLoaderContent = this.generateCdnLoaderContent(); + + // 将 loadFromCdn.js 添加到输出文件中 + compilation.assets['loadFromCdn.js'] = { + source: () => cdnLoaderContent, + size: () => cdnLoaderContent.length + }; + + // 修改 HTML 文件 + Object.keys(compilation.assets).forEach(filename => { + if (filename.endsWith('.html')) { + const asset = compilation.assets[filename]; + let content = asset.source(); + + // 替换入口脚本的加载方式 + content = content.replace( + /<\/script>/, + ` + ` + ); + + compilation.assets[filename] = { + source: () => content, + size: () => content.length + }; + } + }); + + callback(); + }); + } + + generateCdnLoaderContent() { + const { from, module, timeout, retry } = this.options; + + const cdnUrls = Object.entries(module).reduce((acc, [name, config]) => { + acc[name] = config.direct? [config.js] : Array.isArray(from) ? from.map(url => `${url}/${name}/${config.version}/${config.js}`) : [`${from}/${name}/${config.version}/${config.js}`]; + return acc; + }, {}); + + return ` + const GLOBAL_CONFIG = { + TIMEOUT: ${timeout}, + MAX_RETRIES: ${retry}, + onLoadError: null, + }; + + const cdnUrls = ${JSON.stringify(cdnUrls, null, 2)}; + + function loadScriptWithTimeout(url, timeout) { + return new Promise((resolve, reject) => { + const script = document.createElement('script'); + script.src = url; + + const timeoutId = setTimeout(() => { + reject(new Error(\`Loading \${url} timed out\`)); + }, timeout); + + script.onload = () => { + clearTimeout(timeoutId); + resolve(script); + }; + + script.onerror = () => { + clearTimeout(timeoutId); + reject(new Error(\`Failed to load \${url}\`)); + }; + + document.head.appendChild(script); + }); + } + + async function loadLibraryWithRetry(library, retryCount = 0) { + const controllers = cdnUrls[library].map(() => new AbortController()); + + try { + const loadPromises = cdnUrls[library].map((url, index) => { + return loadScriptWithTimeout(url, GLOBAL_CONFIG.TIMEOUT) + .then(script => { + controllers.forEach((controller, i) => { + if (i !== index) controller.abort(); + }); + return script; + }); + }); + + return await Promise.any(loadPromises); + } catch (error) { + if (retryCount < GLOBAL_CONFIG.MAX_RETRIES) { + console.warn(\`Retry loading \${library}, attempt \${retryCount + 1}\`); + return loadLibraryWithRetry(library, retryCount + 1); + } else { + if (GLOBAL_CONFIG.onLoadError) { + GLOBAL_CONFIG.onLoadError(library, error); + } + throw error; + } + } + } + + function loadExternals() { + const libraries = Object.keys(cdnUrls); + return Promise.all(libraries.map(library => loadLibraryWithRetry(library))); + } + + // 默认的错误处理函数 + GLOBAL_CONFIG.onLoadError = (library, error) => { + console.error(\`Failed to load \${library} after \${GLOBAL_CONFIG.MAX_RETRIES} retries:\`, error); + }; + `; + } +} + +module.exports = CdnLoaderPlugin; diff --git a/src/CdnConfig.ts b/src/CdnConfig.ts new file mode 100644 index 0000000..33abb15 --- /dev/null +++ b/src/CdnConfig.ts @@ -0,0 +1,17 @@ +export type CdnConfig = { + from: string | string[], + module: { + [name: string]: ModuleConfig + }, + timeout: number, + retry: number, +} + +export type ModuleConfig = { + scope: string, + version: string, + js: string, + css?: string, + // js和css是否直接是链接 + direct?: boolean, +} \ No newline at end of file diff --git a/template/src/configuration/attrUpdateMatrix.ts b/template/src/configuration/attrUpdateMatrix.ts index 1e6e4a5..1ec0522 100644 --- a/template/src/configuration/attrUpdateMatrix.ts +++ b/template/src/configuration/attrUpdateMatrix.ts @@ -3,13 +3,13 @@ import { EntityDict } from '@project/oak-app-domain'; const attrUpdateMatrix: AttrUpdateMatrix = { - store: { - name: { - filter: { - iState: 'offline', - }, - }, - }, + // store: { + // name: { + // filter: { + // iState: 'offline', + // }, + // }, + // }, } export default attrUpdateMatrix; \ No newline at end of file