code:
/*
	MIT License http://www.opensource.org/licenses/mit-license.php
	Author Tobias Koppers @sokra
*/

"use strict";

const eslintScope = require("eslint-scope");
const Referencer = require("eslint-scope/lib/referencer");
const {
	CachedSource,
	ConcatSource,
	ReplaceSource
} = require("webpack-sources");
const ConcatenationScope = require("../ConcatenationScope");
const { UsageState } = require("../ExportsInfo");
const Module = require("../Module");
const { JAVASCRIPT_MODULE_TYPE_ESM } = require("../ModuleTypeConstants");
const RuntimeGlobals = require("../RuntimeGlobals");
const Template = require("../Template");
const HarmonyImportDependency = require("../dependencies/HarmonyImportDependency");
const JavascriptParser = require("../javascript/JavascriptParser");
const { equals } = require("../util/ArrayHelpers");
const LazySet = require("../util/LazySet");
const { concatComparators } = require("../util/comparators");
const createHash = require("../util/createHash");
const { makePathsRelative } = require("../util/identifier");
const makeSerializable = require("../util/makeSerializable");
const { getAllReferences, getPathInAst } = require("../util/mergeScope");
const propertyAccess = require("../util/propertyAccess");
const { propertyName } = require("../util/propertyName");
const {
	filterRuntime,
	intersectRuntime,
	mergeRuntimeCondition,
	mergeRuntimeConditionNonFalse,
	runtimeConditionToString,
	subtractRuntimeCondition
} = require("../util/runtime");

/** @typedef {import("eslint-scope").Reference} Reference */
/** @typedef {import("eslint-scope").Scope} Scope */
/** @typedef {import("eslint-scope").Variable} Variable */
/** @typedef {import("webpack-sources").Source} Source */
/** @typedef {import("../../declarations/WebpackOptions").WebpackOptionsNormalized} WebpackOptions */
/** @typedef {import("../ChunkGraph")} ChunkGraph */
/** @typedef {import("../CodeGenerationResults")} CodeGenerationResults */
/** @typedef {import("../Compilation")} Compilation */
/** @typedef {import("../Dependency")} Dependency */
/** @typedef {import("../Dependency").UpdateHashContext} UpdateHashContext */
/** @typedef {import("../DependencyTemplate").DependencyTemplateContext} DependencyTemplateContext */
/** @typedef {import("../DependencyTemplates")} DependencyTemplates */
/** @typedef {import("../ExportsInfo").ExportInfo} ExportInfo */
/** @typedef {import("../Module").BuildInfo} BuildInfo */
/** @typedef {import("../Module").BuildMeta} BuildMeta */
/** @typedef {import("../Module").CodeGenerationContext} CodeGenerationContext */
/** @typedef {import("../Module").CodeGenerationResult} CodeGenerationResult */
/** @typedef {import("../Module").LibIdentOptions} LibIdentOptions */
/** @typedef {import("../Module").ReadOnlyRuntimeRequirements} ReadOnlyRuntimeRequirements */
/** @typedef {import("../Module").SourceTypes} SourceTypes */
/** @typedef {import("../ModuleGraph")} ModuleGraph */
/** @typedef {import("../ModuleGraphConnection")} ModuleGraphConnection */
/** @typedef {import("../ModuleGraphConnection").ConnectionState} ConnectionState */
/** @typedef {import("../RequestShortener")} RequestShortener */
/** @typedef {import("../ResolverFactory").ResolverWithOptions} ResolverWithOptions */
/** @typedef {import("../RuntimeTemplate")} RuntimeTemplate */
/** @typedef {import("../WebpackError")} WebpackError */
/** @typedef {import("../javascript/JavascriptModulesPlugin").ChunkRenderContext} ChunkRenderContext */
/** @typedef {import("../javascript/JavascriptParser").Program} Program */
/** @typedef {import("../javascript/JavascriptParser").Range} Range */
/** @typedef {import("../serialization/ObjectMiddleware").ObjectDeserializerContext} ObjectDeserializerContext */
/** @typedef {import("../util/Hash")} Hash */
/** @typedef {typeof import("../util/Hash")} HashConstructor */
/** @typedef {import("../util/fs").InputFileSystem} InputFileSystem */
/** @typedef {import("../util/runtime").RuntimeSpec} RuntimeSpec */

/**
 * @template T
 * @typedef {import("../InitFragment")<T>} InitFragment
 */

/**
 * @template T
 * @typedef {import("../util/comparators").Comparator<T>} Comparator
 */

// fix eslint-scope to support class properties correctly
// cspell:word Referencer
const ReferencerClass = /** @type {any} */ (Referencer);
if (!ReferencerClass.prototype.PropertyDefinition) {
	ReferencerClass.prototype.PropertyDefinition =
		ReferencerClass.prototype.Property;
}

/**
 * @typedef {object} ReexportInfo
 * @property {Module} module
 * @property {string[]} export
 */

/** @typedef {RawBinding | SymbolBinding} Binding */

/**
 * @typedef {object} RawBinding
 * @property {ModuleInfo} info
 * @property {string} rawName
 * @property {string=} comment
 * @property {string[]} ids
 * @property {string[]} exportName
 */

/**
 * @typedef {object} SymbolBinding
 * @property {ConcatenatedModuleInfo} info
 * @property {string} name
 * @property {string=} comment
 * @property {string[]} ids
 * @property {string[]} exportName
 */

/** @typedef {ConcatenatedModuleInfo | ExternalModuleInfo } ModuleInfo */
/** @typedef {ConcatenatedModuleInfo | ExternalModuleInfo | ReferenceToModuleInfo } ModuleInfoOrReference */

/**
 * @typedef {object} ConcatenatedModuleInfo
 * @property {"concatenated"} type
 * @property {Module} module
 * @property {number} index
 * @property {Program | undefined} ast
 * @property {Source} internalSource
 * @property {ReplaceSource} source
 * @property {InitFragment<ChunkRenderContext>[]=} chunkInitFragments
 * @property {ReadOnlyRuntimeRequirements} runtimeRequirements
 * @property {Scope} globalScope
 * @property {Scope} moduleScope
 * @property {Map<string, string>} internalNames
 * @property {Map<string, string> | undefined} exportMap
 * @property {Map<string, string> | undefined} rawExportMap
 * @property {string=} namespaceExportSymbol
 * @property {string | undefined} namespaceObjectName
 * @property {boolean} interopNamespaceObjectUsed
 * @property {string | undefined} interopNamespaceObjectName
 * @property {boolean} interopNamespaceObject2Used
 * @property {string | undefined} interopNamespaceObject2Name
 * @property {boolean} interopDefaultAccessUsed
 * @property {string | undefined} interopDefaultAccessName
 */

/**
 * @typedef {object} ExternalModuleInfo
 * @property {"external"} type
 * @property {Module} module
 * @property {RuntimeSpec | boolean} runtimeCondition
 * @property {number} index
 * @property {string} name
 * @property {boolean} interopNamespaceObjectUsed
 * @property {string} interopNamespaceObjectName
 * @property {boolean} interopNamespaceObject2Used
 * @property {string} interopNamespaceObject2Name
 * @property {boolean} interopDefaultAccessUsed
 * @property {string} interopDefaultAccessName
 */

/**
 * @typedef {object} ReferenceToModuleInfo
 * @property {"reference"} type
 * @property {RuntimeSpec | boolean} runtimeCondition
 * @property {ConcatenatedModuleInfo | ExternalModuleInfo} target
 */

/** @typedef {Set<string>} UsedNames */

const RESERVED_NAMES = new Set(
	[
		// internal names (should always be renamed)
		ConcatenationScope.DEFAULT_EXPORT,
		ConcatenationScope.NAMESPACE_OBJECT_EXPORT,

		// keywords
		"abstract,arguments,async,await,boolean,break,byte,case,catch,char,class,const,continue",
		"debugger,default,delete,do,double,else,enum,eval,export,extends,false,final,finally,float",
		"for,function,goto,if,implements,import,in,instanceof,int,interface,let,long,native,new,null",
		"package,private,protected,public,return,short,static,super,switch,synchronized,this,throw",
		"throws,transient,true,try,typeof,var,void,volatile,while,with,yield",

		// commonjs/amd
		"module,__dirname,__filename,exports,require,define",

		// js globals
		"Array,Date,eval,function,hasOwnProperty,Infinity,isFinite,isNaN,isPrototypeOf,length,Math",
		"NaN,name,Number,Object,prototype,String,toString,undefined,valueOf",

		// browser globals
		"alert,all,anchor,anchors,area,assign,blur,button,checkbox,clearInterval,clearTimeout",
		"clientInformation,close,closed,confirm,constructor,crypto,decodeURI,decodeURIComponent",
		"defaultStatus,document,element,elements,embed,embeds,encodeURI,encodeURIComponent,escape",
		"event,fileUpload,focus,form,forms,frame,innerHeight,innerWidth,layer,layers,link,location",
		"mimeTypes,navigate,navigator,frames,frameRate,hidden,history,image,images,offscreenBuffering",
		"open,opener,option,outerHeight,outerWidth,packages,pageXOffset,pageYOffset,parent,parseFloat",
		"parseInt,password,pkcs11,plugin,prompt,propertyIsEnum,radio,reset,screenX,screenY,scroll",
		"secure,select,self,setInterval,setTimeout,status,submit,taint,text,textarea,top,unescape",
		"untaint,window",

		// window events
		"onblur,onclick,onerror,onfocus,onkeydown,onkeypress,onkeyup,onmouseover,onload,onmouseup,onmousedown,onsubmit"
	]
		.join(",")
		.split(",")
);

const createComparator = (property, comparator) => (a, b) =>
	comparator(a[property], b[property]);

/**
 * @param {number} a a
 * @param {number} b b
 * @returns {0 | 1 | -1} result
 */
const compareNumbers = (a, b) => {
	if (isNaN(a)) {
		if (!isNaN(b)) {
			return 1;
		}
	} else {
		if (isNaN(b)) {
			return -1;
		}
		if (a !== b) {
			return a < b ? -1 : 1;
		}
	}
	return 0;
};
const bySourceOrder = createComparator("sourceOrder", compareNumbers);
const byRangeStart = createComparator("rangeStart", compareNumbers);

/**
 * @param {Iterable<string>} iterable iterable object
 * @returns {string} joined iterable object
 */
const joinIterableWithComma = iterable => {
	// This is more performant than Array.from().join(", ")
	// as it doesn't create an array
	let str = "";
	let first = true;
	for (const item of iterable) {
		if (first) {
			first = false;
		} else {
			str += ", ";
		}
		str += item;
	}
	return str;
};

/**
 * @typedef {object} ConcatenationEntry
 * @property {"concatenated" | "external"} type
 * @property {Module} module
 * @property {RuntimeSpec | boolean} runtimeCondition
 */

/**
 * @param {ModuleGraph} moduleGraph the module graph
 * @param {ModuleInfo} info module info
 * @param {string[]} exportName exportName
 * @param {Map<Module, ModuleInfo>} moduleToInfoMap moduleToInfoMap
 * @param {RuntimeSpec} runtime for which runtime
 * @param {RequestShortener} requestShortener the request shortener
 * @param {RuntimeTemplate} runtimeTemplate the runtime template
 * @param {Set<ConcatenatedModuleInfo>} neededNamespaceObjects modules for which a namespace object should be generated
 * @param {boolean} asCall asCall
 * @param {boolean | undefined} strictHarmonyModule strictHarmonyModule
 * @param {boolean | undefined} asiSafe asiSafe
 * @param {Set<ExportInfo>} alreadyVisited alreadyVisited
 * @returns {Binding} the final variable
 */
