@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.
@@ -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
- var aliasToArrayPlugin = {
9
- name: 'alias-transform-plugin',
10
- config: (config, {
11
- command
12
- }) => {
13
- if (!config.resolve) config.resolve = {};
14
- if (!config.resolve.alias) config.resolve.alias = [];
15
- const {
16
- alias
17
- } = config.resolve;
18
- if (typeof alias === 'object' && !Array.isArray(alias)) {
19
- config.resolve.alias = Object.entries(alias).map(([find, replacement]) => ({
20
- find,
21
- replacement
22
- }));
23
- }
24
- }
25
- };
26
-
27
- var normalizeBuildPlugin = (singleChunkModules => ({
28
- name: 'normalizeBuild',
29
- config: (config, {
30
- command
31
- }) => {
32
- if (!config.build) config.build = {};
33
- if (!config.build.rollupOptions) config.build.rollupOptions = {};
34
- let {
35
- rollupOptions
36
- } = config.build;
37
- if (!rollupOptions.output) rollupOptions.output = {};
38
- normalizeManualChunks(rollupOptions.output, singleChunkModules);
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
- if (typeof original === 'object' && original) {
53
- return original[id];
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
- output.manualChunks = wrapManualChunks(output.manualChunks);
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.replace(/^\.?\/?/, './')] = normalizeExposesItem(key, exposes[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(path__default.join(key, 'package.json')).version;
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
- var normalizeOptimizeDepsPlugin = {
180
- name: 'normalizeOptimizeDeps',
181
- config: (config, {
182
- command
183
- }) => {
184
- let {
185
- optimizeDeps
186
- } = config;
187
- if (!optimizeDeps) {
188
- config.optimizeDeps = {};
189
- optimizeDeps = config.optimizeDeps;
190
- }
191
- if (!optimizeDeps.include) optimizeDeps.include = [];
192
- if (!optimizeDeps.needsInterop) optimizeDeps.needsInterop = [];
193
- }
194
- };
195
-
196
- const addEntry = ({
197
- entryName,
198
- entryPath,
199
- fileName
200
- }) => {
201
- let entryFiles = [];
202
- let htmlFilePath;
203
- return [{
204
- name: 'add-entry',
205
- apply: "serve",
206
- configureServer(server) {
207
- var _server$httpServer;
208
- (_server$httpServer = server.httpServer) == null || _server$httpServer.once == null || _server$httpServer.once('listening', () => {
209
- const {
210
- port
211
- } = server.config.server;
212
- fetch(`http://localhost:${port}${entryPath}`);
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
- const s = new MagicString(code);
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
- const emptyPath = require.resolve('an-empty-js-file');
356
- const filter = createFilter();
357
- let command = '';
358
- function wrapRemoteEntry() {
359
- return `
360
- import {init, get} from "__mf__cwdRemoteEntry"
361
- export {init, get}
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 wrapHostInit() {
365
- return `
366
- import {init} from "__mf__cwdRemoteEntry"
367
- init()
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 pkg = await import(${JSON.stringify(key)})
325
+ const {${JSON.stringify(key)}: pkgDynamicImport} = localSharedImportMap
326
+ const res = await pkgDynamicImport()
407
327
  return function () {
408
- return pkg
328
+ return res
409
329
  }
410
330
  },
411
331
  shareConfig: {
@@ -447,71 +367,319 @@ function generateRemoteEntry(options) {
447
367
  }
448
368
  `;
449
369
  }
450
- function wrapShare(id, shared) {
451
- const shareConfig = shared[id].shareConfig;
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
- code: `
454
- import {loadShare} from "@module-federation/runtime"
455
- const res = await loadShare(${JSON.stringify(id)}, {
456
- customShareInfo: {shareConfig:{
457
- singleton: ${shareConfig.singleton},
458
- strictVersion: ${JSON.stringify(shareConfig.strictVersion)},
459
- requiredVersion: ${JSON.stringify(shareConfig.requiredVersion)}
460
- }}})
461
- export default res()
462
- `,
463
- map: null,
464
- syntheticNamedExports: 'default'
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
- let con;
468
- function wrapRemote(id) {
418
+
419
+ function generateRemotes(id, command) {
469
420
  return {
470
421
  code: `
471
422
  import {loadRemote} from "@module-federation/runtime"
472
- export ${command !== 'build' ? 'default' : 'const dynamicExport = '} await loadRemote(${JSON.stringify(id)})
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
- syntheticNamedExports: 'dynamicExport'
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
- const alias = [{
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: emptyPath + '?__mf__wrapRemoteEntry__',
676
+ entryPath: WRAP_REMOTE_ENTRY_PATH,
509
677
  fileName: filename
510
678
  }), ...addEntry({
511
679
  entryName: 'hostInit',
512
- entryPath: emptyPath + '?__mf__isHostInit'
513
- }), overrideModule({
514
- override: Object.keys(shared)
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
- con = config;
523
- command = _command;
524
- config.resolve.alias.push(...alias);
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
- resolveId(id) {
532
- if (id === '__mf__cwdRemoteEntry') {
533
- return '__mf__cwdRemoteEntry';
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
  }