remote-reload-utils 0.0.16 → 1.0.0
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 +56 -35
- package/dist/index.d.ts +2 -2
- package/dist/loader/index.d.ts +4 -2
- package/dist/loader/remote-source/index.d.ts +28 -0
- package/dist/loader/utils.d.ts +6 -2
- package/dist/main.cjs +84 -38
- package/dist/main.js +82 -39
- package/dist/types/index.d.ts +1 -1
- package/loadRemote.md +67 -2
- package/package.json +21 -19
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.0.0
|
|
4
|
+
|
|
5
|
+
### Major Changes
|
|
6
|
+
|
|
7
|
+
- 9306c65: changeset cli 集成 & 变更日志记录
|
|
8
|
+
|
|
3
9
|
所有重要的项目变更都将记录在此文件中。
|
|
4
10
|
|
|
5
11
|
格式基于 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.0.0/),
|
|
@@ -7,63 +13,62 @@
|
|
|
7
13
|
|
|
8
14
|
## [未发布]
|
|
9
15
|
|
|
16
|
+
## [0.0.17] - 2026-03-19
|
|
17
|
+
|
|
18
|
+
### Release
|
|
19
|
+
|
|
20
|
+
- Published version 0.0.17 with patch bump
|
|
21
|
+
|
|
10
22
|
## [0.0.16] - 2026-03-18
|
|
11
23
|
|
|
12
24
|
### Release
|
|
13
25
|
|
|
14
26
|
- Published version 0.0.16 with patch bump
|
|
15
27
|
|
|
16
|
-
|
|
17
28
|
## [0.0.15] - 2026-03-17
|
|
18
29
|
|
|
19
30
|
### Release
|
|
20
31
|
|
|
21
32
|
- Published version 0.0.15 with patch bump
|
|
22
33
|
|
|
23
|
-
|
|
24
34
|
## [0.0.14] - 2026-03-17
|
|
25
35
|
|
|
26
36
|
### Release
|
|
27
37
|
|
|
28
38
|
- Published version 0.0.14 with patch bump
|
|
29
39
|
|
|
30
|
-
|
|
31
40
|
## [0.0.13] - 2026-03-17
|
|
32
41
|
|
|
33
42
|
### Release
|
|
34
43
|
|
|
35
44
|
- Published version 0.0.13 with patch bump
|
|
36
45
|
|
|
37
|
-
|
|
38
46
|
## [0.0.12] - 2026-03-16
|
|
39
47
|
|
|
40
48
|
### Release
|
|
41
49
|
|
|
42
50
|
- Published version 0.0.12 with patch bump
|
|
43
51
|
|
|
44
|
-
|
|
45
52
|
## [0.0.11] - 2026-03-16
|
|
46
53
|
|
|
47
54
|
### Release
|
|
48
55
|
|
|
49
56
|
- Published version 0.0.11 with patch bump
|
|
50
57
|
|
|
51
|
-
|
|
52
58
|
## [0.0.10] - 2026-03-15
|
|
53
59
|
|
|
54
60
|
### Release
|
|
55
61
|
|
|
56
62
|
- Published version 0.0.10 with patch bump
|
|
57
63
|
|
|
58
|
-
|
|
59
64
|
## [0.0.9] - 2026-03-15
|
|
60
65
|
|
|
61
66
|
### Release
|
|
62
67
|
|
|
63
68
|
- Published version 0.0.9 with patch bump
|
|
64
69
|
|
|
65
|
-
|
|
66
70
|
### Refactor
|
|
71
|
+
|
|
67
72
|
- 重构项目为模块化目录结构
|
|
68
73
|
- `src/loader/` - 核心加载模块
|
|
69
74
|
- `src/version/` - 版本管理模块
|
|
@@ -77,6 +82,7 @@
|
|
|
77
82
|
- 更新 package.json types 路径为 `./dist/index.d.ts`
|
|
78
83
|
|
|
79
84
|
### Changed
|
|
85
|
+
|
|
80
86
|
- 修复 preloadRemote 引用从 loadRemote2 改为 loadRemote
|
|
81
87
|
- 移除 App.tsx 中未使用的 React 导入
|
|
82
88
|
- 添加 release 脚本到 package.json
|
|
@@ -84,6 +90,7 @@
|
|
|
84
90
|
## [0.0.8] - 2026-03-15
|
|
85
91
|
|
|
86
92
|
### Refactor
|
|
93
|
+
|
|
87
94
|
- 抽离公共工具函数到 `loadRemoteUtils.ts`
|
|
88
95
|
- 新增 `fetchLatestVersion()` - 从 npm registry 获取最新版本
|
|
89
96
|
- 新增 `getVersionCache()` / `setVersionCache()` - 缓存管理
|
|
@@ -96,14 +103,17 @@
|
|
|
96
103
|
- 简化 `loadRemote.ts`,使用公共工具函数
|
|
97
104
|
|
|
98
105
|
### Changed
|
|
106
|
+
|
|
99
107
|
- 更新 `index.ts` 导出,新增工具函数导出
|
|
100
108
|
|
|
101
109
|
### Docs
|
|
110
|
+
|
|
102
111
|
- 更新 `loadRemote.md`,添加工具函数 API 文档
|
|
103
112
|
|
|
104
113
|
## [0.0.7] - 2026-03-15
|
|
105
114
|
|
|
106
115
|
### Features
|
|
116
|
+
|
|
107
117
|
- 新增微前端工具函数集
|
|
108
118
|
- 远程模块加载
|
|
109
119
|
- React 多版本支持
|
|
@@ -114,15 +124,18 @@
|
|
|
114
124
|
## [0.0.6] - 2026-03-14
|
|
115
125
|
|
|
116
126
|
### Docs
|
|
127
|
+
|
|
117
128
|
- 更新 README 和 loadRemote.md 文档
|
|
118
129
|
- 添加使用指南和 API 参考
|
|
119
130
|
|
|
120
131
|
### Chore
|
|
132
|
+
|
|
121
133
|
- 更新 remote-reload-utils 版本至 0.0.8
|
|
122
134
|
|
|
123
135
|
## [0.0.5] - 2026-03-14
|
|
124
136
|
|
|
125
137
|
### Features
|
|
138
|
+
|
|
126
139
|
- 添加多版本远程加载功能
|
|
127
140
|
- 支持自定义 shared 配置
|
|
128
141
|
- 改进错误处理机制
|
|
@@ -130,33 +143,39 @@
|
|
|
130
143
|
## [0.0.4] - 2026-03-14
|
|
131
144
|
|
|
132
145
|
### Chore
|
|
146
|
+
|
|
133
147
|
- 更新错误处理逻辑
|
|
134
148
|
- 优化加载流程
|
|
135
149
|
|
|
136
150
|
## [0.0.3] - 2026-03-14
|
|
137
151
|
|
|
138
152
|
### Docs
|
|
153
|
+
|
|
139
154
|
- 添加项目描述和 git 仓库信息
|
|
140
155
|
- 更新 README 中的示例代码
|
|
141
156
|
- 将 loadRemote.md 从 src 移动到根目录
|
|
142
157
|
|
|
143
158
|
### Chore
|
|
159
|
+
|
|
144
160
|
- 更新 remote-reload-utils 的版本号至 0.0.3
|
|
145
161
|
|
|
146
162
|
## [0.0.2] - 2026-03-14
|
|
147
163
|
|
|
148
164
|
### Features
|
|
165
|
+
|
|
149
166
|
- 添加 React 多版本加载功能(v17/v18/v19)
|
|
150
167
|
- 添加共享配置支持
|
|
151
168
|
- 默认共享 react 和 react-dom
|
|
152
169
|
- 支持自定义共享模块
|
|
153
170
|
|
|
154
171
|
### Docs
|
|
172
|
+
|
|
155
173
|
- 更新示例代码展示如何使用 loadRemoteMultiVersion
|
|
156
174
|
|
|
157
175
|
## [0.0.1] - 2026-03-14
|
|
158
176
|
|
|
159
177
|
### Features
|
|
178
|
+
|
|
160
179
|
- 实现多版本远程模块加载功能
|
|
161
180
|
- 支持 CDN 故障转移(jsdelivr、unpkg)
|
|
162
181
|
- 支持本地 fallback 兜底
|
|
@@ -170,30 +189,32 @@
|
|
|
170
189
|
|
|
171
190
|
## 版本说明
|
|
172
191
|
|
|
173
|
-
| 版本
|
|
174
|
-
|
|
175
|
-
| 0.0.
|
|
176
|
-
|
|
177
|
-
| 0.0.
|
|
178
|
-
|
|
179
|
-
| 0.0.
|
|
180
|
-
|
|
181
|
-
| 0.0.
|
|
182
|
-
|
|
183
|
-
| 0.0.
|
|
184
|
-
|
|
185
|
-
| 0.0.
|
|
186
|
-
|
|
187
|
-
| 0.0.
|
|
188
|
-
|
|
189
|
-
| 0.0.
|
|
190
|
-
|
|
191
|
-
| 0.0.9
|
|
192
|
-
|
|
|
193
|
-
| 0.0.
|
|
194
|
-
| 0.0.
|
|
195
|
-
| 0.0.
|
|
196
|
-
| 0.0.
|
|
197
|
-
| 0.0.
|
|
198
|
-
| 0.0.
|
|
199
|
-
| 0.0.
|
|
192
|
+
| 版本 | 日期 | 主要变更 |
|
|
193
|
+
| ------ | ---------- | ------------------------------ |
|
|
194
|
+
| 0.0.17 | 2026-03-19 | patch 版本发布 |
|
|
195
|
+
| ------ | ------ | ---------- |
|
|
196
|
+
| 0.0.16 | 2026-03-18 | patch 版本发布 |
|
|
197
|
+
| ------ | ------ | ---------- |
|
|
198
|
+
| 0.0.15 | 2026-03-17 | patch 版本发布 |
|
|
199
|
+
| ------ | ------ | ---------- |
|
|
200
|
+
| 0.0.14 | 2026-03-17 | patch 版本发布 |
|
|
201
|
+
| ------ | ------ | ---------- |
|
|
202
|
+
| 0.0.13 | 2026-03-17 | patch 版本发布 |
|
|
203
|
+
| ------ | ------ | ---------- |
|
|
204
|
+
| 0.0.12 | 2026-03-16 | patch 版本发布 |
|
|
205
|
+
| ------ | ------ | ---------- |
|
|
206
|
+
| 0.0.11 | 2026-03-16 | patch 版本发布 |
|
|
207
|
+
| ------ | ------ | ---------- |
|
|
208
|
+
| 0.0.10 | 2026-03-15 | patch 版本发布 |
|
|
209
|
+
| ------ | ------ | ---------- |
|
|
210
|
+
| 0.0.9 | 2026-03-15 | patch 版本发布 |
|
|
211
|
+
| ------ | ------ | ---------- |
|
|
212
|
+
| 0.0.9 | 2026-03-15 | 重构为模块化目录结构 |
|
|
213
|
+
| 0.0.8 | 2026-03-15 | 重构代码结构,抽离公共工具函数 |
|
|
214
|
+
| 0.0.7 | 2026-03-15 | 新增微前端工具函数集 |
|
|
215
|
+
| 0.0.6 | 2026-03-14 | 文档更新 |
|
|
216
|
+
| 0.0.5 | 2026-03-14 | 多版本远程加载 |
|
|
217
|
+
| 0.0.4 | 2026-03-14 | 错误处理优化 |
|
|
218
|
+
| 0.0.3 | 2026-03-14 | 文档和配置更新 |
|
|
219
|
+
| 0.0.2 | 2026-03-14 | React 多版本支持 |
|
|
220
|
+
| 0.0.1 | 2026-03-14 | 初始版本 |
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { loadReactVersion } from './version/react';
|
|
2
2
|
export { checkVersionCompatibility, satisfiesVersion, findCompatibleVersion, getCompatibleReactVersions, fetchAvailableVersions, sortVersions, getLatestVersion, getStableVersions, extractMajorVersion, isPrerelease, compareVersions, parseVersion, } from './version';
|
|
3
|
-
export { loadRemoteMultiVersion } from './loader';
|
|
4
|
-
export { fetchLatestVersion, getVersionCache, setVersionCache, buildCdnUrls, tryLoadRemote, getFinalSharedConfig, resolveFinalVersion, buildFinalUrls, type LoadResult, } from './loader/utils';
|
|
3
|
+
export { loadRemoteMultiVersion, createRemoteSourcePlugin, type RemoteSourcePlugin, type RemoteSourcePluginContext, type LoadRemoteExtraOptions, } from './loader';
|
|
4
|
+
export { fetchLatestVersion, getVersionCache, setVersionCache, buildCdnUrls, tryLoadRemote, getFinalSharedConfig, resolveFinalVersion, buildFinalUrls, type LoadResult, type RuntimeRemote, } from './loader/utils';
|
|
5
5
|
export { preloadRemote, preloadRemoteList, cancelPreload, clearPreloadCache, getPreloadStatus, } from './preload';
|
|
6
6
|
export { unloadRemote, unloadAll, registerRemoteInstance, registerLoadedModule, getLoadedRemotes, isRemoteLoaded, } from './unload';
|
|
7
7
|
export { checkRemoteHealth, checkModuleLoadable, getRemoteHealthReport, formatHealthStatus, } from './health';
|
package/dist/loader/index.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import { ModuleFederationRuntimePlugin } from '@module-federation/enhanced/runtime';
|
|
1
|
+
import type { ModuleFederationRuntimePlugin } from '@module-federation/enhanced/runtime';
|
|
2
2
|
import type { LoadRemoteOptions } from '../types';
|
|
3
|
+
import { createRemoteSourcePlugin, type LoadRemoteExtraOptions, type RemoteSourcePlugin, type RemoteSourcePluginContext } from './remote-source';
|
|
4
|
+
export { createRemoteSourcePlugin, type RemoteSourcePlugin, type RemoteSourcePluginContext, type LoadRemoteExtraOptions, };
|
|
3
5
|
/**
|
|
4
6
|
* 多版本共存的 loadRemote
|
|
5
7
|
*/
|
|
6
|
-
export declare function loadRemoteMultiVersion(options: LoadRemoteOptions, plugins
|
|
8
|
+
export declare function loadRemoteMultiVersion(options: LoadRemoteOptions, plugins?: ModuleFederationRuntimePlugin[], extraOptions?: LoadRemoteExtraOptions): Promise<import("./utils").LoadResult>;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { LoadRemoteOptions } from '../../types';
|
|
2
|
+
import type { RuntimeRemote } from '../utils';
|
|
3
|
+
type MaybePromise<T> = T | Promise<T>;
|
|
4
|
+
export interface RemoteSourcePluginContext {
|
|
5
|
+
options: LoadRemoteOptions;
|
|
6
|
+
scopeName: string;
|
|
7
|
+
pkg: string;
|
|
8
|
+
finalVersion: string;
|
|
9
|
+
currentEntry: string;
|
|
10
|
+
allEntries: string[];
|
|
11
|
+
}
|
|
12
|
+
export interface RemoteSourcePlugin {
|
|
13
|
+
name: string;
|
|
14
|
+
registerRemotes?: (context: RemoteSourcePluginContext) => MaybePromise<RuntimeRemote[] | void>;
|
|
15
|
+
}
|
|
16
|
+
export interface LoadRemoteExtraOptions {
|
|
17
|
+
remoteSourcePlugins?: RemoteSourcePlugin[];
|
|
18
|
+
baseRemotes?: RuntimeRemote[];
|
|
19
|
+
registerOptions?: {
|
|
20
|
+
force?: boolean;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
export declare function resolveRegisteredRemotes(context: RemoteSourcePluginContext, baseRemotes: RuntimeRemote[], remoteSourcePlugins: RemoteSourcePlugin[]): Promise<RuntimeRemote[]>;
|
|
24
|
+
/**
|
|
25
|
+
* 创建静态 remote 来源插件
|
|
26
|
+
*/
|
|
27
|
+
export declare function createRemoteSourcePlugin(name: string, remotes: RuntimeRemote[]): RemoteSourcePlugin;
|
|
28
|
+
export {};
|
package/dist/loader/utils.d.ts
CHANGED
|
@@ -20,10 +20,14 @@ export interface LoadResult {
|
|
|
20
20
|
scopeName: string;
|
|
21
21
|
mf: ReturnType<typeof createInstance>;
|
|
22
22
|
}
|
|
23
|
+
export type RuntimeRemote = Parameters<ReturnType<typeof createInstance>['registerRemotes']>[0][number];
|
|
23
24
|
/**
|
|
24
|
-
* 尝试加载单个远程模块 URL
|
|
25
|
+
* 尝试加载单个远程模块 URL
|
|
26
|
+
* 注意:实际的重试机制在外层 loadRemoteMultiVersion 的 URL 遍历中实现
|
|
25
27
|
*/
|
|
26
|
-
export declare function tryLoadRemote(scopeName: string, url: string,
|
|
28
|
+
export declare function tryLoadRemote(scopeName: string, url: string, _retries: number, _delay: number, sharedConfig: Record<string, any>, plugins: ModuleFederationRuntimePlugin[], extraRemotes?: RuntimeRemote[], registerOptions?: {
|
|
29
|
+
force?: boolean;
|
|
30
|
+
}): Promise<LoadResult>;
|
|
27
31
|
/**
|
|
28
32
|
* 获取最终的共享配置
|
|
29
33
|
*/
|
package/dist/main.cjs
CHANGED
|
@@ -60,6 +60,7 @@ __webpack_require__.d(__webpack_exports__, {
|
|
|
60
60
|
satisfiesVersion: ()=>satisfiesVersion,
|
|
61
61
|
formatHealthStatus: ()=>formatHealthStatus,
|
|
62
62
|
unloadAll: ()=>unloadAll,
|
|
63
|
+
createRemoteSourcePlugin: ()=>createRemoteSourcePlugin,
|
|
63
64
|
setVersionCache: ()=>setVersionCache,
|
|
64
65
|
isPrerelease: ()=>isPrerelease,
|
|
65
66
|
checkRemoteHealth: ()=>checkRemoteHealth,
|
|
@@ -323,8 +324,20 @@ function buildCdnUrls(pkg, version) {
|
|
|
323
324
|
}
|
|
324
325
|
const mfInstanceCache = new Map();
|
|
325
326
|
const mfInstanceLoadingCache = new Map();
|
|
326
|
-
|
|
327
|
-
|
|
327
|
+
function buildRemotesIdentity(remotes) {
|
|
328
|
+
if (0 === remotes.length) return '';
|
|
329
|
+
return remotes.map((remote)=>JSON.stringify({
|
|
330
|
+
name: remote.name,
|
|
331
|
+
entry: 'entry' in remote ? remote.entry : '',
|
|
332
|
+
version: 'version' in remote ? remote.version : '',
|
|
333
|
+
alias: remote.alias || '',
|
|
334
|
+
type: remote.type || '',
|
|
335
|
+
entryGlobalName: remote.entryGlobalName || ''
|
|
336
|
+
})).sort().join('|');
|
|
337
|
+
}
|
|
338
|
+
async function tryLoadRemote(scopeName, url, _retries, _delay, sharedConfig, plugins, extraRemotes = [], registerOptions = {}) {
|
|
339
|
+
const remotesIdentity = buildRemotesIdentity(extraRemotes);
|
|
340
|
+
const cacheKey = `${scopeName}::${url}::${remotesIdentity}::${registerOptions.force ? 'force' : 'normal'}`;
|
|
328
341
|
const cachedMfs = mfInstanceCache.get(cacheKey);
|
|
329
342
|
if (cachedMfs) return {
|
|
330
343
|
scopeName,
|
|
@@ -332,41 +345,32 @@ async function tryLoadRemote(scopeName, url, retries, delay, sharedConfig, plugi
|
|
|
332
345
|
};
|
|
333
346
|
const loadingMfs = mfInstanceLoadingCache.get(cacheKey);
|
|
334
347
|
if (loadingMfs) return loadingMfs;
|
|
335
|
-
const
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
fallbackPlugin()
|
|
350
|
-
]
|
|
351
|
-
});
|
|
352
|
-
const result = {
|
|
353
|
-
scopeName,
|
|
354
|
-
mf
|
|
355
|
-
};
|
|
356
|
-
mfInstanceCache.set(cacheKey, mf);
|
|
357
|
-
return result;
|
|
358
|
-
} catch (e) {
|
|
359
|
-
lastError = e;
|
|
360
|
-
console.warn(`[MF] URL ${url} 加载失败,第 ${i + 1} 次重试...`);
|
|
361
|
-
if (i < retries - 1) await new Promise((res)=>setTimeout(res, delay));
|
|
362
|
-
}
|
|
363
|
-
throw new Error(`[MF] URL ${url} 经过 ${retries} 次重试仍加载失败。`, {
|
|
364
|
-
cause: lastError
|
|
348
|
+
const loadPromise = Promise.resolve().then(()=>{
|
|
349
|
+
const mf = (0, runtime_namespaceObject.createInstance)({
|
|
350
|
+
name: 'host',
|
|
351
|
+
remotes: [
|
|
352
|
+
{
|
|
353
|
+
name: scopeName,
|
|
354
|
+
entry: url
|
|
355
|
+
}
|
|
356
|
+
],
|
|
357
|
+
shared: sharedConfig,
|
|
358
|
+
plugins: [
|
|
359
|
+
...plugins,
|
|
360
|
+
fallbackPlugin()
|
|
361
|
+
]
|
|
365
362
|
});
|
|
366
|
-
|
|
367
|
-
|
|
363
|
+
if (extraRemotes.length > 0) mf.registerRemotes(extraRemotes, registerOptions);
|
|
364
|
+
const result = {
|
|
365
|
+
scopeName,
|
|
366
|
+
mf
|
|
367
|
+
};
|
|
368
|
+
mfInstanceCache.set(cacheKey, mf);
|
|
369
|
+
return result;
|
|
370
|
+
});
|
|
371
|
+
mfInstanceLoadingCache.set(cacheKey, loadPromise);
|
|
368
372
|
try {
|
|
369
|
-
return await
|
|
373
|
+
return await loadPromise;
|
|
370
374
|
} finally{
|
|
371
375
|
mfInstanceLoadingCache.delete(cacheKey);
|
|
372
376
|
}
|
|
@@ -488,15 +492,55 @@ function buildFinalUrls(pkg, version, localFallback) {
|
|
|
488
492
|
if (localFallback) urls.push(localFallback);
|
|
489
493
|
return urls;
|
|
490
494
|
}
|
|
491
|
-
|
|
495
|
+
function dedupeRemotes(remotes) {
|
|
496
|
+
const map = new Map();
|
|
497
|
+
for (const remote of remotes){
|
|
498
|
+
if (!remote?.name) continue;
|
|
499
|
+
const key = [
|
|
500
|
+
remote.name,
|
|
501
|
+
'entry' in remote ? remote.entry || '' : '',
|
|
502
|
+
'version' in remote ? remote.version || '' : '',
|
|
503
|
+
remote.alias || ''
|
|
504
|
+
].join('::');
|
|
505
|
+
if (!map.has(key)) map.set(key, remote);
|
|
506
|
+
}
|
|
507
|
+
return Array.from(map.values());
|
|
508
|
+
}
|
|
509
|
+
async function resolveRegisteredRemotes(context, baseRemotes, remoteSourcePlugins) {
|
|
510
|
+
const remoteList = [
|
|
511
|
+
...baseRemotes
|
|
512
|
+
];
|
|
513
|
+
for (const plugin of remoteSourcePlugins)if (plugin.registerRemotes) try {
|
|
514
|
+
const pluginRemotes = await plugin.registerRemotes(context);
|
|
515
|
+
if (pluginRemotes?.length) remoteList.push(...pluginRemotes);
|
|
516
|
+
} catch (error) {
|
|
517
|
+
throw new Error(`[MF] remote 来源插件 ${plugin.name} 执行失败: ${error.message}`);
|
|
518
|
+
}
|
|
519
|
+
return dedupeRemotes(remoteList).filter((remote)=>!(remote.name === context.scopeName && 'entry' in remote && remote.entry === context.currentEntry));
|
|
520
|
+
}
|
|
521
|
+
function createRemoteSourcePlugin(name, remotes) {
|
|
522
|
+
return {
|
|
523
|
+
name,
|
|
524
|
+
registerRemotes: ()=>remotes
|
|
525
|
+
};
|
|
526
|
+
}
|
|
527
|
+
async function loadRemoteMultiVersion(options, plugins = [], extraOptions = {}) {
|
|
492
528
|
const { name, pkg, version = 'latest', retries = 3, delay = 1000, localFallback, cacheTTL = 86400000, revalidate = true, shared: customShared } = options;
|
|
529
|
+
const { remoteSourcePlugins = [], baseRemotes = [], registerOptions = {} } = extraOptions;
|
|
493
530
|
const finalVersion = await resolveFinalVersion(pkg, version, cacheTTL, revalidate);
|
|
494
531
|
const scopeName = `${name}`;
|
|
495
532
|
const urls = buildFinalUrls(pkg, finalVersion, localFallback);
|
|
496
533
|
const finalSharedConfig = getFinalSharedConfig(customShared);
|
|
497
|
-
console.log(finalSharedConfig, 'finalSharedConfig');
|
|
498
534
|
for (const url of urls)try {
|
|
499
|
-
|
|
535
|
+
const registeredRemotes = await resolveRegisteredRemotes({
|
|
536
|
+
options,
|
|
537
|
+
scopeName,
|
|
538
|
+
pkg,
|
|
539
|
+
finalVersion,
|
|
540
|
+
currentEntry: url,
|
|
541
|
+
allEntries: urls
|
|
542
|
+
}, baseRemotes, remoteSourcePlugins);
|
|
543
|
+
return await tryLoadRemote(scopeName, url, retries, delay, finalSharedConfig, plugins, registeredRemotes, registerOptions);
|
|
500
544
|
} catch (e) {
|
|
501
545
|
console.warn(`[MF] 切换 CDN 路径:${url} 失败,尝试下一个...`, e);
|
|
502
546
|
}
|
|
@@ -897,6 +941,7 @@ exports.checkVersionCompatibility = __webpack_exports__.checkVersionCompatibilit
|
|
|
897
941
|
exports.clearPreloadCache = __webpack_exports__.clearPreloadCache;
|
|
898
942
|
exports.compareVersions = __webpack_exports__.compareVersions;
|
|
899
943
|
exports.createEventBus = __webpack_exports__.createEventBus;
|
|
944
|
+
exports.createRemoteSourcePlugin = __webpack_exports__.createRemoteSourcePlugin;
|
|
900
945
|
exports.eventBus = __webpack_exports__.eventBus;
|
|
901
946
|
exports.extractMajorVersion = __webpack_exports__.extractMajorVersion;
|
|
902
947
|
exports.fallbackPlugin = __webpack_exports__.fallbackPlugin;
|
|
@@ -938,6 +983,7 @@ for(var __webpack_i__ in __webpack_exports__)if (-1 === [
|
|
|
938
983
|
"clearPreloadCache",
|
|
939
984
|
"compareVersions",
|
|
940
985
|
"createEventBus",
|
|
986
|
+
"createRemoteSourcePlugin",
|
|
941
987
|
"eventBus",
|
|
942
988
|
"extractMajorVersion",
|
|
943
989
|
"fallbackPlugin",
|
package/dist/main.js
CHANGED
|
@@ -256,8 +256,20 @@ function buildCdnUrls(pkg, version) {
|
|
|
256
256
|
}
|
|
257
257
|
const mfInstanceCache = new Map();
|
|
258
258
|
const mfInstanceLoadingCache = new Map();
|
|
259
|
-
|
|
260
|
-
|
|
259
|
+
function buildRemotesIdentity(remotes) {
|
|
260
|
+
if (0 === remotes.length) return '';
|
|
261
|
+
return remotes.map((remote)=>JSON.stringify({
|
|
262
|
+
name: remote.name,
|
|
263
|
+
entry: 'entry' in remote ? remote.entry : '',
|
|
264
|
+
version: 'version' in remote ? remote.version : '',
|
|
265
|
+
alias: remote.alias || '',
|
|
266
|
+
type: remote.type || '',
|
|
267
|
+
entryGlobalName: remote.entryGlobalName || ''
|
|
268
|
+
})).sort().join('|');
|
|
269
|
+
}
|
|
270
|
+
async function tryLoadRemote(scopeName, url, _retries, _delay, sharedConfig, plugins, extraRemotes = [], registerOptions = {}) {
|
|
271
|
+
const remotesIdentity = buildRemotesIdentity(extraRemotes);
|
|
272
|
+
const cacheKey = `${scopeName}::${url}::${remotesIdentity}::${registerOptions.force ? 'force' : 'normal'}`;
|
|
261
273
|
const cachedMfs = mfInstanceCache.get(cacheKey);
|
|
262
274
|
if (cachedMfs) return {
|
|
263
275
|
scopeName,
|
|
@@ -265,41 +277,32 @@ async function tryLoadRemote(scopeName, url, retries, delay, sharedConfig, plugi
|
|
|
265
277
|
};
|
|
266
278
|
const loadingMfs = mfInstanceLoadingCache.get(cacheKey);
|
|
267
279
|
if (loadingMfs) return loadingMfs;
|
|
268
|
-
const
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
fallbackPlugin()
|
|
283
|
-
]
|
|
284
|
-
});
|
|
285
|
-
const result = {
|
|
286
|
-
scopeName,
|
|
287
|
-
mf
|
|
288
|
-
};
|
|
289
|
-
mfInstanceCache.set(cacheKey, mf);
|
|
290
|
-
return result;
|
|
291
|
-
} catch (e) {
|
|
292
|
-
lastError = e;
|
|
293
|
-
console.warn(`[MF] URL ${url} 加载失败,第 ${i + 1} 次重试...`);
|
|
294
|
-
if (i < retries - 1) await new Promise((res)=>setTimeout(res, delay));
|
|
295
|
-
}
|
|
296
|
-
throw new Error(`[MF] URL ${url} 经过 ${retries} 次重试仍加载失败。`, {
|
|
297
|
-
cause: lastError
|
|
280
|
+
const loadPromise = Promise.resolve().then(()=>{
|
|
281
|
+
const mf = createInstance({
|
|
282
|
+
name: 'host',
|
|
283
|
+
remotes: [
|
|
284
|
+
{
|
|
285
|
+
name: scopeName,
|
|
286
|
+
entry: url
|
|
287
|
+
}
|
|
288
|
+
],
|
|
289
|
+
shared: sharedConfig,
|
|
290
|
+
plugins: [
|
|
291
|
+
...plugins,
|
|
292
|
+
fallbackPlugin()
|
|
293
|
+
]
|
|
298
294
|
});
|
|
299
|
-
|
|
300
|
-
|
|
295
|
+
if (extraRemotes.length > 0) mf.registerRemotes(extraRemotes, registerOptions);
|
|
296
|
+
const result = {
|
|
297
|
+
scopeName,
|
|
298
|
+
mf
|
|
299
|
+
};
|
|
300
|
+
mfInstanceCache.set(cacheKey, mf);
|
|
301
|
+
return result;
|
|
302
|
+
});
|
|
303
|
+
mfInstanceLoadingCache.set(cacheKey, loadPromise);
|
|
301
304
|
try {
|
|
302
|
-
return await
|
|
305
|
+
return await loadPromise;
|
|
303
306
|
} finally{
|
|
304
307
|
mfInstanceLoadingCache.delete(cacheKey);
|
|
305
308
|
}
|
|
@@ -421,15 +424,55 @@ function buildFinalUrls(pkg, version, localFallback) {
|
|
|
421
424
|
if (localFallback) urls.push(localFallback);
|
|
422
425
|
return urls;
|
|
423
426
|
}
|
|
424
|
-
|
|
427
|
+
function dedupeRemotes(remotes) {
|
|
428
|
+
const map = new Map();
|
|
429
|
+
for (const remote of remotes){
|
|
430
|
+
if (!remote?.name) continue;
|
|
431
|
+
const key = [
|
|
432
|
+
remote.name,
|
|
433
|
+
'entry' in remote ? remote.entry || '' : '',
|
|
434
|
+
'version' in remote ? remote.version || '' : '',
|
|
435
|
+
remote.alias || ''
|
|
436
|
+
].join('::');
|
|
437
|
+
if (!map.has(key)) map.set(key, remote);
|
|
438
|
+
}
|
|
439
|
+
return Array.from(map.values());
|
|
440
|
+
}
|
|
441
|
+
async function resolveRegisteredRemotes(context, baseRemotes, remoteSourcePlugins) {
|
|
442
|
+
const remoteList = [
|
|
443
|
+
...baseRemotes
|
|
444
|
+
];
|
|
445
|
+
for (const plugin of remoteSourcePlugins)if (plugin.registerRemotes) try {
|
|
446
|
+
const pluginRemotes = await plugin.registerRemotes(context);
|
|
447
|
+
if (pluginRemotes?.length) remoteList.push(...pluginRemotes);
|
|
448
|
+
} catch (error) {
|
|
449
|
+
throw new Error(`[MF] remote 来源插件 ${plugin.name} 执行失败: ${error.message}`);
|
|
450
|
+
}
|
|
451
|
+
return dedupeRemotes(remoteList).filter((remote)=>!(remote.name === context.scopeName && 'entry' in remote && remote.entry === context.currentEntry));
|
|
452
|
+
}
|
|
453
|
+
function createRemoteSourcePlugin(name, remotes) {
|
|
454
|
+
return {
|
|
455
|
+
name,
|
|
456
|
+
registerRemotes: ()=>remotes
|
|
457
|
+
};
|
|
458
|
+
}
|
|
459
|
+
async function loadRemoteMultiVersion(options, plugins = [], extraOptions = {}) {
|
|
425
460
|
const { name, pkg, version = 'latest', retries = 3, delay = 1000, localFallback, cacheTTL = 86400000, revalidate = true, shared: customShared } = options;
|
|
461
|
+
const { remoteSourcePlugins = [], baseRemotes = [], registerOptions = {} } = extraOptions;
|
|
426
462
|
const finalVersion = await resolveFinalVersion(pkg, version, cacheTTL, revalidate);
|
|
427
463
|
const scopeName = `${name}`;
|
|
428
464
|
const urls = buildFinalUrls(pkg, finalVersion, localFallback);
|
|
429
465
|
const finalSharedConfig = getFinalSharedConfig(customShared);
|
|
430
|
-
console.log(finalSharedConfig, 'finalSharedConfig');
|
|
431
466
|
for (const url of urls)try {
|
|
432
|
-
|
|
467
|
+
const registeredRemotes = await resolveRegisteredRemotes({
|
|
468
|
+
options,
|
|
469
|
+
scopeName,
|
|
470
|
+
pkg,
|
|
471
|
+
finalVersion,
|
|
472
|
+
currentEntry: url,
|
|
473
|
+
allEntries: urls
|
|
474
|
+
}, baseRemotes, remoteSourcePlugins);
|
|
475
|
+
return await tryLoadRemote(scopeName, url, retries, delay, finalSharedConfig, plugins, registeredRemotes, registerOptions);
|
|
433
476
|
} catch (e) {
|
|
434
477
|
console.warn(`[MF] 切换 CDN 路径:${url} 失败,尝试下一个...`, e);
|
|
435
478
|
}
|
|
@@ -821,4 +864,4 @@ const eventBus = EventBusClass.create();
|
|
|
821
864
|
function createEventBus() {
|
|
822
865
|
return EventBusClass.create();
|
|
823
866
|
}
|
|
824
|
-
export { buildCdnUrls, buildFinalUrls, cancelPreload, checkModuleLoadable, checkRemoteHealth, checkVersionCompatibility, clearPreloadCache, compareVersions, createEventBus, eventBus, extractMajorVersion, fallbackPlugin, fetchAvailableVersions, fetchLatestVersion, findCompatibleVersion, formatHealthStatus, getCompatibleReactVersions, getFinalSharedConfig, getLatestVersion, getLoadedRemotes, getPreloadStatus, getRemoteHealthReport, getStableVersions, getVersionCache, isPrerelease, isRemoteLoaded, loadReactVersion, loadRemoteMultiVersion, parseVersion, preloadRemote, preloadRemoteList, registerLoadedModule, registerRemoteInstance, resolveFinalVersion, satisfiesVersion, setVersionCache, sortVersions, tryLoadRemote, unloadAll, unloadRemote };
|
|
867
|
+
export { buildCdnUrls, buildFinalUrls, cancelPreload, checkModuleLoadable, checkRemoteHealth, checkVersionCompatibility, clearPreloadCache, compareVersions, createEventBus, createRemoteSourcePlugin, eventBus, extractMajorVersion, fallbackPlugin, fetchAvailableVersions, fetchLatestVersion, findCompatibleVersion, formatHealthStatus, getCompatibleReactVersions, getFinalSharedConfig, getLatestVersion, getLoadedRemotes, getPreloadStatus, getRemoteHealthReport, getStableVersions, getVersionCache, isPrerelease, isRemoteLoaded, loadReactVersion, loadRemoteMultiVersion, parseVersion, preloadRemote, preloadRemoteList, registerLoadedModule, registerRemoteInstance, resolveFinalVersion, satisfiesVersion, setVersionCache, sortVersions, tryLoadRemote, unloadAll, unloadRemote };
|
package/dist/types/index.d.ts
CHANGED
package/loadRemote.md
CHANGED
|
@@ -168,7 +168,8 @@ async function loadMultipleComponents() {
|
|
|
168
168
|
```typescript
|
|
169
169
|
function loadRemoteMultiVersion(
|
|
170
170
|
options: LoadRemoteOptions,
|
|
171
|
-
plugins
|
|
171
|
+
plugins?: ModuleFederationRuntimePlugin[],
|
|
172
|
+
extraOptions?: LoadRemoteExtraOptions,
|
|
172
173
|
): Promise<LoadResult>
|
|
173
174
|
```
|
|
174
175
|
|
|
@@ -192,6 +193,14 @@ function loadRemoteMultiVersion(
|
|
|
192
193
|
|
|
193
194
|
Module Federation 运行时插件数组,默认会添加 `fallbackPlugin()`。
|
|
194
195
|
|
|
196
|
+
**extraOptions**: `LoadRemoteExtraOptions`
|
|
197
|
+
|
|
198
|
+
| 属性 | 类型 | 必填 | 默认值 | 描述 |
|
|
199
|
+
|------|------|------|--------|------|
|
|
200
|
+
| `baseRemotes` | `RuntimeRemote[]` | ❌ | `[]` | 直接注册的附加 remote 列表 |
|
|
201
|
+
| `remoteSourcePlugins` | `RemoteSourcePlugin[]` | ❌ | `[]` | 通过插件动态返回并注册 remote 列表 |
|
|
202
|
+
| `registerOptions` | `{ force?: boolean }` | ❌ | `{}` | 透传给 `registerRemotes` 的配置 |
|
|
203
|
+
|
|
195
204
|
#### 返回值
|
|
196
205
|
|
|
197
206
|
```typescript
|
|
@@ -869,6 +878,38 @@ const { mf } = await loadRemoteMultiVersion(options, [
|
|
|
869
878
|
]);
|
|
870
879
|
```
|
|
871
880
|
|
|
881
|
+
#### 4. 通过 `registerRemotes` 动态注册多个来源
|
|
882
|
+
|
|
883
|
+
```typescript
|
|
884
|
+
import { loadRemoteMultiVersion, createRemoteSourcePlugin } from 'remote-reload-utils';
|
|
885
|
+
|
|
886
|
+
const remoteSourcePlugin = createRemoteSourcePlugin('multi-remote-source', [
|
|
887
|
+
{
|
|
888
|
+
name: 'remote_ui_v2',
|
|
889
|
+
entry: 'https://cdn.example.com/remote-ui-v2/dist/remoteEntry.js',
|
|
890
|
+
},
|
|
891
|
+
{
|
|
892
|
+
name: 'remote_widget',
|
|
893
|
+
entry: 'https://cdn.example.com/remote-widget/dist/remoteEntry.js',
|
|
894
|
+
},
|
|
895
|
+
]);
|
|
896
|
+
|
|
897
|
+
const { scopeName, mf } = await loadRemoteMultiVersion(
|
|
898
|
+
{
|
|
899
|
+
name: 'react_mf_lib',
|
|
900
|
+
pkg: 'test-mf-unpkg',
|
|
901
|
+
version: 'latest',
|
|
902
|
+
},
|
|
903
|
+
[],
|
|
904
|
+
{
|
|
905
|
+
remoteSourcePlugins: [remoteSourcePlugin],
|
|
906
|
+
},
|
|
907
|
+
);
|
|
908
|
+
|
|
909
|
+
const Button = await mf.loadRemote(`${scopeName}/Button`);
|
|
910
|
+
const Widget = await mf.loadRemote('remote_widget/Widget');
|
|
911
|
+
```
|
|
912
|
+
|
|
872
913
|
#### 2. 监控加载状态
|
|
873
914
|
|
|
874
915
|
```typescript
|
|
@@ -1238,7 +1279,31 @@ interface LoadRemoteOptions {
|
|
|
1238
1279
|
localFallback?: string; // 本地兜底
|
|
1239
1280
|
cacheTTL?: number; // 缓存时间
|
|
1240
1281
|
revalidate?: boolean; // 灰度更新
|
|
1241
|
-
shared?: Record<string,
|
|
1282
|
+
shared?: Record<string, any>; // 自定义 shared 配置
|
|
1283
|
+
}
|
|
1284
|
+
|
|
1285
|
+
interface LoadRemoteExtraOptions {
|
|
1286
|
+
remoteSourcePlugins?: RemoteSourcePlugin[];
|
|
1287
|
+
baseRemotes?: RuntimeRemote[];
|
|
1288
|
+
registerOptions?: {
|
|
1289
|
+
force?: boolean;
|
|
1290
|
+
};
|
|
1291
|
+
}
|
|
1292
|
+
|
|
1293
|
+
interface RemoteSourcePluginContext {
|
|
1294
|
+
options: LoadRemoteOptions;
|
|
1295
|
+
scopeName: string;
|
|
1296
|
+
pkg: string;
|
|
1297
|
+
finalVersion: string;
|
|
1298
|
+
currentEntry: string;
|
|
1299
|
+
allEntries: string[];
|
|
1300
|
+
}
|
|
1301
|
+
|
|
1302
|
+
interface RemoteSourcePlugin {
|
|
1303
|
+
name: string;
|
|
1304
|
+
registerRemotes?: (
|
|
1305
|
+
context: RemoteSourcePluginContext
|
|
1306
|
+
) => RuntimeRemote[] | void | Promise<RuntimeRemote[] | void>;
|
|
1242
1307
|
}
|
|
1243
1308
|
|
|
1244
1309
|
interface VersionCache {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "remote-reload-utils",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Utilities for remote reload in Module Federation & React Component",
|
|
6
6
|
"exports": {
|
|
@@ -57,23 +57,6 @@
|
|
|
57
57
|
"optional": true
|
|
58
58
|
}
|
|
59
59
|
},
|
|
60
|
-
"scripts": {
|
|
61
|
-
"build": "rslib build",
|
|
62
|
-
"dev": "rslib build --watch",
|
|
63
|
-
"prepublishOnly": "npm run build",
|
|
64
|
-
"format": "biome format --write",
|
|
65
|
-
"check": "biome check --write",
|
|
66
|
-
"test": "vitest run",
|
|
67
|
-
"test:watch": "vitest",
|
|
68
|
-
"lint": "biome format ./src --write",
|
|
69
|
-
"release:prepare": "node scripts/generateReleasePr.mjs",
|
|
70
|
-
"release:finalize": "node scripts/finalizeRelease.mjs",
|
|
71
|
-
"release:patch": "pnpm release:prepare -t patch",
|
|
72
|
-
"release:minor": "pnpm release:prepare -t minor",
|
|
73
|
-
"release:major": "pnpm release:prepare -t major",
|
|
74
|
-
"publish:dry": "npm publish --dry-run",
|
|
75
|
-
"publish:live": "npm publish"
|
|
76
|
-
},
|
|
77
60
|
"devDependencies": {
|
|
78
61
|
"@biomejs/biome": "2.0.6",
|
|
79
62
|
"@rslib/core": "0.17.0",
|
|
@@ -90,7 +73,26 @@
|
|
|
90
73
|
"typescript": "^5.9.2",
|
|
91
74
|
"vitest": "^2.1.8"
|
|
92
75
|
},
|
|
76
|
+
"publishConfig": {
|
|
77
|
+
"access": "public"
|
|
78
|
+
},
|
|
93
79
|
"dependencies": {
|
|
94
80
|
"@module-federation/enhanced": "0.18.3"
|
|
81
|
+
},
|
|
82
|
+
"scripts": {
|
|
83
|
+
"build": "rslib build",
|
|
84
|
+
"dev": "rslib build --watch",
|
|
85
|
+
"format": "biome format --write",
|
|
86
|
+
"check": "biome check --write",
|
|
87
|
+
"test": "vitest run",
|
|
88
|
+
"test:watch": "vitest",
|
|
89
|
+
"lint": "biome format ./src --write",
|
|
90
|
+
"release:prepare": "node scripts/generateReleasePr.mjs",
|
|
91
|
+
"release:finalize": "node scripts/finalizeRelease.mjs",
|
|
92
|
+
"release:patch": "pnpm release:prepare -t patch",
|
|
93
|
+
"release:minor": "pnpm release:prepare -t minor",
|
|
94
|
+
"release:major": "pnpm release:prepare -t major",
|
|
95
|
+
"publish:dry": "npm publish --dry-run",
|
|
96
|
+
"publish:live": "npm publish"
|
|
95
97
|
}
|
|
96
|
-
}
|
|
98
|
+
}
|