const getFinalBinding = (
	moduleGraph,
	info,
	exportName,
	moduleToInfoMap,
	runtime,
	requestShortener,
	runtimeTemplate,
	neededNamespaceObjects,
	asCall,
	strictHarmonyModule,
	asiSafe,
	alreadyVisited = new Set()
) => {
	const exportsType = info.module.getExportsType(
		moduleGraph,
		strictHarmonyModule
	);
	if (exportName.length === 0) {
		switch (exportsType) {
			case "default-only":
				info.interopNamespaceObject2Used = true;
				return {
					info,
					rawName: /** @type {string} */ (info.interopNamespaceObject2Name),
					ids: exportName,
					exportName
				};
			case "default-with-named":
				info.interopNamespaceObjectUsed = true;
				return {
					info,
					rawName: /** @type {string} */ (info.interopNamespaceObjectName),
					ids: exportName,
					exportName
				};
			case "namespace":
			case "dynamic":
				break;
			default:
				throw new Error(`Unexpected exportsType ${exportsType}`);
		}
	} else {
		switch (exportsType) {
			case "namespace":
				break;
			case "default-with-named":
				switch (exportName[0]) {
					case "default":
						exportName = exportName.slice(1);
						break;
					case "__esModule":
						return {
							info,
							rawName: "/* __esModule */true",
							ids: exportName.slice(1),
							exportName
						};
				}
				break;
			case "default-only": {
				const exportId = exportName[0];
				if (exportId === "__esModule") {
					return {
						info,
						rawName: "/* __esModule */true",
						ids: exportName.slice(1),
						exportName
					};
				}
				exportName = exportName.slice(1);
				if (exportId !== "default") {
					return {
						info,
						rawName:
							"/* non-default import from default-exporting module */undefined",
						ids: exportName,
						exportName
					};
				}
				break;
			}
			case "dynamic":
				switch (exportName[0]) {
					case "default": {
						exportName = exportName.slice(1);
						info.interopDefaultAccessUsed = true;
						const defaultExport = asCall
							? `${info.interopDefaultAccessName}()`
							: asiSafe
								? `(${info.interopDefaultAccessName}())`
								: asiSafe === false
									? `;(${info.interopDefaultAccessName}())`
									: `${info.interopDefaultAccessName}.a`;
						return {
							info,
							rawName: defaultExport,
							ids: exportName,
							exportName
						};
					}
					case "__esModule":
						return {
							info,
							rawName: "/* __esModule */true",
							ids: exportName.slice(1),
							exportName
						};
				}
				break;
			default:
				throw new Error(`Unexpected exportsType ${exportsType}`);
		}
	}
	if (exportName.length === 0) {
		switch (info.type) {
			case "concatenated":
				neededNamespaceObjects.add(info);
				return {
					info,
					rawName: /** @type {string} */ (info.namespaceObjectName),
					ids: exportName,
					exportName
				};
			case "external":
				return { info, rawName: info.name, ids: exportName, exportName };
		}
	}
	const exportsInfo = moduleGraph.getExportsInfo(info.module);
	const exportInfo = exportsInfo.getExportInfo(exportName[0]);
	if (alreadyVisited.has(exportInfo)) {
		return {
			info,
			rawName: "/* circular reexport */ Object(function x() { x() }())",
			ids: [],
			exportName
		};
	}
	alreadyVisited.add(exportInfo);
	switch (info.type) {
		case "concatenated": {
			const exportId = exportName[0];
			if (exportInfo.provided === false) {
				// It's not provided, but it could be on the prototype
				neededNamespaceObjects.add(info);
				return {
					info,
					rawName: /** @type {string} */ (info.namespaceObjectName),
					ids: exportName,
					exportName
				};
			}
			const directExport = info.exportMap && info.exportMap.get(exportId);
			if (directExport) {
				const usedName = /** @type {string[]} */ (
					exportsInfo.getUsedName(exportName, runtime)
				);
				if (!usedName) {
					return {
						info,
						rawName: "/* unused export */ undefined",
						ids: exportName.slice(1),
						exportName
					};
				}
				return {
					info,
					name: directExport,
					ids: usedName.slice(1),
					exportName
				};
			}
			const rawExport = info.rawExportMap && info.rawExportMap.get(exportId);
			if (rawExport) {
				return {
					info,
					rawName: rawExport,
					ids: exportName.slice(1),
					exportName
				};
			}
			const reexport = exportInfo.findTarget(moduleGraph, module =>
				moduleToInfoMap.has(module)
			);
			if (reexport === false) {
				throw new Error(
					`Target module of reexport from '${info.module.readableIdentifier(
						requestShortener
					)}' is not part of the concatenation (export '${exportId}')\nModules in the concatenation:\n${Array.from(
						moduleToInfoMap,
						([m, info]) =>
							` * ${info.type} ${m.readableIdentifier(requestShortener)}`
					).join("\n")}`
				);
			}
			if (reexport) {
				const refInfo = moduleToInfoMap.get(reexport.module);
				return getFinalBinding(
					moduleGraph,
					/** @type {ModuleInfo} */ (refInfo),
					reexport.export
						? [...reexport.export, ...exportName.slice(1)]
						: exportName.slice(1),
					moduleToInfoMap,
					runtime,
					requestShortener,
					runtimeTemplate,
					neededNamespaceObjects,
					asCall,
					/** @type {BuildMeta} */
					(info.module.buildMeta).strictHarmonyModule,
					asiSafe,
					alreadyVisited
				);
			}
			if (info.namespaceExportSymbol) {
				const usedName = /** @type {string[]} */ (
					exportsInfo.getUsedName(exportName, runtime)
				);
				return {
					info,
					rawName: /** @type {string} */ (info.namespaceObjectName),
					ids: usedName,
					exportName
				};
			}
			throw new Error(
				`Cannot get final name for export '${exportName.join(
					"."
				)}' of ${info.module.readableIdentifier(requestShortener)}`
			);
		}

		case "external": {
			const used = /** @type {string[]} */ (
				exportsInfo.getUsedName(exportName, runtime)
			);
			if (!used) {
				return {
					info,
					rawName: "/* unused export */ undefined",
					ids: exportName.slice(1),
					exportName
				};
			}
			const comment = equals(used, exportName)
				? ""
				: Template.toNormalComment(`${exportName.join(".")}`);
			return { info, rawName: info.name + comment, ids: used, exportName };
		}
	}
};

/**
 * @param {ModuleGraph} moduleGraph the module graph
 * @param {ModuleInfo} info module info
 * @param {string[]} exportName exportName
 * @param {Map<Module, ModuleInfo>} moduleToInfoMap moduleToInfoMap
 * @param {RuntimeSpec} runtime for which runtime
 * @param {RequestShortener} requestShortener the request shortener
 * @param {RuntimeTemplate} runtimeTemplate the runtime template
 * @param {Set<ConcatenatedModuleInfo>} neededNamespaceObjects modules for which a namespace object should be generated
 * @param {boolean} asCall asCall
 * @param {boolean | undefined} callContext callContext
 * @param {boolean | undefined} strictHarmonyModule strictHarmonyModule
 * @param {boolean | undefined} asiSafe asiSafe
 * @returns {string} the final name
 */
const getFinalName = (
	moduleGraph,
	info,
	exportName,
	moduleToInfoMap,
	runtime,
	requestShortener,
	runtimeTemplate,
	neededNamespaceObjects,
	asCall,
	callContext,
	strictHarmonyModule,
	asiSafe
) => {
	const binding = getFinalBinding(
		moduleGraph,
		info,
		exportName,
		moduleToInfoMap,
		runtime,
		requestShortener,
		runtimeTemplate,
		neededNamespaceObjects,
		asCall,
		strictHarmonyModule,
		asiSafe
	);
	{
		const { ids, comment } = binding;
		let reference;
		let isPropertyAccess;
		if ("rawName" in binding) {
			reference = `${binding.rawName}${comment || ""}${propertyAccess(ids)}`;
			isPropertyAccess = ids.length > 0;
		} else {
			const { info, name: exportId } = binding;
			const name = info.internalNames.get(exportId);
			if (!name) {
				throw new Error(
					`The export "${exportId}" in "${info.module.readableIdentifier(
						requestShortener
					)}" has no internal name (existing names: ${
						Array.from(
							info.internalNames,
							([name, symbol]) => `${name}: ${symbol}`
						).join(", ") || "none"
					})`
				);
			}
			reference = `${name}${comment || ""}${propertyAccess(ids)}`;
			isPropertyAccess = ids.length > 1;
		}
		if (isPropertyAccess && asCall && callContext === false) {
			return asiSafe
				? `(0,${reference})`
				: asiSafe === false
					? `;(0,${reference})`
					: `/*#__PURE__*/Object(${reference})`;
		}
		return reference;
	}
};

/**
 * @param {Scope | null} s scope
 * @param {UsedNames} nameSet name set
 * @param {TODO} scopeSet1 scope set 1
 * @param {TODO} scopeSet2 scope set 2
 */
const addScopeSymbols = (s, nameSet, scopeSet1, scopeSet2) => {
	let scope = s;
	while (scope) {
		if (scopeSet1.has(scope)) break;
		if (scopeSet2.has(scope)) break;
		scopeSet1.add(scope);
		for (const variable of scope.variables) {
			nameSet.add(variable.name);
		}
		scope = scope.upper;
	}
};

const TYPES = new Set(["javascript"]);

class ConcatenatedModule extends Module {
	/**
	 * @param {Module} rootModule the root module of the concatenation
	 * @param {Set<Module>} modules all modules in the concatenation (including the root module)
	 * @param {RuntimeSpec} runtime the runtime
	 * @param {object=} associatedObjectForCache object for caching
	 * @param {string | HashConstructor=} hashFunction hash function to use
	 * @returns {ConcatenatedModule} the module
	 */
	static create(
		rootModule,
		modules,
		runtime,
		associatedObjectForCache,
		hashFunction = "md4"
	) {
		const identifier = ConcatenatedModule._createIdentifier(
			rootModule,
			modules,
			associatedObjectForCache,
			hashFunction
		);
		return new ConcatenatedModule({
			identifier,
			rootModule,
			modules,
			runtime
		});
	}

	/**
	 * @param {object} options options
	 * @param {string} options.identifier the identifier of the module
	 * @param {Module=} options.rootModule the root module of the concatenation
	 * @param {RuntimeSpec} options.runtime the selected runtime
	 * @param {Set<Module>=} options.modules all concatenated modules
	 */
	constructor({ identifier, rootModule, modules, runtime }) {
		super(JAVASCRIPT_MODULE_TYPE_ESM, null, rootModule && rootModule.layer);

		// Info from Factory
		/** @type {string} */
		this._identifier = identifier;
		/** @type {Module} */
		this.rootModule = rootModule;
		/** @type {Set<Module>} */
		this._modules = modules;
		this._runtime = runtime;
		this.factoryMeta = rootModule && rootModule.factoryMeta;
	}

	/**
	 * Assuming this module is in the cache. Update the (cached) module with
	 * the fresh module from the factory. Usually updates internal references
	 * and properties.
	 * @param {Module} module fresh module
	 * @returns {void}
	 */
	updateCacheModule(module) {
		throw new Error("Must not be called");
	}

	/**
	 * @returns {SourceTypes} types available (do not mutate)
	 */
	getSourceTypes() {
		return TYPES;
	}

	get modules() {
		return Array.from(this._modules);
	}

	/**
	 * @returns {string} a unique identifier of the module
	 */
	identifier() {
		return this._identifier;
	}

	/**
	 * @param {RequestShortener} requestShortener the request shortener
	 * @returns {string} a user readable identifier of the module
	 */
	readableIdentifier(requestShortener) {
		return (
			this.rootModule.readableIdentifier(requestShortener) +
			` + ${this._modules.size - 1} modules`
		);
	}

	/**
	 * @param {LibIdentOptions} options options
	 * @returns {string | null} an identifier for library inclusion
	 */
	libIdent(options) {
		return this.rootModule.libIdent(options);
	}

	/**
	 * @returns {string | null} absolute path which should be used for condition matching (usually the resource path)
	 */
	nameForCondition() {
		return this.rootModule.nameForCondition();
	}

	/**
	 * @param {ModuleGraph} moduleGraph the module graph
	 * @returns {ConnectionState} how this module should be connected to referencing modules when consumed for side-effects only
	 */
	getSideEffectsConnectionState(moduleGraph) {
		return this.rootModule.getSideEffectsConnectionState(moduleGraph);
	}

