@module-federation/enhanced
Version:
This package provides enhanced features for module federation.
240 lines (238 loc) • 10.7 kB
JavaScript
Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: 'Module' } });
const require_runtime = require('../../../_virtual/_rolldown/runtime.js');
const require_lib_container_options = require('../../container/options.js');
const require_lib_sharing_ConsumeSharedPlugin = require('../ConsumeSharedPlugin.js');
const require_lib_sharing_tree_shaking_SharedUsedExportsOptimizerPlugin = require('./SharedUsedExportsOptimizerPlugin.js');
const require_lib_sharing_tree_shaking_CollectSharedEntryPlugin = require('./CollectSharedEntryPlugin.js');
const require_lib_sharing_tree_shaking_SharedContainerPlugin_SharedContainerPlugin = require('./SharedContainerPlugin/SharedContainerPlugin.js');
const require_lib_sharing_tree_shaking_IndependentSharedRuntimeModule = require('./IndependentSharedRuntimeModule.js');
let _module_federation_sdk = require("@module-federation/sdk");
let path = require("path");
path = require_runtime.__toESM(path);
let fs = require("fs");
fs = require_runtime.__toESM(fs);
//#region src/lib/sharing/tree-shaking/IndependentSharedPlugin.ts
const IGNORED_ENTRY = "ignored-entry";
const filterPlugin = (plugin, treeShakingSharedExcludePlugins = []) => {
if (!plugin) return true;
const pluginName = plugin["name"] || plugin["constructor"]?.name;
if (!pluginName) return true;
return ![
"IndependentSharedPlugin",
"ModuleFederationPlugin",
"SharedUsedExportsOptimizerPlugin",
"HtmlWebpackPlugin",
"TreeShakingSharedPlugin",
...treeShakingSharedExcludePlugins
].includes(pluginName);
};
const resolveOutputDir = (outputDir, shareName) => {
return shareName ? path.join(outputDir, (0, _module_federation_sdk.encodeName)(shareName)) : outputDir;
};
var IndependentSharedPlugin = class {
constructor(options) {
this.buildAssets = {};
this.name = "IndependentSharedPlugin";
const { outputDir, plugins, treeShaking, shared, name, manifest, injectTreeShakingUsedExports, library, treeShakingSharedExcludePlugins } = options;
this.shared = shared;
this.treeShakingSharedExcludePlugins = treeShakingSharedExcludePlugins;
this.mfName = name;
this.outputDir = outputDir || "independent-packages";
this.plugins = plugins || [];
this.treeShaking = treeShaking;
this.manifest = manifest;
this.injectTreeShakingUsedExports = injectTreeShakingUsedExports ?? true;
this.library = library;
this.sharedOptions = require_lib_container_options.parseOptions(shared, (item, key) => {
if (typeof item !== "string") throw new Error(`Unexpected array in shared configuration for key "${key}"`);
return item === key || !(0, _module_federation_sdk.isRequiredVersion)(item) ? { import: item } : {
import: key,
requiredVersion: item
};
}, (item) => {
return item;
});
}
static {
this.IndependentShareBuildAssetsFilename = "independent-share-build-assets.json";
}
apply(compiler) {
const { manifest } = this;
let runCount = 0;
compiler.hooks.beforeRun.tapPromise("IndependentSharedPlugin", async () => {
if (runCount) return;
await this.createIndependentCompilers(compiler);
runCount++;
});
compiler.hooks.watchRun.tapPromise("IndependentSharedPlugin", async () => {
if (runCount) return;
await this.createIndependentCompilers(compiler);
runCount++;
});
compiler.hooks.thisCompilation.tap("IndependentSharedPlugin", (compilation) => {
compilation.hooks.additionalTreeRuntimeRequirements.tap("OptimizeDependencyReferencedExportsPlugin", (chunk) => {
compilation.addRuntimeModule(chunk, new require_lib_sharing_tree_shaking_IndependentSharedRuntimeModule.default(this.buildAssets, this.library?.type || "global"));
});
if (!manifest) return;
compilation.hooks.processAssets.tapPromise({
name: "injectReferenceExports",
stage: compilation.constructor.PROCESS_ASSETS_STAGE_OPTIMIZE_TRANSFER
}, async () => {
const stats = compilation.getAsset(_module_federation_sdk.StatsFileName);
if (!stats) return;
const statsContent = JSON.parse(stats.source.source().toString());
const { shared } = statsContent;
Object.entries(this.buildAssets).forEach(([key, item]) => {
const targetShared = shared.find((s) => s.name === key);
if (!targetShared) return;
item.forEach(([entry, version, globalName]) => {
if (version === targetShared.version) {
targetShared.fallback = entry;
targetShared.fallbackName = globalName;
}
});
});
compilation.updateAsset(_module_federation_sdk.StatsFileName, new compiler.webpack.sources.RawSource(JSON.stringify(statsContent)));
});
});
}
createEntry(context) {
const { sharedOptions } = this;
const entryContent = sharedOptions.reduce((acc, cur, index) => {
return `${acc}import shared_${index} from '${cur[0]}';\n`;
}, "");
const entryPath = path.resolve(context, "node_modules", ".federation", "shared-entry.js");
if (!fs.existsSync(path.dirname(entryPath))) fs.mkdirSync(path.dirname(entryPath), { recursive: true });
fs.writeFileSync(entryPath, entryContent);
return entryPath;
}
async createIndependentCompilers(parentCompiler) {
const { sharedOptions, outputDir } = this;
console.log("Start building shared fallback resources ...");
const shareRequestsMap = await this.createIndependentCompiler(parentCompiler);
await Promise.all(sharedOptions.map(async ([shareName, shareConfig]) => {
if (!shareConfig.treeShaking) return;
const shareRequests = shareRequestsMap[shareName].requests;
await Promise.all(shareRequests.map(async ([request, version]) => {
const sharedConfig = sharedOptions.find(([name]) => name === shareName)?.[1];
const [shareFileName, globalName, sharedVersion] = await this.createIndependentCompiler(parentCompiler, {
shareRequestsMap,
currentShare: {
shareName,
version,
request,
independentShareFileName: sharedConfig?.treeShaking?.filename
}
});
if (typeof shareFileName === "string") {
this.buildAssets[shareName] ||= [];
this.buildAssets[shareName].push([
path.join(resolveOutputDir(outputDir, shareName), shareFileName),
sharedVersion,
globalName
]);
}
}));
}));
console.log("All shared fallback have been compiled successfully");
}
async createIndependentCompiler(parentCompiler, extraOptions) {
const { treeShaking, plugins, outputDir, sharedOptions, mfName, library, treeShakingSharedExcludePlugins } = this;
const outputDirWithShareName = resolveOutputDir(outputDir, extraOptions?.currentShare?.shareName || "");
const parentConfig = parentCompiler.options;
const finalPlugins = [];
let extraPlugin;
if (!extraOptions) extraPlugin = new require_lib_sharing_tree_shaking_CollectSharedEntryPlugin.default({ sharedOptions });
else {
extraPlugin = new require_lib_sharing_tree_shaking_SharedContainerPlugin_SharedContainerPlugin.default({
mfName: `${mfName}_${treeShaking ? "t" : "f"}`,
library,
...extraOptions.currentShare
});
(parentConfig.plugins || []).forEach((plugin) => {
if (plugin !== void 0 && typeof plugin !== "string" && filterPlugin(plugin, treeShakingSharedExcludePlugins)) finalPlugins.push(plugin);
});
plugins.forEach((plugin) => {
finalPlugins.push(plugin);
});
finalPlugins.push(new require_lib_sharing_ConsumeSharedPlugin.default({ consumes: sharedOptions.filter(([key, options]) => extraOptions?.currentShare.shareName !== (options.shareKey || key)).map(([key, options]) => ({ [key]: {
import: !extraOptions ? options.import : false,
shareKey: options.shareKey || key,
shareScope: options.shareScope,
requiredVersion: options.requiredVersion,
strictVersion: options.strictVersion,
singleton: options.singleton,
packageName: options.packageName,
eager: options.eager
} })) }));
if (treeShaking) finalPlugins.push(new require_lib_sharing_tree_shaking_SharedUsedExportsOptimizerPlugin.default(sharedOptions, this.injectTreeShakingUsedExports, [IGNORED_ENTRY], this.manifest));
}
finalPlugins.push(extraPlugin);
const fullOutputDir = path.resolve(parentCompiler.outputPath, outputDirWithShareName);
const compilerConfig = {
...parentConfig,
mode: parentConfig.mode || "development",
ignoreWarnings: [],
entry: { [IGNORED_ENTRY]: this.createEntry(parentCompiler.context) },
output: {
path: fullOutputDir,
clean: true,
publicPath: parentConfig.output?.publicPath || "auto"
},
plugins: finalPlugins,
optimization: {
...parentConfig.optimization,
splitChunks: false
}
};
const compiler = parentCompiler.webpack.webpack(compilerConfig);
if (parentCompiler.inputFileSystem) compiler.inputFileSystem = parentCompiler.inputFileSystem;
if (parentCompiler.outputFileSystem) compiler.outputFileSystem = parentCompiler.outputFileSystem;
if (parentCompiler.intermediateFileSystem) compiler.intermediateFileSystem = parentCompiler.intermediateFileSystem;
const { currentShare, shareRequestsMap } = extraOptions || {};
return new Promise((resolve, reject) => {
compiler.run((err, stats) => {
const shareName = currentShare?.shareName || "unknown shared package";
const hasStatsErrors = !!stats && (stats.hasErrors?.() || stats.toJson?.()?.errors?.length > 0);
if (err || hasStatsErrors) {
const lines = [];
if (err) {
const errMsg = err && (err.stack || err.message) || String(err);
lines.push(`Compiler error: ${errMsg}`);
}
if (stats?.toJson) {
const json = stats.toJson({
all: false,
errors: true,
warnings: false,
errorDetails: true,
moduleTrace: true
});
const errors = json && json.errors || [];
if (errors.length) {
lines.push(`Webpack errors (${errors.length}):`);
const max = 5;
for (let i = 0; i < Math.min(errors.length, max); i++) {
const e = errors[i];
const where = e.moduleName || e.file || e.moduleIdentifier || "";
const loc = e.loc ? ` @ ${e.loc}` : "";
const msg = e.message || e.details || String(e);
lines.push(` [${i + 1}] ${where}${loc}`);
lines.push(` ${msg}`);
}
if (errors.length > max) lines.push(` ... and ${errors.length - max} more errors`);
}
}
console.error(`❌ Shared "${shareName}" compilation failed\n${lines.join("\n")}`);
reject(err || /* @__PURE__ */ new Error(`Shared "${shareName}" compilation failed`));
return;
}
shareRequestsMap && console.log(`Shared "${shareName}" compilation succeeded`);
resolve(extraPlugin.getData());
});
});
}
};
//#endregion
exports.default = IndependentSharedPlugin;
//# sourceMappingURL=IndependentSharedPlugin.js.map