@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.
- package/README.md +4 -0
- package/lib/index.cjs +1241 -1080
- package/lib/index.esm.js +1241 -1080
- package/lib/index.modern.js +1673 -1492
- package/lib/index.umd.js +1241 -1080
- package/lib/plugins/pluginModuleParseEnd.d.ts +3 -1
- package/lib/plugins/pluginVarRemoteEntry.d.ts +3 -0
- package/lib/utils/bundleHelpers.d.ts +2 -0
- package/lib/utils/normalizeModuleFederationOptions.d.ts +11 -0
- package/package.json +1 -1
package/lib/index.modern.js
CHANGED
|
@@ -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
|
-
|
|
131
|
-
const
|
|
132
|
-
|
|
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
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
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
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
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
|
-
|
|
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
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
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
|
-
|
|
382
|
-
|
|
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
|
-
}
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
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
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
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
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
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
|
|
437
|
-
|
|
438
|
-
result[key] = normalizeShareItem(key, shared[key]);
|
|
439
|
-
});
|
|
438
|
+
if (typeof dtsOptions === 'object' && dtsOptions && dtsOptions.displayErrorInTerminal === false) {
|
|
439
|
+
return;
|
|
440
440
|
}
|
|
441
|
-
|
|
442
|
-
}
|
|
443
|
-
function
|
|
444
|
-
if (
|
|
445
|
-
|
|
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
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
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
|
-
|
|
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
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
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
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
}
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
}
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
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
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
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
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
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
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
})
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
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
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
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
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
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
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
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
|
|
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
|
-
*
|
|
1095
|
-
*
|
|
1096
|
-
* @param
|
|
1097
|
-
* @
|
|
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
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
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
|
-
*
|
|
1122
|
-
*
|
|
1123
|
-
*
|
|
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
|
-
*
|
|
1139
|
-
* @param
|
|
1140
|
-
* @
|
|
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
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
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
|
-
*
|
|
1162
|
-
* @param
|
|
1163
|
-
* @
|
|
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
|
|
1168
|
-
|
|
1169
|
-
|
|
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
|
-
|
|
1185
|
-
|
|
853
|
+
/**
|
|
854
|
+
* https://github.com/module-federation/vite/issues/68
|
|
855
|
+
*/
|
|
856
|
+
function getLocalSharedImportMapPath_temp() {
|
|
1186
857
|
const {
|
|
1187
|
-
name
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
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
|
-
*
|
|
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
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
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
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
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
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
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
|
-
|
|
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
|
-
*
|
|
1359
|
-
* @param
|
|
1360
|
-
* @returns Complete manifest object
|
|
1003
|
+
* Set the root path for finding node_modules
|
|
1004
|
+
* @param root - Root path
|
|
1361
1005
|
*/
|
|
1362
|
-
|
|
1363
|
-
|
|
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
|
-
|
|
1366
|
-
} =
|
|
1367
|
-
const
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
})));
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
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
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
const
|
|
1458
|
-
const
|
|
1459
|
-
function
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
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
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
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
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
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
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
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
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
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
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
const
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
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
|
-
|
|
1619
|
-
|
|
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
|
-
|
|
1623
|
-
|
|
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
|
-
|
|
1627
|
-
|
|
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
|
|
1430
|
+
return cssAssets;
|
|
1630
1431
|
};
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
if (
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
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
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
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
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
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
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
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
|
-
|
|
1701
|
-
|
|
1511
|
+
// Handle empty original base case
|
|
1512
|
+
if (originalBase === '') {
|
|
1513
|
+
return 'auto';
|
|
1702
1514
|
}
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
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
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
const
|
|
1715
|
-
|
|
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
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
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
|
-
|
|
1638
|
+
_command = command;
|
|
1639
|
+
_originalConfigBase = config.base;
|
|
1731
1640
|
},
|
|
1732
1641
|
configResolved(config) {
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
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
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
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
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
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
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
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
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
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
|
-
|
|
1818
|
-
|
|
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
|
-
|
|
1823
|
-
name: '
|
|
1830
|
+
}, {
|
|
1831
|
+
enforce: 'pre',
|
|
1832
|
+
name: 'parseStart',
|
|
1824
1833
|
apply: 'build',
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
},
|
|
1828
|
-
async generateBundle() {
|
|
1829
|
-
var _consumeOptions;
|
|
1830
|
-
if (hasGeneratedBundle) {
|
|
1834
|
+
load(id) {
|
|
1835
|
+
if (excludeFn(id)) {
|
|
1831
1836
|
return;
|
|
1832
1837
|
}
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1845
|
-
|
|
1853
|
+
parseEndSet.add(id);
|
|
1854
|
+
if (exposesParseEnd && parseStartSet.size === parseEndSet.size) {
|
|
1855
|
+
_resolve(1);
|
|
1846
1856
|
}
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
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 (
|
|
1861
|
-
|
|
1862
|
-
await consumeTypesAPI(consumeOptions);
|
|
1863
|
-
} catch (error) {
|
|
1864
|
-
logDtsError(error, normalizedDtsOptions);
|
|
1865
|
-
}
|
|
1879
|
+
if (id === VIRTUAL_EXPOSES) {
|
|
1880
|
+
return VIRTUAL_EXPOSES;
|
|
1866
1881
|
}
|
|
1867
|
-
|
|
1868
|
-
|
|
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
|
-
|
|
1880
|
-
|
|
1885
|
+
},
|
|
1886
|
+
load(id) {
|
|
1887
|
+
if (id === REMOTE_ENTRY_ID) {
|
|
1888
|
+
return parsePromise.then(_ => generateRemoteEntry(getNormalizeModuleFederationOptions()));
|
|
1881
1889
|
}
|
|
1882
|
-
|
|
1883
|
-
|
|
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 };
|