	/**
	 * @param {WebpackOptions} options webpack options
	 * @param {Compilation} compilation the compilation
	 * @param {ResolverWithOptions} resolver the resolver
	 * @param {InputFileSystem} fs the file system
	 * @param {function(WebpackError=): void} callback callback function
	 * @returns {void}
	 */
	build(options, compilation, resolver, fs, callback) {
		const { rootModule } = this;
		const { moduleArgument, exportsArgument } =
			/** @type {BuildInfo} */
			(rootModule.buildInfo);
		this.buildInfo = {
			strict: true,
			cacheable: true,
			moduleArgument,
			exportsArgument,
			fileDependencies: new LazySet(),
			contextDependencies: new LazySet(),
			missingDependencies: new LazySet(),
			topLevelDeclarations: new Set(),
			assets: undefined
		};
		this.buildMeta = rootModule.buildMeta;
		this.clearDependenciesAndBlocks();
		this.clearWarningsAndErrors();

		for (const m of this._modules) {
			// populate cacheable
			if (!(/** @type {BuildInfo} */ (m.buildInfo).cacheable)) {
				this.buildInfo.cacheable = false;
			}

			// populate dependencies
			for (const d of m.dependencies.filter(
				dep =>
					!(dep instanceof HarmonyImportDependency) ||
					!this._modules.has(
						/** @type {Module} */ (compilation.moduleGraph.getModule(dep))
					)
			)) {
				this.dependencies.push(d);
			}
			// populate blocks
			for (const d of m.blocks) {
				this.blocks.push(d);
			}

			// populate warnings
			const warnings = m.getWarnings();
			if (warnings !== undefined) {
				for (const warning of warnings) {
					this.addWarning(warning);
				}
			}

			// populate errors
			const errors = m.getErrors();
			if (errors !== undefined) {
				for (const error of errors) {
					this.addError(error);
				}
			}

			const { assets, assetsInfo, topLevelDeclarations } =
				/** @type {BuildInfo} */ (m.buildInfo);

			// populate topLevelDeclarations
			if (topLevelDeclarations) {
				const topLevelDeclarations = this.buildInfo.topLevelDeclarations;
				if (topLevelDeclarations !== undefined) {
					for (const decl of topLevelDeclarations) {
						topLevelDeclarations.add(decl);
					}
				}
			} else {
				this.buildInfo.topLevelDeclarations = undefined;
			}

			// populate assets
			if (assets) {
				if (this.buildInfo.assets === undefined) {
					this.buildInfo.assets = Object.create(null);
				}
				Object.assign(/** @type {BuildInfo} */ (this.buildInfo).assets, assets);
			}
			if (assetsInfo) {
				if (this.buildInfo.assetsInfo === undefined) {
					this.buildInfo.assetsInfo = new Map();
				}
				for (const [key, value] of assetsInfo) {
					this.buildInfo.assetsInfo.set(key, value);
				}
			}
		}
		callback();
	}

	/**
	 * @param {string=} type the source type for which the size should be estimated
	 * @returns {number} the estimated size of the module (must be non-zero)
	 */
	size(type) {
		// Guess size from embedded modules
		let size = 0;
		for (const module of this._modules) {
			size += module.size(type);
		}
		return size;
	}

	/**
	 * @private
	 * @param {Module} rootModule the root of the concatenation
	 * @param {Set<Module>} modulesSet a set of modules which should be concatenated
	 * @param {RuntimeSpec} runtime for this runtime
	 * @param {ModuleGraph} moduleGraph the module graph
	 * @returns {ConcatenationEntry[]} concatenation list
	 */
	_createConcatenationList(rootModule, modulesSet, runtime, moduleGraph) {
		/** @type {ConcatenationEntry[]} */
		const list = [];
		/** @type {Map<Module, RuntimeSpec | true>} */
		const existingEntries = new Map();

		/**
		 * @param {Module} module a module
		 * @returns {Iterable<{ connection: ModuleGraphConnection, runtimeCondition: RuntimeSpec | true }>} imported modules in order
		 */
		const getConcatenatedImports = module => {
			let connections = Array.from(moduleGraph.getOutgoingConnections(module));
			if (module === rootModule) {
				for (const c of moduleGraph.getOutgoingConnections(this))
					connections.push(c);
			}
			/**
			 * @type {Array<{ connection: ModuleGraphConnection, sourceOrder: number, rangeStart: number }>}
			 */
			const references = connections
				.filter(connection => {
					if (!(connection.dependency instanceof HarmonyImportDependency))
						return false;
					return (
						connection &&
						connection.resolvedOriginModule === module &&
						connection.module &&
						connection.isTargetActive(runtime)
					);
				})
				.map(connection => {
					const dep = /** @type {HarmonyImportDependency} */ (
						connection.dependency
					);
					return {
						connection,
						sourceOrder: dep.sourceOrder,
						rangeStart: dep.range && dep.range[0]
					};
				});
			/**
			 * bySourceOrder
			 * @example
			 * import a from "a"; // sourceOrder=1
			 * import b from "b"; // sourceOrder=2
			 *
			 * byRangeStart
			 * @example
			 * import {a, b} from "a"; // sourceOrder=1
			 * a.a(); // first range
			 * b.b(); // second range
			 *
			 * If there is no reexport, we have the same source.
			 * If there is reexport, but module has side effects, this will lead to reexport module only.
			 * If there is side-effects-free reexport, we can get simple deterministic result with range start comparison.
			 */
			references.sort(concatComparators(bySourceOrder, byRangeStart));
			/** @type {Map<Module, { connection: ModuleGraphConnection, runtimeCondition: RuntimeSpec | true }>} */
			const referencesMap = new Map();
			for (const { connection } of references) {
				const runtimeCondition = filterRuntime(runtime, r =>
					connection.isTargetActive(r)
				);
				if (runtimeCondition === false) continue;
				const module = connection.module;
				const entry = referencesMap.get(module);
				if (entry === undefined) {
					referencesMap.set(module, { connection, runtimeCondition });
					continue;
				}
				entry.runtimeCondition = mergeRuntimeConditionNonFalse(
					entry.runtimeCondition,
					runtimeCondition,
					runtime
				);
			}
			return referencesMap.values();
		};

		/**
		 * @param {ModuleGraphConnection} connection graph connection
		 * @param {RuntimeSpec | true} runtimeCondition runtime condition
		 * @returns {void}
		 */
		const enterModule = (connection, runtimeCondition) => {
			const module = connection.module;
			if (!module) return;
			const existingEntry = existingEntries.get(module);
			if (existingEntry === true) {
				return;
			}
			if (modulesSet.has(module)) {
				existingEntries.set(module, true);
				if (runtimeCondition !== true) {
					throw new Error(
						`Cannot runtime-conditional concatenate a module (${module.identifier()} in ${this.rootModule.identifier()}, ${runtimeConditionToString(
							runtimeCondition
						)}). This should not happen.`
					);
				}
				const imports = getConcatenatedImports(module);
				for (const { connection, runtimeCondition } of imports)
					enterModule(connection, runtimeCondition);
				list.push({
					type: "concatenated",
					module: connection.module,
					runtimeCondition
				});
			} else {
				if (existingEntry !== undefined) {
					const reducedRuntimeCondition = subtractRuntimeCondition(
						runtimeCondition,
						existingEntry,
						runtime
					);
					if (reducedRuntimeCondition === false) return;
					runtimeCondition = reducedRuntimeCondition;
					existingEntries.set(
						connection.module,
						mergeRuntimeConditionNonFalse(
							existingEntry,
							runtimeCondition,
							runtime
						)
					);
				} else {
					existingEntries.set(connection.module, runtimeCondition);
				}
				if (list.length > 0) {
					const lastItem = list[list.length - 1];
					if (
						lastItem.type === "external" &&
						lastItem.module === connection.module
					) {
						lastItem.runtimeCondition = mergeRuntimeCondition(
							lastItem.runtimeCondition,
							runtimeCondition,
							runtime
						);
						return;
					}
				}
				list.push({
					type: "external",
					get module() {
						// We need to use a getter here, because the module in the dependency
						// could be replaced by some other process (i. e. also replaced with a
						// concatenated module)
						return connection.module;
					},
					runtimeCondition
				});
			}
		};

		existingEntries.set(rootModule, true);
		const imports = getConcatenatedImports(rootModule);
		for (const { connection, runtimeCondition } of imports)
			enterModule(connection, runtimeCondition);
		list.push({
			type: "concatenated",
			module: rootModule,
			runtimeCondition: true
		});

		return list;
	}

	/**
	 * @param {Module} rootModule the root module of the concatenation
	 * @param {Set<Module>} modules all modules in the concatenation (including the root module)
	 * @param {object=} associatedObjectForCache object for caching
	 * @param {string | HashConstructor=} hashFunction hash function to use
	 * @returns {string} the identifier
	 */
	static _createIdentifier(
		rootModule,
		modules,
		associatedObjectForCache,
		hashFunction = "md4"
	) {
		const cachedMakePathsRelative = makePathsRelative.bindContextCache(
			/** @type {string} */ (rootModule.context),
			associatedObjectForCache
		);
		let identifiers = [];
		for (const module of modules) {
			identifiers.push(cachedMakePathsRelative(module.identifier()));
		}
		identifiers.sort();
		const hash = createHash(hashFunction);
		hash.update(identifiers.join(" "));
		return rootModule.identifier() + "|" + hash.digest("hex");
	}

	/**
	 * @param {LazySet<string>} fileDependencies set where file dependencies are added to
	 * @param {LazySet<string>} contextDependencies set where context dependencies are added to
	 * @param {LazySet<string>} missingDependencies set where missing dependencies are added to
	 * @param {LazySet<string>} buildDependencies set where build dependencies are added to
	 */
	addCacheDependencies(
		fileDependencies,
		contextDependencies,
		missingDependencies,
		buildDependencies
	) {
		for (const module of this._modules) {
			module.addCacheDependencies(
				fileDependencies,
				contextDependencies,
				missingDependencies,
				buildDependencies
			);
		}
	}

