remote-reload-utils 0.0.8 → 0.0.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +154 -0
- package/README.md +62 -0
- package/dist/components/ErrorBoundary.d.ts +34 -0
- package/dist/components/RemoteModuleCard.d.ts +84 -0
- package/dist/components/SuspenseLoader.d.ts +131 -0
- package/dist/components/index.d.ts +6 -0
- package/dist/event-bus/index.d.ts +40 -0
- package/dist/health/index.d.ts +23 -0
- package/dist/index.d.ts +12 -2
- package/dist/loader/index.d.ts +6 -0
- package/dist/loader/utils.d.ts +38 -0
- package/dist/main.cjs +1084 -52
- package/dist/main.js +935 -51
- package/dist/preload/index.d.ts +19 -0
- package/dist/{types.d.ts → types/index.d.ts} +15 -0
- package/dist/unload/index.d.ts +29 -0
- package/dist/version/index.d.ts +34 -0
- package/loadRemote.md +1356 -28
- package/package.json +24 -7
- package/dist/loadRemote.d.ts +0 -9
- package/dist/loadRemote2.d.ts +0 -11
- package/dist/react-mf-adapter.d.ts +0 -0
- /package/dist/{plugins.d.ts → plugins/fallback.d.ts} +0 -0
- /package/dist/{loadReactVersion.d.ts → version/react.d.ts} +0 -0
package/dist/main.cjs
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
var __webpack_require__ = {};
|
|
3
|
+
(()=>{
|
|
4
|
+
__webpack_require__.n = (module)=>{
|
|
5
|
+
var getter = module && module.__esModule ? ()=>module['default'] : ()=>module;
|
|
6
|
+
__webpack_require__.d(getter, {
|
|
7
|
+
a: getter
|
|
8
|
+
});
|
|
9
|
+
return getter;
|
|
10
|
+
};
|
|
11
|
+
})();
|
|
3
12
|
(()=>{
|
|
4
13
|
__webpack_require__.d = (exports1, definition)=>{
|
|
5
14
|
for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
|
|
@@ -24,9 +33,219 @@ var __webpack_require__ = {};
|
|
|
24
33
|
var __webpack_exports__ = {};
|
|
25
34
|
__webpack_require__.r(__webpack_exports__);
|
|
26
35
|
__webpack_require__.d(__webpack_exports__, {
|
|
27
|
-
|
|
36
|
+
compareVersions: ()=>compareVersions,
|
|
37
|
+
fetchAvailableVersions: ()=>fetchAvailableVersions,
|
|
38
|
+
sortVersions: ()=>sortVersions,
|
|
39
|
+
useRemoteModuleHook: ()=>useRemoteModuleHook,
|
|
40
|
+
registerRemoteInstance: ()=>registerRemoteInstance,
|
|
41
|
+
fallbackPlugin: ()=>fallbackPlugin,
|
|
42
|
+
createEventBus: ()=>createEventBus,
|
|
43
|
+
withRemote: ()=>withRemote,
|
|
44
|
+
getPreloadStatus: ()=>getPreloadStatus,
|
|
45
|
+
parseVersion: ()=>parseVersion,
|
|
46
|
+
resolveFinalVersion: ()=>resolveFinalVersion,
|
|
47
|
+
buildFinalUrls: ()=>buildFinalUrls,
|
|
48
|
+
fetchLatestVersion: ()=>fetchLatestVersion,
|
|
49
|
+
ErrorBoundary: ()=>ErrorBoundary,
|
|
50
|
+
getFinalSharedConfig: ()=>getFinalSharedConfig,
|
|
51
|
+
isRemoteLoaded: ()=>isRemoteLoaded,
|
|
52
|
+
tryLoadRemote: ()=>tryLoadRemote,
|
|
53
|
+
getVersionCache: ()=>getVersionCache,
|
|
54
|
+
eventBus: ()=>eventBus,
|
|
55
|
+
loadRemoteMultiVersion: ()=>loadRemoteMultiVersion,
|
|
56
|
+
clearPreloadCache: ()=>clearPreloadCache,
|
|
57
|
+
getRemoteHealthReport: ()=>getRemoteHealthReport,
|
|
58
|
+
checkModuleLoadable: ()=>checkModuleLoadable,
|
|
59
|
+
getStableVersions: ()=>getStableVersions,
|
|
60
|
+
extractMajorVersion: ()=>extractMajorVersion,
|
|
61
|
+
buildCdnUrls: ()=>buildCdnUrls,
|
|
62
|
+
loadReactVersion: ()=>loadReactVersion,
|
|
63
|
+
preloadRemoteList: ()=>preloadRemoteList,
|
|
64
|
+
SuspenseRemoteLoader: ()=>SuspenseRemoteLoader,
|
|
65
|
+
registerLoadedModule: ()=>registerLoadedModule,
|
|
66
|
+
getCompatibleReactVersions: ()=>getCompatibleReactVersions,
|
|
67
|
+
SuspenseRemote: ()=>SuspenseRemote,
|
|
68
|
+
unloadRemote: ()=>unloadRemote,
|
|
69
|
+
cancelPreload: ()=>cancelPreload,
|
|
70
|
+
findCompatibleVersion: ()=>findCompatibleVersion,
|
|
71
|
+
getLoadedRemotes: ()=>getLoadedRemotes,
|
|
72
|
+
getLatestVersion: ()=>getLatestVersion,
|
|
73
|
+
preloadRemote: ()=>preloadRemote,
|
|
74
|
+
satisfiesVersion: ()=>satisfiesVersion,
|
|
75
|
+
formatHealthStatus: ()=>formatHealthStatus,
|
|
76
|
+
RemoteModuleCard: ()=>RemoteModuleCard,
|
|
77
|
+
unloadAll: ()=>unloadAll,
|
|
78
|
+
setVersionCache: ()=>setVersionCache,
|
|
79
|
+
lazyRemote: ()=>lazyRemote,
|
|
80
|
+
checkRemoteHealth: ()=>checkRemoteHealth,
|
|
81
|
+
isPrerelease: ()=>isPrerelease,
|
|
82
|
+
checkVersionCompatibility: ()=>checkVersionCompatibility
|
|
28
83
|
});
|
|
29
84
|
const runtime_namespaceObject = require("@module-federation/enhanced/runtime");
|
|
85
|
+
async function loadReactVersion(version) {
|
|
86
|
+
const runtime = await (0, runtime_namespaceObject.createInstance)({
|
|
87
|
+
name: `react_${version}_runtime`,
|
|
88
|
+
remotes: [
|
|
89
|
+
{
|
|
90
|
+
name: `react@${version}`,
|
|
91
|
+
entry: `https://cdn.jsdelivr.net/npm/react@${version}/umd/react.production.min.js`,
|
|
92
|
+
type: 'var',
|
|
93
|
+
entryGlobalName: 'React'
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
name: `react-dom@${version}`,
|
|
97
|
+
entry: `https://cdn.jsdelivr.net/npm/react-dom@${version}/umd/react-dom.production.min.js`,
|
|
98
|
+
type: 'var',
|
|
99
|
+
entryGlobalName: 'ReactDOM'
|
|
100
|
+
}
|
|
101
|
+
]
|
|
102
|
+
});
|
|
103
|
+
const React = await runtime.loadRemote(`react@${version}`);
|
|
104
|
+
const ReactDOM = await runtime.loadRemote(`react-dom@${version}`);
|
|
105
|
+
return {
|
|
106
|
+
React,
|
|
107
|
+
ReactDOM
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
function parseVersion(version) {
|
|
111
|
+
const cleaned = version.replace(/^v/i, '');
|
|
112
|
+
const parts = cleaned.split(/[-+]/);
|
|
113
|
+
const [major, minor, patch] = parts[0].split('.').map(Number);
|
|
114
|
+
return {
|
|
115
|
+
major: isNaN(major) ? 0 : major,
|
|
116
|
+
minor: isNaN(minor) ? 0 : minor,
|
|
117
|
+
patch: isNaN(patch) ? 0 : patch,
|
|
118
|
+
prerelease: parts[1],
|
|
119
|
+
build: parts[2],
|
|
120
|
+
raw: cleaned
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
function compareVersions(v1, v2) {
|
|
124
|
+
const p1 = parseVersion(v1);
|
|
125
|
+
const p2 = parseVersion(v2);
|
|
126
|
+
if (p1.major !== p2.major) return p1.major - p2.major;
|
|
127
|
+
if (p1.minor !== p2.minor) return p1.minor - p2.minor;
|
|
128
|
+
if (p1.patch !== p2.patch) return p1.patch - p2.patch;
|
|
129
|
+
if (p1.prerelease && !p2.prerelease) return -1;
|
|
130
|
+
if (!p1.prerelease && p2.prerelease) return 1;
|
|
131
|
+
if (p1.prerelease && p2.prerelease) return p1.prerelease.localeCompare(p2.prerelease);
|
|
132
|
+
return 0;
|
|
133
|
+
}
|
|
134
|
+
function satisfiesVersion(current, required) {
|
|
135
|
+
const opMatch = required.match(/^(>=|<=|>|<|=|~|\^)?/);
|
|
136
|
+
const operator = opMatch?.[1] || '=';
|
|
137
|
+
const version = required.replace(/^(>=|<=|>|<|=|~|\^)/, '');
|
|
138
|
+
const cmp = compareVersions(current, version);
|
|
139
|
+
switch(operator){
|
|
140
|
+
case '>':
|
|
141
|
+
return cmp > 0;
|
|
142
|
+
case '>=':
|
|
143
|
+
return cmp >= 0;
|
|
144
|
+
case '<':
|
|
145
|
+
return cmp < 0;
|
|
146
|
+
case '<=':
|
|
147
|
+
return cmp <= 0;
|
|
148
|
+
case '=':
|
|
149
|
+
case '':
|
|
150
|
+
return 0 === cmp;
|
|
151
|
+
case '^':
|
|
152
|
+
return parseVersion(current).major === parseVersion(version).major && (parseVersion(current).major > 0 || parseVersion(current).minor === parseVersion(version).minor);
|
|
153
|
+
case '~':
|
|
154
|
+
return parseVersion(current).major === parseVersion(version).major && parseVersion(current).minor === parseVersion(version).minor;
|
|
155
|
+
default:
|
|
156
|
+
return 0 === cmp;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
function checkVersionCompatibility(currentVersion, requiredVersion, packageName) {
|
|
160
|
+
const isCompatible = satisfiesVersion(currentVersion, requiredVersion);
|
|
161
|
+
const current = parseVersion(currentVersion);
|
|
162
|
+
const required = parseVersion(requiredVersion);
|
|
163
|
+
let message;
|
|
164
|
+
let suggestion;
|
|
165
|
+
let severity;
|
|
166
|
+
if (isCompatible) {
|
|
167
|
+
message = `${packageName}@${currentVersion} 满足要求 ${requiredVersion}`;
|
|
168
|
+
severity = 'info';
|
|
169
|
+
} else {
|
|
170
|
+
const cmp = compareVersions(currentVersion, requiredVersion);
|
|
171
|
+
if (cmp < 0) {
|
|
172
|
+
message = `${packageName}@${currentVersion} 版本过低,需要 ${requiredVersion}`;
|
|
173
|
+
severity = 'error';
|
|
174
|
+
current.patch;
|
|
175
|
+
current.major, current.minor;
|
|
176
|
+
suggestion = `建议升级到 ${current.major}.${current.minor}.x 或更高版本`;
|
|
177
|
+
} else {
|
|
178
|
+
message = `${packageName}@${currentVersion} 版本过高,需要 ${requiredVersion}`;
|
|
179
|
+
severity = 'warning';
|
|
180
|
+
suggestion = `建议降级到 ${required.major}.${required.minor}.x 或匹配主版本的兼容版本`;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return {
|
|
184
|
+
compatible: isCompatible,
|
|
185
|
+
currentVersion,
|
|
186
|
+
requiredVersion,
|
|
187
|
+
suggestion,
|
|
188
|
+
severity,
|
|
189
|
+
message
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
function findCompatibleVersion(availableVersions, range) {
|
|
193
|
+
let candidates = availableVersions;
|
|
194
|
+
if (range.exact) candidates = candidates.filter((v)=>v === range.exact);
|
|
195
|
+
else {
|
|
196
|
+
if (range.min) candidates = candidates.filter((v)=>compareVersions(v, range.min) >= 0);
|
|
197
|
+
if (range.max) candidates = candidates.filter((v)=>compareVersions(v, range.max) <= 0);
|
|
198
|
+
}
|
|
199
|
+
if (0 === candidates.length) return null;
|
|
200
|
+
return candidates.sort((a, b)=>compareVersions(b, a))[0];
|
|
201
|
+
}
|
|
202
|
+
function getCompatibleReactVersions(hostVersion) {
|
|
203
|
+
const host = parseVersion(hostVersion);
|
|
204
|
+
const versions = [];
|
|
205
|
+
for(let i = host.major; i >= 15; i--)for(let j = 0; j <= 5; j++){
|
|
206
|
+
const version = `${i}.${j}.0`;
|
|
207
|
+
if (checkVersionCompatibility(version, `^${host.major}.0.0`, 'react').compatible) versions.push(version);
|
|
208
|
+
}
|
|
209
|
+
const uniqueVersions = [];
|
|
210
|
+
const seen = new Set();
|
|
211
|
+
for (const v of versions)if (!seen.has(v)) {
|
|
212
|
+
seen.add(v);
|
|
213
|
+
uniqueVersions.push(v);
|
|
214
|
+
}
|
|
215
|
+
return uniqueVersions;
|
|
216
|
+
}
|
|
217
|
+
async function fetchAvailableVersions(pkg) {
|
|
218
|
+
try {
|
|
219
|
+
const res = await fetch(`https://registry.npmjs.org/${pkg}`);
|
|
220
|
+
if (!res.ok) return [];
|
|
221
|
+
const data = await res.json();
|
|
222
|
+
return Object.keys(data.versions || {});
|
|
223
|
+
} catch {
|
|
224
|
+
return [];
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
function sortVersions(versions, order = 'desc') {
|
|
228
|
+
return [
|
|
229
|
+
...versions
|
|
230
|
+
].sort((a, b)=>{
|
|
231
|
+
const cmp = compareVersions(a, b);
|
|
232
|
+
return 'desc' === order ? -cmp : cmp;
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
function getLatestVersion(versions) {
|
|
236
|
+
if (0 === versions.length) return null;
|
|
237
|
+
return sortVersions(versions, 'desc')[0];
|
|
238
|
+
}
|
|
239
|
+
function getStableVersions(versions) {
|
|
240
|
+
return versions.filter((v)=>!v.includes('alpha') && !v.includes('beta') && !v.includes('rc'));
|
|
241
|
+
}
|
|
242
|
+
function extractMajorVersion(version) {
|
|
243
|
+
return parseVersion(version).major;
|
|
244
|
+
}
|
|
245
|
+
function isPrerelease(version) {
|
|
246
|
+
const v = parseVersion(version);
|
|
247
|
+
return !!v.prerelease;
|
|
248
|
+
}
|
|
30
249
|
const fallbackPlugin = ()=>({
|
|
31
250
|
name: 'fallback-plugin',
|
|
32
251
|
errorLoadRemote (args) {
|
|
@@ -35,29 +254,95 @@ const fallbackPlugin = ()=>({
|
|
|
35
254
|
return fallback;
|
|
36
255
|
}
|
|
37
256
|
});
|
|
257
|
+
const DEFAULT_CDN_TEMPLATES = [
|
|
258
|
+
'https://cdn.jsdelivr.net/npm/{pkg}@{version}/dist/remoteEntry.js',
|
|
259
|
+
'https://unpkg.com/{pkg}@{version}/dist/remoteEntry.js'
|
|
260
|
+
];
|
|
261
|
+
const DEFAULT_SHARED_CONFIG = {
|
|
262
|
+
react: {
|
|
263
|
+
shareConfig: {
|
|
264
|
+
singleton: true,
|
|
265
|
+
eager: true,
|
|
266
|
+
requiredVersion: false
|
|
267
|
+
}
|
|
268
|
+
},
|
|
269
|
+
'react-dom': {
|
|
270
|
+
shareConfig: {
|
|
271
|
+
singleton: true,
|
|
272
|
+
eager: true,
|
|
273
|
+
requiredVersion: false
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
};
|
|
38
277
|
async function fetchLatestVersion(pkg) {
|
|
39
278
|
const res = await fetch(`https://registry.npmjs.org/${pkg}`);
|
|
40
|
-
if (!res.ok) throw new Error(
|
|
279
|
+
if (!res.ok) throw new Error(`[MF] 无法获取 ${pkg} 的版本信息,状态码:${res.status}`);
|
|
41
280
|
const data = await res.json();
|
|
42
|
-
|
|
281
|
+
const latest = data['dist-tags']?.latest;
|
|
282
|
+
if (!latest) throw new Error(`[MF] 无法从 NPM 获取 ${pkg} 的 latest tag`);
|
|
283
|
+
return latest;
|
|
43
284
|
}
|
|
44
285
|
function getVersionCache() {
|
|
45
286
|
try {
|
|
46
|
-
|
|
47
|
-
|
|
287
|
+
const cacheStr = localStorage.getItem('mf-multi-version');
|
|
288
|
+
return cacheStr ? JSON.parse(cacheStr) : {};
|
|
289
|
+
} catch (e) {
|
|
290
|
+
console.error('[MF Cache] 读取缓存失败:', e);
|
|
48
291
|
return {};
|
|
49
292
|
}
|
|
50
293
|
}
|
|
51
294
|
function setVersionCache(pkg, version) {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
295
|
+
try {
|
|
296
|
+
const cache = getVersionCache();
|
|
297
|
+
cache[pkg] = cache[pkg] || {};
|
|
298
|
+
cache[pkg][version] = {
|
|
299
|
+
timestamp: Date.now()
|
|
300
|
+
};
|
|
301
|
+
localStorage.setItem('mf-multi-version', JSON.stringify(cache));
|
|
302
|
+
} catch (e) {
|
|
303
|
+
console.error('[MF Cache] 写入缓存失败:', e);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
function buildCdnUrls(pkg, version) {
|
|
307
|
+
return DEFAULT_CDN_TEMPLATES.map((template)=>template.replace('{pkg}', pkg).replace('{version}', version));
|
|
308
|
+
}
|
|
309
|
+
async function tryLoadRemote(scopeName, url, retries, delay, sharedConfig, plugins) {
|
|
310
|
+
let lastError;
|
|
311
|
+
for(let i = 0; i < retries; i++)try {
|
|
312
|
+
const mf = (0, runtime_namespaceObject.createInstance)({
|
|
313
|
+
name: 'host',
|
|
314
|
+
remotes: [
|
|
315
|
+
{
|
|
316
|
+
name: scopeName,
|
|
317
|
+
entry: url
|
|
318
|
+
}
|
|
319
|
+
],
|
|
320
|
+
shared: sharedConfig,
|
|
321
|
+
plugins: [
|
|
322
|
+
...plugins,
|
|
323
|
+
fallbackPlugin()
|
|
324
|
+
]
|
|
325
|
+
});
|
|
326
|
+
return {
|
|
327
|
+
scopeName,
|
|
328
|
+
mf
|
|
329
|
+
};
|
|
330
|
+
} catch (e) {
|
|
331
|
+
lastError = e;
|
|
332
|
+
console.warn(`[MF] URL ${url} 加载失败,第 ${i + 1} 次重试...`);
|
|
333
|
+
if (i < retries - 1) await new Promise((res)=>setTimeout(res, delay));
|
|
334
|
+
}
|
|
335
|
+
throw new Error(`[MF] URL ${url} 经过 ${retries} 次重试仍加载失败。`, {
|
|
336
|
+
cause: lastError
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
function getFinalSharedConfig(customShared) {
|
|
340
|
+
return {
|
|
341
|
+
...DEFAULT_SHARED_CONFIG,
|
|
342
|
+
...customShared || {}
|
|
56
343
|
};
|
|
57
|
-
localStorage.setItem('mf-multi-version', JSON.stringify(cache));
|
|
58
344
|
}
|
|
59
|
-
async function
|
|
60
|
-
const { name, pkg, version = 'latest', retries = 3, delay = 1000, localFallback, cacheTTL = 86400000, revalidate = true } = options;
|
|
345
|
+
async function resolveFinalVersion(pkg, version, cacheTTL, revalidate) {
|
|
61
346
|
let finalVersion = version;
|
|
62
347
|
if ('latest' === version) {
|
|
63
348
|
const cache = getVersionCache();
|
|
@@ -66,61 +351,808 @@ async function loadRemoteMultiVersion(options, plugins) {
|
|
|
66
351
|
if (latestCached && Date.now() - versions[latestCached].timestamp < cacheTTL) {
|
|
67
352
|
finalVersion = latestCached;
|
|
68
353
|
if (revalidate) fetchLatestVersion(pkg).then((latest)=>{
|
|
69
|
-
if (latest !== latestCached)
|
|
70
|
-
|
|
354
|
+
if (latest !== latestCached) {
|
|
355
|
+
console.log(`[MF] 发现 ${pkg} 新版本 ${latest},已更新缓存。`);
|
|
356
|
+
setVersionCache(pkg, latest);
|
|
357
|
+
}
|
|
358
|
+
}).catch((e)=>console.error(`[MF] 异步检查最新版本失败:`, e));
|
|
71
359
|
} else {
|
|
72
360
|
finalVersion = await fetchLatestVersion(pkg);
|
|
73
361
|
setVersionCache(pkg, finalVersion);
|
|
74
362
|
}
|
|
75
363
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
];
|
|
364
|
+
return finalVersion;
|
|
365
|
+
}
|
|
366
|
+
function buildFinalUrls(pkg, version, localFallback) {
|
|
367
|
+
const urls = buildCdnUrls(pkg, version);
|
|
81
368
|
if (localFallback) urls.push(localFallback);
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
369
|
+
return urls;
|
|
370
|
+
}
|
|
371
|
+
async function loadRemoteMultiVersion(options, plugins) {
|
|
372
|
+
const { name, pkg, version = 'latest', retries = 3, delay = 1000, localFallback, cacheTTL = 86400000, revalidate = true, shared: customShared } = options;
|
|
373
|
+
const finalVersion = await resolveFinalVersion(pkg, version, cacheTTL, revalidate);
|
|
374
|
+
const scopeName = `${name}`;
|
|
375
|
+
const urls = buildFinalUrls(pkg, finalVersion, localFallback);
|
|
376
|
+
const finalSharedConfig = getFinalSharedConfig(customShared);
|
|
377
|
+
for (const url of urls)try {
|
|
378
|
+
return await tryLoadRemote(scopeName, url, retries, delay, finalSharedConfig, plugins);
|
|
379
|
+
} catch (e) {
|
|
380
|
+
console.warn(`[MF] 切换 CDN 路径:${url} 失败,尝试下一个...`, e);
|
|
381
|
+
}
|
|
382
|
+
throw new Error(`[MF] 所有加载源 (${urls.length} 个) 均加载失败。`);
|
|
383
|
+
}
|
|
384
|
+
const preloadCache = {};
|
|
385
|
+
const PRELOAD_CACHE_TTL = 300000;
|
|
386
|
+
function getCachedPreload(pkg, version) {
|
|
387
|
+
const cached = preloadCache[pkg];
|
|
388
|
+
if (!cached) return null;
|
|
389
|
+
if (cached.version !== version) return null;
|
|
390
|
+
if (Date.now() - cached.timestamp > PRELOAD_CACHE_TTL) {
|
|
391
|
+
delete preloadCache[pkg];
|
|
392
|
+
return null;
|
|
393
|
+
}
|
|
394
|
+
return {
|
|
395
|
+
scopeName: cached.scopeName,
|
|
396
|
+
mf: cached.mf
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
function setCachedPreload(pkg, version, scopeName, mf) {
|
|
400
|
+
preloadCache[pkg] = {
|
|
401
|
+
version,
|
|
402
|
+
scopeName,
|
|
403
|
+
mf,
|
|
404
|
+
timestamp: Date.now()
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
function executeWhenIdle(callback) {
|
|
408
|
+
if ('undefined' != typeof requestIdleCallback) requestIdleCallback(()=>callback());
|
|
409
|
+
else setTimeout(callback, 1);
|
|
410
|
+
}
|
|
411
|
+
async function preloadRemote(options) {
|
|
412
|
+
const { pkg, version = 'latest', priority = 'idle', force = false } = options;
|
|
413
|
+
if (!force) {
|
|
414
|
+
const cached = getCachedPreload(pkg, version);
|
|
415
|
+
if (cached) return cached;
|
|
416
|
+
}
|
|
417
|
+
const preloadTask = async ()=>{
|
|
418
|
+
try {
|
|
419
|
+
const { scopeName, mf } = await loadRemoteMultiVersion(options, []);
|
|
420
|
+
setCachedPreload(pkg, version, scopeName, mf);
|
|
421
|
+
return {
|
|
422
|
+
scopeName,
|
|
423
|
+
mf
|
|
424
|
+
};
|
|
425
|
+
} catch (e) {
|
|
426
|
+
console.warn(`[MF Preload] 预加载失败 ${pkg}@${version}:`, e);
|
|
427
|
+
return null;
|
|
428
|
+
}
|
|
429
|
+
};
|
|
430
|
+
if ('high' === priority) return preloadTask();
|
|
431
|
+
return new Promise((resolve)=>{
|
|
432
|
+
executeWhenIdle(async ()=>{
|
|
433
|
+
const result = await preloadTask();
|
|
434
|
+
resolve(result);
|
|
435
|
+
});
|
|
436
|
+
});
|
|
437
|
+
}
|
|
438
|
+
function preloadRemoteList(optionsList, onProgress) {
|
|
439
|
+
let loaded = 0;
|
|
440
|
+
const total = optionsList.length;
|
|
441
|
+
const promises = optionsList.map((options)=>preloadRemote(options).then((result)=>{
|
|
442
|
+
loaded++;
|
|
443
|
+
onProgress?.(loaded, total);
|
|
444
|
+
return result;
|
|
445
|
+
}));
|
|
446
|
+
return Promise.all(promises);
|
|
447
|
+
}
|
|
448
|
+
function cancelPreload(pkg) {
|
|
449
|
+
const cached = preloadCache[pkg];
|
|
450
|
+
if (cached) delete preloadCache[pkg];
|
|
451
|
+
}
|
|
452
|
+
function clearPreloadCache() {
|
|
453
|
+
Object.keys(preloadCache).forEach((pkg)=>delete preloadCache[pkg]);
|
|
454
|
+
}
|
|
455
|
+
function getPreloadStatus(pkg) {
|
|
456
|
+
const cached = preloadCache[pkg];
|
|
457
|
+
if (!cached) return null;
|
|
458
|
+
return {
|
|
459
|
+
loaded: true,
|
|
460
|
+
timestamp: cached.timestamp
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
const remoteInstances = new Map();
|
|
464
|
+
function generateInstanceKey(name, pkg, version) {
|
|
465
|
+
return `${name}::${pkg}@${version}`;
|
|
466
|
+
}
|
|
467
|
+
async function unloadRemote(options) {
|
|
468
|
+
const { name, pkg, version = '*', clearCache = false } = options;
|
|
469
|
+
const keysToDelete = [];
|
|
470
|
+
remoteInstances.forEach((instance, key)=>{
|
|
471
|
+
const versionMatch = '*' === version || instance.version === version;
|
|
472
|
+
const pkgMatch = instance.pkg === pkg;
|
|
473
|
+
const nameMatch = instance.name === name;
|
|
474
|
+
if (nameMatch && pkgMatch && versionMatch) keysToDelete.push(key);
|
|
475
|
+
});
|
|
476
|
+
for (const key of keysToDelete){
|
|
477
|
+
const instance = remoteInstances.get(key);
|
|
478
|
+
if (instance) {
|
|
479
|
+
await cleanupInstance(instance);
|
|
480
|
+
remoteInstances.delete(key);
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
if (clearCache) clearVersionCache(pkg, version);
|
|
484
|
+
return keysToDelete.length > 0;
|
|
485
|
+
}
|
|
486
|
+
async function cleanupInstance(instance) {
|
|
487
|
+
try {
|
|
488
|
+
instance.loadedModules.clear();
|
|
489
|
+
if (instance.mf && 'function' == typeof instance.mf.cleanup) await instance.mf.cleanup();
|
|
490
|
+
console.log(`[MF Unload] 已卸载 ${instance.pkg}@${instance.version}`);
|
|
491
|
+
} catch (e) {
|
|
492
|
+
console.warn(`[MF Unload] 卸载时出错 ${instance.pkg}:`, e);
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
function clearVersionCache(pkg, version) {
|
|
496
|
+
try {
|
|
497
|
+
const cacheKey = 'mf-multi-version';
|
|
498
|
+
const cacheStr = localStorage.getItem(cacheKey);
|
|
499
|
+
if (!cacheStr) return;
|
|
500
|
+
const cache = JSON.parse(cacheStr);
|
|
501
|
+
if (!cache[pkg]) return;
|
|
502
|
+
if ('*' === version) delete cache[pkg];
|
|
503
|
+
else delete cache[pkg][version];
|
|
504
|
+
localStorage.setItem(cacheKey, JSON.stringify(cache));
|
|
505
|
+
console.log(`[MF Unload] 已清除版本缓存 ${pkg}@${version}`);
|
|
506
|
+
} catch (e) {
|
|
507
|
+
console.warn('[MF Unload] 清除缓存失败:', e);
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
function registerRemoteInstance(name, scopeName, pkg, version, mf) {
|
|
511
|
+
const key = generateInstanceKey(name, pkg, version);
|
|
512
|
+
const instance = {
|
|
513
|
+
name,
|
|
514
|
+
scopeName,
|
|
515
|
+
pkg,
|
|
516
|
+
version,
|
|
517
|
+
mf,
|
|
518
|
+
loadedModules: new Set(),
|
|
519
|
+
timestamp: Date.now()
|
|
520
|
+
};
|
|
521
|
+
remoteInstances.set(key, instance);
|
|
522
|
+
return key;
|
|
523
|
+
}
|
|
524
|
+
function registerLoadedModule(key, moduleId) {
|
|
525
|
+
const instance = remoteInstances.get(key);
|
|
526
|
+
if (instance) instance.loadedModules.add(moduleId);
|
|
527
|
+
}
|
|
528
|
+
function unloadAll(clearAllCache = false) {
|
|
529
|
+
return new Promise((resolve)=>{
|
|
530
|
+
const keys = Array.from(remoteInstances.keys());
|
|
531
|
+
if (0 === keys.length) return void resolve();
|
|
532
|
+
let completed = 0;
|
|
533
|
+
keys.forEach(async (key)=>{
|
|
534
|
+
const instance = remoteInstances.get(key);
|
|
535
|
+
if (instance) {
|
|
536
|
+
await cleanupInstance(instance);
|
|
537
|
+
remoteInstances.delete(key);
|
|
538
|
+
}
|
|
539
|
+
completed++;
|
|
540
|
+
if (completed >= keys.length) {
|
|
541
|
+
if (clearAllCache) try {
|
|
542
|
+
localStorage.removeItem('mf-multi-version');
|
|
543
|
+
} catch (e) {
|
|
544
|
+
console.warn('[MF Unload] 清除所有缓存失败:', e);
|
|
105
545
|
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
...plugins,
|
|
109
|
-
fallbackPlugin()
|
|
110
|
-
]
|
|
546
|
+
resolve();
|
|
547
|
+
}
|
|
111
548
|
});
|
|
549
|
+
});
|
|
550
|
+
}
|
|
551
|
+
function getLoadedRemotes() {
|
|
552
|
+
const result = [];
|
|
553
|
+
remoteInstances.forEach((instance)=>{
|
|
554
|
+
result.push({
|
|
555
|
+
name: instance.name,
|
|
556
|
+
pkg: instance.pkg,
|
|
557
|
+
version: instance.version,
|
|
558
|
+
loadedModules: instance.loadedModules.size,
|
|
559
|
+
timestamp: instance.timestamp
|
|
560
|
+
});
|
|
561
|
+
});
|
|
562
|
+
return result;
|
|
563
|
+
}
|
|
564
|
+
function isRemoteLoaded(name, pkg, version) {
|
|
565
|
+
const key = generateInstanceKey(name, pkg, version || '*');
|
|
566
|
+
return remoteInstances.has(key);
|
|
567
|
+
}
|
|
568
|
+
const CDN_URLS = [
|
|
569
|
+
'https://cdn.jsdelivr.net/npm/{pkg}@{version}/dist/remoteEntry.js',
|
|
570
|
+
'https://unpkg.com/{pkg}@{version}/dist/remoteEntry.js'
|
|
571
|
+
];
|
|
572
|
+
async function checkCdnAccess(cdnUrl) {
|
|
573
|
+
const start = performance.now();
|
|
574
|
+
try {
|
|
575
|
+
const controller = new AbortController();
|
|
576
|
+
const timeout = setTimeout(()=>controller.abort(), 5000);
|
|
577
|
+
const res = await fetch(cdnUrl, {
|
|
578
|
+
method: 'HEAD',
|
|
579
|
+
signal: controller.signal
|
|
580
|
+
});
|
|
581
|
+
clearTimeout(timeout);
|
|
582
|
+
const latency = Math.round(performance.now() - start);
|
|
112
583
|
return {
|
|
113
|
-
|
|
114
|
-
|
|
584
|
+
reachable: res.ok,
|
|
585
|
+
latency
|
|
586
|
+
};
|
|
587
|
+
} catch (e) {
|
|
588
|
+
const latency = Math.round(performance.now() - start);
|
|
589
|
+
return {
|
|
590
|
+
reachable: false,
|
|
591
|
+
latency
|
|
115
592
|
};
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
async function health_fetchLatestVersion(pkg) {
|
|
596
|
+
try {
|
|
597
|
+
const res = await fetch(`https://registry.npmjs.org/${pkg}`);
|
|
598
|
+
if (!res.ok) return null;
|
|
599
|
+
const data = await res.json();
|
|
600
|
+
return data['dist-tags']?.latest || null;
|
|
116
601
|
} catch {
|
|
117
|
-
|
|
602
|
+
return null;
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
async function checkRemoteHealth(options) {
|
|
606
|
+
const { pkg, version = 'latest' } = options;
|
|
607
|
+
const actualVersion = 'latest' === version ? await health_fetchLatestVersion(pkg) || version : version;
|
|
608
|
+
const results = [];
|
|
609
|
+
for (const template of CDN_URLS){
|
|
610
|
+
const url = template.replace('{pkg}', pkg).replace('{version}', actualVersion);
|
|
611
|
+
const result = await checkCdnAccess(url);
|
|
612
|
+
results.push({
|
|
613
|
+
cdn: url,
|
|
614
|
+
...result
|
|
615
|
+
});
|
|
616
|
+
}
|
|
617
|
+
const workingCdn = results.find((r)=>r.reachable);
|
|
618
|
+
const bestCdn = results.sort((a, b)=>a.latency - b.latency)[0];
|
|
619
|
+
let status = 'unhealthy';
|
|
620
|
+
if (workingCdn) status = bestCdn.latency < 1000 ? 'healthy' : 'degraded';
|
|
621
|
+
return {
|
|
622
|
+
pkg,
|
|
623
|
+
version: actualVersion,
|
|
624
|
+
status,
|
|
625
|
+
latency: bestCdn?.latency || 0,
|
|
626
|
+
cdn: bestCdn?.cdn || '',
|
|
627
|
+
details: {
|
|
628
|
+
cdnReachable: !!workingCdn,
|
|
629
|
+
remoteEntryValid: false,
|
|
630
|
+
modulesLoadable: false
|
|
631
|
+
}
|
|
632
|
+
};
|
|
633
|
+
}
|
|
634
|
+
async function checkModuleLoadable(scopeName, modulePath, mf) {
|
|
635
|
+
try {
|
|
636
|
+
if (!mf || 'function' != typeof mf.loadRemote) return false;
|
|
637
|
+
const mod = await mf.loadRemote(`${scopeName}/${modulePath}`);
|
|
638
|
+
return null != mod;
|
|
639
|
+
} catch {
|
|
640
|
+
return false;
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
async function getRemoteHealthReport(remotes) {
|
|
644
|
+
const results = await Promise.all(remotes.map((r)=>checkRemoteHealth(r)));
|
|
645
|
+
let overall = 'healthy';
|
|
646
|
+
if (results.some((r)=>'unhealthy' === r.status)) overall = 'unhealthy';
|
|
647
|
+
else if (results.some((r)=>'degraded' === r.status)) overall = 'degraded';
|
|
648
|
+
return {
|
|
649
|
+
timestamp: Date.now(),
|
|
650
|
+
overall,
|
|
651
|
+
remotes: results
|
|
652
|
+
};
|
|
653
|
+
}
|
|
654
|
+
function formatHealthStatus(status) {
|
|
655
|
+
const icons = {
|
|
656
|
+
healthy: '🟢',
|
|
657
|
+
degraded: '🟡',
|
|
658
|
+
unhealthy: '🔴'
|
|
659
|
+
};
|
|
660
|
+
return `${icons[status]} ${status}`;
|
|
661
|
+
}
|
|
662
|
+
class EventBusClass {
|
|
663
|
+
listeners = new Map();
|
|
664
|
+
eventHistory = new Map();
|
|
665
|
+
maxHistorySize = 100;
|
|
666
|
+
generateId() {
|
|
667
|
+
return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
118
668
|
}
|
|
119
|
-
|
|
669
|
+
on(event, callback, options) {
|
|
670
|
+
const onceValue = options?.once ?? false;
|
|
671
|
+
if (!this.listeners.has(event)) this.listeners.set(event, new Set());
|
|
672
|
+
const subscription = {
|
|
673
|
+
callback,
|
|
674
|
+
once: onceValue,
|
|
675
|
+
filter: options?.filter
|
|
676
|
+
};
|
|
677
|
+
this.listeners.get(event).add(subscription);
|
|
678
|
+
return ()=>{
|
|
679
|
+
this.off(event, callback);
|
|
680
|
+
};
|
|
681
|
+
}
|
|
682
|
+
once(event, callback) {
|
|
683
|
+
return this.on(event, callback, {
|
|
684
|
+
once: true
|
|
685
|
+
});
|
|
686
|
+
}
|
|
687
|
+
off(event, callback) {
|
|
688
|
+
if (!this.listeners.has(event)) return;
|
|
689
|
+
if (callback) {
|
|
690
|
+
const subscriptions = this.listeners.get(event);
|
|
691
|
+
subscriptions.forEach((sub)=>{
|
|
692
|
+
if (sub.callback === callback) subscriptions.delete(sub);
|
|
693
|
+
});
|
|
694
|
+
} else this.listeners.delete(event);
|
|
695
|
+
}
|
|
696
|
+
emit(event, data, meta) {
|
|
697
|
+
const eventMeta = {
|
|
698
|
+
timestamp: Date.now(),
|
|
699
|
+
source: meta?.source,
|
|
700
|
+
id: meta?.id || this.generateId()
|
|
701
|
+
};
|
|
702
|
+
this.addToHistory(event, data, eventMeta);
|
|
703
|
+
const subscriptions = this.listeners.get(event);
|
|
704
|
+
if (!subscriptions) return;
|
|
705
|
+
const toRemove = [];
|
|
706
|
+
subscriptions.forEach((sub)=>{
|
|
707
|
+
if (sub.filter && !sub.filter(data, eventMeta)) return;
|
|
708
|
+
try {
|
|
709
|
+
sub.callback(data, eventMeta);
|
|
710
|
+
} catch (e) {
|
|
711
|
+
console.error(`[MF EventBus] 事件 ${event} 处理出错:`, e);
|
|
712
|
+
}
|
|
713
|
+
if (sub.once) toRemove.push(sub);
|
|
714
|
+
});
|
|
715
|
+
toRemove.forEach((sub)=>subscriptions.delete(sub));
|
|
716
|
+
}
|
|
717
|
+
addToHistory(event, data, meta) {
|
|
718
|
+
if (!this.eventHistory.has(event)) this.eventHistory.set(event, []);
|
|
719
|
+
const history = this.eventHistory.get(event);
|
|
720
|
+
history.push({
|
|
721
|
+
data,
|
|
722
|
+
meta
|
|
723
|
+
});
|
|
724
|
+
if (history.length > this.maxHistorySize) history.shift();
|
|
725
|
+
}
|
|
726
|
+
getHistory(event) {
|
|
727
|
+
return this.eventHistory.get(event) || [];
|
|
728
|
+
}
|
|
729
|
+
getEvents() {
|
|
730
|
+
return Array.from(this.listeners.keys());
|
|
731
|
+
}
|
|
732
|
+
getListenerCount(event) {
|
|
733
|
+
return this.listeners.get(event)?.size || 0;
|
|
734
|
+
}
|
|
735
|
+
hasListeners(event) {
|
|
736
|
+
return this.listeners.has(event) && this.listeners.get(event).size > 0;
|
|
737
|
+
}
|
|
738
|
+
clear(event) {
|
|
739
|
+
if (event) {
|
|
740
|
+
this.listeners.delete(event);
|
|
741
|
+
this.eventHistory.delete(event);
|
|
742
|
+
} else {
|
|
743
|
+
this.listeners.clear();
|
|
744
|
+
this.eventHistory.clear();
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
listenerExists(event, callback) {
|
|
748
|
+
const subscriptions = this.listeners.get(event);
|
|
749
|
+
if (!subscriptions) return false;
|
|
750
|
+
let exists = false;
|
|
751
|
+
subscriptions.forEach((sub)=>{
|
|
752
|
+
if (sub.callback === callback) exists = true;
|
|
753
|
+
});
|
|
754
|
+
return exists;
|
|
755
|
+
}
|
|
756
|
+
emitAsync(event, data, meta) {
|
|
757
|
+
return new Promise((resolve)=>{
|
|
758
|
+
this.emit(event, data, meta);
|
|
759
|
+
resolve();
|
|
760
|
+
});
|
|
761
|
+
}
|
|
762
|
+
static create() {
|
|
763
|
+
return new EventBusClass();
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
const eventBus = EventBusClass.create();
|
|
767
|
+
function createEventBus() {
|
|
768
|
+
return EventBusClass.create();
|
|
769
|
+
}
|
|
770
|
+
const external_react_namespaceObject = require("react");
|
|
771
|
+
var external_react_default = /*#__PURE__*/ __webpack_require__.n(external_react_namespaceObject);
|
|
772
|
+
class ErrorBoundary extends external_react_default().Component {
|
|
773
|
+
constructor(props){
|
|
774
|
+
super(props);
|
|
775
|
+
this.state = {
|
|
776
|
+
hasError: false,
|
|
777
|
+
error: null,
|
|
778
|
+
errorInfo: null
|
|
779
|
+
};
|
|
780
|
+
}
|
|
781
|
+
static getDerivedStateFromError(error) {
|
|
782
|
+
return {
|
|
783
|
+
hasError: true,
|
|
784
|
+
error,
|
|
785
|
+
errorInfo: null
|
|
786
|
+
};
|
|
787
|
+
}
|
|
788
|
+
componentDidCatch(error, errorInfo) {
|
|
789
|
+
this.setState({
|
|
790
|
+
errorInfo
|
|
791
|
+
});
|
|
792
|
+
this.props.onError?.(error, errorInfo);
|
|
793
|
+
console.error('[RemoteReloadUtils] ErrorBoundary caught error:', error, errorInfo);
|
|
794
|
+
}
|
|
795
|
+
handleReset = ()=>{
|
|
796
|
+
this.setState({
|
|
797
|
+
hasError: false,
|
|
798
|
+
error: null,
|
|
799
|
+
errorInfo: null
|
|
800
|
+
});
|
|
801
|
+
this.props.onReset?.();
|
|
802
|
+
};
|
|
803
|
+
render() {
|
|
804
|
+
if (this.state.hasError && this.state.error) {
|
|
805
|
+
if ('function' == typeof this.props.fallback) return this.props.fallback(this.state.error, this.handleReset);
|
|
806
|
+
if (void 0 !== this.props.fallback) return this.props.fallback;
|
|
807
|
+
return /*#__PURE__*/ external_react_default().createElement("div", {
|
|
808
|
+
role: "alert",
|
|
809
|
+
style: {
|
|
810
|
+
padding: '16px',
|
|
811
|
+
border: '1px solid #ffcccc',
|
|
812
|
+
backgroundColor: '#fff5f5',
|
|
813
|
+
borderRadius: '4px'
|
|
814
|
+
}
|
|
815
|
+
}, /*#__PURE__*/ external_react_default().createElement("h3", null, "Something went wrong"), /*#__PURE__*/ external_react_default().createElement("p", null, this.state.error.message), /*#__PURE__*/ external_react_default().createElement("button", {
|
|
816
|
+
onClick: this.handleReset,
|
|
817
|
+
style: {
|
|
818
|
+
marginTop: '8px',
|
|
819
|
+
padding: '8px 16px',
|
|
820
|
+
cursor: 'pointer'
|
|
821
|
+
}
|
|
822
|
+
}, "Try again"));
|
|
823
|
+
}
|
|
824
|
+
return this.props.children;
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
function useRemoteModule({ pkg, version, moduleName, scopeName, onError, onLoad }) {
|
|
828
|
+
const [moduleState, setModuleState] = (0, external_react_namespaceObject.useState)({
|
|
829
|
+
loading: true,
|
|
830
|
+
error: null,
|
|
831
|
+
component: null
|
|
832
|
+
});
|
|
833
|
+
(0, external_react_namespaceObject.useEffect)(()=>{
|
|
834
|
+
let mounted = true;
|
|
835
|
+
async function loadModule() {
|
|
836
|
+
try {
|
|
837
|
+
setModuleState((prev)=>({
|
|
838
|
+
...prev,
|
|
839
|
+
loading: true,
|
|
840
|
+
error: null
|
|
841
|
+
}));
|
|
842
|
+
const { mf } = await loadRemoteMultiVersion({
|
|
843
|
+
name: scopeName,
|
|
844
|
+
pkg,
|
|
845
|
+
version
|
|
846
|
+
}, []);
|
|
847
|
+
if (!mf || !mounted) return;
|
|
848
|
+
const mod = await mf.loadRemote(`${scopeName}/${moduleName}`);
|
|
849
|
+
if (!mounted) return;
|
|
850
|
+
if (mod && 'object' == typeof mod && 'default' in mod) {
|
|
851
|
+
const Component = mod.default;
|
|
852
|
+
setModuleState({
|
|
853
|
+
loading: false,
|
|
854
|
+
error: null,
|
|
855
|
+
component: Component
|
|
856
|
+
});
|
|
857
|
+
onLoad?.(Component);
|
|
858
|
+
} else throw new Error(`Module "${scopeName}/${moduleName}" does not export a default component`);
|
|
859
|
+
} catch (err) {
|
|
860
|
+
if (mounted) {
|
|
861
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
862
|
+
setModuleState({
|
|
863
|
+
loading: false,
|
|
864
|
+
error,
|
|
865
|
+
component: null
|
|
866
|
+
});
|
|
867
|
+
onError?.(error);
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
loadModule();
|
|
872
|
+
return ()=>{
|
|
873
|
+
mounted = false;
|
|
874
|
+
};
|
|
875
|
+
}, [
|
|
876
|
+
pkg,
|
|
877
|
+
version,
|
|
878
|
+
moduleName,
|
|
879
|
+
scopeName,
|
|
880
|
+
onError,
|
|
881
|
+
onLoad
|
|
882
|
+
]);
|
|
883
|
+
return moduleState;
|
|
884
|
+
}
|
|
885
|
+
function RemoteModuleCardContent({ pkg, version, moduleName, scopeName, loadingFallback, errorFallback, componentProps, className, style, onError, onLoad }) {
|
|
886
|
+
const moduleState = useRemoteModule({
|
|
887
|
+
pkg,
|
|
888
|
+
version,
|
|
889
|
+
moduleName,
|
|
890
|
+
scopeName,
|
|
891
|
+
onError,
|
|
892
|
+
onLoad
|
|
893
|
+
});
|
|
894
|
+
const [retryCount, setRetryCount] = (0, external_react_namespaceObject.useState)(0);
|
|
895
|
+
const handleRetry = (0, external_react_namespaceObject.useCallback)(()=>{
|
|
896
|
+
setRetryCount((prev)=>prev + 1);
|
|
897
|
+
}, []);
|
|
898
|
+
(0, external_react_namespaceObject.useEffect)(()=>{}, [
|
|
899
|
+
retryCount
|
|
900
|
+
]);
|
|
901
|
+
if (moduleState.loading) return /*#__PURE__*/ external_react_default().createElement("div", {
|
|
902
|
+
className: className,
|
|
903
|
+
style: style,
|
|
904
|
+
role: "status",
|
|
905
|
+
"aria-live": "polite"
|
|
906
|
+
}, loadingFallback || /*#__PURE__*/ external_react_default().createElement("div", {
|
|
907
|
+
className: "module-card module-card--loading"
|
|
908
|
+
}, /*#__PURE__*/ external_react_default().createElement("div", {
|
|
909
|
+
className: "loading-spinner",
|
|
910
|
+
"aria-hidden": "true"
|
|
911
|
+
}), /*#__PURE__*/ external_react_default().createElement("span", null, "Loading ", moduleName, "...")));
|
|
912
|
+
if (moduleState.error) {
|
|
913
|
+
if ('function' == typeof errorFallback) return /*#__PURE__*/ external_react_default().createElement(external_react_default().Fragment, null, errorFallback(moduleState.error, handleRetry));
|
|
914
|
+
if (void 0 !== errorFallback) return /*#__PURE__*/ external_react_default().createElement(external_react_default().Fragment, null, errorFallback);
|
|
915
|
+
return /*#__PURE__*/ external_react_default().createElement("div", {
|
|
916
|
+
className: className,
|
|
917
|
+
style: style,
|
|
918
|
+
role: "alert"
|
|
919
|
+
}, /*#__PURE__*/ external_react_default().createElement("div", {
|
|
920
|
+
className: "module-card module-card--error"
|
|
921
|
+
}, /*#__PURE__*/ external_react_default().createElement("span", {
|
|
922
|
+
className: "error-icon",
|
|
923
|
+
"aria-hidden": "true"
|
|
924
|
+
}, "!"), /*#__PURE__*/ external_react_default().createElement("span", null, "Failed to load ", moduleName), /*#__PURE__*/ external_react_default().createElement("p", {
|
|
925
|
+
className: "error-message"
|
|
926
|
+
}, moduleState.error.message), /*#__PURE__*/ external_react_default().createElement("button", {
|
|
927
|
+
onClick: handleRetry,
|
|
928
|
+
className: "retry-button",
|
|
929
|
+
type: "button"
|
|
930
|
+
}, "Retry")));
|
|
931
|
+
}
|
|
932
|
+
if (!moduleState.component) return null;
|
|
933
|
+
const Component = moduleState.component;
|
|
934
|
+
return /*#__PURE__*/ external_react_default().createElement("div", {
|
|
935
|
+
className: className,
|
|
936
|
+
style: style
|
|
937
|
+
}, /*#__PURE__*/ external_react_default().createElement(Component, componentProps));
|
|
938
|
+
}
|
|
939
|
+
function RemoteModuleCard(props) {
|
|
940
|
+
const { disableErrorBoundary, errorFallback, loadingFallback, errorBoundaryOptions } = props;
|
|
941
|
+
if (disableErrorBoundary) return /*#__PURE__*/ external_react_default().createElement(external_react_namespaceObject.Suspense, {
|
|
942
|
+
fallback: loadingFallback || /*#__PURE__*/ external_react_default().createElement("div", null, "Loading...")
|
|
943
|
+
}, /*#__PURE__*/ external_react_default().createElement(RemoteModuleCardContent, props));
|
|
944
|
+
return /*#__PURE__*/ external_react_default().createElement(ErrorBoundary, {
|
|
945
|
+
fallback: errorFallback,
|
|
946
|
+
onError: errorBoundaryOptions?.onError,
|
|
947
|
+
onReset: errorBoundaryOptions?.onReset
|
|
948
|
+
}, /*#__PURE__*/ external_react_default().createElement(external_react_namespaceObject.Suspense, {
|
|
949
|
+
fallback: loadingFallback || /*#__PURE__*/ external_react_default().createElement("div", null, "Loading...")
|
|
950
|
+
}, /*#__PURE__*/ external_react_default().createElement(RemoteModuleCardContent, props)));
|
|
951
|
+
}
|
|
952
|
+
function lazyRemote(options) {
|
|
953
|
+
const { pkg, version = 'latest', moduleName, scopeName, maxRetries = 3, retryDelay = 1000 } = options;
|
|
954
|
+
let retryCount = 0;
|
|
955
|
+
const loadComponent = async ()=>{
|
|
956
|
+
try {
|
|
957
|
+
const { mf } = await loadRemoteMultiVersion({
|
|
958
|
+
name: scopeName,
|
|
959
|
+
pkg,
|
|
960
|
+
version
|
|
961
|
+
}, []);
|
|
962
|
+
if (!mf) throw new Error(`[RemoteReloadUtils] Failed to get Module Federation instance for ${scopeName}`);
|
|
963
|
+
const mod = await mf.loadRemote(`${scopeName}/${moduleName}`);
|
|
964
|
+
if (!mod || 'object' != typeof mod || !('default' in mod)) throw new Error(`[RemoteReloadUtils] Module "${scopeName}/${moduleName}" does not export a default component`);
|
|
965
|
+
return {
|
|
966
|
+
default: mod.default
|
|
967
|
+
};
|
|
968
|
+
} catch (error) {
|
|
969
|
+
if (retryCount < maxRetries) {
|
|
970
|
+
retryCount++;
|
|
971
|
+
await new Promise((resolve)=>setTimeout(resolve, retryDelay * retryCount));
|
|
972
|
+
return loadComponent();
|
|
973
|
+
}
|
|
974
|
+
throw error;
|
|
975
|
+
}
|
|
976
|
+
};
|
|
977
|
+
return /*#__PURE__*/ (0, external_react_namespaceObject.lazy)(loadComponent);
|
|
978
|
+
}
|
|
979
|
+
function SuspenseRemote({ fallback, children }) {
|
|
980
|
+
return /*#__PURE__*/ external_react_default().createElement(external_react_namespaceObject.Suspense, {
|
|
981
|
+
fallback: fallback || /*#__PURE__*/ external_react_default().createElement("div", null, "Loading...")
|
|
982
|
+
}, children);
|
|
983
|
+
}
|
|
984
|
+
function SuspenseRemoteLoader({ pkg, version = 'latest', moduleName, scopeName, fallback, errorFallback, componentProps }) {
|
|
985
|
+
const RemoteComponent = lazyRemote({
|
|
986
|
+
pkg,
|
|
987
|
+
version,
|
|
988
|
+
moduleName,
|
|
989
|
+
scopeName
|
|
990
|
+
});
|
|
991
|
+
if (errorFallback) return /*#__PURE__*/ external_react_default().createElement(ErrorBoundary, {
|
|
992
|
+
fallback: errorFallback
|
|
993
|
+
}, /*#__PURE__*/ external_react_default().createElement(external_react_namespaceObject.Suspense, {
|
|
994
|
+
fallback: fallback || /*#__PURE__*/ external_react_default().createElement("div", null, "Loading...")
|
|
995
|
+
}, /*#__PURE__*/ external_react_default().createElement(RemoteComponent, componentProps)));
|
|
996
|
+
return /*#__PURE__*/ external_react_default().createElement(external_react_namespaceObject.Suspense, {
|
|
997
|
+
fallback: fallback || /*#__PURE__*/ external_react_default().createElement("div", null, "Loading...")
|
|
998
|
+
}, /*#__PURE__*/ external_react_default().createElement(RemoteComponent, componentProps));
|
|
999
|
+
}
|
|
1000
|
+
function withRemote(WrappedComponent, options) {
|
|
1001
|
+
const RemoteComponent = lazyRemote(options);
|
|
1002
|
+
return function(props) {
|
|
1003
|
+
return /*#__PURE__*/ external_react_default().createElement(external_react_namespaceObject.Suspense, {
|
|
1004
|
+
fallback: /*#__PURE__*/ external_react_default().createElement("div", null, "Loading...")
|
|
1005
|
+
}, /*#__PURE__*/ external_react_default().createElement(RemoteComponent, props));
|
|
1006
|
+
};
|
|
1007
|
+
}
|
|
1008
|
+
function useRemoteModuleHook(options) {
|
|
1009
|
+
const { pkg, version = 'latest', moduleName, scopeName } = options;
|
|
1010
|
+
const [state, setState] = (0, external_react_namespaceObject.useState)({
|
|
1011
|
+
component: null,
|
|
1012
|
+
loading: true,
|
|
1013
|
+
error: null
|
|
1014
|
+
});
|
|
1015
|
+
(0, external_react_namespaceObject.useEffect)(()=>{
|
|
1016
|
+
let mounted = true;
|
|
1017
|
+
async function load() {
|
|
1018
|
+
try {
|
|
1019
|
+
setState((prev)=>({
|
|
1020
|
+
...prev,
|
|
1021
|
+
loading: true,
|
|
1022
|
+
error: null
|
|
1023
|
+
}));
|
|
1024
|
+
const { mf } = await loadRemoteMultiVersion({
|
|
1025
|
+
name: scopeName,
|
|
1026
|
+
pkg,
|
|
1027
|
+
version
|
|
1028
|
+
}, []);
|
|
1029
|
+
if (!mf) throw new Error(`[RemoteReloadUtils] Failed to get Module Federation instance for ${scopeName}`);
|
|
1030
|
+
const mod = await mf.loadRemote(`${scopeName}/${moduleName}`);
|
|
1031
|
+
if (mounted) if (mod && 'object' == typeof mod && 'default' in mod) setState({
|
|
1032
|
+
component: mod.default,
|
|
1033
|
+
loading: false,
|
|
1034
|
+
error: null
|
|
1035
|
+
});
|
|
1036
|
+
else throw new Error(`[RemoteReloadUtils] Module "${scopeName}/${moduleName}" does not export a default component`);
|
|
1037
|
+
} catch (err) {
|
|
1038
|
+
if (mounted) setState({
|
|
1039
|
+
component: null,
|
|
1040
|
+
loading: false,
|
|
1041
|
+
error: err instanceof Error ? err : new Error(String(err))
|
|
1042
|
+
});
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
load();
|
|
1046
|
+
return ()=>{
|
|
1047
|
+
mounted = false;
|
|
1048
|
+
};
|
|
1049
|
+
}, [
|
|
1050
|
+
pkg,
|
|
1051
|
+
version,
|
|
1052
|
+
moduleName,
|
|
1053
|
+
scopeName
|
|
1054
|
+
]);
|
|
1055
|
+
return {
|
|
1056
|
+
component: state.component,
|
|
1057
|
+
loading: state.loading,
|
|
1058
|
+
error: state.error
|
|
1059
|
+
};
|
|
120
1060
|
}
|
|
1061
|
+
exports.ErrorBoundary = __webpack_exports__.ErrorBoundary;
|
|
1062
|
+
exports.RemoteModuleCard = __webpack_exports__.RemoteModuleCard;
|
|
1063
|
+
exports.SuspenseRemote = __webpack_exports__.SuspenseRemote;
|
|
1064
|
+
exports.SuspenseRemoteLoader = __webpack_exports__.SuspenseRemoteLoader;
|
|
1065
|
+
exports.buildCdnUrls = __webpack_exports__.buildCdnUrls;
|
|
1066
|
+
exports.buildFinalUrls = __webpack_exports__.buildFinalUrls;
|
|
1067
|
+
exports.cancelPreload = __webpack_exports__.cancelPreload;
|
|
1068
|
+
exports.checkModuleLoadable = __webpack_exports__.checkModuleLoadable;
|
|
1069
|
+
exports.checkRemoteHealth = __webpack_exports__.checkRemoteHealth;
|
|
1070
|
+
exports.checkVersionCompatibility = __webpack_exports__.checkVersionCompatibility;
|
|
1071
|
+
exports.clearPreloadCache = __webpack_exports__.clearPreloadCache;
|
|
1072
|
+
exports.compareVersions = __webpack_exports__.compareVersions;
|
|
1073
|
+
exports.createEventBus = __webpack_exports__.createEventBus;
|
|
1074
|
+
exports.eventBus = __webpack_exports__.eventBus;
|
|
1075
|
+
exports.extractMajorVersion = __webpack_exports__.extractMajorVersion;
|
|
1076
|
+
exports.fallbackPlugin = __webpack_exports__.fallbackPlugin;
|
|
1077
|
+
exports.fetchAvailableVersions = __webpack_exports__.fetchAvailableVersions;
|
|
1078
|
+
exports.fetchLatestVersion = __webpack_exports__.fetchLatestVersion;
|
|
1079
|
+
exports.findCompatibleVersion = __webpack_exports__.findCompatibleVersion;
|
|
1080
|
+
exports.formatHealthStatus = __webpack_exports__.formatHealthStatus;
|
|
1081
|
+
exports.getCompatibleReactVersions = __webpack_exports__.getCompatibleReactVersions;
|
|
1082
|
+
exports.getFinalSharedConfig = __webpack_exports__.getFinalSharedConfig;
|
|
1083
|
+
exports.getLatestVersion = __webpack_exports__.getLatestVersion;
|
|
1084
|
+
exports.getLoadedRemotes = __webpack_exports__.getLoadedRemotes;
|
|
1085
|
+
exports.getPreloadStatus = __webpack_exports__.getPreloadStatus;
|
|
1086
|
+
exports.getRemoteHealthReport = __webpack_exports__.getRemoteHealthReport;
|
|
1087
|
+
exports.getStableVersions = __webpack_exports__.getStableVersions;
|
|
1088
|
+
exports.getVersionCache = __webpack_exports__.getVersionCache;
|
|
1089
|
+
exports.isPrerelease = __webpack_exports__.isPrerelease;
|
|
1090
|
+
exports.isRemoteLoaded = __webpack_exports__.isRemoteLoaded;
|
|
1091
|
+
exports.lazyRemote = __webpack_exports__.lazyRemote;
|
|
1092
|
+
exports.loadReactVersion = __webpack_exports__.loadReactVersion;
|
|
121
1093
|
exports.loadRemoteMultiVersion = __webpack_exports__.loadRemoteMultiVersion;
|
|
1094
|
+
exports.parseVersion = __webpack_exports__.parseVersion;
|
|
1095
|
+
exports.preloadRemote = __webpack_exports__.preloadRemote;
|
|
1096
|
+
exports.preloadRemoteList = __webpack_exports__.preloadRemoteList;
|
|
1097
|
+
exports.registerLoadedModule = __webpack_exports__.registerLoadedModule;
|
|
1098
|
+
exports.registerRemoteInstance = __webpack_exports__.registerRemoteInstance;
|
|
1099
|
+
exports.resolveFinalVersion = __webpack_exports__.resolveFinalVersion;
|
|
1100
|
+
exports.satisfiesVersion = __webpack_exports__.satisfiesVersion;
|
|
1101
|
+
exports.setVersionCache = __webpack_exports__.setVersionCache;
|
|
1102
|
+
exports.sortVersions = __webpack_exports__.sortVersions;
|
|
1103
|
+
exports.tryLoadRemote = __webpack_exports__.tryLoadRemote;
|
|
1104
|
+
exports.unloadAll = __webpack_exports__.unloadAll;
|
|
1105
|
+
exports.unloadRemote = __webpack_exports__.unloadRemote;
|
|
1106
|
+
exports.useRemoteModuleHook = __webpack_exports__.useRemoteModuleHook;
|
|
1107
|
+
exports.withRemote = __webpack_exports__.withRemote;
|
|
122
1108
|
for(var __webpack_i__ in __webpack_exports__)if (-1 === [
|
|
123
|
-
"
|
|
1109
|
+
"ErrorBoundary",
|
|
1110
|
+
"RemoteModuleCard",
|
|
1111
|
+
"SuspenseRemote",
|
|
1112
|
+
"SuspenseRemoteLoader",
|
|
1113
|
+
"buildCdnUrls",
|
|
1114
|
+
"buildFinalUrls",
|
|
1115
|
+
"cancelPreload",
|
|
1116
|
+
"checkModuleLoadable",
|
|
1117
|
+
"checkRemoteHealth",
|
|
1118
|
+
"checkVersionCompatibility",
|
|
1119
|
+
"clearPreloadCache",
|
|
1120
|
+
"compareVersions",
|
|
1121
|
+
"createEventBus",
|
|
1122
|
+
"eventBus",
|
|
1123
|
+
"extractMajorVersion",
|
|
1124
|
+
"fallbackPlugin",
|
|
1125
|
+
"fetchAvailableVersions",
|
|
1126
|
+
"fetchLatestVersion",
|
|
1127
|
+
"findCompatibleVersion",
|
|
1128
|
+
"formatHealthStatus",
|
|
1129
|
+
"getCompatibleReactVersions",
|
|
1130
|
+
"getFinalSharedConfig",
|
|
1131
|
+
"getLatestVersion",
|
|
1132
|
+
"getLoadedRemotes",
|
|
1133
|
+
"getPreloadStatus",
|
|
1134
|
+
"getRemoteHealthReport",
|
|
1135
|
+
"getStableVersions",
|
|
1136
|
+
"getVersionCache",
|
|
1137
|
+
"isPrerelease",
|
|
1138
|
+
"isRemoteLoaded",
|
|
1139
|
+
"lazyRemote",
|
|
1140
|
+
"loadReactVersion",
|
|
1141
|
+
"loadRemoteMultiVersion",
|
|
1142
|
+
"parseVersion",
|
|
1143
|
+
"preloadRemote",
|
|
1144
|
+
"preloadRemoteList",
|
|
1145
|
+
"registerLoadedModule",
|
|
1146
|
+
"registerRemoteInstance",
|
|
1147
|
+
"resolveFinalVersion",
|
|
1148
|
+
"satisfiesVersion",
|
|
1149
|
+
"setVersionCache",
|
|
1150
|
+
"sortVersions",
|
|
1151
|
+
"tryLoadRemote",
|
|
1152
|
+
"unloadAll",
|
|
1153
|
+
"unloadRemote",
|
|
1154
|
+
"useRemoteModuleHook",
|
|
1155
|
+
"withRemote"
|
|
124
1156
|
].indexOf(__webpack_i__)) exports[__webpack_i__] = __webpack_exports__[__webpack_i__];
|
|
125
1157
|
Object.defineProperty(exports, '__esModule', {
|
|
126
1158
|
value: true
|