Index: imaps-frontend/node_modules/webpack/lib/javascript/ArrayPushCallbackChunkFormatPlugin.js
===================================================================
--- imaps-frontend/node_modules/webpack/lib/javascript/ArrayPushCallbackChunkFormatPlugin.js	(revision 79a03170cdde4e801d12257ee86723774970427b)
+++ imaps-frontend/node_modules/webpack/lib/javascript/ArrayPushCallbackChunkFormatPlugin.js	(revision 79a03170cdde4e801d12257ee86723774970427b)
@@ -0,0 +1,156 @@
+/*
+	MIT License http://www.opensource.org/licenses/mit-license.php
+	Author Tobias Koppers @sokra
+*/
+
+"use strict";
+
+const { ConcatSource, PrefixSource, RawSource } = require("webpack-sources");
+const { RuntimeGlobals } = require("..");
+const HotUpdateChunk = require("../HotUpdateChunk");
+const Template = require("../Template");
+const { getCompilationHooks } = require("./JavascriptModulesPlugin");
+const {
+	generateEntryStartup,
+	updateHashForEntryStartup
+} = require("./StartupHelpers");
+
+/** @typedef {import("../Compiler")} Compiler */
+
+class ArrayPushCallbackChunkFormatPlugin {
+	/**
+	 * Apply the plugin
+	 * @param {Compiler} compiler the compiler instance
+	 * @returns {void}
+	 */
+	apply(compiler) {
+		compiler.hooks.thisCompilation.tap(
+			"ArrayPushCallbackChunkFormatPlugin",
+			compilation => {
+				compilation.hooks.additionalChunkRuntimeRequirements.tap(
+					"ArrayPushCallbackChunkFormatPlugin",
+					(chunk, set, { chunkGraph }) => {
+						if (chunk.hasRuntime()) return;
+						if (chunkGraph.getNumberOfEntryModules(chunk) > 0) {
+							set.add(RuntimeGlobals.onChunksLoaded);
+							set.add(RuntimeGlobals.exports);
+							set.add(RuntimeGlobals.require);
+						}
+						set.add(RuntimeGlobals.chunkCallback);
+					}
+				);
+				const hooks = getCompilationHooks(compilation);
+				hooks.renderChunk.tap(
+					"ArrayPushCallbackChunkFormatPlugin",
+					(modules, renderContext) => {
+						const { chunk, chunkGraph, runtimeTemplate } = renderContext;
+						const hotUpdateChunk =
+							chunk instanceof HotUpdateChunk ? chunk : null;
+						const globalObject = runtimeTemplate.globalObject;
+						const source = new ConcatSource();
+						const runtimeModules =
+							chunkGraph.getChunkRuntimeModulesInOrder(chunk);
+						if (hotUpdateChunk) {
+							const hotUpdateGlobal =
+								runtimeTemplate.outputOptions.hotUpdateGlobal;
+							source.add(
+								`${globalObject}[${JSON.stringify(hotUpdateGlobal)}](`
+							);
+							source.add(`${JSON.stringify(chunk.id)},`);
+							source.add(modules);
+							if (runtimeModules.length > 0) {
+								source.add(",\n");
+								const runtimePart = Template.renderChunkRuntimeModules(
+									runtimeModules,
+									renderContext
+								);
+								source.add(runtimePart);
+							}
+							source.add(")");
+						} else {
+							const chunkLoadingGlobal =
+								runtimeTemplate.outputOptions.chunkLoadingGlobal;
+							source.add(
+								`(${globalObject}[${JSON.stringify(
+									chunkLoadingGlobal
+								)}] = ${globalObject}[${JSON.stringify(
+									chunkLoadingGlobal
+								)}] || []).push([`
+							);
+							source.add(`${JSON.stringify(chunk.ids)},`);
+							source.add(modules);
+							const entries = Array.from(
+								chunkGraph.getChunkEntryModulesWithChunkGroupIterable(chunk)
+							);
+							if (runtimeModules.length > 0 || entries.length > 0) {
+								const runtime = new ConcatSource(
+									`${
+										runtimeTemplate.supportsArrowFunction()
+											? `${RuntimeGlobals.require} =>`
+											: `function(${RuntimeGlobals.require})`
+									} { // webpackRuntimeModules\n`
+								);
+								if (runtimeModules.length > 0) {
+									runtime.add(
+										Template.renderRuntimeModules(runtimeModules, {
+											...renderContext,
+											codeGenerationResults: compilation.codeGenerationResults
+										})
+									);
+								}
+								if (entries.length > 0) {
+									const startupSource = new RawSource(
+										generateEntryStartup(
+											chunkGraph,
+											runtimeTemplate,
+											entries,
+											chunk,
+											true
+										)
+									);
+									runtime.add(
+										hooks.renderStartup.call(
+											startupSource,
+											entries[entries.length - 1][0],
+											{
+												...renderContext,
+												inlined: false
+											}
+										)
+									);
+									if (
+										chunkGraph
+											.getChunkRuntimeRequirements(chunk)
+											.has(RuntimeGlobals.returnExportsFromRuntime)
+									) {
+										runtime.add(`return ${RuntimeGlobals.exports};\n`);
+									}
+								}
+								runtime.add("}\n");
+								source.add(",\n");
+								source.add(new PrefixSource("/******/ ", runtime));
+							}
+							source.add("])");
+						}
+						return source;
+					}
+				);
+				hooks.chunkHash.tap(
+					"ArrayPushCallbackChunkFormatPlugin",
+					(chunk, hash, { chunkGraph, runtimeTemplate }) => {
+						if (chunk.hasRuntime()) return;
+						hash.update(
+							`ArrayPushCallbackChunkFormatPlugin1${runtimeTemplate.outputOptions.chunkLoadingGlobal}${runtimeTemplate.outputOptions.hotUpdateGlobal}${runtimeTemplate.globalObject}`
+						);
+						const entries = Array.from(
+							chunkGraph.getChunkEntryModulesWithChunkGroupIterable(chunk)
+						);
+						updateHashForEntryStartup(hash, chunkGraph, entries, chunk);
+					}
+				);
+			}
+		);
+	}
+}
+
+module.exports = ArrayPushCallbackChunkFormatPlugin;
Index: imaps-frontend/node_modules/webpack/lib/javascript/BasicEvaluatedExpression.js
===================================================================
--- imaps-frontend/node_modules/webpack/lib/javascript/BasicEvaluatedExpression.js	(revision 79a03170cdde4e801d12257ee86723774970427b)
+++ imaps-frontend/node_modules/webpack/lib/javascript/BasicEvaluatedExpression.js	(revision 79a03170cdde4e801d12257ee86723774970427b)
@@ -0,0 +1,594 @@
+/*
+	MIT License http://www.opensource.org/licenses/mit-license.php
+	Author Tobias Koppers @sokra
+*/
+
+"use strict";
+
+/** @typedef {import("estree").Node} Node */
+/** @typedef {import("./JavascriptParser").Range} Range */
+/** @typedef {import("./JavascriptParser").VariableInfoInterface} VariableInfoInterface */
+
+const TypeUnknown = 0;
+const TypeUndefined = 1;
+const TypeNull = 2;
+const TypeString = 3;
+const TypeNumber = 4;
+const TypeBoolean = 5;
+const TypeRegExp = 6;
+const TypeConditional = 7;
+const TypeArray = 8;
+const TypeConstArray = 9;
+const TypeIdentifier = 10;
+const TypeWrapped = 11;
+const TypeTemplateString = 12;
+const TypeBigInt = 13;
+
+class BasicEvaluatedExpression {
+	constructor() {
+		this.type = TypeUnknown;
+		/** @type {Range | undefined} */
+		this.range = undefined;
+		/** @type {boolean} */
+		this.falsy = false;
+		/** @type {boolean} */
+		this.truthy = false;
+		/** @type {boolean | undefined} */
+		this.nullish = undefined;
+		/** @type {boolean} */
+		this.sideEffects = true;
+		/** @type {boolean | undefined} */
+		this.bool = undefined;
+		/** @type {number | undefined} */
+		this.number = undefined;
+		/** @type {bigint | undefined} */
+		this.bigint = undefined;
+		/** @type {RegExp | undefined} */
+		this.regExp = undefined;
+		/** @type {string | undefined} */
+		this.string = undefined;
+		/** @type {BasicEvaluatedExpression[] | undefined} */
+		this.quasis = undefined;
+		/** @type {BasicEvaluatedExpression[] | undefined} */
+		this.parts = undefined;
+		/** @type {any[] | undefined} */
+		this.array = undefined;
+		/** @type {BasicEvaluatedExpression[] | undefined} */
+		this.items = undefined;
+		/** @type {BasicEvaluatedExpression[] | undefined} */
+		this.options = undefined;
+		/** @type {BasicEvaluatedExpression | undefined | null} */
+		this.prefix = undefined;
+		/** @type {BasicEvaluatedExpression | undefined | null} */
+		this.postfix = undefined;
+		/** @type {BasicEvaluatedExpression[] | undefined} */
+		this.wrappedInnerExpressions = undefined;
+		/** @type {string | VariableInfoInterface | undefined} */
+		this.identifier = undefined;
+		/** @type {string | VariableInfoInterface | undefined} */
+		this.rootInfo = undefined;
+		/** @type {(() => string[]) | undefined} */
+		this.getMembers = undefined;
+		/** @type {(() => boolean[]) | undefined} */
+		this.getMembersOptionals = undefined;
+		/** @type {(() => Range[]) | undefined} */
+		this.getMemberRanges = undefined;
+		/** @type {Node | undefined} */
+		this.expression = undefined;
+	}
+
+	isUnknown() {
+		return this.type === TypeUnknown;
+	}
+
+	isNull() {
+		return this.type === TypeNull;
+	}
+
+	isUndefined() {
+		return this.type === TypeUndefined;
+	}
+
+	isString() {
+		return this.type === TypeString;
+	}
+
+	isNumber() {
+		return this.type === TypeNumber;
+	}
+
+	isBigInt() {
+		return this.type === TypeBigInt;
+	}
+
+	isBoolean() {
+		return this.type === TypeBoolean;
+	}
+
+	isRegExp() {
+		return this.type === TypeRegExp;
+	}
+
+	isConditional() {
+		return this.type === TypeConditional;
+	}
+
+	isArray() {
+		return this.type === TypeArray;
+	}
+
+	isConstArray() {
+		return this.type === TypeConstArray;
+	}
+
+	isIdentifier() {
+		return this.type === TypeIdentifier;
+	}
+
+	isWrapped() {
+		return this.type === TypeWrapped;
+	}
+
+	isTemplateString() {
+		return this.type === TypeTemplateString;
+	}
+
+	/**
+	 * Is expression a primitive or an object type value?
+	 * @returns {boolean | undefined} true: primitive type, false: object type, undefined: unknown/runtime-defined
+	 */
+	isPrimitiveType() {
+		switch (this.type) {
+			case TypeUndefined:
+			case TypeNull:
+			case TypeString:
+			case TypeNumber:
+			case TypeBoolean:
+			case TypeBigInt:
+			case TypeWrapped:
+			case TypeTemplateString:
+				return true;
+			case TypeRegExp:
+			case TypeArray:
+			case TypeConstArray:
+				return false;
+			default:
+				return undefined;
+		}
+	}
+
+	/**
+	 * Is expression a runtime or compile-time value?
+	 * @returns {boolean} true: compile time value, false: runtime value
+	 */
+	isCompileTimeValue() {
+		switch (this.type) {
+			case TypeUndefined:
+			case TypeNull:
+			case TypeString:
+			case TypeNumber:
+			case TypeBoolean:
+			case TypeRegExp:
+			case TypeConstArray:
+			case TypeBigInt:
+				return true;
+			default:
+				return false;
+		}
+	}
+
+	/**
+	 * Gets the compile-time value of the expression
+	 * @returns {any} the javascript value
+	 */
+	asCompileTimeValue() {
+		switch (this.type) {
+			case TypeUndefined:
+				return;
+			case TypeNull:
+				return null;
+			case TypeString:
+				return this.string;
+			case TypeNumber:
+				return this.number;
+			case TypeBoolean:
+				return this.bool;
+			case TypeRegExp:
+				return this.regExp;
+			case TypeConstArray:
+				return this.array;
+			case TypeBigInt:
+				return this.bigint;
+			default:
+				throw new Error(
+					"asCompileTimeValue must only be called for compile-time values"
+				);
+		}
+	}
+
+	isTruthy() {
+		return this.truthy;
+	}
+
+	isFalsy() {
+		return this.falsy;
+	}
+
+	isNullish() {
+		return this.nullish;
+	}
+
+	/**
+	 * Can this expression have side effects?
+	 * @returns {boolean} false: never has side effects
+	 */
+	couldHaveSideEffects() {
+		return this.sideEffects;
+	}
+
+	/**
+	 * Creates a boolean representation of this evaluated expression.
+	 * @returns {boolean | undefined} true: truthy, false: falsy, undefined: unknown
+	 */
+	asBool() {
+		if (this.truthy) return true;
+		if (this.falsy || this.nullish) return false;
+		if (this.isBoolean()) return this.bool;
+		if (this.isNull()) return false;
+		if (this.isUndefined()) return false;
+		if (this.isString()) return this.string !== "";
+		if (this.isNumber()) return this.number !== 0;
+		if (this.isBigInt()) return this.bigint !== BigInt(0);
+		if (this.isRegExp()) return true;
+		if (this.isArray()) return true;
+		if (this.isConstArray()) return true;
+		if (this.isWrapped()) {
+			return (this.prefix && this.prefix.asBool()) ||
+				(this.postfix && this.postfix.asBool())
+				? true
+				: undefined;
+		}
+		if (this.isTemplateString()) {
+			const str = this.asString();
+			if (typeof str === "string") return str !== "";
+		}
+	}
+
+	/**
+	 * Creates a nullish coalescing representation of this evaluated expression.
+	 * @returns {boolean | undefined} true: nullish, false: not nullish, undefined: unknown
+	 */
+	asNullish() {
+		const nullish = this.isNullish();
+
+		if (nullish === true || this.isNull() || this.isUndefined()) return true;
+
+		if (nullish === false) return false;
+		if (this.isTruthy()) return false;
+		if (this.isBoolean()) return false;
+		if (this.isString()) return false;
+		if (this.isNumber()) return false;
+		if (this.isBigInt()) return false;
+		if (this.isRegExp()) return false;
+		if (this.isArray()) return false;
+		if (this.isConstArray()) return false;
+		if (this.isTemplateString()) return false;
+		if (this.isRegExp()) return false;
+	}
+
+	/**
+	 * Creates a string representation of this evaluated expression.
+	 * @returns {string | undefined} the string representation or undefined if not possible
+	 */
+	asString() {
+		if (this.isBoolean()) return `${this.bool}`;
+		if (this.isNull()) return "null";
+		if (this.isUndefined()) return "undefined";
+		if (this.isString()) return this.string;
+		if (this.isNumber()) return `${this.number}`;
+		if (this.isBigInt()) return `${this.bigint}`;
+		if (this.isRegExp()) return `${this.regExp}`;
+		if (this.isArray()) {
+			const array = [];
+			for (const item of /** @type {BasicEvaluatedExpression[]} */ (
+				this.items
+			)) {
+				const itemStr = item.asString();
+				if (itemStr === undefined) return;
+				array.push(itemStr);
+			}
+			return `${array}`;
+		}
+		if (this.isConstArray()) return `${this.array}`;
+		if (this.isTemplateString()) {
+			let str = "";
+			for (const part of /** @type {BasicEvaluatedExpression[]} */ (
+				this.parts
+			)) {
+				const partStr = part.asString();
+				if (partStr === undefined) return;
+				str += partStr;
+			}
+			return str;
+		}
+	}
+
+	/**
+	 * @param {string} string value
+	 * @returns {BasicEvaluatedExpression} basic evaluated expression
+	 */
+	setString(string) {
+		this.type = TypeString;
+		this.string = string;
+		this.sideEffects = false;
+		return this;
+	}
+
+	setUndefined() {
+		this.type = TypeUndefined;
+		this.sideEffects = false;
+		return this;
+	}
+
+	setNull() {
+		this.type = TypeNull;
+		this.sideEffects = false;
+		return this;
+	}
+
+	/**
+	 * Set's the value of this expression to a number
+	 * @param {number} number number to set
+	 * @returns {this} this
+	 */
+	setNumber(number) {
+		this.type = TypeNumber;
+		this.number = number;
+		this.sideEffects = false;
+		return this;
+	}
+
+	/**
+	 * Set's the value of this expression to a BigInt
+	 * @param {bigint} bigint bigint to set
+	 * @returns {this} this
+	 */
+	setBigInt(bigint) {
+		this.type = TypeBigInt;
+		this.bigint = bigint;
+		this.sideEffects = false;
+		return this;
+	}
+
+	/**
+	 * Set's the value of this expression to a boolean
+	 * @param {boolean} bool boolean to set
+	 * @returns {this} this
+	 */
+	setBoolean(bool) {
+		this.type = TypeBoolean;
+		this.bool = bool;
+		this.sideEffects = false;
+		return this;
+	}
+
+	/**
+	 * Set's the value of this expression to a regular expression
+	 * @param {RegExp} regExp regular expression to set
+	 * @returns {this} this
+	 */
+	setRegExp(regExp) {
+		this.type = TypeRegExp;
+		this.regExp = regExp;
+		this.sideEffects = false;
+		return this;
+	}
+
+	/**
+	 * Set's the value of this expression to a particular identifier and its members.
+	 * @param {string | VariableInfoInterface} identifier identifier to set
+	 * @param {string | VariableInfoInterface} rootInfo root info
+	 * @param {() => string[]} getMembers members
+	 * @param {() => boolean[]=} getMembersOptionals optional members
+	 * @param {() => Range[]=} getMemberRanges ranges of progressively increasing sub-expressions
+	 * @returns {this} this
+	 */
+	setIdentifier(
+		identifier,
+		rootInfo,
+		getMembers,
+		getMembersOptionals,
+		getMemberRanges
+	) {
+		this.type = TypeIdentifier;
+		this.identifier = identifier;
+		this.rootInfo = rootInfo;
+		this.getMembers = getMembers;
+		this.getMembersOptionals = getMembersOptionals;
+		this.getMemberRanges = getMemberRanges;
+		this.sideEffects = true;
+		return this;
+	}
+
+	/**
+	 * Wraps an array of expressions with a prefix and postfix expression.
+	 * @param {BasicEvaluatedExpression | null | undefined} prefix Expression to be added before the innerExpressions
+	 * @param {BasicEvaluatedExpression | null | undefined} postfix Expression to be added after the innerExpressions
+	 * @param {BasicEvaluatedExpression[] | undefined} innerExpressions Expressions to be wrapped
+	 * @returns {this} this
+	 */
+	setWrapped(prefix, postfix, innerExpressions) {
+		this.type = TypeWrapped;
+		this.prefix = prefix;
+		this.postfix = postfix;
+		this.wrappedInnerExpressions = innerExpressions;
+		this.sideEffects = true;
+		return this;
+	}
+
+	/**
+	 * Stores the options of a conditional expression.
+	 * @param {BasicEvaluatedExpression[]} options optional (consequent/alternate) expressions to be set
+	 * @returns {this} this
+	 */
+	setOptions(options) {
+		this.type = TypeConditional;
+		this.options = options;
+		this.sideEffects = true;
+		return this;
+	}
+
+	/**
+	 * Adds options to a conditional expression.
+	 * @param {BasicEvaluatedExpression[]} options optional (consequent/alternate) expressions to be added
+	 * @returns {this} this
+	 */
+	addOptions(options) {
+		if (!this.options) {
+			this.type = TypeConditional;
+			this.options = [];
+			this.sideEffects = true;
+		}
+		for (const item of options) {
+			this.options.push(item);
+		}
+		return this;
+	}
+
+	/**
+	 * Set's the value of this expression to an array of expressions.
+	 * @param {BasicEvaluatedExpression[]} items expressions to set
+	 * @returns {this} this
+	 */
+	setItems(items) {
+		this.type = TypeArray;
+		this.items = items;
+		this.sideEffects = items.some(i => i.couldHaveSideEffects());
+		return this;
+	}
+
+	/**
+	 * Set's the value of this expression to an array of strings.
+	 * @param {string[]} array array to set
+	 * @returns {this} this
+	 */
+	setArray(array) {
+		this.type = TypeConstArray;
+		this.array = array;
+		this.sideEffects = false;
+		return this;
+	}
+
+	/**
+	 * Set's the value of this expression to a processed/unprocessed template string. Used
+	 * for evaluating TemplateLiteral expressions in the JavaScript Parser.
+	 * @param {BasicEvaluatedExpression[]} quasis template string quasis
+	 * @param {BasicEvaluatedExpression[]} parts template string parts
+	 * @param {"cooked" | "raw"} kind template string kind
+	 * @returns {this} this
+	 */
+	setTemplateString(quasis, parts, kind) {
+		this.type = TypeTemplateString;
+		this.quasis = quasis;
+		this.parts = parts;
+		this.templateStringKind = kind;
+		this.sideEffects = parts.some(p => p.sideEffects);
+		return this;
+	}
+
+	setTruthy() {
+		this.falsy = false;
+		this.truthy = true;
+		this.nullish = false;
+		return this;
+	}
+
+	setFalsy() {
+		this.falsy = true;
+		this.truthy = false;
+		return this;
+	}
+
+	/**
+	 * Set's the value of the expression to nullish.
+	 * @param {boolean} value true, if the expression is nullish
+	 * @returns {this} this
+	 */
+	setNullish(value) {
+		this.nullish = value;
+
+		if (value) return this.setFalsy();
+
+		return this;
+	}
+
+	/**
+	 * Set's the range for the expression.
+	 * @param {[number, number]} range range to set
+	 * @returns {this} this
+	 */
+	setRange(range) {
+		this.range = range;
+		return this;
+	}
+
+	/**
+	 * Set whether or not the expression has side effects.
+	 * @param {boolean} sideEffects true, if the expression has side effects
+	 * @returns {this} this
+	 */
+	setSideEffects(sideEffects = true) {
+		this.sideEffects = sideEffects;
+		return this;
+	}
+
+	/**
+	 * Set the expression node for the expression.
+	 * @param {Node | undefined} expression expression
+	 * @returns {this} this
+	 */
+	setExpression(expression) {
+		this.expression = expression;
+		return this;
+	}
+}
+
+/**
+ * @param {string} flags regexp flags
+ * @returns {boolean} is valid flags
+ */
+BasicEvaluatedExpression.isValidRegExpFlags = flags => {
+	const len = flags.length;
+
+	if (len === 0) return true;
+	if (len > 4) return false;
+
+	// cspell:word gimy
+	let remaining = 0b0000; // bit per RegExp flag: gimy
+
+	for (let i = 0; i < len; i++)
+		switch (flags.charCodeAt(i)) {
+			case 103 /* g */:
+				if (remaining & 0b1000) return false;
+				remaining |= 0b1000;
+				break;
+			case 105 /* i */:
+				if (remaining & 0b0100) return false;
+				remaining |= 0b0100;
+				break;
+			case 109 /* m */:
+				if (remaining & 0b0010) return false;
+				remaining |= 0b0010;
+				break;
+			case 121 /* y */:
+				if (remaining & 0b0001) return false;
+				remaining |= 0b0001;
+				break;
+			default:
+				return false;
+		}
+
+	return true;
+};
+
+module.exports = BasicEvaluatedExpression;
Index: imaps-frontend/node_modules/webpack/lib/javascript/ChunkHelpers.js
===================================================================
--- imaps-frontend/node_modules/webpack/lib/javascript/ChunkHelpers.js	(revision 79a03170cdde4e801d12257ee86723774970427b)
+++ imaps-frontend/node_modules/webpack/lib/javascript/ChunkHelpers.js	(revision 79a03170cdde4e801d12257ee86723774970427b)
@@ -0,0 +1,33 @@
+/*
+	MIT License http://www.opensource.org/licenses/mit-license.php
+	Author Tobias Koppers @sokra
+*/
+
+"use strict";
+
+const Entrypoint = require("../Entrypoint");
+
+/** @typedef {import("../Chunk")} Chunk */
+
+/**
+ * @param {Entrypoint} entrypoint a chunk group
+ * @param {(Chunk | null)=} excludedChunk1 current chunk which is excluded
+ * @param {(Chunk | null)=} excludedChunk2 runtime chunk which is excluded
+ * @returns {Set<Chunk>} chunks
+ */
+const getAllChunks = (entrypoint, excludedChunk1, excludedChunk2) => {
+	const queue = new Set([entrypoint]);
+	const chunks = new Set();
+	for (const entrypoint of queue) {
+		for (const chunk of entrypoint.chunks) {
+			if (chunk === excludedChunk1) continue;
+			if (chunk === excludedChunk2) continue;
+			chunks.add(chunk);
+		}
+		for (const parent of entrypoint.parentsIterable) {
+			if (parent instanceof Entrypoint) queue.add(parent);
+		}
+	}
+	return chunks;
+};
+module.exports.getAllChunks = getAllChunks;
Index: imaps-frontend/node_modules/webpack/lib/javascript/CommonJsChunkFormatPlugin.js
===================================================================
--- imaps-frontend/node_modules/webpack/lib/javascript/CommonJsChunkFormatPlugin.js	(revision 79a03170cdde4e801d12257ee86723774970427b)
+++ imaps-frontend/node_modules/webpack/lib/javascript/CommonJsChunkFormatPlugin.js	(revision 79a03170cdde4e801d12257ee86723774970427b)
@@ -0,0 +1,175 @@
+/*
+	MIT License http://www.opensource.org/licenses/mit-license.php
+	Author Tobias Koppers @sokra
+*/
+
+"use strict";
+
+const { ConcatSource, RawSource } = require("webpack-sources");
+const RuntimeGlobals = require("../RuntimeGlobals");
+const Template = require("../Template");
+const { getUndoPath } = require("../util/identifier");
+const {
+	getChunkFilenameTemplate,
+	getCompilationHooks
+} = require("./JavascriptModulesPlugin");
+const {
+	generateEntryStartup,
+	updateHashForEntryStartup
+} = require("./StartupHelpers");
+
+/** @typedef {import("../Chunk")} Chunk */
+/** @typedef {import("../Compiler")} Compiler */
+/** @typedef {import("../Entrypoint")} Entrypoint */
+
+class CommonJsChunkFormatPlugin {
+	/**
+	 * Apply the plugin
+	 * @param {Compiler} compiler the compiler instance
+	 * @returns {void}
+	 */
+	apply(compiler) {
+		compiler.hooks.thisCompilation.tap(
+			"CommonJsChunkFormatPlugin",
+			compilation => {
+				compilation.hooks.additionalChunkRuntimeRequirements.tap(
+					"CommonJsChunkFormatPlugin",
+					(chunk, set, { chunkGraph }) => {
+						if (chunk.hasRuntime()) return;
+						if (chunkGraph.getNumberOfEntryModules(chunk) > 0) {
+							set.add(RuntimeGlobals.require);
+							set.add(RuntimeGlobals.startupEntrypoint);
+							set.add(RuntimeGlobals.externalInstallChunk);
+						}
+					}
+				);
+				const hooks = getCompilationHooks(compilation);
+				hooks.renderChunk.tap(
+					"CommonJsChunkFormatPlugin",
+					(modules, renderContext) => {
+						const { chunk, chunkGraph, runtimeTemplate } = renderContext;
+						const source = new ConcatSource();
+						source.add(`exports.id = ${JSON.stringify(chunk.id)};\n`);
+						source.add(`exports.ids = ${JSON.stringify(chunk.ids)};\n`);
+						source.add("exports.modules = ");
+						source.add(modules);
+						source.add(";\n");
+						const runtimeModules =
+							chunkGraph.getChunkRuntimeModulesInOrder(chunk);
+						if (runtimeModules.length > 0) {
+							source.add("exports.runtime =\n");
+							source.add(
+								Template.renderChunkRuntimeModules(
+									runtimeModules,
+									renderContext
+								)
+							);
+						}
+						const entries = Array.from(
+							chunkGraph.getChunkEntryModulesWithChunkGroupIterable(chunk)
+						);
+						if (entries.length > 0) {
+							const runtimeChunk =
+								/** @type {Entrypoint} */
+								(entries[0][1]).getRuntimeChunk();
+							const currentOutputName = compilation
+								.getPath(
+									getChunkFilenameTemplate(chunk, compilation.outputOptions),
+									{
+										chunk,
+										contentHashType: "javascript"
+									}
+								)
+								.replace(/^\/+/g, "")
+								.split("/");
+							const runtimeOutputName = compilation
+								.getPath(
+									getChunkFilenameTemplate(
+										/** @type {Chunk} */
+										(runtimeChunk),
+										compilation.outputOptions
+									),
+									{
+										chunk: /** @type {Chunk} */ (runtimeChunk),
+										contentHashType: "javascript"
+									}
+								)
+								.replace(/^\/+/g, "")
+								.split("/");
+
+							// remove common parts
+							while (
+								currentOutputName.length > 1 &&
+								runtimeOutputName.length > 1 &&
+								currentOutputName[0] === runtimeOutputName[0]
+							) {
+								currentOutputName.shift();
+								runtimeOutputName.shift();
+							}
+							const last = runtimeOutputName.join("/");
+							// create final path
+							const runtimePath =
+								getUndoPath(currentOutputName.join("/"), last, true) + last;
+
+							const entrySource = new ConcatSource();
+							entrySource.add(
+								`(${
+									runtimeTemplate.supportsArrowFunction()
+										? "() => "
+										: "function() "
+								}{\n`
+							);
+							entrySource.add("var exports = {};\n");
+							entrySource.add(source);
+							entrySource.add(";\n\n// load runtime\n");
+							entrySource.add(
+								`var ${RuntimeGlobals.require} = require(${JSON.stringify(
+									runtimePath
+								)});\n`
+							);
+							entrySource.add(
+								`${RuntimeGlobals.externalInstallChunk}(exports);\n`
+							);
+							const startupSource = new RawSource(
+								generateEntryStartup(
+									chunkGraph,
+									runtimeTemplate,
+									entries,
+									chunk,
+									false
+								)
+							);
+							entrySource.add(
+								hooks.renderStartup.call(
+									startupSource,
+									entries[entries.length - 1][0],
+									{
+										...renderContext,
+										inlined: false
+									}
+								)
+							);
+							entrySource.add("\n})()");
+							return entrySource;
+						}
+						return source;
+					}
+				);
+				hooks.chunkHash.tap(
+					"CommonJsChunkFormatPlugin",
+					(chunk, hash, { chunkGraph }) => {
+						if (chunk.hasRuntime()) return;
+						hash.update("CommonJsChunkFormatPlugin");
+						hash.update("1");
+						const entries = Array.from(
+							chunkGraph.getChunkEntryModulesWithChunkGroupIterable(chunk)
+						);
+						updateHashForEntryStartup(hash, chunkGraph, entries, chunk);
+					}
+				);
+			}
+		);
+	}
+}
+
+module.exports = CommonJsChunkFormatPlugin;
Index: imaps-frontend/node_modules/webpack/lib/javascript/EnableChunkLoadingPlugin.js
===================================================================
--- imaps-frontend/node_modules/webpack/lib/javascript/EnableChunkLoadingPlugin.js	(revision 79a03170cdde4e801d12257ee86723774970427b)
+++ imaps-frontend/node_modules/webpack/lib/javascript/EnableChunkLoadingPlugin.js	(revision 79a03170cdde4e801d12257ee86723774970427b)
@@ -0,0 +1,121 @@
+/*
+	MIT License http://www.opensource.org/licenses/mit-license.php
+	Author Tobias Koppers @sokra
+*/
+
+"use strict";
+
+/** @typedef {import("../../declarations/WebpackOptions").ChunkLoadingType} ChunkLoadingType */
+/** @typedef {import("../Compiler")} Compiler */
+
+/** @type {WeakMap<Compiler, Set<ChunkLoadingType>>} */
+const enabledTypes = new WeakMap();
+
+/**
+ * @param {Compiler} compiler compiler
+ * @returns {Set<ChunkLoadingType>} enabled types
+ */
+const getEnabledTypes = compiler => {
+	let set = enabledTypes.get(compiler);
+	if (set === undefined) {
+		set = new Set();
+		enabledTypes.set(compiler, set);
+	}
+	return set;
+};
+
+class EnableChunkLoadingPlugin {
+	/**
+	 * @param {ChunkLoadingType} type library type that should be available
+	 */
+	constructor(type) {
+		this.type = type;
+	}
+
+	/**
+	 * @param {Compiler} compiler the compiler instance
+	 * @param {ChunkLoadingType} type type of library
+	 * @returns {void}
+	 */
+	static setEnabled(compiler, type) {
+		getEnabledTypes(compiler).add(type);
+	}
+
+	/**
+	 * @param {Compiler} compiler the compiler instance
+	 * @param {ChunkLoadingType} type type of library
+	 * @returns {void}
+	 */
+	static checkEnabled(compiler, type) {
+		if (!getEnabledTypes(compiler).has(type)) {
+			throw new Error(
+				`Chunk loading type "${type}" is not enabled. ` +
+					"EnableChunkLoadingPlugin need to be used to enable this type of chunk loading. " +
+					'This usually happens through the "output.enabledChunkLoadingTypes" option. ' +
+					'If you are using a function as entry which sets "chunkLoading", you need to add all potential chunk loading types to "output.enabledChunkLoadingTypes". ' +
+					`These types are enabled: ${Array.from(
+						getEnabledTypes(compiler)
+					).join(", ")}`
+			);
+		}
+	}
+
+	/**
+	 * Apply the plugin
+	 * @param {Compiler} compiler the compiler instance
+	 * @returns {void}
+	 */
+	apply(compiler) {
+		const { type } = this;
+
+		// Only enable once
+		const enabled = getEnabledTypes(compiler);
+		if (enabled.has(type)) return;
+		enabled.add(type);
+
+		if (typeof type === "string") {
+			switch (type) {
+				case "jsonp": {
+					const JsonpChunkLoadingPlugin = require("../web/JsonpChunkLoadingPlugin");
+					new JsonpChunkLoadingPlugin().apply(compiler);
+					break;
+				}
+				case "import-scripts": {
+					const ImportScriptsChunkLoadingPlugin = require("../webworker/ImportScriptsChunkLoadingPlugin");
+					new ImportScriptsChunkLoadingPlugin().apply(compiler);
+					break;
+				}
+				case "require": {
+					// @ts-expect-error https://github.com/microsoft/TypeScript/issues/41697
+					const CommonJsChunkLoadingPlugin = require("../node/CommonJsChunkLoadingPlugin");
+					new CommonJsChunkLoadingPlugin({
+						asyncChunkLoading: false
+					}).apply(compiler);
+					break;
+				}
+				case "async-node": {
+					// @ts-expect-error https://github.com/microsoft/TypeScript/issues/41697
+					const CommonJsChunkLoadingPlugin = require("../node/CommonJsChunkLoadingPlugin");
+					new CommonJsChunkLoadingPlugin({
+						asyncChunkLoading: true
+					}).apply(compiler);
+					break;
+				}
+				case "import":
+				case "universal": {
+					const ModuleChunkLoadingPlugin = require("../esm/ModuleChunkLoadingPlugin");
+					new ModuleChunkLoadingPlugin().apply(compiler);
+					break;
+				}
+				default:
+					throw new Error(`Unsupported chunk loading type ${type}.
+Plugins which provide custom chunk loading types must call EnableChunkLoadingPlugin.setEnabled(compiler, type) to disable this error.`);
+			}
+		} else {
+			// TODO support plugin instances here
+			// apply them to the compiler
+		}
+	}
+}
+
+module.exports = EnableChunkLoadingPlugin;
Index: imaps-frontend/node_modules/webpack/lib/javascript/JavascriptGenerator.js
===================================================================
--- imaps-frontend/node_modules/webpack/lib/javascript/JavascriptGenerator.js	(revision 79a03170cdde4e801d12257ee86723774970427b)
+++ imaps-frontend/node_modules/webpack/lib/javascript/JavascriptGenerator.js	(revision 79a03170cdde4e801d12257ee86723774970427b)
@@ -0,0 +1,255 @@
+/*
+	MIT License http://www.opensource.org/licenses/mit-license.php
+	Author Tobias Koppers @sokra
+*/
+
+"use strict";
+
+const util = require("util");
+const { RawSource, ReplaceSource } = require("webpack-sources");
+const Generator = require("../Generator");
+const InitFragment = require("../InitFragment");
+const { JS_TYPES } = require("../ModuleSourceTypesConstants");
+const HarmonyCompatibilityDependency = require("../dependencies/HarmonyCompatibilityDependency");
+
+/** @typedef {import("webpack-sources").Source} Source */
+/** @typedef {import("../DependenciesBlock")} DependenciesBlock */
+/** @typedef {import("../Dependency")} Dependency */
+/** @typedef {import("../DependencyTemplate")} DependencyTemplate */
+/** @typedef {import("../DependencyTemplate").DependencyTemplateContext} DependencyTemplateContext */
+/** @typedef {import("../DependencyTemplates")} DependencyTemplates */
+/** @typedef {import("../Generator").GenerateContext} GenerateContext */
+/** @typedef {import("../Module")} Module */
+/** @typedef {import("../Module").ConcatenationBailoutReasonContext} ConcatenationBailoutReasonContext */
+/** @typedef {import("../Module").SourceTypes} SourceTypes */
+/** @typedef {import("../NormalModule")} NormalModule */
+/** @typedef {import("../RuntimeTemplate")} RuntimeTemplate */
+
+// TODO: clean up this file
+// replace with newer constructs
+
+const deprecatedGetInitFragments = util.deprecate(
+	/**
+	 * @param {DependencyTemplate} template template
+	 * @param {Dependency} dependency dependency
+	 * @param {DependencyTemplateContext} templateContext template context
+	 * @returns {InitFragment<GenerateContext>[]} init fragments
+	 */
+	(template, dependency, templateContext) =>
+		/** @type {DependencyTemplate & { getInitFragments: function(Dependency, DependencyTemplateContext): InitFragment<GenerateContext>[] }} */
+		(template).getInitFragments(dependency, templateContext),
+	"DependencyTemplate.getInitFragment is deprecated (use apply(dep, source, { initFragments }) instead)",
+	"DEP_WEBPACK_JAVASCRIPT_GENERATOR_GET_INIT_FRAGMENTS"
+);
+
+class JavascriptGenerator extends Generator {
+	/**
+	 * @param {NormalModule} module fresh module
+	 * @returns {SourceTypes} available types (do not mutate)
+	 */
+	getTypes(module) {
+		return JS_TYPES;
+	}
+
+	/**
+	 * @param {NormalModule} module the module
+	 * @param {string=} type source type
+	 * @returns {number} estimate size of the module
+	 */
+	getSize(module, type) {
+		const originalSource = module.originalSource();
+		if (!originalSource) {
+			return 39;
+		}
+		return originalSource.size();
+	}
+
+	/**
+	 * @param {NormalModule} module module for which the bailout reason should be determined
+	 * @param {ConcatenationBailoutReasonContext} context context
+	 * @returns {string | undefined} reason why this module can't be concatenated, undefined when it can be concatenated
+	 */
+	getConcatenationBailoutReason(module, context) {
+		// Only harmony modules are valid for optimization
+		if (
+			!module.buildMeta ||
+			module.buildMeta.exportsType !== "namespace" ||
+			module.presentationalDependencies === undefined ||
+			!module.presentationalDependencies.some(
+				d => d instanceof HarmonyCompatibilityDependency
+			)
+		) {
+			return "Module is not an ECMAScript module";
+		}
+
+		// Some expressions are not compatible with module concatenation
+		// because they may produce unexpected results. The plugin bails out
+		// if some were detected upfront.
+		if (module.buildInfo && module.buildInfo.moduleConcatenationBailout) {
+			return `Module uses ${module.buildInfo.moduleConcatenationBailout}`;
+		}
+	}
+
+	/**
+	 * @param {NormalModule} module module for which the code should be generated
+	 * @param {GenerateContext} generateContext context for generate
+	 * @returns {Source | null} generated code
+	 */
+	generate(module, generateContext) {
+		const originalSource = module.originalSource();
+		if (!originalSource) {
+			return new RawSource("throw new Error('No source available');");
+		}
+
+		const source = new ReplaceSource(originalSource);
+		/** @type {InitFragment<GenerateContext>[]} */
+		const initFragments = [];
+
+		this.sourceModule(module, initFragments, source, generateContext);
+
+		return InitFragment.addToSource(source, initFragments, generateContext);
+	}
+
+	/**
+	 * @param {Module} module the module to generate
+	 * @param {InitFragment<GenerateContext>[]} initFragments mutable list of init fragments
+	 * @param {ReplaceSource} source the current replace source which can be modified
+	 * @param {GenerateContext} generateContext the generateContext
+	 * @returns {void}
+	 */
+	sourceModule(module, initFragments, source, generateContext) {
+		for (const dependency of module.dependencies) {
+			this.sourceDependency(
+				module,
+				dependency,
+				initFragments,
+				source,
+				generateContext
+			);
+		}
+
+		if (module.presentationalDependencies !== undefined) {
+			for (const dependency of module.presentationalDependencies) {
+				this.sourceDependency(
+					module,
+					dependency,
+					initFragments,
+					source,
+					generateContext
+				);
+			}
+		}
+
+		for (const childBlock of module.blocks) {
+			this.sourceBlock(
+				module,
+				childBlock,
+				initFragments,
+				source,
+				generateContext
+			);
+		}
+	}
+
+	/**
+	 * @param {Module} module the module to generate
+	 * @param {DependenciesBlock} block the dependencies block which will be processed
+	 * @param {InitFragment<GenerateContext>[]} initFragments mutable list of init fragments
+	 * @param {ReplaceSource} source the current replace source which can be modified
+	 * @param {GenerateContext} generateContext the generateContext
+	 * @returns {void}
+	 */
+	sourceBlock(module, block, initFragments, source, generateContext) {
+		for (const dependency of block.dependencies) {
+			this.sourceDependency(
+				module,
+				dependency,
+				initFragments,
+				source,
+				generateContext
+			);
+		}
+
+		for (const childBlock of block.blocks) {
+			this.sourceBlock(
+				module,
+				childBlock,
+				initFragments,
+				source,
+				generateContext
+			);
+		}
+	}
+
+	/**
+	 * @param {Module} module the current module
+	 * @param {Dependency} dependency the dependency to generate
+	 * @param {InitFragment<GenerateContext>[]} initFragments mutable list of init fragments
+	 * @param {ReplaceSource} source the current replace source which can be modified
+	 * @param {GenerateContext} generateContext the render context
+	 * @returns {void}
+	 */
+	sourceDependency(module, dependency, initFragments, source, generateContext) {
+		const constructor =
+			/** @type {new (...args: EXPECTED_ANY[]) => Dependency} */
+			(dependency.constructor);
+		const template = generateContext.dependencyTemplates.get(constructor);
+		if (!template) {
+			throw new Error(
+				`No template for dependency: ${dependency.constructor.name}`
+			);
+		}
+
+		/** @type {InitFragment<GenerateContext>[] | undefined} */
+		let chunkInitFragments;
+
+		/** @type {DependencyTemplateContext} */
+		const templateContext = {
+			runtimeTemplate: generateContext.runtimeTemplate,
+			dependencyTemplates: generateContext.dependencyTemplates,
+			moduleGraph: generateContext.moduleGraph,
+			chunkGraph: generateContext.chunkGraph,
+			module,
+			runtime: generateContext.runtime,
+			runtimeRequirements: generateContext.runtimeRequirements,
+			concatenationScope: generateContext.concatenationScope,
+			codeGenerationResults:
+				/** @type {NonNullable<GenerateContext["codeGenerationResults"]>} */
+				(generateContext.codeGenerationResults),
+			initFragments,
+			get chunkInitFragments() {
+				if (!chunkInitFragments) {
+					const data =
+						/** @type {NonNullable<GenerateContext["getData"]>} */
+						(generateContext.getData)();
+					chunkInitFragments = data.get("chunkInitFragments");
+					if (!chunkInitFragments) {
+						chunkInitFragments = [];
+						data.set("chunkInitFragments", chunkInitFragments);
+					}
+				}
+
+				return chunkInitFragments;
+			}
+		};
+
+		template.apply(dependency, source, templateContext);
+
+		// TODO remove in webpack 6
+		if ("getInitFragments" in template) {
+			const fragments = deprecatedGetInitFragments(
+				template,
+				dependency,
+				templateContext
+			);
+
+			if (fragments) {
+				for (const fragment of fragments) {
+					initFragments.push(fragment);
+				}
+			}
+		}
+	}
+}
+
+module.exports = JavascriptGenerator;
Index: imaps-frontend/node_modules/webpack/lib/javascript/JavascriptModulesPlugin.js
===================================================================
--- imaps-frontend/node_modules/webpack/lib/javascript/JavascriptModulesPlugin.js	(revision 79a03170cdde4e801d12257ee86723774970427b)
+++ imaps-frontend/node_modules/webpack/lib/javascript/JavascriptModulesPlugin.js	(revision 79a03170cdde4e801d12257ee86723774970427b)
@@ -0,0 +1,1651 @@
+/*
+	MIT License http://www.opensource.org/licenses/mit-license.php
+	Author Tobias Koppers @sokra
+*/
+
+"use strict";
+
+const eslintScope = require("eslint-scope");
+const { SyncWaterfallHook, SyncHook, SyncBailHook } = require("tapable");
+const vm = require("vm");
+const {
+	ConcatSource,
+	OriginalSource,
+	PrefixSource,
+	RawSource,
+	CachedSource,
+	ReplaceSource
+} = require("webpack-sources");
+const Compilation = require("../Compilation");
+const { tryRunOrWebpackError } = require("../HookWebpackError");
+const HotUpdateChunk = require("../HotUpdateChunk");
+const InitFragment = require("../InitFragment");
+const {
+	JAVASCRIPT_MODULE_TYPE_AUTO,
+	JAVASCRIPT_MODULE_TYPE_DYNAMIC,
+	JAVASCRIPT_MODULE_TYPE_ESM,
+	WEBPACK_MODULE_TYPE_RUNTIME
+} = require("../ModuleTypeConstants");
+const RuntimeGlobals = require("../RuntimeGlobals");
+const Template = require("../Template");
+const { last, someInIterable } = require("../util/IterableHelpers");
+const StringXor = require("../util/StringXor");
+const { compareModulesByIdentifier } = require("../util/comparators");
+const {
+	getPathInAst,
+	getAllReferences,
+	RESERVED_NAMES,
+	findNewName,
+	addScopeSymbols,
+	getUsedNamesInScopeInfo
+} = require("../util/concatenate");
+const createHash = require("../util/createHash");
+const nonNumericOnlyHash = require("../util/nonNumericOnlyHash");
+const { intersectRuntime } = require("../util/runtime");
+const JavascriptGenerator = require("./JavascriptGenerator");
+const JavascriptParser = require("./JavascriptParser");
+
+/** @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").Output} OutputOptions */
+/** @typedef {import("../Chunk")} Chunk */
+/** @typedef {import("../ChunkGraph")} ChunkGraph */
+/** @typedef {import("../CodeGenerationResults")} CodeGenerationResults */
+/** @typedef {import("../Compilation").ChunkHashContext} ChunkHashContext */
+/** @typedef {import("../Compilation").ModuleObject} ModuleObject */
+/** @typedef {import("../Compiler")} Compiler */
+/** @typedef {import("../DependencyTemplates")} DependencyTemplates */
+/** @typedef {import("../Entrypoint")} Entrypoint */
+/** @typedef {import("../Module")} Module */
+/** @typedef {import("../Module").BuildInfo} BuildInfo */
+/** @typedef {import("../ModuleGraph")} ModuleGraph */
+/** @typedef {import("../RuntimeTemplate")} RuntimeTemplate */
+/** @typedef {import("../TemplatedPathPlugin").TemplatePath} TemplatePath */
+/** @typedef {import("../WebpackError")} WebpackError */
+/** @typedef {import("../javascript/JavascriptParser").Range} Range */
+/** @typedef {import("../util/Hash")} Hash */
+/** @typedef {import("../util/createHash").Algorithm} Algorithm */
+
+/**
+ * @param {Chunk} chunk a chunk
+ * @param {ChunkGraph} chunkGraph the chunk graph
+ * @returns {boolean} true, when a JS file is needed for this chunk
+ */
+const chunkHasJs = (chunk, chunkGraph) => {
+	if (chunkGraph.getNumberOfEntryModules(chunk) > 0) return true;
+
+	return Boolean(
+		chunkGraph.getChunkModulesIterableBySourceType(chunk, "javascript")
+	);
+};
+
+/**
+ * @param {Chunk} chunk a chunk
+ * @param {ChunkGraph} chunkGraph the chunk graph
+ * @returns {boolean} true, when a JS file is needed for this chunk
+ */
+const chunkHasRuntimeOrJs = (chunk, chunkGraph) => {
+	if (
+		chunkGraph.getChunkModulesIterableBySourceType(
+			chunk,
+			WEBPACK_MODULE_TYPE_RUNTIME
+		)
+	)
+		return true;
+
+	return Boolean(
+		chunkGraph.getChunkModulesIterableBySourceType(chunk, "javascript")
+	);
+};
+
+/**
+ * @param {Module} module a module
+ * @param {string} code the code
+ * @returns {string} generated code for the stack
+ */
+const printGeneratedCodeForStack = (module, code) => {
+	const lines = code.split("\n");
+	const n = `${lines.length}`.length;
+	return `\n\nGenerated code for ${module.identifier()}\n${lines
+		.map(
+			/**
+			 * @param {string} line the line
+			 * @param {number} i the index
+			 * @param {string[]} lines the lines
+			 * @returns {string} the line with line number
+			 */
+			(line, i, lines) => {
+				const iStr = `${i + 1}`;
+				return `${" ".repeat(n - iStr.length)}${iStr} | ${line}`;
+			}
+		)
+		.join("\n")}`;
+};
+
+/**
+ * @typedef {object} RenderContext
+ * @property {Chunk} chunk the chunk
+ * @property {DependencyTemplates} dependencyTemplates the dependency templates
+ * @property {RuntimeTemplate} runtimeTemplate the runtime template
+ * @property {ModuleGraph} moduleGraph the module graph
+ * @property {ChunkGraph} chunkGraph the chunk graph
+ * @property {CodeGenerationResults} codeGenerationResults results of code generation
+ * @property {boolean | undefined} strictMode rendering in strict context
+ */
+
+/**
+ * @typedef {object} MainRenderContext
+ * @property {Chunk} chunk the chunk
+ * @property {DependencyTemplates} dependencyTemplates the dependency templates
+ * @property {RuntimeTemplate} runtimeTemplate the runtime template
+ * @property {ModuleGraph} moduleGraph the module graph
+ * @property {ChunkGraph} chunkGraph the chunk graph
+ * @property {CodeGenerationResults} codeGenerationResults results of code generation
+ * @property {string} hash hash to be used for render call
+ * @property {boolean | undefined} strictMode rendering in strict context
+ */
+
+/**
+ * @typedef {object} ChunkRenderContext
+ * @property {Chunk} chunk the chunk
+ * @property {DependencyTemplates} dependencyTemplates the dependency templates
+ * @property {RuntimeTemplate} runtimeTemplate the runtime template
+ * @property {ModuleGraph} moduleGraph the module graph
+ * @property {ChunkGraph} chunkGraph the chunk graph
+ * @property {CodeGenerationResults} codeGenerationResults results of code generation
+ * @property {InitFragment<ChunkRenderContext>[]} chunkInitFragments init fragments for the chunk
+ * @property {boolean | undefined} strictMode rendering in strict context
+ */
+
+/**
+ * @typedef {object} RenderBootstrapContext
+ * @property {Chunk} chunk the chunk
+ * @property {CodeGenerationResults} codeGenerationResults results of code generation
+ * @property {RuntimeTemplate} runtimeTemplate the runtime template
+ * @property {ModuleGraph} moduleGraph the module graph
+ * @property {ChunkGraph} chunkGraph the chunk graph
+ * @property {string} hash hash to be used for render call
+ */
+
+/** @typedef {RenderContext & { inlined: boolean }} StartupRenderContext */
+
+/**
+ * @typedef {object} CompilationHooks
+ * @property {SyncWaterfallHook<[Source, Module, ChunkRenderContext]>} renderModuleContent
+ * @property {SyncWaterfallHook<[Source, Module, ChunkRenderContext]>} renderModuleContainer
+ * @property {SyncWaterfallHook<[Source, Module, ChunkRenderContext]>} renderModulePackage
+ * @property {SyncWaterfallHook<[Source, RenderContext]>} renderChunk
+ * @property {SyncWaterfallHook<[Source, RenderContext]>} renderMain
+ * @property {SyncWaterfallHook<[Source, RenderContext]>} renderContent
+ * @property {SyncWaterfallHook<[Source, RenderContext]>} render
+ * @property {SyncWaterfallHook<[Source, Module, StartupRenderContext]>} renderStartup
+ * @property {SyncWaterfallHook<[string, RenderBootstrapContext]>} renderRequire
+ * @property {SyncBailHook<[Module, RenderBootstrapContext], string | void>} inlineInRuntimeBailout
+ * @property {SyncBailHook<[Module, RenderContext], string | void>} embedInRuntimeBailout
+ * @property {SyncBailHook<[RenderContext], string | void>} strictRuntimeBailout
+ * @property {SyncHook<[Chunk, Hash, ChunkHashContext]>} chunkHash
+ * @property {SyncBailHook<[Chunk, RenderContext], boolean | void>} useSourceMap
+ */
+
+/** @type {WeakMap<Compilation, CompilationHooks>} */
+const compilationHooksMap = new WeakMap();
+
+const PLUGIN_NAME = "JavascriptModulesPlugin";
+
+/** @typedef {{ header: string[], beforeStartup: string[], startup: string[], afterStartup: string[], allowInlineStartup: boolean }} Bootstrap */
+
+class JavascriptModulesPlugin {
+	/**
+	 * @param {Compilation} compilation the compilation
+	 * @returns {CompilationHooks} the attached hooks
+	 */
+	static getCompilationHooks(compilation) {
+		if (!(compilation instanceof Compilation)) {
+			throw new TypeError(
+				"The 'compilation' argument must be an instance of Compilation"
+			);
+		}
+		let hooks = compilationHooksMap.get(compilation);
+		if (hooks === undefined) {
+			hooks = {
+				renderModuleContent: new SyncWaterfallHook([
+					"source",
+					"module",
+					"renderContext"
+				]),
+				renderModuleContainer: new SyncWaterfallHook([
+					"source",
+					"module",
+					"renderContext"
+				]),
+				renderModulePackage: new SyncWaterfallHook([
+					"source",
+					"module",
+					"renderContext"
+				]),
+				render: new SyncWaterfallHook(["source", "renderContext"]),
+				renderContent: new SyncWaterfallHook(["source", "renderContext"]),
+				renderStartup: new SyncWaterfallHook([
+					"source",
+					"module",
+					"startupRenderContext"
+				]),
+				renderChunk: new SyncWaterfallHook(["source", "renderContext"]),
+				renderMain: new SyncWaterfallHook(["source", "renderContext"]),
+				renderRequire: new SyncWaterfallHook(["code", "renderContext"]),
+				inlineInRuntimeBailout: new SyncBailHook(["module", "renderContext"]),
+				embedInRuntimeBailout: new SyncBailHook(["module", "renderContext"]),
+				strictRuntimeBailout: new SyncBailHook(["renderContext"]),
+				chunkHash: new SyncHook(["chunk", "hash", "context"]),
+				useSourceMap: new SyncBailHook(["chunk", "renderContext"])
+			};
+			compilationHooksMap.set(compilation, hooks);
+		}
+		return hooks;
+	}
+
+	constructor(options = {}) {
+		this.options = options;
+		/** @type {WeakMap<Source, TODO>} */
+		this._moduleFactoryCache = new WeakMap();
+	}
+
+	/**
+	 * Apply the plugin
+	 * @param {Compiler} compiler the compiler instance
+	 * @returns {void}
+	 */
+	apply(compiler) {
+		compiler.hooks.compilation.tap(
+			PLUGIN_NAME,
+			(compilation, { normalModuleFactory }) => {
+				const hooks = JavascriptModulesPlugin.getCompilationHooks(compilation);
+				normalModuleFactory.hooks.createParser
+					.for(JAVASCRIPT_MODULE_TYPE_AUTO)
+					.tap(PLUGIN_NAME, options => new JavascriptParser("auto"));
+				normalModuleFactory.hooks.createParser
+					.for(JAVASCRIPT_MODULE_TYPE_DYNAMIC)
+					.tap(PLUGIN_NAME, options => new JavascriptParser("script"));
+				normalModuleFactory.hooks.createParser
+					.for(JAVASCRIPT_MODULE_TYPE_ESM)
+					.tap(PLUGIN_NAME, options => new JavascriptParser("module"));
+				normalModuleFactory.hooks.createGenerator
+					.for(JAVASCRIPT_MODULE_TYPE_AUTO)
+					.tap(PLUGIN_NAME, () => new JavascriptGenerator());
+				normalModuleFactory.hooks.createGenerator
+					.for(JAVASCRIPT_MODULE_TYPE_DYNAMIC)
+					.tap(PLUGIN_NAME, () => new JavascriptGenerator());
+				normalModuleFactory.hooks.createGenerator
+					.for(JAVASCRIPT_MODULE_TYPE_ESM)
+					.tap(PLUGIN_NAME, () => new JavascriptGenerator());
+				compilation.hooks.renderManifest.tap(PLUGIN_NAME, (result, options) => {
+					const {
+						hash,
+						chunk,
+						chunkGraph,
+						moduleGraph,
+						runtimeTemplate,
+						dependencyTemplates,
+						outputOptions,
+						codeGenerationResults
+					} = options;
+
+					const hotUpdateChunk = chunk instanceof HotUpdateChunk ? chunk : null;
+					const filenameTemplate =
+						JavascriptModulesPlugin.getChunkFilenameTemplate(
+							chunk,
+							outputOptions
+						);
+
+					let render;
+
+					if (hotUpdateChunk) {
+						render = () =>
+							this.renderChunk(
+								{
+									chunk,
+									dependencyTemplates,
+									runtimeTemplate,
+									moduleGraph,
+									chunkGraph,
+									codeGenerationResults,
+									strictMode: runtimeTemplate.isModule()
+								},
+								hooks
+							);
+					} else if (chunk.hasRuntime()) {
+						if (!chunkHasRuntimeOrJs(chunk, chunkGraph)) {
+							return result;
+						}
+
+						render = () =>
+							this.renderMain(
+								{
+									hash,
+									chunk,
+									dependencyTemplates,
+									runtimeTemplate,
+									moduleGraph,
+									chunkGraph,
+									codeGenerationResults,
+									strictMode: runtimeTemplate.isModule()
+								},
+								hooks,
+								compilation
+							);
+					} else {
+						if (!chunkHasJs(chunk, chunkGraph)) {
+							return result;
+						}
+
+						render = () =>
+							this.renderChunk(
+								{
+									chunk,
+									dependencyTemplates,
+									runtimeTemplate,
+									moduleGraph,
+									chunkGraph,
+									codeGenerationResults,
+									strictMode: runtimeTemplate.isModule()
+								},
+								hooks
+							);
+					}
+
+					result.push({
+						render,
+						filenameTemplate,
+						pathOptions: {
+							hash,
+							runtime: chunk.runtime,
+							chunk,
+							contentHashType: "javascript"
+						},
+						info: {
+							javascriptModule: compilation.runtimeTemplate.isModule()
+						},
+						identifier: hotUpdateChunk
+							? `hotupdatechunk${chunk.id}`
+							: `chunk${chunk.id}`,
+						hash: chunk.contentHash.javascript
+					});
+
+					return result;
+				});
+				compilation.hooks.chunkHash.tap(PLUGIN_NAME, (chunk, hash, context) => {
+					hooks.chunkHash.call(chunk, hash, context);
+					if (chunk.hasRuntime()) {
+						this.updateHashWithBootstrap(
+							hash,
+							{
+								hash: "0000",
+								chunk,
+								codeGenerationResults: context.codeGenerationResults,
+								chunkGraph: context.chunkGraph,
+								moduleGraph: context.moduleGraph,
+								runtimeTemplate: context.runtimeTemplate
+							},
+							hooks
+						);
+					}
+				});
+				compilation.hooks.contentHash.tap(PLUGIN_NAME, chunk => {
+					const {
+						chunkGraph,
+						codeGenerationResults,
+						moduleGraph,
+						runtimeTemplate,
+						outputOptions: {
+							hashSalt,
+							hashDigest,
+							hashDigestLength,
+							hashFunction
+						}
+					} = compilation;
+					const hash = createHash(/** @type {Algorithm} */ (hashFunction));
+					if (hashSalt) hash.update(hashSalt);
+					if (chunk.hasRuntime()) {
+						this.updateHashWithBootstrap(
+							hash,
+							{
+								hash: "0000",
+								chunk,
+								codeGenerationResults,
+								chunkGraph: compilation.chunkGraph,
+								moduleGraph: compilation.moduleGraph,
+								runtimeTemplate: compilation.runtimeTemplate
+							},
+							hooks
+						);
+					} else {
+						hash.update(`${chunk.id} `);
+						hash.update(chunk.ids ? chunk.ids.join(",") : "");
+					}
+					hooks.chunkHash.call(chunk, hash, {
+						chunkGraph,
+						codeGenerationResults,
+						moduleGraph,
+						runtimeTemplate
+					});
+					const modules = chunkGraph.getChunkModulesIterableBySourceType(
+						chunk,
+						"javascript"
+					);
+					if (modules) {
+						const xor = new StringXor();
+						for (const m of modules) {
+							xor.add(chunkGraph.getModuleHash(m, chunk.runtime));
+						}
+						xor.updateHash(hash);
+					}
+					const runtimeModules = chunkGraph.getChunkModulesIterableBySourceType(
+						chunk,
+						WEBPACK_MODULE_TYPE_RUNTIME
+					);
+					if (runtimeModules) {
+						const xor = new StringXor();
+						for (const m of runtimeModules) {
+							xor.add(chunkGraph.getModuleHash(m, chunk.runtime));
+						}
+						xor.updateHash(hash);
+					}
+					const digest = /** @type {string} */ (hash.digest(hashDigest));
+					chunk.contentHash.javascript = nonNumericOnlyHash(
+						digest,
+						/** @type {number} */
+						(hashDigestLength)
+					);
+				});
+				compilation.hooks.additionalTreeRuntimeRequirements.tap(
+					PLUGIN_NAME,
+					(chunk, set, { chunkGraph }) => {
+						if (
+							!set.has(RuntimeGlobals.startupNoDefault) &&
+							chunkGraph.hasChunkEntryDependentChunks(chunk)
+						) {
+							set.add(RuntimeGlobals.onChunksLoaded);
+							set.add(RuntimeGlobals.exports);
+							set.add(RuntimeGlobals.require);
+						}
+					}
+				);
+				compilation.hooks.executeModule.tap(PLUGIN_NAME, (options, context) => {
+					const source = options.codeGenerationResult.sources.get("javascript");
+					if (source === undefined) return;
+					const { module } = options;
+					const code = source.source();
+
+					const fn = vm.runInThisContext(
+						`(function(${module.moduleArgument}, ${module.exportsArgument}, ${RuntimeGlobals.require}) {\n${code}\n/**/})`,
+						{
+							filename: module.identifier(),
+							lineOffset: -1
+						}
+					);
+
+					const moduleObject =
+						/** @type {ModuleObject} */
+						(options.moduleObject);
+
+					try {
+						fn.call(
+							moduleObject.exports,
+							moduleObject,
+							moduleObject.exports,
+							context.__webpack_require__
+						);
+					} catch (err) {
+						/** @type {Error} */
+						(err).stack += printGeneratedCodeForStack(
+							options.module,
+							/** @type {string} */ (code)
+						);
+						throw err;
+					}
+				});
+				compilation.hooks.executeModule.tap(PLUGIN_NAME, (options, context) => {
+					const source = options.codeGenerationResult.sources.get("runtime");
+					if (source === undefined) return;
+					let code = source.source();
+					if (typeof code !== "string") code = code.toString();
+
+					const fn = vm.runInThisContext(
+						`(function(${RuntimeGlobals.require}) {\n${code}\n/**/})`,
+						{
+							filename: options.module.identifier(),
+							lineOffset: -1
+						}
+					);
+					try {
+						// eslint-disable-next-line no-useless-call
+						fn.call(null, context.__webpack_require__);
+					} catch (err) {
+						/** @type {Error} */
+						(err).stack += printGeneratedCodeForStack(options.module, code);
+						throw err;
+					}
+				});
+			}
+		);
+	}
+
+	/**
+	 * @param {Chunk} chunk chunk
+	 * @param {OutputOptions} outputOptions output options
+	 * @returns {TemplatePath} used filename template
+	 */
+	static getChunkFilenameTemplate(chunk, outputOptions) {
+		if (chunk.filenameTemplate) {
+			return chunk.filenameTemplate;
+		} else if (chunk instanceof HotUpdateChunk) {
+			return /** @type {TemplatePath} */ (outputOptions.hotUpdateChunkFilename);
+		} else if (chunk.canBeInitial()) {
+			return /** @type {TemplatePath} */ (outputOptions.filename);
+		}
+		return /** @type {TemplatePath} */ (outputOptions.chunkFilename);
+	}
+
+	/**
+	 * @param {Module} module the rendered module
+	 * @param {ChunkRenderContext} renderContext options object
+	 * @param {CompilationHooks} hooks hooks
+	 * @param {boolean} factory true: renders as factory method, false: pure module content
+	 * @returns {Source | null} the newly generated source from rendering
+	 */
+	renderModule(module, renderContext, hooks, factory) {
+		const {
+			chunk,
+			chunkGraph,
+			runtimeTemplate,
+			codeGenerationResults,
+			strictMode
+		} = renderContext;
+		try {
+			const codeGenResult = codeGenerationResults.get(module, chunk.runtime);
+			const moduleSource = codeGenResult.sources.get("javascript");
+			if (!moduleSource) return null;
+			if (codeGenResult.data !== undefined) {
+				const chunkInitFragments = codeGenResult.data.get("chunkInitFragments");
+				if (chunkInitFragments) {
+					for (const i of chunkInitFragments)
+						renderContext.chunkInitFragments.push(i);
+				}
+			}
+			const moduleSourcePostContent = tryRunOrWebpackError(
+				() =>
+					hooks.renderModuleContent.call(moduleSource, module, renderContext),
+				"JavascriptModulesPlugin.getCompilationHooks().renderModuleContent"
+			);
+			let moduleSourcePostContainer;
+			if (factory) {
+				const runtimeRequirements = chunkGraph.getModuleRuntimeRequirements(
+					module,
+					chunk.runtime
+				);
+				const needModule = runtimeRequirements.has(RuntimeGlobals.module);
+				const needExports = runtimeRequirements.has(RuntimeGlobals.exports);
+				const needRequire =
+					runtimeRequirements.has(RuntimeGlobals.require) ||
+					runtimeRequirements.has(RuntimeGlobals.requireScope);
+				const needThisAsExports = runtimeRequirements.has(
+					RuntimeGlobals.thisAsExports
+				);
+				const needStrict =
+					/** @type {BuildInfo} */
+					(module.buildInfo).strict && !strictMode;
+				const cacheEntry = this._moduleFactoryCache.get(
+					moduleSourcePostContent
+				);
+				let source;
+				if (
+					cacheEntry &&
+					cacheEntry.needModule === needModule &&
+					cacheEntry.needExports === needExports &&
+					cacheEntry.needRequire === needRequire &&
+					cacheEntry.needThisAsExports === needThisAsExports &&
+					cacheEntry.needStrict === needStrict
+				) {
+					source = cacheEntry.source;
+				} else {
+					const factorySource = new ConcatSource();
+					const args = [];
+					if (needExports || needRequire || needModule)
+						args.push(
+							needModule
+								? module.moduleArgument
+								: `__unused_webpack_${module.moduleArgument}`
+						);
+					if (needExports || needRequire)
+						args.push(
+							needExports
+								? module.exportsArgument
+								: `__unused_webpack_${module.exportsArgument}`
+						);
+					if (needRequire) args.push(RuntimeGlobals.require);
+					if (!needThisAsExports && runtimeTemplate.supportsArrowFunction()) {
+						factorySource.add(`/***/ ((${args.join(", ")}) => {\n\n`);
+					} else {
+						factorySource.add(`/***/ (function(${args.join(", ")}) {\n\n`);
+					}
+					if (needStrict) {
+						factorySource.add('"use strict";\n');
+					}
+					factorySource.add(moduleSourcePostContent);
+					factorySource.add("\n\n/***/ })");
+					source = new CachedSource(factorySource);
+					this._moduleFactoryCache.set(moduleSourcePostContent, {
+						source,
+						needModule,
+						needExports,
+						needRequire,
+						needThisAsExports,
+						needStrict
+					});
+				}
+				moduleSourcePostContainer = tryRunOrWebpackError(
+					() => hooks.renderModuleContainer.call(source, module, renderContext),
+					"JavascriptModulesPlugin.getCompilationHooks().renderModuleContainer"
+				);
+			} else {
+				moduleSourcePostContainer = moduleSourcePostContent;
+			}
+			return tryRunOrWebpackError(
+				() =>
+					hooks.renderModulePackage.call(
+						moduleSourcePostContainer,
+						module,
+						renderContext
+					),
+				"JavascriptModulesPlugin.getCompilationHooks().renderModulePackage"
+			);
+		} catch (err) {
+			/** @type {WebpackError} */
+			(err).module = module;
+			throw err;
+		}
+	}
+
+	/**
+	 * @param {RenderContext} renderContext the render context
+	 * @param {CompilationHooks} hooks hooks
+	 * @returns {Source} the rendered source
+	 */
+	renderChunk(renderContext, hooks) {
+		const { chunk, chunkGraph } = renderContext;
+		const modules = chunkGraph.getOrderedChunkModulesIterableBySourceType(
+			chunk,
+			"javascript",
+			compareModulesByIdentifier
+		);
+		const allModules = modules ? Array.from(modules) : [];
+		let strictHeader;
+		let allStrict = renderContext.strictMode;
+		if (
+			!allStrict &&
+			allModules.every(m => /** @type {BuildInfo} */ (m.buildInfo).strict)
+		) {
+			const strictBailout = hooks.strictRuntimeBailout.call(renderContext);
+			strictHeader = strictBailout
+				? `// runtime can't be in strict mode because ${strictBailout}.\n`
+				: '"use strict";\n';
+			if (!strictBailout) allStrict = true;
+		}
+		/** @type {ChunkRenderContext} */
+		const chunkRenderContext = {
+			...renderContext,
+			chunkInitFragments: [],
+			strictMode: allStrict
+		};
+		const moduleSources =
+			Template.renderChunkModules(chunkRenderContext, allModules, module =>
+				this.renderModule(module, chunkRenderContext, hooks, true)
+			) || new RawSource("{}");
+		let source = tryRunOrWebpackError(
+			() => hooks.renderChunk.call(moduleSources, chunkRenderContext),
+			"JavascriptModulesPlugin.getCompilationHooks().renderChunk"
+		);
+		source = tryRunOrWebpackError(
+			() => hooks.renderContent.call(source, chunkRenderContext),
+			"JavascriptModulesPlugin.getCompilationHooks().renderContent"
+		);
+		if (!source) {
+			throw new Error(
+				"JavascriptModulesPlugin error: JavascriptModulesPlugin.getCompilationHooks().renderContent plugins should return something"
+			);
+		}
+		source = InitFragment.addToSource(
+			source,
+			chunkRenderContext.chunkInitFragments,
+			chunkRenderContext
+		);
+		source = tryRunOrWebpackError(
+			() => hooks.render.call(source, chunkRenderContext),
+			"JavascriptModulesPlugin.getCompilationHooks().render"
+		);
+		if (!source) {
+			throw new Error(
+				"JavascriptModulesPlugin error: JavascriptModulesPlugin.getCompilationHooks().render plugins should return something"
+			);
+		}
+		chunk.rendered = true;
+		return strictHeader
+			? new ConcatSource(strictHeader, source, ";")
+			: renderContext.runtimeTemplate.isModule()
+				? source
+				: new ConcatSource(source, ";");
+	}
+
+	/**
+	 * @param {MainRenderContext} renderContext options object
+	 * @param {CompilationHooks} hooks hooks
+	 * @param {Compilation} compilation the compilation
+	 * @returns {Source} the newly generated source from rendering
+	 */
+	renderMain(renderContext, hooks, compilation) {
+		const { chunk, chunkGraph, runtimeTemplate } = renderContext;
+
+		const runtimeRequirements = chunkGraph.getTreeRuntimeRequirements(chunk);
+		const iife = runtimeTemplate.isIIFE();
+
+		const bootstrap = this.renderBootstrap(renderContext, hooks);
+		const useSourceMap = hooks.useSourceMap.call(chunk, renderContext);
+
+		const allModules = Array.from(
+			chunkGraph.getOrderedChunkModulesIterableBySourceType(
+				chunk,
+				"javascript",
+				compareModulesByIdentifier
+			) || []
+		);
+
+		const hasEntryModules = chunkGraph.getNumberOfEntryModules(chunk) > 0;
+		/** @type {Set<Module> | undefined} */
+		let inlinedModules;
+		if (bootstrap.allowInlineStartup && hasEntryModules) {
+			inlinedModules = new Set(chunkGraph.getChunkEntryModulesIterable(chunk));
+		}
+
+		const source = new ConcatSource();
+		let prefix;
+		if (iife) {
+			if (runtimeTemplate.supportsArrowFunction()) {
+				source.add("/******/ (() => { // webpackBootstrap\n");
+			} else {
+				source.add("/******/ (function() { // webpackBootstrap\n");
+			}
+			prefix = "/******/ \t";
+		} else {
+			prefix = "/******/ ";
+		}
+		let allStrict = renderContext.strictMode;
+		if (
+			!allStrict &&
+			allModules.every(m => /** @type {BuildInfo} */ (m.buildInfo).strict)
+		) {
+			const strictBailout = hooks.strictRuntimeBailout.call(renderContext);
+			if (strictBailout) {
+				source.add(
+					`${
+						prefix
+					}// runtime can't be in strict mode because ${strictBailout}.\n`
+				);
+			} else {
+				allStrict = true;
+				source.add(`${prefix}"use strict";\n`);
+			}
+		}
+
+		/** @type {ChunkRenderContext} */
+		const chunkRenderContext = {
+			...renderContext,
+			chunkInitFragments: [],
+			strictMode: allStrict
+		};
+
+		const chunkModules = Template.renderChunkModules(
+			chunkRenderContext,
+			inlinedModules
+				? allModules.filter(
+						m => !(/** @type {Set<Module>} */ (inlinedModules).has(m))
+					)
+				: allModules,
+			module => this.renderModule(module, chunkRenderContext, hooks, true),
+			prefix
+		);
+		if (
+			chunkModules ||
+			runtimeRequirements.has(RuntimeGlobals.moduleFactories) ||
+			runtimeRequirements.has(RuntimeGlobals.moduleFactoriesAddOnly) ||
+			runtimeRequirements.has(RuntimeGlobals.require)
+		) {
+			source.add(`${prefix}var __webpack_modules__ = (`);
+			source.add(chunkModules || "{}");
+			source.add(");\n");
+			source.add(
+				"/************************************************************************/\n"
+			);
+		}
+
+		if (bootstrap.header.length > 0) {
+			const header = `${Template.asString(bootstrap.header)}\n`;
+			source.add(
+				new PrefixSource(
+					prefix,
+					useSourceMap
+						? new OriginalSource(header, "webpack/bootstrap")
+						: new RawSource(header)
+				)
+			);
+			source.add(
+				"/************************************************************************/\n"
+			);
+		}
+
+		const runtimeModules =
+			renderContext.chunkGraph.getChunkRuntimeModulesInOrder(chunk);
+
+		if (runtimeModules.length > 0) {
+			source.add(
+				new PrefixSource(
+					prefix,
+					Template.renderRuntimeModules(runtimeModules, chunkRenderContext)
+				)
+			);
+			source.add(
+				"/************************************************************************/\n"
+			);
+			// runtimeRuntimeModules calls codeGeneration
+			for (const module of runtimeModules) {
+				compilation.codeGeneratedModules.add(module);
+			}
+		}
+		if (inlinedModules) {
+			if (bootstrap.beforeStartup.length > 0) {
+				const beforeStartup = `${Template.asString(bootstrap.beforeStartup)}\n`;
+				source.add(
+					new PrefixSource(
+						prefix,
+						useSourceMap
+							? new OriginalSource(beforeStartup, "webpack/before-startup")
+							: new RawSource(beforeStartup)
+					)
+				);
+			}
+			const lastInlinedModule = /** @type {Module} */ (last(inlinedModules));
+			const startupSource = new ConcatSource();
+
+			if (runtimeRequirements.has(RuntimeGlobals.exports)) {
+				startupSource.add(`var ${RuntimeGlobals.exports} = {};\n`);
+			}
+
+			const avoidEntryIife = compilation.options.optimization.avoidEntryIife;
+			/** @type {Map<Module, Source> | false} */
+			let renamedInlinedModule = false;
+			if (avoidEntryIife) {
+				renamedInlinedModule = this.getRenamedInlineModule(
+					allModules,
+					renderContext,
+					inlinedModules,
+					chunkRenderContext,
+					hooks,
+					allStrict,
+					Boolean(chunkModules)
+				);
+			}
+
+			for (const m of inlinedModules) {
+				const renderedModule = renamedInlinedModule
+					? renamedInlinedModule.get(m)
+					: this.renderModule(m, chunkRenderContext, hooks, false);
+
+				if (renderedModule) {
+					const innerStrict =
+						!allStrict && /** @type {BuildInfo} */ (m.buildInfo).strict;
+					const runtimeRequirements = chunkGraph.getModuleRuntimeRequirements(
+						m,
+						chunk.runtime
+					);
+					const exports = runtimeRequirements.has(RuntimeGlobals.exports);
+					const webpackExports =
+						exports && m.exportsArgument === RuntimeGlobals.exports;
+					const iife = innerStrict
+						? "it needs to be in strict mode."
+						: inlinedModules.size > 1
+							? // TODO check globals and top-level declarations of other entries and chunk modules
+								// to make a better decision
+								"it needs to be isolated against other entry modules."
+							: chunkModules && !renamedInlinedModule
+								? "it needs to be isolated against other modules in the chunk."
+								: exports && !webpackExports
+									? `it uses a non-standard name for the exports (${m.exportsArgument}).`
+									: hooks.embedInRuntimeBailout.call(m, renderContext);
+					let footer;
+					if (iife !== undefined) {
+						startupSource.add(
+							`// This entry needs to be wrapped in an IIFE because ${iife}\n`
+						);
+						const arrow = runtimeTemplate.supportsArrowFunction();
+						if (arrow) {
+							startupSource.add("(() => {\n");
+							footer = "\n})();\n\n";
+						} else {
+							startupSource.add("!function() {\n");
+							footer = "\n}();\n";
+						}
+						if (innerStrict) startupSource.add('"use strict";\n');
+					} else {
+						footer = "\n";
+					}
+					if (exports) {
+						if (m !== lastInlinedModule)
+							startupSource.add(`var ${m.exportsArgument} = {};\n`);
+						else if (m.exportsArgument !== RuntimeGlobals.exports)
+							startupSource.add(
+								`var ${m.exportsArgument} = ${RuntimeGlobals.exports};\n`
+							);
+					}
+					startupSource.add(renderedModule);
+					startupSource.add(footer);
+				}
+			}
+			if (runtimeRequirements.has(RuntimeGlobals.onChunksLoaded)) {
+				startupSource.add(
+					`${RuntimeGlobals.exports} = ${RuntimeGlobals.onChunksLoaded}(${RuntimeGlobals.exports});\n`
+				);
+			}
+			source.add(
+				hooks.renderStartup.call(startupSource, lastInlinedModule, {
+					...renderContext,
+					inlined: true
+				})
+			);
+			if (bootstrap.afterStartup.length > 0) {
+				const afterStartup = `${Template.asString(bootstrap.afterStartup)}\n`;
+				source.add(
+					new PrefixSource(
+						prefix,
+						useSourceMap
+							? new OriginalSource(afterStartup, "webpack/after-startup")
+							: new RawSource(afterStartup)
+					)
+				);
+			}
+		} else {
+			const lastEntryModule =
+				/** @type {Module} */
+				(last(chunkGraph.getChunkEntryModulesIterable(chunk)));
+			/** @type {function(string[], string): Source} */
+			const toSource = useSourceMap
+				? (content, name) =>
+						new OriginalSource(Template.asString(content), name)
+				: content => new RawSource(Template.asString(content));
+			source.add(
+				new PrefixSource(
+					prefix,
+					new ConcatSource(
+						toSource(bootstrap.beforeStartup, "webpack/before-startup"),
+						"\n",
+						hooks.renderStartup.call(
+							toSource(bootstrap.startup.concat(""), "webpack/startup"),
+							lastEntryModule,
+							{
+								...renderContext,
+								inlined: false
+							}
+						),
+						toSource(bootstrap.afterStartup, "webpack/after-startup"),
+						"\n"
+					)
+				)
+			);
+		}
+		if (
+			hasEntryModules &&
+			runtimeRequirements.has(RuntimeGlobals.returnExportsFromRuntime)
+		) {
+			source.add(`${prefix}return ${RuntimeGlobals.exports};\n`);
+		}
+		if (iife) {
+			source.add("/******/ })()\n");
+		}
+
+		/** @type {Source} */
+		let finalSource = tryRunOrWebpackError(
+			() => hooks.renderMain.call(source, renderContext),
+			"JavascriptModulesPlugin.getCompilationHooks().renderMain"
+		);
+		if (!finalSource) {
+			throw new Error(
+				"JavascriptModulesPlugin error: JavascriptModulesPlugin.getCompilationHooks().renderMain plugins should return something"
+			);
+		}
+		finalSource = tryRunOrWebpackError(
+			() => hooks.renderContent.call(finalSource, renderContext),
+			"JavascriptModulesPlugin.getCompilationHooks().renderContent"
+		);
+		if (!finalSource) {
+			throw new Error(
+				"JavascriptModulesPlugin error: JavascriptModulesPlugin.getCompilationHooks().renderContent plugins should return something"
+			);
+		}
+
+		finalSource = InitFragment.addToSource(
+			finalSource,
+			chunkRenderContext.chunkInitFragments,
+			chunkRenderContext
+		);
+		finalSource = tryRunOrWebpackError(
+			() => hooks.render.call(finalSource, renderContext),
+			"JavascriptModulesPlugin.getCompilationHooks().render"
+		);
+		if (!finalSource) {
+			throw new Error(
+				"JavascriptModulesPlugin error: JavascriptModulesPlugin.getCompilationHooks().render plugins should return something"
+			);
+		}
+		chunk.rendered = true;
+		return iife ? new ConcatSource(finalSource, ";") : finalSource;
+	}
+
+	/**
+	 * @param {Hash} hash the hash to be updated
+	 * @param {RenderBootstrapContext} renderContext options object
+	 * @param {CompilationHooks} hooks hooks
+	 */
+	updateHashWithBootstrap(hash, renderContext, hooks) {
+		const bootstrap = this.renderBootstrap(renderContext, hooks);
+		for (const _k of Object.keys(bootstrap)) {
+			const key = /** @type {keyof Bootstrap} */ (_k);
+			hash.update(key);
+			if (Array.isArray(bootstrap[key])) {
+				for (const line of bootstrap[key]) {
+					hash.update(line);
+				}
+			} else {
+				hash.update(JSON.stringify(bootstrap[key]));
+			}
+		}
+	}
+
+	/**
+	 * @param {RenderBootstrapContext} renderContext options object
+	 * @param {CompilationHooks} hooks hooks
+	 * @returns {Bootstrap} the generated source of the bootstrap code
+	 */
+	renderBootstrap(renderContext, hooks) {
+		const {
+			chunkGraph,
+			codeGenerationResults,
+			moduleGraph,
+			chunk,
+			runtimeTemplate
+		} = renderContext;
+
+		const runtimeRequirements = chunkGraph.getTreeRuntimeRequirements(chunk);
+
+		const requireFunction = runtimeRequirements.has(RuntimeGlobals.require);
+		const moduleCache = runtimeRequirements.has(RuntimeGlobals.moduleCache);
+		const moduleFactories = runtimeRequirements.has(
+			RuntimeGlobals.moduleFactories
+		);
+		const moduleUsed = runtimeRequirements.has(RuntimeGlobals.module);
+		const requireScopeUsed = runtimeRequirements.has(
+			RuntimeGlobals.requireScope
+		);
+		const interceptModuleExecution = runtimeRequirements.has(
+			RuntimeGlobals.interceptModuleExecution
+		);
+
+		const useRequire =
+			requireFunction || interceptModuleExecution || moduleUsed;
+
+		/**
+		 * @type {{startup: string[], beforeStartup: string[], header: string[], afterStartup: string[], allowInlineStartup: boolean}}
+		 */
+		const result = {
+			header: [],
+			beforeStartup: [],
+			startup: [],
+			afterStartup: [],
+			allowInlineStartup: true
+		};
+
+		const { header: buf, startup, beforeStartup, afterStartup } = result;
+
+		if (result.allowInlineStartup && moduleFactories) {
+			startup.push(
+				"// module factories are used so entry inlining is disabled"
+			);
+			result.allowInlineStartup = false;
+		}
+		if (result.allowInlineStartup && moduleCache) {
+			startup.push("// module cache are used so entry inlining is disabled");
+			result.allowInlineStartup = false;
+		}
+		if (result.allowInlineStartup && interceptModuleExecution) {
+			startup.push(
+				"// module execution is intercepted so entry inlining is disabled"
+			);
+			result.allowInlineStartup = false;
+		}
+
+		if (useRequire || moduleCache) {
+			buf.push("// The module cache");
+			buf.push("var __webpack_module_cache__ = {};");
+			buf.push("");
+		}
+
+		if (useRequire) {
+			buf.push("// The require function");
+			buf.push(`function ${RuntimeGlobals.require}(moduleId) {`);
+			buf.push(Template.indent(this.renderRequire(renderContext, hooks)));
+			buf.push("}");
+			buf.push("");
+		} else if (runtimeRequirements.has(RuntimeGlobals.requireScope)) {
+			buf.push("// The require scope");
+			buf.push(`var ${RuntimeGlobals.require} = {};`);
+			buf.push("");
+		}
+
+		if (
+			moduleFactories ||
+			runtimeRequirements.has(RuntimeGlobals.moduleFactoriesAddOnly)
+		) {
+			buf.push("// expose the modules object (__webpack_modules__)");
+			buf.push(`${RuntimeGlobals.moduleFactories} = __webpack_modules__;`);
+			buf.push("");
+		}
+
+		if (moduleCache) {
+			buf.push("// expose the module cache");
+			buf.push(`${RuntimeGlobals.moduleCache} = __webpack_module_cache__;`);
+			buf.push("");
+		}
+
+		if (interceptModuleExecution) {
+			buf.push("// expose the module execution interceptor");
+			buf.push(`${RuntimeGlobals.interceptModuleExecution} = [];`);
+			buf.push("");
+		}
+
+		if (!runtimeRequirements.has(RuntimeGlobals.startupNoDefault)) {
+			if (chunkGraph.getNumberOfEntryModules(chunk) > 0) {
+				/** @type {string[]} */
+				const buf2 = [];
+				const runtimeRequirements =
+					chunkGraph.getTreeRuntimeRequirements(chunk);
+				buf2.push("// Load entry module and return exports");
+				let i = chunkGraph.getNumberOfEntryModules(chunk);
+				for (const [
+					entryModule,
+					entrypoint
+				] of chunkGraph.getChunkEntryModulesWithChunkGroupIterable(chunk)) {
+					if (!chunkGraph.getModuleSourceTypes(entryModule).has("javascript")) {
+						i--;
+						continue;
+					}
+					const chunks =
+						/** @type {Entrypoint} */
+						(entrypoint).chunks.filter(c => c !== chunk);
+					if (result.allowInlineStartup && chunks.length > 0) {
+						buf2.push(
+							"// This entry module depends on other loaded chunks and execution need to be delayed"
+						);
+						result.allowInlineStartup = false;
+					}
+					if (
+						result.allowInlineStartup &&
+						someInIterable(
+							moduleGraph.getIncomingConnectionsByOriginModule(entryModule),
+							([originModule, connections]) =>
+								originModule &&
+								connections.some(c => c.isTargetActive(chunk.runtime)) &&
+								someInIterable(
+									chunkGraph.getModuleRuntimes(originModule),
+									runtime =>
+										intersectRuntime(runtime, chunk.runtime) !== undefined
+								)
+						)
+					) {
+						buf2.push(
+							"// This entry module is referenced by other modules so it can't be inlined"
+						);
+						result.allowInlineStartup = false;
+					}
+
+					let data;
+					if (codeGenerationResults.has(entryModule, chunk.runtime)) {
+						const result = codeGenerationResults.get(
+							entryModule,
+							chunk.runtime
+						);
+						data = result.data;
+					}
+					if (
+						result.allowInlineStartup &&
+						(!data || !data.get("topLevelDeclarations")) &&
+						(!entryModule.buildInfo ||
+							!entryModule.buildInfo.topLevelDeclarations)
+					) {
+						buf2.push(
+							"// This entry module doesn't tell about it's top-level declarations so it can't be inlined"
+						);
+						result.allowInlineStartup = false;
+					}
+					if (result.allowInlineStartup) {
+						const bailout = hooks.inlineInRuntimeBailout.call(
+							entryModule,
+							renderContext
+						);
+						if (bailout !== undefined) {
+							buf2.push(
+								`// This entry module can't be inlined because ${bailout}`
+							);
+							result.allowInlineStartup = false;
+						}
+					}
+					i--;
+					const moduleId = chunkGraph.getModuleId(entryModule);
+					const entryRuntimeRequirements =
+						chunkGraph.getModuleRuntimeRequirements(entryModule, chunk.runtime);
+					let moduleIdExpr = JSON.stringify(moduleId);
+					if (runtimeRequirements.has(RuntimeGlobals.entryModuleId)) {
+						moduleIdExpr = `${RuntimeGlobals.entryModuleId} = ${moduleIdExpr}`;
+					}
+					if (
+						result.allowInlineStartup &&
+						entryRuntimeRequirements.has(RuntimeGlobals.module)
+					) {
+						result.allowInlineStartup = false;
+						buf2.push(
+							"// This entry module used 'module' so it can't be inlined"
+						);
+					}
+					if (chunks.length > 0) {
+						buf2.push(
+							`${i === 0 ? `var ${RuntimeGlobals.exports} = ` : ""}${
+								RuntimeGlobals.onChunksLoaded
+							}(undefined, ${JSON.stringify(
+								chunks.map(c => c.id)
+							)}, ${runtimeTemplate.returningFunction(
+								`${RuntimeGlobals.require}(${moduleIdExpr})`
+							)})`
+						);
+					} else if (useRequire) {
+						buf2.push(
+							`${i === 0 ? `var ${RuntimeGlobals.exports} = ` : ""}${
+								RuntimeGlobals.require
+							}(${moduleIdExpr});`
+						);
+					} else {
+						if (i === 0) buf2.push(`var ${RuntimeGlobals.exports} = {};`);
+						if (requireScopeUsed) {
+							buf2.push(
+								`__webpack_modules__[${moduleIdExpr}](0, ${
+									i === 0 ? RuntimeGlobals.exports : "{}"
+								}, ${RuntimeGlobals.require});`
+							);
+						} else if (entryRuntimeRequirements.has(RuntimeGlobals.exports)) {
+							buf2.push(
+								`__webpack_modules__[${moduleIdExpr}](0, ${
+									i === 0 ? RuntimeGlobals.exports : "{}"
+								});`
+							);
+						} else {
+							buf2.push(`__webpack_modules__[${moduleIdExpr}]();`);
+						}
+					}
+				}
+				if (runtimeRequirements.has(RuntimeGlobals.onChunksLoaded)) {
+					buf2.push(
+						`${RuntimeGlobals.exports} = ${RuntimeGlobals.onChunksLoaded}(${RuntimeGlobals.exports});`
+					);
+				}
+				if (
+					runtimeRequirements.has(RuntimeGlobals.startup) ||
+					(runtimeRequirements.has(RuntimeGlobals.startupOnlyBefore) &&
+						runtimeRequirements.has(RuntimeGlobals.startupOnlyAfter))
+				) {
+					result.allowInlineStartup = false;
+					buf.push("// the startup function");
+					buf.push(
+						`${RuntimeGlobals.startup} = ${runtimeTemplate.basicFunction("", [
+							...buf2,
+							`return ${RuntimeGlobals.exports};`
+						])};`
+					);
+					buf.push("");
+					startup.push("// run startup");
+					startup.push(
+						`var ${RuntimeGlobals.exports} = ${RuntimeGlobals.startup}();`
+					);
+				} else if (runtimeRequirements.has(RuntimeGlobals.startupOnlyBefore)) {
+					buf.push("// the startup function");
+					buf.push(
+						`${RuntimeGlobals.startup} = ${runtimeTemplate.emptyFunction()};`
+					);
+					beforeStartup.push("// run runtime startup");
+					beforeStartup.push(`${RuntimeGlobals.startup}();`);
+					startup.push("// startup");
+					startup.push(Template.asString(buf2));
+				} else if (runtimeRequirements.has(RuntimeGlobals.startupOnlyAfter)) {
+					buf.push("// the startup function");
+					buf.push(
+						`${RuntimeGlobals.startup} = ${runtimeTemplate.emptyFunction()};`
+					);
+					startup.push("// startup");
+					startup.push(Template.asString(buf2));
+					afterStartup.push("// run runtime startup");
+					afterStartup.push(`${RuntimeGlobals.startup}();`);
+				} else {
+					startup.push("// startup");
+					startup.push(Template.asString(buf2));
+				}
+			} else if (
+				runtimeRequirements.has(RuntimeGlobals.startup) ||
+				runtimeRequirements.has(RuntimeGlobals.startupOnlyBefore) ||
+				runtimeRequirements.has(RuntimeGlobals.startupOnlyAfter)
+			) {
+				buf.push(
+					"// the startup function",
+					"// It's empty as no entry modules are in this chunk",
+					`${RuntimeGlobals.startup} = ${runtimeTemplate.emptyFunction()};`,
+					""
+				);
+			}
+		} else if (
+			runtimeRequirements.has(RuntimeGlobals.startup) ||
+			runtimeRequirements.has(RuntimeGlobals.startupOnlyBefore) ||
+			runtimeRequirements.has(RuntimeGlobals.startupOnlyAfter)
+		) {
+			result.allowInlineStartup = false;
+			buf.push(
+				"// the startup function",
+				"// It's empty as some runtime module handles the default behavior",
+				`${RuntimeGlobals.startup} = ${runtimeTemplate.emptyFunction()};`
+			);
+			startup.push("// run startup");
+			startup.push(
+				`var ${RuntimeGlobals.exports} = ${RuntimeGlobals.startup}();`
+			);
+		}
+		return result;
+	}
+
+	/**
+	 * @param {RenderBootstrapContext} renderContext options object
+	 * @param {CompilationHooks} hooks hooks
+	 * @returns {string} the generated source of the require function
+	 */
+	renderRequire(renderContext, hooks) {
+		const {
+			chunk,
+			chunkGraph,
+			runtimeTemplate: { outputOptions }
+		} = renderContext;
+		const runtimeRequirements = chunkGraph.getTreeRuntimeRequirements(chunk);
+		const moduleExecution = runtimeRequirements.has(
+			RuntimeGlobals.interceptModuleExecution
+		)
+			? Template.asString([
+					`var execOptions = { id: moduleId, module: module, factory: __webpack_modules__[moduleId], require: ${RuntimeGlobals.require} };`,
+					`${RuntimeGlobals.interceptModuleExecution}.forEach(function(handler) { handler(execOptions); });`,
+					"module = execOptions.module;",
+					"execOptions.factory.call(module.exports, module, module.exports, execOptions.require);"
+				])
+			: runtimeRequirements.has(RuntimeGlobals.thisAsExports)
+				? Template.asString([
+						`__webpack_modules__[moduleId].call(module.exports, module, module.exports, ${RuntimeGlobals.require});`
+					])
+				: Template.asString([
+						`__webpack_modules__[moduleId](module, module.exports, ${RuntimeGlobals.require});`
+					]);
+		const needModuleId = runtimeRequirements.has(RuntimeGlobals.moduleId);
+		const needModuleLoaded = runtimeRequirements.has(
+			RuntimeGlobals.moduleLoaded
+		);
+		const content = Template.asString([
+			"// Check if module is in cache",
+			"var cachedModule = __webpack_module_cache__[moduleId];",
+			"if (cachedModule !== undefined) {",
+			outputOptions.strictModuleErrorHandling
+				? Template.indent([
+						"if (cachedModule.error !== undefined) throw cachedModule.error;",
+						"return cachedModule.exports;"
+					])
+				: Template.indent("return cachedModule.exports;"),
+			"}",
+			"// Create a new module (and put it into the cache)",
+			"var module = __webpack_module_cache__[moduleId] = {",
+			Template.indent([
+				needModuleId ? "id: moduleId," : "// no module.id needed",
+				needModuleLoaded ? "loaded: false," : "// no module.loaded needed",
+				"exports: {}"
+			]),
+			"};",
+			"",
+			outputOptions.strictModuleExceptionHandling
+				? Template.asString([
+						"// Execute the module function",
+						"var threw = true;",
+						"try {",
+						Template.indent([moduleExecution, "threw = false;"]),
+						"} finally {",
+						Template.indent([
+							"if(threw) delete __webpack_module_cache__[moduleId];"
+						]),
+						"}"
+					])
+				: outputOptions.strictModuleErrorHandling
+					? Template.asString([
+							"// Execute the module function",
+							"try {",
+							Template.indent(moduleExecution),
+							"} catch(e) {",
+							Template.indent(["module.error = e;", "throw e;"]),
+							"}"
+						])
+					: Template.asString([
+							"// Execute the module function",
+							moduleExecution
+						]),
+			needModuleLoaded
+				? Template.asString([
+						"",
+						"// Flag the module as loaded",
+						`${RuntimeGlobals.moduleLoaded} = true;`,
+						""
+					])
+				: "",
+			"// Return the exports of the module",
+			"return module.exports;"
+		]);
+		return tryRunOrWebpackError(
+			() => hooks.renderRequire.call(content, renderContext),
+			"JavascriptModulesPlugin.getCompilationHooks().renderRequire"
+		);
+	}
+
+	/**
+	 * @param {Module[]} allModules allModules
+	 * @param {MainRenderContext} renderContext renderContext
+	 * @param {Set<Module>} inlinedModules inlinedModules
+	 * @param {ChunkRenderContext} chunkRenderContext chunkRenderContext
+	 * @param {CompilationHooks} hooks hooks
+	 * @param {boolean | undefined} allStrict allStrict
+	 * @param {boolean} hasChunkModules hasChunkModules
+	 * @returns {Map<Module, Source> | false} renamed inlined modules
+	 */
+	getRenamedInlineModule(
+		allModules,
+		renderContext,
+		inlinedModules,
+		chunkRenderContext,
+		hooks,
+		allStrict,
+		hasChunkModules
+	) {
+		const innerStrict =
+			!allStrict &&
+			allModules.every(m => /** @type {BuildInfo} */ (m.buildInfo).strict);
+		const isMultipleEntries = inlinedModules.size > 1;
+		const singleEntryWithModules = inlinedModules.size === 1 && hasChunkModules;
+
+		// TODO:
+		// This step is before the IIFE reason calculation. Ideally, it should only be executed when this function can optimize the
+		// IIFE reason. Otherwise, it should directly return false. There are four reasons now, we have skipped two already, the left
+		// one is 'it uses a non-standard name for the exports'.
+		if (isMultipleEntries || innerStrict || !singleEntryWithModules) {
+			return false;
+		}
+
+		/** @type {Map<Module, Source>} */
+		const renamedInlinedModules = new Map();
+		const { runtimeTemplate } = renderContext;
+
+		/** @typedef {{ source: Source, module: Module, ast: any, variables: Set<Variable>, through: Set<Reference>, usedInNonInlined: Set<Variable>, moduleScope: Scope }} Info */
+		/** @type {Map<Module, Info>} */
+		const inlinedModulesToInfo = new Map();
+		/** @type {Set<string>} */
+		const nonInlinedModuleThroughIdentifiers = new Set();
+		/** @type {Map<Module, Source>} */
+
+		for (const m of allModules) {
+			const isInlinedModule = inlinedModules && inlinedModules.has(m);
+			const moduleSource = this.renderModule(
+				m,
+				chunkRenderContext,
+				hooks,
+				!isInlinedModule
+			);
+
+			if (!moduleSource) continue;
+			const code = /** @type {string} */ (moduleSource.source());
+			const ast = JavascriptParser._parse(code, {
+				sourceType: "auto"
+			});
+
+			const scopeManager = eslintScope.analyze(ast, {
+				ecmaVersion: 6,
+				sourceType: "module",
+				optimistic: true,
+				ignoreEval: true
+			});
+
+			const globalScope = /** @type {Scope} */ (scopeManager.acquire(ast));
+			if (inlinedModules && inlinedModules.has(m)) {
+				const moduleScope = globalScope.childScopes[0];
+				inlinedModulesToInfo.set(m, {
+					source: moduleSource,
+					ast,
+					module: m,
+					variables: new Set(moduleScope.variables),
+					through: new Set(moduleScope.through),
+					usedInNonInlined: new Set(),
+					moduleScope
+				});
+			} else {
+				for (const ref of globalScope.through) {
+					nonInlinedModuleThroughIdentifiers.add(ref.identifier.name);
+				}
+			}
+		}
+
+		for (const [, { variables, usedInNonInlined }] of inlinedModulesToInfo) {
+			for (const variable of variables) {
+				if (
+					nonInlinedModuleThroughIdentifiers.has(variable.name) ||
+					RESERVED_NAMES.has(variable.name)
+				) {
+					usedInNonInlined.add(variable);
+				}
+			}
+		}
+
+		for (const [m, moduleInfo] of inlinedModulesToInfo) {
+			const { ast, source: _source, usedInNonInlined } = moduleInfo;
+			const source = new ReplaceSource(_source);
+			if (usedInNonInlined.size === 0) {
+				renamedInlinedModules.set(m, source);
+				continue;
+			}
+
+			const info = /** @type {Info} */ (inlinedModulesToInfo.get(m));
+			const allUsedNames = new Set(
+				Array.from(info.through, v => v.identifier.name)
+			);
+
+			for (const variable of usedInNonInlined) {
+				allUsedNames.add(variable.name);
+			}
+
+			for (const variable of info.variables) {
+				allUsedNames.add(variable.name);
+				const references = getAllReferences(variable);
+				const allIdentifiers = new Set(
+					references.map(r => r.identifier).concat(variable.identifiers)
+				);
+
+				const usedNamesInScopeInfo = new Map();
+				const ignoredScopes = new Set();
+
+				const name = variable.name;
+				const { usedNames, alreadyCheckedScopes } = getUsedNamesInScopeInfo(
+					usedNamesInScopeInfo,
+					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 = findNewName(
+						variable.name,
+						allUsedNames,
+						usedNames,
+						m.readableIdentifier(runtimeTemplate.requestShortener)
+					);
+					allUsedNames.add(newName);
+					for (const identifier of allIdentifiers) {
+						const r = /** @type {Range} */ (identifier.range);
+						const path = getPathInAst(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);
+				}
+			}
+
+			renamedInlinedModules.set(m, source);
+		}
+
+		return renamedInlinedModules;
+	}
+}
+
+module.exports = JavascriptModulesPlugin;
+module.exports.chunkHasJs = chunkHasJs;
Index: imaps-frontend/node_modules/webpack/lib/javascript/JavascriptParser.js
===================================================================
--- imaps-frontend/node_modules/webpack/lib/javascript/JavascriptParser.js	(revision 79a03170cdde4e801d12257ee86723774970427b)
+++ imaps-frontend/node_modules/webpack/lib/javascript/JavascriptParser.js	(revision 79a03170cdde4e801d12257ee86723774970427b)
@@ -0,0 +1,5007 @@
+/*
+	MIT License http://www.opensource.org/licenses/mit-license.php
+	Author Tobias Koppers @sokra
+*/
+
+"use strict";
+
+const { Parser: AcornParser, tokTypes } = require("acorn");
+const { SyncBailHook, HookMap } = require("tapable");
+const vm = require("vm");
+const Parser = require("../Parser");
+const StackedMap = require("../util/StackedMap");
+const binarySearchBounds = require("../util/binarySearchBounds");
+const {
+	webpackCommentRegExp,
+	createMagicCommentContext
+} = require("../util/magicComment");
+const memoize = require("../util/memoize");
+const BasicEvaluatedExpression = require("./BasicEvaluatedExpression");
+
+/** @typedef {import("acorn").Options} AcornOptions */
+/** @typedef {import("estree").AssignmentExpression} AssignmentExpression */
+/** @typedef {import("estree").BinaryExpression} BinaryExpression */
+/** @typedef {import("estree").BlockStatement} BlockStatement */
+/** @typedef {import("estree").SequenceExpression} SequenceExpression */
+/** @typedef {import("estree").CallExpression} CallExpression */
+/** @typedef {import("estree").BaseCallExpression} BaseCallExpression */
+/** @typedef {import("estree").StaticBlock} StaticBlock */
+/** @typedef {import("estree").ClassDeclaration} ClassDeclaration */
+/** @typedef {import("estree").ForStatement} ForStatement */
+/** @typedef {import("estree").SwitchStatement} SwitchStatement */
+/** @typedef {import("estree").ClassExpression} ClassExpression */
+/** @typedef {import("estree").Comment} Comment */
+/** @typedef {import("estree").ConditionalExpression} ConditionalExpression */
+/** @typedef {import("estree").Declaration} Declaration */
+/** @typedef {import("estree").PrivateIdentifier} PrivateIdentifier */
+/** @typedef {import("estree").PropertyDefinition} PropertyDefinition */
+/** @typedef {import("estree").Expression} Expression */
+/** @typedef {import("estree").Identifier} Identifier */
+/** @typedef {import("estree").VariableDeclaration} VariableDeclaration */
+/** @typedef {import("estree").IfStatement} IfStatement */
+/** @typedef {import("estree").LabeledStatement} LabeledStatement */
+/** @typedef {import("estree").Literal} Literal */
+/** @typedef {import("estree").LogicalExpression} LogicalExpression */
+/** @typedef {import("estree").ChainExpression} ChainExpression */
+/** @typedef {import("estree").MemberExpression} MemberExpression */
+/** @typedef {import("estree").YieldExpression} YieldExpression */
+/** @typedef {import("estree").MetaProperty} MetaProperty */
+/** @typedef {import("estree").Property} Property */
+/** @typedef {import("estree").AssignmentPattern} AssignmentPattern */
+/** @typedef {import("estree").ChainElement} ChainElement */
+/** @typedef {import("estree").Pattern} Pattern */
+/** @typedef {import("estree").UpdateExpression} UpdateExpression */
+/** @typedef {import("estree").ObjectExpression} ObjectExpression */
+/** @typedef {import("estree").UnaryExpression} UnaryExpression */
+/** @typedef {import("estree").ArrayExpression} ArrayExpression */
+/** @typedef {import("estree").ArrayPattern} ArrayPattern */
+/** @typedef {import("estree").AwaitExpression} AwaitExpression */
+/** @typedef {import("estree").ThisExpression} ThisExpression */
+/** @typedef {import("estree").RestElement} RestElement */
+/** @typedef {import("estree").ObjectPattern} ObjectPattern */
+/** @typedef {import("estree").SwitchCase} SwitchCase */
+/** @typedef {import("estree").CatchClause} CatchClause */
+/** @typedef {import("estree").VariableDeclarator} VariableDeclarator */
+/** @typedef {import("estree").ForInStatement} ForInStatement */
+/** @typedef {import("estree").ForOfStatement} ForOfStatement */
+/** @typedef {import("estree").ReturnStatement} ReturnStatement */
+/** @typedef {import("estree").WithStatement} WithStatement */
+/** @typedef {import("estree").ThrowStatement} ThrowStatement */
+/** @typedef {import("estree").MethodDefinition} MethodDefinition */
+/** @typedef {import("estree").NewExpression} NewExpression */
+/** @typedef {import("estree").SpreadElement} SpreadElement */
+/** @typedef {import("estree").FunctionExpression} FunctionExpression */
+/** @typedef {import("estree").WhileStatement} WhileStatement */
+/** @typedef {import("estree").ArrowFunctionExpression} ArrowFunctionExpression */
+/** @typedef {import("estree").ExpressionStatement} ExpressionStatement */
+/** @typedef {import("estree").FunctionDeclaration} FunctionDeclaration */
+/** @typedef {import("estree").DoWhileStatement} DoWhileStatement */
+/** @typedef {import("estree").TryStatement} TryStatement */
+/** @typedef {import("estree").Node} Node */
+/** @typedef {import("estree").Program} Program */
+/** @typedef {import("estree").Directive} Directive */
+/** @typedef {import("estree").Statement} Statement */
+/** @typedef {import("estree").ExportDefaultDeclaration} ExportDefaultDeclaration */
+/** @typedef {import("estree").Super} Super */
+/** @typedef {import("estree").TaggedTemplateExpression} TaggedTemplateExpression */
+/** @typedef {import("estree").TemplateLiteral} TemplateLiteral */
+/** @typedef {import("estree").AssignmentProperty} AssignmentProperty */
+/**
+ * @template T
+ * @typedef {import("tapable").AsArray<T>} AsArray<T>
+ */
+/** @typedef {import("../Parser").ParserState} ParserState */
+/** @typedef {import("../Parser").PreparsedAst} PreparsedAst */
+/** @typedef {{declaredScope: ScopeInfo, freeName: string | true | undefined, tagInfo: TagInfo | undefined}} VariableInfoInterface */
+/** @typedef {{ name: string | VariableInfo, rootInfo: string | VariableInfo, getMembers: () => string[], getMembersOptionals: () => boolean[], getMemberRanges: () => Range[] }} GetInfoResult */
+/** @typedef {Statement | ModuleDeclaration | Expression} StatementPathItem */
+/** @typedef {function(string): void} OnIdentString */
+/** @typedef {function(string, Identifier): void} OnIdent */
+/** @typedef {StatementPathItem[]} StatementPath */
+
+// TODO remove cast when @types/estree has been updated to import assertions
+/** @typedef {import("estree").BaseNode & { type: "ImportAttribute", key: Identifier | Literal, value: Literal }} ImportAttribute */
+/** @typedef {import("estree").ImportDeclaration & { attributes?: Array<ImportAttribute> }} ImportDeclaration */
+/** @typedef {import("estree").ExportNamedDeclaration & { attributes?: Array<ImportAttribute> }} ExportNamedDeclaration */
+/** @typedef {import("estree").ExportAllDeclaration & { attributes?: Array<ImportAttribute> }} ExportAllDeclaration */
+/** @typedef {import("estree").ImportExpression & { options?: Expression | null }} ImportExpression */
+/** @typedef {ImportDeclaration | ExportNamedDeclaration | ExportDefaultDeclaration | ExportAllDeclaration} ModuleDeclaration */
+
+/** @type {string[]} */
+const EMPTY_ARRAY = [];
+const ALLOWED_MEMBER_TYPES_CALL_EXPRESSION = 0b01;
+const ALLOWED_MEMBER_TYPES_EXPRESSION = 0b10;
+const ALLOWED_MEMBER_TYPES_ALL = 0b11;
+
+const LEGACY_ASSERT_ATTRIBUTES = Symbol("assert");
+
+/**
+ * @param {any} Parser parser
+ * @returns {typeof AcornParser} extender acorn parser
+ */
+const importAssertions = Parser =>
+	/** @type {typeof AcornParser} */ (
+		/** @type {unknown} */ (
+			class extends Parser {
+				parseWithClause() {
+					const nodes = [];
+
+					const isAssertLegacy = this.value === "assert";
+
+					if (isAssertLegacy) {
+						if (!this.eat(tokTypes.name)) {
+							return nodes;
+						}
+					} else if (!this.eat(tokTypes._with)) {
+						return nodes;
+					}
+
+					this.expect(tokTypes.braceL);
+
+					const attributeKeys = {};
+					let first = true;
+
+					while (!this.eat(tokTypes.braceR)) {
+						if (!first) {
+							this.expect(tokTypes.comma);
+							if (this.afterTrailingComma(tokTypes.braceR)) {
+								break;
+							}
+						} else {
+							first = false;
+						}
+
+						const attr = this.parseImportAttribute();
+						const keyName =
+							attr.key.type === "Identifier" ? attr.key.name : attr.key.value;
+
+						if (Object.prototype.hasOwnProperty.call(attributeKeys, keyName)) {
+							this.raiseRecoverable(
+								attr.key.start,
+								`Duplicate attribute key '${keyName}'`
+							);
+						}
+
+						attributeKeys[keyName] = true;
+						nodes.push(attr);
+					}
+
+					if (isAssertLegacy) {
+						nodes[LEGACY_ASSERT_ATTRIBUTES] = true;
+					}
+
+					return nodes;
+				}
+			}
+		)
+	);
+
+// Syntax: https://developer.mozilla.org/en/SpiderMonkey/Parser_API
+const parser = AcornParser.extend(importAssertions);
+
+/** @typedef {Record<string, string> & { _isLegacyAssert?: boolean }} ImportAttributes */
+
+/**
+ * @param {ImportDeclaration | ExportNamedDeclaration | ExportAllDeclaration | ImportExpression} node node with assertions
+ * @returns {ImportAttributes | undefined} import attributes
+ */
+const getImportAttributes = node => {
+	if (node.type === "ImportExpression") {
+		if (
+			node.options &&
+			node.options.type === "ObjectExpression" &&
+			node.options.properties[0] &&
+			node.options.properties[0].type === "Property" &&
+			node.options.properties[0].key.type === "Identifier" &&
+			(node.options.properties[0].key.name === "with" ||
+				node.options.properties[0].key.name === "assert") &&
+			node.options.properties[0].value.type === "ObjectExpression" &&
+			node.options.properties[0].value.properties.length > 0
+		) {
+			const properties =
+				/** @type {Property[]} */
+				(node.options.properties[0].value.properties);
+			const result = /** @type {ImportAttributes} */ ({});
+			for (const property of properties) {
+				const key =
+					/** @type {string} */
+					(
+						property.key.type === "Identifier"
+							? property.key.name
+							: /** @type {Literal} */ (property.key).value
+					);
+				result[key] =
+					/** @type {string} */
+					(/** @type {Literal} */ (property.value).value);
+			}
+			const key =
+				node.options.properties[0].key.type === "Identifier"
+					? node.options.properties[0].key.name
+					: /** @type {Literal} */ (node.options.properties[0].key).value;
+
+			if (key === "assert") {
+				result._isLegacyAssert = true;
+			}
+
+			return result;
+		}
+
+		return;
+	}
+
+	if (node.attributes === undefined || node.attributes.length === 0) {
+		return;
+	}
+
+	const result = /** @type {ImportAttributes} */ ({});
+
+	for (const attribute of node.attributes) {
+		const key =
+			/** @type {string} */
+			(
+				attribute.key.type === "Identifier"
+					? attribute.key.name
+					: attribute.key.value
+			);
+
+		result[key] = /** @type {string} */ (attribute.value.value);
+	}
+
+	if (node.attributes[LEGACY_ASSERT_ATTRIBUTES]) {
+		result._isLegacyAssert = true;
+	}
+
+	return result;
+};
+
+class VariableInfo {
+	/**
+	 * @param {ScopeInfo} declaredScope scope in which the variable is declared
+	 * @param {string | true | undefined} freeName which free name the variable aliases, or true when none
+	 * @param {TagInfo | undefined} tagInfo info about tags
+	 */
+	constructor(declaredScope, freeName, tagInfo) {
+		this.declaredScope = declaredScope;
+		this.freeName = freeName;
+		this.tagInfo = tagInfo;
+	}
+}
+
+/** @typedef {string | ScopeInfo | VariableInfo} ExportedVariableInfo */
+/** @typedef {Literal | string | null | undefined} ImportSource */
+/** @typedef {Omit<AcornOptions, "sourceType" | "ecmaVersion"> & { sourceType: "module" | "script" | "auto", ecmaVersion?: AcornOptions["ecmaVersion"] }} ParseOptions */
+
+/**
+ * @typedef {object} TagInfo
+ * @property {any} tag
+ * @property {any} data
+ * @property {TagInfo | undefined} next
+ */
+
+/**
+ * @typedef {object} ScopeInfo
+ * @property {StackedMap<string, VariableInfo | ScopeInfo>} definitions
+ * @property {boolean | "arrow"} topLevelScope
+ * @property {boolean | string} inShorthand
+ * @property {boolean} inTaggedTemplateTag
+ * @property {boolean} inTry
+ * @property {boolean} isStrict
+ * @property {boolean} isAsmJs
+ */
+
+/** @typedef {[number, number]} Range */
+
+/**
+ * @typedef {object} DestructuringAssignmentProperty
+ * @property {string} id
+ * @property {Range | undefined=} range
+ * @property {boolean | string} shorthand
+ */
+
+/**
+ * Helper function for joining two ranges into a single range. This is useful
+ * when working with AST nodes, as it allows you to combine the ranges of child nodes
+ * to create the range of the _parent node_.
+ * @param {[number, number]} startRange start range to join
+ * @param {[number, number]} endRange end range to join
+ * @returns {[number, number]} joined range
+ * @example
+ * ```js
+ * 	const startRange = [0, 5];
+ * 	const endRange = [10, 15];
+ * 	const joinedRange = joinRanges(startRange, endRange);
+ * 	console.log(joinedRange); // [0, 15]
+ * ```
+ */
+const joinRanges = (startRange, endRange) => {
+	if (!endRange) return startRange;
+	if (!startRange) return endRange;
+	return [startRange[0], endRange[1]];
+};
+
+/**
+ * Helper function used to generate a string representation of a
+ * [member expression](https://github.com/estree/estree/blob/master/es5.md#memberexpression).
+ * @param {string} object object to name
+ * @param {string[]} membersReversed reversed list of members
+ * @returns {string} member expression as a string
+ * @example
+ * ```js
+ * const membersReversed = ["property1", "property2", "property3"]; // Members parsed from the AST
+ * const name = objectAndMembersToName("myObject", membersReversed);
+ *
+ * console.log(name); // "myObject.property1.property2.property3"
+ * ```
+ */
+const objectAndMembersToName = (object, membersReversed) => {
+	let name = object;
+	for (let i = membersReversed.length - 1; i >= 0; i--) {
+		name = `${name}.${membersReversed[i]}`;
+	}
+	return name;
+};
+
+/**
+ * Grabs the name of a given expression and returns it as a string or undefined. Has particular
+ * handling for [Identifiers](https://github.com/estree/estree/blob/master/es5.md#identifier),
+ * [ThisExpressions](https://github.com/estree/estree/blob/master/es5.md#identifier), and
+ * [MetaProperties](https://github.com/estree/estree/blob/master/es2015.md#metaproperty) which is
+ * specifically for handling the `new.target` meta property.
+ * @param {Expression | SpreadElement | Super} expression expression
+ * @returns {string | "this" | undefined} name or variable info
+ */
+const getRootName = expression => {
+	switch (expression.type) {
+		case "Identifier":
+			return expression.name;
+		case "ThisExpression":
+			return "this";
+		case "MetaProperty":
+			return `${expression.meta.name}.${expression.property.name}`;
+		default:
+			return undefined;
+	}
+};
+
+/** @type {AcornOptions} */
+const defaultParserOptions = {
+	ranges: true,
+	locations: true,
+	ecmaVersion: "latest",
+	sourceType: "module",
+	// https://github.com/tc39/proposal-hashbang
+	allowHashBang: true,
+	onComment: undefined
+};
+
+const EMPTY_COMMENT_OPTIONS = {
+	options: null,
+	errors: null
+};
+
+class JavascriptParser extends Parser {
+	/**
+	 * @param {"module" | "script" | "auto"} sourceType default source type
+	 */
+	constructor(sourceType = "auto") {
+		super();
+		this.hooks = Object.freeze({
+			/** @type {HookMap<SyncBailHook<[UnaryExpression], BasicEvaluatedExpression | null | undefined>>} */
+			evaluateTypeof: new HookMap(() => new SyncBailHook(["expression"])),
+			/** @type {HookMap<SyncBailHook<[Expression | SpreadElement | PrivateIdentifier], BasicEvaluatedExpression | null | undefined>>} */
+			evaluate: new HookMap(() => new SyncBailHook(["expression"])),
+			/** @type {HookMap<SyncBailHook<[Identifier | ThisExpression | MemberExpression | MetaProperty], BasicEvaluatedExpression | null | undefined>>} */
+			evaluateIdentifier: new HookMap(() => new SyncBailHook(["expression"])),
+			/** @type {HookMap<SyncBailHook<[Identifier | ThisExpression | MemberExpression], BasicEvaluatedExpression | null | undefined>>} */
+			evaluateDefinedIdentifier: new HookMap(
+				() => new SyncBailHook(["expression"])
+			),
+			/** @type {HookMap<SyncBailHook<[NewExpression], BasicEvaluatedExpression | null | undefined>>} */
+			evaluateNewExpression: new HookMap(
+				() => new SyncBailHook(["expression"])
+			),
+			/** @type {HookMap<SyncBailHook<[CallExpression], BasicEvaluatedExpression | null | undefined>>} */
+			evaluateCallExpression: new HookMap(
+				() => new SyncBailHook(["expression"])
+			),
+			/** @type {HookMap<SyncBailHook<[CallExpression, BasicEvaluatedExpression], BasicEvaluatedExpression | null | undefined>>} */
+			evaluateCallExpressionMember: new HookMap(
+				() => new SyncBailHook(["expression", "param"])
+			),
+			/** @type {HookMap<SyncBailHook<[Expression | Declaration | PrivateIdentifier, number], boolean | void>>} */
+			isPure: new HookMap(
+				() => new SyncBailHook(["expression", "commentsStartPosition"])
+			),
+			/** @type {SyncBailHook<[Statement | ModuleDeclaration], boolean | void>} */
+			preStatement: new SyncBailHook(["statement"]),
+
+			/** @type {SyncBailHook<[Statement | ModuleDeclaration], boolean | void>} */
+			blockPreStatement: new SyncBailHook(["declaration"]),
+			/** @type {SyncBailHook<[Statement | ModuleDeclaration], boolean | void>} */
+			statement: new SyncBailHook(["statement"]),
+			/** @type {SyncBailHook<[IfStatement], boolean | void>} */
+			statementIf: new SyncBailHook(["statement"]),
+			/** @type {SyncBailHook<[Expression, ClassExpression | ClassDeclaration], boolean | void>} */
+			classExtendsExpression: new SyncBailHook([
+				"expression",
+				"classDefinition"
+			]),
+			/** @type {SyncBailHook<[MethodDefinition | PropertyDefinition | StaticBlock, ClassExpression | ClassDeclaration], boolean | void>} */
+			classBodyElement: new SyncBailHook(["element", "classDefinition"]),
+			/** @type {SyncBailHook<[Expression, MethodDefinition | PropertyDefinition, ClassExpression | ClassDeclaration], boolean | void>} */
+			classBodyValue: new SyncBailHook([
+				"expression",
+				"element",
+				"classDefinition"
+			]),
+			/** @type {HookMap<SyncBailHook<[LabeledStatement], boolean | void>>} */
+			label: new HookMap(() => new SyncBailHook(["statement"])),
+			/** @type {SyncBailHook<[ImportDeclaration, ImportSource], boolean | void>} */
+			import: new SyncBailHook(["statement", "source"]),
+			/** @type {SyncBailHook<[ImportDeclaration, ImportSource, string | null, string], boolean | void>} */
+			importSpecifier: new SyncBailHook([
+				"statement",
+				"source",
+				"exportName",
+				"identifierName"
+			]),
+			/** @type {SyncBailHook<[ExportDefaultDeclaration | ExportNamedDeclaration], boolean | void>} */
+			export: new SyncBailHook(["statement"]),
+			/** @type {SyncBailHook<[ExportNamedDeclaration | ExportAllDeclaration, ImportSource], boolean | void>} */
+			exportImport: new SyncBailHook(["statement", "source"]),
+			/** @type {SyncBailHook<[ExportDefaultDeclaration | ExportNamedDeclaration | ExportAllDeclaration, Declaration], boolean | void>} */
+			exportDeclaration: new SyncBailHook(["statement", "declaration"]),
+			/** @type {SyncBailHook<[ExportDefaultDeclaration, FunctionDeclaration | ClassDeclaration], boolean | void>} */
+			exportExpression: new SyncBailHook(["statement", "declaration"]),
+			/** @type {SyncBailHook<[ExportDefaultDeclaration | ExportNamedDeclaration | ExportAllDeclaration, string, string, number | undefined], boolean | void>} */
+			exportSpecifier: new SyncBailHook([
+				"statement",
+				"identifierName",
+				"exportName",
+				"index"
+			]),
+			/** @type {SyncBailHook<[ExportNamedDeclaration | ExportAllDeclaration, ImportSource, string | null, string | null, number | undefined], boolean | void>} */
+			exportImportSpecifier: new SyncBailHook([
+				"statement",
+				"source",
+				"identifierName",
+				"exportName",
+				"index"
+			]),
+			/** @type {SyncBailHook<[VariableDeclarator, Statement], boolean | void>} */
+			preDeclarator: new SyncBailHook(["declarator", "statement"]),
+			/** @type {SyncBailHook<[VariableDeclarator, Statement], boolean | void>} */
+			declarator: new SyncBailHook(["declarator", "statement"]),
+			/** @type {HookMap<SyncBailHook<[Declaration], boolean | void>>} */
+			varDeclaration: new HookMap(() => new SyncBailHook(["declaration"])),
+			/** @type {HookMap<SyncBailHook<[Declaration], boolean | void>>} */
+			varDeclarationLet: new HookMap(() => new SyncBailHook(["declaration"])),
+			/** @type {HookMap<SyncBailHook<[Declaration], boolean | void>>} */
+			varDeclarationConst: new HookMap(() => new SyncBailHook(["declaration"])),
+			/** @type {HookMap<SyncBailHook<[Declaration], boolean | void>>} */
+			varDeclarationVar: new HookMap(() => new SyncBailHook(["declaration"])),
+			/** @type {HookMap<SyncBailHook<[Identifier], boolean | void>>} */
+			pattern: new HookMap(() => new SyncBailHook(["pattern"])),
+			/** @type {HookMap<SyncBailHook<[Expression], boolean | void>>} */
+			canRename: new HookMap(() => new SyncBailHook(["initExpression"])),
+			/** @type {HookMap<SyncBailHook<[Expression], boolean | void>>} */
+			rename: new HookMap(() => new SyncBailHook(["initExpression"])),
+			/** @type {HookMap<SyncBailHook<[AssignmentExpression], boolean | void>>} */
+			assign: new HookMap(() => new SyncBailHook(["expression"])),
+			/** @type {HookMap<SyncBailHook<[AssignmentExpression, string[]], boolean | void>>} */
+			assignMemberChain: new HookMap(
+				() => new SyncBailHook(["expression", "members"])
+			),
+			/** @type {HookMap<SyncBailHook<[Expression], boolean | void>>} */
+			typeof: new HookMap(() => new SyncBailHook(["expression"])),
+			/** @type {SyncBailHook<[ImportExpression], boolean | void>} */
+			importCall: new SyncBailHook(["expression"]),
+			/** @type {SyncBailHook<[Expression | ForOfStatement], boolean | void>} */
+			topLevelAwait: new SyncBailHook(["expression"]),
+			/** @type {HookMap<SyncBailHook<[CallExpression], boolean | void>>} */
+			call: new HookMap(() => new SyncBailHook(["expression"])),
+			/** Something like "a.b()" */
+			/** @type {HookMap<SyncBailHook<[CallExpression, string[], boolean[], Range[]], boolean | void>>} */
+			callMemberChain: new HookMap(
+				() =>
+					new SyncBailHook([
+						"expression",
+						"members",
+						"membersOptionals",
+						"memberRanges"
+					])
+			),
+			/** Something like "a.b().c.d" */
+			/** @type {HookMap<SyncBailHook<[Expression, string[], CallExpression, string[], Range[]], boolean | void>>} */
+			memberChainOfCallMemberChain: new HookMap(
+				() =>
+					new SyncBailHook([
+						"expression",
+						"calleeMembers",
+						"callExpression",
+						"members",
+						"memberRanges"
+					])
+			),
+			/** Something like "a.b().c.d()"" */
+			/** @type {HookMap<SyncBailHook<[CallExpression, string[], CallExpression, string[], Range[]], boolean | void>>} */
+			callMemberChainOfCallMemberChain: new HookMap(
+				() =>
+					new SyncBailHook([
+						"expression",
+						"calleeMembers",
+						"innerCallExpression",
+						"members",
+						"memberRanges"
+					])
+			),
+			/** @type {SyncBailHook<[ChainExpression], boolean | void>} */
+			optionalChaining: new SyncBailHook(["optionalChaining"]),
+			/** @type {HookMap<SyncBailHook<[NewExpression], boolean | void>>} */
+			new: new HookMap(() => new SyncBailHook(["expression"])),
+			/** @type {SyncBailHook<[BinaryExpression], boolean | void>} */
+			binaryExpression: new SyncBailHook(["binaryExpression"]),
+			/** @type {HookMap<SyncBailHook<[Expression], boolean | void>>} */
+			expression: new HookMap(() => new SyncBailHook(["expression"])),
+			/** @type {HookMap<SyncBailHook<[MemberExpression, string[], boolean[], Range[]], boolean | void>>} */
+			expressionMemberChain: new HookMap(
+				() =>
+					new SyncBailHook([
+						"expression",
+						"members",
+						"membersOptionals",
+						"memberRanges"
+					])
+			),
+			/** @type {HookMap<SyncBailHook<[MemberExpression, string[]], boolean | void>>} */
+			unhandledExpressionMemberChain: new HookMap(
+				() => new SyncBailHook(["expression", "members"])
+			),
+			/** @type {SyncBailHook<[ConditionalExpression], boolean | void>} */
+			expressionConditionalOperator: new SyncBailHook(["expression"]),
+			/** @type {SyncBailHook<[LogicalExpression], boolean | void>} */
+			expressionLogicalOperator: new SyncBailHook(["expression"]),
+			/** @type {SyncBailHook<[Program, Comment[]], boolean | void>} */
+			program: new SyncBailHook(["ast", "comments"]),
+			/** @type {SyncBailHook<[Program, Comment[]], boolean | void>} */
+			finish: new SyncBailHook(["ast", "comments"])
+		});
+		this.sourceType = sourceType;
+		/** @type {ScopeInfo} */
+		this.scope = undefined;
+		/** @type {ParserState} */
+		this.state = undefined;
+		/** @type {Comment[] | undefined} */
+		this.comments = undefined;
+		/** @type {Set<number> | undefined} */
+		this.semicolons = undefined;
+		/** @type {StatementPath | undefined} */
+		this.statementPath = undefined;
+		/** @type {Statement | ModuleDeclaration | Expression | undefined} */
+		this.prevStatement = undefined;
+		/** @type {WeakMap<Expression, Set<DestructuringAssignmentProperty>> | undefined} */
+		this.destructuringAssignmentProperties = undefined;
+		this.currentTagData = undefined;
+		this.magicCommentContext = createMagicCommentContext();
+		this._initializeEvaluating();
+	}
+
+	_initializeEvaluating() {
+		this.hooks.evaluate.for("Literal").tap("JavascriptParser", _expr => {
+			const expr = /** @type {Literal} */ (_expr);
+
+			switch (typeof expr.value) {
+				case "number":
+					return new BasicEvaluatedExpression()
+						.setNumber(expr.value)
+						.setRange(/** @type {Range} */ (expr.range));
+				case "bigint":
+					return new BasicEvaluatedExpression()
+						.setBigInt(expr.value)
+						.setRange(/** @type {Range} */ (expr.range));
+				case "string":
+					return new BasicEvaluatedExpression()
+						.setString(expr.value)
+						.setRange(/** @type {Range} */ (expr.range));
+				case "boolean":
+					return new BasicEvaluatedExpression()
+						.setBoolean(expr.value)
+						.setRange(/** @type {Range} */ (expr.range));
+			}
+			if (expr.value === null) {
+				return new BasicEvaluatedExpression()
+					.setNull()
+					.setRange(/** @type {Range} */ (expr.range));
+			}
+			if (expr.value instanceof RegExp) {
+				return new BasicEvaluatedExpression()
+					.setRegExp(expr.value)
+					.setRange(/** @type {Range} */ (expr.range));
+			}
+		});
+		this.hooks.evaluate.for("NewExpression").tap("JavascriptParser", _expr => {
+			const expr = /** @type {NewExpression} */ (_expr);
+			const callee = expr.callee;
+			if (callee.type !== "Identifier") return;
+			if (callee.name !== "RegExp") {
+				return this.callHooksForName(
+					this.hooks.evaluateNewExpression,
+					callee.name,
+					expr
+				);
+			} else if (
+				expr.arguments.length > 2 ||
+				this.getVariableInfo("RegExp") !== "RegExp"
+			)
+				return;
+
+			let regExp;
+			const arg1 = expr.arguments[0];
+
+			if (arg1) {
+				if (arg1.type === "SpreadElement") return;
+
+				const evaluatedRegExp = this.evaluateExpression(arg1);
+
+				if (!evaluatedRegExp) return;
+
+				regExp = evaluatedRegExp.asString();
+
+				if (!regExp) return;
+			} else {
+				return (
+					new BasicEvaluatedExpression()
+						// eslint-disable-next-line prefer-regex-literals
+						.setRegExp(new RegExp(""))
+						.setRange(/** @type {Range} */ (expr.range))
+				);
+			}
+
+			let flags;
+			const arg2 = expr.arguments[1];
+
+			if (arg2) {
+				if (arg2.type === "SpreadElement") return;
+
+				const evaluatedFlags = this.evaluateExpression(arg2);
+
+				if (!evaluatedFlags) return;
+
+				if (!evaluatedFlags.isUndefined()) {
+					flags = evaluatedFlags.asString();
+
+					if (
+						flags === undefined ||
+						!BasicEvaluatedExpression.isValidRegExpFlags(flags)
+					)
+						return;
+				}
+			}
+
+			return new BasicEvaluatedExpression()
+				.setRegExp(flags ? new RegExp(regExp, flags) : new RegExp(regExp))
+				.setRange(/** @type {Range} */ (expr.range));
+		});
+		this.hooks.evaluate
+			.for("LogicalExpression")
+			.tap("JavascriptParser", _expr => {
+				const expr = /** @type {LogicalExpression} */ (_expr);
+
+				const left = this.evaluateExpression(expr.left);
+				let returnRight = false;
+				/** @type {boolean | undefined} */
+				let allowedRight;
+				if (expr.operator === "&&") {
+					const leftAsBool = left.asBool();
+					if (leftAsBool === false)
+						return left.setRange(/** @type {Range} */ (expr.range));
+					returnRight = leftAsBool === true;
+					allowedRight = false;
+				} else if (expr.operator === "||") {
+					const leftAsBool = left.asBool();
+					if (leftAsBool === true)
+						return left.setRange(/** @type {Range} */ (expr.range));
+					returnRight = leftAsBool === false;
+					allowedRight = true;
+				} else if (expr.operator === "??") {
+					const leftAsNullish = left.asNullish();
+					if (leftAsNullish === false)
+						return left.setRange(/** @type {Range} */ (expr.range));
+					if (leftAsNullish !== true) return;
+					returnRight = true;
+				} else return;
+				const right = this.evaluateExpression(expr.right);
+				if (returnRight) {
+					if (left.couldHaveSideEffects()) right.setSideEffects();
+					return right.setRange(/** @type {Range} */ (expr.range));
+				}
+
+				const asBool = right.asBool();
+
+				if (allowedRight === true && asBool === true) {
+					return new BasicEvaluatedExpression()
+						.setRange(/** @type {Range} */ (expr.range))
+						.setTruthy();
+				} else if (allowedRight === false && asBool === false) {
+					return new BasicEvaluatedExpression()
+						.setRange(/** @type {Range} */ (expr.range))
+						.setFalsy();
+				}
+			});
+
+		/**
+		 * In simple logical cases, we can use valueAsExpression to assist us in evaluating the expression on
+		 * either side of a [BinaryExpression](https://github.com/estree/estree/blob/master/es5.md#binaryexpression).
+		 * This supports scenarios in webpack like conditionally `import()`'ing modules based on some simple evaluation:
+		 *
+		 * ```js
+		 * if (1 === 3) {
+		 *  import("./moduleA"); // webpack will auto evaluate this and not import the modules
+		 * }
+		 * ```
+		 *
+		 * Additional scenarios include evaluation of strings inside of dynamic import statements:
+		 *
+		 * ```js
+		 * const foo = "foo";
+		 * const bar = "bar";
+		 *
+		 * import("./" + foo + bar); // webpack will auto evaluate this into import("./foobar")
+		 * ```
+		 * @param {boolean | number | bigint | string} value the value to convert to an expression
+		 * @param {BinaryExpression | UnaryExpression} expr the expression being evaluated
+		 * @param {boolean} sideEffects whether the expression has side effects
+		 * @returns {BasicEvaluatedExpression | undefined} the evaluated expression
+		 * @example
+		 *
+		 * ```js
+		 * const binaryExpr = new BinaryExpression("+",
+		 * 	{ type: "Literal", value: 2 },
+		 * 	{ type: "Literal", value: 3 }
+		 * );
+		 *
+		 * const leftValue = 2;
+		 * const rightValue = 3;
+		 *
+		 * const leftExpr = valueAsExpression(leftValue, binaryExpr.left, false);
+		 * const rightExpr = valueAsExpression(rightValue, binaryExpr.right, false);
+		 * const result = new BasicEvaluatedExpression()
+		 * 	.setNumber(leftExpr.number + rightExpr.number)
+		 * 	.setRange(binaryExpr.range);
+		 *
+		 * console.log(result.number); // Output: 5
+		 * ```
+		 */
+		const valueAsExpression = (value, expr, sideEffects) => {
+			switch (typeof value) {
+				case "boolean":
+					return new BasicEvaluatedExpression()
+						.setBoolean(value)
+						.setSideEffects(sideEffects)
+						.setRange(/** @type {Range} */ (expr.range));
+				case "number":
+					return new BasicEvaluatedExpression()
+						.setNumber(value)
+						.setSideEffects(sideEffects)
+						.setRange(/** @type {Range} */ (expr.range));
+				case "bigint":
+					return new BasicEvaluatedExpression()
+						.setBigInt(value)
+						.setSideEffects(sideEffects)
+						.setRange(/** @type {Range} */ (expr.range));
+				case "string":
+					return new BasicEvaluatedExpression()
+						.setString(value)
+						.setSideEffects(sideEffects)
+						.setRange(/** @type {Range} */ (expr.range));
+			}
+		};
+
+		this.hooks.evaluate
+			.for("BinaryExpression")
+			.tap("JavascriptParser", _expr => {
+				const expr = /** @type {BinaryExpression} */ (_expr);
+
+				/**
+				 * Evaluates a binary expression if and only if it is a const operation (e.g. 1 + 2, "a" + "b", etc.).
+				 * @template T
+				 * @param {(leftOperand: T, rightOperand: T) => boolean | number | bigint | string} operandHandler the handler for the operation (e.g. (a, b) => a + b)
+				 * @returns {BasicEvaluatedExpression | undefined} the evaluated expression
+				 */
+				const handleConstOperation = operandHandler => {
+					const left = this.evaluateExpression(expr.left);
+					if (!left.isCompileTimeValue()) return;
+
+					const right = this.evaluateExpression(expr.right);
+					if (!right.isCompileTimeValue()) return;
+
+					const result = operandHandler(
+						left.asCompileTimeValue(),
+						right.asCompileTimeValue()
+					);
+					return valueAsExpression(
+						result,
+						expr,
+						left.couldHaveSideEffects() || right.couldHaveSideEffects()
+					);
+				};
+
+				/**
+				 * Helper function to determine if two booleans are always different. This is used in `handleStrictEqualityComparison`
+				 * to determine if an expressions boolean or nullish conversion is equal or not.
+				 * @param {boolean} a first boolean to compare
+				 * @param {boolean} b second boolean to compare
+				 * @returns {boolean} true if the two booleans are always different, false otherwise
+				 */
+				const isAlwaysDifferent = (a, b) =>
+					(a === true && b === false) || (a === false && b === true);
+
+				/**
+				 * @param {BasicEvaluatedExpression} left left
+				 * @param {BasicEvaluatedExpression} right right
+				 * @param {BasicEvaluatedExpression} res res
+				 * @param {boolean} eql true for "===" and false for "!=="
+				 * @returns {BasicEvaluatedExpression | undefined} result
+				 */
+				const handleTemplateStringCompare = (left, right, res, eql) => {
+					/**
+					 * @param {BasicEvaluatedExpression[]} parts parts
+					 * @returns {string} value
+					 */
+					const getPrefix = parts => {
+						let value = "";
+						for (const p of parts) {
+							const v = p.asString();
+							if (v !== undefined) value += v;
+							else break;
+						}
+						return value;
+					};
+					/**
+					 * @param {BasicEvaluatedExpression[]} parts parts
+					 * @returns {string} value
+					 */
+					const getSuffix = parts => {
+						let value = "";
+						for (let i = parts.length - 1; i >= 0; i--) {
+							const v = parts[i].asString();
+							if (v !== undefined) value = v + value;
+							else break;
+						}
+						return value;
+					};
+					const leftPrefix = getPrefix(
+						/** @type {BasicEvaluatedExpression[]} */ (left.parts)
+					);
+					const rightPrefix = getPrefix(
+						/** @type {BasicEvaluatedExpression[]} */ (right.parts)
+					);
+					const leftSuffix = getSuffix(
+						/** @type {BasicEvaluatedExpression[]} */ (left.parts)
+					);
+					const rightSuffix = getSuffix(
+						/** @type {BasicEvaluatedExpression[]} */ (right.parts)
+					);
+					const lenPrefix = Math.min(leftPrefix.length, rightPrefix.length);
+					const lenSuffix = Math.min(leftSuffix.length, rightSuffix.length);
+					const prefixMismatch =
+						lenPrefix > 0 &&
+						leftPrefix.slice(0, lenPrefix) !== rightPrefix.slice(0, lenPrefix);
+					const suffixMismatch =
+						lenSuffix > 0 &&
+						leftSuffix.slice(-lenSuffix) !== rightSuffix.slice(-lenSuffix);
+					if (prefixMismatch || suffixMismatch) {
+						return res
+							.setBoolean(!eql)
+							.setSideEffects(
+								left.couldHaveSideEffects() || right.couldHaveSideEffects()
+							);
+					}
+				};
+
+				/**
+				 * Helper function to handle BinaryExpressions using strict equality comparisons (e.g. "===" and "!==").
+				 * @param {boolean} eql true for "===" and false for "!=="
+				 * @returns {BasicEvaluatedExpression | undefined} the evaluated expression
+				 */
+				const handleStrictEqualityComparison = eql => {
+					const left = this.evaluateExpression(expr.left);
+					const right = this.evaluateExpression(expr.right);
+					const res = new BasicEvaluatedExpression();
+					res.setRange(/** @type {Range} */ (expr.range));
+
+					const leftConst = left.isCompileTimeValue();
+					const rightConst = right.isCompileTimeValue();
+
+					if (leftConst && rightConst) {
+						return res
+							.setBoolean(
+								eql ===
+									(left.asCompileTimeValue() === right.asCompileTimeValue())
+							)
+							.setSideEffects(
+								left.couldHaveSideEffects() || right.couldHaveSideEffects()
+							);
+					}
+
+					if (left.isArray() && right.isArray()) {
+						return res
+							.setBoolean(!eql)
+							.setSideEffects(
+								left.couldHaveSideEffects() || right.couldHaveSideEffects()
+							);
+					}
+					if (left.isTemplateString() && right.isTemplateString()) {
+						return handleTemplateStringCompare(left, right, res, eql);
+					}
+
+					const leftPrimitive = left.isPrimitiveType();
+					const rightPrimitive = right.isPrimitiveType();
+
+					if (
+						// Primitive !== Object or
+						// compile-time object types are never equal to something at runtime
+						(leftPrimitive === false &&
+							(leftConst || rightPrimitive === true)) ||
+						(rightPrimitive === false &&
+							(rightConst || leftPrimitive === true)) ||
+						// Different nullish or boolish status also means not equal
+						isAlwaysDifferent(
+							/** @type {boolean} */ (left.asBool()),
+							/** @type {boolean} */ (right.asBool())
+						) ||
+						isAlwaysDifferent(
+							/** @type {boolean} */ (left.asNullish()),
+							/** @type {boolean} */ (right.asNullish())
+						)
+					) {
+						return res
+							.setBoolean(!eql)
+							.setSideEffects(
+								left.couldHaveSideEffects() || right.couldHaveSideEffects()
+							);
+					}
+				};
+
+				/**
+				 * Helper function to handle BinaryExpressions using abstract equality comparisons (e.g. "==" and "!=").
+				 * @param {boolean} eql true for "==" and false for "!="
+				 * @returns {BasicEvaluatedExpression | undefined} the evaluated expression
+				 */
+				const handleAbstractEqualityComparison = eql => {
+					const left = this.evaluateExpression(expr.left);
+					const right = this.evaluateExpression(expr.right);
+					const res = new BasicEvaluatedExpression();
+					res.setRange(/** @type {Range} */ (expr.range));
+
+					const leftConst = left.isCompileTimeValue();
+					const rightConst = right.isCompileTimeValue();
+
+					if (leftConst && rightConst) {
+						return res
+							.setBoolean(
+								eql ===
+									// eslint-disable-next-line eqeqeq
+									(left.asCompileTimeValue() == right.asCompileTimeValue())
+							)
+							.setSideEffects(
+								left.couldHaveSideEffects() || right.couldHaveSideEffects()
+							);
+					}
+
+					if (left.isArray() && right.isArray()) {
+						return res
+							.setBoolean(!eql)
+							.setSideEffects(
+								left.couldHaveSideEffects() || right.couldHaveSideEffects()
+							);
+					}
+					if (left.isTemplateString() && right.isTemplateString()) {
+						return handleTemplateStringCompare(left, right, res, eql);
+					}
+				};
+
+				if (expr.operator === "+") {
+					const left = this.evaluateExpression(expr.left);
+					const right = this.evaluateExpression(expr.right);
+					const res = new BasicEvaluatedExpression();
+					if (left.isString()) {
+						if (right.isString()) {
+							res.setString(
+								/** @type {string} */ (left.string) +
+									/** @type {string} */ (right.string)
+							);
+						} else if (right.isNumber()) {
+							res.setString(/** @type {string} */ (left.string) + right.number);
+						} else if (
+							right.isWrapped() &&
+							right.prefix &&
+							right.prefix.isString()
+						) {
+							// "left" + ("prefix" + inner + "postfix")
+							// => ("leftPrefix" + inner + "postfix")
+							res.setWrapped(
+								new BasicEvaluatedExpression()
+									.setString(
+										/** @type {string} */ (left.string) +
+											/** @type {string} */ (right.prefix.string)
+									)
+									.setRange(
+										joinRanges(
+											/** @type {Range} */ (left.range),
+											/** @type {Range} */ (right.prefix.range)
+										)
+									),
+								right.postfix,
+								right.wrappedInnerExpressions
+							);
+						} else if (right.isWrapped()) {
+							// "left" + ([null] + inner + "postfix")
+							// => ("left" + inner + "postfix")
+							res.setWrapped(
+								left,
+								right.postfix,
+								right.wrappedInnerExpressions
+							);
+						} else {
+							// "left" + expr
+							// => ("left" + expr + "")
+							res.setWrapped(left, null, [right]);
+						}
+					} else if (left.isNumber()) {
+						if (right.isString()) {
+							res.setString(left.number + /** @type {string} */ (right.string));
+						} else if (right.isNumber()) {
+							res.setNumber(
+								/** @type {number} */ (left.number) +
+									/** @type {number} */ (right.number)
+							);
+						} else {
+							return;
+						}
+					} else if (left.isBigInt()) {
+						if (right.isBigInt()) {
+							res.setBigInt(
+								/** @type {bigint} */ (left.bigint) +
+									/** @type {bigint} */ (right.bigint)
+							);
+						}
+					} else if (left.isWrapped()) {
+						if (left.postfix && left.postfix.isString() && right.isString()) {
+							// ("prefix" + inner + "postfix") + "right"
+							// => ("prefix" + inner + "postfixRight")
+							res.setWrapped(
+								left.prefix,
+								new BasicEvaluatedExpression()
+									.setString(
+										/** @type {string} */ (left.postfix.string) +
+											/** @type {string} */ (right.string)
+									)
+									.setRange(
+										joinRanges(
+											/** @type {Range} */ (left.postfix.range),
+											/** @type {Range} */ (right.range)
+										)
+									),
+								left.wrappedInnerExpressions
+							);
+						} else if (
+							left.postfix &&
+							left.postfix.isString() &&
+							right.isNumber()
+						) {
+							// ("prefix" + inner + "postfix") + 123
+							// => ("prefix" + inner + "postfix123")
+							res.setWrapped(
+								left.prefix,
+								new BasicEvaluatedExpression()
+									.setString(
+										/** @type {string} */ (left.postfix.string) +
+											/** @type {number} */ (right.number)
+									)
+									.setRange(
+										joinRanges(
+											/** @type {Range} */ (left.postfix.range),
+											/** @type {Range} */ (right.range)
+										)
+									),
+								left.wrappedInnerExpressions
+							);
+						} else if (right.isString()) {
+							// ("prefix" + inner + [null]) + "right"
+							// => ("prefix" + inner + "right")
+							res.setWrapped(left.prefix, right, left.wrappedInnerExpressions);
+						} else if (right.isNumber()) {
+							// ("prefix" + inner + [null]) + 123
+							// => ("prefix" + inner + "123")
+							res.setWrapped(
+								left.prefix,
+								new BasicEvaluatedExpression()
+									.setString(String(right.number))
+									.setRange(/** @type {Range} */ (right.range)),
+								left.wrappedInnerExpressions
+							);
+						} else if (right.isWrapped()) {
+							// ("prefix1" + inner1 + "postfix1") + ("prefix2" + inner2 + "postfix2")
+							// ("prefix1" + inner1 + "postfix1" + "prefix2" + inner2 + "postfix2")
+							res.setWrapped(
+								left.prefix,
+								right.postfix,
+								left.wrappedInnerExpressions &&
+									right.wrappedInnerExpressions &&
+									left.wrappedInnerExpressions
+										.concat(left.postfix ? [left.postfix] : [])
+										.concat(right.prefix ? [right.prefix] : [])
+										.concat(right.wrappedInnerExpressions)
+							);
+						} else {
+							// ("prefix" + inner + postfix) + expr
+							// => ("prefix" + inner + postfix + expr + [null])
+							res.setWrapped(
+								left.prefix,
+								null,
+								left.wrappedInnerExpressions &&
+									left.wrappedInnerExpressions.concat(
+										left.postfix ? [left.postfix, right] : [right]
+									)
+							);
+						}
+					} else if (right.isString()) {
+						// left + "right"
+						// => ([null] + left + "right")
+						res.setWrapped(null, right, [left]);
+					} else if (right.isWrapped()) {
+						// left + (prefix + inner + "postfix")
+						// => ([null] + left + prefix + inner + "postfix")
+						res.setWrapped(
+							null,
+							right.postfix,
+							right.wrappedInnerExpressions &&
+								(right.prefix ? [left, right.prefix] : [left]).concat(
+									right.wrappedInnerExpressions
+								)
+						);
+					} else {
+						return;
+					}
+					if (left.couldHaveSideEffects() || right.couldHaveSideEffects())
+						res.setSideEffects();
+					res.setRange(/** @type {Range} */ (expr.range));
+					return res;
+				} else if (expr.operator === "-") {
+					return handleConstOperation((l, r) => l - r);
+				} else if (expr.operator === "*") {
+					return handleConstOperation((l, r) => l * r);
+				} else if (expr.operator === "/") {
+					return handleConstOperation((l, r) => l / r);
+				} else if (expr.operator === "**") {
+					return handleConstOperation((l, r) => l ** r);
+				} else if (expr.operator === "===") {
+					return handleStrictEqualityComparison(true);
+				} else if (expr.operator === "==") {
+					return handleAbstractEqualityComparison(true);
+				} else if (expr.operator === "!==") {
+					return handleStrictEqualityComparison(false);
+				} else if (expr.operator === "!=") {
+					return handleAbstractEqualityComparison(false);
+				} else if (expr.operator === "&") {
+					return handleConstOperation((l, r) => l & r);
+				} else if (expr.operator === "|") {
+					return handleConstOperation((l, r) => l | r);
+				} else if (expr.operator === "^") {
+					return handleConstOperation((l, r) => l ^ r);
+				} else if (expr.operator === ">>>") {
+					return handleConstOperation((l, r) => l >>> r);
+				} else if (expr.operator === ">>") {
+					return handleConstOperation((l, r) => l >> r);
+				} else if (expr.operator === "<<") {
+					return handleConstOperation((l, r) => l << r);
+				} else if (expr.operator === "<") {
+					return handleConstOperation((l, r) => l < r);
+				} else if (expr.operator === ">") {
+					return handleConstOperation((l, r) => l > r);
+				} else if (expr.operator === "<=") {
+					return handleConstOperation((l, r) => l <= r);
+				} else if (expr.operator === ">=") {
+					return handleConstOperation((l, r) => l >= r);
+				}
+			});
+		this.hooks.evaluate
+			.for("UnaryExpression")
+			.tap("JavascriptParser", _expr => {
+				const expr = /** @type {UnaryExpression} */ (_expr);
+
+				/**
+				 * Evaluates a UnaryExpression if and only if it is a basic const operator (e.g. +a, -a, ~a).
+				 * @template T
+				 * @param {(operand: T) => boolean | number | bigint | string} operandHandler handler for the operand
+				 * @returns {BasicEvaluatedExpression | undefined} evaluated expression
+				 */
+				const handleConstOperation = operandHandler => {
+					const argument = this.evaluateExpression(expr.argument);
+					if (!argument.isCompileTimeValue()) return;
+					const result = operandHandler(argument.asCompileTimeValue());
+					return valueAsExpression(
+						result,
+						expr,
+						argument.couldHaveSideEffects()
+					);
+				};
+
+				if (expr.operator === "typeof") {
+					switch (expr.argument.type) {
+						case "Identifier": {
+							const res = this.callHooksForName(
+								this.hooks.evaluateTypeof,
+								expr.argument.name,
+								expr
+							);
+							if (res !== undefined) return res;
+							break;
+						}
+						case "MetaProperty": {
+							const res = this.callHooksForName(
+								this.hooks.evaluateTypeof,
+								/** @type {string} */ (getRootName(expr.argument)),
+								expr
+							);
+							if (res !== undefined) return res;
+							break;
+						}
+						case "MemberExpression": {
+							const res = this.callHooksForExpression(
+								this.hooks.evaluateTypeof,
+								expr.argument,
+								expr
+							);
+							if (res !== undefined) return res;
+							break;
+						}
+						case "ChainExpression": {
+							const res = this.callHooksForExpression(
+								this.hooks.evaluateTypeof,
+								expr.argument.expression,
+								expr
+							);
+							if (res !== undefined) return res;
+							break;
+						}
+						case "FunctionExpression": {
+							return new BasicEvaluatedExpression()
+								.setString("function")
+								.setRange(/** @type {Range} */ (expr.range));
+						}
+					}
+					const arg = this.evaluateExpression(expr.argument);
+					if (arg.isUnknown()) return;
+					if (arg.isString()) {
+						return new BasicEvaluatedExpression()
+							.setString("string")
+							.setRange(/** @type {Range} */ (expr.range));
+					}
+					if (arg.isWrapped()) {
+						return new BasicEvaluatedExpression()
+							.setString("string")
+							.setSideEffects()
+							.setRange(/** @type {Range} */ (expr.range));
+					}
+					if (arg.isUndefined()) {
+						return new BasicEvaluatedExpression()
+							.setString("undefined")
+							.setRange(/** @type {Range} */ (expr.range));
+					}
+					if (arg.isNumber()) {
+						return new BasicEvaluatedExpression()
+							.setString("number")
+							.setRange(/** @type {Range} */ (expr.range));
+					}
+					if (arg.isBigInt()) {
+						return new BasicEvaluatedExpression()
+							.setString("bigint")
+							.setRange(/** @type {Range} */ (expr.range));
+					}
+					if (arg.isBoolean()) {
+						return new BasicEvaluatedExpression()
+							.setString("boolean")
+							.setRange(/** @type {Range} */ (expr.range));
+					}
+					if (arg.isConstArray() || arg.isRegExp() || arg.isNull()) {
+						return new BasicEvaluatedExpression()
+							.setString("object")
+							.setRange(/** @type {Range} */ (expr.range));
+					}
+					if (arg.isArray()) {
+						return new BasicEvaluatedExpression()
+							.setString("object")
+							.setSideEffects(arg.couldHaveSideEffects())
+							.setRange(/** @type {Range} */ (expr.range));
+					}
+				} else if (expr.operator === "!") {
+					const argument = this.evaluateExpression(expr.argument);
+					const bool = argument.asBool();
+					if (typeof bool !== "boolean") return;
+					return new BasicEvaluatedExpression()
+						.setBoolean(!bool)
+						.setSideEffects(argument.couldHaveSideEffects())
+						.setRange(/** @type {Range} */ (expr.range));
+				} else if (expr.operator === "~") {
+					return handleConstOperation(v => ~v);
+				} else if (expr.operator === "+") {
+					// eslint-disable-next-line no-implicit-coercion
+					return handleConstOperation(v => +v);
+				} else if (expr.operator === "-") {
+					return handleConstOperation(v => -v);
+				}
+			});
+		this.hooks.evaluateTypeof
+			.for("undefined")
+			.tap("JavascriptParser", expr =>
+				new BasicEvaluatedExpression()
+					.setString("undefined")
+					.setRange(/** @type {Range} */ (expr.range))
+			);
+		this.hooks.evaluate.for("Identifier").tap("JavascriptParser", expr => {
+			if (/** @type {Identifier} */ (expr).name === "undefined") {
+				return new BasicEvaluatedExpression()
+					.setUndefined()
+					.setRange(/** @type {Range} */ (expr.range));
+			}
+		});
+		/**
+		 * @param {"Identifier" | "ThisExpression" | "MemberExpression"} exprType expression type name
+		 * @param {function(Expression | SpreadElement): GetInfoResult | undefined} getInfo get info
+		 * @returns {void}
+		 */
+		const tapEvaluateWithVariableInfo = (exprType, getInfo) => {
+			/** @type {Expression | undefined} */
+			let cachedExpression;
+			/** @type {GetInfoResult | undefined} */
+			let cachedInfo;
+			this.hooks.evaluate.for(exprType).tap("JavascriptParser", expr => {
+				const expression =
+					/** @type {Identifier | ThisExpression | MemberExpression} */ (expr);
+
+				const info = getInfo(expression);
+				if (info !== undefined) {
+					return this.callHooksForInfoWithFallback(
+						this.hooks.evaluateIdentifier,
+						info.name,
+						name => {
+							cachedExpression = expression;
+							cachedInfo = info;
+						},
+						name => {
+							const hook = this.hooks.evaluateDefinedIdentifier.get(name);
+							if (hook !== undefined) {
+								return hook.call(expression);
+							}
+						},
+						expression
+					);
+				}
+			});
+			this.hooks.evaluate
+				.for(exprType)
+				.tap({ name: "JavascriptParser", stage: 100 }, expr => {
+					const expression =
+						/** @type {Identifier | ThisExpression | MemberExpression} */
+						(expr);
+					const info =
+						cachedExpression === expression ? cachedInfo : getInfo(expression);
+					if (info !== undefined) {
+						return new BasicEvaluatedExpression()
+							.setIdentifier(
+								info.name,
+								info.rootInfo,
+								info.getMembers,
+								info.getMembersOptionals,
+								info.getMemberRanges
+							)
+							.setRange(/** @type {Range} */ (expression.range));
+					}
+				});
+			this.hooks.finish.tap("JavascriptParser", () => {
+				// Cleanup for GC
+				cachedExpression = cachedInfo = undefined;
+			});
+		};
+		tapEvaluateWithVariableInfo("Identifier", expr => {
+			const info = this.getVariableInfo(/** @type {Identifier} */ (expr).name);
+			if (
+				typeof info === "string" ||
+				(info instanceof VariableInfo && typeof info.freeName === "string")
+			) {
+				return {
+					name: info,
+					rootInfo: info,
+					getMembers: () => [],
+					getMembersOptionals: () => [],
+					getMemberRanges: () => []
+				};
+			}
+		});
+		tapEvaluateWithVariableInfo("ThisExpression", expr => {
+			const info = this.getVariableInfo("this");
+			if (
+				typeof info === "string" ||
+				(info instanceof VariableInfo && typeof info.freeName === "string")
+			) {
+				return {
+					name: info,
+					rootInfo: info,
+					getMembers: () => [],
+					getMembersOptionals: () => [],
+					getMemberRanges: () => []
+				};
+			}
+		});
+		this.hooks.evaluate.for("MetaProperty").tap("JavascriptParser", expr => {
+			const metaProperty = /** @type {MetaProperty} */ (expr);
+
+			return this.callHooksForName(
+				this.hooks.evaluateIdentifier,
+				/** @type {string} */ (getRootName(metaProperty)),
+				metaProperty
+			);
+		});
+		tapEvaluateWithVariableInfo("MemberExpression", expr =>
+			this.getMemberExpressionInfo(
+				/** @type {MemberExpression} */ (expr),
+				ALLOWED_MEMBER_TYPES_EXPRESSION
+			)
+		);
+
+		this.hooks.evaluate.for("CallExpression").tap("JavascriptParser", _expr => {
+			const expr = /** @type {CallExpression} */ (_expr);
+			if (
+				expr.callee.type === "MemberExpression" &&
+				expr.callee.property.type ===
+					(expr.callee.computed ? "Literal" : "Identifier")
+			) {
+				// type Super also possible here
+				const param = this.evaluateExpression(
+					/** @type {Expression} */ (expr.callee.object)
+				);
+				const property =
+					expr.callee.property.type === "Literal"
+						? `${expr.callee.property.value}`
+						: expr.callee.property.name;
+				const hook = this.hooks.evaluateCallExpressionMember.get(property);
+				if (hook !== undefined) {
+					return hook.call(expr, param);
+				}
+			} else if (expr.callee.type === "Identifier") {
+				return this.callHooksForName(
+					this.hooks.evaluateCallExpression,
+					expr.callee.name,
+					expr
+				);
+			}
+		});
+		this.hooks.evaluateCallExpressionMember
+			.for("indexOf")
+			.tap("JavascriptParser", (expr, param) => {
+				if (!param.isString()) return;
+				if (expr.arguments.length === 0) return;
+				const [arg1, arg2] = expr.arguments;
+				if (arg1.type === "SpreadElement") return;
+				const arg1Eval = this.evaluateExpression(arg1);
+				if (!arg1Eval.isString()) return;
+				const arg1Value = /** @type {string} */ (arg1Eval.string);
+
+				let result;
+				if (arg2) {
+					if (arg2.type === "SpreadElement") return;
+					const arg2Eval = this.evaluateExpression(arg2);
+					if (!arg2Eval.isNumber()) return;
+					result = /** @type {string} */ (param.string).indexOf(
+						arg1Value,
+						arg2Eval.number
+					);
+				} else {
+					result = /** @type {string} */ (param.string).indexOf(arg1Value);
+				}
+				return new BasicEvaluatedExpression()
+					.setNumber(result)
+					.setSideEffects(param.couldHaveSideEffects())
+					.setRange(/** @type {Range} */ (expr.range));
+			});
+		this.hooks.evaluateCallExpressionMember
+			.for("replace")
+			.tap("JavascriptParser", (expr, param) => {
+				if (!param.isString()) return;
+				if (expr.arguments.length !== 2) return;
+				if (expr.arguments[0].type === "SpreadElement") return;
+				if (expr.arguments[1].type === "SpreadElement") return;
+				const arg1 = this.evaluateExpression(expr.arguments[0]);
+				const arg2 = this.evaluateExpression(expr.arguments[1]);
+				if (!arg1.isString() && !arg1.isRegExp()) return;
+				const arg1Value = /** @type {string | RegExp} */ (
+					arg1.regExp || arg1.string
+				);
+				if (!arg2.isString()) return;
+				const arg2Value = /** @type {string} */ (arg2.string);
+				return new BasicEvaluatedExpression()
+					.setString(
+						/** @type {string} */ (param.string).replace(arg1Value, arg2Value)
+					)
+					.setSideEffects(param.couldHaveSideEffects())
+					.setRange(/** @type {Range} */ (expr.range));
+			});
+		for (const fn of ["substr", "substring", "slice"]) {
+			this.hooks.evaluateCallExpressionMember
+				.for(fn)
+				.tap("JavascriptParser", (expr, param) => {
+					if (!param.isString()) return;
+					let arg1;
+					let result;
+					const str = /** @type {string} */ (param.string);
+					switch (expr.arguments.length) {
+						case 1:
+							if (expr.arguments[0].type === "SpreadElement") return;
+							arg1 = this.evaluateExpression(expr.arguments[0]);
+							if (!arg1.isNumber()) return;
+							result = str[
+								/** @type {"substr" | "substring" | "slice"} */ (fn)
+							](/** @type {number} */ (arg1.number));
+							break;
+						case 2: {
+							if (expr.arguments[0].type === "SpreadElement") return;
+							if (expr.arguments[1].type === "SpreadElement") return;
+							arg1 = this.evaluateExpression(expr.arguments[0]);
+							const arg2 = this.evaluateExpression(expr.arguments[1]);
+							if (!arg1.isNumber()) return;
+							if (!arg2.isNumber()) return;
+							result = str[
+								/** @type {"substr" | "substring" | "slice"} */ (fn)
+							](
+								/** @type {number} */ (arg1.number),
+								/** @type {number} */ (arg2.number)
+							);
+							break;
+						}
+						default:
+							return;
+					}
+					return new BasicEvaluatedExpression()
+						.setString(result)
+						.setSideEffects(param.couldHaveSideEffects())
+						.setRange(/** @type {Range} */ (expr.range));
+				});
+		}
+
+		/**
+		 * @param {"cooked" | "raw"} kind kind of values to get
+		 * @param {TemplateLiteral} templateLiteralExpr TemplateLiteral expr
+		 * @returns {{quasis: BasicEvaluatedExpression[], parts: BasicEvaluatedExpression[]}} Simplified template
+		 */
+		const getSimplifiedTemplateResult = (kind, templateLiteralExpr) => {
+			/** @type {BasicEvaluatedExpression[]} */
+			const quasis = [];
+			/** @type {BasicEvaluatedExpression[]} */
+			const parts = [];
+
+			for (let i = 0; i < templateLiteralExpr.quasis.length; i++) {
+				const quasiExpr = templateLiteralExpr.quasis[i];
+				const quasi = quasiExpr.value[kind];
+
+				if (i > 0) {
+					const prevExpr = parts[parts.length - 1];
+					const expr = this.evaluateExpression(
+						templateLiteralExpr.expressions[i - 1]
+					);
+					const exprAsString = expr.asString();
+					if (
+						typeof exprAsString === "string" &&
+						!expr.couldHaveSideEffects()
+					) {
+						// We can merge quasi + expr + quasi when expr
+						// is a const string
+
+						prevExpr.setString(prevExpr.string + exprAsString + quasi);
+						prevExpr.setRange([
+							/** @type {Range} */ (prevExpr.range)[0],
+							/** @type {Range} */ (quasiExpr.range)[1]
+						]);
+						// We unset the expression as it doesn't match to a single expression
+						prevExpr.setExpression(undefined);
+						continue;
+					}
+					parts.push(expr);
+				}
+
+				const part = new BasicEvaluatedExpression()
+					.setString(/** @type {string} */ (quasi))
+					.setRange(/** @type {Range} */ (quasiExpr.range))
+					.setExpression(quasiExpr);
+				quasis.push(part);
+				parts.push(part);
+			}
+			return {
+				quasis,
+				parts
+			};
+		};
+
+		this.hooks.evaluate
+			.for("TemplateLiteral")
+			.tap("JavascriptParser", _node => {
+				const node = /** @type {TemplateLiteral} */ (_node);
+
+				const { quasis, parts } = getSimplifiedTemplateResult("cooked", node);
+				if (parts.length === 1) {
+					return parts[0].setRange(/** @type {Range} */ (node.range));
+				}
+				return new BasicEvaluatedExpression()
+					.setTemplateString(quasis, parts, "cooked")
+					.setRange(/** @type {Range} */ (node.range));
+			});
+		this.hooks.evaluate
+			.for("TaggedTemplateExpression")
+			.tap("JavascriptParser", _node => {
+				const node = /** @type {TaggedTemplateExpression} */ (_node);
+				const tag = this.evaluateExpression(node.tag);
+
+				if (tag.isIdentifier() && tag.identifier === "String.raw") {
+					const { quasis, parts } = getSimplifiedTemplateResult(
+						"raw",
+						node.quasi
+					);
+					return new BasicEvaluatedExpression()
+						.setTemplateString(quasis, parts, "raw")
+						.setRange(/** @type {Range} */ (node.range));
+				}
+			});
+
+		this.hooks.evaluateCallExpressionMember
+			.for("concat")
+			.tap("JavascriptParser", (expr, param) => {
+				if (!param.isString() && !param.isWrapped()) return;
+				let stringSuffix = null;
+				let hasUnknownParams = false;
+				const innerExpressions = [];
+				for (let i = expr.arguments.length - 1; i >= 0; i--) {
+					const arg = expr.arguments[i];
+					if (arg.type === "SpreadElement") return;
+					const argExpr = this.evaluateExpression(arg);
+					if (
+						hasUnknownParams ||
+						(!argExpr.isString() && !argExpr.isNumber())
+					) {
+						hasUnknownParams = true;
+						innerExpressions.push(argExpr);
+						continue;
+					}
+
+					/** @type {string} */
+					const value = argExpr.isString()
+						? /** @type {string} */ (argExpr.string)
+						: String(/** @type {number} */ (argExpr.number));
+
+					/** @type {string} */
+					const newString = value + (stringSuffix ? stringSuffix.string : "");
+					const newRange = /** @type {Range} */ ([
+						/** @type {Range} */ (argExpr.range)[0],
+						/** @type {Range} */ ((stringSuffix || argExpr).range)[1]
+					]);
+					stringSuffix = new BasicEvaluatedExpression()
+						.setString(newString)
+						.setSideEffects(
+							(stringSuffix && stringSuffix.couldHaveSideEffects()) ||
+								argExpr.couldHaveSideEffects()
+						)
+						.setRange(newRange);
+				}
+
+				if (hasUnknownParams) {
+					const prefix = param.isString() ? param : param.prefix;
+					const inner =
+						param.isWrapped() && param.wrappedInnerExpressions
+							? param.wrappedInnerExpressions.concat(innerExpressions.reverse())
+							: innerExpressions.reverse();
+					return new BasicEvaluatedExpression()
+						.setWrapped(prefix, stringSuffix, inner)
+						.setRange(/** @type {Range} */ (expr.range));
+				} else if (param.isWrapped()) {
+					const postfix = stringSuffix || param.postfix;
+					const inner = param.wrappedInnerExpressions
+						? param.wrappedInnerExpressions.concat(innerExpressions.reverse())
+						: innerExpressions.reverse();
+					return new BasicEvaluatedExpression()
+						.setWrapped(param.prefix, postfix, inner)
+						.setRange(/** @type {Range} */ (expr.range));
+				}
+				const newString =
+					/** @type {string} */ (param.string) +
+					(stringSuffix ? stringSuffix.string : "");
+				return new BasicEvaluatedExpression()
+					.setString(newString)
+					.setSideEffects(
+						(stringSuffix && stringSuffix.couldHaveSideEffects()) ||
+							param.couldHaveSideEffects()
+					)
+					.setRange(/** @type {Range} */ (expr.range));
+			});
+		this.hooks.evaluateCallExpressionMember
+			.for("split")
+			.tap("JavascriptParser", (expr, param) => {
+				if (!param.isString()) return;
+				if (expr.arguments.length !== 1) return;
+				if (expr.arguments[0].type === "SpreadElement") return;
+				let result;
+				const arg = this.evaluateExpression(expr.arguments[0]);
+				if (arg.isString()) {
+					result =
+						/** @type {string} */
+						(param.string).split(/** @type {string} */ (arg.string));
+				} else if (arg.isRegExp()) {
+					result = /** @type {string} */ (param.string).split(
+						/** @type {RegExp} */ (arg.regExp)
+					);
+				} else {
+					return;
+				}
+				return new BasicEvaluatedExpression()
+					.setArray(result)
+					.setSideEffects(param.couldHaveSideEffects())
+					.setRange(/** @type {Range} */ (expr.range));
+			});
+		this.hooks.evaluate
+			.for("ConditionalExpression")
+			.tap("JavascriptParser", _expr => {
+				const expr = /** @type {ConditionalExpression} */ (_expr);
+
+				const condition = this.evaluateExpression(expr.test);
+				const conditionValue = condition.asBool();
+				let res;
+				if (conditionValue === undefined) {
+					const consequent = this.evaluateExpression(expr.consequent);
+					const alternate = this.evaluateExpression(expr.alternate);
+					res = new BasicEvaluatedExpression();
+					if (consequent.isConditional()) {
+						res.setOptions(
+							/** @type {BasicEvaluatedExpression[]} */ (consequent.options)
+						);
+					} else {
+						res.setOptions([consequent]);
+					}
+					if (alternate.isConditional()) {
+						res.addOptions(
+							/** @type {BasicEvaluatedExpression[]} */ (alternate.options)
+						);
+					} else {
+						res.addOptions([alternate]);
+					}
+				} else {
+					res = this.evaluateExpression(
+						conditionValue ? expr.consequent : expr.alternate
+					);
+					if (condition.couldHaveSideEffects()) res.setSideEffects();
+				}
+				res.setRange(/** @type {Range} */ (expr.range));
+				return res;
+			});
+		this.hooks.evaluate
+			.for("ArrayExpression")
+			.tap("JavascriptParser", _expr => {
+				const expr = /** @type {ArrayExpression} */ (_expr);
+
+				const items = expr.elements.map(
+					element =>
+						element !== null &&
+						element.type !== "SpreadElement" &&
+						this.evaluateExpression(element)
+				);
+				if (!items.every(Boolean)) return;
+				return new BasicEvaluatedExpression()
+					.setItems(/** @type {BasicEvaluatedExpression[]} */ (items))
+					.setRange(/** @type {Range} */ (expr.range));
+			});
+		this.hooks.evaluate
+			.for("ChainExpression")
+			.tap("JavascriptParser", _expr => {
+				const expr = /** @type {ChainExpression} */ (_expr);
+				/** @type {Expression[]} */
+				const optionalExpressionsStack = [];
+				/** @type {Expression|Super} */
+				let next = expr.expression;
+
+				while (
+					next.type === "MemberExpression" ||
+					next.type === "CallExpression"
+				) {
+					if (next.type === "MemberExpression") {
+						if (next.optional) {
+							// SuperNode can not be optional
+							optionalExpressionsStack.push(
+								/** @type {Expression} */ (next.object)
+							);
+						}
+						next = next.object;
+					} else {
+						if (next.optional) {
+							// SuperNode can not be optional
+							optionalExpressionsStack.push(
+								/** @type {Expression} */ (next.callee)
+							);
+						}
+						next = next.callee;
+					}
+				}
+
+				while (optionalExpressionsStack.length > 0) {
+					const expression =
+						/** @type {Expression} */
+						(optionalExpressionsStack.pop());
+					const evaluated = this.evaluateExpression(expression);
+
+					if (evaluated.asNullish()) {
+						return evaluated.setRange(/** @type {Range} */ (_expr.range));
+					}
+				}
+				return this.evaluateExpression(expr.expression);
+			});
+	}
+
+	/**
+	 * @param {Expression} node node
+	 * @returns {Set<DestructuringAssignmentProperty> | undefined} destructured identifiers
+	 */
+	destructuringAssignmentPropertiesFor(node) {
+		if (!this.destructuringAssignmentProperties) return;
+		return this.destructuringAssignmentProperties.get(node);
+	}
+
+	/**
+	 * @param {Expression | SpreadElement} expr expression
+	 * @returns {string | VariableInfoInterface | undefined} identifier
+	 */
+	getRenameIdentifier(expr) {
+		const result = this.evaluateExpression(expr);
+		if (result.isIdentifier()) {
+			return result.identifier;
+		}
+	}
+
+	/**
+	 * @param {ClassExpression | ClassDeclaration} classy a class node
+	 * @returns {void}
+	 */
+	walkClass(classy) {
+		if (
+			classy.superClass &&
+			!this.hooks.classExtendsExpression.call(classy.superClass, classy)
+		) {
+			this.walkExpression(classy.superClass);
+		}
+		if (classy.body && classy.body.type === "ClassBody") {
+			const scopeParams = [];
+			// Add class name in scope for recursive calls
+			if (classy.id) {
+				scopeParams.push(classy.id);
+			}
+			this.inClassScope(true, scopeParams, () => {
+				for (const classElement of /** @type {TODO} */ (classy.body.body)) {
+					if (!this.hooks.classBodyElement.call(classElement, classy)) {
+						if (classElement.computed && classElement.key) {
+							this.walkExpression(classElement.key);
+						}
+						if (classElement.value) {
+							if (
+								!this.hooks.classBodyValue.call(
+									classElement.value,
+									classElement,
+									classy
+								)
+							) {
+								const wasTopLevel = this.scope.topLevelScope;
+								this.scope.topLevelScope = false;
+								this.walkExpression(classElement.value);
+								this.scope.topLevelScope = wasTopLevel;
+							}
+						} else if (classElement.type === "StaticBlock") {
+							const wasTopLevel = this.scope.topLevelScope;
+							this.scope.topLevelScope = false;
+							this.walkBlockStatement(classElement);
+							this.scope.topLevelScope = wasTopLevel;
+						}
+					}
+				}
+			});
+		}
+	}
+
+	/**
+	 * Pre walking iterates the scope for variable declarations
+	 * @param {(Statement | ModuleDeclaration)[]} statements statements
+	 */
+	preWalkStatements(statements) {
+		for (let index = 0, len = statements.length; index < len; index++) {
+			const statement = statements[index];
+			this.preWalkStatement(statement);
+		}
+	}
+
+	/**
+	 * Block pre walking iterates the scope for block variable declarations
+	 * @param {(Statement | ModuleDeclaration)[]} statements statements
+	 */
+	blockPreWalkStatements(statements) {
+		for (let index = 0, len = statements.length; index < len; index++) {
+			const statement = statements[index];
+			this.blockPreWalkStatement(statement);
+		}
+	}
+
+	/**
+	 * Walking iterates the statements and expressions and processes them
+	 * @param {(Statement | ModuleDeclaration)[]} statements statements
+	 */
+	walkStatements(statements) {
+		for (let index = 0, len = statements.length; index < len; index++) {
+			const statement = statements[index];
+			this.walkStatement(statement);
+		}
+	}
+
+	/**
+	 * Walking iterates the statements and expressions and processes them
+	 * @param {Statement | ModuleDeclaration} statement statement
+	 */
+	preWalkStatement(statement) {
+		/** @type {StatementPath} */
+		(this.statementPath).push(statement);
+		if (this.hooks.preStatement.call(statement)) {
+			this.prevStatement =
+				/** @type {StatementPath} */
+				(this.statementPath).pop();
+			return;
+		}
+		switch (statement.type) {
+			case "BlockStatement":
+				this.preWalkBlockStatement(statement);
+				break;
+			case "DoWhileStatement":
+				this.preWalkDoWhileStatement(statement);
+				break;
+			case "ForInStatement":
+				this.preWalkForInStatement(statement);
+				break;
+			case "ForOfStatement":
+				this.preWalkForOfStatement(statement);
+				break;
+			case "ForStatement":
+				this.preWalkForStatement(statement);
+				break;
+			case "FunctionDeclaration":
+				this.preWalkFunctionDeclaration(statement);
+				break;
+			case "IfStatement":
+				this.preWalkIfStatement(statement);
+				break;
+			case "LabeledStatement":
+				this.preWalkLabeledStatement(statement);
+				break;
+			case "SwitchStatement":
+				this.preWalkSwitchStatement(statement);
+				break;
+			case "TryStatement":
+				this.preWalkTryStatement(statement);
+				break;
+			case "VariableDeclaration":
+				this.preWalkVariableDeclaration(statement);
+				break;
+			case "WhileStatement":
+				this.preWalkWhileStatement(statement);
+				break;
+			case "WithStatement":
+				this.preWalkWithStatement(statement);
+				break;
+		}
+		this.prevStatement =
+			/** @type {StatementPath} */
+			(this.statementPath).pop();
+	}
+
+	/**
+	 * @param {Statement | ModuleDeclaration} statement statement
+	 */
+	blockPreWalkStatement(statement) {
+		/** @type {StatementPath} */
+		(this.statementPath).push(statement);
+		if (this.hooks.blockPreStatement.call(statement)) {
+			this.prevStatement =
+				/** @type {StatementPath} */
+				(this.statementPath).pop();
+			return;
+		}
+		switch (statement.type) {
+			case "ImportDeclaration":
+				this.blockPreWalkImportDeclaration(statement);
+				break;
+			case "ExportAllDeclaration":
+				this.blockPreWalkExportAllDeclaration(statement);
+				break;
+			case "ExportDefaultDeclaration":
+				this.blockPreWalkExportDefaultDeclaration(statement);
+				break;
+			case "ExportNamedDeclaration":
+				this.blockPreWalkExportNamedDeclaration(statement);
+				break;
+			case "VariableDeclaration":
+				this.blockPreWalkVariableDeclaration(statement);
+				break;
+			case "ClassDeclaration":
+				this.blockPreWalkClassDeclaration(statement);
+				break;
+			case "ExpressionStatement":
+				this.blockPreWalkExpressionStatement(statement);
+		}
+		this.prevStatement =
+			/** @type {StatementPath} */
+			(this.statementPath).pop();
+	}
+
+	/**
+	 * @param {Statement | ModuleDeclaration} statement statement
+	 */
+	walkStatement(statement) {
+		/** @type {StatementPath} */
+		(this.statementPath).push(statement);
+		if (this.hooks.statement.call(statement) !== undefined) {
+			this.prevStatement =
+				/** @type {StatementPath} */
+				(this.statementPath).pop();
+			return;
+		}
+		switch (statement.type) {
+			case "BlockStatement":
+				this.walkBlockStatement(statement);
+				break;
+			case "ClassDeclaration":
+				this.walkClassDeclaration(statement);
+				break;
+			case "DoWhileStatement":
+				this.walkDoWhileStatement(statement);
+				break;
+			case "ExportDefaultDeclaration":
+				this.walkExportDefaultDeclaration(statement);
+				break;
+			case "ExportNamedDeclaration":
+				this.walkExportNamedDeclaration(statement);
+				break;
+			case "ExpressionStatement":
+				this.walkExpressionStatement(statement);
+				break;
+			case "ForInStatement":
+				this.walkForInStatement(statement);
+				break;
+			case "ForOfStatement":
+				this.walkForOfStatement(statement);
+				break;
+			case "ForStatement":
+				this.walkForStatement(statement);
+				break;
+			case "FunctionDeclaration":
+				this.walkFunctionDeclaration(statement);
+				break;
+			case "IfStatement":
+				this.walkIfStatement(statement);
+				break;
+			case "LabeledStatement":
+				this.walkLabeledStatement(statement);
+				break;
+			case "ReturnStatement":
+				this.walkReturnStatement(statement);
+				break;
+			case "SwitchStatement":
+				this.walkSwitchStatement(statement);
+				break;
+			case "ThrowStatement":
+				this.walkThrowStatement(statement);
+				break;
+			case "TryStatement":
+				this.walkTryStatement(statement);
+				break;
+			case "VariableDeclaration":
+				this.walkVariableDeclaration(statement);
+				break;
+			case "WhileStatement":
+				this.walkWhileStatement(statement);
+				break;
+			case "WithStatement":
+				this.walkWithStatement(statement);
+				break;
+		}
+		this.prevStatement =
+			/** @type {StatementPath} */
+			(this.statementPath).pop();
+	}
+
+	/**
+	 * Walks a statements that is nested within a parent statement
+	 * and can potentially be a non-block statement.
+	 * This enforces the nested statement to never be in ASI position.
+	 * @param {Statement} statement the nested statement
+	 */
+	walkNestedStatement(statement) {
+		this.prevStatement = undefined;
+		this.walkStatement(statement);
+	}
+
+	// Real Statements
+	/**
+	 * @param {BlockStatement} statement block statement
+	 */
+	preWalkBlockStatement(statement) {
+		this.preWalkStatements(statement.body);
+	}
+
+	/**
+	 * @param {BlockStatement} statement block statement
+	 */
+	walkBlockStatement(statement) {
+		this.inBlockScope(() => {
+			const body = statement.body;
+			const prev = this.prevStatement;
+			this.blockPreWalkStatements(body);
+			this.prevStatement = prev;
+			this.walkStatements(body);
+		});
+	}
+
+	/**
+	 * @param {ExpressionStatement} statement expression statement
+	 */
+	walkExpressionStatement(statement) {
+		this.walkExpression(statement.expression);
+	}
+
+	/**
+	 * @param {IfStatement} statement if statement
+	 */
+	preWalkIfStatement(statement) {
+		this.preWalkStatement(statement.consequent);
+		if (statement.alternate) {
+			this.preWalkStatement(statement.alternate);
+		}
+	}
+
+	/**
+	 * @param {IfStatement} statement if statement
+	 */
+	walkIfStatement(statement) {
+		const result = this.hooks.statementIf.call(statement);
+		if (result === undefined) {
+			this.walkExpression(statement.test);
+			this.walkNestedStatement(statement.consequent);
+			if (statement.alternate) {
+				this.walkNestedStatement(statement.alternate);
+			}
+		} else if (result) {
+			this.walkNestedStatement(statement.consequent);
+		} else if (statement.alternate) {
+			this.walkNestedStatement(statement.alternate);
+		}
+	}
+
+	/**
+	 * @param {LabeledStatement} statement with statement
+	 */
+	preWalkLabeledStatement(statement) {
+		this.preWalkStatement(statement.body);
+	}
+
+	/**
+	 * @param {LabeledStatement} statement with statement
+	 */
+	walkLabeledStatement(statement) {
+		const hook = this.hooks.label.get(statement.label.name);
+		if (hook !== undefined) {
+			const result = hook.call(statement);
+			if (result === true) return;
+		}
+		this.walkNestedStatement(statement.body);
+	}
+
+	/**
+	 * @param {WithStatement} statement with statement
+	 */
+	preWalkWithStatement(statement) {
+		this.preWalkStatement(statement.body);
+	}
+
+	/**
+	 * @param {WithStatement} statement with statement
+	 */
+	walkWithStatement(statement) {
+		this.walkExpression(statement.object);
+		this.walkNestedStatement(statement.body);
+	}
+
+	/**
+	 * @param {SwitchStatement} statement switch statement
+	 */
+	preWalkSwitchStatement(statement) {
+		this.preWalkSwitchCases(statement.cases);
+	}
+
+	/**
+	 * @param {SwitchStatement} statement switch statement
+	 */
+	walkSwitchStatement(statement) {
+		this.walkExpression(statement.discriminant);
+		this.walkSwitchCases(statement.cases);
+	}
+
+	/**
+	 * @param {ReturnStatement | ThrowStatement} statement return or throw statement
+	 */
+	walkTerminatingStatement(statement) {
+		if (statement.argument) this.walkExpression(statement.argument);
+	}
+
+	/**
+	 * @param {ReturnStatement} statement return statement
+	 */
+	walkReturnStatement(statement) {
+		this.walkTerminatingStatement(statement);
+	}
+
+	/**
+	 * @param {ThrowStatement} statement return statement
+	 */
+	walkThrowStatement(statement) {
+		this.walkTerminatingStatement(statement);
+	}
+
+	/**
+	 * @param {TryStatement} statement try statement
+	 */
+	preWalkTryStatement(statement) {
+		this.preWalkStatement(statement.block);
+		if (statement.handler) this.preWalkCatchClause(statement.handler);
+		if (statement.finalizer) this.preWalkStatement(statement.finalizer);
+	}
+
+	/**
+	 * @param {TryStatement} statement try statement
+	 */
+	walkTryStatement(statement) {
+		if (this.scope.inTry) {
+			this.walkStatement(statement.block);
+		} else {
+			this.scope.inTry = true;
+			this.walkStatement(statement.block);
+			this.scope.inTry = false;
+		}
+		if (statement.handler) this.walkCatchClause(statement.handler);
+		if (statement.finalizer) this.walkStatement(statement.finalizer);
+	}
+
+	/**
+	 * @param {WhileStatement} statement while statement
+	 */
+	preWalkWhileStatement(statement) {
+		this.preWalkStatement(statement.body);
+	}
+
+	/**
+	 * @param {WhileStatement} statement while statement
+	 */
+	walkWhileStatement(statement) {
+		this.walkExpression(statement.test);
+		this.walkNestedStatement(statement.body);
+	}
+
+	/**
+	 * @param {DoWhileStatement} statement do while statement
+	 */
+	preWalkDoWhileStatement(statement) {
+		this.preWalkStatement(statement.body);
+	}
+
+	/**
+	 * @param {DoWhileStatement} statement do while statement
+	 */
+	walkDoWhileStatement(statement) {
+		this.walkNestedStatement(statement.body);
+		this.walkExpression(statement.test);
+	}
+
+	/**
+	 * @param {ForStatement} statement for statement
+	 */
+	preWalkForStatement(statement) {
+		if (statement.init && statement.init.type === "VariableDeclaration") {
+			this.preWalkStatement(statement.init);
+		}
+		this.preWalkStatement(statement.body);
+	}
+
+	/**
+	 * @param {ForStatement} statement for statement
+	 */
+	walkForStatement(statement) {
+		this.inBlockScope(() => {
+			if (statement.init) {
+				if (statement.init.type === "VariableDeclaration") {
+					this.blockPreWalkVariableDeclaration(statement.init);
+					this.prevStatement = undefined;
+					this.walkStatement(statement.init);
+				} else {
+					this.walkExpression(statement.init);
+				}
+			}
+			if (statement.test) {
+				this.walkExpression(statement.test);
+			}
+			if (statement.update) {
+				this.walkExpression(statement.update);
+			}
+			const body = statement.body;
+			if (body.type === "BlockStatement") {
+				// no need to add additional scope
+				const prev = this.prevStatement;
+				this.blockPreWalkStatements(body.body);
+				this.prevStatement = prev;
+				this.walkStatements(body.body);
+			} else {
+				this.walkNestedStatement(body);
+			}
+		});
+	}
+
+	/**
+	 * @param {ForInStatement} statement for statement
+	 */
+	preWalkForInStatement(statement) {
+		if (statement.left.type === "VariableDeclaration") {
+			this.preWalkVariableDeclaration(statement.left);
+		}
+		this.preWalkStatement(statement.body);
+	}
+
+	/**
+	 * @param {ForInStatement} statement for statement
+	 */
+	walkForInStatement(statement) {
+		this.inBlockScope(() => {
+			if (statement.left.type === "VariableDeclaration") {
+				this.blockPreWalkVariableDeclaration(statement.left);
+				this.walkVariableDeclaration(statement.left);
+			} else {
+				this.walkPattern(statement.left);
+			}
+			this.walkExpression(statement.right);
+			const body = statement.body;
+			if (body.type === "BlockStatement") {
+				// no need to add additional scope
+				const prev = this.prevStatement;
+				this.blockPreWalkStatements(body.body);
+				this.prevStatement = prev;
+				this.walkStatements(body.body);
+			} else {
+				this.walkNestedStatement(body);
+			}
+		});
+	}
+
+	/**
+	 * @param {ForOfStatement} statement statement
+	 */
+	preWalkForOfStatement(statement) {
+		if (statement.await && this.scope.topLevelScope === true) {
+			this.hooks.topLevelAwait.call(statement);
+		}
+		if (statement.left.type === "VariableDeclaration") {
+			this.preWalkVariableDeclaration(statement.left);
+		}
+		this.preWalkStatement(statement.body);
+	}
+
+	/**
+	 * @param {ForOfStatement} statement for statement
+	 */
+	walkForOfStatement(statement) {
+		this.inBlockScope(() => {
+			if (statement.left.type === "VariableDeclaration") {
+				this.blockPreWalkVariableDeclaration(statement.left);
+				this.walkVariableDeclaration(statement.left);
+			} else {
+				this.walkPattern(statement.left);
+			}
+			this.walkExpression(statement.right);
+			const body = statement.body;
+			if (body.type === "BlockStatement") {
+				// no need to add additional scope
+				const prev = this.prevStatement;
+				this.blockPreWalkStatements(body.body);
+				this.prevStatement = prev;
+				this.walkStatements(body.body);
+			} else {
+				this.walkNestedStatement(body);
+			}
+		});
+	}
+
+	/**
+	 * @param {FunctionDeclaration} statement function declaration
+	 */
+	preWalkFunctionDeclaration(statement) {
+		if (statement.id) {
+			this.defineVariable(statement.id.name);
+		}
+	}
+
+	/**
+	 * @param {FunctionDeclaration} statement function declaration
+	 */
+	walkFunctionDeclaration(statement) {
+		const wasTopLevel = this.scope.topLevelScope;
+		this.scope.topLevelScope = false;
+		this.inFunctionScope(true, statement.params, () => {
+			for (const param of statement.params) {
+				this.walkPattern(param);
+			}
+			if (statement.body.type === "BlockStatement") {
+				this.detectMode(statement.body.body);
+				const prev = this.prevStatement;
+				this.preWalkStatement(statement.body);
+				this.prevStatement = prev;
+				this.walkStatement(statement.body);
+			} else {
+				this.walkExpression(statement.body);
+			}
+		});
+		this.scope.topLevelScope = wasTopLevel;
+	}
+
+	/**
+	 * @param {ExpressionStatement} statement expression statement
+	 */
+	blockPreWalkExpressionStatement(statement) {
+		const expression = statement.expression;
+		switch (expression.type) {
+			case "AssignmentExpression":
+				this.preWalkAssignmentExpression(expression);
+		}
+	}
+
+	/**
+	 * @param {AssignmentExpression} expression assignment expression
+	 */
+	preWalkAssignmentExpression(expression) {
+		if (
+			expression.left.type !== "ObjectPattern" ||
+			!this.destructuringAssignmentProperties
+		)
+			return;
+		const keys = this._preWalkObjectPattern(expression.left);
+		if (!keys) return;
+
+		// check multiple assignments
+		if (this.destructuringAssignmentProperties.has(expression)) {
+			const set =
+				/** @type {Set<DestructuringAssignmentProperty>} */
+				(this.destructuringAssignmentProperties.get(expression));
+			this.destructuringAssignmentProperties.delete(expression);
+			for (const id of set) keys.add(id);
+		}
+
+		this.destructuringAssignmentProperties.set(
+			expression.right.type === "AwaitExpression"
+				? expression.right.argument
+				: expression.right,
+			keys
+		);
+
+		if (expression.right.type === "AssignmentExpression") {
+			this.preWalkAssignmentExpression(expression.right);
+		}
+	}
+
+	/**
+	 * @param {ImportDeclaration} statement statement
+	 */
+	blockPreWalkImportDeclaration(statement) {
+		const source = /** @type {ImportSource} */ (statement.source.value);
+		this.hooks.import.call(statement, source);
+		for (const specifier of statement.specifiers) {
+			const name = specifier.local.name;
+			switch (specifier.type) {
+				case "ImportDefaultSpecifier":
+					if (
+						!this.hooks.importSpecifier.call(statement, source, "default", name)
+					) {
+						this.defineVariable(name);
+					}
+					break;
+				case "ImportSpecifier":
+					if (
+						!this.hooks.importSpecifier.call(
+							statement,
+							source,
+							/** @type {Identifier} */
+							(specifier.imported).name ||
+								/** @type {string} */
+								(
+									/** @type {Literal} */
+									(specifier.imported).value
+								),
+							name
+						)
+					) {
+						this.defineVariable(name);
+					}
+					break;
+				case "ImportNamespaceSpecifier":
+					if (!this.hooks.importSpecifier.call(statement, source, null, name)) {
+						this.defineVariable(name);
+					}
+					break;
+				default:
+					this.defineVariable(name);
+			}
+		}
+	}
+
+	/**
+	 * @param {Declaration} declaration declaration
+	 * @param {OnIdent} onIdent on ident callback
+	 */
+	enterDeclaration(declaration, onIdent) {
+		switch (declaration.type) {
+			case "VariableDeclaration":
+				for (const declarator of declaration.declarations) {
+					switch (declarator.type) {
+						case "VariableDeclarator": {
+							this.enterPattern(declarator.id, onIdent);
+							break;
+						}
+					}
+				}
+				break;
+			case "FunctionDeclaration":
+				this.enterPattern(declaration.id, onIdent);
+				break;
+			case "ClassDeclaration":
+				this.enterPattern(declaration.id, onIdent);
+				break;
+		}
+	}
+
+	/**
+	 * @param {ExportNamedDeclaration} statement statement
+	 */
+	blockPreWalkExportNamedDeclaration(statement) {
+		let source;
+		if (statement.source) {
+			source = /** @type {ImportSource} */ (statement.source.value);
+			this.hooks.exportImport.call(statement, source);
+		} else {
+			this.hooks.export.call(statement);
+		}
+		if (
+			statement.declaration &&
+			!this.hooks.exportDeclaration.call(statement, statement.declaration)
+		) {
+			const prev = this.prevStatement;
+			this.preWalkStatement(statement.declaration);
+			this.prevStatement = prev;
+			this.blockPreWalkStatement(statement.declaration);
+			let index = 0;
+			this.enterDeclaration(statement.declaration, def => {
+				this.hooks.exportSpecifier.call(statement, def, def, index++);
+			});
+		}
+		if (statement.specifiers) {
+			for (
+				let specifierIndex = 0;
+				specifierIndex < statement.specifiers.length;
+				specifierIndex++
+			) {
+				const specifier = statement.specifiers[specifierIndex];
+				switch (specifier.type) {
+					case "ExportSpecifier": {
+						const localName =
+							/** @type {Identifier} */ (specifier.local).name ||
+							/** @type {string} */ (
+								/** @type {Literal} */ (specifier.local).value
+							);
+						const name =
+							/** @type {Identifier} */
+							(specifier.exported).name ||
+							/** @type {string} */
+							(/** @type {Literal} */ (specifier.exported).value);
+						if (source) {
+							this.hooks.exportImportSpecifier.call(
+								statement,
+								source,
+								localName,
+								name,
+								specifierIndex
+							);
+						} else {
+							this.hooks.exportSpecifier.call(
+								statement,
+								localName,
+								name,
+								specifierIndex
+							);
+						}
+						break;
+					}
+				}
+			}
+		}
+	}
+
+	/**
+	 * @param {ExportNamedDeclaration} statement the statement
+	 */
+	walkExportNamedDeclaration(statement) {
+		if (statement.declaration) {
+			this.walkStatement(statement.declaration);
+		}
+	}
+
+	/**
+	 * @param {TODO} statement statement
+	 */
+	blockPreWalkExportDefaultDeclaration(statement) {
+		const prev = this.prevStatement;
+		this.preWalkStatement(statement.declaration);
+		this.prevStatement = prev;
+		this.blockPreWalkStatement(statement.declaration);
+		if (
+			/** @type {FunctionDeclaration | ClassDeclaration} */ (
+				statement.declaration
+			).id &&
+			statement.declaration.type !== "FunctionExpression" &&
+			statement.declaration.type !== "ClassExpression"
+		) {
+			const declaration =
+				/** @type {FunctionDeclaration | ClassDeclaration} */
+				(statement.declaration);
+			this.hooks.exportSpecifier.call(
+				statement,
+				declaration.id.name,
+				"default",
+				undefined
+			);
+		}
+	}
+
+	/**
+	 * @param {ExportDefaultDeclaration} statement statement
+	 */
+	walkExportDefaultDeclaration(statement) {
+		this.hooks.export.call(statement);
+		if (
+			/** @type {FunctionDeclaration | ClassDeclaration} */ (
+				statement.declaration
+			).id &&
+			statement.declaration.type !== "FunctionExpression" &&
+			statement.declaration.type !== "ClassExpression"
+		) {
+			const declaration =
+				/** @type {FunctionDeclaration | ClassDeclaration} */
+				(statement.declaration);
+			if (!this.hooks.exportDeclaration.call(statement, declaration)) {
+				this.walkStatement(declaration);
+			}
+		} else {
+			// Acorn parses `export default function() {}` as `FunctionDeclaration` and
+			// `export default class {}` as `ClassDeclaration`, both with `id = null`.
+			// These nodes must be treated as expressions.
+			if (
+				statement.declaration.type === "FunctionDeclaration" ||
+				statement.declaration.type === "ClassDeclaration"
+			) {
+				this.walkStatement(
+					/** @type {FunctionDeclaration | ClassDeclaration} */
+					(statement.declaration)
+				);
+			} else {
+				this.walkExpression(statement.declaration);
+			}
+
+			if (
+				!this.hooks.exportExpression.call(
+					statement,
+					/** @type {TODO} */ (statement).declaration
+				)
+			) {
+				this.hooks.exportSpecifier.call(
+					statement,
+					/** @type {TODO} */ (statement.declaration),
+					"default",
+					undefined
+				);
+			}
+		}
+	}
+
+	/**
+	 * @param {ExportAllDeclaration} statement statement
+	 */
+	blockPreWalkExportAllDeclaration(statement) {
+		const source = /** @type {ImportSource} */ (statement.source.value);
+		const name = statement.exported
+			? /** @type {Identifier} */
+				(statement.exported).name ||
+				/** @type {string} */
+				(/** @type {Literal} */ (statement.exported).value)
+			: null;
+		this.hooks.exportImport.call(statement, source);
+		this.hooks.exportImportSpecifier.call(statement, source, null, name, 0);
+	}
+
+	/**
+	 * @param {VariableDeclaration} statement variable declaration
+	 */
+	preWalkVariableDeclaration(statement) {
+		if (statement.kind !== "var") return;
+		this._preWalkVariableDeclaration(statement, this.hooks.varDeclarationVar);
+	}
+
+	/**
+	 * @param {VariableDeclaration} statement variable declaration
+	 */
+	blockPreWalkVariableDeclaration(statement) {
+		if (statement.kind === "var") return;
+		const hookMap =
+			statement.kind === "const"
+				? this.hooks.varDeclarationConst
+				: this.hooks.varDeclarationLet;
+		this._preWalkVariableDeclaration(statement, hookMap);
+	}
+
+	/**
+	 * @param {VariableDeclaration} statement variable declaration
+	 * @param {TODO} hookMap map of hooks
+	 */
+	_preWalkVariableDeclaration(statement, hookMap) {
+		for (const declarator of statement.declarations) {
+			switch (declarator.type) {
+				case "VariableDeclarator": {
+					this.preWalkVariableDeclarator(declarator);
+					if (!this.hooks.preDeclarator.call(declarator, statement)) {
+						this.enterPattern(declarator.id, (name, decl) => {
+							let hook = hookMap.get(name);
+							if (hook === undefined || !hook.call(decl)) {
+								hook = this.hooks.varDeclaration.get(name);
+								if (hook === undefined || !hook.call(decl)) {
+									this.defineVariable(name);
+								}
+							}
+						});
+					}
+					break;
+				}
+			}
+		}
+	}
+
+	/**
+	 * @param {ObjectPattern} objectPattern object pattern
+	 * @returns {Set<DestructuringAssignmentProperty> | undefined} set of names or undefined if not all keys are identifiers
+	 */
+	_preWalkObjectPattern(objectPattern) {
+		/** @type {Set<DestructuringAssignmentProperty>} */
+		const props = new Set();
+		const properties = objectPattern.properties;
+		for (let i = 0; i < properties.length; i++) {
+			const property = properties[i];
+			if (property.type !== "Property") return;
+			if (property.shorthand && property.value.type === "Identifier") {
+				this.scope.inShorthand = property.value.name;
+			}
+			const key = property.key;
+			if (key.type === "Identifier") {
+				props.add({
+					id: key.name,
+					range: key.range,
+					shorthand: this.scope.inShorthand
+				});
+			} else {
+				const id = this.evaluateExpression(key);
+				const str = id.asString();
+				if (str) {
+					props.add({
+						id: str,
+						range: key.range,
+						shorthand: this.scope.inShorthand
+					});
+				} else {
+					// could not evaluate key
+					return;
+				}
+			}
+			this.scope.inShorthand = false;
+		}
+
+		return props;
+	}
+
+	/**
+	 * @param {VariableDeclarator} declarator variable declarator
+	 */
+	preWalkVariableDeclarator(declarator) {
+		if (
+			!declarator.init ||
+			declarator.id.type !== "ObjectPattern" ||
+			!this.destructuringAssignmentProperties
+		)
+			return;
+		const keys = this._preWalkObjectPattern(declarator.id);
+
+		if (!keys) return;
+		this.destructuringAssignmentProperties.set(
+			declarator.init.type === "AwaitExpression"
+				? declarator.init.argument
+				: declarator.init,
+			keys
+		);
+
+		if (declarator.init.type === "AssignmentExpression") {
+			this.preWalkAssignmentExpression(declarator.init);
+		}
+	}
+
+	/**
+	 * @param {VariableDeclaration} statement variable declaration
+	 */
+	walkVariableDeclaration(statement) {
+		for (const declarator of statement.declarations) {
+			switch (declarator.type) {
+				case "VariableDeclarator": {
+					const renameIdentifier =
+						declarator.init && this.getRenameIdentifier(declarator.init);
+					if (renameIdentifier && declarator.id.type === "Identifier") {
+						const hook = this.hooks.canRename.get(renameIdentifier);
+						if (
+							hook !== undefined &&
+							hook.call(/** @type {Expression} */ (declarator.init))
+						) {
+							// renaming with "var a = b;"
+							const hook = this.hooks.rename.get(renameIdentifier);
+							if (
+								hook === undefined ||
+								!hook.call(/** @type {Expression} */ (declarator.init))
+							) {
+								this.setVariable(declarator.id.name, renameIdentifier);
+							}
+							break;
+						}
+					}
+					if (!this.hooks.declarator.call(declarator, statement)) {
+						this.walkPattern(declarator.id);
+						if (declarator.init) this.walkExpression(declarator.init);
+					}
+					break;
+				}
+			}
+		}
+	}
+
+	/**
+	 * @param {ClassDeclaration} statement class declaration
+	 */
+	blockPreWalkClassDeclaration(statement) {
+		if (statement.id) {
+			this.defineVariable(statement.id.name);
+		}
+	}
+
+	/**
+	 * @param {ClassDeclaration} statement class declaration
+	 */
+	walkClassDeclaration(statement) {
+		this.walkClass(statement);
+	}
+
+	/**
+	 * @param {SwitchCase[]} switchCases switch statement
+	 */
+	preWalkSwitchCases(switchCases) {
+		for (let index = 0, len = switchCases.length; index < len; index++) {
+			const switchCase = switchCases[index];
+			this.preWalkStatements(switchCase.consequent);
+		}
+	}
+
+	/**
+	 * @param {SwitchCase[]} switchCases switch statement
+	 */
+	walkSwitchCases(switchCases) {
+		this.inBlockScope(() => {
+			const len = switchCases.length;
+
+			// we need to pre walk all statements first since we can have invalid code
+			// import A from "module";
+			// switch(1) {
+			//    case 1:
+			//      console.log(A); // should fail at runtime
+			//    case 2:
+			//      const A = 1;
+			// }
+			for (let index = 0; index < len; index++) {
+				const switchCase = switchCases[index];
+
+				if (switchCase.consequent.length > 0) {
+					const prev = this.prevStatement;
+					this.blockPreWalkStatements(switchCase.consequent);
+					this.prevStatement = prev;
+				}
+			}
+
+			for (let index = 0; index < len; index++) {
+				const switchCase = switchCases[index];
+
+				if (switchCase.test) {
+					this.walkExpression(switchCase.test);
+				}
+				if (switchCase.consequent.length > 0) {
+					this.walkStatements(switchCase.consequent);
+				}
+			}
+		});
+	}
+
+	/**
+	 * @param {CatchClause} catchClause catch clause
+	 */
+	preWalkCatchClause(catchClause) {
+		this.preWalkStatement(catchClause.body);
+	}
+
+	/**
+	 * @param {CatchClause} catchClause catch clause
+	 */
+	walkCatchClause(catchClause) {
+		this.inBlockScope(() => {
+			// Error binding is optional in catch clause since ECMAScript 2019
+			if (catchClause.param !== null) {
+				this.enterPattern(catchClause.param, ident => {
+					this.defineVariable(ident);
+				});
+				this.walkPattern(catchClause.param);
+			}
+			const prev = this.prevStatement;
+			this.blockPreWalkStatement(catchClause.body);
+			this.prevStatement = prev;
+			this.walkStatement(catchClause.body);
+		});
+	}
+
+	/**
+	 * @param {Pattern} pattern pattern
+	 */
+	walkPattern(pattern) {
+		switch (pattern.type) {
+			case "ArrayPattern":
+				this.walkArrayPattern(pattern);
+				break;
+			case "AssignmentPattern":
+				this.walkAssignmentPattern(pattern);
+				break;
+			case "MemberExpression":
+				this.walkMemberExpression(pattern);
+				break;
+			case "ObjectPattern":
+				this.walkObjectPattern(pattern);
+				break;
+			case "RestElement":
+				this.walkRestElement(pattern);
+				break;
+		}
+	}
+
+	/**
+	 * @param {AssignmentPattern} pattern assignment pattern
+	 */
+	walkAssignmentPattern(pattern) {
+		this.walkExpression(pattern.right);
+		this.walkPattern(pattern.left);
+	}
+
+	/**
+	 * @param {ObjectPattern} pattern pattern
+	 */
+	walkObjectPattern(pattern) {
+		for (let i = 0, len = pattern.properties.length; i < len; i++) {
+			const prop = pattern.properties[i];
+			if (prop) {
+				if (prop.type === "RestElement") {
+					continue;
+				}
+				if (prop.computed) this.walkExpression(prop.key);
+				if (prop.value) this.walkPattern(prop.value);
+			}
+		}
+	}
+
+	/**
+	 * @param {ArrayPattern} pattern array pattern
+	 */
+	walkArrayPattern(pattern) {
+		for (let i = 0, len = pattern.elements.length; i < len; i++) {
+			const element = pattern.elements[i];
+			if (element) this.walkPattern(element);
+		}
+	}
+
+	/**
+	 * @param {RestElement} pattern rest element
+	 */
+	walkRestElement(pattern) {
+		this.walkPattern(pattern.argument);
+	}
+
+	/**
+	 * @param {(Expression | SpreadElement | null)[]} expressions expressions
+	 */
+	walkExpressions(expressions) {
+		for (const expression of expressions) {
+			if (expression) {
+				this.walkExpression(expression);
+			}
+		}
+	}
+
+	/**
+	 * @param {TODO} expression expression
+	 */
+	walkExpression(expression) {
+		switch (expression.type) {
+			case "ArrayExpression":
+				this.walkArrayExpression(expression);
+				break;
+			case "ArrowFunctionExpression":
+				this.walkArrowFunctionExpression(expression);
+				break;
+			case "AssignmentExpression":
+				this.walkAssignmentExpression(expression);
+				break;
+			case "AwaitExpression":
+				this.walkAwaitExpression(expression);
+				break;
+			case "BinaryExpression":
+				this.walkBinaryExpression(expression);
+				break;
+			case "CallExpression":
+				this.walkCallExpression(expression);
+				break;
+			case "ChainExpression":
+				this.walkChainExpression(expression);
+				break;
+			case "ClassExpression":
+				this.walkClassExpression(expression);
+				break;
+			case "ConditionalExpression":
+				this.walkConditionalExpression(expression);
+				break;
+			case "FunctionExpression":
+				this.walkFunctionExpression(expression);
+				break;
+			case "Identifier":
+				this.walkIdentifier(expression);
+				break;
+			case "ImportExpression":
+				this.walkImportExpression(expression);
+				break;
+			case "LogicalExpression":
+				this.walkLogicalExpression(expression);
+				break;
+			case "MetaProperty":
+				this.walkMetaProperty(expression);
+				break;
+			case "MemberExpression":
+				this.walkMemberExpression(expression);
+				break;
+			case "NewExpression":
+				this.walkNewExpression(expression);
+				break;
+			case "ObjectExpression":
+				this.walkObjectExpression(expression);
+				break;
+			case "SequenceExpression":
+				this.walkSequenceExpression(expression);
+				break;
+			case "SpreadElement":
+				this.walkSpreadElement(expression);
+				break;
+			case "TaggedTemplateExpression":
+				this.walkTaggedTemplateExpression(expression);
+				break;
+			case "TemplateLiteral":
+				this.walkTemplateLiteral(expression);
+				break;
+			case "ThisExpression":
+				this.walkThisExpression(expression);
+				break;
+			case "UnaryExpression":
+				this.walkUnaryExpression(expression);
+				break;
+			case "UpdateExpression":
+				this.walkUpdateExpression(expression);
+				break;
+			case "YieldExpression":
+				this.walkYieldExpression(expression);
+				break;
+		}
+	}
+
+	/**
+	 * @param {AwaitExpression} expression await expression
+	 */
+	walkAwaitExpression(expression) {
+		if (this.scope.topLevelScope === true)
+			this.hooks.topLevelAwait.call(expression);
+		this.walkExpression(expression.argument);
+	}
+
+	/**
+	 * @param {ArrayExpression} expression array expression
+	 */
+	walkArrayExpression(expression) {
+		if (expression.elements) {
+			this.walkExpressions(expression.elements);
+		}
+	}
+
+	/**
+	 * @param {SpreadElement} expression spread element
+	 */
+	walkSpreadElement(expression) {
+		if (expression.argument) {
+			this.walkExpression(expression.argument);
+		}
+	}
+
+	/**
+	 * @param {ObjectExpression} expression object expression
+	 */
+	walkObjectExpression(expression) {
+		for (
+			let propIndex = 0, len = expression.properties.length;
+			propIndex < len;
+			propIndex++
+		) {
+			const prop = expression.properties[propIndex];
+			this.walkProperty(prop);
+		}
+	}
+
+	/**
+	 * @param {Property | SpreadElement} prop property or spread element
+	 */
+	walkProperty(prop) {
+		if (prop.type === "SpreadElement") {
+			this.walkExpression(prop.argument);
+			return;
+		}
+		if (prop.computed) {
+			this.walkExpression(prop.key);
+		}
+		if (prop.shorthand && prop.value && prop.value.type === "Identifier") {
+			this.scope.inShorthand = prop.value.name;
+			this.walkIdentifier(prop.value);
+			this.scope.inShorthand = false;
+		} else {
+			this.walkExpression(prop.value);
+		}
+	}
+
+	/**
+	 * @param {FunctionExpression} expression arrow function expression
+	 */
+	walkFunctionExpression(expression) {
+		const wasTopLevel = this.scope.topLevelScope;
+		this.scope.topLevelScope = false;
+		const scopeParams = [...expression.params];
+
+		// Add function name in scope for recursive calls
+		if (expression.id) {
+			scopeParams.push(expression.id);
+		}
+
+		this.inFunctionScope(true, scopeParams, () => {
+			for (const param of expression.params) {
+				this.walkPattern(param);
+			}
+			if (expression.body.type === "BlockStatement") {
+				this.detectMode(expression.body.body);
+				const prev = this.prevStatement;
+				this.preWalkStatement(expression.body);
+				this.prevStatement = prev;
+				this.walkStatement(expression.body);
+			} else {
+				this.walkExpression(expression.body);
+			}
+		});
+		this.scope.topLevelScope = wasTopLevel;
+	}
+
+	/**
+	 * @param {ArrowFunctionExpression} expression arrow function expression
+	 */
+	walkArrowFunctionExpression(expression) {
+		const wasTopLevel = this.scope.topLevelScope;
+		this.scope.topLevelScope = wasTopLevel ? "arrow" : false;
+		this.inFunctionScope(false, expression.params, () => {
+			for (const param of expression.params) {
+				this.walkPattern(param);
+			}
+			if (expression.body.type === "BlockStatement") {
+				this.detectMode(expression.body.body);
+				const prev = this.prevStatement;
+				this.preWalkStatement(expression.body);
+				this.prevStatement = prev;
+				this.walkStatement(expression.body);
+			} else {
+				this.walkExpression(expression.body);
+			}
+		});
+		this.scope.topLevelScope = wasTopLevel;
+	}
+
+	/**
+	 * @param {SequenceExpression} expression the sequence
+	 */
+	walkSequenceExpression(expression) {
+		if (!expression.expressions) return;
+		// We treat sequence expressions like statements when they are one statement level
+		// This has some benefits for optimizations that only work on statement level
+		const currentStatement =
+			/** @type {StatementPath} */
+			(this.statementPath)[
+				/** @type {StatementPath} */
+				(this.statementPath).length - 1
+			];
+		if (
+			currentStatement === expression ||
+			(currentStatement.type === "ExpressionStatement" &&
+				currentStatement.expression === expression)
+		) {
+			const old =
+				/** @type {StatementPathItem} */
+				(/** @type {StatementPath} */ (this.statementPath).pop());
+			const prev = this.prevStatement;
+			for (const expr of expression.expressions) {
+				/** @type {StatementPath} */
+				(this.statementPath).push(expr);
+				this.walkExpression(expr);
+				this.prevStatement =
+					/** @type {StatementPath} */
+					(this.statementPath).pop();
+			}
+			this.prevStatement = prev;
+			/** @type {StatementPath} */
+			(this.statementPath).push(old);
+		} else {
+			this.walkExpressions(expression.expressions);
+		}
+	}
+
+	/**
+	 * @param {UpdateExpression} expression the update expression
+	 */
+	walkUpdateExpression(expression) {
+		this.walkExpression(expression.argument);
+	}
+
+	/**
+	 * @param {UnaryExpression} expression the unary expression
+	 */
+	walkUnaryExpression(expression) {
+		if (expression.operator === "typeof") {
+			const result = this.callHooksForExpression(
+				this.hooks.typeof,
+				expression.argument,
+				expression
+			);
+			if (result === true) return;
+			if (expression.argument.type === "ChainExpression") {
+				const result = this.callHooksForExpression(
+					this.hooks.typeof,
+					expression.argument.expression,
+					expression
+				);
+				if (result === true) return;
+			}
+		}
+		this.walkExpression(expression.argument);
+	}
+
+	/**
+	 * @param {LogicalExpression | BinaryExpression} expression the expression
+	 */
+	walkLeftRightExpression(expression) {
+		this.walkExpression(expression.left);
+		this.walkExpression(expression.right);
+	}
+
+	/**
+	 * @param {BinaryExpression} expression the binary expression
+	 */
+	walkBinaryExpression(expression) {
+		if (this.hooks.binaryExpression.call(expression) === undefined) {
+			this.walkLeftRightExpression(expression);
+		}
+	}
+
+	/**
+	 * @param {LogicalExpression} expression the logical expression
+	 */
+	walkLogicalExpression(expression) {
+		const result = this.hooks.expressionLogicalOperator.call(expression);
+		if (result === undefined) {
+			this.walkLeftRightExpression(expression);
+		} else if (result) {
+			this.walkExpression(expression.right);
+		}
+	}
+
+	/**
+	 * @param {AssignmentExpression} expression assignment expression
+	 */
+	walkAssignmentExpression(expression) {
+		if (expression.left.type === "Identifier") {
+			const renameIdentifier = this.getRenameIdentifier(expression.right);
+			if (
+				renameIdentifier &&
+				this.callHooksForInfo(
+					this.hooks.canRename,
+					renameIdentifier,
+					expression.right
+				)
+			) {
+				// renaming "a = b;"
+				if (
+					!this.callHooksForInfo(
+						this.hooks.rename,
+						renameIdentifier,
+						expression.right
+					)
+				) {
+					this.setVariable(
+						expression.left.name,
+						typeof renameIdentifier === "string"
+							? this.getVariableInfo(renameIdentifier)
+							: renameIdentifier
+					);
+				}
+				return;
+			}
+			this.walkExpression(expression.right);
+			this.enterPattern(expression.left, (name, decl) => {
+				if (!this.callHooksForName(this.hooks.assign, name, expression)) {
+					this.walkExpression(expression.left);
+				}
+			});
+			return;
+		}
+		if (expression.left.type.endsWith("Pattern")) {
+			this.walkExpression(expression.right);
+			this.enterPattern(expression.left, (name, decl) => {
+				if (!this.callHooksForName(this.hooks.assign, name, expression)) {
+					this.defineVariable(name);
+				}
+			});
+			this.walkPattern(expression.left);
+		} else if (expression.left.type === "MemberExpression") {
+			const exprName = this.getMemberExpressionInfo(
+				expression.left,
+				ALLOWED_MEMBER_TYPES_EXPRESSION
+			);
+			if (
+				exprName &&
+				this.callHooksForInfo(
+					this.hooks.assignMemberChain,
+					exprName.rootInfo,
+					expression,
+					exprName.getMembers()
+				)
+			) {
+				return;
+			}
+			this.walkExpression(expression.right);
+			this.walkExpression(expression.left);
+		} else {
+			this.walkExpression(expression.right);
+			this.walkExpression(expression.left);
+		}
+	}
+
+	/**
+	 * @param {ConditionalExpression} expression conditional expression
+	 */
+	walkConditionalExpression(expression) {
+		const result = this.hooks.expressionConditionalOperator.call(expression);
+		if (result === undefined) {
+			this.walkExpression(expression.test);
+			this.walkExpression(expression.consequent);
+			if (expression.alternate) {
+				this.walkExpression(expression.alternate);
+			}
+		} else if (result) {
+			this.walkExpression(expression.consequent);
+		} else if (expression.alternate) {
+			this.walkExpression(expression.alternate);
+		}
+	}
+
+	/**
+	 * @param {NewExpression} expression new expression
+	 */
+	walkNewExpression(expression) {
+		const result = this.callHooksForExpression(
+			this.hooks.new,
+			expression.callee,
+			expression
+		);
+		if (result === true) return;
+		this.walkExpression(expression.callee);
+		if (expression.arguments) {
+			this.walkExpressions(expression.arguments);
+		}
+	}
+
+	/**
+	 * @param {YieldExpression} expression yield expression
+	 */
+	walkYieldExpression(expression) {
+		if (expression.argument) {
+			this.walkExpression(expression.argument);
+		}
+	}
+
+	/**
+	 * @param {TemplateLiteral} expression template literal
+	 */
+	walkTemplateLiteral(expression) {
+		if (expression.expressions) {
+			this.walkExpressions(expression.expressions);
+		}
+	}
+
+	/**
+	 * @param {TaggedTemplateExpression} expression tagged template expression
+	 */
+	walkTaggedTemplateExpression(expression) {
+		if (expression.tag) {
+			this.scope.inTaggedTemplateTag = true;
+			this.walkExpression(expression.tag);
+			this.scope.inTaggedTemplateTag = false;
+		}
+		if (expression.quasi && expression.quasi.expressions) {
+			this.walkExpressions(expression.quasi.expressions);
+		}
+	}
+
+	/**
+	 * @param {ClassExpression} expression the class expression
+	 */
+	walkClassExpression(expression) {
+		this.walkClass(expression);
+	}
+
+	/**
+	 * @param {ChainExpression} expression expression
+	 */
+	walkChainExpression(expression) {
+		const result = this.hooks.optionalChaining.call(expression);
+
+		if (result === undefined) {
+			if (expression.expression.type === "CallExpression") {
+				this.walkCallExpression(expression.expression);
+			} else {
+				this.walkMemberExpression(expression.expression);
+			}
+		}
+	}
+
+	/**
+	 * @private
+	 * @param {FunctionExpression | ArrowFunctionExpression} functionExpression function expression
+	 * @param {(Expression | SpreadElement)[]} options options
+	 * @param {Expression | SpreadElement | null} currentThis current this
+	 */
+	_walkIIFE(functionExpression, options, currentThis) {
+		/**
+		 * @param {Expression | SpreadElement} argOrThis arg or this
+		 * @returns {string | VariableInfoInterface | undefined} var info
+		 */
+		const getVarInfo = argOrThis => {
+			const renameIdentifier = this.getRenameIdentifier(argOrThis);
+			if (
+				renameIdentifier &&
+				this.callHooksForInfo(
+					this.hooks.canRename,
+					renameIdentifier,
+					/** @type {Expression} */
+					(argOrThis)
+				) &&
+				!this.callHooksForInfo(
+					this.hooks.rename,
+					renameIdentifier,
+					/** @type {Expression} */
+					(argOrThis)
+				)
+			) {
+				return typeof renameIdentifier === "string"
+					? /** @type {string} */ (this.getVariableInfo(renameIdentifier))
+					: renameIdentifier;
+			}
+			this.walkExpression(argOrThis);
+		};
+		const { params, type } = functionExpression;
+		const arrow = type === "ArrowFunctionExpression";
+		const renameThis = currentThis ? getVarInfo(currentThis) : null;
+		const varInfoForArgs = options.map(getVarInfo);
+		const wasTopLevel = this.scope.topLevelScope;
+		this.scope.topLevelScope = wasTopLevel && arrow ? "arrow" : false;
+		const scopeParams =
+			/** @type {(Identifier | string)[]} */
+			(params.filter((identifier, idx) => !varInfoForArgs[idx]));
+
+		// Add function name in scope for recursive calls
+		if (
+			functionExpression.type === "FunctionExpression" &&
+			functionExpression.id
+		) {
+			scopeParams.push(functionExpression.id.name);
+		}
+
+		this.inFunctionScope(true, scopeParams, () => {
+			if (renameThis && !arrow) {
+				this.setVariable("this", renameThis);
+			}
+			for (let i = 0; i < varInfoForArgs.length; i++) {
+				const varInfo = varInfoForArgs[i];
+				if (!varInfo) continue;
+				if (!params[i] || params[i].type !== "Identifier") continue;
+				this.setVariable(/** @type {Identifier} */ (params[i]).name, varInfo);
+			}
+			if (functionExpression.body.type === "BlockStatement") {
+				this.detectMode(functionExpression.body.body);
+				const prev = this.prevStatement;
+				this.preWalkStatement(functionExpression.body);
+				this.prevStatement = prev;
+				this.walkStatement(functionExpression.body);
+			} else {
+				this.walkExpression(functionExpression.body);
+			}
+		});
+		this.scope.topLevelScope = wasTopLevel;
+	}
+
+	/**
+	 * @param {ImportExpression} expression import expression
+	 */
+	walkImportExpression(expression) {
+		const result = this.hooks.importCall.call(expression);
+		if (result === true) return;
+
+		this.walkExpression(expression.source);
+	}
+
+	/**
+	 * @param {CallExpression} expression expression
+	 */
+	walkCallExpression(expression) {
+		/**
+		 * @param {FunctionExpression | ArrowFunctionExpression} fn function
+		 * @returns {boolean} true when simple function
+		 */
+		const isSimpleFunction = fn =>
+			fn.params.every(p => p.type === "Identifier");
+		if (
+			expression.callee.type === "MemberExpression" &&
+			expression.callee.object.type.endsWith("FunctionExpression") &&
+			!expression.callee.computed &&
+			// eslint-disable-next-line no-warning-comments
+			// @ts-ignore
+			// TODO check me and handle more cases
+			(expression.callee.property.name === "call" ||
+				// eslint-disable-next-line no-warning-comments
+				// @ts-ignore
+				expression.callee.property.name === "bind") &&
+			expression.arguments.length > 0 &&
+			isSimpleFunction(
+				/** @type {FunctionExpression | ArrowFunctionExpression} */
+				(expression.callee.object)
+			)
+		) {
+			// (function(…) { }.call/bind(?, …))
+			this._walkIIFE(
+				/** @type {FunctionExpression | ArrowFunctionExpression} */
+				(expression.callee.object),
+				expression.arguments.slice(1),
+				expression.arguments[0]
+			);
+		} else if (
+			expression.callee.type.endsWith("FunctionExpression") &&
+			isSimpleFunction(
+				/** @type {FunctionExpression | ArrowFunctionExpression} */
+				(expression.callee)
+			)
+		) {
+			// (function(…) { }(…))
+			this._walkIIFE(
+				/** @type {FunctionExpression | ArrowFunctionExpression} */
+				(expression.callee),
+				expression.arguments,
+				null
+			);
+		} else {
+			if (expression.callee.type === "MemberExpression") {
+				const exprInfo = this.getMemberExpressionInfo(
+					expression.callee,
+					ALLOWED_MEMBER_TYPES_CALL_EXPRESSION
+				);
+				if (exprInfo && exprInfo.type === "call") {
+					const result = this.callHooksForInfo(
+						this.hooks.callMemberChainOfCallMemberChain,
+						exprInfo.rootInfo,
+						expression,
+						exprInfo.getCalleeMembers(),
+						exprInfo.call,
+						exprInfo.getMembers(),
+						exprInfo.getMemberRanges()
+					);
+					if (result === true) return;
+				}
+			}
+			const callee = this.evaluateExpression(
+				/** @type {TODO} */ (expression.callee)
+			);
+			if (callee.isIdentifier()) {
+				const result1 = this.callHooksForInfo(
+					this.hooks.callMemberChain,
+					/** @type {NonNullable<BasicEvaluatedExpression["rootInfo"]>} */
+					(callee.rootInfo),
+					expression,
+					/** @type {NonNullable<BasicEvaluatedExpression["getMembers"]>} */
+					(callee.getMembers)(),
+					callee.getMembersOptionals
+						? callee.getMembersOptionals()
+						: /** @type {NonNullable<BasicEvaluatedExpression["getMembers"]>} */
+							(callee.getMembers)().map(() => false),
+					callee.getMemberRanges ? callee.getMemberRanges() : []
+				);
+				if (result1 === true) return;
+				const result2 = this.callHooksForInfo(
+					this.hooks.call,
+					/** @type {NonNullable<BasicEvaluatedExpression["identifier"]>} */
+					(callee.identifier),
+					expression
+				);
+				if (result2 === true) return;
+			}
+
+			if (expression.callee) {
+				if (expression.callee.type === "MemberExpression") {
+					// because of call context we need to walk the call context as expression
+					this.walkExpression(expression.callee.object);
+					if (expression.callee.computed === true)
+						this.walkExpression(expression.callee.property);
+				} else {
+					this.walkExpression(expression.callee);
+				}
+			}
+			if (expression.arguments) this.walkExpressions(expression.arguments);
+		}
+	}
+
+	/**
+	 * @param {MemberExpression} expression member expression
+	 */
+	walkMemberExpression(expression) {
+		const exprInfo = this.getMemberExpressionInfo(
+			expression,
+			ALLOWED_MEMBER_TYPES_ALL
+		);
+		if (exprInfo) {
+			switch (exprInfo.type) {
+				case "expression": {
+					const result1 = this.callHooksForInfo(
+						this.hooks.expression,
+						exprInfo.name,
+						expression
+					);
+					if (result1 === true) return;
+					const members = exprInfo.getMembers();
+					const membersOptionals = exprInfo.getMembersOptionals();
+					const memberRanges = exprInfo.getMemberRanges();
+					const result2 = this.callHooksForInfo(
+						this.hooks.expressionMemberChain,
+						exprInfo.rootInfo,
+						expression,
+						members,
+						membersOptionals,
+						memberRanges
+					);
+					if (result2 === true) return;
+					this.walkMemberExpressionWithExpressionName(
+						expression,
+						exprInfo.name,
+						exprInfo.rootInfo,
+						members.slice(),
+						() =>
+							this.callHooksForInfo(
+								this.hooks.unhandledExpressionMemberChain,
+								exprInfo.rootInfo,
+								expression,
+								members
+							)
+					);
+					return;
+				}
+				case "call": {
+					const result = this.callHooksForInfo(
+						this.hooks.memberChainOfCallMemberChain,
+						exprInfo.rootInfo,
+						expression,
+						exprInfo.getCalleeMembers(),
+						exprInfo.call,
+						exprInfo.getMembers(),
+						exprInfo.getMemberRanges()
+					);
+					if (result === true) return;
+					// Fast skip over the member chain as we already called memberChainOfCallMemberChain
+					// and call computed property are literals anyway
+					this.walkExpression(exprInfo.call);
+					return;
+				}
+			}
+		}
+		this.walkExpression(expression.object);
+		if (expression.computed === true) this.walkExpression(expression.property);
+	}
+
+	/**
+	 * @param {TODO} expression member expression
+	 * @param {string} name name
+	 * @param {string | VariableInfo} rootInfo root info
+	 * @param {string[]} members members
+	 * @param {TODO} onUnhandled on unhandled callback
+	 */
+	walkMemberExpressionWithExpressionName(
+		expression,
+		name,
+		rootInfo,
+		members,
+		onUnhandled
+	) {
+		if (expression.object.type === "MemberExpression") {
+			// optimize the case where expression.object is a MemberExpression too.
+			// we can keep info here when calling walkMemberExpression directly
+			const property =
+				expression.property.name || `${expression.property.value}`;
+			name = name.slice(0, -property.length - 1);
+			members.pop();
+			const result = this.callHooksForInfo(
+				this.hooks.expression,
+				name,
+				expression.object
+			);
+			if (result === true) return;
+			this.walkMemberExpressionWithExpressionName(
+				expression.object,
+				name,
+				rootInfo,
+				members,
+				onUnhandled
+			);
+		} else if (!onUnhandled || !onUnhandled()) {
+			this.walkExpression(expression.object);
+		}
+		if (expression.computed === true) this.walkExpression(expression.property);
+	}
+
+	/**
+	 * @param {ThisExpression} expression this expression
+	 */
+	walkThisExpression(expression) {
+		this.callHooksForName(this.hooks.expression, "this", expression);
+	}
+
+	/**
+	 * @param {Identifier} expression identifier
+	 */
+	walkIdentifier(expression) {
+		this.callHooksForName(this.hooks.expression, expression.name, expression);
+	}
+
+	/**
+	 * @param {MetaProperty} metaProperty meta property
+	 */
+	walkMetaProperty(metaProperty) {
+		this.hooks.expression.for(getRootName(metaProperty)).call(metaProperty);
+	}
+
+	/**
+	 * @template T
+	 * @template R
+	 * @param {HookMap<SyncBailHook<T, R>>} hookMap hooks the should be called
+	 * @param {Expression | Super} expr expression
+	 * @param {AsArray<T>} args args for the hook
+	 * @returns {R | undefined} result of hook
+	 */
+	callHooksForExpression(hookMap, expr, ...args) {
+		return this.callHooksForExpressionWithFallback(
+			hookMap,
+			expr,
+			undefined,
+			undefined,
+			...args
+		);
+	}
+
+	/**
+	 * @template T
+	 * @template R
+	 * @param {HookMap<SyncBailHook<T, R>>} hookMap hooks the should be called
+	 * @param {Expression | Super} expr expression info
+	 * @param {(function(string, string | ScopeInfo | VariableInfo, function(): string[]): any) | undefined} fallback callback when variable in not handled by hooks
+	 * @param {(function(string): any) | undefined} defined callback when variable is defined
+	 * @param {AsArray<T>} args args for the hook
+	 * @returns {R | undefined} result of hook
+	 */
+	callHooksForExpressionWithFallback(
+		hookMap,
+		expr,
+		fallback,
+		defined,
+		...args
+	) {
+		const exprName = this.getMemberExpressionInfo(
+			expr,
+			ALLOWED_MEMBER_TYPES_EXPRESSION
+		);
+		if (exprName !== undefined) {
+			const members = exprName.getMembers();
+			return this.callHooksForInfoWithFallback(
+				hookMap,
+				members.length === 0 ? exprName.rootInfo : exprName.name,
+				fallback &&
+					(name => fallback(name, exprName.rootInfo, exprName.getMembers)),
+				defined && (() => defined(exprName.name)),
+				...args
+			);
+		}
+	}
+
+	/**
+	 * @template T
+	 * @template R
+	 * @param {HookMap<SyncBailHook<T, R>>} hookMap hooks the should be called
+	 * @param {string} name key in map
+	 * @param {AsArray<T>} args args for the hook
+	 * @returns {R | undefined} result of hook
+	 */
+	callHooksForName(hookMap, name, ...args) {
+		return this.callHooksForNameWithFallback(
+			hookMap,
+			name,
+			undefined,
+			undefined,
+			...args
+		);
+	}
+
+	/**
+	 * @template T
+	 * @template R
+	 * @param {HookMap<SyncBailHook<T, R>>} hookMap hooks that should be called
+	 * @param {ExportedVariableInfo} info variable info
+	 * @param  {AsArray<T>} args args for the hook
+	 * @returns {R | undefined} result of hook
+	 */
+	callHooksForInfo(hookMap, info, ...args) {
+		return this.callHooksForInfoWithFallback(
+			hookMap,
+			info,
+			undefined,
+			undefined,
+			...args
+		);
+	}
+
+	/**
+	 * @template T
+	 * @template R
+	 * @param {HookMap<SyncBailHook<T, R>>} hookMap hooks the should be called
+	 * @param {ExportedVariableInfo} info variable info
+	 * @param {(function(string): any) | undefined} fallback callback when variable in not handled by hooks
+	 * @param {(function(string=): any) | undefined} defined callback when variable is defined
+	 * @param {AsArray<T>} args args for the hook
+	 * @returns {R | undefined} result of hook
+	 */
+	callHooksForInfoWithFallback(hookMap, info, fallback, defined, ...args) {
+		let name;
+		if (typeof info === "string") {
+			name = info;
+		} else {
+			if (!(info instanceof VariableInfo)) {
+				if (defined !== undefined) {
+					return defined();
+				}
+				return;
+			}
+			let tagInfo = info.tagInfo;
+			while (tagInfo !== undefined) {
+				const hook = hookMap.get(tagInfo.tag);
+				if (hook !== undefined) {
+					this.currentTagData = tagInfo.data;
+					const result = hook.call(...args);
+					this.currentTagData = undefined;
+					if (result !== undefined) return result;
+				}
+				tagInfo = tagInfo.next;
+			}
+			if (info.freeName === true) {
+				if (defined !== undefined) {
+					return defined();
+				}
+				return;
+			}
+			name = info.freeName;
+		}
+		const hook = hookMap.get(name);
+		if (hook !== undefined) {
+			const result = hook.call(...args);
+			if (result !== undefined) return result;
+		}
+		if (fallback !== undefined) {
+			return fallback(/** @type {string} */ (name));
+		}
+	}
+
+	/**
+	 * @template T
+	 * @template R
+	 * @param {HookMap<SyncBailHook<T, R>>} hookMap hooks the should be called
+	 * @param {string} name key in map
+	 * @param {(function(string): any) | undefined} fallback callback when variable in not handled by hooks
+	 * @param {(function(): any) | undefined} defined callback when variable is defined
+	 * @param {AsArray<T>} args args for the hook
+	 * @returns {R | undefined} result of hook
+	 */
+	callHooksForNameWithFallback(hookMap, name, fallback, defined, ...args) {
+		return this.callHooksForInfoWithFallback(
+			hookMap,
+			this.getVariableInfo(name),
+			fallback,
+			defined,
+			...args
+		);
+	}
+
+	/**
+	 * @deprecated
+	 * @param {any} params scope params
+	 * @param {function(): void} fn inner function
+	 * @returns {void}
+	 */
+	inScope(params, fn) {
+		const oldScope = this.scope;
+		this.scope = {
+			topLevelScope: oldScope.topLevelScope,
+			inTry: false,
+			inShorthand: false,
+			inTaggedTemplateTag: false,
+			isStrict: oldScope.isStrict,
+			isAsmJs: oldScope.isAsmJs,
+			definitions: oldScope.definitions.createChild()
+		};
+
+		this.undefineVariable("this");
+
+		this.enterPatterns(params, ident => {
+			this.defineVariable(ident);
+		});
+
+		fn();
+
+		this.scope = oldScope;
+	}
+
+	/**
+	 * @param {boolean} hasThis true, when this is defined
+	 * @param {Identifier[]} params scope params
+	 * @param {function(): void} fn inner function
+	 * @returns {void}
+	 */
+	inClassScope(hasThis, params, fn) {
+		const oldScope = this.scope;
+		this.scope = {
+			topLevelScope: oldScope.topLevelScope,
+			inTry: false,
+			inShorthand: false,
+			inTaggedTemplateTag: false,
+			isStrict: oldScope.isStrict,
+			isAsmJs: oldScope.isAsmJs,
+			definitions: oldScope.definitions.createChild()
+		};
+
+		if (hasThis) {
+			this.undefineVariable("this");
+		}
+
+		this.enterPatterns(params, ident => {
+			this.defineVariable(ident);
+		});
+
+		fn();
+
+		this.scope = oldScope;
+	}
+
+	/**
+	 * @param {boolean} hasThis true, when this is defined
+	 * @param {(Pattern | string)[]} params scope params
+	 * @param {function(): void} fn inner function
+	 * @returns {void}
+	 */
+	inFunctionScope(hasThis, params, fn) {
+		const oldScope = this.scope;
+		this.scope = {
+			topLevelScope: oldScope.topLevelScope,
+			inTry: false,
+			inShorthand: false,
+			inTaggedTemplateTag: false,
+			isStrict: oldScope.isStrict,
+			isAsmJs: oldScope.isAsmJs,
+			definitions: oldScope.definitions.createChild()
+		};
+
+		if (hasThis) {
+			this.undefineVariable("this");
+		}
+
+		this.enterPatterns(params, ident => {
+			this.defineVariable(ident);
+		});
+
+		fn();
+
+		this.scope = oldScope;
+	}
+
+	/**
+	 * @param {function(): void} fn inner function
+	 * @returns {void}
+	 */
+	inBlockScope(fn) {
+		const oldScope = this.scope;
+		this.scope = {
+			topLevelScope: oldScope.topLevelScope,
+			inTry: oldScope.inTry,
+			inShorthand: false,
+			inTaggedTemplateTag: false,
+			isStrict: oldScope.isStrict,
+			isAsmJs: oldScope.isAsmJs,
+			definitions: oldScope.definitions.createChild()
+		};
+
+		fn();
+
+		this.scope = oldScope;
+	}
+
+	/**
+	 * @param {Array<Directive | Statement | ModuleDeclaration>} statements statements
+	 */
+	detectMode(statements) {
+		const isLiteral =
+			statements.length >= 1 &&
+			statements[0].type === "ExpressionStatement" &&
+			statements[0].expression.type === "Literal";
+		if (
+			isLiteral &&
+			/** @type {Literal} */
+			(/** @type {ExpressionStatement} */ (statements[0]).expression).value ===
+				"use strict"
+		) {
+			this.scope.isStrict = true;
+		}
+		if (
+			isLiteral &&
+			/** @type {Literal} */
+			(/** @type {ExpressionStatement} */ (statements[0]).expression).value ===
+				"use asm"
+		) {
+			this.scope.isAsmJs = true;
+		}
+	}
+
+	/**
+	 * @param {(string | Pattern | Property)[]} patterns patterns
+	 * @param {OnIdentString} onIdent on ident callback
+	 */
+	enterPatterns(patterns, onIdent) {
+		for (const pattern of patterns) {
+			if (typeof pattern !== "string") {
+				this.enterPattern(pattern, onIdent);
+			} else if (pattern) {
+				onIdent(pattern);
+			}
+		}
+	}
+
+	/**
+	 * @param {Pattern | Property} pattern pattern
+	 * @param {OnIdent} onIdent on ident callback
+	 */
+	enterPattern(pattern, onIdent) {
+		if (!pattern) return;
+		switch (pattern.type) {
+			case "ArrayPattern":
+				this.enterArrayPattern(pattern, onIdent);
+				break;
+			case "AssignmentPattern":
+				this.enterAssignmentPattern(pattern, onIdent);
+				break;
+			case "Identifier":
+				this.enterIdentifier(pattern, onIdent);
+				break;
+			case "ObjectPattern":
+				this.enterObjectPattern(pattern, onIdent);
+				break;
+			case "RestElement":
+				this.enterRestElement(pattern, onIdent);
+				break;
+			case "Property":
+				if (pattern.shorthand && pattern.value.type === "Identifier") {
+					this.scope.inShorthand = pattern.value.name;
+					this.enterIdentifier(pattern.value, onIdent);
+					this.scope.inShorthand = false;
+				} else {
+					this.enterPattern(/** @type {Pattern} */ (pattern.value), onIdent);
+				}
+				break;
+		}
+	}
+
+	/**
+	 * @param {Identifier} pattern identifier pattern
+	 * @param {OnIdent} onIdent callback
+	 */
+	enterIdentifier(pattern, onIdent) {
+		if (!this.callHooksForName(this.hooks.pattern, pattern.name, pattern)) {
+			onIdent(pattern.name, pattern);
+		}
+	}
+
+	/**
+	 * @param {ObjectPattern} pattern object pattern
+	 * @param {OnIdent} onIdent callback
+	 */
+	enterObjectPattern(pattern, onIdent) {
+		for (
+			let propIndex = 0, len = pattern.properties.length;
+			propIndex < len;
+			propIndex++
+		) {
+			const prop = pattern.properties[propIndex];
+			this.enterPattern(prop, onIdent);
+		}
+	}
+
+	/**
+	 * @param {ArrayPattern} pattern object pattern
+	 * @param {OnIdent} onIdent callback
+	 */
+	enterArrayPattern(pattern, onIdent) {
+		for (
+			let elementIndex = 0, len = pattern.elements.length;
+			elementIndex < len;
+			elementIndex++
+		) {
+			const element = pattern.elements[elementIndex];
+
+			if (element) {
+				this.enterPattern(element, onIdent);
+			}
+		}
+	}
+
+	/**
+	 * @param {RestElement} pattern object pattern
+	 * @param {OnIdent} onIdent callback
+	 */
+	enterRestElement(pattern, onIdent) {
+		this.enterPattern(pattern.argument, onIdent);
+	}
+
+	/**
+	 * @param {AssignmentPattern} pattern object pattern
+	 * @param {OnIdent} onIdent callback
+	 */
+	enterAssignmentPattern(pattern, onIdent) {
+		this.enterPattern(pattern.left, onIdent);
+	}
+
+	/**
+	 * @param {Expression | SpreadElement | PrivateIdentifier} expression expression node
+	 * @returns {BasicEvaluatedExpression} evaluation result
+	 */
+	evaluateExpression(expression) {
+		try {
+			const hook = this.hooks.evaluate.get(expression.type);
+			if (hook !== undefined) {
+				const result = hook.call(expression);
+				if (result !== undefined && result !== null) {
+					result.setExpression(expression);
+					return result;
+				}
+			}
+		} catch (err) {
+			console.warn(err);
+			// ignore error
+		}
+		return new BasicEvaluatedExpression()
+			.setRange(/** @type {Range} */ (expression.range))
+			.setExpression(expression);
+	}
+
+	/**
+	 * @param {Expression} expression expression
+	 * @returns {string} parsed string
+	 */
+	parseString(expression) {
+		switch (expression.type) {
+			case "BinaryExpression":
+				if (expression.operator === "+") {
+					return (
+						this.parseString(/** @type {Expression} */ (expression.left)) +
+						this.parseString(expression.right)
+					);
+				}
+				break;
+			case "Literal":
+				return String(expression.value);
+		}
+		throw new Error(
+			`${expression.type} is not supported as parameter for require`
+		);
+	}
+
+	/**
+	 * @param {Expression} expression expression
+	 * @returns {{ range?: Range, value: string, code: boolean, conditional: TODO }} result
+	 */
+	parseCalculatedString(expression) {
+		switch (expression.type) {
+			case "BinaryExpression":
+				if (expression.operator === "+") {
+					const left = this.parseCalculatedString(
+						/** @type {Expression} */
+						(expression.left)
+					);
+					const right = this.parseCalculatedString(expression.right);
+					if (left.code) {
+						return {
+							range: left.range,
+							value: left.value,
+							code: true,
+							conditional: false
+						};
+					} else if (right.code) {
+						return {
+							range: [
+								/** @type {Range} */
+								(left.range)[0],
+								right.range
+									? right.range[1]
+									: /** @type {Range} */ (left.range)[1]
+							],
+							value: left.value + right.value,
+							code: true,
+							conditional: false
+						};
+					}
+					return {
+						range: [
+							/** @type {Range} */
+							(left.range)[0],
+							/** @type {Range} */
+							(right.range)[1]
+						],
+						value: left.value + right.value,
+						code: false,
+						conditional: false
+					};
+				}
+				break;
+			case "ConditionalExpression": {
+				const consequent = this.parseCalculatedString(expression.consequent);
+				const alternate = this.parseCalculatedString(expression.alternate);
+				const items = [];
+				if (consequent.conditional) {
+					items.push(...consequent.conditional);
+				} else if (!consequent.code) {
+					items.push(consequent);
+				} else {
+					break;
+				}
+				if (alternate.conditional) {
+					items.push(...alternate.conditional);
+				} else if (!alternate.code) {
+					items.push(alternate);
+				} else {
+					break;
+				}
+				return {
+					range: undefined,
+					value: "",
+					code: true,
+					conditional: items
+				};
+			}
+			case "Literal":
+				return {
+					range: expression.range,
+					value: String(expression.value),
+					code: false,
+					conditional: false
+				};
+		}
+		return {
+			range: undefined,
+			value: "",
+			code: true,
+			conditional: false
+		};
+	}
+
+	/**
+	 * @param {string | Buffer | PreparsedAst} source the source to parse
+	 * @param {ParserState} state the parser state
+	 * @returns {ParserState} the parser state
+	 */
+	parse(source, state) {
+		let ast;
+		/** @type {import("acorn").Comment[]} */
+		let comments;
+		const semicolons = new Set();
+		if (source === null) {
+			throw new Error("source must not be null");
+		}
+		if (Buffer.isBuffer(source)) {
+			source = source.toString("utf-8");
+		}
+		if (typeof source === "object") {
+			ast = /** @type {Program} */ (source);
+			comments = source.comments;
+		} else {
+			comments = [];
+			ast = JavascriptParser._parse(source, {
+				sourceType: this.sourceType,
+				onComment: comments,
+				onInsertedSemicolon: pos => semicolons.add(pos)
+			});
+		}
+
+		const oldScope = this.scope;
+		const oldState = this.state;
+		const oldComments = this.comments;
+		const oldSemicolons = this.semicolons;
+		const oldStatementPath = this.statementPath;
+		const oldPrevStatement = this.prevStatement;
+		this.scope = {
+			topLevelScope: true,
+			inTry: false,
+			inShorthand: false,
+			inTaggedTemplateTag: false,
+			isStrict: false,
+			isAsmJs: false,
+			definitions: new StackedMap()
+		};
+		/** @type {ParserState} */
+		this.state = state;
+		this.comments = comments;
+		this.semicolons = semicolons;
+		this.statementPath = [];
+		this.prevStatement = undefined;
+		if (this.hooks.program.call(ast, comments) === undefined) {
+			this.destructuringAssignmentProperties = new WeakMap();
+			this.detectMode(ast.body);
+			this.preWalkStatements(ast.body);
+			this.prevStatement = undefined;
+			this.blockPreWalkStatements(ast.body);
+			this.prevStatement = undefined;
+			this.walkStatements(ast.body);
+			this.destructuringAssignmentProperties = undefined;
+		}
+		this.hooks.finish.call(ast, comments);
+		this.scope = oldScope;
+		/** @type {ParserState} */
+		this.state = oldState;
+		this.comments = oldComments;
+		this.semicolons = oldSemicolons;
+		this.statementPath = oldStatementPath;
+		this.prevStatement = oldPrevStatement;
+		return state;
+	}
+
+	/**
+	 * @param {string} source source code
+	 * @returns {BasicEvaluatedExpression} evaluation result
+	 */
+	evaluate(source) {
+		const ast = JavascriptParser._parse(`(${source})`, {
+			sourceType: this.sourceType,
+			locations: false
+		});
+		if (ast.body.length !== 1 || ast.body[0].type !== "ExpressionStatement") {
+			throw new Error("evaluate: Source is not a expression");
+		}
+		return this.evaluateExpression(ast.body[0].expression);
+	}
+
+	/**
+	 * @param {Expression | Declaration | PrivateIdentifier | null | undefined} expr an expression
+	 * @param {number} commentsStartPos source position from which annotation comments are checked
+	 * @returns {boolean} true, when the expression is pure
+	 */
+	isPure(expr, commentsStartPos) {
+		if (!expr) return true;
+		const result = this.hooks.isPure
+			.for(expr.type)
+			.call(expr, commentsStartPos);
+		if (typeof result === "boolean") return result;
+		switch (expr.type) {
+			// TODO handle more cases
+			case "ClassDeclaration":
+			case "ClassExpression": {
+				if (expr.body.type !== "ClassBody") return false;
+				if (
+					expr.superClass &&
+					!this.isPure(expr.superClass, /** @type {Range} */ (expr.range)[0])
+				) {
+					return false;
+				}
+				const items =
+					/** @type {TODO[]} */
+					(expr.body.body);
+				return items.every(item => {
+					if (
+						item.computed &&
+						item.key &&
+						!this.isPure(item.key, item.range[0])
+					) {
+						return false;
+					}
+
+					if (
+						item.static &&
+						item.value &&
+						!this.isPure(
+							item.value,
+							item.key ? item.key.range[1] : item.range[0]
+						)
+					) {
+						return false;
+					}
+
+					if (item.type === "StaticBlock") {
+						return false;
+					}
+
+					if (
+						expr.superClass &&
+						item.type === "MethodDefinition" &&
+						item.kind === "constructor"
+					) {
+						return false;
+					}
+
+					return true;
+				});
+			}
+
+			case "FunctionDeclaration":
+			case "FunctionExpression":
+			case "ArrowFunctionExpression":
+			case "ThisExpression":
+			case "Literal":
+			case "TemplateLiteral":
+			case "Identifier":
+			case "PrivateIdentifier":
+				return true;
+
+			case "VariableDeclaration":
+				return expr.declarations.every(decl =>
+					this.isPure(decl.init, /** @type {Range} */ (decl.range)[0])
+				);
+
+			case "ConditionalExpression":
+				return (
+					this.isPure(expr.test, commentsStartPos) &&
+					this.isPure(
+						expr.consequent,
+						/** @type {Range} */ (expr.test.range)[1]
+					) &&
+					this.isPure(
+						expr.alternate,
+						/** @type {Range} */ (expr.consequent.range)[1]
+					)
+				);
+
+			case "LogicalExpression":
+				return (
+					this.isPure(expr.left, commentsStartPos) &&
+					this.isPure(expr.right, /** @type {Range} */ (expr.left.range)[1])
+				);
+
+			case "SequenceExpression":
+				return expr.expressions.every(expr => {
+					const pureFlag = this.isPure(expr, commentsStartPos);
+					commentsStartPos = /** @type {Range} */ (expr.range)[1];
+					return pureFlag;
+				});
+
+			case "CallExpression": {
+				const pureFlag =
+					/** @type {Range} */ (expr.range)[0] - commentsStartPos > 12 &&
+					this.getComments([
+						commentsStartPos,
+						/** @type {Range} */ (expr.range)[0]
+					]).some(
+						comment =>
+							comment.type === "Block" &&
+							/^\s*(#|@)__PURE__\s*$/.test(comment.value)
+					);
+				if (!pureFlag) return false;
+				commentsStartPos = /** @type {Range} */ (expr.callee.range)[1];
+				return expr.arguments.every(arg => {
+					if (arg.type === "SpreadElement") return false;
+					const pureFlag = this.isPure(arg, commentsStartPos);
+					commentsStartPos = /** @type {Range} */ (arg.range)[1];
+					return pureFlag;
+				});
+			}
+		}
+		const evaluated = this.evaluateExpression(expr);
+		return !evaluated.couldHaveSideEffects();
+	}
+
+	/**
+	 * @param {Range} range range
+	 * @returns {Comment[]} comments in the range
+	 */
+	getComments(range) {
+		const [rangeStart, rangeEnd] = range;
+		/**
+		 * @param {Comment} comment comment
+		 * @param {number} needle needle
+		 * @returns {number} compared
+		 */
+		const compare = (comment, needle) =>
+			/** @type {Range} */ (comment.range)[0] - needle;
+		const comments = /** @type {Comment[]} */ (this.comments);
+		let idx = binarySearchBounds.ge(comments, rangeStart, compare);
+		/** @type {Comment[]} */
+		const commentsInRange = [];
+		while (
+			comments[idx] &&
+			/** @type {Range} */ (comments[idx].range)[1] <= rangeEnd
+		) {
+			commentsInRange.push(comments[idx]);
+			idx++;
+		}
+
+		return commentsInRange;
+	}
+
+	/**
+	 * @param {number} pos source code position
+	 * @returns {boolean} true when a semicolon has been inserted before this position, false if not
+	 */
+	isAsiPosition(pos) {
+		const currentStatement =
+			/** @type {StatementPath} */
+			(this.statementPath)[
+				/** @type {StatementPath} */
+				(this.statementPath).length - 1
+			];
+		if (currentStatement === undefined) throw new Error("Not in statement");
+		const range = /** @type {Range} */ (currentStatement.range);
+
+		return (
+			// Either asking directly for the end position of the current statement
+			(range[1] === pos &&
+				/** @type {Set<number>} */ (this.semicolons).has(pos)) ||
+			// Or asking for the start position of the current statement,
+			// here we have to check multiple things
+			(range[0] === pos &&
+				// is there a previous statement which might be relevant?
+				this.prevStatement !== undefined &&
+				// is the end position of the previous statement an ASI position?
+				/** @type {Set<number>} */ (this.semicolons).has(
+					/** @type {Range} */ (this.prevStatement.range)[1]
+				))
+		);
+	}
+
+	/**
+	 * @param {number} pos source code position
+	 * @returns {void}
+	 */
+	setAsiPosition(pos) {
+		/** @type {Set<number>} */ (this.semicolons).add(pos);
+	}
+
+	/**
+	 * @param {number} pos source code position
+	 * @returns {void}
+	 */
+	unsetAsiPosition(pos) {
+		/** @type {Set<number>} */ (this.semicolons).delete(pos);
+	}
+
+	/**
+	 * @param {Expression} expr expression
+	 * @returns {boolean} true, when the expression is a statement level expression
+	 */
+	isStatementLevelExpression(expr) {
+		const currentStatement =
+			/** @type {StatementPath} */
+			(this.statementPath)[
+				/** @type {StatementPath} */
+				(this.statementPath).length - 1
+			];
+		return (
+			expr === currentStatement ||
+			(currentStatement.type === "ExpressionStatement" &&
+				currentStatement.expression === expr)
+		);
+	}
+
+	/**
+	 * @param {string} name name
+	 * @param {symbol} tag tag info
+	 * @returns {TODO} tag data
+	 */
+	getTagData(name, tag) {
+		const info = this.scope.definitions.get(name);
+		if (info instanceof VariableInfo) {
+			let tagInfo = info.tagInfo;
+			while (tagInfo !== undefined) {
+				if (tagInfo.tag === tag) return tagInfo.data;
+				tagInfo = tagInfo.next;
+			}
+		}
+	}
+
+	/**
+	 * @param {string} name name
+	 * @param {symbol} tag tag info
+	 * @param {TODO=} data data
+	 */
+	tagVariable(name, tag, data) {
+		const oldInfo = this.scope.definitions.get(name);
+		/** @type {VariableInfo} */
+		let newInfo;
+		if (oldInfo === undefined) {
+			newInfo = new VariableInfo(this.scope, name, {
+				tag,
+				data,
+				next: undefined
+			});
+		} else if (oldInfo instanceof VariableInfo) {
+			newInfo = new VariableInfo(oldInfo.declaredScope, oldInfo.freeName, {
+				tag,
+				data,
+				next: oldInfo.tagInfo
+			});
+		} else {
+			newInfo = new VariableInfo(oldInfo, true, {
+				tag,
+				data,
+				next: undefined
+			});
+		}
+		this.scope.definitions.set(name, newInfo);
+	}
+
+	/**
+	 * @param {string} name variable name
+	 */
+	defineVariable(name) {
+		const oldInfo = this.scope.definitions.get(name);
+		// Don't redefine variable in same scope to keep existing tags
+		if (oldInfo instanceof VariableInfo && oldInfo.declaredScope === this.scope)
+			return;
+		this.scope.definitions.set(name, this.scope);
+	}
+
+	/**
+	 * @param {string} name variable name
+	 */
+	undefineVariable(name) {
+		this.scope.definitions.delete(name);
+	}
+
+	/**
+	 * @param {string} name variable name
+	 * @returns {boolean} true, when variable is defined
+	 */
+	isVariableDefined(name) {
+		const info = this.scope.definitions.get(name);
+		if (info === undefined) return false;
+		if (info instanceof VariableInfo) {
+			return info.freeName === true;
+		}
+		return true;
+	}
+
+	/**
+	 * @param {string} name variable name
+	 * @returns {string | ExportedVariableInfo} info for this variable
+	 */
+	getVariableInfo(name) {
+		const value = this.scope.definitions.get(name);
+		if (value === undefined) {
+			return name;
+		}
+		return value;
+	}
+
+	/**
+	 * @param {string} name variable name
+	 * @param {string | ExportedVariableInfo} variableInfo new info for this variable
+	 * @returns {void}
+	 */
+	setVariable(name, variableInfo) {
+		if (typeof variableInfo === "string") {
+			if (variableInfo === name) {
+				this.scope.definitions.delete(name);
+			} else {
+				this.scope.definitions.set(
+					name,
+					new VariableInfo(this.scope, variableInfo, undefined)
+				);
+			}
+		} else {
+			this.scope.definitions.set(name, variableInfo);
+		}
+	}
+
+	/**
+	 * @param {TagInfo} tagInfo tag info
+	 * @returns {VariableInfo} variable info
+	 */
+	evaluatedVariable(tagInfo) {
+		return new VariableInfo(this.scope, undefined, tagInfo);
+	}
+
+	/**
+	 * @param {Range} range range of the comment
+	 * @returns {{ options: Record<string, any> | null, errors: (Error & { comment: Comment })[] | null }} result
+	 */
+	parseCommentOptions(range) {
+		const comments = this.getComments(range);
+		if (comments.length === 0) {
+			return EMPTY_COMMENT_OPTIONS;
+		}
+		/** @type {Record<string, EXPECTED_ANY> } */
+		const options = {};
+		/** @type {(Error & { comment: Comment })[]} */
+		const errors = [];
+		for (const comment of comments) {
+			const { value } = comment;
+			if (value && webpackCommentRegExp.test(value)) {
+				// try compile only if webpack options comment is present
+				try {
+					for (let [key, val] of Object.entries(
+						vm.runInContext(
+							`(function(){return {${value}};})()`,
+							this.magicCommentContext
+						)
+					)) {
+						if (typeof val === "object" && val !== null) {
+							val =
+								val.constructor.name === "RegExp"
+									? new RegExp(val)
+									: JSON.parse(JSON.stringify(val));
+						}
+						options[key] = val;
+					}
+				} catch (err) {
+					const newErr = new Error(String(/** @type {Error} */ (err).message));
+					newErr.stack = String(/** @type {Error} */ (err).stack);
+					Object.assign(newErr, { comment });
+					errors.push(/** @type {(Error & { comment: Comment })} */ (newErr));
+				}
+			}
+		}
+		return { options, errors };
+	}
+
+	/**
+	 * @param {Expression | Super} expression a member expression
+	 * @returns {{ members: string[], object: Expression | Super, membersOptionals: boolean[], memberRanges: Range[] }} member names (reverse order) and remaining object
+	 */
+	extractMemberExpressionChain(expression) {
+		/** @type {Node} */
+		let expr = expression;
+		const members = [];
+		const membersOptionals = [];
+		const memberRanges = [];
+		while (expr.type === "MemberExpression") {
+			if (expr.computed) {
+				if (expr.property.type !== "Literal") break;
+				members.push(`${expr.property.value}`); // the literal
+				memberRanges.push(/** @type {Range} */ (expr.object.range)); // the range of the expression fragment before the literal
+			} else {
+				if (expr.property.type !== "Identifier") break;
+				members.push(expr.property.name); // the identifier
+				memberRanges.push(/** @type {Range} */ (expr.object.range)); // the range of the expression fragment before the identifier
+			}
+			membersOptionals.push(expr.optional);
+			expr = expr.object;
+		}
+
+		return {
+			members,
+			membersOptionals,
+			memberRanges,
+			object: expr
+		};
+	}
+
+	/**
+	 * @param {string} varName variable name
+	 * @returns {{name: string, info: VariableInfo | string} | undefined} name of the free variable and variable info for that
+	 */
+	getFreeInfoFromVariable(varName) {
+		const info = this.getVariableInfo(varName);
+		let name;
+		if (info instanceof VariableInfo) {
+			name = info.freeName;
+			if (typeof name !== "string") return;
+		} else if (typeof info !== "string") {
+			return;
+		} else {
+			name = info;
+		}
+		return { info, name };
+	}
+
+	/** @typedef {{ type: "call", call: CallExpression, calleeName: string, rootInfo: string | VariableInfo, getCalleeMembers: () => string[], name: string, getMembers: () => string[], getMembersOptionals: () => boolean[], getMemberRanges: () => Range[]}} CallExpressionInfo */
+	/** @typedef {{ type: "expression", rootInfo: string | VariableInfo, name: string, getMembers: () => string[], getMembersOptionals: () => boolean[], getMemberRanges: () => Range[]}} ExpressionExpressionInfo */
+
+	/**
+	 * @param {Expression | Super} expression a member expression
+	 * @param {number} allowedTypes which types should be returned, presented in bit mask
+	 * @returns {CallExpressionInfo | ExpressionExpressionInfo | undefined} expression info
+	 */
+	getMemberExpressionInfo(expression, allowedTypes) {
+		const { object, members, membersOptionals, memberRanges } =
+			this.extractMemberExpressionChain(expression);
+		switch (object.type) {
+			case "CallExpression": {
+				if ((allowedTypes & ALLOWED_MEMBER_TYPES_CALL_EXPRESSION) === 0) return;
+				let callee = object.callee;
+				let rootMembers = EMPTY_ARRAY;
+				if (callee.type === "MemberExpression") {
+					({ object: callee, members: rootMembers } =
+						this.extractMemberExpressionChain(callee));
+				}
+				const rootName = getRootName(callee);
+				if (!rootName) return;
+				const result = this.getFreeInfoFromVariable(rootName);
+				if (!result) return;
+				const { info: rootInfo, name: resolvedRoot } = result;
+				const calleeName = objectAndMembersToName(resolvedRoot, rootMembers);
+				return {
+					type: "call",
+					call: object,
+					calleeName,
+					rootInfo,
+					getCalleeMembers: memoize(() => rootMembers.reverse()),
+					name: objectAndMembersToName(`${calleeName}()`, members),
+					getMembers: memoize(() => members.reverse()),
+					getMembersOptionals: memoize(() => membersOptionals.reverse()),
+					getMemberRanges: memoize(() => memberRanges.reverse())
+				};
+			}
+			case "Identifier":
+			case "MetaProperty":
+			case "ThisExpression": {
+				if ((allowedTypes & ALLOWED_MEMBER_TYPES_EXPRESSION) === 0) return;
+				const rootName = getRootName(object);
+				if (!rootName) return;
+
+				const result = this.getFreeInfoFromVariable(rootName);
+				if (!result) return;
+				const { info: rootInfo, name: resolvedRoot } = result;
+				return {
+					type: "expression",
+					name: objectAndMembersToName(resolvedRoot, members),
+					rootInfo,
+					getMembers: memoize(() => members.reverse()),
+					getMembersOptionals: memoize(() => membersOptionals.reverse()),
+					getMemberRanges: memoize(() => memberRanges.reverse())
+				};
+			}
+		}
+	}
+
+	/**
+	 * @param {MemberExpression} expression an expression
+	 * @returns {{ name: string, rootInfo: ExportedVariableInfo, getMembers: () => string[]} | undefined} name info
+	 */
+	getNameForExpression(expression) {
+		return this.getMemberExpressionInfo(
+			expression,
+			ALLOWED_MEMBER_TYPES_EXPRESSION
+		);
+	}
+
+	/**
+	 * @param {string} code source code
+	 * @param {ParseOptions} options parsing options
+	 * @returns {Program} parsed ast
+	 */
+	static _parse(code, options) {
+		const type = options ? options.sourceType : "module";
+		/** @type {AcornOptions} */
+		const parserOptions = {
+			...defaultParserOptions,
+			allowReturnOutsideFunction: type === "script",
+			...options,
+			sourceType: type === "auto" ? "module" : type
+		};
+
+		/** @type {import("acorn").Program | undefined} */
+		let ast;
+		let error;
+		let threw = false;
+		try {
+			ast = parser.parse(code, parserOptions);
+		} catch (err) {
+			error = err;
+			threw = true;
+		}
+
+		if (threw && type === "auto") {
+			parserOptions.sourceType = "script";
+			if (!("allowReturnOutsideFunction" in options)) {
+				parserOptions.allowReturnOutsideFunction = true;
+			}
+			if (Array.isArray(parserOptions.onComment)) {
+				parserOptions.onComment.length = 0;
+			}
+			try {
+				ast = parser.parse(code, parserOptions);
+				threw = false;
+			} catch (_err) {
+				// we use the error from first parse try
+				// so nothing to do here
+			}
+		}
+
+		if (threw) {
+			throw error;
+		}
+
+		return /** @type {Program} */ (ast);
+	}
+}
+
+module.exports = JavascriptParser;
+module.exports.ALLOWED_MEMBER_TYPES_ALL = ALLOWED_MEMBER_TYPES_ALL;
+module.exports.ALLOWED_MEMBER_TYPES_EXPRESSION =
+	ALLOWED_MEMBER_TYPES_EXPRESSION;
+module.exports.ALLOWED_MEMBER_TYPES_CALL_EXPRESSION =
+	ALLOWED_MEMBER_TYPES_CALL_EXPRESSION;
+module.exports.getImportAttributes = getImportAttributes;
+module.exports.VariableInfo = VariableInfo;
Index: imaps-frontend/node_modules/webpack/lib/javascript/JavascriptParserHelpers.js
===================================================================
--- imaps-frontend/node_modules/webpack/lib/javascript/JavascriptParserHelpers.js	(revision 79a03170cdde4e801d12257ee86723774970427b)
+++ imaps-frontend/node_modules/webpack/lib/javascript/JavascriptParserHelpers.js	(revision 79a03170cdde4e801d12257ee86723774970427b)
@@ -0,0 +1,128 @@
+/*
+	MIT License http://www.opensource.org/licenses/mit-license.php
+	Author Tobias Koppers @sokra
+*/
+
+"use strict";
+
+const UnsupportedFeatureWarning = require("../UnsupportedFeatureWarning");
+const ConstDependency = require("../dependencies/ConstDependency");
+const BasicEvaluatedExpression = require("./BasicEvaluatedExpression");
+
+/** @typedef {import("estree").Expression} Expression */
+/** @typedef {import("estree").Node} Node */
+/** @typedef {import("estree").SourceLocation} SourceLocation */
+/** @typedef {import("./JavascriptParser")} JavascriptParser */
+/** @typedef {import("./JavascriptParser").Range} Range */
+
+/**
+ * @param {JavascriptParser} parser the parser
+ * @param {string} value the const value
+ * @param {(string[] | null)=} runtimeRequirements runtime requirements
+ * @returns {function(Expression): true} plugin function
+ */
+module.exports.toConstantDependency = (parser, value, runtimeRequirements) =>
+	function constDependency(expr) {
+		const dep = new ConstDependency(
+			value,
+			/** @type {Range} */ (expr.range),
+			runtimeRequirements
+		);
+		dep.loc = /** @type {SourceLocation} */ (expr.loc);
+		parser.state.module.addPresentationalDependency(dep);
+		return true;
+	};
+
+/**
+ * @param {string} value the string value
+ * @returns {function(Expression): BasicEvaluatedExpression} plugin function
+ */
+module.exports.evaluateToString = value =>
+	function stringExpression(expr) {
+		return new BasicEvaluatedExpression()
+			.setString(value)
+			.setRange(/** @type {Range} */ (expr.range));
+	};
+
+/**
+ * @param {number} value the number value
+ * @returns {function(Expression): BasicEvaluatedExpression} plugin function
+ */
+module.exports.evaluateToNumber = value =>
+	function stringExpression(expr) {
+		return new BasicEvaluatedExpression()
+			.setNumber(value)
+			.setRange(/** @type {Range} */ (expr.range));
+	};
+
+/**
+ * @param {boolean} value the boolean value
+ * @returns {function(Expression): BasicEvaluatedExpression} plugin function
+ */
+module.exports.evaluateToBoolean = value =>
+	function booleanExpression(expr) {
+		return new BasicEvaluatedExpression()
+			.setBoolean(value)
+			.setRange(/** @type {Range} */ (expr.range));
+	};
+
+/**
+ * @param {string} identifier identifier
+ * @param {string} rootInfo rootInfo
+ * @param {function(): string[]} getMembers getMembers
+ * @param {boolean|null=} truthy is truthy, null if nullish
+ * @returns {function(Expression): BasicEvaluatedExpression} callback
+ */
+module.exports.evaluateToIdentifier = (
+	identifier,
+	rootInfo,
+	getMembers,
+	truthy
+) =>
+	function identifierExpression(expr) {
+		const evaluatedExpression = new BasicEvaluatedExpression()
+			.setIdentifier(identifier, rootInfo, getMembers)
+			.setSideEffects(false)
+			.setRange(/** @type {Range} */ (expr.range));
+		switch (truthy) {
+			case true:
+				evaluatedExpression.setTruthy();
+				break;
+			case null:
+				evaluatedExpression.setNullish(true);
+				break;
+			case false:
+				evaluatedExpression.setFalsy();
+				break;
+		}
+
+		return evaluatedExpression;
+	};
+
+/**
+ * @param {JavascriptParser} parser the parser
+ * @param {string} message the message
+ * @returns {function(Expression): boolean | undefined} callback to handle unsupported expression
+ */
+module.exports.expressionIsUnsupported = (parser, message) =>
+	function unsupportedExpression(expr) {
+		const dep = new ConstDependency(
+			"(void 0)",
+			/** @type {Range} */ (expr.range),
+			null
+		);
+		dep.loc = /** @type {SourceLocation} */ (expr.loc);
+		parser.state.module.addPresentationalDependency(dep);
+		if (!parser.state.module) return;
+		parser.state.module.addWarning(
+			new UnsupportedFeatureWarning(
+				message,
+				/** @type {SourceLocation} */ (expr.loc)
+			)
+		);
+		return true;
+	};
+
+module.exports.skipTraversal = () => true;
+
+module.exports.approve = () => true;
Index: imaps-frontend/node_modules/webpack/lib/javascript/StartupHelpers.js
===================================================================
--- imaps-frontend/node_modules/webpack/lib/javascript/StartupHelpers.js	(revision 79a03170cdde4e801d12257ee86723774970427b)
+++ imaps-frontend/node_modules/webpack/lib/javascript/StartupHelpers.js	(revision 79a03170cdde4e801d12257ee86723774970427b)
@@ -0,0 +1,177 @@
+/*
+	MIT License http://www.opensource.org/licenses/mit-license.php
+	Author Tobias Koppers @sokra
+*/
+
+"use strict";
+
+const RuntimeGlobals = require("../RuntimeGlobals");
+const Template = require("../Template");
+const { isSubset } = require("../util/SetHelpers");
+const { getAllChunks } = require("./ChunkHelpers");
+
+/** @typedef {import("../util/Hash")} Hash */
+/** @typedef {import("../Chunk")} Chunk */
+/** @typedef {import("../Chunk").ChunkId} ChunkId */
+/** @typedef {import("../Compilation")} Compilation */
+/** @typedef {import("../ChunkGraph")} ChunkGraph */
+/** @typedef {import("../ChunkGraph").ModuleId} ModuleId */
+/** @typedef {import("../Entrypoint")} Entrypoint */
+/** @typedef {import("../ChunkGraph").EntryModuleWithChunkGroup} EntryModuleWithChunkGroup */
+/** @typedef {import("../ChunkGroup")} ChunkGroup */
+/** @typedef {import("../RuntimeTemplate")} RuntimeTemplate */
+/** @typedef {(string|number)[]} EntryItem */
+
+const EXPORT_PREFIX = `var ${RuntimeGlobals.exports} = `;
+
+/** @typedef {Set<Chunk>} Chunks */
+/** @typedef {ModuleId[]} ModuleIds */
+
+/**
+ * @param {ChunkGraph} chunkGraph chunkGraph
+ * @param {RuntimeTemplate} runtimeTemplate runtimeTemplate
+ * @param {EntryModuleWithChunkGroup[]} entries entries
+ * @param {Chunk} chunk chunk
+ * @param {boolean} passive true: passive startup with on chunks loaded
+ * @returns {string} runtime code
+ */
+module.exports.generateEntryStartup = (
+	chunkGraph,
+	runtimeTemplate,
+	entries,
+	chunk,
+	passive
+) => {
+	/** @type {string[]} */
+	const runtime = [
+		`var __webpack_exec__ = ${runtimeTemplate.returningFunction(
+			`${RuntimeGlobals.require}(${RuntimeGlobals.entryModuleId} = moduleId)`,
+			"moduleId"
+		)}`
+	];
+
+	/**
+	 * @param {ModuleId} id id
+	 * @returns {string} fn to execute
+	 */
+	const runModule = id => `__webpack_exec__(${JSON.stringify(id)})`;
+	/**
+	 * @param {Chunks} chunks chunks
+	 * @param {ModuleIds} moduleIds module ids
+	 * @param {boolean=} final true when final, otherwise false
+	 */
+	const outputCombination = (chunks, moduleIds, final) => {
+		if (chunks.size === 0) {
+			runtime.push(
+				`${final ? EXPORT_PREFIX : ""}(${moduleIds.map(runModule).join(", ")});`
+			);
+		} else {
+			const fn = runtimeTemplate.returningFunction(
+				moduleIds.map(runModule).join(", ")
+			);
+			runtime.push(
+				`${final && !passive ? EXPORT_PREFIX : ""}${
+					passive
+						? RuntimeGlobals.onChunksLoaded
+						: RuntimeGlobals.startupEntrypoint
+				}(0, ${JSON.stringify(Array.from(chunks, c => c.id))}, ${fn});`
+			);
+			if (final && passive) {
+				runtime.push(`${EXPORT_PREFIX}${RuntimeGlobals.onChunksLoaded}();`);
+			}
+		}
+	};
+
+	/** @type {Chunks | undefined} */
+	let currentChunks;
+	/** @type {ModuleIds | undefined} */
+	let currentModuleIds;
+
+	for (const [module, entrypoint] of entries) {
+		const runtimeChunk =
+			/** @type {Entrypoint} */
+			(entrypoint).getRuntimeChunk();
+		const moduleId = /** @type {ModuleId} */ (chunkGraph.getModuleId(module));
+		const chunks = getAllChunks(
+			/** @type {Entrypoint} */
+			(entrypoint),
+			chunk,
+			runtimeChunk
+		);
+		if (
+			currentChunks &&
+			currentChunks.size === chunks.size &&
+			isSubset(currentChunks, chunks)
+		) {
+			/** @type {ModuleIds} */
+			(currentModuleIds).push(moduleId);
+		} else {
+			if (currentChunks) {
+				outputCombination(
+					currentChunks,
+					/** @type {ModuleIds} */ (currentModuleIds)
+				);
+			}
+			currentChunks = chunks;
+			currentModuleIds = [moduleId];
+		}
+	}
+
+	// output current modules with export prefix
+	if (currentChunks) {
+		outputCombination(
+			currentChunks,
+			/** @type {ModuleIds} */
+			(currentModuleIds),
+			true
+		);
+	}
+	runtime.push("");
+	return Template.asString(runtime);
+};
+
+/**
+ * @param {Hash} hash the hash to update
+ * @param {ChunkGraph} chunkGraph chunkGraph
+ * @param {EntryModuleWithChunkGroup[]} entries entries
+ * @param {Chunk} chunk chunk
+ * @returns {void}
+ */
+module.exports.updateHashForEntryStartup = (
+	hash,
+	chunkGraph,
+	entries,
+	chunk
+) => {
+	for (const [module, entrypoint] of entries) {
+		const runtimeChunk =
+			/** @type {Entrypoint} */
+			(entrypoint).getRuntimeChunk();
+		const moduleId = chunkGraph.getModuleId(module);
+		hash.update(`${moduleId}`);
+		for (const c of getAllChunks(
+			/** @type {Entrypoint} */ (entrypoint),
+			chunk,
+			/** @type {Chunk} */ (runtimeChunk)
+		)) {
+			hash.update(`${c.id}`);
+		}
+	}
+};
+
+/**
+ * @param {Chunk} chunk the chunk
+ * @param {ChunkGraph} chunkGraph the chunk graph
+ * @param {function(Chunk, ChunkGraph): boolean} filterFn filter function
+ * @returns {Set<number | string>} initially fulfilled chunk ids
+ */
+module.exports.getInitialChunkIds = (chunk, chunkGraph, filterFn) => {
+	const initialChunkIds = new Set(chunk.ids);
+	for (const c of chunk.getAllInitialChunks()) {
+		if (c === chunk || filterFn(c, chunkGraph)) continue;
+		for (const id of /** @type {ChunkId[]} */ (c.ids)) {
+			initialChunkIds.add(id);
+		}
+	}
+	return initialChunkIds;
+};