	/**
	 * @param {CodeGenerationContext} context context for code generation
	 * @returns {CodeGenerationResult} result
	 */
	codeGeneration({
		dependencyTemplates,
		runtimeTemplate,
		moduleGraph,
		chunkGraph,
		runtime: generationRuntime,
		codeGenerationResults
	}) {
		/** @type {Set<string>} */
		const runtimeRequirements = new Set();
		const runtime = intersectRuntime(generationRuntime, this._runtime);

		const requestShortener = runtimeTemplate.requestShortener;
		// Meta info for each module
		const [modulesWithInfo, moduleToInfoMap] = this._getModulesWithInfo(
			moduleGraph,
			runtime
		);

		// Set with modules that need a generated namespace object
		/** @type {Set<ConcatenatedModuleInfo>} */
		const neededNamespaceObjects = new Set();

		// Generate source code and analyse scopes
		// Prepare a ReplaceSource for the final source
		for (const info of moduleToInfoMap.values()) {
			this._analyseModule(
				moduleToInfoMap,
				info,
				dependencyTemplates,
				runtimeTemplate,
				moduleGraph,
				chunkGraph,
				runtime,
				codeGenerationResults
			);
		}

		// List of all used names to avoid conflicts
		const allUsedNames = new Set(RESERVED_NAMES);
		// Updated Top level declarations are created by renaming
		const topLevelDeclarations = new Set();

		// List of additional names in scope for module references
		/** @type {Map<string, { usedNames: UsedNames, alreadyCheckedScopes: Set<TODO> }>} */
		const usedNamesInScopeInfo = new Map();
		/**
		 * @param {string} module module identifier
		 * @param {string} id export id
		 * @returns {{ usedNames: UsedNames, alreadyCheckedScopes: Set<TODO> }} info
		 */
		const getUsedNamesInScopeInfo = (module, id) => {
			const key = `${module}-${id}`;
			let info = usedNamesInScopeInfo.get(key);
			if (info === undefined) {
				info = {
					usedNames: new Set(),
					alreadyCheckedScopes: new Set()
				};
				usedNamesInScopeInfo.set(key, info);
			}
			return info;
		};

		// Set of already checked scopes
		const ignoredScopes = new Set();

		// get all global names
		for (const info of modulesWithInfo) {
			if (info.type === "concatenated") {
				// ignore symbols from moduleScope
				if (info.moduleScope) {
					ignoredScopes.add(info.moduleScope);
				}

				// The super class expression in class scopes behaves weird
				// We get ranges of all super class expressions to make
				// renaming to work correctly
				const superClassCache = new WeakMap();
				/**
				 * @param {Scope} scope scope
				 * @returns {TODO} result
				 */
				const getSuperClassExpressions = scope => {
					const cacheEntry = superClassCache.get(scope);
					if (cacheEntry !== undefined) return cacheEntry;
					const superClassExpressions = [];
					for (const childScope of scope.childScopes) {
						if (childScope.type !== "class") continue;
						const block = childScope.block;
						if (
							(block.type === "ClassDeclaration" ||
								block.type === "ClassExpression") &&
							block.superClass
						) {
							superClassExpressions.push({
								range: block.superClass.range,
								variables: childScope.variables
							});
						}
					}
					superClassCache.set(scope, superClassExpressions);
					return superClassExpressions;
				};

				// add global symbols
				if (info.globalScope) {
					for (const reference of info.globalScope.through) {
						const name = reference.identifier.name;
						if (ConcatenationScope.isModuleReference(name)) {
							const match = ConcatenationScope.matchModuleReference(name);
							if (!match) continue;
							const referencedInfo = modulesWithInfo[match.index];
							if (referencedInfo.type === "reference")
								throw new Error("Module reference can't point to a reference");
							const binding = getFinalBinding(
								moduleGraph,
								referencedInfo,
								match.ids,
								moduleToInfoMap,
								runtime,
								requestShortener,
								runtimeTemplate,
								neededNamespaceObjects,
								false,
								/** @type {BuildMeta} */
								(info.module.buildMeta).strictHarmonyModule,
								true
							);
							if (!binding.ids) continue;
							const { usedNames, alreadyCheckedScopes } =
								getUsedNamesInScopeInfo(
									binding.info.module.identifier(),
									"name" in binding ? binding.name : ""
								);
							for (const expr of getSuperClassExpressions(reference.from)) {
								if (
									expr.range[0] <=
										/** @type {Range} */ (reference.identifier.range)[0] &&
									expr.range[1] >=
										/** @type {Range} */ (reference.identifier.range)[1]
								) {
									for (const variable of expr.variables) {
										usedNames.add(variable.name);
									}
								}
							}
							addScopeSymbols(
								reference.from,
								usedNames,
								alreadyCheckedScopes,
								ignoredScopes
							);
						} else {
							allUsedNames.add(name);
						}
					}
				}
			}
		}

		// generate names for symbols
		for (const info of moduleToInfoMap.values()) {
			const { usedNames: namespaceObjectUsedNames } = getUsedNamesInScopeInfo(
				info.module.identifier(),
				""
			);
			switch (info.type) {
				case "concatenated": {
					for (const variable of info.moduleScope.variables) {
						const name = variable.name;
						const { usedNames, alreadyCheckedScopes } = getUsedNamesInScopeInfo(
							info.module.identifier(),
							name
						);
						if (allUsedNames.has(name) || usedNames.has(name)) {
							const references = getAllReferences(variable);
							for (const ref of references) {
								addScopeSymbols(
									ref.from,
									usedNames,
									alreadyCheckedScopes,
									ignoredScopes
								);
							}
							const newName = this.findNewName(
								name,
								allUsedNames,
								usedNames,
								info.module.readableIdentifier(requestShortener)
							);
							allUsedNames.add(newName);
							info.internalNames.set(name, newName);
							topLevelDeclarations.add(newName);
							const source = info.source;
							const allIdentifiers = new Set(
								references.map(r => r.identifier).concat(variable.identifiers)
							);
							for (const identifier of allIdentifiers) {
								const r = /** @type {Range} */ (identifier.range);
								const path = getPathInAst(info.ast, identifier);
								if (path && path.length > 1) {
									const maybeProperty =
										path[1].type === "AssignmentPattern" &&
										path[1].left === path[0]
											? path[2]
											: path[1];
									if (
										maybeProperty.type === "Property" &&
										maybeProperty.shorthand
									) {
										source.insert(r[1], `: ${newName}`);
										continue;
									}
								}
								source.replace(r[0], r[1] - 1, newName);
							}
						} else {
							allUsedNames.add(name);
							info.internalNames.set(name, name);
							topLevelDeclarations.add(name);
						}
					}
					let namespaceObjectName;
					if (info.namespaceExportSymbol) {
						namespaceObjectName = info.internalNames.get(
							info.namespaceExportSymbol
						);
					} else {
						namespaceObjectName = this.findNewName(
							"namespaceObject",
							allUsedNames,
							namespaceObjectUsedNames,
							info.module.readableIdentifier(requestShortener)
						);
						allUsedNames.add(namespaceObjectName);
					}
					info.namespaceObjectName =
						/** @type {string} */
						(namespaceObjectName);
					topLevelDeclarations.add(namespaceObjectName);
					break;
				}
				case "external": {
					const externalName = this.findNewName(
						"",
						allUsedNames,
						namespaceObjectUsedNames,
						info.module.readableIdentifier(requestShortener)
					);
					allUsedNames.add(externalName);
					info.name = externalName;
					topLevelDeclarations.add(externalName);
					break;
				}
			}
			const buildMeta = /** @type {BuildMeta} */ (info.module.buildMeta);
			if (buildMeta.exportsType !== "namespace") {
				const externalNameInterop = this.findNewName(
					"namespaceObject",
					allUsedNames,
					namespaceObjectUsedNames,
					info.module.readableIdentifier(requestShortener)
				);
				allUsedNames.add(externalNameInterop);
				info.interopNamespaceObjectName = externalNameInterop;
				topLevelDeclarations.add(externalNameInterop);
			}
			if (
				buildMeta.exportsType === "default" &&
				buildMeta.defaultObject !== "redirect"
			) {
				const externalNameInterop = this.findNewName(
					"namespaceObject2",
					allUsedNames,
					namespaceObjectUsedNames,
					info.module.readableIdentifier(requestShortener)
				);
				allUsedNames.add(externalNameInterop);
				info.interopNamespaceObject2Name = externalNameInterop;
				topLevelDeclarations.add(externalNameInterop);
			}
			if (buildMeta.exportsType === "dynamic" || !buildMeta.exportsType) {
				const externalNameInterop = this.findNewName(
					"default",
					allUsedNames,
					namespaceObjectUsedNames,
					info.module.readableIdentifier(requestShortener)
				);
				allUsedNames.add(externalNameInterop);
				info.interopDefaultAccessName = externalNameInterop;
				topLevelDeclarations.add(externalNameInterop);
			}
		}

		// Find and replace references to modules
		for (const info of moduleToInfoMap.values()) {
			if (info.type === "concatenated") {
				for (const reference of info.globalScope.through) {
					const name = reference.identifier.name;
					const match = ConcatenationScope.matchModuleReference(name);
					if (match) {
						const referencedInfo = modulesWithInfo[match.index];
						if (referencedInfo.type === "reference")
							throw new Error("Module reference can't point to a reference");
						const finalName = getFinalName(
							moduleGraph,
							referencedInfo,
							match.ids,
							moduleToInfoMap,
							runtime,
							requestShortener,
							runtimeTemplate,
							neededNamespaceObjects,
							match.call,
							!match.directImport,
							/** @type {BuildMeta} */
							(info.module.buildMeta).strictHarmonyModule,
							match.asiSafe
						);
						const r = /** @type {Range} */ (reference.identifier.range);
						const source = info.source;
						// range is extended by 2 chars to cover the appended "._"
						source.replace(r[0], r[1] + 1, finalName);
					}
				}
			}
		}

		// Map with all root exposed used exports
		/** @type {Map<string, function(RequestShortener): string>} */
		const exportsMap = new Map();

		// Set with all root exposed unused exports
		/** @type {Set<string>} */
		const unusedExports = new Set();

		const rootInfo = /** @type {ConcatenatedModuleInfo} */ (
			moduleToInfoMap.get(this.rootModule)
		);
		const strictHarmonyModule =
			/** @type {BuildMeta} */
			(rootInfo.module.buildMeta).strictHarmonyModule;
		const exportsInfo = moduleGraph.getExportsInfo(rootInfo.module);
		for (const exportInfo of exportsInfo.orderedExports) {
			const name = exportInfo.name;
			if (exportInfo.provided === false) continue;
			const used = exportInfo.getUsedName(undefined, runtime);
			if (!used) {
				unusedExports.add(name);
				continue;
			}
			exportsMap.set(used, requestShortener => {
				try {
					const finalName = getFinalName(
						moduleGraph,
						rootInfo,
						[name],
						moduleToInfoMap,
						runtime,
						requestShortener,
						runtimeTemplate,
						neededNamespaceObjects,
						false,
						false,
						strictHarmonyModule,
						true
					);
					return `/* ${
						exportInfo.isReexport() ? "reexport" : "binding"
					} */ ${finalName}`;
				} catch (e) {
					/** @type {Error} */
					(e).message +=
						`\nwhile generating the root export '${name}' (used name: '${used}')`;
					throw e;
				}
			});
		}

		const result = new ConcatSource();

		// add harmony compatibility flag (must be first because of possible circular dependencies)
		if (
			moduleGraph.getExportsInfo(this).otherExportsInfo.getUsed(runtime) !==
			UsageState.Unused
		) {
			result.add(`// ESM COMPAT FLAG\n`);
			result.add(
				runtimeTemplate.defineEsModuleFlagStatement({
					exportsArgument: this.exportsArgument,
					runtimeRequirements
				})
			);
		}

		// define exports
		if (exportsMap.size > 0) {
			runtimeRequirements.add(RuntimeGlobals.exports);
			runtimeRequirements.add(RuntimeGlobals.definePropertyGetters);
			const definitions = [];
			for (const [key, value] of exportsMap) {
				definitions.push(
					`\n  ${propertyName(key)}: ${runtimeTemplate.returningFunction(
						value(requestShortener)
					)}`
				);
			}
			result.add(`\n// EXPORTS\n`);
			result.add(
				`${RuntimeGlobals.definePropertyGetters}(${
					this.exportsArgument
				}, {${definitions.join(",")}\n});\n`
			);
		}

		// list unused exports
		if (unusedExports.size > 0) {
			result.add(
				`\n// UNUSED EXPORTS: ${joinIterableWithComma(unusedExports)}\n`
			);
		}

		// generate namespace objects
		const namespaceObjectSources = new Map();
		for (const info of neededNamespaceObjects) {
			if (info.namespaceExportSymbol) continue;
			const nsObj = [];
			const exportsInfo = moduleGraph.getExportsInfo(info.module);
			for (const exportInfo of exportsInfo.orderedExports) {
				if (exportInfo.provided === false) continue;
				const usedName = exportInfo.getUsedName(undefined, runtime);
				if (usedName) {
					const finalName = getFinalName(
						moduleGraph,
						info,
						[exportInfo.name],
						moduleToInfoMap,
						runtime,
						requestShortener,
						runtimeTemplate,
						neededNamespaceObjects,
						false,
						undefined,
						/** @type {BuildMeta} */
						(info.module.buildMeta).strictHarmonyModule,
						true
					);
					nsObj.push(
						`\n  ${propertyName(usedName)}: ${runtimeTemplate.returningFunction(
							finalName
						)}`
					);
				}
			}
			const name = info.namespaceObjectName;
			const defineGetters =
				nsObj.length > 0
					? `${RuntimeGlobals.definePropertyGetters}(${name}, {${nsObj.join(
							","
						)}\n});\n`
					: "";
			if (nsObj.length > 0)
				runtimeRequirements.add(RuntimeGlobals.definePropertyGetters);
			namespaceObjectSources.set(
				info,
				`
// NAMESPACE OBJECT: ${info.module.readableIdentifier(requestShortener)}
var ${name} = {};
${RuntimeGlobals.makeNamespaceObject}(${name});
${defineGetters}`
			);
			runtimeRequirements.add(RuntimeGlobals.makeNamespaceObject);
		}

		// define required namespace objects (must be before evaluation modules)
		for (const info of modulesWithInfo) {
			if (info.type === "concatenated") {
				const source = namespaceObjectSources.get(info);
				if (!source) continue;
				result.add(source);
			}
		}

		const chunkInitFragments = [];

		// evaluate modules in order
		for (const rawInfo of modulesWithInfo) {
			let name;
			let isConditional = false;
			const info = rawInfo.type === "reference" ? rawInfo.target : rawInfo;
			switch (info.type) {
				case "concatenated": {
					result.add(
						`\n;// CONCATENATED MODULE: ${info.module.readableIdentifier(
							requestShortener
						)}\n`
					);
					result.add(info.source);
					if (info.chunkInitFragments) {
						for (const f of info.chunkInitFragments) chunkInitFragments.push(f);
					}
					if (info.runtimeRequirements) {
						for (const r of info.runtimeRequirements) {
							runtimeRequirements.add(r);
						}
					}
					name = info.namespaceObjectName;
					break;
				}
				case "external": {
					result.add(
						`\n// EXTERNAL MODULE: ${info.module.readableIdentifier(
							requestShortener
						)}\n`
					);
					runtimeRequirements.add(RuntimeGlobals.require);
					const { runtimeCondition } =
						/** @type {ExternalModuleInfo | ReferenceToModuleInfo} */ (rawInfo);
					const condition = runtimeTemplate.runtimeConditionExpression({
						chunkGraph,
						runtimeCondition,
						runtime,
						runtimeRequirements
					});
					if (condition !== "true") {
						isConditional = true;
						result.add(`if (${condition}) {\n`);
					}
					result.add(
						`var ${info.name} = ${RuntimeGlobals.require}(${JSON.stringify(
							chunkGraph.getModuleId(info.module)
						)});`
					);
					name = info.name;
					break;
				}
				default:
					// @ts-expect-error never is expected here
					throw new Error(`Unsupported concatenation entry type ${info.type}`);
			}
			if (info.interopNamespaceObjectUsed) {
				runtimeRequirements.add(RuntimeGlobals.createFakeNamespaceObject);
				result.add(
					`\nvar ${info.interopNamespaceObjectName} = /*#__PURE__*/${RuntimeGlobals.createFakeNamespaceObject}(${name}, 2);`
				);
			}
			if (info.interopNamespaceObject2Used) {
				runtimeRequirements.add(RuntimeGlobals.createFakeNamespaceObject);
				result.add(
					`\nvar ${info.interopNamespaceObject2Name} = /*#__PURE__*/${RuntimeGlobals.createFakeNamespaceObject}(${name});`
				);
			}
			if (info.interopDefaultAccessUsed) {
				runtimeRequirements.add(RuntimeGlobals.compatGetDefaultExport);
				result.add(
					`\nvar ${info.interopDefaultAccessName} = /*#__PURE__*/${RuntimeGlobals.compatGetDefaultExport}(${name});`
				);
			}
			if (isConditional) {
				result.add("\n}");
			}
		}

		const data = new Map();
		if (chunkInitFragments.length > 0)
			data.set("chunkInitFragments", chunkInitFragments);
		data.set("topLevelDeclarations", topLevelDeclarations);

		/** @type {CodeGenerationResult} */
		const resultEntry = {
			sources: new Map([["javascript", new CachedSource(result)]]),
			data,
			runtimeRequirements
		};

		return resultEntry;
	}

