@module-federation/vite 1.0.0-alpha-54ad191 → 1.0.0-alpha-4a73cd4
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/lib/index.cjs +412 -352
- package/lib/index.esm.js +413 -351
- package/lib/index.modern.js +460 -333
- package/lib/index.umd.js +414 -354
- package/lib/plugins/pluginProxyRemoteEntry.d.ts +2 -0
- package/lib/plugins/pluginProxyRemotes.d.ts +3 -0
- package/lib/plugins/pluginProxySharedModule_preBuild.d.ts +7 -0
- package/lib/utils/normalizeModuleFederationOptions.d.ts +2 -1
- package/lib/virtualModules/virtualRemoteEntry.d.ts +11 -0
- package/lib/virtualModules/virtualRemotes.d.ts +5 -0
- package/lib/virtualModules/virtualShared_preBuild.d.ts +12 -0
- package/package.json +4 -2
- package/lib/utils/vitePluginOverrideModule.d.ts +0 -6
- /package/lib/{utils/vitePluginAddEntry.d.ts → plugins/pluginAddEntry.d.ts} +0 -0
package/lib/index.modern.js
CHANGED
|
@@ -1,60 +1,89 @@
|
|
|
1
|
-
import { createFilter } from '@rollup/pluginutils';
|
|
2
|
-
import * as path from 'path';
|
|
3
|
-
import path__default from 'path';
|
|
4
1
|
import * as fs from 'fs';
|
|
2
|
+
import { writeFileSync } from 'fs';
|
|
3
|
+
import * as path from 'pathe';
|
|
4
|
+
import { resolve } from 'pathe';
|
|
5
|
+
import { createFilter } from '@rollup/pluginutils';
|
|
5
6
|
import { walk } from 'estree-walker';
|
|
6
7
|
import MagicString from 'magic-string';
|
|
7
8
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
}
|
|
40
|
-
}));
|
|
41
|
-
function normalizeManualChunks(output, singleChunkModules) {
|
|
42
|
-
const pattern = new RegExp(`node_modules/(${singleChunkModules.join('|')})/`);
|
|
43
|
-
if (!output.manualChunks) output.manualChunks = {};
|
|
44
|
-
const wrapManualChunks = original => (id, ...args) => {
|
|
45
|
-
const [_, moduleName] = id.match(pattern) || [];
|
|
46
|
-
if (moduleName) {
|
|
47
|
-
return moduleName;
|
|
48
|
-
}
|
|
49
|
-
if (typeof original === 'function') {
|
|
50
|
-
return original(id, ...args);
|
|
9
|
+
const addEntry = ({
|
|
10
|
+
entryName,
|
|
11
|
+
entryPath,
|
|
12
|
+
fileName
|
|
13
|
+
}) => {
|
|
14
|
+
let entryFiles = [];
|
|
15
|
+
let htmlFilePath;
|
|
16
|
+
return [{
|
|
17
|
+
name: 'add-entry',
|
|
18
|
+
apply: "serve",
|
|
19
|
+
configureServer(server) {
|
|
20
|
+
var _server$httpServer;
|
|
21
|
+
(_server$httpServer = server.httpServer) == null || _server$httpServer.once == null || _server$httpServer.once('listening', () => {
|
|
22
|
+
const {
|
|
23
|
+
port
|
|
24
|
+
} = server.config.server;
|
|
25
|
+
fetch(path.join(`http://localhost:${port}`, `${entryPath}`)).catch(e => {});
|
|
26
|
+
});
|
|
27
|
+
server.middlewares.use((req, res, next) => {
|
|
28
|
+
if (!fileName) {
|
|
29
|
+
next();
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
if (req.url && req.url.startsWith(fileName.replace(/^\/?/, '/'))) {
|
|
33
|
+
req.url = entryPath;
|
|
34
|
+
}
|
|
35
|
+
next();
|
|
36
|
+
});
|
|
37
|
+
},
|
|
38
|
+
transformIndexHtml(c) {
|
|
39
|
+
return c.replace('<head>', `<head><script type="module" src=${JSON.stringify(entryPath.replace(/.+?\:([/\\])[/\\]?/, '$1').replace(/\\\\?/g, '/'))}></script>`);
|
|
51
40
|
}
|
|
52
|
-
|
|
53
|
-
|
|
41
|
+
}, {
|
|
42
|
+
name: "add-entry",
|
|
43
|
+
enforce: "post",
|
|
44
|
+
apply: "build",
|
|
45
|
+
configResolved(config) {
|
|
46
|
+
const inputOptions = config.build.rollupOptions.input;
|
|
47
|
+
if (!inputOptions) {
|
|
48
|
+
htmlFilePath = path.resolve(config.root, 'index.html');
|
|
49
|
+
} else if (typeof inputOptions === 'string') {
|
|
50
|
+
entryFiles = [inputOptions];
|
|
51
|
+
htmlFilePath = path.resolve(config.root, inputOptions);
|
|
52
|
+
} else if (Array.isArray(inputOptions)) {
|
|
53
|
+
entryFiles = inputOptions;
|
|
54
|
+
htmlFilePath = path.resolve(config.root, inputOptions[0]);
|
|
55
|
+
} else if (typeof inputOptions === 'object') {
|
|
56
|
+
entryFiles = Object.values(inputOptions);
|
|
57
|
+
htmlFilePath = path.resolve(config.root, Object.values(inputOptions)[0]);
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
buildStart() {
|
|
61
|
+
const hasHash = fileName == null || fileName.includes == null ? void 0 : fileName.includes("[hash");
|
|
62
|
+
this.emitFile({
|
|
63
|
+
[hasHash ? "name" : "fileName"]: fileName,
|
|
64
|
+
type: 'chunk',
|
|
65
|
+
id: entryPath,
|
|
66
|
+
preserveSignature: 'strict'
|
|
67
|
+
});
|
|
68
|
+
if (htmlFilePath) {
|
|
69
|
+
const htmlContent = fs.readFileSync(htmlFilePath, 'utf-8');
|
|
70
|
+
const scriptRegex = /<script\s+[^>]*src=["']([^"']+)["'][^>]*>/gi;
|
|
71
|
+
let match;
|
|
72
|
+
while ((match = scriptRegex.exec(htmlContent)) !== null) {
|
|
73
|
+
entryFiles.push(match[1]);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
transform(code, id) {
|
|
78
|
+
if (entryFiles.some(file => id.endsWith(file))) {
|
|
79
|
+
const injection = `
|
|
80
|
+
import ${JSON.stringify(entryPath)};
|
|
81
|
+
`;
|
|
82
|
+
return injection + code;
|
|
83
|
+
}
|
|
54
84
|
}
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
}
|
|
85
|
+
}];
|
|
86
|
+
};
|
|
58
87
|
|
|
59
88
|
function normalizeExposesItem(key, item) {
|
|
60
89
|
let importPath = '';
|
|
@@ -72,7 +101,7 @@ function normalizeExposes(exposes) {
|
|
|
72
101
|
if (!exposes) return {};
|
|
73
102
|
const res = {};
|
|
74
103
|
Object.keys(exposes).forEach(key => {
|
|
75
|
-
res[key
|
|
104
|
+
res[key] = normalizeExposesItem(key, exposes[key]);
|
|
76
105
|
});
|
|
77
106
|
return res;
|
|
78
107
|
}
|
|
@@ -108,7 +137,7 @@ function normalizeRemoteItem(key, remote) {
|
|
|
108
137
|
function normalizeShareItem(key, shareItem) {
|
|
109
138
|
let version;
|
|
110
139
|
try {
|
|
111
|
-
version = require(
|
|
140
|
+
version = require(path.join(key, 'package.json')).version;
|
|
112
141
|
} catch (e) {
|
|
113
142
|
console.log(e);
|
|
114
143
|
}
|
|
@@ -156,8 +185,12 @@ function normalizeLibrary(library) {
|
|
|
156
185
|
if (!library) return undefined;
|
|
157
186
|
return library;
|
|
158
187
|
}
|
|
188
|
+
let config;
|
|
189
|
+
function getNormalizeModuleFederationOptions() {
|
|
190
|
+
return config;
|
|
191
|
+
}
|
|
159
192
|
function normalizeModuleFederationOptions(options) {
|
|
160
|
-
return {
|
|
193
|
+
return config = {
|
|
161
194
|
exposes: normalizeExposes(options.exposes),
|
|
162
195
|
filename: options.filename || 'remoteEntry-[hash]',
|
|
163
196
|
library: normalizeLibrary(options.library),
|
|
@@ -176,197 +209,82 @@ function normalizeModuleFederationOptions(options) {
|
|
|
176
209
|
};
|
|
177
210
|
}
|
|
178
211
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
server.middlewares.use((req, res, next) => {
|
|
215
|
-
if (!fileName) {
|
|
216
|
-
next();
|
|
217
|
-
return;
|
|
218
|
-
}
|
|
219
|
-
if (req.url && req.url.startsWith(fileName.replace(/^\/?/, '/'))) {
|
|
220
|
-
req.url = entryPath;
|
|
221
|
-
}
|
|
222
|
-
next();
|
|
223
|
-
});
|
|
224
|
-
},
|
|
225
|
-
transformIndexHtml(c) {
|
|
226
|
-
return c.replace('<head>', `<head><script type="module" src=${JSON.stringify(entryPath.replace(/.+?\:([/\\])[/\\]?/, '$1').replace(/\\\\/g, '/'))}></script>`);
|
|
227
|
-
}
|
|
228
|
-
}, {
|
|
229
|
-
name: "add-entry",
|
|
230
|
-
enforce: "post",
|
|
231
|
-
apply: "build",
|
|
232
|
-
configResolved(config) {
|
|
233
|
-
const inputOptions = config.build.rollupOptions.input;
|
|
234
|
-
if (!inputOptions) {
|
|
235
|
-
htmlFilePath = path.resolve(config.root, 'index.html');
|
|
236
|
-
} else if (typeof inputOptions === 'string') {
|
|
237
|
-
entryFiles = [inputOptions];
|
|
238
|
-
htmlFilePath = path.resolve(config.root, inputOptions);
|
|
239
|
-
} else if (Array.isArray(inputOptions)) {
|
|
240
|
-
entryFiles = inputOptions;
|
|
241
|
-
htmlFilePath = path.resolve(config.root, inputOptions[0]);
|
|
242
|
-
} else if (typeof inputOptions === 'object') {
|
|
243
|
-
entryFiles = Object.values(inputOptions);
|
|
244
|
-
htmlFilePath = path.resolve(config.root, Object.values(inputOptions)[0]);
|
|
245
|
-
}
|
|
246
|
-
},
|
|
247
|
-
buildStart() {
|
|
248
|
-
const hasHash = fileName == null || fileName.includes == null ? void 0 : fileName.includes("[hash");
|
|
249
|
-
this.emitFile({
|
|
250
|
-
[hasHash ? "name" : "fileName"]: fileName,
|
|
251
|
-
type: 'chunk',
|
|
252
|
-
id: entryPath,
|
|
253
|
-
preserveSignature: 'strict'
|
|
254
|
-
});
|
|
255
|
-
if (htmlFilePath) {
|
|
256
|
-
const htmlContent = fs.readFileSync(htmlFilePath, 'utf-8');
|
|
257
|
-
const scriptRegex = /<script\s+[^>]*src=["']([^"']+)["'][^>]*>/gi;
|
|
258
|
-
let match;
|
|
259
|
-
while ((match = scriptRegex.exec(htmlContent)) !== null) {
|
|
260
|
-
entryFiles.push(match[1]);
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
},
|
|
264
|
-
transform(code, id) {
|
|
265
|
-
if (entryFiles.some(file => id.endsWith(file))) {
|
|
266
|
-
const injection = `
|
|
267
|
-
import ${JSON.stringify(entryPath)};
|
|
268
|
-
`;
|
|
269
|
-
return injection + code;
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
}];
|
|
273
|
-
};
|
|
274
|
-
|
|
275
|
-
function overrideModule(options = {}) {
|
|
276
|
-
let {
|
|
277
|
-
override: _override = [],
|
|
278
|
-
include,
|
|
279
|
-
exclude
|
|
280
|
-
} = options;
|
|
281
|
-
const override = new Set(_override);
|
|
282
|
-
const filterFunction = createFilter(include, exclude);
|
|
283
|
-
const alias = {};
|
|
284
|
-
const keys = override.keys();
|
|
285
|
-
for (const key of keys) {
|
|
286
|
-
alias['__overrideModule__' + key] = require.resolve(`an-empty-js-file`) + `?__overrideModule__=${encodeURIComponent(key)}`;
|
|
287
|
-
}
|
|
288
|
-
return {
|
|
289
|
-
name: 'overrideModule',
|
|
290
|
-
enforce: 'post',
|
|
291
|
-
config(config) {
|
|
292
|
-
if (!config.optimizeDeps) config.optimizeDeps = {};
|
|
293
|
-
if (!config.optimizeDeps.needsInterop) config.optimizeDeps.needsInterop = [];
|
|
294
|
-
Object.keys(alias).forEach(key => {
|
|
295
|
-
var _config$optimizeDeps;
|
|
296
|
-
(_config$optimizeDeps = config.optimizeDeps) == null || (_config$optimizeDeps = _config$optimizeDeps.needsInterop) == null || _config$optimizeDeps.push(key);
|
|
297
|
-
});
|
|
298
|
-
config.optimizeDeps.needsInterop.push('an-empty-js-file');
|
|
299
|
-
config.resolve.alias.push(...Object.keys(alias).map(key => ({
|
|
300
|
-
find: key,
|
|
301
|
-
replacement: alias[key]
|
|
302
|
-
})));
|
|
303
|
-
},
|
|
304
|
-
resolveId(id) {
|
|
305
|
-
return alias[id];
|
|
306
|
-
},
|
|
307
|
-
transform: {
|
|
308
|
-
handler(code, id) {
|
|
309
|
-
if (!filterFunction(id)) return null;
|
|
310
|
-
let ast;
|
|
311
|
-
try {
|
|
312
|
-
ast = this.parse(code, {
|
|
313
|
-
allowReturnOutsideFunction: true
|
|
314
|
-
});
|
|
315
|
-
} catch (e) {
|
|
316
|
-
throw new Error(`${id}: ${e}`);
|
|
212
|
+
/**
|
|
213
|
+
* Even the resolveId hook cannot interfere with vite pre-build,
|
|
214
|
+
* and adding query parameter virtual modules will also fail.
|
|
215
|
+
* You can only proxy to the real file through alias
|
|
216
|
+
*/
|
|
217
|
+
const emptyNpmDir = resolve(require.resolve("an-empty-js-file"), "../");
|
|
218
|
+
/**
|
|
219
|
+
* The original shared module is proxied by getLoadShareModulePath, and the new shared module is prebuilt here
|
|
220
|
+
*/
|
|
221
|
+
const cacheMap2 = {};
|
|
222
|
+
function getPreBuildLibPath(pkg) {
|
|
223
|
+
if (!cacheMap2[pkg]) cacheMap2[pkg] = `__mf__prebuildwrap_${npmPackageNameToFileName(pkg)}.js`;
|
|
224
|
+
const filename = cacheMap2[pkg];
|
|
225
|
+
return filename;
|
|
226
|
+
}
|
|
227
|
+
function getLocalSharedImportMapFileName() {
|
|
228
|
+
const {
|
|
229
|
+
name
|
|
230
|
+
} = getNormalizeModuleFederationOptions();
|
|
231
|
+
return npmPackageNameToFileName(name) + "_" + "__mf__localSharedImportMap.js";
|
|
232
|
+
}
|
|
233
|
+
// Only npm package name import can trigger pre-build, absolute path cannot
|
|
234
|
+
function getLocalSharedImportMapId() {
|
|
235
|
+
return `an-empty-js-file/${getLocalSharedImportMapFileName()}`;
|
|
236
|
+
}
|
|
237
|
+
function getLocalSharedImportMapPath() {
|
|
238
|
+
return resolve(emptyNpmDir, getLocalSharedImportMapFileName());
|
|
239
|
+
}
|
|
240
|
+
function writeLocalSharedImportMap(pkgList) {
|
|
241
|
+
writeFileSync(getLocalSharedImportMapPath(), `
|
|
242
|
+
export default {
|
|
243
|
+
${pkgList.map(pkg => `
|
|
244
|
+
${JSON.stringify(pkg)}: async () => {
|
|
245
|
+
let pkg = await import("${getPreBuildLibPath(pkg)}")
|
|
246
|
+
return pkg
|
|
317
247
|
}
|
|
318
|
-
|
|
319
|
-
walk(ast, {
|
|
320
|
-
enter(node) {
|
|
321
|
-
const replaceIfMatch = sourceNode => {
|
|
322
|
-
if (sourceNode && sourceNode.value) {
|
|
323
|
-
if (override.has(sourceNode.value)) {
|
|
324
|
-
const start = sourceNode.start + 1; // Skip the opening quote
|
|
325
|
-
const end = sourceNode.end - 1; // Skip the closing quote
|
|
326
|
-
s.overwrite(start, end, '__overrideModule__' + sourceNode.value);
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
};
|
|
330
|
-
if (node.type === 'ImportDeclaration') {
|
|
331
|
-
replaceIfMatch(node.source);
|
|
332
|
-
}
|
|
333
|
-
if (node.type === 'ExportNamedDeclaration' || node.type === 'ExportAllDeclaration') {
|
|
334
|
-
replaceIfMatch(node.source);
|
|
335
|
-
}
|
|
336
|
-
if (node.type === 'CallExpression' && node.callee.type === 'Import' && node.arguments.length && node.arguments[0].type === 'Literal') {
|
|
337
|
-
replaceIfMatch(node.arguments[0]);
|
|
338
|
-
}
|
|
339
|
-
if (node.type === 'CallExpression' && node.callee.name === 'require' && node.arguments.length && node.arguments[0].type === 'Literal') {
|
|
340
|
-
replaceIfMatch(node.arguments[0]);
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
});
|
|
344
|
-
return {
|
|
345
|
-
code: s.toString(),
|
|
346
|
-
map: s.generateMap({
|
|
347
|
-
hires: true
|
|
348
|
-
})
|
|
349
|
-
};
|
|
350
|
-
}
|
|
248
|
+
`).join(",")}
|
|
351
249
|
}
|
|
352
|
-
|
|
250
|
+
`);
|
|
353
251
|
}
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
function
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
`;
|
|
252
|
+
/**
|
|
253
|
+
* generate loadShare virtual module
|
|
254
|
+
*/
|
|
255
|
+
const cacheMap1 = {};
|
|
256
|
+
function getLoadShareModulePath(pkg) {
|
|
257
|
+
const {
|
|
258
|
+
name
|
|
259
|
+
} = getNormalizeModuleFederationOptions();
|
|
260
|
+
if (!cacheMap1[pkg]) cacheMap1[pkg] = npmPackageNameToFileName(name) + "_" + `__mf__loadShare_${npmPackageNameToFileName(pkg)}.js`;
|
|
261
|
+
const filename = cacheMap1[pkg];
|
|
262
|
+
return resolve(emptyNpmDir, filename);
|
|
363
263
|
}
|
|
364
|
-
function
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
264
|
+
function writeLoadShareModule(pkg, shareItem, command) {
|
|
265
|
+
writeFileSync(getLoadShareModulePath(pkg), `
|
|
266
|
+
const {loadShare} = require("@module-federation/runtime")
|
|
267
|
+
const res = loadShare(${JSON.stringify(pkg)}, {
|
|
268
|
+
customShareInfo: {shareConfig:{
|
|
269
|
+
singleton: ${shareItem.shareConfig.singleton},
|
|
270
|
+
strictVersion: ${shareItem.shareConfig.strictVersion},
|
|
271
|
+
requiredVersion: ${JSON.stringify(shareItem.shareConfig.requiredVersion)}
|
|
272
|
+
}}})
|
|
273
|
+
const exportModule = ${command !== "build" ? "/*mf top-level-await placeholder replacement mf*/" : "await "}res.then(factory => factory())
|
|
274
|
+
module.exports = exportModule
|
|
275
|
+
`);
|
|
276
|
+
}
|
|
277
|
+
function npmPackageNameToFileName(packageName) {
|
|
278
|
+
// 1. 去掉包名前的 "@"
|
|
279
|
+
// 2. 将包名中的 "/" 替换为 "__" 以避免文件路径问题
|
|
280
|
+
// 3. 去掉不合法的文件名字符
|
|
281
|
+
return packageName.replace(/^@/, '') // 移除作用域前缀 "@"
|
|
282
|
+
.replace(/\//g, '__') // 将 "/" 替换为 "__"
|
|
283
|
+
.replace(/[^a-zA-Z0-9_.-]/g, '_'); // 替换其他非法字符为 "_"
|
|
369
284
|
}
|
|
285
|
+
|
|
286
|
+
const emptyPath = require.resolve('an-empty-js-file');
|
|
287
|
+
const REMOTE_ENTRY_ID = "REMOTE_ENTRY_ID";
|
|
370
288
|
function generateRemoteEntry(options) {
|
|
371
289
|
const pluginImportNames = options.runtimePlugins.map((p, i) => [`$runtimePlugin_${i}`, `import $runtimePlugin_${i} from "${p}";`]);
|
|
372
290
|
return `
|
|
@@ -390,6 +308,7 @@ function generateRemoteEntry(options) {
|
|
|
390
308
|
`;
|
|
391
309
|
}).join(',')}
|
|
392
310
|
}
|
|
311
|
+
import localSharedImportMap from "${getLocalSharedImportMapId()}"
|
|
393
312
|
async function init(shared = {}) {
|
|
394
313
|
const localShared = {
|
|
395
314
|
${Object.keys(options.shared).map(key => {
|
|
@@ -403,9 +322,10 @@ function generateRemoteEntry(options) {
|
|
|
403
322
|
from: ${JSON.stringify(options.name)},
|
|
404
323
|
async get () {
|
|
405
324
|
localShared[${JSON.stringify(key)}].loaded = true
|
|
406
|
-
const
|
|
325
|
+
const {${JSON.stringify(key)}: pkgDynamicImport} = localSharedImportMap
|
|
326
|
+
const res = await pkgDynamicImport()
|
|
407
327
|
return function () {
|
|
408
|
-
return
|
|
328
|
+
return res
|
|
409
329
|
}
|
|
410
330
|
},
|
|
411
331
|
shareConfig: {
|
|
@@ -447,71 +367,319 @@ function generateRemoteEntry(options) {
|
|
|
447
367
|
}
|
|
448
368
|
`;
|
|
449
369
|
}
|
|
450
|
-
|
|
451
|
-
|
|
370
|
+
const WRAP_REMOTE_ENTRY_PATH = emptyPath + '?__mf__wrapRemoteEntry__';
|
|
371
|
+
function generateWrapRemoteEntry() {
|
|
372
|
+
return `
|
|
373
|
+
import {init, get} from "${REMOTE_ENTRY_ID}"
|
|
374
|
+
export {init, get}
|
|
375
|
+
`;
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* Inject entry file, automatically init when used as host,
|
|
379
|
+
* and will not inject remoteEntry
|
|
380
|
+
*/
|
|
381
|
+
const HOST_AUTO_INIT = emptyPath + '?__mf__isHostInit';
|
|
382
|
+
function generateWrapHostInit() {
|
|
383
|
+
return `
|
|
384
|
+
import {init} from "${REMOTE_ENTRY_ID}"
|
|
385
|
+
init()
|
|
386
|
+
`;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
const filter$1 = createFilter();
|
|
390
|
+
function pluginProxyRemoteEntry () {
|
|
452
391
|
return {
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
392
|
+
name: 'proxyRemoteEntry',
|
|
393
|
+
enforce: 'post',
|
|
394
|
+
resolveId(id) {
|
|
395
|
+
if (id === REMOTE_ENTRY_ID) {
|
|
396
|
+
return REMOTE_ENTRY_ID;
|
|
397
|
+
}
|
|
398
|
+
},
|
|
399
|
+
load(id) {
|
|
400
|
+
if (id === REMOTE_ENTRY_ID) {
|
|
401
|
+
return generateRemoteEntry(getNormalizeModuleFederationOptions());
|
|
402
|
+
}
|
|
403
|
+
},
|
|
404
|
+
async transform(code, id) {
|
|
405
|
+
if (!filter$1(id)) return;
|
|
406
|
+
if (id === REMOTE_ENTRY_ID) {
|
|
407
|
+
return generateRemoteEntry(getNormalizeModuleFederationOptions());
|
|
408
|
+
}
|
|
409
|
+
if (id.includes(WRAP_REMOTE_ENTRY_PATH)) {
|
|
410
|
+
return generateWrapRemoteEntry();
|
|
411
|
+
}
|
|
412
|
+
if (id.includes(HOST_AUTO_INIT)) {
|
|
413
|
+
return generateWrapHostInit();
|
|
414
|
+
}
|
|
415
|
+
}
|
|
465
416
|
};
|
|
466
417
|
}
|
|
467
|
-
|
|
468
|
-
function
|
|
418
|
+
|
|
419
|
+
function generateRemotes(id, command) {
|
|
469
420
|
return {
|
|
470
421
|
code: `
|
|
471
422
|
import {loadRemote} from "@module-federation/runtime"
|
|
472
|
-
|
|
423
|
+
const exportModule = await loadRemote(${JSON.stringify(id)})
|
|
424
|
+
${command === "build" && `
|
|
425
|
+
export default 'default' in (exportModule || {}) ? exportModule.default : undefined
|
|
426
|
+
export const __mf__dynamicExports = exportModule
|
|
427
|
+
` || ""}
|
|
428
|
+
${command !== "build" && `
|
|
429
|
+
export default exportModule
|
|
430
|
+
` || ""}
|
|
473
431
|
`,
|
|
474
432
|
map: null,
|
|
475
|
-
|
|
433
|
+
// TODO: vite dev mode invalid, use optimizeDeps.needsInterop
|
|
434
|
+
syntheticNamedExports: '__mf__dynamicExports'
|
|
476
435
|
};
|
|
477
436
|
}
|
|
437
|
+
|
|
438
|
+
const filter = createFilter();
|
|
439
|
+
function pluginProxyRemotes (options) {
|
|
440
|
+
let command;
|
|
441
|
+
const {
|
|
442
|
+
remotes
|
|
443
|
+
} = options;
|
|
444
|
+
const matchRemotesList = Object.keys(remotes).map(item => item.replace(/\//g, "_"));
|
|
445
|
+
return {
|
|
446
|
+
name: "proxyRemotes",
|
|
447
|
+
config(config, {
|
|
448
|
+
command: _command
|
|
449
|
+
}) {
|
|
450
|
+
command = _command;
|
|
451
|
+
Object.keys(remotes).forEach(key => {
|
|
452
|
+
const remote = remotes[key];
|
|
453
|
+
config.resolve.alias.push({
|
|
454
|
+
find: new RegExp(`(${remote.name}(\/.*|$)?)`),
|
|
455
|
+
replacement: '$1',
|
|
456
|
+
customResolver(source) {
|
|
457
|
+
if (!config.optimizeDeps) config.optimizeDeps = {};
|
|
458
|
+
if (!config.optimizeDeps.needsInterop) config.optimizeDeps.needsInterop = [];
|
|
459
|
+
if (config.optimizeDeps.needsInterop.indexOf(source) === -1) config.optimizeDeps.needsInterop.push(source);
|
|
460
|
+
return this.resolve(require.resolve('an-empty-js-file') + '?__moduleRemote__=' + encodeURIComponent(source));
|
|
461
|
+
}
|
|
462
|
+
});
|
|
463
|
+
});
|
|
464
|
+
},
|
|
465
|
+
async transform(code, id) {
|
|
466
|
+
if (!filter(id)) return;
|
|
467
|
+
let [devRemoteModuleName] = matchRemotesList.length && id.match(new RegExp(`\/(${matchRemotesList.join('|')})(\_.*\.js|\.js)`)) || [];
|
|
468
|
+
if (devRemoteModuleName) {
|
|
469
|
+
return generateRemotes(devRemoteModuleName.replace('/', '').replace(/_/g, '/').replace('.js', ''), command);
|
|
470
|
+
}
|
|
471
|
+
let [prodRemoteName] = id.match(/\_\_moduleRemote\_\_=[^&]+/) || [];
|
|
472
|
+
if (prodRemoteName) {
|
|
473
|
+
return generateRemotes(decodeURIComponent(prodRemoteName.replace('__moduleRemote__=', '')), command);
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
};
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
function proxySharedModule(options) {
|
|
480
|
+
let {
|
|
481
|
+
shared = {},
|
|
482
|
+
include,
|
|
483
|
+
exclude
|
|
484
|
+
} = options;
|
|
485
|
+
const filterFunction = createFilter(include, exclude);
|
|
486
|
+
writeLocalSharedImportMap(Object.keys(shared));
|
|
487
|
+
return [{
|
|
488
|
+
name: 'preBuildShared',
|
|
489
|
+
enforce: 'post',
|
|
490
|
+
config(config, {
|
|
491
|
+
command
|
|
492
|
+
}) {
|
|
493
|
+
// config?.optimizeDeps?.include?.push?.("an-empty-js-file");
|
|
494
|
+
// config.optimizeDeps.needsInterop.push('an-empty-js-file');
|
|
495
|
+
config.resolve.alias.push(...Object.keys(shared).map(key => {
|
|
496
|
+
var _config$optimizeDeps;
|
|
497
|
+
config == null || (_config$optimizeDeps = config.optimizeDeps) == null || (_config$optimizeDeps = _config$optimizeDeps.include) == null || _config$optimizeDeps.push == null || _config$optimizeDeps.push(getPreBuildLibPath(key));
|
|
498
|
+
// write proxyFile
|
|
499
|
+
writeLoadShareModule(key, shared[key], command);
|
|
500
|
+
const preBuildLibPath = getLoadShareModulePath(key);
|
|
501
|
+
return {
|
|
502
|
+
// Intercept all dependency requests to the proxy module
|
|
503
|
+
// Dependency requests issued by localSharedImportMap are allowed without proxying.
|
|
504
|
+
find: new RegExp(`^${key}$`),
|
|
505
|
+
replacement: preBuildLibPath,
|
|
506
|
+
customResolver(source, importer) {
|
|
507
|
+
if (importer.includes(`node_modules/${key}/`)) {
|
|
508
|
+
return this.resolve(key);
|
|
509
|
+
}
|
|
510
|
+
return this.resolve(preBuildLibPath);
|
|
511
|
+
}
|
|
512
|
+
};
|
|
513
|
+
}));
|
|
514
|
+
config.resolve.alias.push(...Object.keys(shared).map(key => {
|
|
515
|
+
return {
|
|
516
|
+
find: new RegExp(`^${getPreBuildLibPath(key)}$`),
|
|
517
|
+
customResolver(source, importer) {
|
|
518
|
+
return this.resolve(key);
|
|
519
|
+
}
|
|
520
|
+
};
|
|
521
|
+
}));
|
|
522
|
+
}
|
|
523
|
+
}, {
|
|
524
|
+
name: "prebuild-top-level-await",
|
|
525
|
+
apply: "serve",
|
|
526
|
+
transform(code, id) {
|
|
527
|
+
if (!code.includes("/*mf top-level-await placeholder replacement mf*/")) {
|
|
528
|
+
return null;
|
|
529
|
+
}
|
|
530
|
+
if (!filterFunction(id)) return null;
|
|
531
|
+
let ast;
|
|
532
|
+
try {
|
|
533
|
+
ast = this.parse(code, {
|
|
534
|
+
allowReturnOutsideFunction: true
|
|
535
|
+
});
|
|
536
|
+
} catch (e) {
|
|
537
|
+
throw new Error(`${id}: ${e}`);
|
|
538
|
+
}
|
|
539
|
+
const magicString = new MagicString(code);
|
|
540
|
+
walk(ast, {
|
|
541
|
+
enter(node) {
|
|
542
|
+
// 处理命名导出
|
|
543
|
+
if (node.type === 'ExportNamedDeclaration' && node.specifiers) {
|
|
544
|
+
const exportSpecifiers = node.specifiers.map(specifier => specifier.exported.name);
|
|
545
|
+
const proxyStatements = exportSpecifiers.map(name => `
|
|
546
|
+
const __mfproxy__await${name} = await ${name}();
|
|
547
|
+
const __mfproxy__${name} = () => __mfproxy__await${name};
|
|
548
|
+
`).join('\n');
|
|
549
|
+
const exportStatements = exportSpecifiers.map(name => `__mfproxy__${name} as ${name}`).join(', ');
|
|
550
|
+
const start = node.start;
|
|
551
|
+
const end = node.end;
|
|
552
|
+
const replacement = `${proxyStatements}\nexport { ${exportStatements} };`;
|
|
553
|
+
magicString.overwrite(start, end, replacement);
|
|
554
|
+
}
|
|
555
|
+
// 处理默认导出
|
|
556
|
+
if (node.type === 'ExportDefaultDeclaration') {
|
|
557
|
+
const declaration = node.declaration;
|
|
558
|
+
const start = node.start;
|
|
559
|
+
const end = node.end;
|
|
560
|
+
let proxyStatement;
|
|
561
|
+
let exportStatement = 'default';
|
|
562
|
+
if (declaration.type === 'Identifier') {
|
|
563
|
+
// 处理标识符 (如: export default foo;)
|
|
564
|
+
proxyStatement = `
|
|
565
|
+
const __mfproxy__awaitdefault = await ${declaration.name}();
|
|
566
|
+
const __mfproxy__default = __mfproxy__awaitdefault;
|
|
567
|
+
`;
|
|
568
|
+
} else if (declaration.type === 'CallExpression' || declaration.type === 'FunctionDeclaration') {
|
|
569
|
+
// 处理调用表达式或函数声明 (如: export default someFunction();)
|
|
570
|
+
const declarationCode = code.slice(declaration.start, declaration.end);
|
|
571
|
+
proxyStatement = `
|
|
572
|
+
const __mfproxy__awaitdefault = await (${declarationCode});
|
|
573
|
+
const __mfproxy__default = __mfproxy__awaitdefault;
|
|
574
|
+
`;
|
|
575
|
+
} else {
|
|
576
|
+
// 其他类型 (可以根据需要添加更多处理逻辑)
|
|
577
|
+
proxyStatement = `
|
|
578
|
+
const __mfproxy__awaitdefault = await (${code.slice(declaration.start, declaration.end)});
|
|
579
|
+
const __mfproxy__default = __mfproxy__awaitdefault;
|
|
580
|
+
`;
|
|
581
|
+
}
|
|
582
|
+
const replacement = `${proxyStatement}\nexport { __mfproxy__default as ${exportStatement} };`;
|
|
583
|
+
magicString.overwrite(start, end, replacement);
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
});
|
|
587
|
+
return {
|
|
588
|
+
code: magicString.toString(),
|
|
589
|
+
map: magicString.generateMap({
|
|
590
|
+
hires: true
|
|
591
|
+
})
|
|
592
|
+
};
|
|
593
|
+
}
|
|
594
|
+
}];
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
var aliasToArrayPlugin = {
|
|
598
|
+
name: 'alias-transform-plugin',
|
|
599
|
+
config: (config, {
|
|
600
|
+
command
|
|
601
|
+
}) => {
|
|
602
|
+
if (!config.resolve) config.resolve = {};
|
|
603
|
+
if (!config.resolve.alias) config.resolve.alias = [];
|
|
604
|
+
const {
|
|
605
|
+
alias
|
|
606
|
+
} = config.resolve;
|
|
607
|
+
if (typeof alias === 'object' && !Array.isArray(alias)) {
|
|
608
|
+
config.resolve.alias = Object.entries(alias).map(([find, replacement]) => ({
|
|
609
|
+
find,
|
|
610
|
+
replacement
|
|
611
|
+
}));
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
};
|
|
615
|
+
|
|
616
|
+
var normalizeBuildPlugin = (singleChunkModules => ({
|
|
617
|
+
name: 'normalizeBuild',
|
|
618
|
+
config: (config, {
|
|
619
|
+
command
|
|
620
|
+
}) => {
|
|
621
|
+
if (!config.build) config.build = {};
|
|
622
|
+
if (!config.build.rollupOptions) config.build.rollupOptions = {};
|
|
623
|
+
let {
|
|
624
|
+
rollupOptions
|
|
625
|
+
} = config.build;
|
|
626
|
+
if (!rollupOptions.output) rollupOptions.output = {};
|
|
627
|
+
normalizeManualChunks(rollupOptions.output, singleChunkModules);
|
|
628
|
+
}
|
|
629
|
+
}));
|
|
630
|
+
function normalizeManualChunks(output, singleChunkModules) {
|
|
631
|
+
const pattern = new RegExp(`node_modules/(${singleChunkModules.join('|')})/`);
|
|
632
|
+
if (!output.manualChunks) output.manualChunks = {};
|
|
633
|
+
const wrapManualChunks = original => (id, ...args) => {
|
|
634
|
+
const [_, moduleName] = id.match(pattern) || [];
|
|
635
|
+
if (moduleName) {
|
|
636
|
+
return moduleName;
|
|
637
|
+
}
|
|
638
|
+
if (typeof original === 'function') {
|
|
639
|
+
return original(id, ...args);
|
|
640
|
+
}
|
|
641
|
+
if (typeof original === 'object' && original) {
|
|
642
|
+
return original[id];
|
|
643
|
+
}
|
|
644
|
+
};
|
|
645
|
+
output.manualChunks = wrapManualChunks(output.manualChunks);
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
var normalizeOptimizeDepsPlugin = {
|
|
649
|
+
name: 'normalizeOptimizeDeps',
|
|
650
|
+
config: (config, {
|
|
651
|
+
command
|
|
652
|
+
}) => {
|
|
653
|
+
let {
|
|
654
|
+
optimizeDeps
|
|
655
|
+
} = config;
|
|
656
|
+
if (!optimizeDeps) {
|
|
657
|
+
config.optimizeDeps = {};
|
|
658
|
+
optimizeDeps = config.optimizeDeps;
|
|
659
|
+
}
|
|
660
|
+
if (!optimizeDeps.include) optimizeDeps.include = [];
|
|
661
|
+
if (!optimizeDeps.needsInterop) optimizeDeps.needsInterop = [];
|
|
662
|
+
}
|
|
663
|
+
};
|
|
664
|
+
|
|
478
665
|
function federation(mfUserOptions) {
|
|
479
666
|
const options = normalizeModuleFederationOptions(mfUserOptions);
|
|
480
667
|
const {
|
|
668
|
+
name,
|
|
481
669
|
remotes,
|
|
482
670
|
shared,
|
|
483
671
|
filename
|
|
484
672
|
} = options;
|
|
485
|
-
|
|
486
|
-
find: '@module-federation/runtime',
|
|
487
|
-
replacement: require.resolve('@module-federation/runtime')
|
|
488
|
-
}];
|
|
489
|
-
Object.keys(remotes).forEach(key => {
|
|
490
|
-
const remote = remotes[key];
|
|
491
|
-
alias.push({
|
|
492
|
-
find: new RegExp(`(${remote.name}(\/.*|$)?)`),
|
|
493
|
-
replacement: '$1',
|
|
494
|
-
customResolver(source) {
|
|
495
|
-
if (!con.optimizeDeps) con.optimizeDeps = {};
|
|
496
|
-
if (!con.optimizeDeps.needsInterop) con.optimizeDeps.needsInterop = [];
|
|
497
|
-
if (con.optimizeDeps.needsInterop.indexOf(source) === -1) con.optimizeDeps.needsInterop.push(source);
|
|
498
|
-
return this.resolve(require.resolve('an-empty-js-file') + '?__moduleRemote__=' + encodeURIComponent(source));
|
|
499
|
-
}
|
|
500
|
-
});
|
|
501
|
-
});
|
|
502
|
-
const remotePrefixList = Object.keys(remotes);
|
|
503
|
-
// pkgname will be escaped, and matching path also needs to be processed in the same way:
|
|
504
|
-
// @json2csv/plainjs --> .vite/deps/@json2csv_plainjs
|
|
505
|
-
const sharedKeyMatchList = Object.keys(shared).map(item => `__overrideModule__${item.replace('/', '_')}`);
|
|
673
|
+
if (!name) throw new Error("name is required");
|
|
506
674
|
return [aliasToArrayPlugin, normalizeOptimizeDepsPlugin, normalizeBuildPlugin([...Object.keys(shared), "@module-federation/runtime"]), ...addEntry({
|
|
507
675
|
entryName: 'remoteEntry',
|
|
508
|
-
entryPath:
|
|
676
|
+
entryPath: WRAP_REMOTE_ENTRY_PATH,
|
|
509
677
|
fileName: filename
|
|
510
678
|
}), ...addEntry({
|
|
511
679
|
entryName: 'hostInit',
|
|
512
|
-
entryPath:
|
|
513
|
-
}),
|
|
514
|
-
|
|
680
|
+
entryPath: HOST_AUTO_INIT
|
|
681
|
+
}), pluginProxyRemoteEntry(), pluginProxyRemotes(options), ...proxySharedModule({
|
|
682
|
+
shared
|
|
515
683
|
}), {
|
|
516
684
|
name: 'module-federation-vite',
|
|
517
685
|
enforce: 'post',
|
|
@@ -519,55 +687,14 @@ function federation(mfUserOptions) {
|
|
|
519
687
|
command: _command
|
|
520
688
|
}) {
|
|
521
689
|
var _config$optimizeDeps;
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
(_config$optimizeDeps = config.optimizeDeps) == null || (_config$optimizeDeps = _config$optimizeDeps.include) == null || _config$optimizeDeps.push('@module-federation/runtime');
|
|
526
|
-
Object.keys(shared).forEach(key => {
|
|
527
|
-
var _config$optimizeDeps2;
|
|
528
|
-
(_config$optimizeDeps2 = config.optimizeDeps) == null || (_config$optimizeDeps2 = _config$optimizeDeps2.include) == null || _config$optimizeDeps2.push(key);
|
|
690
|
+
config.resolve.alias.push({
|
|
691
|
+
find: '@module-federation/runtime',
|
|
692
|
+
replacement: require.resolve('@module-federation/runtime')
|
|
529
693
|
});
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
}
|
|
535
|
-
},
|
|
536
|
-
load(id) {
|
|
537
|
-
if (id === '__mf__cwdRemoteEntry') {
|
|
538
|
-
return generateRemoteEntry(options);
|
|
539
|
-
}
|
|
540
|
-
},
|
|
541
|
-
async transform(code, id) {
|
|
542
|
-
if (!filter(id)) return;
|
|
543
|
-
if (id === '__mf__cwdRemoteEntry') {
|
|
544
|
-
return generateRemoteEntry(options);
|
|
545
|
-
}
|
|
546
|
-
if (id.indexOf('__mf__wrapRemoteEntry__') > -1) {
|
|
547
|
-
return wrapRemoteEntry();
|
|
548
|
-
}
|
|
549
|
-
if (id.indexOf('__mf__isHostInit') > -1) {
|
|
550
|
-
return wrapHostInit();
|
|
551
|
-
}
|
|
552
|
-
if (id.indexOf('__mf__cwdRemoteEntry') > -1) {
|
|
553
|
-
return generateRemoteEntry(options);
|
|
554
|
-
}
|
|
555
|
-
let [devSharedModuleName] = sharedKeyMatchList.length && id.match(new RegExp(`\/(${sharedKeyMatchList.join('|')})(\_.*\.js|\.js)`)) || [];
|
|
556
|
-
if (devSharedModuleName) {
|
|
557
|
-
return wrapShare(devSharedModuleName.replace('/__overrideModule__', '').replace(/_/g, '/').replace('.js', ''), shared);
|
|
558
|
-
}
|
|
559
|
-
let [prodSharedName] = id.match(/\_\_overrideModule\_\_=[^&]+/) || [];
|
|
560
|
-
if (prodSharedName) {
|
|
561
|
-
return wrapShare(decodeURIComponent(prodSharedName.replace('__overrideModule__=', '')), shared);
|
|
562
|
-
}
|
|
563
|
-
let [devRemoteModuleName] = remotePrefixList.length && id.match(new RegExp(`\/(${remotePrefixList.join('|')})(\_.*\.js|\.js)`)) || [];
|
|
564
|
-
if (devRemoteModuleName) {
|
|
565
|
-
return wrapRemote(devRemoteModuleName.replace('/', '').replace(/_/g, '/').replace('.js', ''));
|
|
566
|
-
}
|
|
567
|
-
let [prodRemoteName] = id.match(/\_\_moduleRemote\_\_=[^&]+/) || [];
|
|
568
|
-
if (prodRemoteName) {
|
|
569
|
-
return wrapRemote(decodeURIComponent(prodRemoteName.replace('__moduleRemote__=', '')));
|
|
570
|
-
}
|
|
694
|
+
(_config$optimizeDeps = config.optimizeDeps) == null || (_config$optimizeDeps = _config$optimizeDeps.include) == null || _config$optimizeDeps.push('@module-federation/runtime');
|
|
695
|
+
// Object.keys(shared).forEach((key) => {
|
|
696
|
+
// config.optimizeDeps?.include?.push(key);
|
|
697
|
+
// });
|
|
571
698
|
}
|
|
572
699
|
}];
|
|
573
700
|
}
|