| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946 | 
							- /*
 
- 	MIT License http://www.opensource.org/licenses/mit-license.php
 
- 	Author Tobias Koppers @sokra
 
- */
 
- "use strict";
 
- const asyncLib = require("neo-async");
 
- const ChunkGraph = require("../ChunkGraph");
 
- const ModuleGraph = require("../ModuleGraph");
 
- const { JS_TYPE } = require("../ModuleSourceTypesConstants");
 
- const { STAGE_DEFAULT } = require("../OptimizationStages");
 
- const HarmonyImportDependency = require("../dependencies/HarmonyImportDependency");
 
- const { compareModulesByIdentifier } = require("../util/comparators");
 
- const {
 
- 	filterRuntime,
 
- 	intersectRuntime,
 
- 	mergeRuntime,
 
- 	mergeRuntimeOwned,
 
- 	runtimeToString
 
- } = require("../util/runtime");
 
- const ConcatenatedModule = require("./ConcatenatedModule");
 
- /** @typedef {import("../Compilation")} Compilation */
 
- /** @typedef {import("../Compiler")} Compiler */
 
- /** @typedef {import("../Module")} Module */
 
- /** @typedef {import("../Module").BuildInfo} BuildInfo */
 
- /** @typedef {import("../RequestShortener")} RequestShortener */
 
- /** @typedef {import("../util/runtime").RuntimeSpec} RuntimeSpec */
 
- /**
 
-  * @typedef {object} Statistics
 
-  * @property {number} cached
 
-  * @property {number} alreadyInConfig
 
-  * @property {number} invalidModule
 
-  * @property {number} incorrectChunks
 
-  * @property {number} incorrectDependency
 
-  * @property {number} incorrectModuleDependency
 
-  * @property {number} incorrectChunksOfImporter
 
-  * @property {number} incorrectRuntimeCondition
 
-  * @property {number} importerFailed
 
-  * @property {number} added
 
-  */
 
- /**
 
-  * @param {string} msg message
 
-  * @returns {string} formatted message
 
-  */
 
- const formatBailoutReason = msg => `ModuleConcatenation bailout: ${msg}`;
 
- const PLUGIN_NAME = "ModuleConcatenationPlugin";
 