	/**
	 * @param {Map<Module, ModuleInfo>} modulesMap modulesMap
	 * @param {ModuleInfo} info info
	 * @param {DependencyTemplates} dependencyTemplates dependencyTemplates
	 * @param {RuntimeTemplate} runtimeTemplate runtimeTemplate
	 * @param {ModuleGraph} moduleGraph moduleGraph
	 * @param {ChunkGraph} chunkGraph chunkGraph
	 * @param {RuntimeSpec} runtime runtime
	 * @param {CodeGenerationResults} codeGenerationResults codeGenerationResults
	 */
	_analyseModule(
		modulesMap,
		info,
		dependencyTemplates,
		runtimeTemplate,
		moduleGraph,
		chunkGraph,
		runtime,
		codeGenerationResults
	) {
		if (info.type === "concatenated") {
			const m = info.module;
			try {
				// Create a concatenation scope to track and capture information
				const concatenationScope = new ConcatenationScope(modulesMap, info);

				// TODO cache codeGeneration results
				const codeGenResult = m.codeGeneration({
					dependencyTemplates,
					runtimeTemplate,
					moduleGraph,
					chunkGraph,
					runtime,
					concatenationScope,
					codeGenerationResults,
					sourceTypes: TYPES
				});
				const source = /** @type {Source} */ (
					codeGenResult.sources.get("javascript")
				);
				const data = codeGenResult.data;
				const chunkInitFragments = data && data.get("chunkInitFragments");
				const code = source.source().toString();
				let ast;
				try {
					ast = JavascriptParser._parse(code, {
						sourceType: "module"
					});
				} catch (err) {
					if (
						err.loc &&
						typeof err.loc === "object" &&
						typeof err.loc.line === "number"
					) {
						const lineNumber = err.loc.line;
						const lines = code.split("\n");
						err.message +=
							"\n| " +
							lines
								.slice(Math.max(0, lineNumber - 3), lineNumber + 2)
								.join("\n| ");
					}
					throw err;
				}
				const scopeManager = eslintScope.analyze(ast, {
					ecmaVersion: 6,
					sourceType: "module",
					optimistic: true,
					ignoreEval: true,
					impliedStrict: true
				});
				const globalScope = /** @type {Scope} */ (scopeManager.acquire(ast));
				const moduleScope = globalScope.childScopes[0];
				const resultSource = new ReplaceSource(source);
				info.runtimeRequirements = codeGenResult.runtimeRequirements;
				info.ast = ast;
				info.internalSource = source;
				info.source = resultSource;
				info.chunkInitFragments = chunkInitFragments;
				info.globalScope = globalScope;
				info.moduleScope = moduleScope;
			} catch (err) {
				/** @type {Error} */
				(err).message +=
					`\nwhile analyzing module ${m.identifier()} for concatenation`;
				throw err;
			}
		}
	}

	/**
	 * @param {ModuleGraph} moduleGraph the module graph
	 * @param {RuntimeSpec} runtime the runtime
	 * @returns {[ModuleInfoOrReference[], Map<Module, ModuleInfo>]} module info items
	 */
	_getModulesWithInfo(moduleGraph, runtime) {
		const orderedConcatenationList = this._createConcatenationList(
			this.rootModule,
			this._modules,
			runtime,
			moduleGraph
		);
		/** @type {Map<Module, ModuleInfo>} */
		const map = new Map();
		const list = orderedConcatenationList.map((info, index) => {
			let item = map.get(info.module);
			if (item === undefined) {
				switch (info.type) {
					case "concatenated":
						item = {
							type: "concatenated",
							module: info.module,
							index,
							ast: undefined,
							internalSource: undefined,
							runtimeRequirements: undefined,
							source: undefined,
							globalScope: undefined,
							moduleScope: undefined,
							internalNames: new Map(),
							exportMap: undefined,
							rawExportMap: undefined,
							namespaceExportSymbol: undefined,
							namespaceObjectName: undefined,
							interopNamespaceObjectUsed: false,
							interopNamespaceObjectName: undefined,
							interopNamespaceObject2Used: false,
							interopNamespaceObject2Name: undefined,
							interopDefaultAccessUsed: false,
							interopDefaultAccessName: undefined
						};
						break;
					case "external":
						item = {
							type: "external",
							module: info.module,
							runtimeCondition: info.runtimeCondition,
							index,
							name: undefined,
							interopNamespaceObjectUsed: false,
							interopNamespaceObjectName: undefined,
							interopNamespaceObject2Used: false,
							interopNamespaceObject2Name: undefined,
							interopDefaultAccessUsed: false,
							interopDefaultAccessName: undefined
						};
						break;
					default:
						throw new Error(
							`Unsupported concatenation entry type ${info.type}`
						);
				}
				map.set(
					/** @type {ModuleInfo} */ (item).module,
					/** @type {ModuleInfo} */ (item)
				);
				return item;
			} else {
				/** @type {ReferenceToModuleInfo} */
				const ref = {
					type: "reference",
					runtimeCondition: info.runtimeCondition,
					target: item
				};
				return ref;
			}
		});
		return [list, map];
	}

	/**
	 * @param {string} oldName old name
	 * @param {UsedNames} usedNamed1 used named 1
	 * @param {UsedNames} usedNamed2 used named 2
	 * @param {string} extraInfo extra info
	 * @returns {string} found new name
	 */
	findNewName(oldName, usedNamed1, usedNamed2, extraInfo) {
		let name = oldName;

		if (name === ConcatenationScope.DEFAULT_EXPORT) {
			name = "";
		}
		if (name === ConcatenationScope.NAMESPACE_OBJECT_EXPORT) {
			name = "namespaceObject";
		}

		// Remove uncool stuff
		extraInfo = extraInfo.replace(
			/\.+\/|(\/index)?\.([a-zA-Z0-9]{1,4})($|\s|\?)|\s*\+\s*\d+\s*modules/g,
			""
		);

		const splittedInfo = extraInfo.split("/");
		while (splittedInfo.length) {
			name = splittedInfo.pop() + (name ? "_" + name : "");
			const nameIdent = Template.toIdentifier(name);
			if (
				!usedNamed1.has(nameIdent) &&
				(!usedNamed2 || !usedNamed2.has(nameIdent))
			)
				return nameIdent;
		}

		let i = 0;
		let nameWithNumber = Template.toIdentifier(`${name}_${i}`);
		while (
			usedNamed1.has(nameWithNumber) ||
			(usedNamed2 && usedNamed2.has(nameWithNumber))
		) {
			i++;
			nameWithNumber = Template.toIdentifier(`${name}_${i}`);
		}
		return nameWithNumber;
	}

	/**
	 * @param {Hash} hash the hash used to track dependencies
	 * @param {UpdateHashContext} context context
	 * @returns {void}
	 */
	updateHash(hash, context) {
		const { chunkGraph, runtime } = context;
		for (const info of this._createConcatenationList(
			this.rootModule,
			this._modules,
			intersectRuntime(runtime, this._runtime),
			chunkGraph.moduleGraph
		)) {
			switch (info.type) {
				case "concatenated":
					info.module.updateHash(hash, context);
					break;
				case "external":
					hash.update(`${chunkGraph.getModuleId(info.module)}`);
					// TODO runtimeCondition
					break;
			}
		}
		super.updateHash(hash, context);
	}

	/**
	 * @param {ObjectDeserializerContext} context context
	 * @returns {ConcatenatedModule} ConcatenatedModule
	 */
	static deserialize(context) {
		const obj = new ConcatenatedModule({
			identifier: undefined,
			rootModule: undefined,
			modules: undefined,
			runtime: undefined
		});
		obj.deserialize(context);
		return obj;
	}
}

makeSerializable(ConcatenatedModule, "webpack/lib/optimize/ConcatenatedModule");

module.exports = ConcatenatedModule;


code:
/*
	MIT License http://www.opensource.org/licenses/mit-license.php
	Author Tobias Koppers @sokra
*/

"use strict";

const { STAGE_BASIC } = require("../OptimizationStages");

/** @typedef {import("../Chunk")} Chunk */
/** @typedef {import("../ChunkGroup")} ChunkGroup */
/** @typedef {import("../Compiler")} Compiler */

class EnsureChunkConditionsPlugin {
	/**
	 * Apply the plugin
	 * @param {Compiler} compiler the compiler instance
	 * @returns {void}
	 */
	apply(compiler) {
		compiler.hooks.compilation.tap(
			"EnsureChunkConditionsPlugin",
			compilation => {
				/**
				 * @param {Iterable<Chunk>} chunks the chunks
				 */
				const handler = chunks => {
					const chunkGraph = compilation.chunkGraph;
					// These sets are hoisted here to save memory
					// They are cleared at the end of every loop
					/** @type {Set<Chunk>} */
					const sourceChunks = new Set();
					/** @type {Set<ChunkGroup>} */
					const chunkGroups = new Set();
					for (const module of compilation.modules) {
						if (!module.hasChunkCondition()) continue;
						for (const chunk of chunkGraph.getModuleChunksIterable(module)) {
							if (!module.chunkCondition(chunk, compilation)) {
								sourceChunks.add(chunk);
								for (const group of chunk.groupsIterable) {
									chunkGroups.add(group);
								}
							}
						}
						if (sourceChunks.size === 0) continue;
						/** @type {Set<Chunk>} */
						const targetChunks = new Set();
						chunkGroupLoop: for (const chunkGroup of chunkGroups) {
							// Can module be placed in a chunk of this group?
							for (const chunk of chunkGroup.chunks) {
								if (module.chunkCondition(chunk, compilation)) {
									targetChunks.add(chunk);
									continue chunkGroupLoop;
								}
							}
							// We reached the entrypoint: fail
							if (chunkGroup.isInitial()) {
								throw new Error(
									"Cannot fulfil chunk condition of " + module.identifier()
								);
							}
							// Try placing in all parents
							for (const group of chunkGroup.parentsIterable) {
								chunkGroups.add(group);
							}
						}
						for (const sourceChunk of sourceChunks) {
							chunkGraph.disconnectChunkAndModule(sourceChunk, module);
						}
						for (const targetChunk of targetChunks) {
							chunkGraph.connectChunkAndModule(targetChunk, module);
						}
						sourceChunks.clear();
						chunkGroups.clear();
					}
				};
				compilation.hooks.optimizeChunks.tap(
					{
						name: "EnsureChunkConditionsPlugin",
						stage: STAGE_BASIC
					},
					handler
				);
			}
		);
	}
}
module.exports = EnsureChunkConditionsPlugin;

