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