@module-federation/vite
Version:
Vite plugin for Module Federation
1,189 lines (1,168 loc) • 66.7 kB
JavaScript
var defu = require('defu');
var fs = require('fs');
var path = require('pathe');
var MagicString = require('magic-string');
var pluginutils = require('@rollup/pluginutils');
var estreeWalker = require('estree-walker');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
function _interopNamespace(e) {
if (e && e.__esModule) return e;
var n = Object.create(null);
if (e) {
Object.keys(e).forEach(function (k) {
if (k !== 'default') {
var d = Object.getOwnPropertyDescriptor(e, k);
Object.defineProperty(n, k, d.get ? d : {
enumerable: true,
get: function () { return e[k]; }
});
}
});
}
n["default"] = e;
return n;
}
var defu__default = /*#__PURE__*/_interopDefaultLegacy(defu);
var fs__namespace = /*#__PURE__*/_interopNamespace(fs);
var path__namespace = /*#__PURE__*/_interopNamespace(path);
var path__default = /*#__PURE__*/_interopDefaultLegacy(path);
var MagicString__default = /*#__PURE__*/_interopDefaultLegacy(MagicString);
var mapCodeToCodeWithSourcemap = function mapCodeToCodeWithSourcemap(code) {
return Promise.resolve(code).then(function (resolvedCode) {
if (resolvedCode === undefined) {
return;
}
var s = new MagicString__default["default"](resolvedCode);
return {
code: s.toString(),
map: s.generateMap({
hires: true
})
};
});
};
function getFirstHtmlEntryFile(entryFiles) {
return entryFiles.find(function (file) {
return file.endsWith('.html');
});
}
var addEntry = function addEntry(_ref) {
var entryName = _ref.entryName,
entryPath = _ref.entryPath,
fileName = _ref.fileName,
_ref$inject = _ref.inject,
inject = _ref$inject === void 0 ? 'entry' : _ref$inject;
var devEntryPath = entryPath.startsWith('virtual:mf') ? '@id/' + entryPath : entryPath;
var entryFiles = [];
var htmlFilePath;
var _command;
var emitFileId;
var viteConfig;
function injectHtml() {
return inject === 'html' && htmlFilePath;
}
function injectEntry() {
return inject === 'entry' || !htmlFilePath;
}
return [{
name: 'add-entry',
apply: 'serve',
config: function config(_config, _ref2) {
var command = _ref2.command;
_command = command;
},
configResolved: function configResolved(config) {
viteConfig = config;
devEntryPath = config.base + devEntryPath.replace(/\\\\?/g, '/').replace(/.+?\:([/\\])[/\\]?/, '$1').replace(/^\//, '');
},
configureServer: function configureServer(server) {
server.middlewares.use(function (req, res, next) {
if (!fileName) {
next();
return;
}
if (req.url && req.url.startsWith((viteConfig.base + fileName).replace(/^\/?/, '/'))) {
req.url = devEntryPath;
}
next();
});
},
transformIndexHtml: function transformIndexHtml(c) {
if (!injectHtml()) return;
return c.replace('<head>', "<head><script type=\"module\" src=" + JSON.stringify(devEntryPath.replace(/.+?\:([/\\])[/\\]?/, '$1').replace(/\\\\?/g, '/')) + "></script>");
},
transform: function transform(code, id) {
if (id.includes('node_modules') || inject !== 'html' || htmlFilePath) {
return;
}
if (id.includes('.svelte-kit') && id.includes('internal.js')) {
var src = devEntryPath.replace(/.+?\:([/\\])[/\\]?/, '$1').replace(/\\\\?/g, '/');
return code.replace(/<head>/g, '<head><script type=\\"module\\" src=\\"' + src + '\\"></script>');
}
}
}, {
name: 'add-entry',
enforce: 'post',
configResolved: function configResolved(config) {
viteConfig = config;
var inputOptions = config.build.rollupOptions.input;
if (!inputOptions) {
htmlFilePath = path__namespace.resolve(config.root, 'index.html');
} else if (typeof inputOptions === 'string') {
entryFiles = [inputOptions];
} else if (Array.isArray(inputOptions)) {
entryFiles = inputOptions;
} else if (typeof inputOptions === 'object') {
entryFiles = Object.values(inputOptions);
}
if (entryFiles && entryFiles.length > 0) {
htmlFilePath = getFirstHtmlEntryFile(entryFiles);
}
},
buildStart: function buildStart() {
if (_command === 'serve') return;
var hasHash = fileName == null || fileName.includes == null ? void 0 : fileName.includes('[hash');
var emitFileOptions = {
name: entryName,
type: 'chunk',
id: entryPath,
preserveSignature: 'strict'
};
if (!hasHash) {
emitFileOptions.fileName = fileName;
}
emitFileId = this.emitFile(emitFileOptions);
if (htmlFilePath && fs__namespace.existsSync(htmlFilePath)) {
var htmlContent = fs__namespace.readFileSync(htmlFilePath, 'utf-8');
var scriptRegex = /<script\s+[^>]*src=["']([^"']+)["'][^>]*>/gi;
var match;
while ((match = scriptRegex.exec(htmlContent)) !== null) {
entryFiles.push(match[1]);
}
}
},
generateBundle: function generateBundle(options, bundle) {
var _viteConfig$experimen, _viteConfig$experimen2;
if (!injectHtml()) return;
var file = this.getFileName(emitFileId);
var path = (_viteConfig$experimen = viteConfig.experimental) != null && _viteConfig$experimen.renderBuiltUrl ? (_viteConfig$experimen2 = viteConfig.experimental) == null ? void 0 : _viteConfig$experimen2.renderBuiltUrl(file) : viteConfig.base + file;
var scriptContent = "\n <script type=\"module\" src=\"" + path + "\"></script>\n ";
for (var _fileName in bundle) {
if (_fileName.endsWith('.html')) {
var htmlAsset = bundle[_fileName];
if (htmlAsset.type === 'chunk') return;
var htmlContent = htmlAsset.source.toString() || '';
htmlContent = htmlContent.replace('<head>', "<head>" + scriptContent);
htmlAsset.source = htmlContent;
}
}
},
transform: function transform(code, id) {
if (injectEntry() && entryFiles.some(function (file) {
return id.endsWith(file);
})) {
var injection = "\n import " + JSON.stringify(entryPath) + ";\n ";
return mapCodeToCodeWithSourcemap(injection + code);
}
}
}];
};
function _arrayLikeToArray(r, a) {
(null == a || a > r.length) && (a = r.length);
for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e];
return n;
}
function _createForOfIteratorHelperLoose(r, e) {
var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"];
if (t) return (t = t.call(r)).next.bind(t);
if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) {
t && (r = t);
var o = 0;
return function () {
return o >= r.length ? {
done: !0
} : {
done: !1,
value: r[o++]
};
};
}
throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
}
function _extends() {
return _extends = Object.assign ? Object.assign.bind() : function (n) {
for (var e = 1; e < arguments.length; e++) {
var t = arguments[e];
for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]);
}
return n;
}, _extends.apply(null, arguments);
}
function _unsupportedIterableToArray(r, a) {
if (r) {
if ("string" == typeof r) return _arrayLikeToArray(r, a);
var t = {}.toString.call(r).slice(8, -1);
return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0;
}
}
/**
* Check if user-defined alias conflicts with shared modules
* This should run after aliasToArrayPlugin to ensure alias is an array
*/
function checkAliasConflicts(options) {
var _options$shared = options.shared,
shared = _options$shared === void 0 ? {} : _options$shared;
var sharedKeys = Object.keys(shared);
return {
name: 'check-alias-conflicts',
configResolved: function configResolved(config) {
var _config$resolve;
if (sharedKeys.length === 0) return;
var userAliases = ((_config$resolve = config.resolve) == null ? void 0 : _config$resolve.alias) || [];
var conflicts = [];
for (var _i = 0, _sharedKeys = sharedKeys; _i < _sharedKeys.length; _i++) {
var sharedKey = _sharedKeys[_i];
for (var _iterator = _createForOfIteratorHelperLoose(userAliases), _step; !(_step = _iterator()).done;) {
var aliasEntry = _step.value;
var findPattern = aliasEntry.find;
var replacement = aliasEntry.replacement;
// Skip if replacement is not a string (e.g., customResolver)
if (typeof replacement !== 'string') continue;
// Skip Module Federation internal aliases (used for proxying shared modules)
// These are generated with replacement '$1' and should not trigger warnings
if (replacement === '$1') continue;
// Check if alias pattern matches the shared module
var isMatch = false;
if (typeof findPattern === 'string') {
isMatch = findPattern === sharedKey || sharedKey.startsWith(findPattern + '/');
} else if (findPattern instanceof RegExp) {
isMatch = findPattern.test(sharedKey);
}
if (isMatch) {
conflicts.push({
sharedModule: sharedKey,
alias: String(findPattern),
target: replacement
});
}
}
}
if (conflicts.length > 0) {
config.logger.warn('\n[Module Federation] Detected alias conflicts with shared modules:');
conflicts.forEach(function (_ref) {
var sharedModule = _ref.sharedModule,
alias = _ref.alias,
target = _ref.target;
config.logger.warn(" - Shared module \"" + sharedModule + "\" is aliased by \"" + alias + "\" to \"" + target + "\"");
});
config.logger.warn(" This may cause runtime errors as the shared module will bypass Module Federation's sharing mechanism.");
}
}
};
}
/**
* Solve the problem that dev mode dependency prebunding does not support top-level await syntax
*/
function PluginDevProxyModuleTopLevelAwait() {
var filterFunction = pluginutils.createFilter();
var processedFlag = '/* already-processed-by-dev-proxy-module-top-level-await */';
return {
name: 'dev-proxy-module-top-level-await',
apply: 'serve',
transform: function transform(code, id) {
if (code.includes(processedFlag)) {
return null;
}
if (!code.includes('/*mf top-level-await placeholder replacement mf*/')) {
return null;
}
if (!filterFunction(id)) return null;
var ast;
try {
ast = this.parse(code, {
allowReturnOutsideFunction: true
});
} catch (e) {
throw new Error(id + ": " + e);
}
var magicString = new MagicString__default["default"](code);
estreeWalker.walk(ast, {
enter: function enter(node) {
if (node.type === 'ExportNamedDeclaration' && node.specifiers) {
var exportSpecifiers = node.specifiers.map(function (specifier) {
return specifier.exported.name;
});
var proxyStatements = exportSpecifiers.map(function (name) {
return "\n const __mfproxy__await" + name + " = await " + name + "();\n const __mfproxy__" + name + " = () => __mfproxy__await" + name + ";\n ";
}).join('\n');
var exportStatements = exportSpecifiers.map(function (name) {
return "__mfproxy__" + name + " as " + name;
}).join(', ');
var start = node.start;
var end = node.end;
var replacement = proxyStatements + "\nexport { " + exportStatements + " };";
magicString.overwrite(start, end, replacement);
}
if (node.type === 'ExportDefaultDeclaration') {
var declaration = node.declaration;
var _start = node.start;
var _end = node.end;
var proxyStatement;
var exportStatement = 'default';
if (declaration.type === 'Identifier') {
// example: export default foo;
proxyStatement = "\n const __mfproxy__awaitdefault = await " + declaration.name + "();\n const __mfproxy__default = __mfproxy__awaitdefault;\n ";
} else if (declaration.type === 'CallExpression' || declaration.type === 'FunctionDeclaration') {
// example: export default someFunction();
var declarationCode = code.slice(declaration.start, declaration.end);
proxyStatement = "\n const __mfproxy__awaitdefault = await (" + declarationCode + ");\n const __mfproxy__default = __mfproxy__awaitdefault;\n ";
} else {
// other
proxyStatement = "\n const __mfproxy__awaitdefault = await (" + code.slice(declaration.start, declaration.end) + ");\n const __mfproxy__default = __mfproxy__awaitdefault;\n ";
}
var _replacement = proxyStatement + "\nexport { __mfproxy__default as " + exportStatement + " };";
magicString.overwrite(_start, _end, _replacement);
}
}
});
var transformedCode = magicString.toString();
return {
code: processedFlag + "\n" + transformedCode,
map: magicString.generateMap({
hires: true
})
};
}
};
}
function normalizeExposesItem(key, item) {
var importPath = '';
if (typeof item === 'string') {
importPath = item;
}
if (typeof item === 'object') {
importPath = item["import"];
}
return {
"import": importPath
};
}
function normalizeExposes(exposes) {
if (!exposes) return {};
var res = {};
Object.keys(exposes).forEach(function (key) {
res[key] = normalizeExposesItem(key, exposes[key]);
});
return res;
}
function normalizeRemotes(remotes) {
if (!remotes) return {};
var result = {};
if (typeof remotes === 'object') {
Object.keys(remotes).forEach(function (key) {
result[key] = normalizeRemoteItem(key, remotes[key]);
});
}
return result;
}
function normalizeRemoteItem(key, remote) {
if (typeof remote === 'string') {
var _remote$split = remote.split('@'),
entryGlobalName = _remote$split[0];
var entry = remote.replace(entryGlobalName + '@', '');
return {
type: 'var',
name: key,
entry: entry,
entryGlobalName: entryGlobalName,
shareScope: 'default'
};
}
return Object.assign({
type: 'var',
name: key,
shareScope: 'default',
entryGlobalName: key
}, remote);
}
function removePathFromNpmPackage(packageString) {
// 匹配npm包名的正则表达式,忽略路径部分
var regex = /^(?:@[^/]+\/)?[^/]+/;
// 使用正则表达式匹配并提取包名
var match = packageString.match(regex);
// 返回匹配到的包名,如果没有匹配到则返回原字符串
return match ? match[0] : packageString;
}
/**
* Tries to find the package.json's version of a shared package
* if `package.json` is not declared in `exports`
* @param {string} sharedName
* @returns {string | undefined}
*/
function searchPackageVersion(sharedName) {
try {
var sharedPath = require.resolve(sharedName);
var potentialPackageJsonDir = path__namespace.dirname(sharedPath);
var rootDir = path__namespace.parse(potentialPackageJsonDir).root;
while (path__namespace.parse(potentialPackageJsonDir).base !== 'node_modules' && potentialPackageJsonDir !== rootDir) {
var potentialPackageJsonPath = path__namespace.join(potentialPackageJsonDir, 'package.json');
if (fs__namespace.existsSync(potentialPackageJsonPath)) {
var potentialPackageJson = require(potentialPackageJsonPath);
if (typeof potentialPackageJson == 'object' && potentialPackageJson !== null && typeof potentialPackageJson.version === 'string' && potentialPackageJson.name === sharedName) {
return potentialPackageJson.version;
}
}
potentialPackageJsonDir = path__namespace.dirname(potentialPackageJsonDir);
}
} catch (_) {}
return undefined;
}
function normalizeShareItem(key, shareItem) {
var version;
try {
try {
version = require(path__namespace.join(removePathFromNpmPackage(key), 'package.json')).version;
} catch (e1) {
try {
var localPath = path__namespace.join(process.cwd(), 'node_modules', removePathFromNpmPackage(key), 'package.json');
version = require(localPath).version;
} catch (e2) {
version = searchPackageVersion(key);
if (!version) console.error(e1);
}
}
} catch (e) {
console.error("Unexpected error resolving version for " + key + ":", e);
}
if (typeof shareItem === 'string') {
return {
name: shareItem,
version: version,
scope: 'default',
from: '',
shareConfig: {
"import": undefined,
singleton: false,
requiredVersion: version ? "^" + version : '*'
}
};
}
return {
name: key,
from: '',
version: shareItem.version || version,
scope: shareItem.shareScope || 'default',
shareConfig: {
"import": typeof shareItem === 'object' ? shareItem["import"] : undefined,
singleton: shareItem.singleton || false,
requiredVersion: shareItem.requiredVersion || (version ? "^" + version : '*'),
strictVersion: !!shareItem.strictVersion
}
};
}
function normalizeShared(shared) {
if (!shared) return {};
var result = {};
if (Array.isArray(shared)) {
shared.forEach(function (key) {
result[key] = normalizeShareItem(key, key);
});
return result;
}
if (typeof shared === 'object') {
Object.keys(shared).forEach(function (key) {
result[key] = normalizeShareItem(key, shared[key]);
});
}
return result;
}
function normalizeLibrary(library) {
if (!library) return undefined;
return library;
}
function normalizeManifest(manifest) {
if (manifest === void 0) {
manifest = false;
}
if (typeof manifest === 'boolean') {
return manifest;
}
return Object.assign({
filePath: '',
disableAssetsAnalyze: false,
fileName: 'mf-manifest.json'
}, manifest);
}
var config;
function getNormalizeModuleFederationOptions() {
return config;
}
function getNormalizeShareItem(key) {
var options = getNormalizeModuleFederationOptions();
var shareItem = options.shared[key] || options.shared[removePathFromNpmPackage(key)] || options.shared[removePathFromNpmPackage(key) + '/'];
return shareItem;
}
function normalizeModuleFederationOptions(options) {
if (options.virtualModuleDir && options.virtualModuleDir.includes('/')) {
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'.");
}
return config = {
exposes: normalizeExposes(options.exposes),
filename: options.filename || 'remoteEntry-[hash]',
library: normalizeLibrary(options.library),
name: options.name,
// remoteType: options.remoteType,
remotes: normalizeRemotes(options.remotes),
runtime: options.runtime,
shareScope: options.shareScope || 'default',
shared: normalizeShared(options.shared),
runtimePlugins: options.runtimePlugins || [],
implementation: options.implementation || require.resolve('@module-federation/runtime'),
manifest: normalizeManifest(options.manifest),
dev: options.dev,
dts: options.dts,
getPublicPath: options.getPublicPath,
publicPath: options.publicPath,
shareStrategy: options.shareStrategy || 'version-first',
ignoreOrigin: options.ignoreOrigin || false,
virtualModuleDir: options.virtualModuleDir || '__mf__virtual',
hostInitInjectLocation: options.hostInitInjectLocation || 'html',
bundleAllCSS: options.bundleAllCSS || false
};
}
/**
* Escaping rules:
* Convert using the format __${mapping}__, where _ and $ are not allowed in npm package names but can be used in variable names.
* @ => 1
* / => 2
* - => 3
* . => 4
*/
/**
* Encodes a package name into a valid file name.
* @param {string} name - The package name, e.g., "@scope/xx-xx.xx".
* @returns {string} - The encoded file name.
*/
function packageNameEncode(name) {
if (typeof name !== 'string') throw new Error('A string package name is required');
return name.replace(/@/g, '_mf_0_').replace(/\//g, '_mf_1_').replace(/-/g, '_mf_2_').replace(/\./g, '_mf_3_');
}
/**
* Decodes an encoded file name back to the original package name.
* @param {string} encoded - The encoded file name, e.g., "_mf_0_scope_mf_1_xx_mf_2_xx_mf_3_xx".
* @returns {string} - The decoded package name.
*/
function packageNameDecode(encoded) {
if (typeof encoded !== 'string') throw new Error('A string encoded file name is required');
return encoded.replace(/_mf_0_/g, '@').replace(/_mf_1_/g, '/').replace(/_mf_2_/g, '-').replace(/_mf_3_/g, '.');
}
/**
* https://github.com/module-federation/vite/issues/68
*/
function getLocalSharedImportMapPath_temp() {
var _getNormalizeModuleFe = getNormalizeModuleFederationOptions(),
name = _getNormalizeModuleFe.name;
return path__default["default"].resolve('.__mf__temp', packageNameEncode(name), 'localSharedImportMap');
}
function writeLocalSharedImportMap_temp(content) {
var localSharedImportMapId = getLocalSharedImportMapPath_temp();
createFile(localSharedImportMapId + '.js', '\n// Windows temporarily needs this file, https://github.com/module-federation/vite/issues/68\n' + content);
}
function createFile(filePath, content) {
var dir = path__default["default"].dirname(filePath);
fs.mkdirSync(dir, {
recursive: true
});
fs.writeFileSync(filePath, content);
}
// Cache root path
var rootDir;
function findNodeModulesDir(root) {
if (root === void 0) {
root = process.cwd();
}
var currentDir = root;
while (currentDir !== path.parse(currentDir).root) {
var nodeModulesPath = path.join(currentDir, 'node_modules');
if (fs.existsSync(nodeModulesPath)) {
return nodeModulesPath;
}
currentDir = path.dirname(currentDir);
}
return '';
}
// Cache nodeModulesDir result to avoid repeated calculations
var cachedNodeModulesDir;
function getNodeModulesDir() {
if (!cachedNodeModulesDir) {
cachedNodeModulesDir = findNodeModulesDir(rootDir);
}
return cachedNodeModulesDir;
}
function getSuffix(name) {
var base = path.basename(name);
var dotIndex = base.lastIndexOf('.');
if (dotIndex > 0 && dotIndex < base.length - 1) {
return base.slice(dotIndex);
}
return '.js';
}
var patternMap = {};
var cacheMap = {};
/**
* Physically generate files as virtual modules under node_modules/__mf__virtual/*
*/
function assertModuleFound(tag, str) {
if (str === void 0) {
str = '';
}
var module = VirtualModule.findModule(tag, str);
if (!module) {
throw new Error("Module Federation shared module '" + str + "' not found. Please ensure it's installed as a dependency in your package.json.");
}
return module;
}
var VirtualModule = /*#__PURE__*/function () {
function VirtualModule(name, tag, suffix) {
if (tag === void 0) {
tag = '__mf_v__';
}
if (suffix === void 0) {
suffix = '';
}
this.name = void 0;
this.tag = void 0;
this.suffix = void 0;
this.inited = false;
this.name = name;
this.tag = tag;
this.suffix = suffix || getSuffix(name);
if (!cacheMap[this.tag]) cacheMap[this.tag] = {};
cacheMap[this.tag][this.name] = this;
}
/**
* Set the root path for finding node_modules
* @param root - Root path
*/
VirtualModule.setRoot = function setRoot(root) {
rootDir = root;
// Reset cache to ensure using the new root path
cachedNodeModulesDir = undefined;
}
/**
* Ensure virtual package directory exists
*/;
VirtualModule.ensureVirtualPackageExists = function ensureVirtualPackageExists() {
var nodeModulesDir = getNodeModulesDir();
var _getNormalizeModuleFe = getNormalizeModuleFederationOptions(),
virtualModuleDir = _getNormalizeModuleFe.virtualModuleDir;
var virtualPackagePath = path.resolve(nodeModulesDir, virtualModuleDir);
if (!fs.existsSync(virtualPackagePath)) {
fs.mkdirSync(virtualPackagePath);
fs.writeFileSync(path.resolve(virtualPackagePath, 'empty.js'), '');
fs.writeFileSync(path.resolve(virtualPackagePath, 'package.json'), JSON.stringify({
name: virtualModuleDir,
main: 'empty.js'
}));
}
};
VirtualModule.findModule = function findModule(tag, str) {
if (str === void 0) {
str = '';
}
if (!patternMap[tag]) patternMap[tag] = new RegExp("(.*" + packageNameEncode(tag) + "(.+?)" + packageNameEncode(tag) + ".*)");
var moduleName = (str.match(patternMap[tag]) || [])[2];
if (moduleName) return cacheMap[tag][packageNameDecode(moduleName)];
return undefined;
};
var _proto = VirtualModule.prototype;
_proto.getPath = function getPath() {
return path.resolve(getNodeModulesDir(), this.getImportId());
};
_proto.getImportId = function getImportId() {
var _getNormalizeModuleFe2 = getNormalizeModuleFederationOptions(),
mfName = _getNormalizeModuleFe2.name,
virtualModuleDir = _getNormalizeModuleFe2.virtualModuleDir;
return virtualModuleDir + "/" + packageNameEncode("" + mfName + this.tag + this.name + this.tag) + this.suffix;
};
_proto.writeSync = function writeSync(code, force) {
if (!force && this.inited) return;
if (!this.inited) {
this.inited = true;
}
fs.writeFileSync(this.getPath(), code);
};
_proto.write = function write(code) {
fs.writeFile(this.getPath(), code, function () {});
};
return VirtualModule;
}();
var VIRTUAL_EXPOSES = 'virtual:mf-exposes';
function generateExposes() {
var options = getNormalizeModuleFederationOptions();
return "\n export default {\n " + Object.keys(options.exposes).map(function (key) {
return "\n " + JSON.stringify(key) + ": async () => {\n const importModule = await import(" + JSON.stringify(options.exposes[key]["import"]) + ")\n const exportModule = {}\n Object.assign(exportModule, importModule)\n Object.defineProperty(exportModule, \"__esModule\", {\n value: true,\n enumerable: false\n })\n return exportModule\n }\n ";
}).join(',') + "\n }\n ";
}
var virtualRuntimeInitStatus = new VirtualModule('runtimeInit');
function writeRuntimeInitStatus() {
// Use globalThis singleton to ensure only one initPromise exists
var globalKey = "__mf_init__" + virtualRuntimeInitStatus.getImportId() + "__";
virtualRuntimeInitStatus.writeSync("\n const globalKey = " + JSON.stringify(globalKey) + "\n if (!globalThis[globalKey]) {\n let initResolve, initReject\n const initPromise = new Promise((re, rj) => {\n initResolve = re\n initReject = rj\n })\n globalThis[globalKey] = {\n initPromise,\n initResolve,\n initReject\n }\n }\n module.exports = globalThis[globalKey]\n ");
}
var cacheRemoteMap = {};
var LOAD_REMOTE_TAG = '__loadRemote__';
function getRemoteVirtualModule(remote, command) {
if (!cacheRemoteMap[remote]) {
cacheRemoteMap[remote] = new VirtualModule(remote, LOAD_REMOTE_TAG, '.js');
cacheRemoteMap[remote].writeSync(generateRemotes(remote, command));
}
var virtual = cacheRemoteMap[remote];
return virtual;
}
var usedRemotesMap = {
// remote1: {remote1/App, remote1, remote1/Button}
};
function addUsedRemote(remoteKey, remoteModule) {
if (!usedRemotesMap[remoteKey]) usedRemotesMap[remoteKey] = new Set();
usedRemotesMap[remoteKey].add(remoteModule);
}
function getUsedRemotesMap() {
return usedRemotesMap;
}
function generateRemotes(id, command) {
return "\n const {initPromise} = require(\"" + virtualRuntimeInitStatus.getImportId() + "\")\n const res = initPromise.then(runtime => runtime.loadRemote(" + JSON.stringify(id) + "))\n const exportModule = " + (command !== 'build' ? '/*mf top-level-await placeholder replacement mf*/' : 'await ') + "initPromise.then(_ => res)\n module.exports = exportModule\n ";
}
/**
* Even the resolveId hook cannot interfere with vite pre-build,
* and adding query parameter virtual modules will also fail.
* You can only proxy to the real file through alias
*/
// *** __prebuild__
var preBuildCacheMap = {};
var PREBUILD_TAG = '__prebuild__';
function writePreBuildLibPath(pkg) {
if (!preBuildCacheMap[pkg]) preBuildCacheMap[pkg] = new VirtualModule(pkg, PREBUILD_TAG);
preBuildCacheMap[pkg].writeSync('');
}
function getPreBuildLibImportId(pkg) {
if (!preBuildCacheMap[pkg]) preBuildCacheMap[pkg] = new VirtualModule(pkg, PREBUILD_TAG);
var importId = preBuildCacheMap[pkg].getImportId();
return importId;
}
// *** __loadShare__
var LOAD_SHARE_TAG = '__loadShare__';
var loadShareCacheMap = {};
function getLoadShareModulePath(pkg) {
if (!loadShareCacheMap[pkg]) loadShareCacheMap[pkg] = new VirtualModule(pkg, LOAD_SHARE_TAG, '.js');
var filepath = loadShareCacheMap[pkg].getPath();
return filepath;
}
function writeLoadShareModule(pkg, shareItem, command) {
loadShareCacheMap[pkg].writeSync("\n ;() => import(" + JSON.stringify(getPreBuildLibImportId(pkg)) + ").catch(() => {});\n // dev uses dynamic import to separate chunks\n " + (command !== 'build' ? ";() => import(" + JSON.stringify(pkg) + ").catch(() => {});" : '') + "\n const {initPromise} = require(\"" + virtualRuntimeInitStatus.getImportId() + "\")\n const res = initPromise.then(runtime => runtime.loadShare(" + JSON.stringify(pkg) + ", {\n customShareInfo: {shareConfig:{\n singleton: " + shareItem.shareConfig.singleton + ",\n strictVersion: " + shareItem.shareConfig.strictVersion + ",\n requiredVersion: " + JSON.stringify(shareItem.shareConfig.requiredVersion) + "\n }}\n }))\n const exportModule = " + (command !== 'build' ? '/*mf top-level-await placeholder replacement mf*/' : 'await ') + "res.then(factory => factory())\n module.exports = exportModule\n ");
}
var usedShares = new Set();
function getUsedShares() {
return usedShares;
}
function addUsedShares(pkg) {
usedShares.add(pkg);
}
// *** Expose locally provided shared modules here
new VirtualModule('localSharedImportMap');
function getLocalSharedImportMapPath() {
return getLocalSharedImportMapPath_temp();
// return localSharedImportMapModule.getPath()
}
var prevSharedCount;
function writeLocalSharedImportMap() {
var sharedCount = getUsedShares().size;
if (prevSharedCount !== sharedCount) {
prevSharedCount = sharedCount;
writeLocalSharedImportMap_temp(generateLocalSharedImportMap());
// localSharedImportMapModule.writeSync(generateLocalSharedImportMap(), true)
}
}
function generateLocalSharedImportMap() {
var options = getNormalizeModuleFederationOptions();
return "\n import {loadShare} from \"@module-federation/runtime\";\n const importMap = {\n " + Array.from(getUsedShares()).sort().map(function (pkg) {
var shareItem = getNormalizeShareItem(pkg);
return "\n " + JSON.stringify(pkg) + ": async () => {\n " + ((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) + "\");\n return pkg;") + "\n }\n ";
}).join(',') + "\n }\n const usedShared = {\n " + Array.from(getUsedShares()).sort().map(function (key) {
var shareItem = getNormalizeShareItem(key);
if (!shareItem) return null;
return "\n " + JSON.stringify(key) + ": {\n name: " + JSON.stringify(key) + ",\n version: " + JSON.stringify(shareItem.version) + ",\n scope: [" + JSON.stringify(shareItem.scope) + "],\n loaded: false,\n from: " + JSON.stringify(options.name) + ",\n async get () {\n if (" + (shareItem.shareConfig["import"] === false) + ") {\n throw new Error(`Shared module '${" + JSON.stringify(key) + "}' must be provided by host`);\n }\n usedShared[" + JSON.stringify(key) + "].loaded = true\n const {" + JSON.stringify(key) + ": pkgDynamicImport} = importMap\n const res = await pkgDynamicImport()\n const exportModule = {...res}\n // All npm packages pre-built by vite will be converted to esm\n Object.defineProperty(exportModule, \"__esModule\", {\n value: true,\n enumerable: false\n })\n return function () {\n return exportModule\n }\n },\n shareConfig: {\n singleton: " + shareItem.shareConfig.singleton + ",\n requiredVersion: " + JSON.stringify(shareItem.shareConfig.requiredVersion) + ",\n " + (shareItem.shareConfig["import"] === false ? 'import: false,' : '') + "\n }\n }\n ";
}).filter(function (x) {
return x !== null;
}).join(',') + "\n }\n const usedRemotes = [" + Object.keys(getUsedRemotesMap()).map(function (key) {
var _JSON$stringify;
var remote = options.remotes[key];
if (!remote) return null;
return "\n {\n entryGlobalName: " + JSON.stringify(remote.entryGlobalName) + ",\n name: " + JSON.stringify(remote.name) + ",\n type: " + JSON.stringify(remote.type) + ",\n entry: " + JSON.stringify(remote.entry) + ",\n shareScope: " + ((_JSON$stringify = JSON.stringify(remote.shareScope)) != null ? _JSON$stringify : 'default') + ",\n }\n ";
}).filter(function (x) {
return x !== null;
}).join(',') + "\n ]\n export {\n usedShared,\n usedRemotes\n }\n ";
}
var REMOTE_ENTRY_ID = 'virtual:mf-REMOTE_ENTRY_ID';
function generateRemoteEntry(options) {
var pluginImportNames = options.runtimePlugins.map(function (p, i) {
return ["$runtimePlugin_" + i, "import $runtimePlugin_" + i + " from \"" + p + "\";"];
});
return "\n import {init as runtimeInit, loadRemote} from \"@module-federation/runtime\";\n " + pluginImportNames.map(function (item) {
return item[1];
}).join('\n') + "\n import exposesMap from \"" + VIRTUAL_EXPOSES + "\"\n import {usedShared, usedRemotes} from \"" + getLocalSharedImportMapPath() + "\"\n import {\n initResolve\n } from \"" + virtualRuntimeInitStatus.getImportId() + "\"\n const initTokens = {}\n const shareScopeName = " + JSON.stringify(options.shareScope) + "\n const mfName = " + JSON.stringify(options.name) + "\n async function init(shared = {}, initScope = []) {\n const initRes = runtimeInit({\n name: mfName,\n remotes: usedRemotes,\n shared: usedShared,\n plugins: [" + pluginImportNames.map(function (item) {
return item[0] + "()";
}).join(', ') + "],\n " + (options.shareStrategy ? "shareStrategy: '" + options.shareStrategy + "'" : '') + "\n });\n // handling circular init calls\n var initToken = initTokens[shareScopeName];\n if (!initToken)\n initToken = initTokens[shareScopeName] = { from: mfName };\n if (initScope.indexOf(initToken) >= 0) return;\n initScope.push(initToken);\n initRes.initShareScopeMap('" + options.shareScope + "', shared);\n try {\n await Promise.all(await initRes.initializeSharing('" + options.shareScope + "', {\n strategy: '" + options.shareStrategy + "',\n from: \"build\",\n initScope\n }));\n } catch (e) {\n console.error(e)\n }\n initResolve(initRes)\n return initRes\n }\n\n function getExposes(moduleName) {\n if (!(moduleName in exposesMap)) throw new Error(`Module ${moduleName} does not exist in container.`)\n return (exposesMap[moduleName])().then(res => () => res)\n }\n export {\n init,\n getExposes as get\n }\n ";
}
/**
* Inject entry file, automatically init when used as host,
* and will not inject remoteEntry
*/
var HOST_AUTO_INIT_TAG = '__H_A_I__';
var hostAutoInitModule = new VirtualModule('hostAutoInit', HOST_AUTO_INIT_TAG);
function writeHostAutoInit() {
hostAutoInitModule.writeSync("\n const remoteEntryPromise = import(\"" + REMOTE_ENTRY_ID + "\")\n // __tla only serves as a hack for vite-plugin-top-level-await.\n Promise.resolve(remoteEntryPromise)\n .then(remoteEntry => {\n return Promise.resolve(remoteEntry.__tla)\n .then(remoteEntry.init).catch(remoteEntry.init)\n })\n ");
}
function getHostAutoInitImportId() {
return hostAutoInitModule.getImportId();
}
function getHostAutoInitPath() {
return hostAutoInitModule.getPath();
}
function initVirtualModules() {
writeLocalSharedImportMap();
writeHostAutoInit();
writeRuntimeInitStatus();
}
var ASSET_TYPES = ['js', 'css'];
var LOAD_TIMINGS = ['sync', 'async'];
var JS_EXTENSIONS = ['.ts', '.tsx', '.jsx', '.mjs', '.cjs'];
/**
* Creates an empty asset map structure for tracking JS and CSS assets
* @returns Initialized asset map with sync/async arrays for JS and CSS
*/
var createEmptyAssetMap = function createEmptyAssetMap() {
return {
js: {
sync: [],
async: []
},
css: {
sync: [],
async: []
}
};
};
/**
* Tracks an asset in the preload map with deduplication
* @param map - The preload map to update
* @param key - The module key to track under
* @param fileName - The asset filename to track
* @param isAsync - Whether the asset is loaded async
* @param type - The asset type ('js' or 'css')
*/
var trackAsset = function trackAsset(map, key, fileName, isAsync, type) {
if (!map[key]) {
map[key] = createEmptyAssetMap();
}
var target = isAsync ? map[key][type].async : map[key][type].sync;
if (!target.includes(fileName)) {
target.push(fileName);
}
};
/**
* Checks if a file is a CSS file by extension
* @param fileName - The filename to check
* @returns True if file has a CSS extension (.css, .scss, .less)
*/
var isCSSFile = function isCSSFile(fileName) {
return fileName.endsWith('.css') || fileName.endsWith('.scss') || fileName.endsWith('.less');
};
/**
* Collects all CSS assets from the bundle
* @param bundle - The Rollup output bundle
* @returns Set of CSS asset filenames
*/
var collectCssAssets = function collectCssAssets(bundle) {
var cssAssets = new Set();
for (var _i = 0, _Object$entries = Object.entries(bundle); _i < _Object$entries.length; _i++) {
var _Object$entries$_i = _Object$entries[_i],
fileName = _Object$entries$_i[0],
fileData = _Object$entries$_i[1];
if (fileData.type === 'asset' && isCSSFile(fileName)) {
cssAssets.add(fileName);
}
}
return cssAssets;
};
/**
* Processes module assets and tracks them in the files map
* @param bundle - The Rollup output bundle
* @param filesMap - The preload map to populate
* @param moduleMatcher - Function that matches module paths to keys
*/
var processModuleAssets = function processModuleAssets(bundle, filesMap, moduleMatcher) {
for (var _i2 = 0, _Object$entries2 = Object.entries(bundle); _i2 < _Object$entries2.length; _i2++) {
var _Object$entries2$_i = _Object$entries2[_i2],
fileName = _Object$entries2$_i[0],
fileData = _Object$entries2$_i[1];
if (fileData.type !== 'chunk') continue;
if (!fileData.modules) continue;
for (var _i3 = 0, _Object$keys = Object.keys(fileData.modules); _i3 < _Object$keys.length; _i3++) {
var modulePath = _Object$keys[_i3];
var matchKey = moduleMatcher(modulePath);
if (!matchKey) continue;
// Track main JS chunk
trackAsset(filesMap, matchKey, fileName, false, 'js');
// Handle dynamic imports
if (fileData.dynamicImports) {
for (var _iterator = _createForOfIteratorHelperLoose(fileData.dynamicImports), _step; !(_step = _iterator()).done;) {
var dynamicImport = _step.value;
var importData = bundle[dynamicImport];
if (!importData) continue;
var isCss = isCSSFile(dynamicImport);
trackAsset(filesMap, matchKey, dynamicImport, true, isCss ? 'css' : 'js');
}
}
}
}
};
/**
* Deduplicates assets in the files map
* @param filesMap - The preload map to deduplicate
* @returns New deduplicated preload map
*/
var deduplicateAssets = function deduplicateAssets(filesMap) {
var result = {};
for (var _i4 = 0, _Object$entries3 = Object.entries(filesMap); _i4 < _Object$entries3.length; _i4++) {
var _Object$entries3$_i = _Object$entries3[_i4],
key = _Object$entries3$_i[0],
assetMaps = _Object$entries3$_i[1];
result[key] = createEmptyAssetMap();
for (var _i5 = 0, _ASSET_TYPES = ASSET_TYPES; _i5 < _ASSET_TYPES.length; _i5++) {
var type = _ASSET_TYPES[_i5];
for (var _i6 = 0, _LOAD_TIMINGS = LOAD_TIMINGS; _i6 < _LOAD_TIMINGS.length; _i6++) {
var timing = _LOAD_TIMINGS[_i6];
result[key][type][timing] = Array.from(new Set(assetMaps[type][timing]));
}
}
}
return result;
};
/**
* Builds a mapping between module files and their share keys
* @param shareKeys - Set of share keys to map
* @param resolveFn - Function to resolve module paths
* @returns Map of file paths to their corresponding share keys
*/
var buildFileToShareKeyMap = function buildFileToShareKeyMap(shareKeys, resolveFn) {
try {
var fileToShareKey = new Map();
return Promise.resolve(Promise.all(Array.from(shareKeys).map(function (shareKey) {
return resolveFn(getPreBuildLibImportId(shareKey)).then(function (resolution) {
var _resolution$id;
return {
shareKey: shareKey,
file: resolution == null || (_resolution$id = resolution.id) == null ? void 0 : _resolution$id.split('?')[0]
};
})["catch"](function () {
return null;
});
}))).then(function (resolutions) {
for (var _iterator2 = _createForOfIteratorHelperLoose(resolutions), _step2; !(_step2 = _iterator2()).done;) {
var resolution = _step2.value;
if (resolution != null && resolution.file) {
fileToShareKey.set(resolution.file, resolution.shareKey);
}
}
return fileToShareKey;
});
} catch (e) {
return Promise.reject(e);
}
};
/**
* Resolves the public path for remote entries
* @param options - Module Federation options
* @param viteBase - Vite's base config value
* @param originalBase - Original base config before any transformations
* @returns The resolved public path
*/
function resolvePublicPath(options, viteBase, originalBase) {
// Use explicitly set publicPath if provided
if (options.publicPath) {
return options.publicPath;
}
// Handle empty original base case
if (originalBase === '') {
return 'auto';
}
// Use viteBase if available, ensuring it ends with a slash
if (viteBase) {
return viteBase.replace(/\/?$/, '/');
}
// Fallback to auto if no base is specified
return 'auto';
}
var Manifest = function Manifest() {
var mfOptions = getNormalizeModuleFederationOptions();
var name = mfOptions.name,
filename = mfOptions.filename,
getPublicPath = mfOptions.getPublicPath,
manifestOptions = mfOptions.manifest;
var mfManifestName = '';
if (manifestOptions === true) {
mfManifestName = 'mf-manifest.json';
}
if (typeof manifestOptions !== 'boolean') {
mfManifestName = path__namespace.join((manifestOptions == null ? void 0 : manifestOptions.filePath) || '', (manifestOptions == null ? void 0 : manifestOptions.fileName) || '');
}
var root;
var remoteEntryFile;
var publicPath;
var _command;
var _originalConfigBase;
var viteConfig;
/**
* Adds global CSS assets to all module exports
* @param filesMap - The preload map to update
* @param cssAssets - Set of CSS asset filenames to add
*/
var addCssAssetsToAllExports = function addCssAssetsToAllExports(filesMap, cssAssets) {
Object.keys(filesMap).forEach(function (key) {
cssAssets.forEach(function (cssAsset) {
trackAsset(filesMap, key, cssAsset, false, 'css');
});
});
};
return [{
name: 'module-federation-manifest',
apply: 'serve',
/**
* Stores resolved Vite config for later use
*/
/**
* Finalizes configuration after all plugins are resolved
* @param config - Fully resolved Vite config
*/
configResolved: function configResolved(config) {
viteConfig = config;
},
/**
* Configures dev server middleware to handle manifest requests
* @param server - Vite dev server instance
*/
configureServer: function configureServer(server) {
server.middlewares.use(function (req, res, next) {
var _req$url;
if (!mfManifestName) {
next();
return;
}
if (((_req$url = req.url) == null ? void 0 : _req$url.replace(/\?.*/, '')) === (viteConfig.base + mfManifestName).replace(/^\/?/, '/')) {
res.setHeader('Content-Type', 'application/json');
res.setHeader('Access-Control-Allow-Origin', '*');
res.end(JSON.stringify(_extends({}, generateMFManifest({}), {
id: name,
name: name,
metaData: {
name: name,
type: 'app',
buildInfo: {
buildVersion: '1.0.0',
buildName: name
},
remoteEntry: {
name: filename,
path: '',
type: 'module'
},
ssrRemoteEntry: {
name: filename,
path: '',
type: 'module'
},
types: {
path: '',
name: ''
},
globalName: name,
pluginVersion: '0.2.5',
publicPath: publicPath
}
})));
} else {
next();
}
});
}
}, {
name: 'module-federation-manifest',
enforce: 'post',
/**
* Initial plugin configuration
* @param config - Vite config object
* @param command - Current Vite command (serve/build)
*/
config: function config(_config, _ref) {
var command = _ref.command;
if (!_config.build) _config.build = {};
if (!_config.build.manifest) {
_config.build.manifest = _config.build.manifest || !!manifestOptions;
}
_command = command;
_originalConfigBase = _config.base;
},
configResolved: function configResolved(config) {
root = config.root;
var base = config.base;
if (_command === 'serve') {
base = (config.server.origin || '') + config.base;
}
publicPath = resolvePublicPath(mfOptions, base, _originalConfigBase);
},
/**
* Generates the module federation manifest file
* @param options - Rollup output options
* @param bundle - Generated bundle assets
*/
generateBundle: function generateBundle(options, bundle) {
try {
var _this = this;
if (!mfManifestName) return Promise.resolve();
var filesMap = {};
// First pass: Find remoteEntry file
for (var _i = 0, _Object$entries = Object.entries(bundle); _i < _Object$entries.length; _i++) {
var _Object$entries$_i = _Object$entries[_i],
_ = _Object$entries$_i[0],
fileData = _Object$entries$_i[1];
if (mfOptions.filename.replace(/[\[\]]/g, '_').replace(/\.[^/.]+$/, '') === fileData.name || fileData.name === 'remoteEntry') {
remoteEntryFile = fileData.fileName;
break; // We can break early since we only need to find remoteEntry once
}
}
// Second pass: Collect all CSS assets
var allCssAssets = mfOptions.bundleAllCSS ? collectCssAssets(bundle) : new Set();
var exposesModules = Object.keys(mfOptions.exposes).map(function (item) {
return mfOptions.exposes[item]["import"];
});
// Process exposed modules
processModuleAssets(bundle, filesMap, function (modulePath) {
var absoluteModulePath = path__namespace.resolve(root, modulePath);
return exposesModules.find(function (exposeModule) {
var exposePath = path__namespace.resolve(root, exposeModule);
// First try exact path match
if (absoluteModulePath === exposePath) {
return true;
}
// Then try path match without known extensions
var getPathWithoutKnownExt = function getPathWithoutKnownExt(filePath) {
var ext = path__namespace.extname(filePath);
return JS_EXTENSIONS.includes(ext) ? path__namespace.join(path__namespace.dirname(filePath), path__namespace.basename(filePath, ext)) : filePath;
};
var modulePathNoExt = getPathWithoutKnownExt(absoluteModulePath);
var exposePathNoExt = getPathWithoutKnownExt(exposePath);
return modulePathNoExt === exposePathNoExt;
});
});
// Pr