license: mit


code:
/*
	MIT License http://www.opensource.org/licenses/mit-license.php
	Author Tobias Koppers @sokra
*/

"use strict";

/** @typedef {import("../Chunk")} Chunk */
/** @typedef {import("../Chunk").ChunkId} ChunkId */
/** @typedef {import("../Compiler")} Compiler */
/** @typedef {import("../Module")} Module */

class FlagIncludedChunksPlugin {
	/**
	 * Apply the plugin
	 * @param {Compiler} compiler the compiler instance
	 * @returns {void}
	 */
	apply(compiler) {
		compiler.hooks.compilation.tap("FlagIncludedChunksPlugin", compilation => {
			compilation.hooks.optimizeChunkIds.tap(
				"FlagIncludedChunksPlugin",
				chunks => {
					const chunkGraph = compilation.chunkGraph;

					// prepare two bit integers for each module
					// 2^31 is the max number represented as SMI in v8
					// we want the bits distributed this way:
					// the bit 2^31 is pretty rar and only one module should get it
					// so it has a probability of 1 / modulesCount
					// the first bit (2^0) is the easiest and every module could get it
					// if it doesn't get a better bit
					// from bit 2^n to 2^(n+1) there is a probability of p
					// so 1 / modulesCount == p^31
					// <=> p = sqrt31(1 / modulesCount)
					// so we use a modulo of 1 / sqrt31(1 / modulesCount)
					/** @type {WeakMap<Module, number>} */
					const moduleBits = new WeakMap();
					const modulesCount = compilation.modules.size;

					// precalculate the modulo values for each bit
					const modulo = 1 / Math.pow(1 / modulesCount, 1 / 31);
					const modulos = Array.from(
						{ length: 31 },
						(x, i) => Math.pow(modulo, i) | 0
					);

					// iterate all modules to generate bit values
					let i = 0;
					for (const module of compilation.modules) {
						let bit = 30;
						while (i % modulos[bit] !== 0) {
							bit--;
						}
						moduleBits.set(module, 1 << bit);
						i++;
					}

					// iterate all chunks to generate bitmaps
					/** @type {WeakMap<Chunk, number>} */
					const chunkModulesHash = new WeakMap();
					for (const chunk of chunks) {
						let hash = 0;
						for (const module of chunkGraph.getChunkModulesIterable(chunk)) {
							hash |= /** @type {number} */ (moduleBits.get(module));
						}
						chunkModulesHash.set(chunk, hash);
					}

					for (const chunkA of chunks) {
						const chunkAHash =
							/** @type {number} */
							(chunkModulesHash.get(chunkA));
						const chunkAModulesCount =
							chunkGraph.getNumberOfChunkModules(chunkA);
						if (chunkAModulesCount === 0) continue;
						let bestModule = undefined;
						for (const module of chunkGraph.getChunkModulesIterable(chunkA)) {
							if (
								bestModule === undefined ||
								chunkGraph.getNumberOfModuleChunks(bestModule) >
									chunkGraph.getNumberOfModuleChunks(module)
							)
								bestModule = module;
						}
						loopB: for (const chunkB of chunkGraph.getModuleChunksIterable(
							/** @type {Module} */ (bestModule)
						)) {
							// as we iterate the same iterables twice
							// skip if we find ourselves
							if (chunkA === chunkB) continue;

							const chunkBModulesCount =
								chunkGraph.getNumberOfChunkModules(chunkB);

							// ids for empty chunks are not included
							if (chunkBModulesCount === 0) continue;

							// instead of swapping A and B just bail
							// as we loop twice the current A will be B and B then A
							if (chunkAModulesCount > chunkBModulesCount) continue;

							// is chunkA in chunkB?

							// we do a cheap check for the hash value
							const chunkBHash =
								/** @type {number} */
								(chunkModulesHash.get(chunkB));
							if ((chunkBHash & chunkAHash) !== chunkAHash) continue;

							// compare all modules
							for (const m of chunkGraph.getChunkModulesIterable(chunkA)) {
								if (!chunkGraph.isModuleInChunk(m, chunkB)) continue loopB;
							}
							/** @type {ChunkId[]} */
							(chunkB.ids).push(/** @type {ChunkId} */ (chunkA.id));
						}
					}
				}
			);
		});
	}
}
module.exports = FlagIncludedChunksPlugin;


code:
/*
	MIT License http://www.opensource.org/licenses/mit-license.php
	Author Sergey Melyukov @smelukov
*/

"use strict";

const { UsageState } = require("../ExportsInfo");

/** @typedef {import("estree").Node} AnyNode */
/** @typedef {import("../Dependency")} Dependency */
/** @typedef {import("../Module")} Module */
/** @typedef {import("../ModuleGraph")} ModuleGraph */
/** @typedef {import("../ModuleGraphConnection")} ModuleGraphConnection */
/** @typedef {import("../ModuleGraphConnection").ConnectionState} ConnectionState */
/** @typedef {import("../Parser").ParserState} ParserState */
/** @typedef {import("../javascript/JavascriptParser")} JavascriptParser */
/** @typedef {import("../util/runtime").RuntimeSpec} RuntimeSpec */

/** @typedef {Map<TopLevelSymbol | null, Set<string | TopLevelSymbol> | true | undefined>} InnerGraph */
/** @typedef {function(boolean | Set<string> | undefined): void} UsageCallback */

/**
 * @typedef {object} StateObject
 * @property {InnerGraph} innerGraph
 * @property {TopLevelSymbol=} currentTopLevelSymbol
 * @property {Map<TopLevelSymbol, Set<UsageCallback>>} usageCallbackMap
 */

/** @typedef {false|StateObject} State */

/** @type {WeakMap<ParserState, State>} */
const parserStateMap = new WeakMap();
const topLevelSymbolTag = Symbol("top level symbol");

/**
 * @param {ParserState} parserState parser state
 * @returns {State | undefined} state
 */
function getState(parserState) {
	return parserStateMap.get(parserState);
}

/**
 * @param {ParserState} parserState parser state
 * @returns {void}
 */
exports.bailout = parserState => {
	parserStateMap.set(parserState, false);
};

/**
 * @param {ParserState} parserState parser state
 * @returns {void}
 */
exports.enable = parserState => {
	const state = parserStateMap.get(parserState);
	if (state === false) {
		return;
	}
	parserStateMap.set(parserState, {
		innerGraph: new Map(),
		currentTopLevelSymbol: undefined,
		usageCallbackMap: new Map()
	});
};

/**
 * @param {ParserState} parserState parser state
 * @returns {boolean} true, when enabled
 */
exports.isEnabled = parserState => {
	const state = parserStateMap.get(parserState);
	return !!state;
};

/**
 * @param {ParserState} state parser state
 * @param {TopLevelSymbol | null} symbol the symbol, or null for all symbols
 * @param {string | TopLevelSymbol | true} usage usage data
 * @returns {void}
 */
exports.addUsage = (state, symbol, usage) => {
	const innerGraphState = getState(state);

	if (innerGraphState) {
		const { innerGraph } = innerGraphState;
		const info = innerGraph.get(symbol);
		if (usage === true) {
			innerGraph.set(symbol, true);
		} else if (info === undefined) {
			innerGraph.set(symbol, new Set([usage]));
		} else if (info !== true) {
			info.add(usage);
		}
	}
};

/**
 * @param {JavascriptParser} parser the parser
 * @param {string} name name of variable
 * @param {string | TopLevelSymbol | true} usage usage data
 * @returns {void}
 */
exports.addVariableUsage = (parser, name, usage) => {
	const symbol =
		/** @type {TopLevelSymbol} */ (
			parser.getTagData(name, topLevelSymbolTag)
		) || exports.tagTopLevelSymbol(parser, name);
	if (symbol) {
		exports.addUsage(parser.state, symbol, usage);
	}
};

/**
 * @param {ParserState} state parser state
 * @returns {void}
 */
exports.inferDependencyUsage = state => {
	const innerGraphState = getState(state);

	if (!innerGraphState) {
		return;
	}

	const { innerGraph, usageCallbackMap } = innerGraphState;
	const processed = new Map();
	// flatten graph to terminal nodes (string, undefined or true)
	const nonTerminal = new Set(innerGraph.keys());
	while (nonTerminal.size > 0) {
		for (const key of nonTerminal) {
			/** @type {Set<string|TopLevelSymbol> | true} */
			let newSet = new Set();
			let isTerminal = true;
			const value = innerGraph.get(key);
			let alreadyProcessed = processed.get(key);
			if (alreadyProcessed === undefined) {
				alreadyProcessed = new Set();
				processed.set(key, alreadyProcessed);
			}
			if (value !== true && value !== undefined) {
				for (const item of value) {
					alreadyProcessed.add(item);
				}
				for (const item of value) {
					if (typeof item === "string") {
						newSet.add(item);
					} else {
						const itemValue = innerGraph.get(item);
						if (itemValue === true) {
							newSet = true;
							break;
						}
						if (itemValue !== undefined) {
							for (const i of itemValue) {
								if (i === key) continue;
								if (alreadyProcessed.has(i)) continue;
								newSet.add(i);
								if (typeof i !== "string") {
									isTerminal = false;
								}
							}
						}
					}
				}
				if (newSet === true) {
					innerGraph.set(key, true);
				} else if (newSet.size === 0) {
					innerGraph.set(key, undefined);
				} else {
					innerGraph.set(key, newSet);
				}
			}
			if (isTerminal) {
				nonTerminal.delete(key);

				// For the global key, merge with all other keys
				if (key === null) {
					const globalValue = innerGraph.get(null);
					if (globalValue) {
						for (const [key, value] of innerGraph) {
							if (key !== null && value !== true) {
								if (globalValue === true) {
									innerGraph.set(key, true);
								} else {
									const newSet = new Set(value);
									for (const item of globalValue) {
										newSet.add(item);
									}
									innerGraph.set(key, newSet);
								}
							}
						}
					}
				}
			}
		}
	}

	/** @type {Map<Dependency, true | Set<string>>} */
	for (const [symbol, callbacks] of usageCallbackMap) {
		const usage = /** @type {true | Set<string> | undefined} */ (
			innerGraph.get(symbol)
		);
		for (const callback of callbacks) {
			callback(usage === undefined ? false : usage);
		}
	}
};

/**
 * @param {ParserState} state parser state
 * @param {UsageCallback} onUsageCallback on usage callback
 */
exports.onUsage = (state, onUsageCallback) => {
	const innerGraphState = getState(state);

	if (innerGraphState) {
		const { usageCallbackMap, currentTopLevelSymbol } = innerGraphState;
		if (currentTopLevelSymbol) {
			let callbacks = usageCallbackMap.get(currentTopLevelSymbol);

			if (callbacks === undefined) {
				callbacks = new Set();
				usageCallbackMap.set(currentTopLevelSymbol, callbacks);
			}

			callbacks.add(onUsageCallback);
		} else {
			onUsageCallback(true);
		}
	} else {
		onUsageCallback(undefined);
	}
};

/**
 * @param {ParserState} state parser state
 * @param {TopLevelSymbol | undefined} symbol the symbol
 */
exports.setTopLevelSymbol = (state, symbol) => {
	const innerGraphState = getState(state);

	if (innerGraphState) {
		innerGraphState.currentTopLevelSymbol = symbol;
	}
};

/**
 * @param {ParserState} state parser state
 * @returns {TopLevelSymbol|void} usage data
 */
exports.getTopLevelSymbol = state => {
	const innerGraphState = getState(state);

	if (innerGraphState) {
		return innerGraphState.currentTopLevelSymbol;
	}
};

/**
 * @param {JavascriptParser} parser parser
 * @param {string} name name of variable
 * @returns {TopLevelSymbol | undefined} symbol
 */