- class ModuleConcatenationPlugin {
 
- 	/**
 
- 	 * Apply the plugin
 
- 	 * @param {Compiler} compiler the compiler instance
 
- 	 * @returns {void}
 
- 	 */
 
- 	apply(compiler) {
 
- 		const { _backCompat: backCompat } = compiler;
 
- 		compiler.hooks.compilation.tap(PLUGIN_NAME, compilation => {
 
- 			if (compilation.moduleMemCaches) {
 
- 				throw new Error(
 
- 					"optimization.concatenateModules can't be used with cacheUnaffected as module concatenation is a global effect"
 
- 				);
 
- 			}
 
- 			const moduleGraph = compilation.moduleGraph;
 
- 			/** @type {Map<Module, string | ((requestShortener: RequestShortener) => string)>} */
 
- 			const bailoutReasonMap = new Map();
 
- 			/**
 
- 			 * @param {Module} module the module
 
- 			 * @param {string | ((requestShortener: RequestShortener) => string)} reason the reason
 
- 			 */
 
- 			const setBailoutReason = (module, reason) => {
 
- 				setInnerBailoutReason(module, reason);
 
- 				moduleGraph
 
- 					.getOptimizationBailout(module)
 
- 					.push(
 
- 						typeof reason === "function"
 
- 							? rs => formatBailoutReason(reason(rs))
 
- 							: formatBailoutReason(reason)
 
- 					);
 
- 			};
 
- 			/**
 
- 			 * @param {Module} module the module
 
- 			 * @param {string | ((requestShortener: RequestShortener) => string)} reason the reason
 
- 			 */
 
- 			const setInnerBailoutReason = (module, reason) => {
 
- 				bailoutReasonMap.set(module, reason);
 
- 			};
 
- 			/**
 
- 			 * @param {Module} module the module
 
- 			 * @param {RequestShortener} requestShortener the request shortener
 
- 			 * @returns {string | ((requestShortener: RequestShortener) => string) | undefined} the reason
 
- 			 */
 
- 			const getInnerBailoutReason = (module, requestShortener) => {
 
- 				const reason = bailoutReasonMap.get(module);
 
- 				if (typeof reason === "function") return reason(requestShortener);
 
- 				return reason;
 
- 			};
 
- 			/**
 
- 			 * @param {Module} module the module
 
- 			 * @param {Module | ((requestShortener: RequestShortener) => string)} problem the problem
 
- 			 * @returns {(requestShortener: RequestShortener) => string} the reason
 
- 			 */
 
- 			const formatBailoutWarning = (module, problem) => requestShortener => {
 
- 				if (typeof problem === "function") {
 
- 					return formatBailoutReason(
 
- 						`Cannot concat with ${module.readableIdentifier(
 
- 							requestShortener
 
- 						)}: ${problem(requestShortener)}`
 
- 					);
 
- 				}
 
- 				const reason = getInnerBailoutReason(module, requestShortener);
 
- 				const reasonWithPrefix = reason ? `: ${reason}` : "";
 
- 				if (module === problem) {
 
- 					return formatBailoutReason(
 
- 						`Cannot concat with ${module.readableIdentifier(
 
- 							requestShortener
 
- 						)}${reasonWithPrefix}`
 
- 					);
 
- 				}
 
- 				return formatBailoutReason(
 
- 					`Cannot concat with ${module.readableIdentifier(
 
- 						requestShortener
 
- 					)} because of ${problem.readableIdentifier(
 
- 						requestShortener
 
- 					)}${reasonWithPrefix}`
 
- 				);
 
- 			};
 
- 			compilation.hooks.optimizeChunkModules.tapAsync(
 
- 				{
 
- 					name: PLUGIN_NAME,
 
- 					stage: STAGE_DEFAULT
 
- 				},
 
- 				(allChunks, modules, callback) => {
 
- 					const logger = compilation.getLogger(
 
- 						"webpack.ModuleConcatenationPlugin"
 
- 					);
 
- 					const { chunkGraph, moduleGraph } = compilation;
 
- 					const relevantModules = [];
 
- 					const possibleInners = new Set();
 
- 					const context = {
 
- 						chunkGraph,
 
- 						moduleGraph
 
- 					};
 
- 					logger.time("select relevant modules");
 
- 					for (const module of modules) {
 
- 						let canBeRoot = true;
 
- 						let canBeInner = true;
 
- 						const bailoutReason = module.getConcatenationBailoutReason(context);
 
- 						if (bailoutReason) {
 
- 							setBailoutReason(module, bailoutReason);
 
- 							continue;
 
- 						}
 
- 						// Must not be an async module
 
- 						if (moduleGraph.isAsync(module)) {
 
- 							setBailoutReason(module, "Module is async");
 
- 							continue;
 
- 						}
 
- 						// Must be in strict mode
 
- 						if (!(/** @type {BuildInfo} */ (module.buildInfo).strict)) {
 
- 							setBailoutReason(module, "Module is not in strict mode");
 
- 							continue;
 
- 						}
 
- 						// Module must be in any chunk (we don't want to do useless work)
 
- 						if (chunkGraph.getNumberOfModuleChunks(module) === 0) {
 
- 							setBailoutReason(module, "Module is not in any chunk");
 
- 							continue;
 
- 						}
 
- 						// Exports must be known (and not dynamic)
 
- 						const exportsInfo = moduleGraph.getExportsInfo(module);
 
- 						const relevantExports = exportsInfo.getRelevantExports(undefined);
 
- 						const unknownReexports = relevantExports.filter(
 
- 							exportInfo =>
 
- 								exportInfo.isReexport() && !exportInfo.getTarget(moduleGraph)
 
- 						);
 
- 						if (unknownReexports.length > 0) {
 
- 							setBailoutReason(
 
- 								module,
 
- 								`Reexports in this module do not have a static target (${Array.from(
 
- 									unknownReexports,
 
- 									exportInfo =>
 
- 										`${
 
- 											exportInfo.name || "other exports"
 
- 										}: ${exportInfo.getUsedInfo()}`
 
- 								).join(", ")})`
 
- 							);
 
- 							continue;
 
- 						}
 
- 						// Root modules must have a static list of exports
 
- 						const unknownProvidedExports = relevantExports.filter(
 
- 							exportInfo => exportInfo.provided !== true
 
- 						);
 
- 						if (unknownProvidedExports.length > 0) {
 
- 							setBailoutReason(
 
- 								module,
 
- 								`List of module exports is dynamic (${Array.from(
 
- 									unknownProvidedExports,
 
- 									exportInfo =>
 
- 										`${
 
- 											exportInfo.name || "other exports"
 
- 										}: ${exportInfo.getProvidedInfo()} and ${exportInfo.getUsedInfo()}`
 
- 								).join(", ")})`
 
- 							);
 
- 							canBeRoot = false;
 
- 						}
 
- 						// Module must not be an entry point
 
- 						if (chunkGraph.isEntryModule(module)) {
 
- 							setInnerBailoutReason(module, "Module is an entry point");
 
- 							canBeInner = false;
 
- 						}
 
- 						if (moduleGraph.isDeferred(module)) {
 
- 							setInnerBailoutReason(module, "Module is deferred");
 
- 							canBeInner = false;
 
- 						}
 
- 						if (canBeRoot) relevantModules.push(module);
 
- 						if (canBeInner) possibleInners.add(module);
 
- 					}
 
- 					logger.timeEnd("select relevant modules");
 
- 					logger.debug(
 
- 						`${relevantModules.length} potential root modules, ${possibleInners.size} potential inner modules`
 
- 					);
 
- 					// sort by depth
 
- 					// modules with lower depth are more likely suited as roots
 
- 					// this improves performance, because modules already selected as inner are skipped
 
- 					logger.time("sort relevant modules");
 
- 					relevantModules.sort(
 
- 						(a, b) =>
 
- 							/** @type {number} */ (moduleGraph.getDepth(a)) -
 
- 							/** @type {number} */ (moduleGraph.getDepth(b))
 
- 					);
 
- 					logger.timeEnd("sort relevant modules");
 
- 					/** @type {Statistics} */
 
- 					const stats = {
 
- 						cached: 0,
 
- 						alreadyInConfig: 0,
 
- 						invalidModule: 0,
 
- 						incorrectChunks: 0,
 
- 						incorrectDependency: 0,
 
- 						incorrectModuleDependency: 0,
 
- 						incorrectChunksOfImporter: 0,
 
- 						incorrectRuntimeCondition: 0,
 
- 						importerFailed: 0,
 
- 						added: 0
 
- 					};
 
- 					let statsCandidates = 0;
 
- 					let statsSizeSum = 0;
 
- 					let statsEmptyConfigurations = 0;
 
- 					logger.time("find modules to concatenate");
 
- 					const concatConfigurations = [];
 
- 					const usedAsInner = new Set();
 
- 					for (const currentRoot of relevantModules) {
 
- 						// when used by another configuration as inner:
 
- 						// the other configuration is better and we can skip this one
 
- 						// TODO reconsider that when it's only used in a different runtime
 
- 						if (usedAsInner.has(currentRoot)) continue;
 
- 						let chunkRuntime;
 
- 						for (const r of chunkGraph.getModuleRuntimes(currentRoot)) {
 
- 							chunkRuntime = mergeRuntimeOwned(chunkRuntime, r);
 
- 						}
 
- 						const exportsInfo = moduleGraph.getExportsInfo(currentRoot);
 
- 						const filteredRuntime = filterRuntime(chunkRuntime, r =>
 
- 							exportsInfo.isModuleUsed(r)
 
- 						);
 
- 						const activeRuntime =
 
- 							filteredRuntime === true
 
- 								? chunkRuntime
 
- 								: filteredRuntime === false
 
- 									? undefined
 
- 									: filteredRuntime;
 
- 						// create a configuration with the root
 
- 						const currentConfiguration = new ConcatConfiguration(
 
- 							currentRoot,
 
- 							activeRuntime
 
- 						);
 
- 						// cache failures to add modules
 
- 						const failureCache = new Map();
 
- 						// potential optional import candidates
 
- 						/** @type {Set<Module>} */
 
- 						const candidates = new Set();
 
- 						// try to add all imports
 
- 						for (const imp of this._getImports(
 
- 							compilation,
 
- 							currentRoot,
 
- 							activeRuntime
 
- 						)) {
 
- 							candidates.add(imp);
 
- 						}
 
- 						for (const imp of candidates) {
 
- 							const impCandidates = new Set();
 
- 							const problem = this._tryToAdd(
 
- 								compilation,
 
- 								currentConfiguration,
 
- 								imp,
 
- 								chunkRuntime,
 
- 								activeRuntime,
 
- 								possibleInners,
 
- 								impCandidates,
 
- 								failureCache,
 
- 								chunkGraph,
 
- 								true,
 
- 								stats
 
- 							);
 
- 							if (problem) {
 
- 								failureCache.set(imp, problem);
 
- 								currentConfiguration.addWarning(imp, problem);
 
- 							} else {
 
- 								for (const c of impCandidates) {
 
- 									candidates.add(c);
 
- 								}
 
- 							}
 
- 						}
 
- 						statsCandidates += candidates.size;
 
- 						if (!currentConfiguration.isEmpty()) {
 
- 							const modules = currentConfiguration.getModules();
 
- 							statsSizeSum += modules.size;
 
- 							concatConfigurations.push(currentConfiguration);
 
- 							for (const module of modules) {
 
- 								if (module !== currentConfiguration.rootModule) {
 
- 									usedAsInner.add(module);
 
- 								}
 
- 							}
 
- 						} else {
 
- 							statsEmptyConfigurations++;
 
- 							const optimizationBailouts =
 
- 								moduleGraph.getOptimizationBailout(currentRoot);
 
- 							for (const warning of currentConfiguration.getWarningsSorted()) {
 
- 								optimizationBailouts.push(
 
- 									formatBailoutWarning(warning[0], warning[1])
 
- 								);
 
- 							}
 
- 						}
 
- 					}
 
- 					logger.timeEnd("find modules to concatenate");
 
- 					logger.debug(
 
- 						`${
 
- 							concatConfigurations.length
 
- 						} successful concat configurations (avg size: ${
 
- 							statsSizeSum / concatConfigurations.length
 
- 						}), ${statsEmptyConfigurations} bailed out completely`
 
- 					);
 
- 					logger.debug(
 
- 						`${statsCandidates} candidates were considered for adding (${stats.cached} cached failure, ${stats.alreadyInConfig} already in config, ${stats.invalidModule} invalid module, ${stats.incorrectChunks} incorrect chunks, ${stats.incorrectDependency} incorrect dependency, ${stats.incorrectChunksOfImporter} incorrect chunks of importer, ${stats.incorrectModuleDependency} incorrect module dependency, ${stats.incorrectRuntimeCondition} incorrect runtime condition, ${stats.importerFailed} importer failed, ${stats.added} added)`
 
- 					);
 
- 					// HACK: Sort configurations by length and start with the longest one
 
- 					// to get the biggest groups possible. Used modules are marked with usedModules
 
- 					// TODO: Allow to reuse existing configuration while trying to add dependencies.
 
- 					// This would improve performance. O(n^2) -> O(n)
 
- 					logger.time("sort concat configurations");
 
- 					concatConfigurations.sort((a, b) => b.modules.size - a.modules.size);
 
- 					logger.timeEnd("sort concat configurations");
 
- 					const usedModules = new Set();
 
- 					logger.time("create concatenated modules");
 
- 					asyncLib.each(
 
- 						concatConfigurations,
 
- 						(concatConfiguration, callback) => {
 
- 							const rootModule = concatConfiguration.rootModule;
 
- 							// Avoid overlapping configurations
 
- 							// TODO: remove this when todo above is fixed
 
- 							if (usedModules.has(rootModule)) return callback();
 
- 							const modules = concatConfiguration.getModules();
 
- 							for (const m of modules) {
 
- 								usedModules.add(m);
 
- 							}
 
- 							// Create a new ConcatenatedModule
 
- 							ConcatenatedModule.getCompilationHooks(compilation);
 
- 							const newModule = ConcatenatedModule.create(
 
- 								rootModule,
 
- 								modules,
 
- 								concatConfiguration.runtime,
 
- 								compilation,
 
- 								compiler.root,
 
- 								compilation.outputOptions.hashFunction
 
- 							);
 
- 							const build = () => {
 
- 								newModule.build(
 
- 									compiler.options,
 
- 									compilation,
 
- 									/** @type {EXPECTED_ANY} */
 
- 									(null),
 
- 									/** @type {EXPECTED_ANY} */
 
- 									(null),
 
- 									err => {
 
- 										if (err) {
 
- 											if (!err.module) {
 
- 												err.module = newModule;
 
- 											}
 
- 											return callback(err);
 
- 										}
 
- 										integrate();
 
- 									}
 
- 								);
 
- 							};
 
- 							const integrate = () => {
 
- 								if (backCompat) {
 
- 									ChunkGraph.setChunkGraphForModule(newModule, chunkGraph);
 
- 									ModuleGraph.setModuleGraphForModule(newModule, moduleGraph);
 
- 								}
 
- 								for (const warning of concatConfiguration.getWarningsSorted()) {
 
- 									moduleGraph
 
- 										.getOptimizationBailout(newModule)
 
- 										.push(formatBailoutWarning(warning[0], warning[1]));
 
- 								}
 
- 								moduleGraph.cloneModuleAttributes(rootModule, newModule);
 
- 								for (const m of modules) {
 
- 									// add to builtModules when one of the included modules was built
 
- 									if (compilation.builtModules.has(m)) {
 
- 										compilation.builtModules.add(newModule);
 
- 									}
 
- 									if (m !== rootModule) {
 
- 										// attach external references to the concatenated module too
 
- 										moduleGraph.copyOutgoingModuleConnections(
 
- 											m,
 
- 											newModule,
 
- 											c =>
 
- 												c.originModule === m &&
 
- 												!(
 
- 													c.dependency instanceof HarmonyImportDependency &&
 
- 													modules.has(c.module)
 
- 												)
 
- 										);
 
- 										// remove module from chunk
 
- 										for (const chunk of chunkGraph.getModuleChunksIterable(
 
- 											rootModule
 
- 										)) {
 
- 											const sourceTypes = chunkGraph.getChunkModuleSourceTypes(
 
- 												chunk,
 
- 												m
 
- 											);
 
- 											if (sourceTypes.size === 1) {
 
- 												chunkGraph.disconnectChunkAndModule(chunk, m);
 
- 											} else {
 
- 												const newSourceTypes = new Set(sourceTypes);
 
- 												newSourceTypes.delete(JS_TYPE);
 
- 												chunkGraph.setChunkModuleSourceTypes(
 
- 													chunk,
 
- 													m,
 
- 													newSourceTypes
 
- 												);
 
- 											}
 
- 										}
 
- 									}
 
- 								}
 
- 								compilation.modules.delete(rootModule);
 
- 								ChunkGraph.clearChunkGraphForModule(rootModule);
 
- 								ModuleGraph.clearModuleGraphForModule(rootModule);
 
- 								// remove module from chunk
 
- 								chunkGraph.replaceModule(rootModule, newModule);
 
- 								// replace module references with the concatenated module
 
- 								moduleGraph.moveModuleConnections(rootModule, newModule, c => {
 
- 									const otherModule =
 
- 										c.module === rootModule ? c.originModule : c.module;
 
- 									const innerConnection =
 
- 										c.dependency instanceof HarmonyImportDependency &&
 
- 										modules.has(/** @type {Module} */ (otherModule));
 
- 									return !innerConnection;
 
- 								});
 
- 								// add concatenated module to the compilation
 
- 								compilation.modules.add(newModule);
 
- 								callback();
 
- 							};
 
- 							build();
 
- 						},
 
- 						err => {
 
- 							logger.timeEnd("create concatenated modules");
 
- 							process.nextTick(callback.bind(null, err));
 
- 						}
 
- 					);
 
- 				}
 
- 			);
 
- 		});
 
- 	}
 
- 	/**
 
- 	 * @param {Compilation} compilation the compilation
 
- 	 * @param {Module} module the module to be added
 
- 	 * @param {RuntimeSpec} runtime the runtime scope
 
- 	 * @returns {Set<Module>} the imported modules
 
- 	 */
 
- 	_getImports(compilation, module, runtime) {
 
- 		const moduleGraph = compilation.moduleGraph;
 
- 		const set = new Set();
 
- 		for (const dep of module.dependencies) {
 
- 			// Get reference info only for harmony Dependencies
 
- 			if (!(dep instanceof HarmonyImportDependency)) continue;
 
- 			const connection = moduleGraph.getConnection(dep);
 
- 			// Reference is valid and has a module
 
- 			if (
 
- 				!connection ||
 
- 				!connection.module ||
 
- 				!connection.isTargetActive(runtime)
 
- 			) {
 
- 				continue;
 
- 			}
 
- 			const importedNames = compilation.getDependencyReferencedExports(
 
- 				dep,
 
- 				undefined
 
- 			);
 
- 			if (
 
- 				importedNames.every(i =>
 
- 					Array.isArray(i) ? i.length > 0 : i.name.length > 0
 
- 				) ||
 
- 				Array.isArray(moduleGraph.getProvidedExports(module))
 
- 			) {
 
- 				set.add(connection.module);
 
- 			}
 
- 		}
 
- 		return set;
 
- 	}
 
- 	/**
 
- 	 * @param {Compilation} compilation webpack compilation
 
- 	 * @param {ConcatConfiguration} config concat configuration (will be modified when added)
 
- 	 * @param {Module} module the module to be added
 
- 	 * @param {RuntimeSpec} runtime the runtime scope of the generated code
 
- 	 * @param {RuntimeSpec} activeRuntime the runtime scope of the root module
 
- 	 * @param {Set<Module>} possibleModules modules that are candidates
 
- 	 * @param {Set<Module>} candidates list of potential candidates (will be added to)
 
- 	 * @param {Map<Module, Module | ((requestShortener: RequestShortener) => string)>} failureCache cache for problematic modules to be more performant
 
- 	 * @param {ChunkGraph} chunkGraph the chunk graph
 
- 	 * @param {boolean} avoidMutateOnFailure avoid mutating the config when adding fails
 
- 	 * @param {Statistics} statistics gathering metrics
 
- 	 * @returns {null | Module | ((requestShortener: RequestShortener) => string)} the problematic module
 
- 	 */
 
- 	_tryToAdd(
 
- 		compilation,
 
- 		config,
 
- 		module,
 
- 		runtime,
 
- 		activeRuntime,
 
- 		possibleModules,
 
- 		candidates,
 
- 		failureCache,
 
- 		chunkGraph,
 
- 		avoidMutateOnFailure,
 
- 		statistics
 
- 	) {
 
- 		const cacheEntry = failureCache.get(module);
 
- 		if (cacheEntry) {
 
- 			statistics.cached++;
 
- 			return cacheEntry;
 
- 		}
 
- 		// Already added?
 
- 		if (config.has(module)) {
 
- 			statistics.alreadyInConfig++;
 
- 			return null;
 
- 		}
 
- 		// Not possible to add?
 
- 		if (!possibleModules.has(module)) {
 
- 			statistics.invalidModule++;
 
- 			failureCache.set(module, module); // cache failures for performance
 
- 			return module;
 
- 		}
 
- 		// Module must be in the correct chunks
 
- 		const missingChunks = [
 
- 			...chunkGraph.getModuleChunksIterable(config.rootModule)
 
- 		].filter(chunk => !chunkGraph.isModuleInChunk(module, chunk));
 
- 		if (missingChunks.length > 0) {
 
- 			/**
 
- 			 * @param {RequestShortener} requestShortener request shortener
 
- 			 * @returns {string} problem description
 
- 			 */
 
- 			const problem = requestShortener => {
 
- 				const missingChunksList = [
 
- 					...new Set(
 
- 						missingChunks.map(chunk => chunk.name || "unnamed chunk(s)")
 
- 					)
 
- 				].sort();
 
- 				const chunks = [
 
- 					...new Set(
 
- 						[...chunkGraph.getModuleChunksIterable(module)].map(
 
- 							chunk => chunk.name || "unnamed chunk(s)"
 
- 						)
 
- 					)
 
- 				].sort();
 
- 				return `Module ${module.readableIdentifier(
 
- 					requestShortener
 
- 				)} is not in the same chunk(s) (expected in chunk(s) ${missingChunksList.join(
 
- 					", "
 
- 				)}, module is in chunk(s) ${chunks.join(", ")})`;
 
- 			};
 
- 			statistics.incorrectChunks++;
 
- 			failureCache.set(module, problem); // cache failures for performance
 
- 			return problem;
 
- 		}
 
- 		const moduleGraph = compilation.moduleGraph;
 
- 		const incomingConnections =
 
- 			moduleGraph.getIncomingConnectionsByOriginModule(module);
 
- 		const incomingConnectionsFromNonModules =
 
- 			incomingConnections.get(null) || incomingConnections.get(undefined);
 
- 		if (incomingConnectionsFromNonModules) {
 
- 			const activeNonModulesConnections =
 
- 				incomingConnectionsFromNonModules.filter(connection =>
 
- 					// We are not interested in inactive connections
 
- 					// or connections without dependency
 
- 					connection.isActive(runtime)
 
- 				);
 
- 			if (activeNonModulesConnections.length > 0) {
 
- 				/**
 
- 				 * @param {RequestShortener} requestShortener request shortener
 
- 				 * @returns {string} problem description
 
- 				 */
 
- 				const problem = requestShortener => {
 
- 					const importingExplanations = new Set(
 
- 						activeNonModulesConnections.map(c => c.explanation).filter(Boolean)
 
- 					);
 
- 					const explanations = [...importingExplanations].sort();
 
- 					return `Module ${module.readableIdentifier(
 
- 						requestShortener
 
- 					)} is referenced ${
 
- 						explanations.length > 0
 
- 							? `by: ${explanations.join(", ")}`
 
- 							: "in an unsupported way"
 
- 					}`;
 
- 				};
 
- 				statistics.incorrectDependency++;
 
- 				failureCache.set(module, problem); // cache failures for performance
 
- 				return problem;
 
- 			}
 
- 		}
 
- 		/** @type {Map<Module, readonly ModuleGraph.ModuleGraphConnection[]>} */
 
- 		const incomingConnectionsFromModules = new Map();
 
- 		for (const [originModule, connections] of incomingConnections) {
 
- 			if (originModule) {
 
- 				// Ignore connection from orphan modules
 
- 				if (chunkGraph.getNumberOfModuleChunks(originModule) === 0) continue;
 
- 				// We don't care for connections from other runtimes
 
- 				let originRuntime;
 
- 				for (const r of chunkGraph.getModuleRuntimes(originModule)) {
 
- 					originRuntime = mergeRuntimeOwned(originRuntime, r);
 
- 				}
 
- 				if (!intersectRuntime(runtime, originRuntime)) continue;
 
- 				// We are not interested in inactive connections
 
- 				const activeConnections = connections.filter(connection =>
 
- 					connection.isActive(runtime)
 
- 				);
 
- 				if (activeConnections.length > 0) {
 
- 					incomingConnectionsFromModules.set(originModule, activeConnections);
 
- 				}
 
- 			}
 
- 		}
 
- 		const incomingModules = [...incomingConnectionsFromModules.keys()];
 
- 		// Module must be in the same chunks like the referencing module
 
- 		const otherChunkModules = incomingModules.filter(originModule => {
 
- 			for (const chunk of chunkGraph.getModuleChunksIterable(
 
- 				config.rootModule
 
- 			)) {
 
- 				if (!chunkGraph.isModuleInChunk(originModule, chunk)) {
 
- 					return true;
 
- 				}
 
- 			}
 
- 			return false;
 
- 		});
 
- 		if (otherChunkModules.length > 0) {
 
- 			/**
 
- 			 * @param {RequestShortener} requestShortener request shortener
 
- 			 * @returns {string} problem description
 
- 			 */
 
- 			const problem = requestShortener => {
 
- 				const names = otherChunkModules
 
- 					.map(m => m.readableIdentifier(requestShortener))
 
- 					.sort();
 
- 				return `Module ${module.readableIdentifier(
 
- 					requestShortener
 
- 				)} is referenced from different chunks by these modules: ${names.join(
 
- 					", "
 
- 				)}`;
 
- 			};
 
- 			statistics.incorrectChunksOfImporter++;
 
- 			failureCache.set(module, problem); // cache failures for performance
 
- 			return problem;
 
- 		}
 
- 		/** @type {Map<Module, readonly ModuleGraph.ModuleGraphConnection[]>} */
 
- 		const nonHarmonyConnections = new Map();
 
- 		for (const [originModule, connections] of incomingConnectionsFromModules) {
 
- 			const selected = connections.filter(
 
- 				connection =>
 
- 					!connection.dependency ||
 
- 					!(connection.dependency instanceof HarmonyImportDependency)
 
- 			);
 
- 			if (selected.length > 0) {
 
- 				nonHarmonyConnections.set(originModule, connections);
 
- 			}
 
- 		}
 
- 		if (nonHarmonyConnections.size > 0) {
 
- 			/**
 
- 			 * @param {RequestShortener} requestShortener request shortener
 
- 			 * @returns {string} problem description
 
- 			 */
 
- 			const problem = requestShortener => {
 
- 				const names = [...nonHarmonyConnections]
 
- 					.map(
 
- 						([originModule, connections]) =>
 
- 							`${originModule.readableIdentifier(
 
- 								requestShortener
 
- 							)} (referenced with ${[
 
- 								...new Set(
 
- 									connections
 
- 										.map(c => c.dependency && c.dependency.type)
 
- 										.filter(Boolean)
 
- 								)
 
- 							]
 
- 								.sort()
 
- 								.join(", ")})`
 
- 					)
 
- 					.sort();
 
- 				return `Module ${module.readableIdentifier(
 
- 					requestShortener
 
- 				)} is referenced from these modules with unsupported syntax: ${names.join(
 
- 					", "
 
- 				)}`;
 
- 			};
 
- 			statistics.incorrectModuleDependency++;
 
- 			failureCache.set(module, problem); // cache failures for performance
 
- 			return problem;
 
- 		}
 
- 		if (runtime !== undefined && typeof runtime !== "string") {
 
- 			// Module must be consistently referenced in the same runtimes
 
- 			/** @type {{ originModule: Module, runtimeCondition: RuntimeSpec }[]} */
 
- 			const otherRuntimeConnections = [];
 
- 			outer: for (const [
 
- 				originModule,
 
- 				connections
 
- 			] of incomingConnectionsFromModules) {
 
- 				/** @type {false | RuntimeSpec} */
 
- 				let currentRuntimeCondition = false;
 
- 				for (const connection of connections) {
 
- 					const runtimeCondition = filterRuntime(runtime, runtime =>
 
- 						connection.isTargetActive(runtime)
 
- 					);
 
- 					if (runtimeCondition === false) continue;
 
- 					if (runtimeCondition === true) continue outer;
 
- 					currentRuntimeCondition =
 
- 						currentRuntimeCondition !== false
 
- 							? mergeRuntime(currentRuntimeCondition, runtimeCondition)
 
- 							: runtimeCondition;
 
- 				}
 
- 				if (currentRuntimeCondition !== false) {
 
- 					otherRuntimeConnections.push({
 
- 						originModule,
 
- 						runtimeCondition: currentRuntimeCondition
 
- 					});
 
- 				}
 
- 			}
 
- 			if (otherRuntimeConnections.length > 0) {
 
- 				/**
 
- 				 * @param {RequestShortener} requestShortener request shortener
 
- 				 * @returns {string} problem description
 
- 				 */
 
- 				const problem = requestShortener =>
 
- 					`Module ${module.readableIdentifier(
 
- 						requestShortener
 
- 					)} is runtime-dependent referenced by these modules: ${Array.from(
 
- 						otherRuntimeConnections,
 
- 						({ originModule, runtimeCondition }) =>
 
- 							`${originModule.readableIdentifier(
 
- 								requestShortener
 
- 							)} (expected runtime ${runtimeToString(
 
- 								runtime
 
- 							)}, module is only referenced in ${runtimeToString(
 
- 								/** @type {RuntimeSpec} */ (runtimeCondition)
 
- 							)})`
 
- 					).join(", ")}`;
 
- 				statistics.incorrectRuntimeCondition++;
 
- 				failureCache.set(module, problem); // cache failures for performance
 
- 				return problem;
 
- 			}
 
- 		}
 
- 		let backup;
 
- 		if (avoidMutateOnFailure) {
 
- 			backup = config.snapshot();
 
- 		}
 
- 		// Add the module
 
- 		config.add(module);
 
- 		incomingModules.sort(compareModulesByIdentifier);
 
- 		// Every module which depends on the added module must be in the configuration too.
 
- 		for (const originModule of incomingModules) {
 
- 			const problem = this._tryToAdd(
 
- 				compilation,
 
- 				config,
 
- 				originModule,
 
- 				runtime,
 
- 				activeRuntime,
 
- 				possibleModules,
 
- 				candidates,
 
- 				failureCache,
 
- 				chunkGraph,
 
- 				false,
 
- 				statistics
 
- 			);
 
- 			if (problem) {
 
- 				if (backup !== undefined) config.rollback(backup);
 
- 				statistics.importerFailed++;
 
- 				failureCache.set(module, problem); // cache failures for performance
 
- 				return problem;
 
- 			}
 
- 		}
 
- 		// Add imports to possible candidates list
 
- 		for (const imp of this._getImports(compilation, module, runtime)) {
 
- 			candidates.add(imp);
 
- 		}
 
- 		statistics.added++;
 
- 		return null;
 
- 	}
 
- }
 
