summaryrefslogtreecommitdiff
path: root/node_modules/webpack/lib/javascript
diff options
context:
space:
mode:
authorlittleblack111 <littleblack11111@gmail.com>2024-08-30 17:21:18 +0800
committerlittleblack111 <littleblack11111@gmail.com>2024-08-30 17:21:18 +0800
commitefbc2ffe6a1ee5ec38a4396305561d6515d0943e (patch)
tree2b4c94f18f2c792dfda813050755d5c0273fae22 /node_modules/webpack/lib/javascript
parent43acceb75957c880a5ba5a31aae221394a36d5f6 (diff)
node fixes, updates
Diffstat (limited to 'node_modules/webpack/lib/javascript')
-rw-r--r--node_modules/webpack/lib/javascript/ArrayPushCallbackChunkFormatPlugin.js156
-rw-r--r--node_modules/webpack/lib/javascript/BasicEvaluatedExpression.js594
-rw-r--r--node_modules/webpack/lib/javascript/ChunkHelpers.js33
-rw-r--r--node_modules/webpack/lib/javascript/CommonJsChunkFormatPlugin.js175
-rw-r--r--node_modules/webpack/lib/javascript/EnableChunkLoadingPlugin.js123
-rw-r--r--node_modules/webpack/lib/javascript/JavascriptGenerator.js255
-rw-r--r--node_modules/webpack/lib/javascript/JavascriptModulesPlugin.js1573
-rw-r--r--node_modules/webpack/lib/javascript/JavascriptParser.js4785
-rw-r--r--node_modules/webpack/lib/javascript/JavascriptParserHelpers.js128
-rw-r--r--node_modules/webpack/lib/javascript/StartupHelpers.js177
10 files changed, 7999 insertions, 0 deletions
diff --git a/node_modules/webpack/lib/javascript/ArrayPushCallbackChunkFormatPlugin.js b/node_modules/webpack/lib/javascript/ArrayPushCallbackChunkFormatPlugin.js
new file mode 100644
index 00000000..1bb04aba
--- /dev/null
+++ b/node_modules/webpack/lib/javascript/ArrayPushCallbackChunkFormatPlugin.js
@@ -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;
diff --git a/node_modules/webpack/lib/javascript/BasicEvaluatedExpression.js b/node_modules/webpack/lib/javascript/BasicEvaluatedExpression.js
new file mode 100644
index 00000000..05dd14cd
--- /dev/null
+++ b/node_modules/webpack/lib/javascript/BasicEvaluatedExpression.js
@@ -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;
diff --git a/node_modules/webpack/lib/javascript/ChunkHelpers.js b/node_modules/webpack/lib/javascript/ChunkHelpers.js
new file mode 100644
index 00000000..f2e8a12a
--- /dev/null
+++ b/node_modules/webpack/lib/javascript/ChunkHelpers.js
@@ -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;
diff --git a/node_modules/webpack/lib/javascript/CommonJsChunkFormatPlugin.js b/node_modules/webpack/lib/javascript/CommonJsChunkFormatPlugin.js
new file mode 100644
index 00000000..75384ab9
--- /dev/null
+++ b/node_modules/webpack/lib/javascript/CommonJsChunkFormatPlugin.js
@@ -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;
diff --git a/node_modules/webpack/lib/javascript/EnableChunkLoadingPlugin.js b/node_modules/webpack/lib/javascript/EnableChunkLoadingPlugin.js
new file mode 100644
index 00000000..0dc08a38
--- /dev/null
+++ b/node_modules/webpack/lib/javascript/EnableChunkLoadingPlugin.js
@@ -0,0 +1,123 @@
+/*
+ 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": {
+ const ModuleChunkLoadingPlugin = require("../esm/ModuleChunkLoadingPlugin");
+ new ModuleChunkLoadingPlugin().apply(compiler);
+ break;
+ }
+ case "universal":
+ // TODO implement universal chunk loading
+ throw new Error("Universal Chunk Loading is not implemented yet");
+ 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;
diff --git a/node_modules/webpack/lib/javascript/JavascriptGenerator.js b/node_modules/webpack/lib/javascript/JavascriptGenerator.js
new file mode 100644
index 00000000..b154a60b
--- /dev/null
+++ b/node_modules/webpack/lib/javascript/JavascriptGenerator.js
@@ -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 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("../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"
+);
+
+const TYPES = new Set(["javascript"]);
+
+class JavascriptGenerator extends Generator {
+ /**
+ * @param {NormalModule} module fresh module
+ * @returns {Set<string>} available types (do not mutate)
+ */
+ getTypes(module) {
+ return 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} 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: 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;
diff --git a/node_modules/webpack/lib/javascript/JavascriptModulesPlugin.js b/node_modules/webpack/lib/javascript/JavascriptModulesPlugin.js
new file mode 100644
index 00000000..da4a17bd
--- /dev/null
+++ b/node_modules/webpack/lib/javascript/JavascriptModulesPlugin.js
@@ -0,0 +1,1573 @@
+/*
+ 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 createHash = require("../util/createHash");
+const { getPathInAst, getAllReferences } = require("../util/mergeScope");
+const nonNumericOnlyHash = require("../util/nonNumericOnlyHash");
+const { intersectRuntime } = require("../util/runtime");
+const JavascriptGenerator = require("./JavascriptGenerator");
+const JavascriptParser = require("./JavascriptParser");
+
+/** @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("../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("../javascript/JavascriptParser").Range} Range */
+/** @typedef {import("../util/Hash")} Hash */
+
+/**
+ * @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 {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>} 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>} 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;
+
+ let render;
+ const filenameTemplate =
+ JavascriptModulesPlugin.getChunkFilenameTemplate(
+ chunk,
+ outputOptions
+ );
+ if (hotUpdateChunk) {
+ render = () =>
+ this.renderChunk(
+ {
+ chunk,
+ dependencyTemplates,
+ runtimeTemplate,
+ moduleGraph,
+ chunkGraph,
+ codeGenerationResults,
+ strictMode: runtimeTemplate.isModule()
+ },
+ hooks
+ );
+ } else if (chunk.hasRuntime()) {
+ 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(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,
+ 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, moduleObject } = options;
+ const code = source.source();
+
+ const fn = vm.runInThisContext(
+ `(function(${module.moduleArgument}, ${module.exportsArgument}, ${RuntimeGlobals.require}) {\n${code}\n/**/})`,
+ {
+ filename: module.identifier(),
+ lineOffset: -1
+ }
+ );
+ 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) {
+ 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 renamedInlinedModule = this.renameInlineModule(
+ allModules,
+ renderContext,
+ inlinedModules,
+ chunkRenderContext,
+ hooks
+ );
+
+ for (const m of inlinedModules) {
+ const renderedModule =
+ 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 need 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 need to be isolated against other entry modules."
+ : 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 need 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)) {
+ 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
+ * @returns {Map<Module, Source>} renamed inlined modules
+ */
+ renameInlineModule(
+ allModules,
+ renderContext,
+ inlinedModules,
+ chunkRenderContext,
+ hooks
+ ) {
+ const { runtimeTemplate } = renderContext;
+
+ /** @typedef {{ source: Source, ast: any, variables: Set<Variable>, usedInNonInlined: Set<Variable>}} InlinedModulesInfo */
+
+ /** @type {Map<Module, InlinedModulesInfo>} */
+ const inlinedModulesToInfo = new Map();
+ /** @type {Set<string>} */
+ const nonInlinedModuleThroughIdentifiers = new Set();
+ /** @type {Map<Module, Source>} */
+ const renamedInlinedModules = new Map();
+
+ 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,
+ variables: new Set(moduleScope.variables),
+ usedInNonInlined: new Set()
+ });
+ } 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)) {
+ 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 usedNames = new Set(
+ Array.from(
+ /** @type {InlinedModulesInfo} */
+ (inlinedModulesToInfo.get(m)).variables
+ ).map(v => v.name)
+ );
+
+ for (const variable of usedInNonInlined) {
+ const references = getAllReferences(variable);
+ const allIdentifiers = new Set(
+ references.map(r => r.identifier).concat(variable.identifiers)
+ );
+
+ const newName = this.findNewName(
+ variable.name,
+ usedNames,
+ m.readableIdentifier(runtimeTemplate.requestShortener)
+ );
+ usedNames.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);
+ }
+ }
+
+ renamedInlinedModules.set(m, source);
+ }
+
+ return renamedInlinedModules;
+ }
+
+ /**
+ * @param {string} oldName oldName
+ * @param {Set<string>} usedName usedName
+ * @param {string} extraInfo extraInfo
+ * @returns {string} extraInfo
+ */
+ findNewName(oldName, usedName, extraInfo) {
+ let name = oldName;
+
+ // Remove uncool stuff
+ extraInfo = extraInfo.replace(
+ /\.+\/|(\/index)?\.([a-zA-Z0-9]{1,4})($|\s|\?)|\s*\+\s*\d+\s*modules/g,
+ ""
+ );
+ const splittedInfo = extraInfo.split("/");
+ while (splittedInfo.length) {
+ name = splittedInfo.pop() + (name ? `_${name}` : "");
+ const nameIdent = Template.toIdentifier(name);
+ if (!usedName.has(nameIdent)) {
+ return nameIdent;
+ }
+ }
+
+ let i = 0;
+ let nameWithNumber = Template.toIdentifier(`${name}_${i}`);
+ while (usedName.has(nameWithNumber)) {
+ i++;
+ nameWithNumber = Template.toIdentifier(`${name}_${i}`);
+ }
+ return nameWithNumber;
+ }
+}
+
+module.exports = JavascriptModulesPlugin;
+module.exports.chunkHasJs = chunkHasJs;
diff --git a/node_modules/webpack/lib/javascript/JavascriptParser.js b/node_modules/webpack/lib/javascript/JavascriptParser.js
new file mode 100644
index 00000000..94b67732
--- /dev/null
+++ b/node_modules/webpack/lib/javascript/JavascriptParser.js
@@ -0,0 +1,4785 @@
+/*
+ MIT License http://www.opensource.org/licenses/mit-license.php
+ Author Tobias Koppers @sokra
+*/
+
+"use strict";
+
+const { Parser: AcornParser } = require("acorn");
+const { importAttributesOrAssertions } = require("acorn-import-attributes");
+const { SyncBailHook, HookMap } = require("tapable");
+const vm = require("vm");
+const Parser = require("../Parser");
+const StackedMap = require("../util/StackedMap");
+const binarySearchBounds = require("../util/binarySearchBounds");
+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").ImportExpression} ImportExpression */
+/** @typedef {import("estree").ClassDeclaration} ClassDeclaration */
+/** @typedef {import("estree").ForStatement} ForStatement */
+/** @typedef {import("estree").SwitchStatement} SwitchStatement */
+/** @typedef {import("estree").ExportNamedDeclaration} ExportNamedDeclaration */
+/** @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").ModuleDeclaration} ModuleDeclaration */
+/** @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} AnyNode */
+/** @typedef {import("estree").Program} Program */
+/** @typedef {import("estree").Directive} Directive */
+/** @typedef {import("estree").Statement} Statement */
+/** @typedef {import("estree").ImportDeclaration} ImportDeclaration */
+/** @typedef {import("estree").ExportDefaultDeclaration} ExportDefaultDeclaration */
+/** @typedef {import("estree").ExportAllDeclaration} ExportAllDeclaration */
+/** @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, tagInfo: TagInfo | undefined}} VariableInfoInterface */
+/** @typedef {{ name: string | VariableInfo, rootInfo: string | VariableInfo, getMembers: () => string[], getMembersOptionals: () => boolean[], getMemberRanges: () => Range[] }} GetInfoResult */
+/** @typedef {Statement | ModuleDeclaration | Expression} StatementPathItem */
+/** @typedef {TODO} OnIdent */
+
+/** @typedef {Record<string, string> & { _isLegacyAssert?: boolean }} ImportAttributes */
+
+/** @type {string[]} */
+const EMPTY_ARRAY = [];
+const ALLOWED_MEMBER_TYPES_CALL_EXPRESSION = 0b01;
+const ALLOWED_MEMBER_TYPES_EXPRESSION = 0b10;
+const ALLOWED_MEMBER_TYPES_ALL = 0b11;
+
+// Syntax: https://developer.mozilla.org/en/SpiderMonkey/Parser_API
+
+const parser = AcornParser.extend(importAttributesOrAssertions);
+
+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: null
+};
+
+// regexp to match at least one "magic comment"
+const webpackCommentRegExp = new RegExp(/(^|\W)webpack[A-Z]{1,}[A-Za-z]{1,}:/);
+
+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 | undefined | null>>} */
+ evaluateTypeof: new HookMap(() => new SyncBailHook(["expression"])),
+ /** @type {HookMap<SyncBailHook<[Expression | SpreadElement], BasicEvaluatedExpression | undefined | null>>} */
+ evaluate: new HookMap(() => new SyncBailHook(["expression"])),
+ /** @type {HookMap<SyncBailHook<[Identifier | ThisExpression | MemberExpression | MetaProperty], BasicEvaluatedExpression | undefined | null>>} */
+ evaluateIdentifier: new HookMap(() => new SyncBailHook(["expression"])),
+ /** @type {HookMap<SyncBailHook<[Identifier | ThisExpression | MemberExpression], BasicEvaluatedExpression | undefined | null>>} */
+ evaluateDefinedIdentifier: new HookMap(
+ () => new SyncBailHook(["expression"])
+ ),
+ /** @type {HookMap<SyncBailHook<[NewExpression], BasicEvaluatedExpression | undefined | null>>} */
+ evaluateNewExpression: new HookMap(
+ () => new SyncBailHook(["expression"])
+ ),
+ /** @type {HookMap<SyncBailHook<[CallExpression], BasicEvaluatedExpression | undefined | null>>} */
+ evaluateCallExpression: new HookMap(
+ () => new SyncBailHook(["expression"])
+ ),
+ /** @type {HookMap<SyncBailHook<[CallExpression, BasicEvaluatedExpression], BasicEvaluatedExpression | undefined | null>>} */
+ 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, 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, string, 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 {StatementPathItem[]} */
+ 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 = vm.createContext(undefined, {
+ name: "Webpack Magic Comment Parser",
+ codeGeneration: { strings: false, wasm: false }
+ });
+ 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,
+ 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 {string} 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 {MemberExpression} */ (expr);
+
+ const info = getInfo(expr);
+ 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 info = cachedExpression === expr ? cachedInfo : getInfo(expr);
+ if (info !== undefined) {
+ return new BasicEvaluatedExpression()
+ .setIdentifier(
+ info.name,
+ info.rootInfo,
+ info.getMembers,
+ info.getMembersOptionals,
+ info.getMemberRanges
+ )
+ .setRange(/** @type {Range} */ (expr.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,
+ getRootName(expr),
+ 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} 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) {
+ this.statementPath.push(statement);
+ if (this.hooks.preStatement.call(statement)) {
+ this.prevStatement = 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 = this.statementPath.pop();
+ }
+
+ /**
+ * @param {Statement | ModuleDeclaration} statement statement
+ */
+ blockPreWalkStatement(statement) {
+ this.statementPath.push(statement);
+ if (this.hooks.blockPreStatement.call(statement)) {
+ this.prevStatement = 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 = this.statementPath.pop();
+ }
+
+ /**
+ * @param {Statement | ModuleDeclaration} statement statement
+ */
+ walkStatement(statement) {
+ this.statementPath.push(statement);
+ if (this.hooks.statement.call(statement) !== undefined) {
+ this.prevStatement = 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 = 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,
+ specifier.imported.name ||
+ // eslint-disable-next-line no-warning-comments
+ // @ts-ignore
+ // Old version of acorn used it
+ // TODO drop it in webpack@6
+ 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 name =
+ specifier.exported.name ||
+ // eslint-disable-next-line no-warning-comments
+ // @ts-ignore
+ // Old version of acorn used it
+ // TODO drop it in webpack@6
+ specifier.exported.value;
+ if (source) {
+ this.hooks.exportImportSpecifier.call(
+ statement,
+ source,
+ specifier.local.name,
+ name,
+ specifierIndex
+ );
+ } else {
+ this.hooks.exportSpecifier.call(
+ statement,
+ specifier.local.name,
+ 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 ? statement.exported.name : 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(/** @type {TODO} */ (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 = this.statementPath[this.statementPath.length - 1];
+ if (
+ currentStatement === expression ||
+ (currentStatement.type === "ExpressionStatement" &&
+ currentStatement.expression === expression)
+ ) {
+ const old = /** @type {StatementPathItem} */ (this.statementPath.pop());
+ const prev = this.prevStatement;
+ for (const expr of expression.expressions) {
+ this.statementPath.push(expr);
+ this.walkExpression(expr);
+ this.prevStatement = this.statementPath.pop();
+ }
+ this.prevStatement = prev;
+ 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(
+ /** @type {Expression} */ (argOrThis)
+ );
+ if (
+ renameIdentifier &&
+ this.callHooksForInfo(
+ this.hooks.canRename,
+ renameIdentifier,
+ argOrThis
+ ) &&
+ !this.callHooksForInfo(this.hooks.rename, renameIdentifier, 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) {
+ 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(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(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,
+ callee.rootInfo,
+ expression,
+ callee.getMembers(),
+ callee.getMembersOptionals
+ ? callee.getMembersOptionals()
+ : callee.getMembers().map(() => false),
+ callee.getMemberRanges ? callee.getMemberRanges() : []
+ );
+ if (result1 === true) return;
+ const result2 = this.callHooksForInfo(
+ this.hooks.call,
+ 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 {TODO} 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 {MemberExpression} 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(): 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(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, pattern) => {
+ this.defineVariable(ident);
+ });
+
+ fn();
+
+ this.scope = oldScope;
+ }
+
+ /**
+ * @param {boolean} hasThis true, when this is defined
+ * @param {any} 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, pattern) => {
+ this.defineVariable(ident);
+ });
+
+ fn();
+
+ this.scope = oldScope;
+ }
+
+ /**
+ * @param {boolean} hasThis true, when this is defined
+ * @param {any} 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, pattern) => {
+ 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 {OnIdent} 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 {Identifier} */ (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} 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(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 {TODO} result
+ */
+ parseCalculatedString(expression) {
+ switch (expression.type) {
+ case "BinaryExpression":
+ if (expression.operator === "+") {
+ const left = this.parseCalculatedString(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: [
+ left.range[0],
+ right.range ? right.range[1] : left.range[1]
+ ],
+ value: left.value + right.value,
+ code: true,
+ conditional: false
+ };
+ }
+ return {
+ range: [left.range[0], 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;
+ 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 = this.statementPath[this.statementPath.length - 1];
+ if (currentStatement === undefined) throw new Error("Not in statement");
+ return (
+ // Either asking directly for the end position of the current statement
+ (currentStatement.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
+ (currentStatement.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(
+ 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 = this.statementPath[this.statementPath.length - 1];
+ return (
+ expr === currentStatement ||
+ (currentStatement.type === "ExpressionStatement" &&
+ currentStatement.expression === expr)
+ );
+ }
+
+ /**
+ * @param {string} name name
+ * @param {TODO} 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 {TODO} 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 {TODO} TODO
+ */
+ parseCommentOptions(range) {
+ const comments = this.getComments(range);
+ if (comments.length === 0) {
+ return EMPTY_COMMENT_OPTIONS;
+ }
+ const options = {};
+ /** @type {unknown[]} */
+ 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(err.message));
+ newErr.stack = String(err.stack);
+ Object.assign(newErr, { comment });
+ errors.push(newErr);
+ }
+ }
+ }
+ return { options, errors };
+ }
+
+ /**
+ * @param {MemberExpression} expression a member expression
+ * @returns {{ members: string[], object: Expression | Super, membersOptionals: boolean[], memberRanges: Range[] }} member names (reverse order) and remaining object
+ */
+ extractMemberExpressionChain(expression) {
+ /** @type {AnyNode} */
+ 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 {MemberExpression} 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 {AnyNode | undefined} */
+ let ast;
+ let error;
+ let threw = false;
+ try {
+ ast = /** @type {AnyNode} */ (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 = /** @type {AnyNode} */ (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;
diff --git a/node_modules/webpack/lib/javascript/JavascriptParserHelpers.js b/node_modules/webpack/lib/javascript/JavascriptParserHelpers.js
new file mode 100644
index 00000000..7028c4dd
--- /dev/null
+++ b/node_modules/webpack/lib/javascript/JavascriptParserHelpers.js
@@ -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;
diff --git a/node_modules/webpack/lib/javascript/StartupHelpers.js b/node_modules/webpack/lib/javascript/StartupHelpers.js
new file mode 100644
index 00000000..f6f759d4
--- /dev/null
+++ b/node_modules/webpack/lib/javascript/StartupHelpers.js
@@ -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;
+};