exports.tagTopLevelSymbol = (parser, name) => {
	const innerGraphState = getState(parser.state);
	if (!innerGraphState) return;

	parser.defineVariable(name);

	const existingTag = /** @type {TopLevelSymbol} */ (
		parser.getTagData(name, topLevelSymbolTag)
	);
	if (existingTag) {
		return existingTag;
	}

	const fn = new TopLevelSymbol(name);
	parser.tagVariable(name, topLevelSymbolTag, fn);
	return fn;
};

/**
 * @param {Dependency} dependency the dependency
 * @param {Set<string> | boolean} usedByExports usedByExports info
 * @param {ModuleGraph} moduleGraph moduleGraph
 * @param {RuntimeSpec} runtime runtime
 * @returns {boolean} false, when unused. Otherwise true
 */
exports.isDependencyUsedByExports = (
	dependency,
	usedByExports,
	moduleGraph,
	runtime
) => {
	if (usedByExports === false) return false;
	if (usedByExports !== true && usedByExports !== undefined) {
		const selfModule =
			/** @type {Module} */
			(moduleGraph.getParentModule(dependency));
		const exportsInfo = moduleGraph.getExportsInfo(selfModule);
		let used = false;
		for (const exportName of usedByExports) {
			if (exportsInfo.getUsed(exportName, runtime) !== UsageState.Unused)
				used = true;
		}
		if (!used) return false;
	}
	return true;
};

/**
 * @param {Dependency} dependency the dependency
 * @param {Set<string> | boolean | undefined} usedByExports usedByExports info
 * @param {ModuleGraph} moduleGraph moduleGraph
 * @returns {null | false | function(ModuleGraphConnection, RuntimeSpec): ConnectionState} function to determine if the connection is active
 */
exports.getDependencyUsedByExportsCondition = (
	dependency,
	usedByExports,
	moduleGraph
) => {
	if (usedByExports === false) return false;
	if (usedByExports !== true && usedByExports !== undefined) {
		const selfModule =
			/** @type {Module} */
			(moduleGraph.getParentModule(dependency));
		const exportsInfo = moduleGraph.getExportsInfo(selfModule);
		return (connections, runtime) => {
			for (const exportName of usedByExports) {
				if (exportsInfo.getUsed(exportName, runtime) !== UsageState.Unused)
					return true;
			}
			return false;
		};
	}
	return null;
};

class TopLevelSymbol {
	/**
	 * @param {string} name name of the variable
	 */
	constructor(name) {
		this.name = name;
	}
}

exports.TopLevelSymbol = TopLevelSymbol;
exports.topLevelSymbolTag = topLevelSymbolTag;


code:
/*
	MIT License http://www.opensource.org/licenses/mit-license.php
	Author Tobias Koppers @sokra
*/

"use strict";

const {
	JAVASCRIPT_MODULE_TYPE_AUTO,
	JAVASCRIPT_MODULE_TYPE_ESM
} = require("../ModuleTypeConstants");
const PureExpressionDependency = require("../dependencies/PureExpressionDependency");
const InnerGraph = require("./InnerGraph");

/** @typedef {import("estree").ClassDeclaration} ClassDeclarationNode */
/** @typedef {import("estree").ClassExpression} ClassExpressionNode */
/** @typedef {import("estree").Node} Node */
/** @typedef {import("estree").VariableDeclarator} VariableDeclaratorNode */
/** @typedef {import("../../declarations/WebpackOptions").JavascriptParserOptions} JavascriptParserOptions */
/** @typedef {import("../Compiler")} Compiler */
/** @typedef {import("../Dependency")} Dependency */
/** @typedef {import("../Dependency").DependencyLocation} DependencyLocation */
/** @typedef {import("../dependencies/HarmonyImportSpecifierDependency")} HarmonyImportSpecifierDependency */
/** @typedef {import("../javascript/JavascriptParser")} JavascriptParser */
/** @typedef {import("../javascript/JavascriptParser").Range} Range */
/** @typedef {import("./InnerGraph").InnerGraph} InnerGraph */
/** @typedef {import("./InnerGraph").TopLevelSymbol} TopLevelSymbol */

const { topLevelSymbolTag } = InnerGraph;

const PLUGIN_NAME = "InnerGraphPlugin";

class InnerGraphPlugin {
	/**
	 * Apply the plugin
	 * @param {Compiler} compiler the compiler instance
	 * @returns {void}
	 */
	apply(compiler) {
		compiler.hooks.compilation.tap(
			PLUGIN_NAME,
			(compilation, { normalModuleFactory }) => {
				const logger = compilation.getLogger("webpack.InnerGraphPlugin");

				compilation.dependencyTemplates.set(
					PureExpressionDependency,
					new PureExpressionDependency.Template()
				);

				/**
				 * @param {JavascriptParser} parser the parser
				 * @param {JavascriptParserOptions} parserOptions options
				 * @returns {void}
				 */
				const handler = (parser, parserOptions) => {
					const onUsageSuper = sup => {
						InnerGraph.onUsage(parser.state, usedByExports => {
							switch (usedByExports) {
								case undefined:
								case true:
									return;
								default: {
									const dep = new PureExpressionDependency(sup.range);
									dep.loc = sup.loc;
									dep.usedByExports = usedByExports;
									parser.state.module.addDependency(dep);
									break;
								}
							}
						});
					};

					parser.hooks.program.tap(PLUGIN_NAME, () => {
						InnerGraph.enable(parser.state);
					});

					parser.hooks.finish.tap(PLUGIN_NAME, () => {
						if (!InnerGraph.isEnabled(parser.state)) return;

						logger.time("infer dependency usage");
						InnerGraph.inferDependencyUsage(parser.state);
						logger.timeAggregate("infer dependency usage");
					});

					// During prewalking the following datastructures are filled with
					// nodes that have a TopLevelSymbol assigned and
					// variables are tagged with the assigned TopLevelSymbol

					// We differ 3 types of nodes:
					// 1. full statements (export default, function declaration)
					// 2. classes (class declaration, class expression)
					// 3. variable declarators (const x = ...)

					/** @type {WeakMap<Node, TopLevelSymbol>} */
					const statementWithTopLevelSymbol = new WeakMap();
					/** @type {WeakMap<Node, Node>} */
					const statementPurePart = new WeakMap();

					/** @type {WeakMap<ClassExpressionNode | ClassDeclarationNode, TopLevelSymbol>} */
					const classWithTopLevelSymbol = new WeakMap();

					/** @type {WeakMap<VariableDeclaratorNode, TopLevelSymbol>} */
					const declWithTopLevelSymbol = new WeakMap();
					/** @type {WeakSet<VariableDeclaratorNode>} */
					const pureDeclarators = new WeakSet();

					// The following hooks are used during prewalking:

					parser.hooks.preStatement.tap(PLUGIN_NAME, statement => {
						if (!InnerGraph.isEnabled(parser.state)) return;

						if (parser.scope.topLevelScope === true) {
							if (statement.type === "FunctionDeclaration") {
								const name = statement.id ? statement.id.name : "*default*";
								const fn = InnerGraph.tagTopLevelSymbol(parser, name);
								statementWithTopLevelSymbol.set(statement, fn);
								return true;
							}
						}
					});

					parser.hooks.blockPreStatement.tap(PLUGIN_NAME, statement => {
						if (!InnerGraph.isEnabled(parser.state)) return;

						if (parser.scope.topLevelScope === true) {
							if (
								statement.type === "ClassDeclaration" &&
								parser.isPure(
									statement,
									/** @type {Range} */ (statement.range)[0]
								)
							) {
								const name = statement.id ? statement.id.name : "*default*";
								const fn = InnerGraph.tagTopLevelSymbol(parser, name);
								classWithTopLevelSymbol.set(statement, fn);
								return true;
							}
							if (statement.type === "ExportDefaultDeclaration") {
								const name = "*default*";
								const fn = InnerGraph.tagTopLevelSymbol(parser, name);
								const decl = statement.declaration;
								if (
									(decl.type === "ClassExpression" ||
										decl.type === "ClassDeclaration") &&
									parser.isPure(decl, /** @type {Range} */ (decl.range)[0])
								) {
									classWithTopLevelSymbol.set(decl, fn);
								} else if (
									parser.isPure(decl, /** @type {Range} */ (statement.range)[0])
								) {
									statementWithTopLevelSymbol.set(statement, fn);
									if (
										!decl.type.endsWith("FunctionExpression") &&
										!decl.type.endsWith("Declaration") &&
										decl.type !== "Literal"
									) {
										statementPurePart.set(statement, decl);
									}
								}
							}
						}
					});

					parser.hooks.preDeclarator.tap(PLUGIN_NAME, (decl, statement) => {
						if (!InnerGraph.isEnabled(parser.state)) return;
						if (
							parser.scope.topLevelScope === true &&
							decl.init &&
							decl.id.type === "Identifier"
						) {
							const name = decl.id.name;
							if (
								decl.init.type === "ClassExpression" &&
								parser.isPure(
									decl.init,
									/** @type {Range} */ (decl.id.range)[1]
								)
							) {
								const fn = InnerGraph.tagTopLevelSymbol(parser, name);
								classWithTopLevelSymbol.set(decl.init, fn);
							} else if (
								parser.isPure(
									decl.init,
									/** @type {Range} */ (decl.id.range)[1]
								)
							) {
								const fn = InnerGraph.tagTopLevelSymbol(parser, name);
								declWithTopLevelSymbol.set(decl, fn);
								if (
									!decl.init.type.endsWith("FunctionExpression") &&
									decl.init.type !== "Literal"
								) {
									pureDeclarators.add(decl);
								}
							}
						}
					});

					// During real walking we set the TopLevelSymbol state to the assigned
					// TopLevelSymbol by using the fill datastructures.

					// In addition to tracking TopLevelSymbols, we sometimes need to
					// add a PureExpressionDependency. This is needed to skip execution
					// of pure expressions, even when they are not dropped due to
					// minimizing. Otherwise symbols used there might not exist anymore
					// as they are removed as unused by this optimization

					// When we find a reference to a TopLevelSymbol, we register a
					// TopLevelSymbol dependency from TopLevelSymbol in state to the
					// referenced TopLevelSymbol. This way we get a graph of all
					// TopLevelSymbols.

					// The following hooks are called during walking:

					parser.hooks.statement.tap(PLUGIN_NAME, statement => {
						if (!InnerGraph.isEnabled(parser.state)) return;
						if (parser.scope.topLevelScope === true) {
							InnerGraph.setTopLevelSymbol(parser.state, undefined);

							const fn = statementWithTopLevelSymbol.get(statement);
							if (fn) {
								InnerGraph.setTopLevelSymbol(parser.state, fn);
								const purePart = statementPurePart.get(statement);
								if (purePart) {
									InnerGraph.onUsage(parser.state, usedByExports => {
										switch (usedByExports) {
											case undefined:
											case true:
												return;
											default: {
												const dep = new PureExpressionDependency(
													/** @type {Range} */ (purePart.range)
												);
												dep.loc =
													/** @type {DependencyLocation} */
													(statement.loc);
												dep.usedByExports = usedByExports;
												parser.state.module.addDependency(dep);
												break;
											}
										}
									});
								}
							}
						}
					});

					parser.hooks.classExtendsExpression.tap(
						PLUGIN_NAME,
						(expr, statement) => {
							if (!InnerGraph.isEnabled(parser.state)) return;
							if (parser.scope.topLevelScope === true) {
								const fn = classWithTopLevelSymbol.get(statement);
								if (
									fn &&
									parser.isPure(
										expr,
										statement.id
											? /** @type {Range} */ (statement.id.range)[1]
											: /** @type {Range} */ (statement.range)[0]
									)
								) {
									InnerGraph.setTopLevelSymbol(parser.state, fn);
									onUsageSuper(expr);
								}
							}
						}
					);

					parser.hooks.classBodyElement.tap(
						PLUGIN_NAME,
						(element, classDefinition) => {
							if (!InnerGraph.isEnabled(parser.state)) return;
							if (parser.scope.topLevelScope === true) {
								const fn = classWithTopLevelSymbol.get(classDefinition);
								if (fn) {
									InnerGraph.setTopLevelSymbol(parser.state, undefined);
								}
							}
						}
					);

					parser.hooks.classBodyValue.tap(
						PLUGIN_NAME,
						(expression, element, classDefinition) => {
							if (!InnerGraph.isEnabled(parser.state)) return;
							if (parser.scope.topLevelScope === true) {
								const fn = classWithTopLevelSymbol.get(classDefinition);
								if (fn) {
									if (
										!element.static ||
										parser.isPure(
											expression,
											element.key
												? /** @type {Range} */ (element.key.range)[1]
												: /** @type {Range} */ (element.range)[0]
										)
									) {
										InnerGraph.setTopLevelSymbol(parser.state, fn);
										if (element.type !== "MethodDefinition" && element.static) {
											InnerGraph.onUsage(parser.state, usedByExports => {
												switch (usedByExports) {
													case undefined:
													case true:
														return;
													default: {
														const dep = new PureExpressionDependency(
															/** @type {Range} */ (expression.range)
														);
														dep.loc =
															/** @type {DependencyLocation} */
															(expression.loc);
														dep.usedByExports = usedByExports;
														parser.state.module.addDependency(dep);
														break;
													}
												}
											});
										}
									} else {
										InnerGraph.setTopLevelSymbol(parser.state, undefined);
									}
								}
							}
						}
					);

					parser.hooks.declarator.tap(PLUGIN_NAME, (decl, statement) => {
						if (!InnerGraph.isEnabled(parser.state)) return;
						const fn = declWithTopLevelSymbol.get(decl);

						if (fn) {
							InnerGraph.setTopLevelSymbol(parser.state, fn);
							if (pureDeclarators.has(decl)) {
								if (decl.init.type === "ClassExpression") {
									if (decl.init.superClass) {
										onUsageSuper(decl.init.superClass);
									}
								} else {
									InnerGraph.onUsage(parser.state, usedByExports => {
										switch (usedByExports) {
											case undefined:
											case true:
												return;
											default: {
												const dep = new PureExpressionDependency(
													/** @type {Range} */ (decl.init.range)
												);
												dep.loc = /** @type {DependencyLocation} */ (decl.loc);
												dep.usedByExports = usedByExports;
												parser.state.module.addDependency(dep);
												break;
											}
										}
									});
								}
							}
							parser.walkExpression(decl.init);
							InnerGraph.setTopLevelSymbol(parser.state, undefined);
							return true;
						} else if (
							decl.id.type === "Identifier" &&
							decl.init &&
							decl.init.type === "ClassExpression" &&
							classWithTopLevelSymbol.has(decl.init)
						) {
							parser.walkExpression(decl.init);
							InnerGraph.setTopLevelSymbol(parser.state, undefined);
							return true;
						}
					});

					parser.hooks.expression
						.for(topLevelSymbolTag)
						.tap(PLUGIN_NAME, () => {
							const topLevelSymbol = /** @type {TopLevelSymbol} */ (
								parser.currentTagData
							);
							const currentTopLevelSymbol = InnerGraph.getTopLevelSymbol(
								parser.state
							);
							InnerGraph.addUsage(
								parser.state,
								topLevelSymbol,
								currentTopLevelSymbol || true
							);
						});
					parser.hooks.assign.for(topLevelSymbolTag).tap(PLUGIN_NAME, expr => {
						if (!InnerGraph.isEnabled(parser.state)) return;
						if (expr.operator === "=") return true;
					});
				};
				normalModuleFactory.hooks.parser
					.for(JAVASCRIPT_MODULE_TYPE_AUTO)
					.tap(PLUGIN_NAME, handler);
				normalModuleFactory.hooks.parser
					.for(JAVASCRIPT_MODULE_TYPE_ESM)
					.tap(PLUGIN_NAME, handler);

				compilation.hooks.finishModules.tap(PLUGIN_NAME, () => {
					logger.timeAggregateEnd("infer dependency usage");
				});
			}
		);
	}
}

