@rollipop/plugin-module-federation 1.0.0-alpha.27 → 1.0.0-alpha.29
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 +1 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.js +7 -152
- package/package.json +8 -7
package/README.md
CHANGED
|
@@ -93,7 +93,7 @@ export default defineConfig({
|
|
|
93
93
|
});
|
|
94
94
|
```
|
|
95
95
|
|
|
96
|
-
The bundle is emitted as IIFE. Each shared dep import (e.g. `import RN from 'react-native'`) is replaced with a stub that reads from the host's `globalThis.__rollipop_shared__` registry and throws if the dep is not registered. Rollipop's prelude
|
|
96
|
+
The bundle is emitted as IIFE. Each shared dep import (e.g. `import RN from 'react-native'`) is replaced with a stub that reads from the host's `globalThis.__rollipop_shared__` registry and throws if the dep is not registered. Rollipop's prelude and polyfills are skipped because the host has already initialized the React Native runtime.
|
|
97
97
|
|
|
98
98
|
## Script loader contract
|
|
99
99
|
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -15,8 +15,6 @@ const VIRTUAL_REMOTE_PROXY_PREFIX = `${VIRTUAL_PREFIX}remote:`;
|
|
|
15
15
|
const SCRIPT_LOADER_GLOBAL = "__rollipop_script_loader__";
|
|
16
16
|
const SHARED_REGISTRY_GLOBAL = "__rollipop_shared__";
|
|
17
17
|
const REMOTE_CACHE_GLOBAL = "__rollipop_module_federation_cache__";
|
|
18
|
-
const HMR_HOT_PATH = "/hot";
|
|
19
|
-
const HMR_EVENT = "mf:remote-update";
|
|
20
18
|
//#endregion
|
|
21
19
|
//#region src/virtual/_dedent.ts
|
|
22
20
|
const dedent = baseDedent.withOptions({ escapeSpecialCharacters: false });
|
|
@@ -64,18 +62,10 @@ function generateHostInitCode(config) {
|
|
|
64
62
|
shareStrategy: ${JSON.stringify(config.shareStrategy)},
|
|
65
63
|
});
|
|
66
64
|
|
|
67
|
-
const remoteList = ${JSON.stringify(remotesArray)};
|
|
68
|
-
|
|
69
65
|
if (globalThis.${REMOTE_CACHE_GLOBAL} == null) {
|
|
70
66
|
const cache = {
|
|
71
67
|
modules: Object.create(null),
|
|
72
68
|
pending: Object.create(null),
|
|
73
|
-
// Per-id invalidation: only ids actually present in the cache when
|
|
74
|
-
// HMR fired need to bypass the federation runtime. Cleared after
|
|
75
|
-
// each successful bypass load — new ids fall back to the normal
|
|
76
|
-
// loadRemote flow with full shared-module negotiation.
|
|
77
|
-
invalidatedIds: new Set(),
|
|
78
|
-
subscribers: new Set(),
|
|
79
69
|
load(id) {
|
|
80
70
|
if (this.modules[id] !== undefined) {
|
|
81
71
|
return Promise.resolve(this.modules[id]);
|
|
@@ -83,126 +73,17 @@ function generateHostInitCode(config) {
|
|
|
83
73
|
if (this.pending[id]) {
|
|
84
74
|
return this.pending[id];
|
|
85
75
|
}
|
|
86
|
-
const
|
|
87
|
-
let fetcher;
|
|
88
|
-
if (isInvalidated) {
|
|
89
|
-
const slash = id.indexOf('/');
|
|
90
|
-
const remoteName = slash === -1 ? id : id.slice(0, slash);
|
|
91
|
-
const exposePath = slash === -1 ? '.' : './' + id.slice(slash + 1);
|
|
92
|
-
fetcher = Promise.resolve().then(() => {
|
|
93
|
-
const container = globalThis[remoteName];
|
|
94
|
-
if (container == null) {
|
|
95
|
-
throw new Error('[rollipop:module-federation] container ${Q}' + remoteName + '${Q} not registered');
|
|
96
|
-
}
|
|
97
|
-
return container.get(exposePath).then((factory) => {
|
|
98
|
-
return typeof factory === 'function' ? factory() : factory;
|
|
99
|
-
});
|
|
100
|
-
});
|
|
101
|
-
} else {
|
|
102
|
-
fetcher = instance.loadRemote(id);
|
|
103
|
-
}
|
|
76
|
+
const fetcher = instance.loadRemote(id);
|
|
104
77
|
this.pending[id] = fetcher.then((mod) => {
|
|
105
78
|
this.modules[id] = mod;
|
|
106
79
|
delete this.pending[id];
|
|
107
|
-
if (isInvalidated) {
|
|
108
|
-
this.invalidatedIds.delete(id);
|
|
109
|
-
}
|
|
110
80
|
return mod;
|
|
111
81
|
});
|
|
112
82
|
return this.pending[id];
|
|
113
83
|
},
|
|
114
|
-
invalidate(remoteName) {
|
|
115
|
-
for (const key of Object.keys(this.modules)) {
|
|
116
|
-
if (key === remoteName || key.startsWith(remoteName + '/')) {
|
|
117
|
-
this.invalidatedIds.add(key);
|
|
118
|
-
delete this.modules[key];
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
for (const cb of this.subscribers) {
|
|
122
|
-
try {
|
|
123
|
-
cb(remoteName);
|
|
124
|
-
} catch (_e) {}
|
|
125
|
-
}
|
|
126
|
-
},
|
|
127
84
|
};
|
|
128
85
|
globalThis.${REMOTE_CACHE_GLOBAL} = cache;
|
|
129
86
|
}
|
|
130
|
-
|
|
131
|
-
const __cache = globalThis.${REMOTE_CACHE_GLOBAL};
|
|
132
|
-
|
|
133
|
-
async function applyRemoteUpdate(remote) {
|
|
134
|
-
const generation = (remote.__mfHmrGen = (remote.__mfHmrGen || 0) + 1);
|
|
135
|
-
const cacheBust = (remote.entry.indexOf('?') === -1 ? '?' : '&') + '_mf_hmr=' + generation;
|
|
136
|
-
try {
|
|
137
|
-
await globalThis.${SCRIPT_LOADER_GLOBAL}.loadScript({
|
|
138
|
-
scriptId: remote.name + '@' + generation,
|
|
139
|
-
url: remote.entry + cacheBust,
|
|
140
|
-
});
|
|
141
|
-
} catch (error) {
|
|
142
|
-
console.error(
|
|
143
|
-
'[rollipop:module-federation] failed to reload remote ${Q}' + remote.name + '${Q} during HMR. App is running stale code until next successful update.',
|
|
144
|
-
error,
|
|
145
|
-
);
|
|
146
|
-
return;
|
|
147
|
-
}
|
|
148
|
-
__cache.invalidate(remote.name);
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
function subscribeRemoteHmr(remote) {
|
|
152
|
-
let wsUrl;
|
|
153
|
-
try {
|
|
154
|
-
const url = new URL(remote.entry);
|
|
155
|
-
const wsProtocol = url.protocol === 'https:' ? 'wss:' : 'ws:';
|
|
156
|
-
wsUrl = wsProtocol + '//' + url.host + ${JSON.stringify(HMR_HOT_PATH)};
|
|
157
|
-
} catch (_e) {
|
|
158
|
-
return;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
const connect = () => {
|
|
162
|
-
let socket;
|
|
163
|
-
try {
|
|
164
|
-
socket = new WebSocket(wsUrl);
|
|
165
|
-
} catch (_e) {
|
|
166
|
-
setTimeout(connect, 1000);
|
|
167
|
-
return;
|
|
168
|
-
}
|
|
169
|
-
socket.onmessage = (event) => {
|
|
170
|
-
let parsed;
|
|
171
|
-
try {
|
|
172
|
-
parsed = JSON.parse(typeof event.data === 'string' ? event.data : '');
|
|
173
|
-
} catch (_e) {
|
|
174
|
-
return;
|
|
175
|
-
}
|
|
176
|
-
if (parsed == null || parsed.type !== ${JSON.stringify(HMR_EVENT)}) {
|
|
177
|
-
return;
|
|
178
|
-
}
|
|
179
|
-
if (parsed.payload != null && parsed.payload.name !== remote.name) {
|
|
180
|
-
return;
|
|
181
|
-
}
|
|
182
|
-
applyRemoteUpdate(remote);
|
|
183
|
-
};
|
|
184
|
-
socket.onclose = () => setTimeout(connect, 1000);
|
|
185
|
-
socket.onerror = () => {
|
|
186
|
-
try {
|
|
187
|
-
socket.close();
|
|
188
|
-
} catch (_e) {}
|
|
189
|
-
};
|
|
190
|
-
};
|
|
191
|
-
connect();
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
// Defer to the next tick so React Native's InitializeCore has set up
|
|
195
|
-
// WebSocket / setTimeout before the subscription starts.
|
|
196
|
-
if (typeof __DEV__ !== 'undefined' && __DEV__) {
|
|
197
|
-
Promise.resolve().then(() => {
|
|
198
|
-
if (typeof WebSocket === 'undefined') {
|
|
199
|
-
return;
|
|
200
|
-
}
|
|
201
|
-
for (const r of remoteList) {
|
|
202
|
-
subscribeRemoteHmr(r);
|
|
203
|
-
}
|
|
204
|
-
});
|
|
205
|
-
}
|
|
206
87
|
`;
|
|
207
88
|
}
|
|
208
89
|
//#endregion
|
|
@@ -383,15 +264,6 @@ function generateRemoteProxyCode({ remoteId, reactAware }) {
|
|
|
383
264
|
}
|
|
384
265
|
|
|
385
266
|
function __FederatedProxy(props) {
|
|
386
|
-
const [, setVersion] = __mfReact.useState(0);
|
|
387
|
-
__mfReact.useEffect(() => {
|
|
388
|
-
const listener = () => setVersion((v) => v + 1);
|
|
389
|
-
__cache.subscribers.add(listener);
|
|
390
|
-
return () => {
|
|
391
|
-
__cache.subscribers.delete(listener);
|
|
392
|
-
};
|
|
393
|
-
}, []);
|
|
394
|
-
|
|
395
267
|
const mod = __getMod();
|
|
396
268
|
if (mod === undefined) {
|
|
397
269
|
throw __ensureLoaded();
|
|
@@ -483,26 +355,23 @@ function moduleFederationPlugin(config) {
|
|
|
483
355
|
if (hasRemotes && hasExposes) throw new Error(`[${PLUGIN_NAME}] A single config cannot define both 'remotes' and 'exposes'. Split into two configs (one host, one remote) and run them as separate Rollipop processes.`);
|
|
484
356
|
let resolvedConfig = null;
|
|
485
357
|
let normalized = null;
|
|
486
|
-
let broadcast = null;
|
|
487
358
|
const exposesAbsolute = {};
|
|
488
359
|
const sharedRoots = new Set(collectSharedNames(federationConfig));
|
|
489
|
-
let debounceTimer = null;
|
|
490
360
|
return {
|
|
491
361
|
name: PLUGIN_NAME,
|
|
492
362
|
config(pluginConfig) {
|
|
493
|
-
const serializer = pluginConfig.serializer ??= {};
|
|
494
363
|
if (hasExposes) {
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
return {
|
|
364
|
+
pluginConfig.prelude = [];
|
|
365
|
+
pluginConfig.polyfills = [];
|
|
366
|
+
return { rolldownOptions: (opts, context) => ({
|
|
498
367
|
input: opts.input,
|
|
499
368
|
output: {
|
|
500
369
|
...opts.output,
|
|
501
|
-
format: "iife"
|
|
370
|
+
format: context.buildType === "serve" ? "rollipop" : "iife"
|
|
502
371
|
}
|
|
503
372
|
}) };
|
|
504
373
|
}
|
|
505
|
-
if (hasRemotes && config.runtime?.implement != null) (
|
|
374
|
+
if (hasRemotes && config.runtime?.implement != null) (pluginConfig.polyfills ??= []).push({
|
|
506
375
|
type: "iife",
|
|
507
376
|
code: config.runtime.implement
|
|
508
377
|
});
|
|
@@ -565,21 +434,7 @@ function moduleFederationPlugin(config) {
|
|
|
565
434
|
return { code: magicString };
|
|
566
435
|
}
|
|
567
436
|
return null;
|
|
568
|
-
} }
|
|
569
|
-
watchChange() {
|
|
570
|
-
if (!hasExposes || broadcast == null) return;
|
|
571
|
-
if (debounceTimer != null) clearTimeout(debounceTimer);
|
|
572
|
-
debounceTimer = setTimeout(() => {
|
|
573
|
-
debounceTimer = null;
|
|
574
|
-
broadcast?.();
|
|
575
|
-
}, 100);
|
|
576
|
-
},
|
|
577
|
-
configureServer(server) {
|
|
578
|
-
if (!hasExposes) return;
|
|
579
|
-
broadcast = () => {
|
|
580
|
-
server.hot.sendAll(HMR_EVENT, { name: federationConfig.name });
|
|
581
|
-
};
|
|
582
|
-
}
|
|
437
|
+
} }
|
|
583
438
|
};
|
|
584
439
|
}
|
|
585
440
|
function collectSharedNames(config) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rollipop/plugin-module-federation",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.29",
|
|
4
4
|
"stableVersion": "1.0.0-alpha.23",
|
|
5
5
|
"homepage": "https://github.com/rollipop-dev/rollipop#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -37,17 +37,18 @@
|
|
|
37
37
|
"build": "vp pack"
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@module-federation/runtime": "2.
|
|
41
|
-
"@module-federation/sdk": "2.5.1",
|
|
40
|
+
"@module-federation/runtime": "2.8.0",
|
|
42
41
|
"dedent": "1.7.2",
|
|
43
|
-
"es-toolkit": "1.
|
|
42
|
+
"es-toolkit": "1.49.0"
|
|
44
43
|
},
|
|
45
44
|
"devDependencies": {
|
|
46
|
-
"
|
|
45
|
+
"@vitest/coverage-v8": "4.1.10",
|
|
46
|
+
"rollipop": "1.0.0-alpha.29",
|
|
47
47
|
"typescript": "6.0.3",
|
|
48
|
-
"vite-plus": "0.
|
|
48
|
+
"vite-plus": "0.2.5",
|
|
49
|
+
"vitest": "4.1.10"
|
|
49
50
|
},
|
|
50
51
|
"peerDependencies": {
|
|
51
|
-
"rollipop": "1.0.0-alpha.
|
|
52
|
+
"rollipop": "1.0.0-alpha.29"
|
|
52
53
|
}
|
|
53
54
|
}
|