UNPKG

@module-federation/enhanced

Version:

This package provides enhanced features for module federation.

240 lines (238 loc) 10.7 kB
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