@sbee/vite-federation 0.0.1
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/README.md +168 -0
- package/dist/index.js +1284 -0
- package/dist/index.mjs +1261 -0
- package/dist/runtime/index.d.ts +80 -0
- package/dist/runtime.js +176 -0
- package/dist/runtime.mjs +170 -0
- package/dist/satisfy.js +223 -0
- package/dist/satisfy.mjs +222 -0
- package/package.json +58 -0
- package/types/index.d.ts +334 -0
- package/types/pluginHooks.d.ts +4 -0
- package/types/viteDevServer.d.ts +22 -0
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dynamic Remote Loading Utility
|
|
3
|
+
*
|
|
4
|
+
* Cho phép host-app tải remote modules khi chạy (runtime) mà **không cần**
|
|
5
|
+
* khai báo trước trong `vite.config.ts`.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```ts
|
|
9
|
+
* import { loadRemote, registerRemote, preloadRemote } from '@sbee/vite-federation/runtime'
|
|
10
|
+
*
|
|
11
|
+
* // Cách 1: Load thẳng không cần đăng ký trước
|
|
12
|
+
* const Button = await loadRemote('https://remote.example.com/remoteEntry.js', './Button')
|
|
13
|
+
*
|
|
14
|
+
* // Cách 2: Đăng ký trước rồi load sau
|
|
15
|
+
* registerRemote('my-remote', { url: 'https://remote.example.com/remoteEntry.js' })
|
|
16
|
+
* const Button = await loadRemote('my-remote', './Button')
|
|
17
|
+
*
|
|
18
|
+
* // Cách 3: Preload trước để init sẵn
|
|
19
|
+
* const remote = await preloadRemote('my-remote', { url: 'https://remote.example.com/remoteEntry.js' })
|
|
20
|
+
* const Button = await remote.get('./Button')
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
export interface RemoteConfig {
|
|
24
|
+
/** URL tới remote entry file (remoteEntry.js) hoặc function trả về URL */
|
|
25
|
+
url: string | (() => Promise<string>);
|
|
26
|
+
/** Định dạng remote: 'esm' (import()) hay 'var' (script tag + window global) */
|
|
27
|
+
format?: 'esm' | 'var';
|
|
28
|
+
/** Hệ thống build của remote: 'vite' (mặc định) hay 'webpack' */
|
|
29
|
+
from?: 'vite' | 'webpack';
|
|
30
|
+
/** Share scope name, mặc định 'default' */
|
|
31
|
+
shareScope?: string;
|
|
32
|
+
}
|
|
33
|
+
export interface RemoteHandle {
|
|
34
|
+
/** Tải một module đã expose từ remote */
|
|
35
|
+
get<T = any>(moduleName: string): Promise<T>;
|
|
36
|
+
/** Init lại share scope cho remote */
|
|
37
|
+
init(shareScope: Record<string, any>): void;
|
|
38
|
+
/** Remote đã được init chưa */
|
|
39
|
+
readonly inited: boolean;
|
|
40
|
+
}
|
|
41
|
+
/** Lấy hoặc khởi tạo global share scope */
|
|
42
|
+
export declare function getShareScope(scopeName?: string): Record<string, any>;
|
|
43
|
+
/** Đăng ký shared packages vào share scope (thường dùng bởi host) */
|
|
44
|
+
export declare function provideShared(scopeName: string, packages: Record<string, Record<string, {
|
|
45
|
+
get: () => Promise<() => any>;
|
|
46
|
+
}>>): void;
|
|
47
|
+
/**
|
|
48
|
+
* Đăng ký một remote để dùng sau này.
|
|
49
|
+
* Hàm này đồng bộ, không tải gì cả — chỉ lưu config.
|
|
50
|
+
*/
|
|
51
|
+
export declare function registerRemote(name: string, config: RemoteConfig): void;
|
|
52
|
+
/**
|
|
53
|
+
* Preload & init một remote, trả về handle để gọi `.get()`.
|
|
54
|
+
* Cache lại — gọi nhiều lần với cùng `name` chỉ tải & init một lần,
|
|
55
|
+
* kể cả khi gọi đồng thời (dedup promise in-flight).
|
|
56
|
+
*/
|
|
57
|
+
export declare function preloadRemote(name: string, config?: RemoteConfig): Promise<RemoteHandle>;
|
|
58
|
+
/**
|
|
59
|
+
* Tải một module từ remote (có thể chưa đăng ký trước).
|
|
60
|
+
*
|
|
61
|
+
* @param remoteUrlOrName - URL tới remoteEntry.js HOẶC tên đã đăng ký qua `registerRemote()`
|
|
62
|
+
* @param moduleName - Đường dẫn module đã expose, VD: `'./Button'`, `'./App'`
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* ```ts
|
|
66
|
+
* // Dùng URL trực tiếp
|
|
67
|
+
* const Button = await loadRemote('https://my-app.com/remoteEntry.js', './Button')
|
|
68
|
+
*
|
|
69
|
+
* // Dùng tên đã đăng ký
|
|
70
|
+
* registerRemote('my-remote', { url: 'https://my-app.com/remoteEntry.js' })
|
|
71
|
+
* const Button = await loadRemote('my-remote', './Button')
|
|
72
|
+
* ```
|
|
73
|
+
*/
|
|
74
|
+
export declare function loadRemote<T = any>(remoteUrlOrName: string, moduleName: string, config?: RemoteConfig): Promise<T>;
|
|
75
|
+
/**
|
|
76
|
+
* Tải nhiều module cùng lúc từ một remote.
|
|
77
|
+
*/
|
|
78
|
+
export declare function loadRemoteModules<T extends Record<string, string>>(remoteUrlOrName: string, modules: T, config?: RemoteConfig): Promise<{
|
|
79
|
+
[K in keyof T]: any;
|
|
80
|
+
}>;
|
package/dist/runtime.js
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
//#region src/runtime/index.ts
|
|
3
|
+
var _remoteMap = /* @__PURE__ */ new Map();
|
|
4
|
+
var _remoteConfigMap = /* @__PURE__ */ new Map();
|
|
5
|
+
var _pendingMap = /* @__PURE__ */ new Map();
|
|
6
|
+
/** Lấy hoặc khởi tạo global share scope */
|
|
7
|
+
function getShareScope(scopeName = "default") {
|
|
8
|
+
const g = globalThis;
|
|
9
|
+
g.__federation_shared__ = g.__federation_shared__ || {};
|
|
10
|
+
g.__federation_shared__[scopeName] = g.__federation_shared__[scopeName] || {};
|
|
11
|
+
return g.__federation_shared__[scopeName];
|
|
12
|
+
}
|
|
13
|
+
/** Đăng ký shared packages vào share scope (thường dùng bởi host) */
|
|
14
|
+
function provideShared(scopeName, packages) {
|
|
15
|
+
const scope = getShareScope(scopeName);
|
|
16
|
+
for (const [pkgName, versions] of Object.entries(packages)) {
|
|
17
|
+
scope[pkgName] = scope[pkgName] || {};
|
|
18
|
+
Object.assign(scope[pkgName], versions);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function resolveUrl(url) {
|
|
22
|
+
return typeof url === "function" ? url() : Promise.resolve(url);
|
|
23
|
+
}
|
|
24
|
+
function unwrapModule(module, from) {
|
|
25
|
+
if (from === "webpack") return Object.prototype.toString.call(module).indexOf("Module") > -1 && module.default ? module.default : module;
|
|
26
|
+
return module?.__esModule || module?.[Symbol.toStringTag] === "Module" ? module.default : module;
|
|
27
|
+
}
|
|
28
|
+
function mergeShareScope(target, source) {
|
|
29
|
+
const merged = Object.assign({}, target, source);
|
|
30
|
+
for (const key of Object.keys(merged)) if (typeof merged[key] === "object" && typeof source[key] === "object") merged[key] = mergeShareScope(merged[key], source[key]);
|
|
31
|
+
return merged;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Đăng ký một remote để dùng sau này.
|
|
35
|
+
* Hàm này đồng bộ, không tải gì cả — chỉ lưu config.
|
|
36
|
+
*/
|
|
37
|
+
function registerRemote(name, config) {
|
|
38
|
+
_remoteConfigMap.set(name, config);
|
|
39
|
+
}
|
|
40
|
+
/** Tải + init một remote entry đúng MỘT lần cho mỗi name (dedup promise in-flight). */
|
|
41
|
+
function ensureLoaded(name, cfg, shareScope) {
|
|
42
|
+
const existingEntry = _remoteMap.get(name);
|
|
43
|
+
if (existingEntry?.inited) return Promise.resolve(existingEntry);
|
|
44
|
+
const pending = _pendingMap.get(name);
|
|
45
|
+
if (pending) return pending;
|
|
46
|
+
const tracked = (async () => {
|
|
47
|
+
if (cfg.format === "var") {
|
|
48
|
+
const url = await resolveUrl(cfg.url);
|
|
49
|
+
return await new Promise((resolve, reject) => {
|
|
50
|
+
const finish = (lib) => {
|
|
51
|
+
const wrapped = Object.assign(lib, { inited: false });
|
|
52
|
+
wrapped.init(shareScope);
|
|
53
|
+
wrapped.inited = true;
|
|
54
|
+
resolve(wrapped);
|
|
55
|
+
};
|
|
56
|
+
const existing = window[name];
|
|
57
|
+
if (existing) {
|
|
58
|
+
finish(existing);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
const script = document.createElement("script");
|
|
62
|
+
script.type = "text/javascript";
|
|
63
|
+
script.onload = () => {
|
|
64
|
+
const lib = window[name];
|
|
65
|
+
if (!lib) return reject(/* @__PURE__ */ new Error(`[mf-runtime] Remote "${name}" không expose global "${name}"`));
|
|
66
|
+
finish(lib);
|
|
67
|
+
};
|
|
68
|
+
script.onerror = () => reject(/* @__PURE__ */ new Error(`[mf-runtime] Không thể tải remote "${name}" từ ${url}`));
|
|
69
|
+
script.src = url;
|
|
70
|
+
document.head.appendChild(script);
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
const lib = await import(
|
|
74
|
+
/* @vite-ignore */
|
|
75
|
+
await resolveUrl(cfg.url)
|
|
76
|
+
);
|
|
77
|
+
const wrapped = Object.assign(lib, { inited: false });
|
|
78
|
+
wrapped.init(shareScope);
|
|
79
|
+
wrapped.inited = true;
|
|
80
|
+
return wrapped;
|
|
81
|
+
})().then((entry) => {
|
|
82
|
+
_remoteMap.set(name, entry);
|
|
83
|
+
return entry;
|
|
84
|
+
}).finally(() => {
|
|
85
|
+
_pendingMap.delete(name);
|
|
86
|
+
});
|
|
87
|
+
_pendingMap.set(name, tracked);
|
|
88
|
+
return tracked;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Preload & init một remote, trả về handle để gọi `.get()`.
|
|
92
|
+
* Cache lại — gọi nhiều lần với cùng `name` chỉ tải & init một lần,
|
|
93
|
+
* kể cả khi gọi đồng thời (dedup promise in-flight).
|
|
94
|
+
*/
|
|
95
|
+
async function preloadRemote(name, config) {
|
|
96
|
+
const cfg = config ?? _remoteConfigMap.get(name);
|
|
97
|
+
if (!cfg) throw new Error(`[mf-runtime] Remote "${name}" chưa được đăng ký. Gọi registerRemote() trước hoặc truyền config.`);
|
|
98
|
+
const shareScope = getShareScope(cfg.shareScope || "default");
|
|
99
|
+
await ensureLoaded(name, cfg, shareScope);
|
|
100
|
+
return {
|
|
101
|
+
async get(moduleName) {
|
|
102
|
+
const e = _remoteMap.get(name);
|
|
103
|
+
if (!e) throw new Error(`[mf-runtime] Remote "${name}" chưa được load`);
|
|
104
|
+
return unwrapModule((await e.get(moduleName))(), cfg.from);
|
|
105
|
+
},
|
|
106
|
+
init(shareScopeInput) {
|
|
107
|
+
const e = _remoteMap.get(name);
|
|
108
|
+
if (e) {
|
|
109
|
+
const merged = mergeShareScope(shareScope, shareScopeInput);
|
|
110
|
+
e.init(merged);
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
get inited() {
|
|
114
|
+
return _remoteMap.get(name)?.inited ?? false;
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Phân giải `remoteUrlOrName` thành `{ name, cfg }`:
|
|
120
|
+
* - Nếu là tên đã đăng ký qua `registerRemote()` → dùng config đã lưu (trừ khi truyền `config` override).
|
|
121
|
+
* - Nếu là URL → tạo tên tự động và đăng ký config `{ url }`.
|
|
122
|
+
*/
|
|
123
|
+
function resolveRemote(remoteUrlOrName, config) {
|
|
124
|
+
if (_remoteConfigMap.has(remoteUrlOrName)) return {
|
|
125
|
+
name: remoteUrlOrName,
|
|
126
|
+
cfg: config ?? _remoteConfigMap.get(remoteUrlOrName)
|
|
127
|
+
};
|
|
128
|
+
const name = `__dynamic__${remoteUrlOrName}`;
|
|
129
|
+
const cfg = config ?? { url: remoteUrlOrName };
|
|
130
|
+
if (!_remoteConfigMap.has(name)) registerRemote(name, cfg);
|
|
131
|
+
return {
|
|
132
|
+
name,
|
|
133
|
+
cfg
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Tải một module từ remote (có thể chưa đăng ký trước).
|
|
138
|
+
*
|
|
139
|
+
* @param remoteUrlOrName - URL tới remoteEntry.js HOẶC tên đã đăng ký qua `registerRemote()`
|
|
140
|
+
* @param moduleName - Đường dẫn module đã expose, VD: `'./Button'`, `'./App'`
|
|
141
|
+
*
|
|
142
|
+
* @example
|
|
143
|
+
* ```ts
|
|
144
|
+
* // Dùng URL trực tiếp
|
|
145
|
+
* const Button = await loadRemote('https://my-app.com/remoteEntry.js', './Button')
|
|
146
|
+
*
|
|
147
|
+
* // Dùng tên đã đăng ký
|
|
148
|
+
* registerRemote('my-remote', { url: 'https://my-app.com/remoteEntry.js' })
|
|
149
|
+
* const Button = await loadRemote('my-remote', './Button')
|
|
150
|
+
* ```
|
|
151
|
+
*/
|
|
152
|
+
async function loadRemote(remoteUrlOrName, moduleName, config) {
|
|
153
|
+
const { name, cfg } = resolveRemote(remoteUrlOrName, config);
|
|
154
|
+
return (await preloadRemote(name, cfg)).get(moduleName);
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Tải nhiều module cùng lúc từ một remote.
|
|
158
|
+
*/
|
|
159
|
+
async function loadRemoteModules(remoteUrlOrName, modules, config) {
|
|
160
|
+
const { name, cfg } = resolveRemote(remoteUrlOrName, config);
|
|
161
|
+
const remote = await preloadRemote(name, cfg);
|
|
162
|
+
const entries = Object.entries(modules);
|
|
163
|
+
const results = await Promise.all(entries.map(([, mod]) => remote.get(mod)));
|
|
164
|
+
const result = {};
|
|
165
|
+
entries.forEach(([key], i) => {
|
|
166
|
+
result[key] = results[i];
|
|
167
|
+
});
|
|
168
|
+
return result;
|
|
169
|
+
}
|
|
170
|
+
//#endregion
|
|
171
|
+
exports.getShareScope = getShareScope;
|
|
172
|
+
exports.loadRemote = loadRemote;
|
|
173
|
+
exports.loadRemoteModules = loadRemoteModules;
|
|
174
|
+
exports.preloadRemote = preloadRemote;
|
|
175
|
+
exports.provideShared = provideShared;
|
|
176
|
+
exports.registerRemote = registerRemote;
|
package/dist/runtime.mjs
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
//#region src/runtime/index.ts
|
|
2
|
+
var _remoteMap = /* @__PURE__ */ new Map();
|
|
3
|
+
var _remoteConfigMap = /* @__PURE__ */ new Map();
|
|
4
|
+
var _pendingMap = /* @__PURE__ */ new Map();
|
|
5
|
+
/** Lấy hoặc khởi tạo global share scope */
|
|
6
|
+
function getShareScope(scopeName = "default") {
|
|
7
|
+
const g = globalThis;
|
|
8
|
+
g.__federation_shared__ = g.__federation_shared__ || {};
|
|
9
|
+
g.__federation_shared__[scopeName] = g.__federation_shared__[scopeName] || {};
|
|
10
|
+
return g.__federation_shared__[scopeName];
|
|
11
|
+
}
|
|
12
|
+
/** Đăng ký shared packages vào share scope (thường dùng bởi host) */
|
|
13
|
+
function provideShared(scopeName, packages) {
|
|
14
|
+
const scope = getShareScope(scopeName);
|
|
15
|
+
for (const [pkgName, versions] of Object.entries(packages)) {
|
|
16
|
+
scope[pkgName] = scope[pkgName] || {};
|
|
17
|
+
Object.assign(scope[pkgName], versions);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function resolveUrl(url) {
|
|
21
|
+
return typeof url === "function" ? url() : Promise.resolve(url);
|
|
22
|
+
}
|
|
23
|
+
function unwrapModule(module, from) {
|
|
24
|
+
if (from === "webpack") return Object.prototype.toString.call(module).indexOf("Module") > -1 && module.default ? module.default : module;
|
|
25
|
+
return module?.__esModule || module?.[Symbol.toStringTag] === "Module" ? module.default : module;
|
|
26
|
+
}
|
|
27
|
+
function mergeShareScope(target, source) {
|
|
28
|
+
const merged = Object.assign({}, target, source);
|
|
29
|
+
for (const key of Object.keys(merged)) if (typeof merged[key] === "object" && typeof source[key] === "object") merged[key] = mergeShareScope(merged[key], source[key]);
|
|
30
|
+
return merged;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Đăng ký một remote để dùng sau này.
|
|
34
|
+
* Hàm này đồng bộ, không tải gì cả — chỉ lưu config.
|
|
35
|
+
*/
|
|
36
|
+
function registerRemote(name, config) {
|
|
37
|
+
_remoteConfigMap.set(name, config);
|
|
38
|
+
}
|
|
39
|
+
/** Tải + init một remote entry đúng MỘT lần cho mỗi name (dedup promise in-flight). */
|
|
40
|
+
function ensureLoaded(name, cfg, shareScope) {
|
|
41
|
+
const existingEntry = _remoteMap.get(name);
|
|
42
|
+
if (existingEntry?.inited) return Promise.resolve(existingEntry);
|
|
43
|
+
const pending = _pendingMap.get(name);
|
|
44
|
+
if (pending) return pending;
|
|
45
|
+
const tracked = (async () => {
|
|
46
|
+
if (cfg.format === "var") {
|
|
47
|
+
const url = await resolveUrl(cfg.url);
|
|
48
|
+
return await new Promise((resolve, reject) => {
|
|
49
|
+
const finish = (lib) => {
|
|
50
|
+
const wrapped = Object.assign(lib, { inited: false });
|
|
51
|
+
wrapped.init(shareScope);
|
|
52
|
+
wrapped.inited = true;
|
|
53
|
+
resolve(wrapped);
|
|
54
|
+
};
|
|
55
|
+
const existing = window[name];
|
|
56
|
+
if (existing) {
|
|
57
|
+
finish(existing);
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
const script = document.createElement("script");
|
|
61
|
+
script.type = "text/javascript";
|
|
62
|
+
script.onload = () => {
|
|
63
|
+
const lib = window[name];
|
|
64
|
+
if (!lib) return reject(/* @__PURE__ */ new Error(`[mf-runtime] Remote "${name}" không expose global "${name}"`));
|
|
65
|
+
finish(lib);
|
|
66
|
+
};
|
|
67
|
+
script.onerror = () => reject(/* @__PURE__ */ new Error(`[mf-runtime] Không thể tải remote "${name}" từ ${url}`));
|
|
68
|
+
script.src = url;
|
|
69
|
+
document.head.appendChild(script);
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
const lib = await import(
|
|
73
|
+
/* @vite-ignore */
|
|
74
|
+
await resolveUrl(cfg.url)
|
|
75
|
+
);
|
|
76
|
+
const wrapped = Object.assign(lib, { inited: false });
|
|
77
|
+
wrapped.init(shareScope);
|
|
78
|
+
wrapped.inited = true;
|
|
79
|
+
return wrapped;
|
|
80
|
+
})().then((entry) => {
|
|
81
|
+
_remoteMap.set(name, entry);
|
|
82
|
+
return entry;
|
|
83
|
+
}).finally(() => {
|
|
84
|
+
_pendingMap.delete(name);
|
|
85
|
+
});
|
|
86
|
+
_pendingMap.set(name, tracked);
|
|
87
|
+
return tracked;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Preload & init một remote, trả về handle để gọi `.get()`.
|
|
91
|
+
* Cache lại — gọi nhiều lần với cùng `name` chỉ tải & init một lần,
|
|
92
|
+
* kể cả khi gọi đồng thời (dedup promise in-flight).
|
|
93
|
+
*/
|
|
94
|
+
async function preloadRemote(name, config) {
|
|
95
|
+
const cfg = config ?? _remoteConfigMap.get(name);
|
|
96
|
+
if (!cfg) throw new Error(`[mf-runtime] Remote "${name}" chưa được đăng ký. Gọi registerRemote() trước hoặc truyền config.`);
|
|
97
|
+
const shareScope = getShareScope(cfg.shareScope || "default");
|
|
98
|
+
await ensureLoaded(name, cfg, shareScope);
|
|
99
|
+
return {
|
|
100
|
+
async get(moduleName) {
|
|
101
|
+
const e = _remoteMap.get(name);
|
|
102
|
+
if (!e) throw new Error(`[mf-runtime] Remote "${name}" chưa được load`);
|
|
103
|
+
return unwrapModule((await e.get(moduleName))(), cfg.from);
|
|
104
|
+
},
|
|
105
|
+
init(shareScopeInput) {
|
|
106
|
+
const e = _remoteMap.get(name);
|
|
107
|
+
if (e) {
|
|
108
|
+
const merged = mergeShareScope(shareScope, shareScopeInput);
|
|
109
|
+
e.init(merged);
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
get inited() {
|
|
113
|
+
return _remoteMap.get(name)?.inited ?? false;
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Phân giải `remoteUrlOrName` thành `{ name, cfg }`:
|
|
119
|
+
* - Nếu là tên đã đăng ký qua `registerRemote()` → dùng config đã lưu (trừ khi truyền `config` override).
|
|
120
|
+
* - Nếu là URL → tạo tên tự động và đăng ký config `{ url }`.
|
|
121
|
+
*/
|
|
122
|
+
function resolveRemote(remoteUrlOrName, config) {
|
|
123
|
+
if (_remoteConfigMap.has(remoteUrlOrName)) return {
|
|
124
|
+
name: remoteUrlOrName,
|
|
125
|
+
cfg: config ?? _remoteConfigMap.get(remoteUrlOrName)
|
|
126
|
+
};
|
|
127
|
+
const name = `__dynamic__${remoteUrlOrName}`;
|
|
128
|
+
const cfg = config ?? { url: remoteUrlOrName };
|
|
129
|
+
if (!_remoteConfigMap.has(name)) registerRemote(name, cfg);
|
|
130
|
+
return {
|
|
131
|
+
name,
|
|
132
|
+
cfg
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Tải một module từ remote (có thể chưa đăng ký trước).
|
|
137
|
+
*
|
|
138
|
+
* @param remoteUrlOrName - URL tới remoteEntry.js HOẶC tên đã đăng ký qua `registerRemote()`
|
|
139
|
+
* @param moduleName - Đường dẫn module đã expose, VD: `'./Button'`, `'./App'`
|
|
140
|
+
*
|
|
141
|
+
* @example
|
|
142
|
+
* ```ts
|
|
143
|
+
* // Dùng URL trực tiếp
|
|
144
|
+
* const Button = await loadRemote('https://my-app.com/remoteEntry.js', './Button')
|
|
145
|
+
*
|
|
146
|
+
* // Dùng tên đã đăng ký
|
|
147
|
+
* registerRemote('my-remote', { url: 'https://my-app.com/remoteEntry.js' })
|
|
148
|
+
* const Button = await loadRemote('my-remote', './Button')
|
|
149
|
+
* ```
|
|
150
|
+
*/
|
|
151
|
+
async function loadRemote(remoteUrlOrName, moduleName, config) {
|
|
152
|
+
const { name, cfg } = resolveRemote(remoteUrlOrName, config);
|
|
153
|
+
return (await preloadRemote(name, cfg)).get(moduleName);
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Tải nhiều module cùng lúc từ một remote.
|
|
157
|
+
*/
|
|
158
|
+
async function loadRemoteModules(remoteUrlOrName, modules, config) {
|
|
159
|
+
const { name, cfg } = resolveRemote(remoteUrlOrName, config);
|
|
160
|
+
const remote = await preloadRemote(name, cfg);
|
|
161
|
+
const entries = Object.entries(modules);
|
|
162
|
+
const results = await Promise.all(entries.map(([, mod]) => remote.get(mod)));
|
|
163
|
+
const result = {};
|
|
164
|
+
entries.forEach(([key], i) => {
|
|
165
|
+
result[key] = results[i];
|
|
166
|
+
});
|
|
167
|
+
return result;
|
|
168
|
+
}
|
|
169
|
+
//#endregion
|
|
170
|
+
export { getShareScope, loadRemote, loadRemoteModules, preloadRemote, provideShared, registerRemote };
|
package/dist/satisfy.js
ADDED
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
//#region src/utils/semver/constants.ts
|
|
3
|
+
var buildIdentifier = "[0-9A-Za-z-]+";
|
|
4
|
+
var build = `(?:\\+(${buildIdentifier}(?:\\.${buildIdentifier})*))`;
|
|
5
|
+
var numericIdentifier = "0|[1-9]\\d*";
|
|
6
|
+
var numericIdentifierLoose = "[0-9]+";
|
|
7
|
+
var nonNumericIdentifier = "\\d*[a-zA-Z-][a-zA-Z0-9-]*";
|
|
8
|
+
var preReleaseIdentifierLoose = `(?:${numericIdentifierLoose}|${nonNumericIdentifier})`;
|
|
9
|
+
var preReleaseLoose = `(?:-?(${preReleaseIdentifierLoose}(?:\\.${preReleaseIdentifierLoose})*))`;
|
|
10
|
+
var preReleaseIdentifier = `(?:${numericIdentifier}|${nonNumericIdentifier})`;
|
|
11
|
+
var preRelease = `(?:-(${preReleaseIdentifier}(?:\\.${preReleaseIdentifier})*))`;
|
|
12
|
+
var xRangeIdentifier = `${numericIdentifier}|x|X|\\*`;
|
|
13
|
+
var xRangePlain = `[v=\\s]*(${xRangeIdentifier})(?:\\.(${xRangeIdentifier})(?:\\.(${xRangeIdentifier})(?:${preRelease})?${build}?)?)?`;
|
|
14
|
+
var hyphenRange = new RegExp(`^\\s*(${xRangePlain})\\s+-\\s+(${xRangePlain})\\s*$`);
|
|
15
|
+
var loosePlain = `[v=\\s]*${`(${numericIdentifierLoose})\\.(${numericIdentifierLoose})\\.(${numericIdentifierLoose})`}${preReleaseLoose}?${build}?`;
|
|
16
|
+
var gtlt = "((?:<|>)?=?)";
|
|
17
|
+
var comparatorTrim = new RegExp(`(\\s*)${gtlt}\\s*(${loosePlain}|${xRangePlain})`);
|
|
18
|
+
var loneTilde = "(?:~>?)";
|
|
19
|
+
var tildeTrim = new RegExp(`(\\s*)${loneTilde}\\s+`);
|
|
20
|
+
var loneCaret = "(?:\\^)";
|
|
21
|
+
var caretTrim = new RegExp(`(\\s*)${loneCaret}\\s+`);
|
|
22
|
+
var star = /* @__PURE__ */ new RegExp("(<|>)?=?\\s*\\*");
|
|
23
|
+
var caret = new RegExp(`^${loneCaret}${xRangePlain}$`);
|
|
24
|
+
var fullPlain = `v?${`(${numericIdentifier})\\.(${numericIdentifier})\\.(${numericIdentifier})`}${preRelease}?${build}?`;
|
|
25
|
+
var tilde = new RegExp(`^${loneTilde}${xRangePlain}$`);
|
|
26
|
+
var xRange = new RegExp(`^${gtlt}\\s*${xRangePlain}$`);
|
|
27
|
+
var comparator = new RegExp(`^${gtlt}\\s*(${fullPlain})$|^$`);
|
|
28
|
+
var gte0 = /* @__PURE__ */ new RegExp("^\\s*>=\\s*0.0.0\\s*$");
|
|
29
|
+
//#endregion
|
|
30
|
+
//#region src/utils/semver/utils.ts
|
|
31
|
+
function isXVersion(version) {
|
|
32
|
+
return !version || version.toLowerCase() === "x" || version === "*";
|
|
33
|
+
}
|
|
34
|
+
function pipe(...fns) {
|
|
35
|
+
return (x) => fns.reduce((v, f) => f(v), x);
|
|
36
|
+
}
|
|
37
|
+
function extractComparator(comparatorString) {
|
|
38
|
+
return comparatorString.match(comparator);
|
|
39
|
+
}
|
|
40
|
+
function combineVersion(major, minor, patch, preRelease) {
|
|
41
|
+
const mainVersion = `${major}.${minor}.${patch}`;
|
|
42
|
+
if (preRelease) return `${mainVersion}-${preRelease}`;
|
|
43
|
+
return mainVersion;
|
|
44
|
+
}
|
|
45
|
+
//#endregion
|
|
46
|
+
//#region src/utils/semver/parser.ts
|
|
47
|
+
function parseHyphen(range) {
|
|
48
|
+
return range.replace(hyphenRange, (_range, from, fromMajor, fromMinor, fromPatch, _fromPreRelease, _fromBuild, to, toMajor, toMinor, toPatch, toPreRelease) => {
|
|
49
|
+
if (isXVersion(fromMajor)) from = "";
|
|
50
|
+
else if (isXVersion(fromMinor)) from = `>=${fromMajor}.0.0`;
|
|
51
|
+
else if (isXVersion(fromPatch)) from = `>=${fromMajor}.${fromMinor}.0`;
|
|
52
|
+
else from = `>=${from}`;
|
|
53
|
+
if (isXVersion(toMajor)) to = "";
|
|
54
|
+
else if (isXVersion(toMinor)) to = `<${+toMajor + 1}.0.0-0`;
|
|
55
|
+
else if (isXVersion(toPatch)) to = `<${toMajor}.${+toMinor + 1}.0-0`;
|
|
56
|
+
else if (toPreRelease) to = `<=${toMajor}.${toMinor}.${toPatch}-${toPreRelease}`;
|
|
57
|
+
else to = `<=${to}`;
|
|
58
|
+
return `${from} ${to}`.trim();
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
function parseComparatorTrim(range) {
|
|
62
|
+
return range.replace(comparatorTrim, "$1$2$3");
|
|
63
|
+
}
|
|
64
|
+
function parseTildeTrim(range) {
|
|
65
|
+
return range.replace(tildeTrim, "$1~");
|
|
66
|
+
}
|
|
67
|
+
function parseCaretTrim(range) {
|
|
68
|
+
return range.replace(caretTrim, "$1^");
|
|
69
|
+
}
|
|
70
|
+
function parseCarets(range) {
|
|
71
|
+
return range.trim().split(/\s+/).map((rangeVersion) => {
|
|
72
|
+
return rangeVersion.replace(caret, (_, major, minor, patch, preRelease) => {
|
|
73
|
+
if (isXVersion(major)) return "";
|
|
74
|
+
else if (isXVersion(minor)) return `>=${major}.0.0 <${+major + 1}.0.0-0`;
|
|
75
|
+
else if (isXVersion(patch)) if (major === "0") return `>=${major}.${minor}.0 <${major}.${+minor + 1}.0-0`;
|
|
76
|
+
else return `>=${major}.${minor}.0 <${+major + 1}.0.0-0`;
|
|
77
|
+
else if (preRelease) if (major === "0") if (minor === "0") return `>=${major}.${minor}.${patch}-${preRelease} <${major}.${minor}.${+patch + 1}-0`;
|
|
78
|
+
else return `>=${major}.${minor}.${patch}-${preRelease} <${major}.${+minor + 1}.0-0`;
|
|
79
|
+
else return `>=${major}.${minor}.${patch}-${preRelease} <${+major + 1}.0.0-0`;
|
|
80
|
+
else {
|
|
81
|
+
if (major === "0") if (minor === "0") return `>=${major}.${minor}.${patch} <${major}.${minor}.${+patch + 1}-0`;
|
|
82
|
+
else return `>=${major}.${minor}.${patch} <${major}.${+minor + 1}.0-0`;
|
|
83
|
+
return `>=${major}.${minor}.${patch} <${+major + 1}.0.0-0`;
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
}).join(" ");
|
|
87
|
+
}
|
|
88
|
+
function parseTildes(range) {
|
|
89
|
+
return range.trim().split(/\s+/).map((rangeVersion) => {
|
|
90
|
+
return rangeVersion.replace(tilde, (_, major, minor, patch, preRelease) => {
|
|
91
|
+
if (isXVersion(major)) return "";
|
|
92
|
+
else if (isXVersion(minor)) return `>=${major}.0.0 <${+major + 1}.0.0-0`;
|
|
93
|
+
else if (isXVersion(patch)) return `>=${major}.${minor}.0 <${major}.${+minor + 1}.0-0`;
|
|
94
|
+
else if (preRelease) return `>=${major}.${minor}.${patch}-${preRelease} <${major}.${+minor + 1}.0-0`;
|
|
95
|
+
return `>=${major}.${minor}.${patch} <${major}.${+minor + 1}.0-0`;
|
|
96
|
+
});
|
|
97
|
+
}).join(" ");
|
|
98
|
+
}
|
|
99
|
+
function parseXRanges(range) {
|
|
100
|
+
return range.split(/\s+/).map((rangeVersion) => {
|
|
101
|
+
return rangeVersion.trim().replace(xRange, (ret, gtlt, major, minor, patch, preRelease) => {
|
|
102
|
+
const isXMajor = isXVersion(major);
|
|
103
|
+
const isXMinor = isXMajor || isXVersion(minor);
|
|
104
|
+
const isXPatch = isXMinor || isXVersion(patch);
|
|
105
|
+
if (gtlt === "=" && isXPatch) gtlt = "";
|
|
106
|
+
preRelease = "";
|
|
107
|
+
if (isXMajor) if (gtlt === ">" || gtlt === "<") return "<0.0.0-0";
|
|
108
|
+
else return "*";
|
|
109
|
+
else if (gtlt && isXPatch) {
|
|
110
|
+
if (isXMinor) minor = 0;
|
|
111
|
+
patch = 0;
|
|
112
|
+
if (gtlt === ">") {
|
|
113
|
+
gtlt = ">=";
|
|
114
|
+
if (isXMinor) {
|
|
115
|
+
major = +major + 1;
|
|
116
|
+
minor = 0;
|
|
117
|
+
patch = 0;
|
|
118
|
+
} else {
|
|
119
|
+
minor = +minor + 1;
|
|
120
|
+
patch = 0;
|
|
121
|
+
}
|
|
122
|
+
} else if (gtlt === "<=") {
|
|
123
|
+
gtlt = "<";
|
|
124
|
+
if (isXMinor) major = +major + 1;
|
|
125
|
+
else minor = +minor + 1;
|
|
126
|
+
}
|
|
127
|
+
if (gtlt === "<") preRelease = "-0";
|
|
128
|
+
return `${gtlt + major}.${minor}.${patch}${preRelease}`;
|
|
129
|
+
} else if (isXMinor) return `>=${major}.0.0${preRelease} <${+major + 1}.0.0-0`;
|
|
130
|
+
else if (isXPatch) return `>=${major}.${minor}.0${preRelease} <${major}.${+minor + 1}.0-0`;
|
|
131
|
+
return ret;
|
|
132
|
+
});
|
|
133
|
+
}).join(" ");
|
|
134
|
+
}
|
|
135
|
+
function parseStar(range) {
|
|
136
|
+
return range.trim().replace(star, "");
|
|
137
|
+
}
|
|
138
|
+
function parseGTE0(comparatorString) {
|
|
139
|
+
return comparatorString.trim().replace(gte0, "");
|
|
140
|
+
}
|
|
141
|
+
//#endregion
|
|
142
|
+
//#region src/utils/semver/compare.ts
|
|
143
|
+
function compareAtom(rangeAtom, versionAtom) {
|
|
144
|
+
rangeAtom = +rangeAtom || rangeAtom;
|
|
145
|
+
versionAtom = +versionAtom || versionAtom;
|
|
146
|
+
if (rangeAtom > versionAtom) return 1;
|
|
147
|
+
if (rangeAtom === versionAtom) return 0;
|
|
148
|
+
return -1;
|
|
149
|
+
}
|
|
150
|
+
function comparePreRelease(rangeAtom, versionAtom) {
|
|
151
|
+
const { preRelease: rangePreRelease } = rangeAtom;
|
|
152
|
+
const { preRelease: versionPreRelease } = versionAtom;
|
|
153
|
+
if (rangePreRelease === void 0 && !!versionPreRelease) return 1;
|
|
154
|
+
if (!!rangePreRelease && versionPreRelease === void 0) return -1;
|
|
155
|
+
if (rangePreRelease === void 0 && versionPreRelease === void 0) return 0;
|
|
156
|
+
for (let i = 0, n = rangePreRelease.length; i <= n; i++) {
|
|
157
|
+
const rangeElement = rangePreRelease[i];
|
|
158
|
+
const versionElement = versionPreRelease[i];
|
|
159
|
+
if (rangeElement === versionElement) continue;
|
|
160
|
+
if (rangeElement === void 0 && versionElement === void 0) return 0;
|
|
161
|
+
if (!rangeElement) return 1;
|
|
162
|
+
if (!versionElement) return -1;
|
|
163
|
+
return compareAtom(rangeElement, versionElement);
|
|
164
|
+
}
|
|
165
|
+
return 0;
|
|
166
|
+
}
|
|
167
|
+
function compareVersion(rangeAtom, versionAtom) {
|
|
168
|
+
return compareAtom(rangeAtom.major, versionAtom.major) || compareAtom(rangeAtom.minor, versionAtom.minor) || compareAtom(rangeAtom.patch, versionAtom.patch) || comparePreRelease(rangeAtom, versionAtom);
|
|
169
|
+
}
|
|
170
|
+
function eq(rangeAtom, versionAtom) {
|
|
171
|
+
return rangeAtom.version === versionAtom.version;
|
|
172
|
+
}
|
|
173
|
+
function compare(rangeAtom, versionAtom) {
|
|
174
|
+
switch (rangeAtom.operator) {
|
|
175
|
+
case "":
|
|
176
|
+
case "=": return eq(rangeAtom, versionAtom);
|
|
177
|
+
case ">": return compareVersion(rangeAtom, versionAtom) < 0;
|
|
178
|
+
case ">=": return eq(rangeAtom, versionAtom) || compareVersion(rangeAtom, versionAtom) < 0;
|
|
179
|
+
case "<": return compareVersion(rangeAtom, versionAtom) > 0;
|
|
180
|
+
case "<=": return eq(rangeAtom, versionAtom) || compareVersion(rangeAtom, versionAtom) > 0;
|
|
181
|
+
case void 0: return true;
|
|
182
|
+
default: return false;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
//#endregion
|
|
186
|
+
//#region src/utils/semver/satisfy.ts
|
|
187
|
+
function parseComparatorString(range) {
|
|
188
|
+
return pipe(parseCarets, parseTildes, parseXRanges, parseStar)(range);
|
|
189
|
+
}
|
|
190
|
+
function parseRange(range) {
|
|
191
|
+
return pipe(parseHyphen, parseComparatorTrim, parseTildeTrim, parseCaretTrim)(range.trim()).split(/\s+/).join(" ");
|
|
192
|
+
}
|
|
193
|
+
function satisfy(version, range) {
|
|
194
|
+
if (!version) return false;
|
|
195
|
+
const comparators = parseRange(range).split(" ").map((rangeVersion) => parseComparatorString(rangeVersion)).join(" ").split(/\s+/).map((comparator) => parseGTE0(comparator));
|
|
196
|
+
const extractedVersion = extractComparator(version);
|
|
197
|
+
if (!extractedVersion) return false;
|
|
198
|
+
const [, versionOperator, , versionMajor, versionMinor, versionPatch, versionPreRelease] = extractedVersion;
|
|
199
|
+
const versionAtom = {
|
|
200
|
+
operator: versionOperator,
|
|
201
|
+
version: combineVersion(versionMajor, versionMinor, versionPatch, versionPreRelease),
|
|
202
|
+
major: versionMajor,
|
|
203
|
+
minor: versionMinor,
|
|
204
|
+
patch: versionPatch,
|
|
205
|
+
preRelease: versionPreRelease?.split(".")
|
|
206
|
+
};
|
|
207
|
+
for (const comparator of comparators) {
|
|
208
|
+
const extractedComparator = extractComparator(comparator);
|
|
209
|
+
if (!extractedComparator) return false;
|
|
210
|
+
const [, rangeOperator, , rangeMajor, rangeMinor, rangePatch, rangePreRelease] = extractedComparator;
|
|
211
|
+
if (!compare({
|
|
212
|
+
operator: rangeOperator,
|
|
213
|
+
version: combineVersion(rangeMajor, rangeMinor, rangePatch, rangePreRelease),
|
|
214
|
+
major: rangeMajor,
|
|
215
|
+
minor: rangeMinor,
|
|
216
|
+
patch: rangePatch,
|
|
217
|
+
preRelease: rangePreRelease?.split(".")
|
|
218
|
+
}, versionAtom)) return false;
|
|
219
|
+
}
|
|
220
|
+
return true;
|
|
221
|
+
}
|
|
222
|
+
//#endregion
|
|
223
|
+
exports.satisfy = satisfy;
|