- /** @typedef {Module | ((requestShortener: RequestShortener) => string)} Problem */
 
- class ConcatConfiguration {
 
- 	/**
 
- 	 * @param {Module} rootModule the root module
 
- 	 * @param {RuntimeSpec} runtime the runtime
 
- 	 */
 
- 	constructor(rootModule, runtime) {
 
- 		this.rootModule = rootModule;
 
- 		this.runtime = runtime;
 
- 		/** @type {Set<Module>} */
 
- 		this.modules = new Set();
 
- 		this.modules.add(rootModule);
 
- 		/** @type {Map<Module, Problem>} */
 
- 		this.warnings = new Map();
 
- 	}
 
- 	/**
 
- 	 * @param {Module} module the module
 
- 	 */
 
- 	add(module) {
 
- 		this.modules.add(module);
 
- 	}
 
- 	/**
 
- 	 * @param {Module} module the module
 
- 	 * @returns {boolean} true, when the module is in the module set
 
- 	 */
 
- 	has(module) {
 
- 		return this.modules.has(module);
 
- 	}
 
- 	isEmpty() {
 
- 		return this.modules.size === 1;
 
- 	}
 
- 	/**
 
- 	 * @param {Module} module the module
 
- 	 * @param {Problem} problem the problem
 
- 	 */
 
- 	addWarning(module, problem) {
 
- 		this.warnings.set(module, problem);
 
- 	}
 
- 	/**
 
- 	 * @returns {Map<Module, Problem>} warnings
 
- 	 */
 
- 	getWarningsSorted() {
 
- 		return new Map(
 
- 			[...this.warnings].sort((a, b) => {
 
- 				const ai = a[0].identifier();
 
- 				const bi = b[0].identifier();
 
- 				if (ai < bi) return -1;
 
- 				if (ai > bi) return 1;
 
- 				return 0;
 
- 			})
 
- 		);
 
- 	}
 
- 	/**
 
- 	 * @returns {Set<Module>} modules as set
 
- 	 */
 
- 	getModules() {
 
- 		return this.modules;
 
- 	}
 
- 	snapshot() {
 
- 		return this.modules.size;
 
- 	}
 
- 	/**
 
- 	 * @param {number} snapshot snapshot
 
- 	 */
 
- 	rollback(snapshot) {
 
- 		const modules = this.modules;
 
- 		for (const m of modules) {
 
- 			if (snapshot === 0) {
 
- 				modules.delete(m);
 
- 			} else {
 
- 				snapshot--;
 
- 			}
 
- 		}
 
- 	}
 
- }
 
- module.exports = ModuleConcatenationPlugin;
 
 
  |