module.exports = InnerGraphPlugin;


code:
/*
	MIT License http://www.opensource.org/licenses/mit-license.php
	Author Tobias Koppers @sokra
*/

"use strict";

const { STAGE_ADVANCED } = require("../OptimizationStages");
const LazyBucketSortedSet = require("../util/LazyBucketSortedSet");
const { compareChunks } = require("../util/comparators");
const createSchemaValidation = require("../util/create-schema-validation");

/** @typedef {import("../../declarations/plugins/optimize/LimitChunkCountPlugin").LimitChunkCountPluginOptions} LimitChunkCountPluginOptions */
/** @typedef {import("../Chunk")} Chunk */
/** @typedef {import("../Compiler")} Compiler */

const validate = createSchemaValidation(
	require("../../schemas/plugins/optimize/LimitChunkCountPlugin.check.js"),
	() => require("../../schemas/plugins/optimize/LimitChunkCountPlugin.json"),
	{
		name: "Limit Chunk Count Plugin",
		baseDataPath: "options"
	}
);

/**
 * @typedef {object} ChunkCombination
 * @property {boolean} deleted this is set to true when combination was removed
 * @property {number} sizeDiff
 * @property {number} integratedSize
 * @property {Chunk} a
 * @property {Chunk} b
 * @property {number} aIdx
 * @property {number} bIdx
 * @property {number} aSize
 * @property {number} bSize
 */

/**
 * @template K, V
 * @param {Map<K, Set<V>>} map map
 * @param {K} key key
 * @param {V} value value
 */
const addToSetMap = (map, key, value) => {
	const set = map.get(key);
	if (set === undefined) {
		map.set(key, new Set([value]));
	} else {
		set.add(value);
	}
};

class LimitChunkCountPlugin {
	/**
	 * @param {LimitChunkCountPluginOptions=} options options object
	 */
	constructor(options) {
		validate(options);
		this.options = options;
	}

	/**
	 * @param {Compiler} compiler the webpack compiler
	 * @returns {void}
	 */
	apply(compiler) {
		const options = this.options;
		compiler.hooks.compilation.tap("LimitChunkCountPlugin", compilation => {
			compilation.hooks.optimizeChunks.tap(
				{
					name: "LimitChunkCountPlugin",
					stage: STAGE_ADVANCED
				},
				chunks => {
					const chunkGraph = compilation.chunkGraph;
					const maxChunks =
						/** @type {LimitChunkCountPluginOptions} */
						(options).maxChunks;
					if (!maxChunks) return;
					if (maxChunks < 1) return;
					if (compilation.chunks.size <= maxChunks) return;

					let remainingChunksToMerge = compilation.chunks.size - maxChunks;

					// order chunks in a deterministic way
					const compareChunksWithGraph = compareChunks(chunkGraph);
					const orderedChunks = Array.from(chunks).sort(compareChunksWithGraph);

					// create a lazy sorted data structure to keep all combinations
					// this is large. Size = chunks * (chunks - 1) / 2
					// It uses a multi layer bucket sort plus normal sort in the last layer
					// It's also lazy so only accessed buckets are sorted
					const combinations = new LazyBucketSortedSet(
						// Layer 1: ordered by largest size benefit
						c => c.sizeDiff,
						(a, b) => b - a,
						// Layer 2: ordered by smallest combined size
						/**
						 * @param {ChunkCombination} c combination
						 * @returns {number} integrated size
						 */
						c => c.integratedSize,
						(a, b) => a - b,
						// Layer 3: ordered by position difference in orderedChunk (-> to be deterministic)
						/**
						 * @param {ChunkCombination} c combination
						 * @returns {number} position difference
						 */
						c => c.bIdx - c.aIdx,
						(a, b) => a - b,
						// Layer 4: ordered by position in orderedChunk (-> to be deterministic)
						(a, b) => a.bIdx - b.bIdx
					);

					// we keep a mapping from chunk to all combinations
					// but this mapping is not kept up-to-date with deletions
					// so `deleted` flag need to be considered when iterating this
					/** @type {Map<Chunk, Set<ChunkCombination>>} */
					const combinationsByChunk = new Map();

					orderedChunks.forEach((b, bIdx) => {
						// create combination pairs with size and integrated size
						for (let aIdx = 0; aIdx < bIdx; aIdx++) {
							const a = orderedChunks[aIdx];
							// filter pairs that can not be integrated!
							if (!chunkGraph.canChunksBeIntegrated(a, b)) continue;

							const integratedSize = chunkGraph.getIntegratedChunksSize(
								a,
								b,
								options
							);

							const aSize = chunkGraph.getChunkSize(a, options);
							const bSize = chunkGraph.getChunkSize(b, options);
							const c = {
								deleted: false,
								sizeDiff: aSize + bSize - integratedSize,
								integratedSize,
								a,
								b,
								aIdx,
								bIdx,
								aSize,
								bSize
							};
							combinations.add(c);
							addToSetMap(combinationsByChunk, a, c);
							addToSetMap(combinationsByChunk, b, c);
						}
						return combinations;
					});

					// list of modified chunks during this run
					// combinations affected by this change are skipped to allow
					// further optimizations
					/** @type {Set<Chunk>} */
					const modifiedChunks = new Set();

					let changed = false;
					loop: while (true) {
						const combination = combinations.popFirst();
						if (combination === undefined) break;

						combination.deleted = true;
						const { a, b, integratedSize } = combination;

						// skip over pair when
						// one of the already merged chunks is a parent of one of the chunks
						if (modifiedChunks.size > 0) {
							const queue = new Set(a.groupsIterable);
							for (const group of b.groupsIterable) {
								queue.add(group);
							}
							for (const group of queue) {
								for (const mChunk of modifiedChunks) {
									if (mChunk !== a && mChunk !== b && mChunk.isInGroup(group)) {
										// This is a potential pair which needs recalculation
										// We can't do that now, but it merge before following pairs
										// so we leave space for it, and consider chunks as modified
										// just for the worse case
										remainingChunksToMerge--;
										if (remainingChunksToMerge <= 0) break loop;
										modifiedChunks.add(a);
										modifiedChunks.add(b);
										continue loop;
									}
								}
								for (const parent of group.parentsIterable) {
									queue.add(parent);
								}
							}
						}

						// merge the chunks
						if (chunkGraph.canChunksBeIntegrated(a, b)) {
							chunkGraph.integrateChunks(a, b);
							compilation.chunks.delete(b);

							// flag chunk a as modified as further optimization are possible for all children here
							modifiedChunks.add(a);

							changed = true;
							remainingChunksToMerge--;
							if (remainingChunksToMerge <= 0) break;

							// Update all affected combinations
							// delete all combination with the removed chunk
							// we will use combinations with the kept chunk instead
							for (const combination of /** @type {Set<ChunkCombination>} */ (
								combinationsByChunk.get(a)
							)) {
								if (combination.deleted) continue;
								combination.deleted = true;
								combinations.delete(combination);
							}

							// Update combinations with the kept chunk with new sizes
							for (const combination of /** @type {Set<ChunkCombination>} */ (
								combinationsByChunk.get(b)
							)) {
								if (combination.deleted) continue;
								if (combination.a === b) {
									if (!chunkGraph.canChunksBeIntegrated(a, combination.b)) {
										combination.deleted = true;
										combinations.delete(combination);
										continue;
									}
									// Update size
									const newIntegratedSize = chunkGraph.getIntegratedChunksSize(
										a,
										combination.b,
										options
									);
									const finishUpdate = combinations.startUpdate(combination);
									combination.a = a;
									combination.integratedSize = newIntegratedSize;
									combination.aSize = integratedSize;
									combination.sizeDiff =
										combination.bSize + integratedSize - newIntegratedSize;
									finishUpdate();
								} else if (combination.b === b) {
									if (!chunkGraph.canChunksBeIntegrated(combination.a, a)) {
										combination.deleted = true;
										combinations.delete(combination);
										continue;
									}
									// Update size
									const newIntegratedSize = chunkGraph.getIntegratedChunksSize(
										combination.a,
										a,
										options
									);

									const finishUpdate = combinations.startUpdate(combination);
									combination.b = a;
									combination.integratedSize = newIntegratedSize;
									combination.bSize = integratedSize;
									combination.sizeDiff =
										integratedSize + combination.aSize - newIntegratedSize;
									finishUpdate();
								}
							}
							combinationsByChunk.set(
								a,
								/** @type {Set<ChunkCombination>} */ (
									combinationsByChunk.get(b)
								)
							);
							combinationsByChunk.delete(b);
						}
					}
					if (changed) return true;
				}
			);
		});
	}
}
module.exports = LimitChunkCountPlugin;