@module-federation/vite 1.9.8 → 1.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -124,17 +124,46 @@ const addEntry = ({
124
124
  }
125
125
  },
126
126
  generateBundle(options, bundle) {
127
- var _viteConfig$experimen, _viteConfig$experimen2;
128
127
  if (!injectHtml()) return;
129
128
  const file = this.getFileName(emitFileId);
130
- const path = (_viteConfig$experimen = viteConfig.experimental) != null && _viteConfig$experimen.renderBuiltUrl ? (_viteConfig$experimen2 = viteConfig.experimental) == null ? void 0 : _viteConfig$experimen2.renderBuiltUrl(file) : viteConfig.base + file;
131
- const scriptContent = `
132
- <script type="module" src="${path}"></script>
133
- `;
129
+ // Helper to resolve path with proper renderBuiltUrl handling
130
+ const resolvePath = htmlFileName => {
131
+ var _viteConfig$experimen;
132
+ if (!((_viteConfig$experimen = viteConfig.experimental) != null && _viteConfig$experimen.renderBuiltUrl)) {
133
+ return viteConfig.base + file;
134
+ }
135
+ const result = viteConfig.experimental.renderBuiltUrl(file, {
136
+ hostId: htmlFileName,
137
+ hostType: 'html',
138
+ type: 'asset',
139
+ ssr: false
140
+ });
141
+ // Handle return types
142
+ if (typeof result === 'string') {
143
+ return result;
144
+ }
145
+ if (result && typeof result === 'object') {
146
+ if ('runtime' in result) {
147
+ // Runtime code cannot be used in <script src="">
148
+ console.warn('[vite-plugin-federation] renderBuiltUrl returned runtime code for HTML injection. ' + 'Runtime code cannot be used in <script src="">. Falling back to base path.');
149
+ return viteConfig.base + file;
150
+ }
151
+ if (result.relative) {
152
+ return file;
153
+ }
154
+ }
155
+ // Fallback for undefined or unexpected values
156
+ return viteConfig.base + file;
157
+ };
158
+ // Process each HTML file
134
159
  for (const fileName in bundle) {
135
160
  if (fileName.endsWith('.html')) {
136
161
  let htmlAsset = bundle[fileName];
137
162
  if (htmlAsset.type === 'chunk') return;
163
+ const path = resolvePath(fileName);
164
+ const scriptContent = `
165
+ <script type="module" src="${path}"></script>
166
+ `;
138
167
  let htmlContent = htmlAsset.source.toString() || '';
139
168
  htmlContent = htmlContent.replace('<head>', `<head>${scriptContent}`);
140
169
  htmlAsset.source = htmlContent;
@@ -300,1595 +329,1629 @@ function _extends() {
300
329
  }, _extends.apply(null, arguments);
301
330
  }
302
331
 
303
- function normalizeExposesItem(key, item) {
304
- let importPath = '';
305
- if (typeof item === 'string') {
306
- importPath = item;
307
- }
308
- if (typeof item === 'object') {
309
- importPath = item.import;
332
+ const DEFAULT_DEV_OPTIONS = {
333
+ disableLiveReload: true,
334
+ disableHotTypesReload: false,
335
+ disableDynamicRemoteTypeHints: false
336
+ };
337
+ const DYNAMIC_HINTS_PLUGIN = '@module-federation/dts-plugin/dynamic-remote-type-hints-plugin';
338
+ const getIPv4 = () => process.env['FEDERATION_IPV4'] || '127.0.0.1';
339
+ const forkDevWorkerPath = (() => {
340
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
341
+ return require.resolve('@module-federation/dts-plugin/dist/fork-dev-worker.js');
342
+ })();
343
+ class DevWorker {
344
+ constructor(options) {
345
+ this.worker = rpc.createRpcWorker(forkDevWorkerPath, {}, undefined, false);
346
+ this.worker.connect(options);
310
347
  }
311
- return {
312
- import: importPath
313
- };
314
- }
315
- function normalizeExposes(exposes) {
316
- if (!exposes) return {};
317
- const res = {};
318
- Object.keys(exposes).forEach(key => {
319
- res[key] = normalizeExposesItem(key, exposes[key]);
320
- });
321
- return res;
322
- }
323
- function normalizeRemotes(remotes) {
324
- if (!remotes) return {};
325
- const result = {};
326
- if (typeof remotes === 'object') {
327
- Object.keys(remotes).forEach(key => {
328
- result[key] = normalizeRemoteItem(key, remotes[key]);
348
+ update() {
349
+ var _this$worker$process;
350
+ (_this$worker$process = this.worker.process) == null || _this$worker$process.send == null || _this$worker$process.send({
351
+ type: rpc.RpcGMCallTypes.CALL,
352
+ id: this.worker.id,
353
+ args: [undefined, 'update']
329
354
  });
330
355
  }
331
- return result;
332
- }
333
- function normalizeRemoteItem(key, remote) {
334
- if (typeof remote === 'string') {
335
- const [entryGlobalName] = remote.split('@');
336
- const entry = remote.replace(entryGlobalName + '@', '');
337
- return {
338
- type: 'var',
339
- name: key,
340
- entry,
341
- entryGlobalName,
342
- shareScope: 'default'
343
- };
356
+ exit() {
357
+ this.worker.terminate();
344
358
  }
345
- return Object.assign({
346
- type: 'var',
347
- name: key,
348
- shareScope: 'default',
349
- entryGlobalName: key
350
- }, remote);
351
- }
352
- function removePathFromNpmPackage(packageString) {
353
- // 匹配npm包名的正则表达式,忽略路径部分
354
- const regex = /^(?:@[^/]+\/)?[^/]+/;
355
- // 使用正则表达式匹配并提取包名
356
- const match = packageString.match(regex);
357
- // 返回匹配到的包名,如果没有匹配到则返回原字符串
358
- return match ? match[0] : packageString;
359
359
  }
360
- /**
361
- * Tries to find the package.json's version of a shared package
362
- * if `package.json` is not declared in `exports`
363
- * @param {string} sharedName
364
- * @returns {string | undefined}
365
- */
366
- function searchPackageVersion(sharedName) {
367
- try {
368
- const sharedPath = require.resolve(sharedName);
369
- let potentialPackageJsonDir = path.dirname(sharedPath);
370
- const rootDir = path.parse(potentialPackageJsonDir).root;
371
- while (path.parse(potentialPackageJsonDir).base !== 'node_modules' && potentialPackageJsonDir !== rootDir) {
372
- const potentialPackageJsonPath = path.join(potentialPackageJsonDir, 'package.json');
373
- if (fs.existsSync(potentialPackageJsonPath)) {
374
- const potentialPackageJson = require(potentialPackageJsonPath);
375
- if (typeof potentialPackageJson == 'object' && potentialPackageJson !== null && typeof potentialPackageJson.version === 'string' && potentialPackageJson.name === sharedName) {
376
- return potentialPackageJson.version;
377
- }
378
- }
379
- potentialPackageJsonDir = path.dirname(potentialPackageJsonDir);
360
+ const normalizeDevOptions = dev => {
361
+ if (dev === false) {
362
+ return false;
363
+ }
364
+ if (dev === true || typeof dev === 'undefined') {
365
+ return _extends({}, DEFAULT_DEV_OPTIONS);
366
+ }
367
+ return _extends({}, DEFAULT_DEV_OPTIONS, dev);
368
+ };
369
+ const buildDtsModuleFederationConfig = options => {
370
+ const exposes = {};
371
+ Object.entries(options.exposes).forEach(([key, value]) => {
372
+ if (typeof value === 'string') {
373
+ exposes[key] = value;
374
+ return;
380
375
  }
381
- } catch (_) {}
382
- return undefined;
383
- }
384
- function normalizeShareItem(key, shareItem) {
385
- let version;
386
- try {
387
- try {
388
- version = require(path.join(removePathFromNpmPackage(key), 'package.json')).version;
389
- } catch (e1) {
390
- try {
391
- const localPath = path.join(process.cwd(), 'node_modules', removePathFromNpmPackage(key), 'package.json');
392
- version = require(localPath).version;
393
- } catch (e2) {
394
- version = searchPackageVersion(key);
395
- if (!version) console.error(e1);
396
- }
376
+ const importValue = Array.isArray(value.import) ? value.import[0] : value.import;
377
+ if (importValue) {
378
+ exposes[key] = importValue;
397
379
  }
398
- } catch (e) {
399
- console.error(`Unexpected error resolving version for ${key}:`, e);
400
- }
401
- if (typeof shareItem === 'string') {
402
- return {
403
- name: shareItem,
404
- version,
405
- scope: 'default',
406
- from: '',
407
- shareConfig: {
408
- import: undefined,
409
- singleton: false,
410
- requiredVersion: version ? `^${version}` : '*'
411
- }
412
- };
380
+ });
381
+ const remotes = {};
382
+ Object.entries(options.remotes).forEach(([key, remote]) => {
383
+ var _remote$entryGlobalNa, _remote$entryGlobalNa2;
384
+ if (typeof remote === 'string') {
385
+ remotes[key] = remote;
386
+ return;
387
+ }
388
+ if (!remote.entry) {
389
+ return;
390
+ }
391
+ const entryLooksLikeUrl = ((_remote$entryGlobalNa = remote.entryGlobalName) == null ? void 0 : _remote$entryGlobalNa.startsWith('http')) || ((_remote$entryGlobalNa2 = remote.entryGlobalName) == null ? void 0 : _remote$entryGlobalNa2.includes('.json'));
392
+ const entryGlobalName = entryLooksLikeUrl ? remote.name || key : remote.entryGlobalName || remote.name || key;
393
+ remotes[key] = `${entryGlobalName}@${remote.entry}`;
394
+ });
395
+ return _extends({}, options, {
396
+ exposes,
397
+ remotes
398
+ });
399
+ };
400
+ const resolveOutputDir = config => {
401
+ const {
402
+ outDir
403
+ } = config.build;
404
+ if (path.isAbsolute(outDir)) {
405
+ return path.relative(config.root, outDir);
413
406
  }
414
- return {
415
- name: key,
416
- from: '',
417
- version: shareItem.version || version,
418
- scope: shareItem.shareScope || 'default',
419
- shareConfig: {
420
- import: typeof shareItem === 'object' ? shareItem.import : undefined,
421
- singleton: shareItem.singleton || false,
422
- requiredVersion: shareItem.requiredVersion || (version ? `^${version}` : '*'),
423
- strictVersion: !!shareItem.strictVersion
407
+ return outDir;
408
+ };
409
+ const ensureRuntimePlugin = (options, pluginId) => {
410
+ const hasPlugin = options.runtimePlugins.some(plugin => {
411
+ if (typeof plugin === 'string') {
412
+ return plugin === pluginId;
424
413
  }
414
+ return plugin[0] === pluginId;
415
+ });
416
+ if (!hasPlugin) {
417
+ options.runtimePlugins.push(pluginId);
418
+ }
419
+ };
420
+ const normalizeDevDtsOptions = (dts, context) => {
421
+ const defaultGenerateTypes = {
422
+ compileInChildProcess: true
425
423
  };
426
- }
427
- function normalizeShared(shared) {
428
- if (!shared) return {};
429
- const result = {};
430
- if (Array.isArray(shared)) {
431
- shared.forEach(key => {
432
- result[key] = normalizeShareItem(key, key);
433
- });
434
- return result;
424
+ const defaultConsumeTypes = {
425
+ consumeAPITypes: true
426
+ };
427
+ return normalizeOptions(isTSProject(dts, context), {
428
+ generateTypes: defaultGenerateTypes,
429
+ consumeTypes: defaultConsumeTypes,
430
+ extraOptions: {},
431
+ displayErrorInTerminal: typeof dts === 'object' && dts ? dts.displayErrorInTerminal : undefined
432
+ }, 'mfOptions.dts')(dts);
433
+ };
434
+ const logDtsError = (error, dtsOptions) => {
435
+ if (dtsOptions === false) {
436
+ return;
435
437
  }
436
- if (typeof shared === 'object') {
437
- Object.keys(shared).forEach(key => {
438
- result[key] = normalizeShareItem(key, shared[key]);
439
- });
438
+ if (typeof dtsOptions === 'object' && dtsOptions && dtsOptions.displayErrorInTerminal === false) {
439
+ return;
440
440
  }
441
- return result;
442
- }
443
- function normalizeLibrary(library) {
444
- if (!library) return undefined;
445
- return library;
446
- }
447
- function normalizeManifest(manifest = false) {
448
- if (typeof manifest === 'boolean') {
449
- return manifest;
441
+ console.error(error);
442
+ };
443
+ function pluginDts(options) {
444
+ if (options.dts === false) {
445
+ return [];
450
446
  }
451
- return Object.assign({
452
- filePath: '',
453
- disableAssetsAnalyze: false,
454
- fileName: 'mf-manifest.json'
455
- }, manifest);
456
- }
457
- let config;
458
- function getNormalizeModuleFederationOptions() {
459
- return config;
460
- }
461
- function getNormalizeShareItem(key) {
462
- const options = getNormalizeModuleFederationOptions();
463
- const shareItem = options.shared[key] || options.shared[removePathFromNpmPackage(key)] || options.shared[removePathFromNpmPackage(key) + '/'];
464
- return shareItem;
465
- }
466
- function normalizeModuleFederationOptions(options) {
467
- if (options.virtualModuleDir && options.virtualModuleDir.includes('/')) {
468
- throw new Error(`Invalid virtualModuleDir: "${options.virtualModuleDir}". ` + `The virtualModuleDir option cannot contain slashes (/). ` + `Please use a single directory name like '__mf__virtual__your_app_name'.`);
469
- }
470
- return config = {
471
- exposes: normalizeExposes(options.exposes),
472
- filename: options.filename || 'remoteEntry-[hash]',
473
- library: normalizeLibrary(options.library),
474
- name: options.name,
475
- // remoteType: options.remoteType,
476
- remotes: normalizeRemotes(options.remotes),
477
- runtime: options.runtime,
478
- shareScope: options.shareScope || 'default',
479
- shared: normalizeShared(options.shared),
480
- runtimePlugins: options.runtimePlugins || [],
481
- implementation: options.implementation || require.resolve('@module-federation/runtime'),
482
- manifest: normalizeManifest(options.manifest),
483
- dev: options.dev,
484
- dts: options.dts,
485
- getPublicPath: options.getPublicPath,
486
- publicPath: options.publicPath,
487
- shareStrategy: options.shareStrategy || 'version-first',
488
- ignoreOrigin: options.ignoreOrigin || false,
489
- virtualModuleDir: options.virtualModuleDir || '__mf__virtual',
490
- hostInitInjectLocation: options.hostInitInjectLocation || 'html',
491
- bundleAllCSS: options.bundleAllCSS || false
492
- };
493
- }
494
-
495
- /**
496
- * Escaping rules:
497
- * Convert using the format __${mapping}__, where _ and $ are not allowed in npm package names but can be used in variable names.
498
- * @ => 1
499
- * / => 2
500
- * - => 3
501
- * . => 4
502
- */
503
- /**
504
- * Encodes a package name into a valid file name.
505
- * @param {string} name - The package name, e.g., "@scope/xx-xx.xx".
506
- * @returns {string} - The encoded file name.
507
- */
508
- function packageNameEncode(name) {
509
- if (typeof name !== 'string') throw new Error('A string package name is required');
510
- return name.replace(/@/g, '_mf_0_').replace(/\//g, '_mf_1_').replace(/-/g, '_mf_2_').replace(/\./g, '_mf_3_');
511
- }
512
- /**
513
- * Decodes an encoded file name back to the original package name.
514
- * @param {string} encoded - The encoded file name, e.g., "_mf_0_scope_mf_1_xx_mf_2_xx_mf_3_xx".
515
- * @returns {string} - The decoded package name.
516
- */
517
- function packageNameDecode(encoded) {
518
- if (typeof encoded !== 'string') throw new Error('A string encoded file name is required');
519
- return encoded.replace(/_mf_0_/g, '@').replace(/_mf_1_/g, '/').replace(/_mf_2_/g, '-').replace(/_mf_3_/g, '.');
520
- }
521
-
522
- /**
523
- * https://github.com/module-federation/vite/issues/68
524
- */
525
- function getLocalSharedImportMapPath_temp() {
526
- const {
527
- name
528
- } = getNormalizeModuleFederationOptions();
529
- return path__default.resolve('.__mf__temp', packageNameEncode(name), 'localSharedImportMap');
530
- }
531
- function writeLocalSharedImportMap_temp(content) {
532
- const localSharedImportMapId = getLocalSharedImportMapPath_temp();
533
- createFile(localSharedImportMapId + '.js', '\n// Windows temporarily needs this file, https://github.com/module-federation/vite/issues/68\n' + content);
534
- }
535
- function createFile(filePath, content) {
536
- const dir = path__default.dirname(filePath);
537
- mkdirSync(dir, {
538
- recursive: true
539
- });
540
- writeFileSync(filePath, content);
541
- }
542
-
543
- /**
544
- * Serializes a JavaScript object into a string of source code that can be evaluated.
545
- * This function is used to create runtime plugin options without relying solely on JSON.stringify,
546
- * allowing support for non-JSON types like RegExp, Date, Map, Set, and Functions.
547
- * It also safely handles circular references.
548
- *
549
- * @param {Record<string, unknown>} options - The options object to serialize.
550
- * @returns {string} The resulting JavaScript source code string.
551
- */
552
- function serializeRuntimeOptions(options) {
553
- // Use a WeakSet to track objects already encountered, which helps in detecting circular references.
554
- const seenObjects = new WeakSet();
555
- /**
556
- * Recursive inner function to serialize any value into a source code string.
557
- */
558
- function valueToCode(val) {
559
- // 1. Handle primitive values
560
- if (val === null) return 'null';
561
- const type = typeof val;
562
- if (type === 'string') return JSON.stringify(val);
563
- if (type === 'number' || type === 'boolean') return String(val);
564
- if (type === 'undefined') return 'undefined';
565
- // Handle Symbol
566
- if (type === 'symbol') {
567
- var _val$description;
568
- const desc = (_val$description = val.description) != null ? _val$description : '';
569
- return `Symbol(${JSON.stringify(desc)})`;
570
- }
571
- // Handle Function (returns the function's source code)
572
- if (type === 'function') return val.toString();
573
- // 2. Handle special built-in objects
574
- if (val instanceof Date) return `new Date(${JSON.stringify(val.toISOString())})`;
575
- if (val instanceof RegExp) {
576
- return `new RegExp(${JSON.stringify(val.source)}, ${JSON.stringify(val.flags)})`;
577
- }
578
- // 3. Check for circular references and mark object as seen
579
- // This applies to objects, arrays, maps, and sets.
580
- if (type === 'object') {
581
- if (seenObjects.has(val)) {
582
- // This object has been seen previously in the recursion path
583
- return `"__circular__"`;
447
+ const dtsModuleFederationConfig = buildDtsModuleFederationConfig(options);
448
+ let resolvedConfig;
449
+ let devWorker;
450
+ let normalizedDevOptions;
451
+ let hasGeneratedBundle = false;
452
+ const devPlugin = {
453
+ name: 'module-federation-dts-dev',
454
+ apply: 'serve',
455
+ config(config) {
456
+ normalizedDevOptions = normalizeDevOptions(options.dev);
457
+ if (!normalizedDevOptions) {
458
+ return;
584
459
  }
585
- seenObjects.add(val);
586
- }
587
- // 4. Handle Array, Map, Set
588
- if (Array.isArray(val)) {
589
- // Recursively serialize each element
590
- return `[${val.map(valueToCode).join(', ')}]`;
591
- }
592
- if (val instanceof Map) {
593
- // Serialize Map entries into an array of [key, value] pairs
594
- const entries = Array.from(val.entries()).map(([k, v]) => `[${valueToCode(k)}, ${valueToCode(v)}]`);
595
- return `new Map([${entries.join(', ')}])`;
596
- }
597
- if (val instanceof Set) {
598
- // Serialize Set values into an array
599
- const items = Array.from(val.values()).map(valueToCode);
600
- return `new Set([${items.join(', ')}])`;
601
- }
602
- // 5. Handle plain objects (the default object type)
603
- if (type === 'object') {
604
- const properties = [];
605
- // Iterate over the object's own enumerable properties
606
- for (const key in val) {
607
- if (Object.prototype.hasOwnProperty.call(val, key)) {
608
- // Wrap the key in JSON.stringify to handle non-identifier keys
609
- properties.push(`${JSON.stringify(key)}: ${valueToCode(val[key])}`);
610
- }
460
+ if (normalizedDevOptions.disableDynamicRemoteTypeHints) {
461
+ return;
611
462
  }
612
- return `{${properties.join(', ')}}`;
613
- }
614
- // 6. Fallback case (e.g., BigInt, other object types)
615
- // Coerce to string and then JSON.stringify that string for safety
616
- return JSON.stringify(String(val));
617
- }
618
- // Start serialization for the top-level object
619
- const topLevelProps = [];
620
- // Iterate over the properties of the root 'options' object
621
- for (const key in options) {
622
- if (Object.prototype.hasOwnProperty.call(options, key)) {
623
- topLevelProps.push(`${JSON.stringify(key)}: ${valueToCode(options[key])}`);
624
- }
625
- }
626
- return `{${topLevelProps.join(', ')}}`;
627
- }
628
-
629
- // Cache root path
630
- let rootDir;
631
- function findNodeModulesDir(root = process.cwd()) {
632
- let currentDir = root;
633
- while (currentDir !== parse(currentDir).root) {
634
- const nodeModulesPath = join(currentDir, 'node_modules');
635
- if (existsSync(nodeModulesPath)) {
636
- return nodeModulesPath;
637
- }
638
- currentDir = dirname(currentDir);
639
- }
640
- return '';
641
- }
642
- // Cache nodeModulesDir result to avoid repeated calculations
643
- let cachedNodeModulesDir;
644
- function getNodeModulesDir() {
645
- if (!cachedNodeModulesDir) {
646
- cachedNodeModulesDir = findNodeModulesDir(rootDir);
647
- }
648
- return cachedNodeModulesDir;
649
- }
650
- function getSuffix(name) {
651
- const base = basename(name);
652
- const dotIndex = base.lastIndexOf('.');
653
- if (dotIndex > 0 && dotIndex < base.length - 1) {
654
- return base.slice(dotIndex);
655
- }
656
- return '.js';
657
- }
658
- const patternMap = {};
659
- const cacheMap = {};
660
- /**
661
- * Physically generate files as virtual modules under node_modules/__mf__virtual/*
662
- */
663
- function assertModuleFound(tag, str = '') {
664
- const module = VirtualModule.findModule(tag, str);
665
- if (!module) {
666
- throw new Error(`Module Federation shared module '${str}' not found. Please ensure it's installed as a dependency in your package.json.`);
667
- }
668
- return module;
669
- }
670
- class VirtualModule {
671
- /**
672
- * Set the root path for finding node_modules
673
- * @param root - Root path
674
- */
675
- static setRoot(root) {
676
- rootDir = root;
677
- // Reset cache to ensure using the new root path
678
- cachedNodeModulesDir = undefined;
679
- }
680
- /**
681
- * Ensure virtual package directory exists
682
- */
683
- static ensureVirtualPackageExists() {
684
- const nodeModulesDir = getNodeModulesDir();
685
- const {
686
- virtualModuleDir
687
- } = getNormalizeModuleFederationOptions();
688
- const virtualPackagePath = resolve(nodeModulesDir, virtualModuleDir);
689
- if (!existsSync(virtualPackagePath)) {
690
- mkdirSync(virtualPackagePath);
691
- writeFileSync(resolve(virtualPackagePath, 'empty.js'), '');
692
- writeFileSync(resolve(virtualPackagePath, 'package.json'), JSON.stringify({
693
- name: virtualModuleDir,
694
- main: 'empty.js'
695
- }));
696
- }
697
- }
698
- static findModule(tag, str = '') {
699
- if (!patternMap[tag]) patternMap[tag] = new RegExp(`(.*${packageNameEncode(tag)}(.+?)${packageNameEncode(tag)}.*)`);
700
- const moduleName = (str.match(patternMap[tag]) || [])[2];
701
- if (moduleName) return cacheMap[tag][packageNameDecode(moduleName)];
702
- return undefined;
703
- }
704
- constructor(name, tag = '__mf_v__', suffix = '') {
705
- this.name = void 0;
706
- this.tag = void 0;
707
- this.suffix = void 0;
708
- this.inited = false;
709
- this.name = name;
710
- this.tag = tag;
711
- this.suffix = suffix || getSuffix(name);
712
- if (!cacheMap[this.tag]) cacheMap[this.tag] = {};
713
- cacheMap[this.tag][this.name] = this;
714
- }
715
- getPath() {
716
- return resolve(getNodeModulesDir(), this.getImportId());
717
- }
718
- getImportId() {
719
- const {
720
- name: mfName,
721
- virtualModuleDir
722
- } = getNormalizeModuleFederationOptions();
723
- return `${virtualModuleDir}/${packageNameEncode(`${mfName}${this.tag}${this.name}${this.tag}`)}${this.suffix}`;
724
- }
725
- writeSync(code, force) {
726
- if (!force && this.inited) return;
727
- if (!this.inited) {
728
- this.inited = true;
729
- }
730
- writeFileSync(this.getPath(), code);
731
- }
732
- write(code) {
733
- writeFile(this.getPath(), code, function () {});
734
- }
735
- }
736
-
737
- const VIRTUAL_EXPOSES = 'virtual:mf-exposes';
738
- function generateExposes() {
739
- const options = getNormalizeModuleFederationOptions();
740
- return `
741
- export default {
742
- ${Object.keys(options.exposes).map(key => {
743
- return `
744
- ${JSON.stringify(key)}: async () => {
745
- const importModule = await import(${JSON.stringify(options.exposes[key].import)})
746
- const exportModule = {}
747
- Object.assign(exportModule, importModule)
748
- Object.defineProperty(exportModule, "__esModule", {
749
- value: true,
750
- enumerable: false
751
- })
752
- return exportModule
753
- }
754
- `;
755
- }).join(',')}
756
- }
757
- `;
758
- }
759
-
760
- const virtualRuntimeInitStatus = new VirtualModule('runtimeInit');
761
- function writeRuntimeInitStatus() {
762
- // Use globalThis singleton to ensure only one initPromise exists
763
- const globalKey = `__mf_init__${virtualRuntimeInitStatus.getImportId()}__`;
764
- virtualRuntimeInitStatus.writeSync(`
765
- const globalKey = ${JSON.stringify(globalKey)}
766
- if (!globalThis[globalKey]) {
767
- let initResolve, initReject
768
- const initPromise = new Promise((re, rj) => {
769
- initResolve = re
770
- initReject = rj
771
- })
772
- globalThis[globalKey] = {
773
- initPromise,
774
- initResolve,
775
- initReject
463
+ ensureRuntimePlugin(options, DYNAMIC_HINTS_PLUGIN);
464
+ const define = config.define ? _extends({}, config.define) : {};
465
+ if (!('FEDERATION_IPV4' in define)) {
466
+ define.FEDERATION_IPV4 = JSON.stringify(getIPv4());
776
467
  }
777
- }
778
- module.exports = globalThis[globalKey]
779
- `);
780
- }
781
-
782
- const cacheRemoteMap = {};
783
- const LOAD_REMOTE_TAG = '__loadRemote__';
784
- function getRemoteVirtualModule(remote, command) {
785
- if (!cacheRemoteMap[remote]) {
786
- cacheRemoteMap[remote] = new VirtualModule(remote, LOAD_REMOTE_TAG, '.js');
787
- cacheRemoteMap[remote].writeSync(generateRemotes(remote, command));
788
- }
789
- const virtual = cacheRemoteMap[remote];
790
- return virtual;
791
- }
792
- const usedRemotesMap = {
793
- // remote1: {remote1/App, remote1, remote1/Button}
794
- };
795
- function addUsedRemote(remoteKey, remoteModule) {
796
- if (!usedRemotesMap[remoteKey]) usedRemotesMap[remoteKey] = new Set();
797
- usedRemotesMap[remoteKey].add(remoteModule);
798
- }
799
- function getUsedRemotesMap() {
800
- return usedRemotesMap;
801
- }
802
- function generateRemotes(id, command) {
803
- return `
804
- const {initPromise} = require("${virtualRuntimeInitStatus.getImportId()}")
805
- const res = initPromise.then(runtime => runtime.loadRemote(${JSON.stringify(id)}))
806
- const exportModule = ${command !== 'build' ? '/*mf top-level-await placeholder replacement mf*/' : 'await '}initPromise.then(_ => res)
807
- module.exports = exportModule
808
- `;
809
- }
810
-
811
- /**
812
- * Even the resolveId hook cannot interfere with vite pre-build,
813
- * and adding query parameter virtual modules will also fail.
814
- * You can only proxy to the real file through alias
815
- */
816
- // *** __prebuild__
817
- const preBuildCacheMap = {};
818
- const PREBUILD_TAG = '__prebuild__';
819
- function writePreBuildLibPath(pkg) {
820
- if (!preBuildCacheMap[pkg]) preBuildCacheMap[pkg] = new VirtualModule(pkg, PREBUILD_TAG);
821
- preBuildCacheMap[pkg].writeSync('');
822
- }
823
- function getPreBuildLibImportId(pkg) {
824
- if (!preBuildCacheMap[pkg]) preBuildCacheMap[pkg] = new VirtualModule(pkg, PREBUILD_TAG);
825
- const importId = preBuildCacheMap[pkg].getImportId();
826
- return importId;
827
- }
828
- // *** __loadShare__
829
- const LOAD_SHARE_TAG = '__loadShare__';
830
- const loadShareCacheMap = {};
831
- function getLoadShareModulePath(pkg) {
832
- if (!loadShareCacheMap[pkg]) loadShareCacheMap[pkg] = new VirtualModule(pkg, LOAD_SHARE_TAG, '.js');
833
- const filepath = loadShareCacheMap[pkg].getPath();
834
- return filepath;
835
- }
836
- function writeLoadShareModule(pkg, shareItem, command) {
837
- loadShareCacheMap[pkg].writeSync(`
838
- ;() => import(${JSON.stringify(getPreBuildLibImportId(pkg))}).catch(() => {});
839
- // dev uses dynamic import to separate chunks
840
- ${command !== 'build' ? `;() => import(${JSON.stringify(pkg)}).catch(() => {});` : ''}
841
- const {initPromise} = require("${virtualRuntimeInitStatus.getImportId()}")
842
- const res = initPromise.then(runtime => runtime.loadShare(${JSON.stringify(pkg)}, {
843
- customShareInfo: {shareConfig:{
844
- singleton: ${shareItem.shareConfig.singleton},
845
- strictVersion: ${shareItem.shareConfig.strictVersion},
846
- requiredVersion: ${JSON.stringify(shareItem.shareConfig.requiredVersion)}
847
- }}
848
- }))
849
- const exportModule = ${command !== 'build' ? '/*mf top-level-await placeholder replacement mf*/' : 'await '}res.then(factory => factory())
850
- module.exports = exportModule
851
- `);
852
- }
853
-
854
- let usedShares = new Set();
855
- function getUsedShares() {
856
- return usedShares;
857
- }
858
- function addUsedShares(pkg) {
859
- usedShares.add(pkg);
860
- }
861
- // *** Expose locally provided shared modules here
862
- new VirtualModule('localSharedImportMap');
863
- function getLocalSharedImportMapPath() {
864
- return getLocalSharedImportMapPath_temp();
865
- // return localSharedImportMapModule.getPath()
866
- }
867
- let prevSharedCount;
868
- function writeLocalSharedImportMap() {
869
- const sharedCount = getUsedShares().size;
870
- if (prevSharedCount !== sharedCount) {
871
- prevSharedCount = sharedCount;
872
- writeLocalSharedImportMap_temp(generateLocalSharedImportMap());
873
- // localSharedImportMapModule.writeSync(generateLocalSharedImportMap(), true)
874
- }
875
- }
876
- function generateLocalSharedImportMap() {
877
- const options = getNormalizeModuleFederationOptions();
878
- return `
879
- import {loadShare} from "@module-federation/runtime";
880
- const importMap = {
881
- ${Array.from(getUsedShares()).sort().map(pkg => {
882
- const shareItem = getNormalizeShareItem(pkg);
883
- return `
884
- ${JSON.stringify(pkg)}: async () => {
885
- ${(shareItem == null ? void 0 : shareItem.shareConfig.import) === false ? `throw new Error(\`Shared module '\${${JSON.stringify(pkg)}}' must be provided by host\`);` : `let pkg = await import("${getPreBuildLibImportId(pkg)}");
886
- return pkg;`}
468
+ config.define = define;
469
+ },
470
+ configResolved(config) {
471
+ resolvedConfig = config;
472
+ },
473
+ configureServer(server) {
474
+ if (!normalizedDevOptions || !resolvedConfig) {
475
+ return;
476
+ }
477
+ const devOptions = normalizedDevOptions;
478
+ if (devOptions.disableDynamicRemoteTypeHints && devOptions.disableHotTypesReload && devOptions.disableLiveReload) {
479
+ return;
480
+ }
481
+ if (!options.name) {
482
+ throw new Error('name is required if you want to enable dev server!');
483
+ }
484
+ const outputDir = resolveOutputDir(resolvedConfig);
485
+ const normalizedDtsOptions = normalizeDevDtsOptions(options.dts, resolvedConfig.root);
486
+ if (typeof normalizedDtsOptions !== 'object') {
487
+ return;
488
+ }
489
+ const normalizedGenerateTypes = normalizeOptions(Boolean(normalizedDtsOptions), {
490
+ compileInChildProcess: true
491
+ }, 'mfOptions.dts.generateTypes')(normalizedDtsOptions.generateTypes);
492
+ const remote = normalizedGenerateTypes === false ? undefined : _extends({
493
+ implementation: normalizedDtsOptions.implementation,
494
+ context: resolvedConfig.root,
495
+ outputDir,
496
+ moduleFederationConfig: _extends({}, dtsModuleFederationConfig),
497
+ hostRemoteTypesFolder: normalizedGenerateTypes.typesFolder || '@mf-types'
498
+ }, normalizedGenerateTypes, {
499
+ typesFolder: '.dev-server'
500
+ });
501
+ if (remote && !remote.tsConfigPath && typeof normalizedDtsOptions === 'object' && normalizedDtsOptions.tsConfigPath) {
502
+ remote.tsConfigPath = normalizedDtsOptions.tsConfigPath;
503
+ }
504
+ const normalizedConsumeTypes = normalizeOptions(Boolean(normalizedDtsOptions), {
505
+ consumeAPITypes: true
506
+ }, 'mfOptions.dts.consumeTypes')(normalizedDtsOptions.consumeTypes);
507
+ const host = normalizedConsumeTypes === false ? undefined : _extends({
508
+ implementation: normalizedDtsOptions.implementation,
509
+ context: resolvedConfig.root,
510
+ moduleFederationConfig: dtsModuleFederationConfig,
511
+ typesFolder: normalizedConsumeTypes.typesFolder || '@mf-types',
512
+ abortOnError: false
513
+ }, normalizedConsumeTypes);
514
+ const extraOptions = normalizedDtsOptions.extraOptions || {};
515
+ if (!remote && !host && devOptions.disableLiveReload) {
516
+ return;
517
+ }
518
+ const startDevWorker = async () => {
519
+ var _server$httpServer;
520
+ let remoteTypeUrls;
521
+ if (host) {
522
+ remoteTypeUrls = await new Promise(resolve => {
523
+ consumeTypesAPI({
524
+ host,
525
+ extraOptions,
526
+ displayErrorInTerminal: normalizedDtsOptions.displayErrorInTerminal
527
+ }, resolve);
528
+ });
887
529
  }
888
- `;
889
- }).join(',')}
890
- }
891
- const usedShared = {
892
- ${Array.from(getUsedShares()).sort().map(key => {
893
- const shareItem = getNormalizeShareItem(key);
894
- if (!shareItem) return null;
895
- return `
896
- ${JSON.stringify(key)}: {
897
- name: ${JSON.stringify(key)},
898
- version: ${JSON.stringify(shareItem.version)},
899
- scope: [${JSON.stringify(shareItem.scope)}],
900
- loaded: false,
901
- from: ${JSON.stringify(options.name)},
902
- async get () {
903
- if (${shareItem.shareConfig.import === false}) {
904
- throw new Error(\`Shared module '\${${JSON.stringify(key)}}' must be provided by host\`);
905
- }
906
- usedShared[${JSON.stringify(key)}].loaded = true
907
- const {${JSON.stringify(key)}: pkgDynamicImport} = importMap
908
- const res = await pkgDynamicImport()
909
- const exportModule = {...res}
910
- // All npm packages pre-built by vite will be converted to esm
911
- Object.defineProperty(exportModule, "__esModule", {
912
- value: true,
913
- enumerable: false
914
- })
915
- return function () {
916
- return exportModule
917
- }
918
- },
919
- shareConfig: {
920
- singleton: ${shareItem.shareConfig.singleton},
921
- requiredVersion: ${JSON.stringify(shareItem.shareConfig.requiredVersion)},
922
- ${shareItem.shareConfig.import === false ? 'import: false,' : ''}
923
- }
924
- }
925
- `;
926
- }).filter(x => x !== null).join(',')}
530
+ devWorker = new DevWorker({
531
+ name: options.name,
532
+ remote,
533
+ host: host ? _extends({}, host, {
534
+ remoteTypeUrls
535
+ }) : undefined,
536
+ extraOptions,
537
+ disableLiveReload: devOptions.disableLiveReload,
538
+ disableHotTypesReload: devOptions.disableHotTypesReload
539
+ });
540
+ const update = () => {
541
+ var _devWorker;
542
+ return (_devWorker = devWorker) == null ? void 0 : _devWorker.update();
543
+ };
544
+ server.watcher.on('change', update);
545
+ server.watcher.on('add', update);
546
+ server.watcher.on('unlink', update);
547
+ (_server$httpServer = server.httpServer) == null || _server$httpServer.once('close', () => {
548
+ var _devWorker2;
549
+ (_devWorker2 = devWorker) == null || _devWorker2.exit();
550
+ server.watcher.off('change', update);
551
+ server.watcher.off('add', update);
552
+ server.watcher.off('unlink', update);
553
+ });
554
+ };
555
+ startDevWorker().catch(error => {
556
+ logDtsError(error, normalizedDtsOptions);
557
+ });
927
558
  }
928
- const usedRemotes = [${Object.keys(getUsedRemotesMap()).map(key => {
929
- var _JSON$stringify;
930
- const remote = options.remotes[key];
931
- if (!remote) return null;
932
- return `
933
- {
934
- entryGlobalName: ${JSON.stringify(remote.entryGlobalName)},
935
- name: ${JSON.stringify(remote.name)},
936
- type: ${JSON.stringify(remote.type)},
937
- entry: ${JSON.stringify(remote.entry)},
938
- shareScope: ${(_JSON$stringify = JSON.stringify(remote.shareScope)) != null ? _JSON$stringify : 'default'},
939
- }
940
- `;
941
- }).filter(x => x !== null).join(',')}
942
- ]
943
- export {
944
- usedShared,
945
- usedRemotes
559
+ };
560
+ const buildPlugin = {
561
+ name: 'module-federation-dts-build',
562
+ apply: 'build',
563
+ configResolved(config) {
564
+ resolvedConfig = config;
565
+ },
566
+ async generateBundle() {
567
+ var _consumeOptions;
568
+ if (hasGeneratedBundle) {
569
+ return;
946
570
  }
947
- `;
948
- }
949
- const REMOTE_ENTRY_ID = 'virtual:mf-REMOTE_ENTRY_ID';
950
- function generateRemoteEntry(options) {
951
- const pluginImportNames = options.runtimePlugins.map((p, i) => {
952
- if (typeof p === 'string') {
953
- return [`$runtimePlugin_${i}`, `import $runtimePlugin_${i} from "${p}";`, `undefined`];
954
- } else {
955
- return [`$runtimePlugin_${i}`, `import $runtimePlugin_${i} from "${p[0]}";`, serializeRuntimeOptions(p[1])];
956
- }
957
- });
958
- return `
959
- import {init as runtimeInit, loadRemote} from "@module-federation/runtime";
960
- ${pluginImportNames.map(item => item[1]).join('\n')}
961
- import exposesMap from "${VIRTUAL_EXPOSES}"
962
- import {usedShared, usedRemotes} from "${getLocalSharedImportMapPath()}"
963
- import {
964
- initResolve
965
- } from "${virtualRuntimeInitStatus.getImportId()}"
966
- const initTokens = {}
967
- const shareScopeName = ${JSON.stringify(options.shareScope)}
968
- const mfName = ${JSON.stringify(options.name)}
969
- async function init(shared = {}, initScope = []) {
970
- const initRes = runtimeInit({
971
- name: mfName,
972
- remotes: usedRemotes,
973
- shared: usedShared,
974
- plugins: [${pluginImportNames.map(item => `${item[0]}(${item[2]})`).join(', ')}],
975
- ${options.shareStrategy ? `shareStrategy: '${options.shareStrategy}'` : ''}
976
- });
977
- // handling circular init calls
978
- var initToken = initTokens[shareScopeName];
979
- if (!initToken)
980
- initToken = initTokens[shareScopeName] = { from: mfName };
981
- if (initScope.indexOf(initToken) >= 0) return;
982
- initScope.push(initToken);
983
- initRes.initShareScopeMap('${options.shareScope}', shared);
984
- try {
985
- await Promise.all(await initRes.initializeSharing('${options.shareScope}', {
986
- strategy: '${options.shareStrategy}',
987
- from: "build",
988
- initScope
989
- }));
990
- } catch (e) {
991
- console.error(e)
992
- }
993
- initResolve(initRes)
994
- return initRes
995
- }
996
-
997
- function getExposes(moduleName) {
998
- if (!(moduleName in exposesMap)) throw new Error(\`Module \${moduleName} does not exist in container.\`)
999
- return (exposesMap[moduleName])().then(res => () => res)
1000
- }
1001
- export {
1002
- init,
1003
- getExposes as get
1004
- }
1005
- `;
1006
- }
1007
- /**
1008
- * Inject entry file, automatically init when used as host,
1009
- * and will not inject remoteEntry
1010
- */
1011
- const HOST_AUTO_INIT_TAG = '__H_A_I__';
1012
- const hostAutoInitModule = new VirtualModule('hostAutoInit', HOST_AUTO_INIT_TAG);
1013
- function writeHostAutoInit() {
1014
- hostAutoInitModule.writeSync(`
1015
- const remoteEntryPromise = import("${REMOTE_ENTRY_ID}")
1016
- // __tla only serves as a hack for vite-plugin-top-level-await.
1017
- Promise.resolve(remoteEntryPromise)
1018
- .then(remoteEntry => {
1019
- return Promise.resolve(remoteEntry.__tla)
1020
- .then(remoteEntry.init).catch(remoteEntry.init)
1021
- })
1022
- `);
1023
- }
1024
- function getHostAutoInitImportId() {
1025
- return hostAutoInitModule.getImportId();
1026
- }
1027
- function getHostAutoInitPath() {
1028
- return hostAutoInitModule.getPath();
1029
- }
1030
-
1031
- function initVirtualModules() {
1032
- writeLocalSharedImportMap();
1033
- writeHostAutoInit();
1034
- writeRuntimeInitStatus();
571
+ hasGeneratedBundle = true;
572
+ if (!resolvedConfig) {
573
+ return;
574
+ }
575
+ let normalizedDtsOptions;
576
+ try {
577
+ normalizedDtsOptions = normalizeDtsOptions(dtsModuleFederationConfig, resolvedConfig.root);
578
+ } catch (error) {
579
+ logDtsError(error, options.dts);
580
+ return;
581
+ }
582
+ if (typeof normalizedDtsOptions !== 'object') {
583
+ return;
584
+ }
585
+ const context = resolvedConfig.root;
586
+ const outputDir = resolveOutputDir(resolvedConfig);
587
+ let consumeOptions;
588
+ try {
589
+ consumeOptions = normalizeConsumeTypesOptions({
590
+ context,
591
+ dtsOptions: normalizedDtsOptions,
592
+ pluginOptions: dtsModuleFederationConfig
593
+ });
594
+ } catch (error) {
595
+ logDtsError(error, normalizedDtsOptions);
596
+ return;
597
+ }
598
+ if ((_consumeOptions = consumeOptions) != null && (_consumeOptions = _consumeOptions.host) != null && _consumeOptions.typesOnBuild) {
599
+ try {
600
+ await consumeTypesAPI(consumeOptions);
601
+ } catch (error) {
602
+ logDtsError(error, normalizedDtsOptions);
603
+ }
604
+ }
605
+ let generateOptions;
606
+ try {
607
+ generateOptions = normalizeGenerateTypesOptions({
608
+ context,
609
+ outputDir,
610
+ dtsOptions: normalizedDtsOptions,
611
+ pluginOptions: dtsModuleFederationConfig
612
+ });
613
+ } catch (error) {
614
+ logDtsError(error, normalizedDtsOptions);
615
+ return;
616
+ }
617
+ if (!generateOptions) {
618
+ return;
619
+ }
620
+ try {
621
+ await generateTypesAPI({
622
+ dtsManagerOptions: generateOptions
623
+ });
624
+ } catch (error) {
625
+ logDtsError(error, normalizedDtsOptions);
626
+ }
627
+ }
628
+ };
629
+ return [devPlugin, buildPlugin];
1035
630
  }
1036
631
 
1037
- const ASSET_TYPES = ['js', 'css'];
1038
- const LOAD_TIMINGS = ['sync', 'async'];
1039
- const JS_EXTENSIONS = ['.ts', '.tsx', '.jsx', '.mjs', '.cjs'];
1040
- /**
1041
- * Creates an empty asset map structure for tracking JS and CSS assets
1042
- * @returns Initialized asset map with sync/async arrays for JS and CSS
1043
- */
1044
- const createEmptyAssetMap = () => ({
1045
- js: {
1046
- sync: [],
1047
- async: []
1048
- },
1049
- css: {
1050
- sync: [],
1051
- async: []
632
+ function normalizeExposesItem(key, item) {
633
+ let importPath = '';
634
+ if (typeof item === 'string') {
635
+ importPath = item;
1052
636
  }
1053
- });
1054
- /**
1055
- * Tracks an asset in the preload map with deduplication
1056
- * @param map - The preload map to update
1057
- * @param key - The module key to track under
1058
- * @param fileName - The asset filename to track
1059
- * @param isAsync - Whether the asset is loaded async
1060
- * @param type - The asset type ('js' or 'css')
1061
- */
1062
- const trackAsset = (map, key, fileName, isAsync, type) => {
1063
- if (!map[key]) {
1064
- map[key] = createEmptyAssetMap();
637
+ if (typeof item === 'object') {
638
+ importPath = item.import;
1065
639
  }
1066
- const target = isAsync ? map[key][type].async : map[key][type].sync;
1067
- if (!target.includes(fileName)) {
1068
- target.push(fileName);
640
+ return {
641
+ import: importPath
642
+ };
643
+ }
644
+ function normalizeExposes(exposes) {
645
+ if (!exposes) return {};
646
+ const res = {};
647
+ Object.keys(exposes).forEach(key => {
648
+ res[key] = normalizeExposesItem(key, exposes[key]);
649
+ });
650
+ return res;
651
+ }
652
+ function normalizeRemotes(remotes) {
653
+ if (!remotes) return {};
654
+ const result = {};
655
+ if (typeof remotes === 'object') {
656
+ Object.keys(remotes).forEach(key => {
657
+ result[key] = normalizeRemoteItem(key, remotes[key]);
658
+ });
1069
659
  }
1070
- };
1071
- /**
1072
- * Checks if a file is a CSS file by extension
1073
- * @param fileName - The filename to check
1074
- * @returns True if file has a CSS extension (.css, .scss, .less)
1075
- */
1076
- const isCSSFile = fileName => {
1077
- return fileName.endsWith('.css') || fileName.endsWith('.scss') || fileName.endsWith('.less');
1078
- };
1079
- /**
1080
- * Collects all CSS assets from the bundle
1081
- * @param bundle - The Rollup output bundle
1082
- * @returns Set of CSS asset filenames
1083
- */
1084
- const collectCssAssets = bundle => {
1085
- const cssAssets = new Set();
1086
- for (const [fileName, fileData] of Object.entries(bundle)) {
1087
- if (fileData.type === 'asset' && isCSSFile(fileName)) {
1088
- cssAssets.add(fileName);
1089
- }
660
+ return result;
661
+ }
662
+ function normalizeRemoteItem(key, remote) {
663
+ if (typeof remote === 'string') {
664
+ const [entryGlobalName] = remote.split('@');
665
+ const entry = remote.replace(entryGlobalName + '@', '');
666
+ return {
667
+ type: 'var',
668
+ name: key,
669
+ entry,
670
+ entryGlobalName,
671
+ shareScope: 'default'
672
+ };
1090
673
  }
1091
- return cssAssets;
1092
- };
674
+ return Object.assign({
675
+ type: 'var',
676
+ name: key,
677
+ shareScope: 'default',
678
+ entryGlobalName: key
679
+ }, remote);
680
+ }
681
+ function removePathFromNpmPackage(packageString) {
682
+ // 匹配npm包名的正则表达式,忽略路径部分
683
+ const regex = /^(?:@[^/]+\/)?[^/]+/;
684
+ // 使用正则表达式匹配并提取包名
685
+ const match = packageString.match(regex);
686
+ // 返回匹配到的包名,如果没有匹配到则返回原字符串
687
+ return match ? match[0] : packageString;
688
+ }
1093
689
  /**
1094
- * Processes module assets and tracks them in the files map
1095
- * @param bundle - The Rollup output bundle
1096
- * @param filesMap - The preload map to populate
1097
- * @param moduleMatcher - Function that matches module paths to keys
690
+ * Tries to find the package.json's version of a shared package
691
+ * if `package.json` is not declared in `exports`
692
+ * @param {string} sharedName
693
+ * @returns {string | undefined}
1098
694
  */
1099
- const processModuleAssets = (bundle, filesMap, moduleMatcher) => {
1100
- for (const [fileName, fileData] of Object.entries(bundle)) {
1101
- if (fileData.type !== 'chunk') continue;
1102
- if (!fileData.modules) continue;
1103
- for (const modulePath of Object.keys(fileData.modules)) {
1104
- const matchKey = moduleMatcher(modulePath);
1105
- if (!matchKey) continue;
1106
- // Track main JS chunk
1107
- trackAsset(filesMap, matchKey, fileName, false, 'js');
1108
- // Handle dynamic imports
1109
- if (fileData.dynamicImports) {
1110
- for (const dynamicImport of fileData.dynamicImports) {
1111
- const importData = bundle[dynamicImport];
1112
- if (!importData) continue;
1113
- const isCss = isCSSFile(dynamicImport);
1114
- trackAsset(filesMap, matchKey, dynamicImport, true, isCss ? 'css' : 'js');
695
+ function searchPackageVersion(sharedName) {
696
+ try {
697
+ const sharedPath = require.resolve(sharedName);
698
+ let potentialPackageJsonDir = path.dirname(sharedPath);
699
+ const rootDir = path.parse(potentialPackageJsonDir).root;
700
+ while (path.parse(potentialPackageJsonDir).base !== 'node_modules' && potentialPackageJsonDir !== rootDir) {
701
+ const potentialPackageJsonPath = path.join(potentialPackageJsonDir, 'package.json');
702
+ if (fs.existsSync(potentialPackageJsonPath)) {
703
+ const potentialPackageJson = require(potentialPackageJsonPath);
704
+ if (typeof potentialPackageJson == 'object' && potentialPackageJson !== null && typeof potentialPackageJson.version === 'string' && potentialPackageJson.name === sharedName) {
705
+ return potentialPackageJson.version;
1115
706
  }
1116
707
  }
708
+ potentialPackageJsonDir = path.dirname(potentialPackageJsonDir);
709
+ }
710
+ } catch (_) {}
711
+ return undefined;
712
+ }
713
+ function normalizeShareItem(key, shareItem) {
714
+ let version;
715
+ try {
716
+ try {
717
+ version = require(path.join(removePathFromNpmPackage(key), 'package.json')).version;
718
+ } catch (e1) {
719
+ try {
720
+ const localPath = path.join(process.cwd(), 'node_modules', removePathFromNpmPackage(key), 'package.json');
721
+ version = require(localPath).version;
722
+ } catch (e2) {
723
+ version = searchPackageVersion(key);
724
+ if (!version) console.error(e1);
725
+ }
1117
726
  }
727
+ } catch (e) {
728
+ console.error(`Unexpected error resolving version for ${key}:`, e);
1118
729
  }
1119
- };
730
+ if (typeof shareItem === 'string') {
731
+ return {
732
+ name: shareItem,
733
+ version,
734
+ scope: 'default',
735
+ from: '',
736
+ shareConfig: {
737
+ import: undefined,
738
+ singleton: false,
739
+ requiredVersion: version ? `^${version}` : '*'
740
+ }
741
+ };
742
+ }
743
+ return {
744
+ name: key,
745
+ from: '',
746
+ version: shareItem.version || version,
747
+ scope: shareItem.shareScope || 'default',
748
+ shareConfig: {
749
+ import: typeof shareItem === 'object' ? shareItem.import : undefined,
750
+ singleton: shareItem.singleton || false,
751
+ requiredVersion: shareItem.requiredVersion || (version ? `^${version}` : '*'),
752
+ strictVersion: !!shareItem.strictVersion
753
+ }
754
+ };
755
+ }
756
+ function normalizeShared(shared) {
757
+ if (!shared) return {};
758
+ const result = {};
759
+ if (Array.isArray(shared)) {
760
+ shared.forEach(key => {
761
+ result[key] = normalizeShareItem(key, key);
762
+ });
763
+ return result;
764
+ }
765
+ if (typeof shared === 'object') {
766
+ Object.keys(shared).forEach(key => {
767
+ result[key] = normalizeShareItem(key, shared[key]);
768
+ });
769
+ }
770
+ return result;
771
+ }
772
+ function normalizeLibrary(library) {
773
+ if (!library) return undefined;
774
+ return library;
775
+ }
776
+ function normalizeManifest(manifest = false) {
777
+ if (typeof manifest === 'boolean') {
778
+ return manifest;
779
+ }
780
+ return Object.assign({
781
+ filePath: '',
782
+ disableAssetsAnalyze: false,
783
+ fileName: 'mf-manifest.json'
784
+ }, manifest);
785
+ }
786
+ let config;
787
+ function getNormalizeModuleFederationOptions() {
788
+ return config;
789
+ }
790
+ function getNormalizeShareItem(key) {
791
+ const options = getNormalizeModuleFederationOptions();
792
+ const shareItem = options.shared[key] || options.shared[removePathFromNpmPackage(key)] || options.shared[removePathFromNpmPackage(key) + '/'];
793
+ return shareItem;
794
+ }
795
+ function normalizeModuleFederationOptions(options) {
796
+ if (options.virtualModuleDir && options.virtualModuleDir.includes('/')) {
797
+ throw new Error(`Invalid virtualModuleDir: "${options.virtualModuleDir}". ` + `The virtualModuleDir option cannot contain slashes (/). ` + `Please use a single directory name like '__mf__virtual__your_app_name'.`);
798
+ }
799
+ return config = {
800
+ exposes: normalizeExposes(options.exposes),
801
+ filename: options.filename || 'remoteEntry-[hash]',
802
+ library: normalizeLibrary(options.library),
803
+ name: options.name,
804
+ // remoteType: options.remoteType,
805
+ remotes: normalizeRemotes(options.remotes),
806
+ runtime: options.runtime,
807
+ shareScope: options.shareScope || 'default',
808
+ shared: normalizeShared(options.shared),
809
+ runtimePlugins: options.runtimePlugins || [],
810
+ implementation: options.implementation || require.resolve('@module-federation/runtime'),
811
+ manifest: normalizeManifest(options.manifest),
812
+ dev: options.dev,
813
+ dts: options.dts,
814
+ getPublicPath: options.getPublicPath,
815
+ publicPath: options.publicPath,
816
+ shareStrategy: options.shareStrategy || 'version-first',
817
+ ignoreOrigin: options.ignoreOrigin || false,
818
+ virtualModuleDir: options.virtualModuleDir || '__mf__virtual',
819
+ hostInitInjectLocation: options.hostInitInjectLocation || 'html',
820
+ bundleAllCSS: options.bundleAllCSS || false,
821
+ moduleParseTimeout: options.moduleParseTimeout || 10,
822
+ varFilename: options.varFilename
823
+ };
824
+ }
825
+
1120
826
  /**
1121
- * Deduplicates assets in the files map
1122
- * @param filesMap - The preload map to deduplicate
1123
- * @returns New deduplicated preload map
827
+ * Escaping rules:
828
+ * Convert using the format __${mapping}__, where _ and $ are not allowed in npm package names but can be used in variable names.
829
+ * @ => 1
830
+ * / => 2
831
+ * - => 3
832
+ * . => 4
1124
833
  */
1125
- const deduplicateAssets = filesMap => {
1126
- const result = {};
1127
- for (const [key, assetMaps] of Object.entries(filesMap)) {
1128
- result[key] = createEmptyAssetMap();
1129
- for (const type of ASSET_TYPES) {
1130
- for (const timing of LOAD_TIMINGS) {
1131
- result[key][type][timing] = Array.from(new Set(assetMaps[type][timing]));
1132
- }
1133
- }
1134
- }
1135
- return result;
1136
- };
1137
834
  /**
1138
- * Builds a mapping between module files and their share keys
1139
- * @param shareKeys - Set of share keys to map
1140
- * @param resolveFn - Function to resolve module paths
1141
- * @returns Map of file paths to their corresponding share keys
835
+ * Encodes a package name into a valid file name.
836
+ * @param {string} name - The package name, e.g., "@scope/xx-xx.xx".
837
+ * @returns {string} - The encoded file name.
1142
838
  */
1143
- const buildFileToShareKeyMap = async (shareKeys, resolveFn) => {
1144
- const fileToShareKey = new Map();
1145
- const resolutions = await Promise.all(Array.from(shareKeys).map(shareKey => resolveFn(getPreBuildLibImportId(shareKey)).then(resolution => {
1146
- var _resolution$id;
1147
- return {
1148
- shareKey,
1149
- file: resolution == null || (_resolution$id = resolution.id) == null ? void 0 : _resolution$id.split('?')[0]
1150
- };
1151
- }).catch(() => null)));
1152
- for (const resolution of resolutions) {
1153
- if (resolution != null && resolution.file) {
1154
- fileToShareKey.set(resolution.file, resolution.shareKey);
1155
- }
1156
- }
1157
- return fileToShareKey;
1158
- };
1159
-
839
+ function packageNameEncode(name) {
840
+ if (typeof name !== 'string') throw new Error('A string package name is required');
841
+ return name.replace(/@/g, '_mf_0_').replace(/\//g, '_mf_1_').replace(/-/g, '_mf_2_').replace(/\./g, '_mf_3_');
842
+ }
1160
843
  /**
1161
- * Resolves the public path for remote entries
1162
- * @param options - Module Federation options
1163
- * @param viteBase - Vite's base config value
1164
- * @param originalBase - Original base config before any transformations
1165
- * @returns The resolved public path
844
+ * Decodes an encoded file name back to the original package name.
845
+ * @param {string} encoded - The encoded file name, e.g., "_mf_0_scope_mf_1_xx_mf_2_xx_mf_3_xx".
846
+ * @returns {string} - The decoded package name.
1166
847
  */
1167
- function resolvePublicPath(options, viteBase, originalBase) {
1168
- // Use explicitly set publicPath if provided
1169
- if (options.publicPath) {
1170
- return options.publicPath;
1171
- }
1172
- // Handle empty original base case
1173
- if (originalBase === '') {
1174
- return 'auto';
1175
- }
1176
- // Use viteBase if available, ensuring it ends with a slash
1177
- if (viteBase) {
1178
- return viteBase.replace(/\/?$/, '/');
1179
- }
1180
- // Fallback to auto if no base is specified
1181
- return 'auto';
848
+ function packageNameDecode(encoded) {
849
+ if (typeof encoded !== 'string') throw new Error('A string encoded file name is required');
850
+ return encoded.replace(/_mf_0_/g, '@').replace(/_mf_1_/g, '/').replace(/_mf_2_/g, '-').replace(/_mf_3_/g, '.');
1182
851
  }
1183
852
 
1184
- const Manifest = () => {
1185
- const mfOptions = getNormalizeModuleFederationOptions();
853
+ /**
854
+ * https://github.com/module-federation/vite/issues/68
855
+ */
856
+ function getLocalSharedImportMapPath_temp() {
1186
857
  const {
1187
- name,
1188
- filename,
1189
- getPublicPath,
1190
- manifest: manifestOptions
1191
- } = mfOptions;
1192
- let mfManifestName = '';
1193
- if (manifestOptions === true) {
1194
- mfManifestName = 'mf-manifest.json';
1195
- }
1196
- if (typeof manifestOptions !== 'boolean') {
1197
- mfManifestName = path.join((manifestOptions == null ? void 0 : manifestOptions.filePath) || '', (manifestOptions == null ? void 0 : manifestOptions.fileName) || '');
1198
- }
1199
- let root;
1200
- let remoteEntryFile;
1201
- let publicPath;
1202
- let _command;
1203
- let _originalConfigBase;
1204
- let viteConfig;
858
+ name
859
+ } = getNormalizeModuleFederationOptions();
860
+ return path__default.resolve('.__mf__temp', packageNameEncode(name), 'localSharedImportMap');
861
+ }
862
+ function writeLocalSharedImportMap_temp(content) {
863
+ const localSharedImportMapId = getLocalSharedImportMapPath_temp();
864
+ createFile(localSharedImportMapId + '.js', '\n// Windows temporarily needs this file, https://github.com/module-federation/vite/issues/68\n' + content);
865
+ }
866
+ function createFile(filePath, content) {
867
+ const dir = path__default.dirname(filePath);
868
+ mkdirSync(dir, {
869
+ recursive: true
870
+ });
871
+ writeFileSync(filePath, content);
872
+ }
873
+
874
+ /**
875
+ * Serializes a JavaScript object into a string of source code that can be evaluated.
876
+ * This function is used to create runtime plugin options without relying solely on JSON.stringify,
877
+ * allowing support for non-JSON types like RegExp, Date, Map, Set, and Functions.
878
+ * It also safely handles circular references.
879
+ *
880
+ * @param {Record<string, unknown>} options - The options object to serialize.
881
+ * @returns {string} The resulting JavaScript source code string.
882
+ */
883
+ function serializeRuntimeOptions(options) {
884
+ // Use a WeakSet to track objects already encountered, which helps in detecting circular references.
885
+ const seenObjects = new WeakSet();
1205
886
  /**
1206
- * Adds global CSS assets to all module exports
1207
- * @param filesMap - The preload map to update
1208
- * @param cssAssets - Set of CSS asset filenames to add
887
+ * Recursive inner function to serialize any value into a source code string.
1209
888
  */
1210
- const addCssAssetsToAllExports = (filesMap, cssAssets) => {
1211
- Object.keys(filesMap).forEach(key => {
1212
- cssAssets.forEach(cssAsset => {
1213
- trackAsset(filesMap, key, cssAsset, false, 'css');
1214
- });
1215
- });
1216
- };
1217
- return [{
1218
- name: 'module-federation-manifest',
1219
- apply: 'serve',
1220
- /**
1221
- * Stores resolved Vite config for later use
1222
- */
1223
- /**
1224
- * Finalizes configuration after all plugins are resolved
1225
- * @param config - Fully resolved Vite config
1226
- */
1227
- configResolved(config) {
1228
- viteConfig = config;
1229
- },
1230
- /**
1231
- * Configures dev server middleware to handle manifest requests
1232
- * @param server - Vite dev server instance
1233
- */
1234
- configureServer(server) {
1235
- server.middlewares.use((req, res, next) => {
1236
- var _req$url;
1237
- if (!mfManifestName) {
1238
- next();
1239
- return;
1240
- }
1241
- if (((_req$url = req.url) == null ? void 0 : _req$url.replace(/\?.*/, '')) === (viteConfig.base + mfManifestName).replace(/^\/?/, '/')) {
1242
- res.setHeader('Content-Type', 'application/json');
1243
- res.setHeader('Access-Control-Allow-Origin', '*');
1244
- res.end(JSON.stringify(_extends({}, generateMFManifest({}), {
1245
- id: name,
1246
- name: name,
1247
- metaData: {
1248
- name: name,
1249
- type: 'app',
1250
- buildInfo: {
1251
- buildVersion: '1.0.0',
1252
- buildName: name
1253
- },
1254
- remoteEntry: {
1255
- name: filename,
1256
- path: '',
1257
- type: 'module'
1258
- },
1259
- ssrRemoteEntry: {
1260
- name: filename,
1261
- path: '',
1262
- type: 'module'
1263
- },
1264
- types: {
1265
- path: '',
1266
- name: ''
1267
- },
1268
- globalName: name,
1269
- pluginVersion: '0.2.5',
1270
- publicPath
1271
- }
1272
- })));
1273
- } else {
1274
- next();
1275
- }
1276
- });
889
+ function valueToCode(val) {
890
+ // 1. Handle primitive values
891
+ if (val === null) return 'null';
892
+ const type = typeof val;
893
+ if (type === 'string') return JSON.stringify(val);
894
+ if (type === 'number' || type === 'boolean') return String(val);
895
+ if (type === 'undefined') return 'undefined';
896
+ // Handle Symbol
897
+ if (type === 'symbol') {
898
+ var _val$description;
899
+ const desc = (_val$description = val.description) != null ? _val$description : '';
900
+ return `Symbol(${JSON.stringify(desc)})`;
1277
901
  }
1278
- }, {
1279
- name: 'module-federation-manifest',
1280
- enforce: 'post',
1281
- /**
1282
- * Initial plugin configuration
1283
- * @param config - Vite config object
1284
- * @param command - Current Vite command (serve/build)
1285
- */
1286
- config(config, {
1287
- command
1288
- }) {
1289
- if (!config.build) config.build = {};
1290
- if (!config.build.manifest) {
1291
- config.build.manifest = config.build.manifest || !!manifestOptions;
1292
- }
1293
- _command = command;
1294
- _originalConfigBase = config.base;
1295
- },
1296
- configResolved(config) {
1297
- root = config.root;
1298
- let base = config.base;
1299
- if (_command === 'serve') {
1300
- base = (config.server.origin || '') + config.base;
1301
- }
1302
- publicPath = resolvePublicPath(mfOptions, base, _originalConfigBase);
1303
- },
1304
- /**
1305
- * Generates the module federation manifest file
1306
- * @param options - Rollup output options
1307
- * @param bundle - Generated bundle assets
1308
- */
1309
- async generateBundle(options, bundle) {
1310
- if (!mfManifestName) return;
1311
- let filesMap = {};
1312
- // First pass: Find remoteEntry file
1313
- for (const [_, fileData] of Object.entries(bundle)) {
1314
- if (mfOptions.filename.replace(/[\[\]]/g, '_').replace(/\.[^/.]+$/, '') === fileData.name || fileData.name === 'remoteEntry') {
1315
- remoteEntryFile = fileData.fileName;
1316
- break; // We can break early since we only need to find remoteEntry once
1317
- }
902
+ // Handle Function (returns the function's source code)
903
+ if (type === 'function') return val.toString();
904
+ // 2. Handle special built-in objects
905
+ if (val instanceof Date) return `new Date(${JSON.stringify(val.toISOString())})`;
906
+ if (val instanceof RegExp) {
907
+ return `new RegExp(${JSON.stringify(val.source)}, ${JSON.stringify(val.flags)})`;
908
+ }
909
+ // 3. Check for circular references and mark object as seen
910
+ // This applies to objects, arrays, maps, and sets.
911
+ if (type === 'object') {
912
+ if (seenObjects.has(val)) {
913
+ // This object has been seen previously in the recursion path
914
+ return `"__circular__"`;
1318
915
  }
1319
- // Second pass: Collect all CSS assets
1320
- const allCssAssets = mfOptions.bundleAllCSS ? collectCssAssets(bundle) : new Set();
1321
- const exposesModules = Object.keys(mfOptions.exposes).map(item => mfOptions.exposes[item].import);
1322
- // Process exposed modules
1323
- processModuleAssets(bundle, filesMap, modulePath => {
1324
- const absoluteModulePath = path.resolve(root, modulePath);
1325
- return exposesModules.find(exposeModule => {
1326
- const exposePath = path.resolve(root, exposeModule);
1327
- // First try exact path match
1328
- if (absoluteModulePath === exposePath) {
1329
- return true;
1330
- }
1331
- // Then try path match without known extensions
1332
- const getPathWithoutKnownExt = filePath => {
1333
- const ext = path.extname(filePath);
1334
- return JS_EXTENSIONS.includes(ext) ? path.join(path.dirname(filePath), path.basename(filePath, ext)) : filePath;
1335
- };
1336
- const modulePathNoExt = getPathWithoutKnownExt(absoluteModulePath);
1337
- const exposePathNoExt = getPathWithoutKnownExt(exposePath);
1338
- return modulePathNoExt === exposePathNoExt;
1339
- });
1340
- });
1341
- // Process shared modules
1342
- const fileToShareKey = await buildFileToShareKeyMap(getUsedShares(), this.resolve.bind(this));
1343
- processModuleAssets(bundle, filesMap, modulePath => fileToShareKey.get(modulePath));
1344
- // Add all CSS assets to every export if bundleAllCSS is enabled
1345
- if (mfOptions.bundleAllCSS) {
1346
- addCssAssetsToAllExports(filesMap, allCssAssets);
916
+ seenObjects.add(val);
917
+ }
918
+ // 4. Handle Array, Map, Set
919
+ if (Array.isArray(val)) {
920
+ // Recursively serialize each element
921
+ return `[${val.map(valueToCode).join(', ')}]`;
922
+ }
923
+ if (val instanceof Map) {
924
+ // Serialize Map entries into an array of [key, value] pairs
925
+ const entries = Array.from(val.entries()).map(([k, v]) => `[${valueToCode(k)}, ${valueToCode(v)}]`);
926
+ return `new Map([${entries.join(', ')}])`;
927
+ }
928
+ if (val instanceof Set) {
929
+ // Serialize Set values into an array
930
+ const items = Array.from(val.values()).map(valueToCode);
931
+ return `new Set([${items.join(', ')}])`;
932
+ }
933
+ // 5. Handle plain objects (the default object type)
934
+ if (type === 'object') {
935
+ const properties = [];
936
+ // Iterate over the object's own enumerable properties
937
+ for (const key in val) {
938
+ if (Object.prototype.hasOwnProperty.call(val, key)) {
939
+ // Wrap the key in JSON.stringify to handle non-identifier keys
940
+ properties.push(`${JSON.stringify(key)}: ${valueToCode(val[key])}`);
941
+ }
1347
942
  }
1348
- // Final deduplication of all assets
1349
- filesMap = deduplicateAssets(filesMap);
1350
- this.emitFile({
1351
- type: 'asset',
1352
- fileName: mfManifestName,
1353
- source: JSON.stringify(generateMFManifest(filesMap))
1354
- });
943
+ return `{${properties.join(', ')}}`;
1355
944
  }
1356
- }];
945
+ // 6. Fallback case (e.g., BigInt, other object types)
946
+ // Coerce to string and then JSON.stringify that string for safety
947
+ return JSON.stringify(String(val));
948
+ }
949
+ // Start serialization for the top-level object
950
+ const topLevelProps = [];
951
+ // Iterate over the properties of the root 'options' object
952
+ for (const key in options) {
953
+ if (Object.prototype.hasOwnProperty.call(options, key)) {
954
+ topLevelProps.push(`${JSON.stringify(key)}: ${valueToCode(options[key])}`);
955
+ }
956
+ }
957
+ return `{${topLevelProps.join(', ')}}`;
958
+ }
959
+
960
+ // Cache root path
961
+ let rootDir;
962
+ function findNodeModulesDir(root = process.cwd()) {
963
+ let currentDir = root;
964
+ while (currentDir !== parse(currentDir).root) {
965
+ const nodeModulesPath = join(currentDir, 'node_modules');
966
+ if (existsSync(nodeModulesPath)) {
967
+ return nodeModulesPath;
968
+ }
969
+ currentDir = dirname(currentDir);
970
+ }
971
+ return '';
972
+ }
973
+ // Cache nodeModulesDir result to avoid repeated calculations
974
+ let cachedNodeModulesDir;
975
+ function getNodeModulesDir() {
976
+ if (!cachedNodeModulesDir) {
977
+ cachedNodeModulesDir = findNodeModulesDir(rootDir);
978
+ }
979
+ return cachedNodeModulesDir;
980
+ }
981
+ function getSuffix(name) {
982
+ const base = basename(name);
983
+ const dotIndex = base.lastIndexOf('.');
984
+ if (dotIndex > 0 && dotIndex < base.length - 1) {
985
+ return base.slice(dotIndex);
986
+ }
987
+ return '.js';
988
+ }
989
+ const patternMap = {};
990
+ const cacheMap = {};
991
+ /**
992
+ * Physically generate files as virtual modules under node_modules/__mf__virtual/*
993
+ */
994
+ function assertModuleFound(tag, str = '') {
995
+ const module = VirtualModule.findModule(tag, str);
996
+ if (!module) {
997
+ throw new Error(`Module Federation shared module '${str}' not found. Please ensure it's installed as a dependency in your package.json.`);
998
+ }
999
+ return module;
1000
+ }
1001
+ class VirtualModule {
1357
1002
  /**
1358
- * Generates the final manifest JSON structure
1359
- * @param preloadMap - Map of module assets to include
1360
- * @returns Complete manifest object
1003
+ * Set the root path for finding node_modules
1004
+ * @param root - Root path
1361
1005
  */
1362
- function generateMFManifest(preloadMap) {
1363
- const options = getNormalizeModuleFederationOptions();
1006
+ static setRoot(root) {
1007
+ rootDir = root;
1008
+ // Reset cache to ensure using the new root path
1009
+ cachedNodeModulesDir = undefined;
1010
+ }
1011
+ /**
1012
+ * Ensure virtual package directory exists
1013
+ */
1014
+ static ensureVirtualPackageExists() {
1015
+ const nodeModulesDir = getNodeModulesDir();
1364
1016
  const {
1365
- name
1366
- } = options;
1367
- const remoteEntry = {
1368
- name: remoteEntryFile,
1369
- path: '',
1370
- type: 'module'
1371
- };
1372
- // Process remotes
1373
- const remotes = Array.from(Object.entries(getUsedRemotesMap())).flatMap(([remoteKey, modules]) => Array.from(modules).map(moduleKey => ({
1374
- federationContainerName: options.remotes[remoteKey].entry,
1375
- moduleName: moduleKey.replace(remoteKey, '').replace('/', ''),
1376
- alias: remoteKey,
1377
- entry: '*'
1378
- })));
1379
- // Process shared dependencies
1380
- const shared = Array.from(getUsedShares()).map(shareKey => {
1381
- const shareItem = getNormalizeShareItem(shareKey);
1382
- const assets = preloadMap[shareKey] || createEmptyAssetMap();
1383
- return {
1384
- id: `${name}:${shareKey}`,
1385
- name: shareKey,
1386
- version: shareItem.version,
1387
- requiredVersion: shareItem.shareConfig.requiredVersion,
1388
- assets: {
1389
- js: {
1390
- async: assets.js.async,
1391
- sync: assets.js.sync
1392
- },
1393
- css: {
1394
- async: assets.css.async,
1395
- sync: assets.css.sync
1396
- }
1397
- }
1398
- };
1399
- }).filter(Boolean);
1400
- // Process exposed modules
1401
- const exposes = Object.entries(options.exposes).map(([key, value]) => {
1402
- const formatKey = key.replace('./', '');
1403
- const sourceFile = value.import;
1404
- const assets = preloadMap[sourceFile] || createEmptyAssetMap();
1405
- return {
1406
- id: `${name}:${formatKey}`,
1407
- name: formatKey,
1408
- assets: {
1409
- js: {
1410
- async: assets.js.async,
1411
- sync: assets.js.sync
1412
- },
1413
- css: {
1414
- async: assets.css.async,
1415
- sync: assets.css.sync
1416
- }
1417
- },
1418
- path: key
1419
- };
1420
- }).filter(Boolean);
1421
- return {
1422
- id: name,
1423
- name,
1424
- metaData: _extends({
1425
- name,
1426
- type: 'app',
1427
- buildInfo: {
1428
- buildVersion: '1.0.0',
1429
- buildName: name
1430
- },
1431
- remoteEntry,
1432
- ssrRemoteEntry: remoteEntry,
1433
- types: {
1434
- path: '',
1435
- name: ''
1436
- },
1437
- globalName: name,
1438
- pluginVersion: '0.2.5'
1439
- }, !!getPublicPath ? {
1440
- getPublicPath
1441
- } : {
1442
- publicPath
1443
- }),
1444
- shared,
1445
- remotes,
1446
- exposes
1447
- };
1017
+ virtualModuleDir
1018
+ } = getNormalizeModuleFederationOptions();
1019
+ const virtualPackagePath = resolve(nodeModulesDir, virtualModuleDir);
1020
+ if (!existsSync(virtualPackagePath)) {
1021
+ mkdirSync(virtualPackagePath);
1022
+ writeFileSync(resolve(virtualPackagePath, 'empty.js'), '');
1023
+ writeFileSync(resolve(virtualPackagePath, 'package.json'), JSON.stringify({
1024
+ name: virtualModuleDir,
1025
+ main: 'empty.js'
1026
+ }));
1027
+ }
1028
+ }
1029
+ static findModule(tag, str = '') {
1030
+ if (!patternMap[tag]) patternMap[tag] = new RegExp(`(.*${packageNameEncode(tag)}(.+?)${packageNameEncode(tag)}.*)`);
1031
+ const moduleName = (str.match(patternMap[tag]) || [])[2];
1032
+ if (moduleName) return cacheMap[tag][packageNameDecode(moduleName)];
1033
+ return undefined;
1034
+ }
1035
+ constructor(name, tag = '__mf_v__', suffix = '') {
1036
+ this.name = void 0;
1037
+ this.tag = void 0;
1038
+ this.suffix = void 0;
1039
+ this.inited = false;
1040
+ this.name = name;
1041
+ this.tag = tag;
1042
+ this.suffix = suffix || getSuffix(name);
1043
+ if (!cacheMap[this.tag]) cacheMap[this.tag] = {};
1044
+ cacheMap[this.tag][this.name] = this;
1045
+ }
1046
+ getPath() {
1047
+ return resolve(getNodeModulesDir(), this.getImportId());
1048
+ }
1049
+ getImportId() {
1050
+ const {
1051
+ name: mfName,
1052
+ virtualModuleDir
1053
+ } = getNormalizeModuleFederationOptions();
1054
+ return `${virtualModuleDir}/${packageNameEncode(`${mfName}${this.tag}${this.name}${this.tag}`)}${this.suffix}`;
1055
+ }
1056
+ writeSync(code, force) {
1057
+ if (!force && this.inited) return;
1058
+ if (!this.inited) {
1059
+ this.inited = true;
1060
+ }
1061
+ writeFileSync(this.getPath(), code);
1062
+ }
1063
+ write(code) {
1064
+ writeFile(this.getPath(), code, function () {});
1065
+ }
1066
+ }
1067
+
1068
+ const VIRTUAL_EXPOSES = 'virtual:mf-exposes';
1069
+ function generateExposes() {
1070
+ const options = getNormalizeModuleFederationOptions();
1071
+ return `
1072
+ export default {
1073
+ ${Object.keys(options.exposes).map(key => {
1074
+ return `
1075
+ ${JSON.stringify(key)}: async () => {
1076
+ const importModule = await import(${JSON.stringify(options.exposes[key].import)})
1077
+ const exportModule = {}
1078
+ Object.assign(exportModule, importModule)
1079
+ Object.defineProperty(exportModule, "__esModule", {
1080
+ value: true,
1081
+ enumerable: false
1082
+ })
1083
+ return exportModule
1084
+ }
1085
+ `;
1086
+ }).join(',')}
1087
+ }
1088
+ `;
1089
+ }
1090
+
1091
+ const virtualRuntimeInitStatus = new VirtualModule('runtimeInit');
1092
+ function writeRuntimeInitStatus() {
1093
+ // Use globalThis singleton to ensure only one initPromise exists
1094
+ const globalKey = `__mf_init__${virtualRuntimeInitStatus.getImportId()}__`;
1095
+ virtualRuntimeInitStatus.writeSync(`
1096
+ const globalKey = ${JSON.stringify(globalKey)}
1097
+ if (!globalThis[globalKey]) {
1098
+ let initResolve, initReject
1099
+ const initPromise = new Promise((re, rj) => {
1100
+ initResolve = re
1101
+ initReject = rj
1102
+ })
1103
+ globalThis[globalKey] = {
1104
+ initPromise,
1105
+ initResolve,
1106
+ initReject
1107
+ }
1108
+ }
1109
+ module.exports = globalThis[globalKey]
1110
+ `);
1111
+ }
1112
+
1113
+ const cacheRemoteMap = {};
1114
+ const LOAD_REMOTE_TAG = '__loadRemote__';
1115
+ function getRemoteVirtualModule(remote, command) {
1116
+ if (!cacheRemoteMap[remote]) {
1117
+ cacheRemoteMap[remote] = new VirtualModule(remote, LOAD_REMOTE_TAG, '.js');
1118
+ cacheRemoteMap[remote].writeSync(generateRemotes(remote, command));
1448
1119
  }
1120
+ const virtual = cacheRemoteMap[remote];
1121
+ return virtual;
1122
+ }
1123
+ const usedRemotesMap = {
1124
+ // remote1: {remote1/App, remote1, remote1/Button}
1449
1125
  };
1126
+ function addUsedRemote(remoteKey, remoteModule) {
1127
+ if (!usedRemotesMap[remoteKey]) usedRemotesMap[remoteKey] = new Set();
1128
+ usedRemotesMap[remoteKey].add(remoteModule);
1129
+ }
1130
+ function getUsedRemotesMap() {
1131
+ return usedRemotesMap;
1132
+ }
1133
+ function generateRemotes(id, command) {
1134
+ return `
1135
+ const {initPromise} = require("${virtualRuntimeInitStatus.getImportId()}")
1136
+ const res = initPromise.then(runtime => runtime.loadRemote(${JSON.stringify(id)}))
1137
+ const exportModule = ${command !== 'build' ? '/*mf top-level-await placeholder replacement mf*/' : 'await '}initPromise.then(_ => res)
1138
+ module.exports = exportModule
1139
+ `;
1140
+ }
1450
1141
 
1451
- let _resolve,
1452
- promise = new Promise((resolve, reject) => {
1453
- _resolve = resolve;
1454
- });
1455
- let parsePromise = promise;
1456
- let exposesParseEnd = false;
1457
- const parseStartSet = new Set();
1458
- const parseEndSet = new Set();
1459
- function pluginModuleParseEnd (excludeFn) {
1460
- return [{
1461
- name: '_',
1462
- apply: 'serve',
1463
- config() {
1464
- // No waiting in development mode
1465
- _resolve(1);
1142
+ /**
1143
+ * Even the resolveId hook cannot interfere with vite pre-build,
1144
+ * and adding query parameter virtual modules will also fail.
1145
+ * You can only proxy to the real file through alias
1146
+ */
1147
+ // *** __prebuild__
1148
+ const preBuildCacheMap = {};
1149
+ const PREBUILD_TAG = '__prebuild__';
1150
+ function writePreBuildLibPath(pkg) {
1151
+ if (!preBuildCacheMap[pkg]) preBuildCacheMap[pkg] = new VirtualModule(pkg, PREBUILD_TAG);
1152
+ preBuildCacheMap[pkg].writeSync('');
1153
+ }
1154
+ function getPreBuildLibImportId(pkg) {
1155
+ if (!preBuildCacheMap[pkg]) preBuildCacheMap[pkg] = new VirtualModule(pkg, PREBUILD_TAG);
1156
+ const importId = preBuildCacheMap[pkg].getImportId();
1157
+ return importId;
1158
+ }
1159
+ // *** __loadShare__
1160
+ const LOAD_SHARE_TAG = '__loadShare__';
1161
+ const loadShareCacheMap = {};
1162
+ function getLoadShareModulePath(pkg) {
1163
+ if (!loadShareCacheMap[pkg]) loadShareCacheMap[pkg] = new VirtualModule(pkg, LOAD_SHARE_TAG, '.js');
1164
+ const filepath = loadShareCacheMap[pkg].getPath();
1165
+ return filepath;
1166
+ }
1167
+ function writeLoadShareModule(pkg, shareItem, command) {
1168
+ loadShareCacheMap[pkg].writeSync(`
1169
+ ;() => import(${JSON.stringify(getPreBuildLibImportId(pkg))}).catch(() => {});
1170
+ // dev uses dynamic import to separate chunks
1171
+ ${command !== 'build' ? `;() => import(${JSON.stringify(pkg)}).catch(() => {});` : ''}
1172
+ const {initPromise} = require("${virtualRuntimeInitStatus.getImportId()}")
1173
+ const res = initPromise.then(runtime => runtime.loadShare(${JSON.stringify(pkg)}, {
1174
+ customShareInfo: {shareConfig:{
1175
+ singleton: ${shareItem.shareConfig.singleton},
1176
+ strictVersion: ${shareItem.shareConfig.strictVersion},
1177
+ requiredVersion: ${JSON.stringify(shareItem.shareConfig.requiredVersion)}
1178
+ }}
1179
+ }))
1180
+ const exportModule = ${command !== 'build' ? '/*mf top-level-await placeholder replacement mf*/' : 'await '}res.then(factory => factory())
1181
+ module.exports = exportModule
1182
+ `);
1183
+ }
1184
+
1185
+ let usedShares = new Set();
1186
+ function getUsedShares() {
1187
+ return usedShares;
1188
+ }
1189
+ function addUsedShares(pkg) {
1190
+ usedShares.add(pkg);
1191
+ }
1192
+ // *** Expose locally provided shared modules here
1193
+ new VirtualModule('localSharedImportMap');
1194
+ function getLocalSharedImportMapPath() {
1195
+ return getLocalSharedImportMapPath_temp();
1196
+ // return localSharedImportMapModule.getPath()
1197
+ }
1198
+ let prevSharedCount;
1199
+ function writeLocalSharedImportMap() {
1200
+ const sharedCount = getUsedShares().size;
1201
+ if (prevSharedCount !== sharedCount) {
1202
+ prevSharedCount = sharedCount;
1203
+ writeLocalSharedImportMap_temp(generateLocalSharedImportMap());
1204
+ // localSharedImportMapModule.writeSync(generateLocalSharedImportMap(), true)
1205
+ }
1206
+ }
1207
+ function generateLocalSharedImportMap() {
1208
+ const options = getNormalizeModuleFederationOptions();
1209
+ return `
1210
+ import {loadShare} from "@module-federation/runtime";
1211
+ const importMap = {
1212
+ ${Array.from(getUsedShares()).sort().map(pkg => {
1213
+ const shareItem = getNormalizeShareItem(pkg);
1214
+ return `
1215
+ ${JSON.stringify(pkg)}: async () => {
1216
+ ${(shareItem == null ? void 0 : shareItem.shareConfig.import) === false ? `throw new Error(\`Shared module '\${${JSON.stringify(pkg)}}' must be provided by host\`);` : `let pkg = await import("${getPreBuildLibImportId(pkg)}");
1217
+ return pkg;`}
1218
+ }
1219
+ `;
1220
+ }).join(',')}
1466
1221
  }
1467
- }, {
1468
- enforce: 'pre',
1469
- name: 'parseStart',
1470
- apply: 'build',
1471
- load(id) {
1472
- if (excludeFn(id)) {
1473
- return;
1474
- }
1475
- parseStartSet.add(id);
1222
+ const usedShared = {
1223
+ ${Array.from(getUsedShares()).sort().map(key => {
1224
+ const shareItem = getNormalizeShareItem(key);
1225
+ if (!shareItem) return null;
1226
+ return `
1227
+ ${JSON.stringify(key)}: {
1228
+ name: ${JSON.stringify(key)},
1229
+ version: ${JSON.stringify(shareItem.version)},
1230
+ scope: [${JSON.stringify(shareItem.scope)}],
1231
+ loaded: false,
1232
+ from: ${JSON.stringify(options.name)},
1233
+ async get () {
1234
+ if (${shareItem.shareConfig.import === false}) {
1235
+ throw new Error(\`Shared module '\${${JSON.stringify(key)}}' must be provided by host\`);
1236
+ }
1237
+ usedShared[${JSON.stringify(key)}].loaded = true
1238
+ const {${JSON.stringify(key)}: pkgDynamicImport} = importMap
1239
+ const res = await pkgDynamicImport()
1240
+ const exportModule = {...res}
1241
+ // All npm packages pre-built by vite will be converted to esm
1242
+ Object.defineProperty(exportModule, "__esModule", {
1243
+ value: true,
1244
+ enumerable: false
1245
+ })
1246
+ return function () {
1247
+ return exportModule
1248
+ }
1249
+ },
1250
+ shareConfig: {
1251
+ singleton: ${shareItem.shareConfig.singleton},
1252
+ requiredVersion: ${JSON.stringify(shareItem.shareConfig.requiredVersion)},
1253
+ ${shareItem.shareConfig.import === false ? 'import: false,' : ''}
1254
+ }
1255
+ }
1256
+ `;
1257
+ }).filter(x => x !== null).join(',')}
1476
1258
  }
1477
- }, {
1478
- enforce: 'post',
1479
- name: 'parseEnd',
1480
- apply: 'build',
1481
- moduleParsed(module) {
1482
- const id = module.id;
1483
- if (id === VIRTUAL_EXPOSES) {
1484
- // When the entry JS file is empty and only contains exposes export code, it’s necessary to wait for the exposes modules to be resolved in order to collect the dependencies being used.
1485
- exposesParseEnd = true;
1486
- }
1487
- if (excludeFn(id)) {
1488
- return;
1489
- }
1490
- parseEndSet.add(id);
1491
- if (exposesParseEnd && parseStartSet.size === parseEndSet.size) {
1492
- _resolve(1);
1259
+ const usedRemotes = [${Object.keys(getUsedRemotesMap()).map(key => {
1260
+ var _JSON$stringify;
1261
+ const remote = options.remotes[key];
1262
+ if (!remote) return null;
1263
+ return `
1264
+ {
1265
+ entryGlobalName: ${JSON.stringify(remote.entryGlobalName)},
1266
+ name: ${JSON.stringify(remote.name)},
1267
+ type: ${JSON.stringify(remote.type)},
1268
+ entry: ${JSON.stringify(remote.entry)},
1269
+ shareScope: ${(_JSON$stringify = JSON.stringify(remote.shareScope)) != null ? _JSON$stringify : 'default'},
1270
+ }
1271
+ `;
1272
+ }).filter(x => x !== null).join(',')}
1273
+ ]
1274
+ export {
1275
+ usedShared,
1276
+ usedRemotes
1493
1277
  }
1278
+ `;
1279
+ }
1280
+ const REMOTE_ENTRY_ID = 'virtual:mf-REMOTE_ENTRY_ID';
1281
+ function generateRemoteEntry(options) {
1282
+ const pluginImportNames = options.runtimePlugins.map((p, i) => {
1283
+ if (typeof p === 'string') {
1284
+ return [`$runtimePlugin_${i}`, `import $runtimePlugin_${i} from "${p}";`, `undefined`];
1285
+ } else {
1286
+ return [`$runtimePlugin_${i}`, `import $runtimePlugin_${i} from "${p[0]}";`, serializeRuntimeOptions(p[1])];
1494
1287
  }
1495
- }];
1288
+ });
1289
+ return `
1290
+ import {init as runtimeInit, loadRemote} from "@module-federation/runtime";
1291
+ ${pluginImportNames.map(item => item[1]).join('\n')}
1292
+ import exposesMap from "${VIRTUAL_EXPOSES}"
1293
+ import {usedShared, usedRemotes} from "${getLocalSharedImportMapPath()}"
1294
+ import {
1295
+ initResolve
1296
+ } from "${virtualRuntimeInitStatus.getImportId()}"
1297
+ const initTokens = {}
1298
+ const shareScopeName = ${JSON.stringify(options.shareScope)}
1299
+ const mfName = ${JSON.stringify(options.name)}
1300
+ async function init(shared = {}, initScope = []) {
1301
+ const initRes = runtimeInit({
1302
+ name: mfName,
1303
+ remotes: usedRemotes,
1304
+ shared: usedShared,
1305
+ plugins: [${pluginImportNames.map(item => `${item[0]}(${item[2]})`).join(', ')}],
1306
+ ${options.shareStrategy ? `shareStrategy: '${options.shareStrategy}'` : ''}
1307
+ });
1308
+ // handling circular init calls
1309
+ var initToken = initTokens[shareScopeName];
1310
+ if (!initToken)
1311
+ initToken = initTokens[shareScopeName] = { from: mfName };
1312
+ if (initScope.indexOf(initToken) >= 0) return;
1313
+ initScope.push(initToken);
1314
+ initRes.initShareScopeMap('${options.shareScope}', shared);
1315
+ try {
1316
+ await Promise.all(await initRes.initializeSharing('${options.shareScope}', {
1317
+ strategy: '${options.shareStrategy}',
1318
+ from: "build",
1319
+ initScope
1320
+ }));
1321
+ } catch (e) {
1322
+ console.error(e)
1323
+ }
1324
+ initResolve(initRes)
1325
+ return initRes
1326
+ }
1327
+
1328
+ function getExposes(moduleName) {
1329
+ if (!(moduleName in exposesMap)) throw new Error(\`Module \${moduleName} does not exist in container.\`)
1330
+ return (exposesMap[moduleName])().then(res => () => res)
1331
+ }
1332
+ export {
1333
+ init,
1334
+ getExposes as get
1335
+ }
1336
+ `;
1337
+ }
1338
+ /**
1339
+ * Inject entry file, automatically init when used as host,
1340
+ * and will not inject remoteEntry
1341
+ */
1342
+ const HOST_AUTO_INIT_TAG = '__H_A_I__';
1343
+ const hostAutoInitModule = new VirtualModule('hostAutoInit', HOST_AUTO_INIT_TAG);
1344
+ function writeHostAutoInit() {
1345
+ hostAutoInitModule.writeSync(`
1346
+ const remoteEntryPromise = import("${REMOTE_ENTRY_ID}")
1347
+ // __tla only serves as a hack for vite-plugin-top-level-await.
1348
+ Promise.resolve(remoteEntryPromise)
1349
+ .then(remoteEntry => {
1350
+ return Promise.resolve(remoteEntry.__tla)
1351
+ .then(remoteEntry.init).catch(remoteEntry.init)
1352
+ })
1353
+ `);
1354
+ }
1355
+ function getHostAutoInitImportId() {
1356
+ return hostAutoInitModule.getImportId();
1357
+ }
1358
+ function getHostAutoInitPath() {
1359
+ return hostAutoInitModule.getPath();
1496
1360
  }
1497
1361
 
1498
- const filter = createFilter();
1499
- function pluginProxyRemoteEntry () {
1500
- let viteConfig, _command;
1501
- return {
1502
- name: 'proxyRemoteEntry',
1503
- enforce: 'post',
1504
- configResolved(config) {
1505
- viteConfig = config;
1506
- },
1507
- config(config, {
1508
- command
1509
- }) {
1510
- _command = command;
1511
- },
1512
- resolveId(id) {
1513
- if (id === REMOTE_ENTRY_ID) {
1514
- return REMOTE_ENTRY_ID;
1515
- }
1516
- if (id === VIRTUAL_EXPOSES) {
1517
- return VIRTUAL_EXPOSES;
1518
- }
1519
- if (_command === 'serve' && id.includes(getHostAutoInitPath())) {
1520
- return id;
1521
- }
1522
- },
1523
- load(id) {
1524
- if (id === REMOTE_ENTRY_ID) {
1525
- return parsePromise.then(_ => generateRemoteEntry(getNormalizeModuleFederationOptions()));
1526
- }
1527
- if (id === VIRTUAL_EXPOSES) {
1528
- return generateExposes();
1529
- }
1530
- if (_command === 'serve' && id.includes(getHostAutoInitPath())) {
1531
- return id;
1532
- }
1533
- },
1534
- transform(code, id) {
1535
- const transformedCode = (() => {
1536
- if (!filter(id)) return;
1537
- if (id.includes(REMOTE_ENTRY_ID)) {
1538
- return parsePromise.then(_ => generateRemoteEntry(getNormalizeModuleFederationOptions()));
1539
- }
1540
- if (id === VIRTUAL_EXPOSES) {
1541
- return generateExposes();
1542
- }
1543
- if (id.includes(getHostAutoInitPath())) {
1544
- const options = getNormalizeModuleFederationOptions();
1545
- if (_command === 'serve') {
1546
- var _viteConfig$server, _viteConfig$server2;
1547
- const host = typeof ((_viteConfig$server = viteConfig.server) == null ? void 0 : _viteConfig$server.host) === 'string' && viteConfig.server.host !== '0.0.0.0' ? viteConfig.server.host : 'localhost';
1548
- const publicPath = JSON.stringify(resolvePublicPath(options, viteConfig.base) + options.filename);
1549
- return `
1550
- const origin = (window && ${!options.ignoreOrigin}) ? window.origin : "//${host}:${(_viteConfig$server2 = viteConfig.server) == null ? void 0 : _viteConfig$server2.port}"
1551
- const remoteEntryPromise = await import(origin + ${publicPath})
1552
- // __tla only serves as a hack for vite-plugin-top-level-await.
1553
- Promise.resolve(remoteEntryPromise)
1554
- .then(remoteEntry => {
1555
- return Promise.resolve(remoteEntry.__tla)
1556
- .then(remoteEntry.init).catch(remoteEntry.init)
1557
- })
1558
- `;
1559
- }
1560
- return code;
1561
- }
1562
- })();
1563
- return mapCodeToCodeWithSourcemap(transformedCode);
1564
- }
1565
- };
1362
+ function initVirtualModules() {
1363
+ writeLocalSharedImportMap();
1364
+ writeHostAutoInit();
1365
+ writeRuntimeInitStatus();
1566
1366
  }
1567
1367
 
1568
- createFilter();
1569
- function pluginProxyRemotes (options) {
1570
- const {
1571
- remotes
1572
- } = options;
1573
- return {
1574
- name: 'proxyRemotes',
1575
- config(config, {
1576
- command: _command
1577
- }) {
1578
- Object.keys(remotes).forEach(key => {
1579
- const remote = remotes[key];
1580
- config.resolve.alias.push({
1581
- find: new RegExp(`^(${remote.name}(\/.*|$))`),
1582
- replacement: '$1',
1583
- customResolver(source) {
1584
- const remoteModule = getRemoteVirtualModule(source, _command);
1585
- addUsedRemote(remote.name, source);
1586
- return remoteModule.getPath();
1587
- }
1588
- });
1589
- });
1368
+ function findRemoteEntryFile(filename, bundle) {
1369
+ for (const [_, fileData] of Object.entries(bundle)) {
1370
+ if (filename.replace(/[\[\]]/g, '_').replace(/\.[^/.]+$/, '') === fileData.name || fileData.name === 'remoteEntry') {
1371
+ return fileData.fileName; // We can return early since we only need to find remoteEntry once
1590
1372
  }
1591
- };
1373
+ }
1592
1374
  }
1593
1375
 
1594
- const DEFAULT_DEV_OPTIONS = {
1595
- disableLiveReload: true,
1596
- disableHotTypesReload: false,
1597
- disableDynamicRemoteTypeHints: false
1598
- };
1599
- const DYNAMIC_HINTS_PLUGIN = '@module-federation/dts-plugin/dynamic-remote-type-hints-plugin';
1600
- const getIPv4 = () => process.env['FEDERATION_IPV4'] || '127.0.0.1';
1601
- const forkDevWorkerPath = (() => {
1602
- // eslint-disable-next-line @typescript-eslint/no-var-requires
1603
- return require.resolve('@module-federation/dts-plugin/dist/fork-dev-worker.js');
1604
- })();
1605
- class DevWorker {
1606
- constructor(options) {
1607
- this.worker = rpc.createRpcWorker(forkDevWorkerPath, {}, undefined, false);
1608
- this.worker.connect(options);
1609
- }
1610
- update() {
1611
- var _this$worker$process;
1612
- (_this$worker$process = this.worker.process) == null || _this$worker$process.send == null || _this$worker$process.send({
1613
- type: rpc.RpcGMCallTypes.CALL,
1614
- id: this.worker.id,
1615
- args: [undefined, 'update']
1616
- });
1376
+ const ASSET_TYPES = ['js', 'css'];
1377
+ const LOAD_TIMINGS = ['sync', 'async'];
1378
+ const JS_EXTENSIONS = ['.ts', '.tsx', '.jsx', '.mjs', '.cjs'];
1379
+ /**
1380
+ * Creates an empty asset map structure for tracking JS and CSS assets
1381
+ * @returns Initialized asset map with sync/async arrays for JS and CSS
1382
+ */
1383
+ const createEmptyAssetMap = () => ({
1384
+ js: {
1385
+ sync: [],
1386
+ async: []
1387
+ },
1388
+ css: {
1389
+ sync: [],
1390
+ async: []
1617
1391
  }
1618
- exit() {
1619
- this.worker.terminate();
1392
+ });
1393
+ /**
1394
+ * Tracks an asset in the preload map with deduplication
1395
+ * @param map - The preload map to update
1396
+ * @param key - The module key to track under
1397
+ * @param fileName - The asset filename to track
1398
+ * @param isAsync - Whether the asset is loaded async
1399
+ * @param type - The asset type ('js' or 'css')
1400
+ */
1401
+ const trackAsset = (map, key, fileName, isAsync, type) => {
1402
+ if (!map[key]) {
1403
+ map[key] = createEmptyAssetMap();
1620
1404
  }
1621
- }
1622
- const normalizeDevOptions = dev => {
1623
- if (dev === false) {
1624
- return false;
1405
+ const target = isAsync ? map[key][type].async : map[key][type].sync;
1406
+ if (!target.includes(fileName)) {
1407
+ target.push(fileName);
1625
1408
  }
1626
- if (dev === true || typeof dev === 'undefined') {
1627
- return _extends({}, DEFAULT_DEV_OPTIONS);
1409
+ };
1410
+ /**
1411
+ * Checks if a file is a CSS file by extension
1412
+ * @param fileName - The filename to check
1413
+ * @returns True if file has a CSS extension (.css, .scss, .less)
1414
+ */
1415
+ const isCSSFile = fileName => {
1416
+ return fileName.endsWith('.css') || fileName.endsWith('.scss') || fileName.endsWith('.less');
1417
+ };
1418
+ /**
1419
+ * Collects all CSS assets from the bundle
1420
+ * @param bundle - The Rollup output bundle
1421
+ * @returns Set of CSS asset filenames
1422
+ */
1423
+ const collectCssAssets = bundle => {
1424
+ const cssAssets = new Set();
1425
+ for (const [fileName, fileData] of Object.entries(bundle)) {
1426
+ if (fileData.type === 'asset' && isCSSFile(fileName)) {
1427
+ cssAssets.add(fileName);
1428
+ }
1628
1429
  }
1629
- return _extends({}, DEFAULT_DEV_OPTIONS, dev);
1430
+ return cssAssets;
1630
1431
  };
1631
- const buildDtsModuleFederationConfig = options => {
1632
- const exposes = {};
1633
- Object.entries(options.exposes).forEach(([key, value]) => {
1634
- if (typeof value === 'string') {
1635
- exposes[key] = value;
1636
- return;
1637
- }
1638
- const importValue = Array.isArray(value.import) ? value.import[0] : value.import;
1639
- if (importValue) {
1640
- exposes[key] = importValue;
1641
- }
1642
- });
1643
- const remotes = {};
1644
- Object.entries(options.remotes).forEach(([key, remote]) => {
1645
- var _remote$entryGlobalNa, _remote$entryGlobalNa2;
1646
- if (typeof remote === 'string') {
1647
- remotes[key] = remote;
1648
- return;
1649
- }
1650
- if (!remote.entry) {
1651
- return;
1432
+ /**
1433
+ * Processes module assets and tracks them in the files map
1434
+ * @param bundle - The Rollup output bundle
1435
+ * @param filesMap - The preload map to populate
1436
+ * @param moduleMatcher - Function that matches module paths to keys
1437
+ */
1438
+ const processModuleAssets = (bundle, filesMap, moduleMatcher) => {
1439
+ for (const [fileName, fileData] of Object.entries(bundle)) {
1440
+ if (fileData.type !== 'chunk') continue;
1441
+ if (!fileData.modules) continue;
1442
+ for (const modulePath of Object.keys(fileData.modules)) {
1443
+ const matchKey = moduleMatcher(modulePath);
1444
+ if (!matchKey) continue;
1445
+ // Track main JS chunk
1446
+ trackAsset(filesMap, matchKey, fileName, false, 'js');
1447
+ // Handle dynamic imports
1448
+ if (fileData.dynamicImports) {
1449
+ for (const dynamicImport of fileData.dynamicImports) {
1450
+ const importData = bundle[dynamicImport];
1451
+ if (!importData) continue;
1452
+ const isCss = isCSSFile(dynamicImport);
1453
+ trackAsset(filesMap, matchKey, dynamicImport, true, isCss ? 'css' : 'js');
1454
+ }
1455
+ }
1652
1456
  }
1653
- const entryLooksLikeUrl = ((_remote$entryGlobalNa = remote.entryGlobalName) == null ? void 0 : _remote$entryGlobalNa.startsWith('http')) || ((_remote$entryGlobalNa2 = remote.entryGlobalName) == null ? void 0 : _remote$entryGlobalNa2.includes('.json'));
1654
- const entryGlobalName = entryLooksLikeUrl ? remote.name || key : remote.entryGlobalName || remote.name || key;
1655
- remotes[key] = `${entryGlobalName}@${remote.entry}`;
1656
- });
1657
- return _extends({}, options, {
1658
- exposes,
1659
- remotes
1660
- });
1661
- };
1662
- const resolveOutputDir = config => {
1663
- const {
1664
- outDir
1665
- } = config.build;
1666
- if (path.isAbsolute(outDir)) {
1667
- return path.relative(config.root, outDir);
1668
1457
  }
1669
- return outDir;
1670
1458
  };
1671
- const ensureRuntimePlugin = (options, pluginId) => {
1672
- const hasPlugin = options.runtimePlugins.some(plugin => {
1673
- if (typeof plugin === 'string') {
1674
- return plugin === pluginId;
1459
+ /**
1460
+ * Deduplicates assets in the files map
1461
+ * @param filesMap - The preload map to deduplicate
1462
+ * @returns New deduplicated preload map
1463
+ */
1464
+ const deduplicateAssets = filesMap => {
1465
+ const result = {};
1466
+ for (const [key, assetMaps] of Object.entries(filesMap)) {
1467
+ result[key] = createEmptyAssetMap();
1468
+ for (const type of ASSET_TYPES) {
1469
+ for (const timing of LOAD_TIMINGS) {
1470
+ result[key][type][timing] = Array.from(new Set(assetMaps[type][timing]));
1471
+ }
1675
1472
  }
1676
- return plugin[0] === pluginId;
1677
- });
1678
- if (!hasPlugin) {
1679
- options.runtimePlugins.push(pluginId);
1680
1473
  }
1474
+ return result;
1681
1475
  };
1682
- const normalizeDevDtsOptions = (dts, context) => {
1683
- const defaultGenerateTypes = {
1684
- compileInChildProcess: true
1685
- };
1686
- const defaultConsumeTypes = {
1687
- consumeAPITypes: true
1688
- };
1689
- return normalizeOptions(isTSProject(dts, context), {
1690
- generateTypes: defaultGenerateTypes,
1691
- consumeTypes: defaultConsumeTypes,
1692
- extraOptions: {},
1693
- displayErrorInTerminal: typeof dts === 'object' && dts ? dts.displayErrorInTerminal : undefined
1694
- }, 'mfOptions.dts')(dts);
1476
+ /**
1477
+ * Builds a mapping between module files and their share keys
1478
+ * @param shareKeys - Set of share keys to map
1479
+ * @param resolveFn - Function to resolve module paths
1480
+ * @returns Map of file paths to their corresponding share keys
1481
+ */
1482
+ const buildFileToShareKeyMap = async (shareKeys, resolveFn) => {
1483
+ const fileToShareKey = new Map();
1484
+ const resolutions = await Promise.all(Array.from(shareKeys).map(shareKey => resolveFn(getPreBuildLibImportId(shareKey)).then(resolution => {
1485
+ var _resolution$id;
1486
+ return {
1487
+ shareKey,
1488
+ file: resolution == null || (_resolution$id = resolution.id) == null ? void 0 : _resolution$id.split('?')[0]
1489
+ };
1490
+ }).catch(() => null)));
1491
+ for (const resolution of resolutions) {
1492
+ if (resolution != null && resolution.file) {
1493
+ fileToShareKey.set(resolution.file, resolution.shareKey);
1494
+ }
1495
+ }
1496
+ return fileToShareKey;
1695
1497
  };
1696
- const logDtsError = (error, dtsOptions) => {
1697
- if (dtsOptions === false) {
1698
- return;
1498
+
1499
+ /**
1500
+ * Resolves the public path for remote entries
1501
+ * @param options - Module Federation options
1502
+ * @param viteBase - Vite's base config value
1503
+ * @param originalBase - Original base config before any transformations
1504
+ * @returns The resolved public path
1505
+ */
1506
+ function resolvePublicPath(options, viteBase, originalBase) {
1507
+ // Use explicitly set publicPath if provided
1508
+ if (options.publicPath) {
1509
+ return options.publicPath;
1699
1510
  }
1700
- if (typeof dtsOptions === 'object' && dtsOptions && dtsOptions.displayErrorInTerminal === false) {
1701
- return;
1511
+ // Handle empty original base case
1512
+ if (originalBase === '') {
1513
+ return 'auto';
1702
1514
  }
1703
- console.error(error);
1704
- };
1705
- function pluginDts(options) {
1706
- if (options.dts === false) {
1707
- return [];
1515
+ // Use viteBase if available, ensuring it ends with a slash
1516
+ if (viteBase) {
1517
+ return viteBase.replace(/\/?$/, '/');
1708
1518
  }
1709
- const dtsModuleFederationConfig = buildDtsModuleFederationConfig(options);
1710
- let resolvedConfig;
1711
- let devWorker;
1712
- let normalizedDevOptions;
1713
- let hasGeneratedBundle = false;
1714
- const devPlugin = {
1715
- name: 'module-federation-dts-dev',
1519
+ // Fallback to auto if no base is specified
1520
+ return 'auto';
1521
+ }
1522
+
1523
+ const Manifest = () => {
1524
+ const mfOptions = getNormalizeModuleFederationOptions();
1525
+ const {
1526
+ name,
1527
+ filename,
1528
+ getPublicPath,
1529
+ manifest: manifestOptions,
1530
+ varFilename
1531
+ } = mfOptions;
1532
+ let mfManifestName = '';
1533
+ if (manifestOptions === true) {
1534
+ mfManifestName = 'mf-manifest.json';
1535
+ }
1536
+ if (typeof manifestOptions !== 'boolean') {
1537
+ mfManifestName = path.join((manifestOptions == null ? void 0 : manifestOptions.filePath) || '', (manifestOptions == null ? void 0 : manifestOptions.fileName) || '');
1538
+ }
1539
+ let root;
1540
+ let remoteEntryFile;
1541
+ let publicPath;
1542
+ let _command;
1543
+ let _originalConfigBase;
1544
+ let viteConfig;
1545
+ /**
1546
+ * Adds global CSS assets to all module exports
1547
+ * @param filesMap - The preload map to update
1548
+ * @param cssAssets - Set of CSS asset filenames to add
1549
+ */
1550
+ const addCssAssetsToAllExports = (filesMap, cssAssets) => {
1551
+ Object.keys(filesMap).forEach(key => {
1552
+ cssAssets.forEach(cssAsset => {
1553
+ trackAsset(filesMap, key, cssAsset, false, 'css');
1554
+ });
1555
+ });
1556
+ };
1557
+ return [{
1558
+ name: 'module-federation-manifest',
1716
1559
  apply: 'serve',
1717
- config(config) {
1718
- normalizedDevOptions = normalizeDevOptions(options.dev);
1719
- if (!normalizedDevOptions) {
1720
- return;
1721
- }
1722
- if (normalizedDevOptions.disableDynamicRemoteTypeHints) {
1723
- return;
1724
- }
1725
- ensureRuntimePlugin(options, DYNAMIC_HINTS_PLUGIN);
1726
- const define = config.define ? _extends({}, config.define) : {};
1727
- if (!('FEDERATION_IPV4' in define)) {
1728
- define.FEDERATION_IPV4 = JSON.stringify(getIPv4());
1560
+ /**
1561
+ * Stores resolved Vite config for later use
1562
+ */
1563
+ /**
1564
+ * Finalizes configuration after all plugins are resolved
1565
+ * @param config - Fully resolved Vite config
1566
+ */
1567
+ configResolved(config) {
1568
+ viteConfig = config;
1569
+ },
1570
+ /**
1571
+ * Configures dev server middleware to handle manifest requests
1572
+ * @param server - Vite dev server instance
1573
+ */
1574
+ configureServer(server) {
1575
+ server.middlewares.use((req, res, next) => {
1576
+ var _req$url;
1577
+ if (!mfManifestName) {
1578
+ next();
1579
+ return;
1580
+ }
1581
+ if (((_req$url = req.url) == null ? void 0 : _req$url.replace(/\?.*/, '')) === (viteConfig.base + mfManifestName).replace(/^\/?/, '/')) {
1582
+ res.setHeader('Content-Type', 'application/json');
1583
+ res.setHeader('Access-Control-Allow-Origin', '*');
1584
+ res.end(JSON.stringify(_extends({}, generateMFManifest({}), {
1585
+ id: name,
1586
+ name: name,
1587
+ metaData: {
1588
+ name: name,
1589
+ type: 'app',
1590
+ buildInfo: {
1591
+ buildVersion: '1.0.0',
1592
+ buildName: name
1593
+ },
1594
+ remoteEntry: {
1595
+ name: filename,
1596
+ path: '',
1597
+ type: 'module'
1598
+ },
1599
+ ssrRemoteEntry: {
1600
+ name: filename,
1601
+ path: '',
1602
+ type: 'module'
1603
+ },
1604
+ varRemoteEntry: varFilename ? {
1605
+ name: varFilename,
1606
+ path: '',
1607
+ type: 'var'
1608
+ } : undefined,
1609
+ types: {
1610
+ path: '',
1611
+ name: ''
1612
+ },
1613
+ globalName: name,
1614
+ pluginVersion: '0.2.5',
1615
+ publicPath
1616
+ }
1617
+ })));
1618
+ } else {
1619
+ next();
1620
+ }
1621
+ });
1622
+ }
1623
+ }, {
1624
+ name: 'module-federation-manifest',
1625
+ enforce: 'post',
1626
+ /**
1627
+ * Initial plugin configuration
1628
+ * @param config - Vite config object
1629
+ * @param command - Current Vite command (serve/build)
1630
+ */
1631
+ config(config, {
1632
+ command
1633
+ }) {
1634
+ if (!config.build) config.build = {};
1635
+ if (!config.build.manifest) {
1636
+ config.build.manifest = config.build.manifest || !!manifestOptions;
1729
1637
  }
1730
- config.define = define;
1638
+ _command = command;
1639
+ _originalConfigBase = config.base;
1731
1640
  },
1732
1641
  configResolved(config) {
1733
- resolvedConfig = config;
1734
- },
1735
- configureServer(server) {
1736
- if (!normalizedDevOptions || !resolvedConfig) {
1737
- return;
1738
- }
1739
- const devOptions = normalizedDevOptions;
1740
- if (devOptions.disableDynamicRemoteTypeHints && devOptions.disableHotTypesReload && devOptions.disableLiveReload) {
1741
- return;
1742
- }
1743
- if (!options.name) {
1744
- throw new Error('name is required if you want to enable dev server!');
1642
+ root = config.root;
1643
+ let base = config.base;
1644
+ if (_command === 'serve') {
1645
+ base = (config.server.origin || '') + config.base;
1745
1646
  }
1746
- const outputDir = resolveOutputDir(resolvedConfig);
1747
- const normalizedDtsOptions = normalizeDevDtsOptions(options.dts, resolvedConfig.root);
1748
- if (typeof normalizedDtsOptions !== 'object') {
1749
- return;
1647
+ publicPath = resolvePublicPath(mfOptions, base, _originalConfigBase);
1648
+ },
1649
+ /**
1650
+ * Generates the module federation manifest file
1651
+ * @param options - Rollup output options
1652
+ * @param bundle - Generated bundle assets
1653
+ */
1654
+ async generateBundle(options, bundle) {
1655
+ if (!mfManifestName) return;
1656
+ let filesMap = {};
1657
+ const foundRemoteEntryFile = findRemoteEntryFile(mfOptions.filename, bundle);
1658
+ // First pass: Find remoteEntry file
1659
+ if (foundRemoteEntryFile) {
1660
+ remoteEntryFile = foundRemoteEntryFile;
1750
1661
  }
1751
- const normalizedGenerateTypes = normalizeOptions(Boolean(normalizedDtsOptions), {
1752
- compileInChildProcess: true
1753
- }, 'mfOptions.dts.generateTypes')(normalizedDtsOptions.generateTypes);
1754
- const remote = normalizedGenerateTypes === false ? undefined : _extends({
1755
- implementation: normalizedDtsOptions.implementation,
1756
- context: resolvedConfig.root,
1757
- outputDir,
1758
- moduleFederationConfig: _extends({}, dtsModuleFederationConfig),
1759
- hostRemoteTypesFolder: normalizedGenerateTypes.typesFolder || '@mf-types'
1760
- }, normalizedGenerateTypes, {
1761
- typesFolder: '.dev-server'
1662
+ // Second pass: Collect all CSS assets
1663
+ const allCssAssets = mfOptions.bundleAllCSS ? collectCssAssets(bundle) : new Set();
1664
+ const exposesModules = Object.keys(mfOptions.exposes).map(item => mfOptions.exposes[item].import);
1665
+ // Process exposed modules
1666
+ processModuleAssets(bundle, filesMap, modulePath => {
1667
+ const absoluteModulePath = path.resolve(root, modulePath);
1668
+ return exposesModules.find(exposeModule => {
1669
+ const exposePath = path.resolve(root, exposeModule);
1670
+ // First try exact path match
1671
+ if (absoluteModulePath === exposePath) {
1672
+ return true;
1673
+ }
1674
+ // Then try path match without known extensions
1675
+ const getPathWithoutKnownExt = filePath => {
1676
+ const ext = path.extname(filePath);
1677
+ return JS_EXTENSIONS.includes(ext) ? path.join(path.dirname(filePath), path.basename(filePath, ext)) : filePath;
1678
+ };
1679
+ const modulePathNoExt = getPathWithoutKnownExt(absoluteModulePath);
1680
+ const exposePathNoExt = getPathWithoutKnownExt(exposePath);
1681
+ return modulePathNoExt === exposePathNoExt;
1682
+ });
1762
1683
  });
1763
- if (remote && !remote.tsConfigPath && typeof normalizedDtsOptions === 'object' && normalizedDtsOptions.tsConfigPath) {
1764
- remote.tsConfigPath = normalizedDtsOptions.tsConfigPath;
1765
- }
1766
- const normalizedConsumeTypes = normalizeOptions(Boolean(normalizedDtsOptions), {
1767
- consumeAPITypes: true
1768
- }, 'mfOptions.dts.consumeTypes')(normalizedDtsOptions.consumeTypes);
1769
- const host = normalizedConsumeTypes === false ? undefined : _extends({
1770
- implementation: normalizedDtsOptions.implementation,
1771
- context: resolvedConfig.root,
1772
- moduleFederationConfig: dtsModuleFederationConfig,
1773
- typesFolder: normalizedConsumeTypes.typesFolder || '@mf-types',
1774
- abortOnError: false
1775
- }, normalizedConsumeTypes);
1776
- const extraOptions = normalizedDtsOptions.extraOptions || {};
1777
- if (!remote && !host && devOptions.disableLiveReload) {
1778
- return;
1684
+ // Process shared modules
1685
+ const fileToShareKey = await buildFileToShareKeyMap(getUsedShares(), this.resolve.bind(this));
1686
+ processModuleAssets(bundle, filesMap, modulePath => fileToShareKey.get(modulePath));
1687
+ // Add all CSS assets to every export if bundleAllCSS is enabled
1688
+ if (mfOptions.bundleAllCSS) {
1689
+ addCssAssetsToAllExports(filesMap, allCssAssets);
1779
1690
  }
1780
- const startDevWorker = async () => {
1781
- var _server$httpServer;
1782
- let remoteTypeUrls;
1783
- if (host) {
1784
- remoteTypeUrls = await new Promise(resolve => {
1785
- consumeTypesAPI({
1786
- host,
1787
- extraOptions,
1788
- displayErrorInTerminal: normalizedDtsOptions.displayErrorInTerminal
1789
- }, resolve);
1790
- });
1691
+ // Final deduplication of all assets
1692
+ filesMap = deduplicateAssets(filesMap);
1693
+ this.emitFile({
1694
+ type: 'asset',
1695
+ fileName: mfManifestName,
1696
+ source: JSON.stringify(generateMFManifest(filesMap))
1697
+ });
1698
+ }
1699
+ }];
1700
+ /**
1701
+ * Generates the final manifest JSON structure
1702
+ * @param preloadMap - Map of module assets to include
1703
+ * @returns Complete manifest object
1704
+ */
1705
+ function generateMFManifest(preloadMap) {
1706
+ const options = getNormalizeModuleFederationOptions();
1707
+ const {
1708
+ name,
1709
+ varFilename
1710
+ } = options;
1711
+ const remoteEntry = {
1712
+ name: remoteEntryFile,
1713
+ path: '',
1714
+ type: 'module'
1715
+ };
1716
+ const varRemoteEntry = varFilename ? {
1717
+ name: varFilename,
1718
+ path: '',
1719
+ type: 'module'
1720
+ } : undefined;
1721
+ // Process remotes
1722
+ const remotes = Array.from(Object.entries(getUsedRemotesMap())).flatMap(([remoteKey, modules]) => Array.from(modules).map(moduleKey => ({
1723
+ federationContainerName: options.remotes[remoteKey].entry,
1724
+ moduleName: moduleKey.replace(remoteKey, '').replace('/', ''),
1725
+ alias: remoteKey,
1726
+ entry: '*'
1727
+ })));
1728
+ // Process shared dependencies
1729
+ const shared = Array.from(getUsedShares()).map(shareKey => {
1730
+ const shareItem = getNormalizeShareItem(shareKey);
1731
+ const assets = preloadMap[shareKey] || createEmptyAssetMap();
1732
+ return {
1733
+ id: `${name}:${shareKey}`,
1734
+ name: shareKey,
1735
+ version: shareItem.version,
1736
+ requiredVersion: shareItem.shareConfig.requiredVersion,
1737
+ assets: {
1738
+ js: {
1739
+ async: assets.js.async,
1740
+ sync: assets.js.sync
1741
+ },
1742
+ css: {
1743
+ async: assets.css.async,
1744
+ sync: assets.css.sync
1745
+ }
1791
1746
  }
1792
- devWorker = new DevWorker({
1793
- name: options.name,
1794
- remote,
1795
- host: host ? _extends({}, host, {
1796
- remoteTypeUrls
1797
- }) : undefined,
1798
- extraOptions,
1799
- disableLiveReload: devOptions.disableLiveReload,
1800
- disableHotTypesReload: devOptions.disableHotTypesReload
1801
- });
1802
- const update = () => {
1803
- var _devWorker;
1804
- return (_devWorker = devWorker) == null ? void 0 : _devWorker.update();
1805
- };
1806
- server.watcher.on('change', update);
1807
- server.watcher.on('add', update);
1808
- server.watcher.on('unlink', update);
1809
- (_server$httpServer = server.httpServer) == null || _server$httpServer.once('close', () => {
1810
- var _devWorker2;
1811
- (_devWorker2 = devWorker) == null || _devWorker2.exit();
1812
- server.watcher.off('change', update);
1813
- server.watcher.off('add', update);
1814
- server.watcher.off('unlink', update);
1815
- });
1816
1747
  };
1817
- startDevWorker().catch(error => {
1818
- logDtsError(error, normalizedDtsOptions);
1819
- });
1748
+ }).filter(Boolean);
1749
+ // Process exposed modules
1750
+ const exposes = Object.entries(options.exposes).map(([key, value]) => {
1751
+ const formatKey = key.replace('./', '');
1752
+ const sourceFile = value.import;
1753
+ const assets = preloadMap[sourceFile] || createEmptyAssetMap();
1754
+ return {
1755
+ id: `${name}:${formatKey}`,
1756
+ name: formatKey,
1757
+ assets: {
1758
+ js: {
1759
+ async: assets.js.async,
1760
+ sync: assets.js.sync
1761
+ },
1762
+ css: {
1763
+ async: assets.css.async,
1764
+ sync: assets.css.sync
1765
+ }
1766
+ },
1767
+ path: key
1768
+ };
1769
+ }).filter(Boolean);
1770
+ return {
1771
+ id: name,
1772
+ name,
1773
+ metaData: _extends({
1774
+ name,
1775
+ type: 'app',
1776
+ buildInfo: {
1777
+ buildVersion: '1.0.0',
1778
+ buildName: name
1779
+ },
1780
+ remoteEntry,
1781
+ ssrRemoteEntry: remoteEntry,
1782
+ varRemoteEntry,
1783
+ types: {
1784
+ path: '',
1785
+ name: ''
1786
+ },
1787
+ globalName: name,
1788
+ pluginVersion: '0.2.5'
1789
+ }, !!getPublicPath ? {
1790
+ getPublicPath
1791
+ } : {
1792
+ publicPath
1793
+ }),
1794
+ shared,
1795
+ remotes,
1796
+ exposes
1797
+ };
1798
+ }
1799
+ };
1800
+
1801
+ let _resolve, _parseTimeout;
1802
+ const promise = new Promise((resolve, reject) => {
1803
+ _resolve = v => {
1804
+ clearTimeout(_parseTimeout);
1805
+ _parseTimeout = null;
1806
+ resolve(v);
1807
+ };
1808
+ });
1809
+ function setParseTimeout(timeout) {
1810
+ if (!_parseTimeout) {
1811
+ _parseTimeout = setTimeout(() => {
1812
+ console.warn(`Parse timeout (${timeout}s) - forcing resolve`);
1813
+ _resolve(1);
1814
+ }, timeout * 1000);
1815
+ }
1816
+ }
1817
+ let parsePromise = promise;
1818
+ let exposesParseEnd = false;
1819
+ const parseStartSet = new Set();
1820
+ const parseEndSet = new Set();
1821
+ function pluginModuleParseEnd (excludeFn, options) {
1822
+ setParseTimeout(options.moduleParseTimeout);
1823
+ return [{
1824
+ name: '_',
1825
+ apply: 'serve',
1826
+ config() {
1827
+ // No waiting in development mode
1828
+ _resolve(1);
1820
1829
  }
1821
- };
1822
- const buildPlugin = {
1823
- name: 'module-federation-dts-build',
1830
+ }, {
1831
+ enforce: 'pre',
1832
+ name: 'parseStart',
1824
1833
  apply: 'build',
1825
- configResolved(config) {
1826
- resolvedConfig = config;
1827
- },
1828
- async generateBundle() {
1829
- var _consumeOptions;
1830
- if (hasGeneratedBundle) {
1834
+ load(id) {
1835
+ if (excludeFn(id)) {
1831
1836
  return;
1832
1837
  }
1833
- hasGeneratedBundle = true;
1834
- if (!resolvedConfig) {
1835
- return;
1838
+ parseStartSet.add(id);
1839
+ }
1840
+ }, {
1841
+ enforce: 'post',
1842
+ name: 'parseEnd',
1843
+ apply: 'build',
1844
+ moduleParsed(module) {
1845
+ const id = module.id;
1846
+ if (id === VIRTUAL_EXPOSES) {
1847
+ // When the entry JS file is empty and only contains exposes export code, it’s necessary to wait for the exposes modules to be resolved in order to collect the dependencies being used.
1848
+ exposesParseEnd = true;
1836
1849
  }
1837
- let normalizedDtsOptions;
1838
- try {
1839
- normalizedDtsOptions = normalizeDtsOptions(dtsModuleFederationConfig, resolvedConfig.root);
1840
- } catch (error) {
1841
- logDtsError(error, options.dts);
1850
+ if (excludeFn(id)) {
1842
1851
  return;
1843
1852
  }
1844
- if (typeof normalizedDtsOptions !== 'object') {
1845
- return;
1853
+ parseEndSet.add(id);
1854
+ if (exposesParseEnd && parseStartSet.size === parseEndSet.size) {
1855
+ _resolve(1);
1846
1856
  }
1847
- const context = resolvedConfig.root;
1848
- const outputDir = resolveOutputDir(resolvedConfig);
1849
- let consumeOptions;
1850
- try {
1851
- consumeOptions = normalizeConsumeTypesOptions({
1852
- context,
1853
- dtsOptions: normalizedDtsOptions,
1854
- pluginOptions: dtsModuleFederationConfig
1855
- });
1856
- } catch (error) {
1857
- logDtsError(error, normalizedDtsOptions);
1858
- return;
1857
+ }
1858
+ }];
1859
+ }
1860
+
1861
+ const filter = createFilter();
1862
+ function pluginProxyRemoteEntry () {
1863
+ let viteConfig, _command;
1864
+ return {
1865
+ name: 'proxyRemoteEntry',
1866
+ enforce: 'post',
1867
+ configResolved(config) {
1868
+ viteConfig = config;
1869
+ },
1870
+ config(config, {
1871
+ command
1872
+ }) {
1873
+ _command = command;
1874
+ },
1875
+ resolveId(id) {
1876
+ if (id === REMOTE_ENTRY_ID) {
1877
+ return REMOTE_ENTRY_ID;
1859
1878
  }
1860
- if ((_consumeOptions = consumeOptions) != null && (_consumeOptions = _consumeOptions.host) != null && _consumeOptions.typesOnBuild) {
1861
- try {
1862
- await consumeTypesAPI(consumeOptions);
1863
- } catch (error) {
1864
- logDtsError(error, normalizedDtsOptions);
1865
- }
1879
+ if (id === VIRTUAL_EXPOSES) {
1880
+ return VIRTUAL_EXPOSES;
1866
1881
  }
1867
- let generateOptions;
1868
- try {
1869
- generateOptions = normalizeGenerateTypesOptions({
1870
- context,
1871
- outputDir,
1872
- dtsOptions: normalizedDtsOptions,
1873
- pluginOptions: dtsModuleFederationConfig
1874
- });
1875
- } catch (error) {
1876
- logDtsError(error, normalizedDtsOptions);
1877
- return;
1882
+ if (_command === 'serve' && id.includes(getHostAutoInitPath())) {
1883
+ return id;
1878
1884
  }
1879
- if (!generateOptions) {
1880
- return;
1885
+ },
1886
+ load(id) {
1887
+ if (id === REMOTE_ENTRY_ID) {
1888
+ return parsePromise.then(_ => generateRemoteEntry(getNormalizeModuleFederationOptions()));
1881
1889
  }
1882
- try {
1883
- await generateTypesAPI({
1884
- dtsManagerOptions: generateOptions
1885
- });
1886
- } catch (error) {
1887
- logDtsError(error, normalizedDtsOptions);
1890
+ if (id === VIRTUAL_EXPOSES) {
1891
+ return generateExposes();
1888
1892
  }
1893
+ if (_command === 'serve' && id.includes(getHostAutoInitPath())) {
1894
+ return id;
1895
+ }
1896
+ },
1897
+ transform(code, id) {
1898
+ const transformedCode = (() => {
1899
+ if (!filter(id)) return;
1900
+ if (id.includes(REMOTE_ENTRY_ID)) {
1901
+ return parsePromise.then(_ => generateRemoteEntry(getNormalizeModuleFederationOptions()));
1902
+ }
1903
+ if (id === VIRTUAL_EXPOSES) {
1904
+ return generateExposes();
1905
+ }
1906
+ if (id.includes(getHostAutoInitPath())) {
1907
+ const options = getNormalizeModuleFederationOptions();
1908
+ if (_command === 'serve') {
1909
+ var _viteConfig$server, _viteConfig$server2;
1910
+ const host = typeof ((_viteConfig$server = viteConfig.server) == null ? void 0 : _viteConfig$server.host) === 'string' && viteConfig.server.host !== '0.0.0.0' ? viteConfig.server.host : 'localhost';
1911
+ const publicPath = JSON.stringify(resolvePublicPath(options, viteConfig.base) + options.filename);
1912
+ return `
1913
+ const origin = (window && ${!options.ignoreOrigin}) ? window.origin : "//${host}:${(_viteConfig$server2 = viteConfig.server) == null ? void 0 : _viteConfig$server2.port}"
1914
+ const remoteEntryPromise = await import(origin + ${publicPath})
1915
+ // __tla only serves as a hack for vite-plugin-top-level-await.
1916
+ Promise.resolve(remoteEntryPromise)
1917
+ .then(remoteEntry => {
1918
+ return Promise.resolve(remoteEntry.__tla)
1919
+ .then(remoteEntry.init).catch(remoteEntry.init)
1920
+ })
1921
+ `;
1922
+ }
1923
+ return code;
1924
+ }
1925
+ })();
1926
+ return mapCodeToCodeWithSourcemap(transformedCode);
1927
+ }
1928
+ };
1929
+ }
1930
+
1931
+ createFilter();
1932
+ function pluginProxyRemotes (options) {
1933
+ const {
1934
+ remotes
1935
+ } = options;
1936
+ return {
1937
+ name: 'proxyRemotes',
1938
+ config(config, {
1939
+ command: _command
1940
+ }) {
1941
+ Object.keys(remotes).forEach(key => {
1942
+ const remote = remotes[key];
1943
+ config.resolve.alias.push({
1944
+ find: new RegExp(`^(${remote.name}(\/.*|$))`),
1945
+ replacement: '$1',
1946
+ customResolver(source) {
1947
+ const remoteModule = getRemoteVirtualModule(source, _command);
1948
+ addUsedRemote(remote.name, source);
1949
+ return remoteModule.getPath();
1950
+ }
1951
+ });
1952
+ });
1889
1953
  }
1890
1954
  };
1891
- return [devPlugin, buildPlugin];
1892
1955
  }
1893
1956
 
1894
1957
  /**
@@ -2000,6 +2063,122 @@ function proxySharedModule(options) {
2000
2063
  }];
2001
2064
  }
2002
2065
 
2066
+ const VarRemoteEntry = () => {
2067
+ const mfOptions = getNormalizeModuleFederationOptions();
2068
+ const {
2069
+ name,
2070
+ varFilename,
2071
+ filename
2072
+ } = mfOptions;
2073
+ let viteConfig;
2074
+ return [{
2075
+ name: 'module-federation-var-remote-entry',
2076
+ apply: 'serve',
2077
+ /**
2078
+ * Stores resolved Vite config for later use
2079
+ */
2080
+ /**
2081
+ * Finalizes configuration after all plugins are resolved
2082
+ * @param config - Fully resolved Vite config
2083
+ */
2084
+ configResolved(config) {
2085
+ viteConfig = config;
2086
+ },
2087
+ /**
2088
+ * Configures dev server middleware to handle varRemoteEntry requests
2089
+ * @param server - Vite dev server instance
2090
+ */
2091
+ configureServer(server) {
2092
+ server.middlewares.use((req, res, next) => {
2093
+ var _req$url;
2094
+ if (!varFilename) {
2095
+ next();
2096
+ return;
2097
+ }
2098
+ if (((_req$url = req.url) == null ? void 0 : _req$url.replace(/\?.*/, '')) === (viteConfig.base + varFilename).replace(/^\/?/, '/')) {
2099
+ res.setHeader('Content-Type', 'text/javascript');
2100
+ res.setHeader('Access-Control-Allow-Origin', '*');
2101
+ console.log({
2102
+ filename
2103
+ });
2104
+ res.end(generateVarRemoteEntry(filename));
2105
+ } else {
2106
+ next();
2107
+ }
2108
+ });
2109
+ }
2110
+ }, {
2111
+ name: 'module-federation-var-remote-entry',
2112
+ enforce: 'post',
2113
+ /**
2114
+ * Initial plugin configuration
2115
+ * @param config - Vite config object
2116
+ * @param command - Current Vite command (serve/build)
2117
+ */
2118
+ config(config, {
2119
+ command
2120
+ }) {
2121
+ if (!config.build) config.build = {};
2122
+ },
2123
+ /**
2124
+ * Generates the module federation "var" remote entry file
2125
+ * @param options - Rollup output options
2126
+ * @param bundle - Generated bundle assets
2127
+ */
2128
+ async generateBundle(options, bundle) {
2129
+ if (!varFilename) return;
2130
+ const isValidName = isValidVarName(name);
2131
+ if (!isValidName) {
2132
+ viteConfig.logger.warn(`Provided remote name "${name}" is not valid for "var" remoteEntry type, thus it's placed in globalThis['${name}'].\nIt may cause problems, so you would better want to use valid var name (see https://www.w3schools.com/js/js_variables.asp).`);
2133
+ }
2134
+ const remoteEntryFile = findRemoteEntryFile(mfOptions.filename, bundle);
2135
+ if (!remoteEntryFile) throw new Error(`Couldn't find a remoteEntry chunk file for ${mfOptions.filename}, can't generate varRemoteEntry file`);
2136
+ this.emitFile({
2137
+ type: 'asset',
2138
+ fileName: varFilename,
2139
+ source: generateVarRemoteEntry(remoteEntryFile)
2140
+ });
2141
+ }
2142
+ }];
2143
+ function isValidVarName(name) {
2144
+ return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name);
2145
+ }
2146
+ /**
2147
+ * Generates the final "var" remote entry file
2148
+ * @param remoteEntryFile - Path to esm remote entry file
2149
+ * @returns Complete "var" remoteEntry.js file source
2150
+ */
2151
+ function generateVarRemoteEntry(remoteEntryFile) {
2152
+ const options = getNormalizeModuleFederationOptions();
2153
+ const {
2154
+ name,
2155
+ varFilename
2156
+ } = options;
2157
+ const isValidName = isValidVarName(name);
2158
+ // todo: implement publicPath/getPublicPath support
2159
+ return `
2160
+ ${isValidName ? `var ${name};` : ''}
2161
+ ${isValidName ? name : `globalThis['${name}']`} = (function () {
2162
+ function getScriptUrl() {
2163
+ const currentScript = document.currentScript;
2164
+ if (!currentScript) {
2165
+ console.error("[VarRemoteEntry] ${varFilename} script should be called from sync <script> tag (document.currentScript is undefined)")
2166
+ return '/';
2167
+ }
2168
+ return document.currentScript.src.replace(/\\/[^/]*$/, '/');
2169
+ }
2170
+
2171
+ const entry = getScriptUrl() + '${remoteEntryFile}';
2172
+
2173
+ return {
2174
+ get: (...args) => import(entry).then(m => m.get(...args)),
2175
+ init: (...args) => import(entry).then(m => m.init(...args)),
2176
+ };
2177
+ })();
2178
+ `;
2179
+ }
2180
+ };
2181
+
2003
2182
  var aliasToArrayPlugin = {
2004
2183
  name: 'alias-transform-plugin',
2005
2184
  config: (config, {
@@ -2073,6 +2252,8 @@ function federation(mfUserOptions) {
2073
2252
  entryPath: VIRTUAL_EXPOSES
2074
2253
  }), pluginProxyRemoteEntry(), pluginProxyRemotes(options), ...pluginModuleParseEnd(id => {
2075
2254
  return id.includes(getHostAutoInitImportId()) || id.includes(REMOTE_ENTRY_ID) || id.includes(VIRTUAL_EXPOSES) || id.includes(getLocalSharedImportMapPath());
2255
+ }, {
2256
+ moduleParseTimeout: options.moduleParseTimeout
2076
2257
  }), ...proxySharedModule({
2077
2258
  shared
2078
2259
  }), PluginDevProxyModuleTopLevelAwait(), {
@@ -2101,7 +2282,7 @@ function federation(mfUserOptions) {
2101
2282
  (_config$optimizeDeps3 = config.optimizeDeps) == null || (_config$optimizeDeps3 = _config$optimizeDeps3.needsInterop) == null || _config$optimizeDeps3.push(virtualDir);
2102
2283
  (_config$optimizeDeps4 = config.optimizeDeps) == null || (_config$optimizeDeps4 = _config$optimizeDeps4.needsInterop) == null || _config$optimizeDeps4.push(getLocalSharedImportMapPath());
2103
2284
  }
2104
- }, ...Manifest()];
2285
+ }, ...Manifest(), ...VarRemoteEntry()];
2105
2286
  }
2106
2287
 
2107
2288
  export { federation };