webpack/lib/optimize/ConcatenatedModule.js

2233 lines
66 KiB
JavaScript
Raw Normal View History

2017-05-10 19:15:14 +08:00
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
2018-07-30 23:08:51 +08:00
2017-05-10 19:15:14 +08:00
"use strict";
const eslintScope = require("eslint-scope");
2024-03-14 23:50:52 +08:00
const Referencer = require("eslint-scope/lib/referencer");
const { SyncBailHook } = require("tapable");
const {
CachedSource,
ConcatSource,
ReplaceSource
} = require("webpack-sources");
2020-09-11 15:06:24 +08:00
const ConcatenationScope = require("../ConcatenationScope");
const { UsageState } = require("../ExportsInfo");
2018-07-30 23:08:51 +08:00
const Module = require("../Module");
2024-11-01 04:19:07 +08:00
const { JS_TYPES } = require("../ModuleSourceTypesConstants");
const { JAVASCRIPT_MODULE_TYPE_ESM } = require("../ModuleTypeConstants");
const RuntimeGlobals = require("../RuntimeGlobals");
2018-07-30 23:08:51 +08:00
const Template = require("../Template");
const { DEFAULTS } = require("../config/defaults");
2017-05-10 19:15:14 +08:00
const HarmonyImportDependency = require("../dependencies/HarmonyImportDependency");
const HarmonyImportSideEffectDependency = require("../dependencies/HarmonyImportSideEffectDependency");
const HarmonyImportSpecifierDependency = require("../dependencies/HarmonyImportSpecifierDependency");
2019-10-11 21:46:57 +08:00
const JavascriptParser = require("../javascript/JavascriptParser");
const {
getMakeDeferredNamespaceModeFromExportsType,
getOptimizedDeferredModule
} = require("../runtime/MakeDeferredNamespaceObjectRuntime");
const { equals } = require("../util/ArrayHelpers");
const LazySet = require("../util/LazySet");
2022-05-27 19:22:05 +08:00
const { concatComparators } = require("../util/comparators");
const {
RESERVED_NAMES,
addScopeSymbols,
2025-07-03 17:06:45 +08:00
findNewName,
getAllReferences,
getPathInAst,
getUsedNamesInScopeInfo
} = require("../util/concatenate");
const createHash = require("../util/createHash");
const { makePathsRelative } = require("../util/identifier");
2019-12-11 05:58:26 +08:00
const makeSerializable = require("../util/makeSerializable");
const propertyAccess = require("../util/propertyAccess");
2023-05-05 12:36:03 +08:00
const { propertyName } = require("../util/propertyName");
const {
filterRuntime,
intersectRuntime,
mergeRuntimeCondition,
mergeRuntimeConditionNonFalse,
runtimeConditionToString,
subtractRuntimeCondition
} = require("../util/runtime");
2017-05-10 19:15:14 +08:00
2024-03-18 23:28:40 +08:00
/** @typedef {import("eslint-scope").Reference} Reference */
2020-08-03 02:09:36 +08:00
/** @typedef {import("eslint-scope").Scope} Scope */
2024-02-22 22:20:17 +08:00
/** @typedef {import("eslint-scope").Variable} Variable */
2018-07-30 23:08:51 +08:00
/** @typedef {import("webpack-sources").Source} Source */
2025-08-20 18:50:12 +08:00
/** @typedef {import("../config/defaults").WebpackOptionsNormalizedWithDefaults} WebpackOptions */
/** @typedef {import("../ChunkGraph")} ChunkGraph */
2021-12-01 21:15:19 +08:00
/** @typedef {import("../CodeGenerationResults")} CodeGenerationResults */
/** @typedef {import("../Compilation")} Compilation */
/** @typedef {import("../Dependency").UpdateHashContext} UpdateHashContext */
2018-07-11 19:05:13 +08:00
/** @typedef {import("../DependencyTemplates")} DependencyTemplates */
/** @typedef {import("../ExportsInfo").ExportInfo} ExportInfo */
/** @typedef {import("../Module").BuildCallback} BuildCallback */
2024-03-18 23:28:40 +08:00
/** @typedef {import("../Module").BuildInfo} BuildInfo */
2025-08-28 18:34:30 +08:00
/** @typedef {import("../Module").FileSystemDependencies} FileSystemDependencies */
2024-03-18 23:28:40 +08:00
/** @typedef {import("../Module").BuildMeta} BuildMeta */
/** @typedef {import("../Module").CodeGenerationContext} CodeGenerationContext */
/** @typedef {import("../Module").CodeGenerationResult} CodeGenerationResult */
2018-07-20 22:24:35 +08:00
/** @typedef {import("../Module").LibIdentOptions} LibIdentOptions */
2025-09-11 08:10:10 +08:00
/** @typedef {import("../Module").LibIdent} LibIdent */
/** @typedef {import("../Module").NameForCondition} NameForCondition */
2024-03-18 23:28:40 +08:00
/** @typedef {import("../Module").ReadOnlyRuntimeRequirements} ReadOnlyRuntimeRequirements */
2024-10-02 05:18:10 +08:00
/** @typedef {import("../Module").RuntimeRequirements} RuntimeRequirements */
2024-02-17 01:39:12 +08:00
/** @typedef {import("../Module").SourceTypes} SourceTypes */
/** @typedef {import("../ModuleGraph")} ModuleGraph */
/** @typedef {import("../ModuleGraphConnection")} ModuleGraphConnection */
2020-12-05 00:03:12 +08:00
/** @typedef {import("../ModuleGraphConnection").ConnectionState} ConnectionState */
2018-07-30 23:08:51 +08:00
/** @typedef {import("../RequestShortener")} RequestShortener */
/** @typedef {import("../ResolverFactory").ResolverWithOptions} ResolverWithOptions */
2018-07-11 19:05:13 +08:00
/** @typedef {import("../RuntimeTemplate")} RuntimeTemplate */
/** @typedef {import("../javascript/JavascriptModulesPlugin").ChunkRenderContext} ChunkRenderContext */
2024-03-18 23:28:40 +08:00
/** @typedef {import("../javascript/JavascriptParser").Program} Program */
2024-02-22 22:20:17 +08:00
/** @typedef {import("../javascript/JavascriptParser").Range} Range */
2024-03-18 23:28:40 +08:00
/** @typedef {import("../serialization/ObjectMiddleware").ObjectDeserializerContext} ObjectDeserializerContext */
2019-07-17 22:02:33 +08:00
/** @typedef {import("../util/Hash")} Hash */
/** @typedef {typeof import("../util/Hash")} HashConstructor */
/** @typedef {import("../util/concatenate").ScopeInfo} ScopeInfo */
/** @typedef {import("../util/fs").InputFileSystem} InputFileSystem */
/** @typedef {import("../util/identifier").AssociatedObjectForCache} AssociatedObjectForCache */
/** @typedef {import("../util/runtime").RuntimeSpec} RuntimeSpec */
2024-03-18 23:28:40 +08:00
/**
* @template T
* @typedef {import("../InitFragment")<T>} InitFragment
*/
2024-02-22 22:20:17 +08:00
/**
* @template T
* @typedef {import("../util/comparators").Comparator<T>} Comparator
*/
// fix eslint-scope to support class properties correctly
// cspell:word Referencer
2025-09-11 08:10:10 +08:00
const ReferencerClass = Referencer;
if (!ReferencerClass.prototype.PropertyDefinition) {
ReferencerClass.prototype.PropertyDefinition =
ReferencerClass.prototype.Property;
}
/** @typedef {RawBinding | SymbolBinding} Binding */
2025-09-11 08:10:10 +08:00
/** @typedef {string[]} ExportName */
/**
2024-06-11 21:09:50 +08:00
* @typedef {object} RawBinding
* @property {ModuleInfo} info
* @property {string} rawName
* @property {string=} comment
2025-09-11 08:10:10 +08:00
* @property {ExportName} ids
* @property {ExportName} exportName
*/
/**
2024-06-11 21:09:50 +08:00
* @typedef {object} SymbolBinding
* @property {ConcatenatedModuleInfo} info
* @property {string} name
* @property {string=} comment
2025-09-11 08:10:10 +08:00
* @property {ExportName} ids
* @property {ExportName} exportName
*/
/** @typedef {ConcatenatedModuleInfo | ExternalModuleInfo } ModuleInfo */
/** @typedef {ConcatenatedModuleInfo | ExternalModuleInfo | ReferenceToModuleInfo } ModuleInfoOrReference */
/**
2024-06-11 21:09:50 +08:00
* @typedef {object} ConcatenatedModuleInfo
* @property {"concatenated"} type
* @property {Module} module
* @property {number} index
2024-03-18 23:28:40 +08:00
* @property {Program | undefined} ast
2024-08-09 23:42:37 +08:00
* @property {Source | undefined} internalSource
2024-10-25 02:13:59 +08:00
* @property {ReplaceSource | undefined} source
* @property {InitFragment<ChunkRenderContext>[]=} chunkInitFragments
2024-08-09 23:42:37 +08:00
* @property {ReadOnlyRuntimeRequirements | undefined} runtimeRequirements
* @property {Scope | undefined} globalScope
* @property {Scope | undefined} moduleScope
* @property {Map<string, string>} internalNames
2024-03-18 23:28:40 +08:00
* @property {Map<string, string> | undefined} exportMap
* @property {Map<string, string> | undefined} rawExportMap
* @property {string=} namespaceExportSymbol
2024-03-18 23:28:40 +08:00
* @property {string | undefined} namespaceObjectName
* @property {ConcatenationScope | undefined} concatenationScope
* @property {boolean} interopNamespaceObjectUsed "default-with-named" namespace
* @property {string | undefined} interopNamespaceObjectName "default-with-named" namespace
* @property {boolean} interopNamespaceObject2Used "default-only" namespace
* @property {string | undefined} interopNamespaceObject2Name "default-only" namespace
* @property {boolean} interopDefaultAccessUsed runtime namespace object that detects "__esModule"
* @property {string | undefined} interopDefaultAccessName runtime namespace object that detects "__esModule"
*/
/**
2024-06-11 21:09:50 +08:00
* @typedef {object} ExternalModuleInfo
* @property {"external"} type
* @property {Module} module
* @property {RuntimeSpec | boolean} runtimeCondition
* @property {number} index
* @property {string | undefined} name module.exports / harmony namespace object
* @property {string | undefined} deferredName deferred module.exports / harmony namespace object
* @property {boolean} deferred the module is deferred at least once
* @property {boolean} deferredNamespaceObjectUsed deferred namespace object that being used in a not-analyzable way so it must be materialized
* @property {string | undefined} deferredNamespaceObjectName deferred namespace object that being used in a not-analyzable way so it must be materialized
* @property {boolean} interopNamespaceObjectUsed "default-with-named" namespace
* @property {string | undefined} interopNamespaceObjectName "default-with-named" namespace
* @property {boolean} interopNamespaceObject2Used "default-only" namespace
* @property {string | undefined} interopNamespaceObject2Name "default-only" namespace
* @property {boolean} interopDefaultAccessUsed runtime namespace object that detects "__esModule"
* @property {string | undefined} interopDefaultAccessName runtime namespace object that detects "__esModule"
*/
/**
2024-06-11 21:09:50 +08:00
* @typedef {object} ReferenceToModuleInfo
* @property {"reference"} type
* @property {RuntimeSpec | boolean} runtimeCondition
2024-10-25 02:13:59 +08:00
* @property {ModuleInfo} target
*/
2024-08-15 02:38:08 +08:00
/**
* @template T
* @param {string} property property
* @param {function(T[keyof T], T[keyof T]): 0 | 1 | -1} comparator comparator
* @returns {Comparator<T>} comparator
*/
2022-05-27 19:22:05 +08:00
const createComparator = (property, comparator) => (a, b) =>
2024-08-15 02:38:08 +08:00
comparator(
a[/** @type {keyof T} */ (property)],
b[/** @type {keyof T} */ (property)]
);
2024-02-22 22:20:17 +08:00
/**
* @param {number} a a
* @param {number} b b
* @returns {0 | 1 | -1} result
*/
2022-05-27 19:22:05 +08:00
const compareNumbers = (a, b) => {
2024-08-02 02:36:27 +08:00
if (Number.isNaN(a)) {
if (!Number.isNaN(b)) {
return 1;
}
} else {
2024-08-02 02:36:27 +08:00
if (Number.isNaN(b)) {
return -1;
}
2022-05-27 19:22:05 +08:00
if (a !== b) {
return a < b ? -1 : 1;
}
}
return 0;
};
2022-05-27 19:22:05 +08:00
const bySourceOrder = createComparator("sourceOrder", compareNumbers);
const byRangeStart = createComparator("rangeStart", compareNumbers);
const moveDeferToLast = (
/** @type {{ defer?: boolean }} */ a,
/** @type {{ defer?: boolean }} */ b
) => {
if (a.defer === b.defer) return 0;
if (a.defer) return 1;
return -1;
};
const INITIAL_USED_NAMES = new Set(RESERVED_NAMES);
2024-02-22 22:20:17 +08:00
/**
* @param {Iterable<string>} iterable iterable object
* @returns {string} joined iterable object
*/
const joinIterableWithComma = (iterable) => {
// This is more performant than Array.from().join(", ")
// as it doesn't create an array
let str = "";
let first = true;
for (const item of iterable) {
if (first) {
first = false;
} else {
str += ", ";
}
str += item;
}
return str;
};
/**
2024-06-11 21:09:50 +08:00
* @typedef {object} ConcatenationEntry
* @property {"concatenated" | "external"} type
* @property {Module} module
* @property {RuntimeSpec | boolean} runtimeCondition
*/
2025-08-28 18:34:30 +08:00
/** @typedef {Set<ConcatenatedModuleInfo>} NeededNamespaceObjects */
/** @typedef {Map<Module, ModuleInfo>} ModuleToInfoMap */
/**
* @param {ModuleGraph} moduleGraph the module graph
* @param {ModuleInfo} info module info
2025-09-11 08:10:10 +08:00
* @param {ExportName} exportName exportName
2025-08-28 18:34:30 +08:00
* @param {ModuleToInfoMap} moduleToInfoMap moduleToInfoMap
* @param {RuntimeSpec} runtime for which runtime
* @param {RequestShortener} requestShortener the request shortener
* @param {RuntimeTemplate} runtimeTemplate the runtime template
2025-08-28 18:34:30 +08:00
* @param {NeededNamespaceObjects} neededNamespaceObjects modules for which a namespace object should be generated
* @param {boolean} asCall asCall
* @param {boolean} depDeferred the dependency is deferred
2024-02-22 22:20:17 +08:00
* @param {boolean | undefined} strictHarmonyModule strictHarmonyModule
2020-08-29 22:00:03 +08:00
* @param {boolean | undefined} asiSafe asiSafe
* @param {Set<ExportInfo>} alreadyVisited alreadyVisited
* @returns {Binding} the final variable
*/
const getFinalBinding = (
moduleGraph,
2018-02-25 09:00:20 +08:00
info,
exportName,
moduleToInfoMap,
runtime,
requestShortener,
runtimeTemplate,
neededNamespaceObjects,
2018-02-25 09:00:20 +08:00
asCall,
depDeferred,
strictHarmonyModule,
asiSafe,
alreadyVisited = new Set()
2018-02-25 09:00:20 +08:00
) => {
const exportsType = info.module.getExportsType(
moduleGraph,
strictHarmonyModule
);
const deferred =
depDeferred &&
info.type === "external" &&
info.deferred &&
!moduleGraph.isAsync(info.module);
if (exportName.length === 0) {
switch (exportsType) {
case "default-only":
if (deferred) info.deferredNamespaceObjectUsed = true;
else info.interopNamespaceObject2Used = true;
return {
info,
rawName: /** @type {string} */ (
deferred
? info.deferredNamespaceObjectName
: info.interopNamespaceObject2Name
),
ids: exportName,
exportName
};
case "default-with-named":
if (deferred) info.deferredNamespaceObjectUsed = true;
else info.interopNamespaceObjectUsed = true;
return {
info,
rawName: /** @type {string} */ (
deferred
? info.deferredNamespaceObjectName
: info.interopNamespaceObjectName
),
ids: exportName,
exportName
};
case "namespace":
case "dynamic":
break;
default:
throw new Error(`Unexpected exportsType ${exportsType}`);
}
} else {
switch (exportsType) {
case "namespace":
break;
case "default-with-named":
switch (exportName[0]) {
case "default":
exportName = exportName.slice(1);
break;
case "__esModule":
return {
info,
rawName: "/* __esModule */true",
ids: exportName.slice(1),
exportName
};
}
break;
case "default-only": {
const exportId = exportName[0];
if (exportId === "__esModule") {
return {
info,
rawName: "/* __esModule */true",
ids: exportName.slice(1),
exportName
};
}
exportName = exportName.slice(1);
if (exportId !== "default") {
return {
info,
rawName:
"/* non-default import from default-exporting module */undefined",
ids: exportName,
exportName
};
}
break;
}
case "dynamic":
switch (exportName[0]) {
case "default": {
exportName = exportName.slice(1);
if (deferred) {
return {
info,
rawName: `${info.deferredName}.a`,
ids: exportName,
exportName
};
}
info.interopDefaultAccessUsed = true;
const defaultExport = asCall
? `${info.interopDefaultAccessName}()`
: asiSafe
2024-07-31 05:43:19 +08:00
? `(${info.interopDefaultAccessName}())`
: asiSafe === false
? `;(${info.interopDefaultAccessName}())`
: `${info.interopDefaultAccessName}.a`;
return {
info,
rawName: defaultExport,
ids: exportName,
exportName
};
}
case "__esModule":
return {
info,
rawName: "/* __esModule */true",
ids: exportName.slice(1),
exportName
};
}
break;
default:
throw new Error(`Unexpected exportsType ${exportsType}`);
}
}
if (exportName.length === 0) {
switch (info.type) {
case "concatenated":
neededNamespaceObjects.add(info);
return {
info,
2024-08-15 02:38:08 +08:00
rawName:
/** @type {NonNullable<ConcatenatedModuleInfo["namespaceObjectName"]>} */
(info.namespaceObjectName),
ids: exportName,
exportName
};
case "external":
if (deferred) {
info.deferredNamespaceObjectUsed = true;
return {
info,
rawName: /** @type {string} */ (info.deferredNamespaceObjectName),
ids: exportName,
exportName
};
}
2024-08-15 02:38:08 +08:00
return {
info,
rawName:
/** @type {NonNullable<ExternalModuleInfo["name"]>} */
(info.name),
ids: exportName,
exportName
};
}
}
const exportsInfo = moduleGraph.getExportsInfo(info.module);
const exportInfo = exportsInfo.getExportInfo(exportName[0]);
if (alreadyVisited.has(exportInfo)) {
return {
info,
rawName: "/* circular reexport */ Object(function x() { x() }())",
ids: [],
exportName
};
}
alreadyVisited.add(exportInfo);
switch (info.type) {
case "concatenated": {
const exportId = exportName[0];
if (exportInfo.provided === false) {
// It's not provided, but it could be on the prototype
neededNamespaceObjects.add(info);
return {
info,
2024-03-18 23:28:40 +08:00
rawName: /** @type {string} */ (info.namespaceObjectName),
ids: exportName,
exportName
};
}
const directExport = info.exportMap && info.exportMap.get(exportId);
if (directExport) {
2025-09-11 08:10:10 +08:00
const usedName = /** @type {ExportName} */ (
2021-05-11 15:31:46 +08:00
exportsInfo.getUsedName(exportName, runtime)
);
if (!usedName) {
return {
info,
rawName: "/* unused export */ undefined",
ids: exportName.slice(1),
exportName
};
}
return {
info,
name: directExport,
ids: usedName.slice(1),
exportName
};
}
const rawExport = info.rawExportMap && info.rawExportMap.get(exportId);
if (rawExport) {
return {
info,
rawName: rawExport,
ids: exportName.slice(1),
exportName
};
}
const reexport = exportInfo.findTarget(moduleGraph, (module) =>
moduleToInfoMap.has(module)
);
if (reexport === false) {
throw new Error(
`Target module of reexport from '${info.module.readableIdentifier(
requestShortener
)}' is not part of the concatenation (export '${exportId}')\nModules in the concatenation:\n${Array.from(
moduleToInfoMap,
([m, info]) =>
` * ${info.type} ${m.readableIdentifier(requestShortener)}`
).join("\n")}`
);
}
if (reexport) {
const refInfo = moduleToInfoMap.get(reexport.module);
return getFinalBinding(
moduleGraph,
2024-02-22 22:20:17 +08:00
/** @type {ModuleInfo} */ (refInfo),
reexport.export
? [...reexport.export, ...exportName.slice(1)]
: exportName.slice(1),
moduleToInfoMap,
runtime,
requestShortener,
runtimeTemplate,
neededNamespaceObjects,
asCall,
reexport.deferred,
2024-02-22 22:20:17 +08:00
/** @type {BuildMeta} */
(info.module.buildMeta).strictHarmonyModule,
asiSafe,
alreadyVisited
);
}
if (info.namespaceExportSymbol) {
2025-09-11 08:10:10 +08:00
const usedName = /** @type {ExportName} */ (
2021-05-11 15:31:46 +08:00
exportsInfo.getUsedName(exportName, runtime)
);
return {
info,
2024-03-18 23:28:40 +08:00
rawName: /** @type {string} */ (info.namespaceObjectName),
ids: usedName,
exportName
};
}
throw new Error(
`Cannot get final name for export '${exportName.join(
"."
)}' of ${info.module.readableIdentifier(requestShortener)}`
);
}
case "external": {
2025-09-11 08:10:10 +08:00
const used = /** @type {ExportName} */ (
2021-05-11 15:31:46 +08:00
exportsInfo.getUsedName(exportName, runtime)
);
if (!used) {
return {
info,
rawName: "/* unused export */ undefined",
ids: exportName.slice(1),
exportName
};
}
const comment = equals(used, exportName)
? ""
: Template.toNormalComment(`${exportName.join(".")}`);
return {
info,
rawName:
(deferred ? info.deferredName : info.name) +
(deferred ? ".a" : "") +
comment,
ids: used,
exportName
};
}
}
};
/**
* @param {ModuleGraph} moduleGraph the module graph
* @param {ModuleInfo} info module info
2025-09-11 08:10:10 +08:00
* @param {ExportName} exportName exportName
2025-08-28 18:34:30 +08:00
* @param {ModuleToInfoMap} moduleToInfoMap moduleToInfoMap
* @param {RuntimeSpec} runtime for which runtime
* @param {RequestShortener} requestShortener the request shortener
* @param {RuntimeTemplate} runtimeTemplate the runtime template
2025-08-28 18:34:30 +08:00
* @param {NeededNamespaceObjects} neededNamespaceObjects modules for which a namespace object should be generated
* @param {boolean} asCall asCall
* @param {boolean} depDeferred the dependency is deferred
2024-02-22 22:20:17 +08:00
* @param {boolean | undefined} callContext callContext
* @param {boolean | undefined} strictHarmonyModule strictHarmonyModule
2020-08-29 22:00:03 +08:00
* @param {boolean | undefined} asiSafe asiSafe
* @returns {string} the final name
*/
2018-02-25 09:00:20 +08:00
const getFinalName = (
moduleGraph,
2018-02-25 09:00:20 +08:00
info,
exportName,
moduleToInfoMap,
runtime,
2018-02-25 09:00:20 +08:00
requestShortener,
runtimeTemplate,
neededNamespaceObjects,
2018-02-25 09:00:20 +08:00
asCall,
depDeferred,
callContext,
strictHarmonyModule,
asiSafe
2018-02-25 09:00:20 +08:00
) => {
const binding = getFinalBinding(
moduleGraph,
info,
exportName,
moduleToInfoMap,
runtime,
requestShortener,
runtimeTemplate,
neededNamespaceObjects,
asCall,
depDeferred,
strictHarmonyModule,
asiSafe
);
{
const { ids, comment } = binding;
let reference;
let isPropertyAccess;
if ("rawName" in binding) {
reference = `${binding.rawName}${comment || ""}${propertyAccess(ids)}`;
isPropertyAccess = ids.length > 0;
} else {
const { info, name: exportId } = binding;
const name = info.internalNames.get(exportId);
if (!name) {
throw new Error(
`The export "${exportId}" in "${info.module.readableIdentifier(
requestShortener
)}" has no internal name (existing names: ${
Array.from(
info.internalNames,
([name, symbol]) => `${name}: ${symbol}`
).join(", ") || "none"
})`
);
2017-05-10 19:15:14 +08:00
}
reference = `${name}${comment || ""}${propertyAccess(ids)}`;
isPropertyAccess = ids.length > 1;
2018-02-25 09:00:20 +08:00
}
if (isPropertyAccess && asCall && callContext === false) {
return asiSafe
? `(0,${reference})`
: asiSafe === false
2024-07-31 05:43:19 +08:00
? `;(0,${reference})`
: `/*#__PURE__*/Object(${reference})`;
2018-02-25 09:00:20 +08:00
}
return reference;
2017-05-10 19:15:14 +08:00
}
2017-11-08 18:32:05 +08:00
};
2017-05-10 19:15:14 +08:00
/**
* @typedef {object} ConcatenateModuleHooks
* @property {SyncBailHook<[ConcatenatedModule], boolean>} onDemandExportsGeneration
* @property {SyncBailHook<[Partial<ConcatenatedModuleInfo>, ConcatenatedModuleInfo], boolean | void>} concatenatedModuleInfo
*/
2025-08-28 18:34:30 +08:00
/** @typedef {BuildInfo["topLevelDeclarations"]} TopLevelDeclarations */
/** @type {WeakMap<Compilation, ConcatenateModuleHooks>} */
const compilationHooksMap = new WeakMap();
2017-05-10 19:15:14 +08:00
class ConcatenatedModule extends Module {
/**
* @param {Module} rootModule the root module of the concatenation
2020-03-13 00:51:26 +08:00
* @param {Set<Module>} modules all modules in the concatenation (including the root module)
* @param {RuntimeSpec} runtime the runtime
* @param {Compilation} compilation the compilation
* @param {AssociatedObjectForCache=} associatedObjectForCache object for caching
* @param {string | HashConstructor=} hashFunction hash function to use
* @returns {ConcatenatedModule} the module
*/
static create(
rootModule,
modules,
runtime,
compilation,
associatedObjectForCache,
hashFunction = DEFAULTS.HASH_FUNCTION
) {
const identifier = ConcatenatedModule._createIdentifier(
rootModule,
modules,
associatedObjectForCache,
hashFunction
);
return new ConcatenatedModule({
identifier,
rootModule,
modules,
runtime,
compilation
});
}
/**
* @param {Compilation} compilation the compilation
* @returns {ConcatenateModuleHooks} the attached hooks
*/
static getCompilationHooks(compilation) {
let hooks = compilationHooksMap.get(compilation);
if (hooks === undefined) {
hooks = {
onDemandExportsGeneration: new SyncBailHook(["module"]),
concatenatedModuleInfo: new SyncBailHook([
"updatedInfo",
"concatenatedModuleInfo"
])
};
compilationHooksMap.set(compilation, hooks);
}
return hooks;
}
/**
2024-06-11 21:09:50 +08:00
* @param {object} options options
* @param {string} options.identifier the identifier of the module
2024-10-02 05:18:10 +08:00
* @param {Module} options.rootModule the root module of the concatenation
* @param {RuntimeSpec} options.runtime the selected runtime
2024-10-02 05:18:10 +08:00
* @param {Set<Module>} options.modules all concatenated modules
* @param {Compilation} options.compilation the compilation
*/
constructor({ identifier, rootModule, modules, runtime, compilation }) {
super(JAVASCRIPT_MODULE_TYPE_ESM, null, rootModule && rootModule.layer);
// Info from Factory
/** @type {string} */
this._identifier = identifier;
/** @type {Module} */
2017-05-10 19:15:14 +08:00
this.rootModule = rootModule;
/** @type {Set<Module>} */
2019-12-11 05:58:26 +08:00
this._modules = modules;
this._runtime = runtime;
this.factoryMeta = rootModule && rootModule.factoryMeta;
2025-08-30 01:28:23 +08:00
/** @type {Compilation} */
this.compilation = compilation;
}
/**
* Assuming this module is in the cache. Update the (cached) module with
* the fresh module from the factory. Usually updates internal references
* and properties.
* @param {Module} module fresh module
* @returns {void}
*/
updateCacheModule(module) {
throw new Error("Must not be called");
}
/**
2024-02-17 01:39:12 +08:00
* @returns {SourceTypes} types available (do not mutate)
*/
getSourceTypes() {
2024-11-01 04:19:07 +08:00
return JS_TYPES;
}
get modules() {
2025-07-03 17:06:45 +08:00
return [...this._modules];
2019-12-11 05:58:26 +08:00
}
/**
* @returns {string} a unique identifier of the module
2019-12-11 05:58:26 +08:00
*/
identifier() {
return this._identifier;
}
/**
* @param {RequestShortener} requestShortener the request shortener
* @returns {string} a user readable identifier of the module
*/
readableIdentifier(requestShortener) {
2024-07-31 10:39:30 +08:00
return `${this.rootModule.readableIdentifier(
requestShortener
)} + ${this._modules.size - 1} modules`;
}
/**
* @param {LibIdentOptions} options options
2025-09-11 08:10:10 +08:00
* @returns {LibIdent | null} an identifier for library inclusion
*/
libIdent(options) {
return this.rootModule.libIdent(options);
}
/**
2025-09-11 08:10:10 +08:00
* @returns {NameForCondition | null} absolute path which should be used for condition matching (usually the resource path)
*/
nameForCondition() {
return this.rootModule.nameForCondition();
}
2020-12-05 00:03:12 +08:00
/**
* @param {ModuleGraph} moduleGraph the module graph
* @returns {ConnectionState} how this module should be connected to referencing modules when consumed for side-effects only
*/
getSideEffectsConnectionState(moduleGraph) {
return this.rootModule.getSideEffectsConnectionState(moduleGraph);
}
/**
* @param {WebpackOptions} options webpack options
* @param {Compilation} compilation the compilation
* @param {ResolverWithOptions} resolver the resolver
* @param {InputFileSystem} fs the file system
* @param {BuildCallback} callback callback function
* @returns {void}
*/
build(options, compilation, resolver, fs, callback) {
const { rootModule } = this;
2024-02-22 22:20:17 +08:00
const { moduleArgument, exportsArgument } =
/** @type {BuildInfo} */
(rootModule.buildInfo);
this.buildInfo = {
strict: true,
cacheable: true,
2024-02-22 22:20:17 +08:00
moduleArgument,
exportsArgument,
fileDependencies: new LazySet(),
contextDependencies: new LazySet(),
missingDependencies: new LazySet(),
topLevelDeclarations: new Set(),
assets: undefined
};
this.buildMeta = rootModule.buildMeta;
this.clearDependenciesAndBlocks();
this.clearWarningsAndErrors();
for (const m of this._modules) {
// populate cacheable
2024-02-22 22:20:17 +08:00
if (!(/** @type {BuildInfo} */ (m.buildInfo).cacheable)) {
2025-06-24 22:43:58 +08:00
/** @type {BuildInfo} */
(this.buildInfo).cacheable = false;
}
2017-08-07 17:53:07 +08:00
// populate dependencies
for (const d of m.dependencies.filter(
(dep) =>
!(dep instanceof HarmonyImportDependency) ||
2024-02-22 22:20:17 +08:00
!this._modules.has(
2025-06-24 22:43:58 +08:00
/** @type {Module} */
(compilation.moduleGraph.getModule(dep))
2024-02-22 22:20:17 +08:00
)
)) {
this.dependencies.push(d);
}
// populate blocks
for (const d of m.blocks) {
this.blocks.push(d);
}
// populate warnings
const warnings = m.getWarnings();
if (warnings !== undefined) {
for (const warning of warnings) {
this.addWarning(warning);
2018-01-22 20:52:43 +08:00
}
}
// populate errors
const errors = m.getErrors();
if (errors !== undefined) {
for (const error of errors) {
this.addError(error);
}
}
const { assets, assetsInfo, topLevelDeclarations, needCreateRequire } =
2024-02-22 22:20:17 +08:00
/** @type {BuildInfo} */ (m.buildInfo);
2025-06-24 22:43:58 +08:00
const buildInfo = /** @type {BuildInfo} */ (this.buildInfo);
// populate topLevelDeclarations
2024-02-22 22:20:17 +08:00
if (topLevelDeclarations) {
2025-06-24 22:43:58 +08:00
const topLevelDeclarations = buildInfo.topLevelDeclarations;
if (topLevelDeclarations !== undefined) {
2024-02-22 22:20:17 +08:00
for (const decl of topLevelDeclarations) {
topLevelDeclarations.add(decl);
}
}
} else {
2025-06-24 22:43:58 +08:00
buildInfo.topLevelDeclarations = undefined;
}
// populate needCreateRequire
if (needCreateRequire) {
this.buildInfo.needCreateRequire = true;
}
// populate assets
2024-02-22 22:20:17 +08:00
if (assets) {
2025-06-24 22:43:58 +08:00
if (buildInfo.assets === undefined) {
buildInfo.assets = Object.create(null);
}
2024-08-09 23:42:37 +08:00
Object.assign(
/** @type {NonNullable<BuildInfo["assets"]>} */
2025-06-24 22:43:58 +08:00
(buildInfo.assets),
2024-08-09 23:42:37 +08:00
assets
);
}
2024-02-22 22:20:17 +08:00
if (assetsInfo) {
2025-06-24 22:43:58 +08:00
if (buildInfo.assetsInfo === undefined) {
buildInfo.assetsInfo = new Map();
2017-11-06 23:41:26 +08:00
}
2024-02-22 22:20:17 +08:00
for (const [key, value] of assetsInfo) {
2025-06-24 22:43:58 +08:00
buildInfo.assetsInfo.set(key, value);
}
2017-08-07 17:53:07 +08:00
}
}
callback();
2017-05-10 19:15:14 +08:00
}
/**
* @param {string=} type the source type for which the size should be estimated
* @returns {number} the estimated size of the module (must be non-zero)
*/
size(type) {
2017-05-10 19:15:14 +08:00
// Guess size from embedded modules
let size = 0;
for (const module of this._modules) {
size += module.size(type);
}
return size;
2017-08-07 17:53:07 +08:00
}
/**
* @private
* @param {Module} rootModule the root of the concatenation
* @param {Set<Module>} modulesSet a set of modules which should be concatenated
* @param {RuntimeSpec} runtime for this runtime
* @param {ModuleGraph} moduleGraph the module graph
* @returns {ConcatenationEntry[]} concatenation list
*/
_createConcatenationList(rootModule, modulesSet, runtime, moduleGraph) {
/** @type {ConcatenationEntry[]} */
2017-08-07 17:53:07 +08:00
const list = [];
/** @type {Map<Module, RuntimeSpec | true>} */
const existingEntries = new Map();
2025-08-30 01:28:23 +08:00
const deferEnabled = this.compilation.options.experiments.deferImport;
2017-08-07 17:53:07 +08:00
/**
* @param {Module} module a module
* @returns {Iterable<{ connection: ModuleGraphConnection, runtimeCondition: RuntimeSpec | true }>} imported modules in order
*/
const getConcatenatedImports = (module) => {
2025-07-03 17:06:45 +08:00
const connections = [...moduleGraph.getOutgoingConnections(module)];
if (module === rootModule) {
for (const c of moduleGraph.getOutgoingConnections(this)) {
connections.push(c);
}
}
2022-05-27 19:22:05 +08:00
/**
2025-08-28 18:34:30 +08:00
* @type {{ connection: ModuleGraphConnection, sourceOrder: number, rangeStart: number | undefined, defer?: boolean }[]}
2022-05-27 19:22:05 +08:00
*/
const references = connections
.filter((connection) => {
if (!(connection.dependency instanceof HarmonyImportDependency)) {
return false;
}
return (
2020-10-07 15:10:29 +08:00
connection &&
connection.resolvedOriginModule === module &&
2020-10-07 15:10:29 +08:00
connection.module &&
connection.isTargetActive(runtime)
);
})
.map((connection) => {
2025-08-21 21:23:01 +08:00
const dep =
/** @type {HarmonyImportDependency} */
(connection.dependency);
2022-05-27 19:22:05 +08:00
return {
connection,
sourceOrder: dep.sourceOrder,
rangeStart: dep.range && dep.range[0],
defer: dep.defer
2022-05-27 19:22:05 +08:00
};
});
/**
* bySourceOrder
* @example
* import a from "a"; // sourceOrder=1
* import b from "b"; // sourceOrder=2
*
* byRangeStart
* @example
* import {a, b} from "a"; // sourceOrder=1
* a.a(); // first range
* b.b(); // second range
*
* If the import is deferred, we always move it to the last.
2022-05-27 19:22:05 +08:00
* If there is no reexport, we have the same source.
* If there is reexport, but module has side effects, this will lead to reexport module only.
* If there is side-effects-free reexport, we can get simple deterministic result with range start comparison.
*/
references.sort(concatComparators(bySourceOrder, byRangeStart));
if (deferEnabled) {
// do not combine those two sorts. defer is not the same as source or range which has a comparable number, defer is only moving them.
references.sort(moveDeferToLast);
}
/** @type {Map<Module, { connection: ModuleGraphConnection, runtimeCondition: RuntimeSpec | true }>} */
const referencesMap = new Map();
for (const { connection } of references) {
const runtimeCondition = filterRuntime(runtime, (r) =>
connection.isTargetActive(r)
);
if (runtimeCondition === false) continue;
const module = connection.module;
const entry = referencesMap.get(module);
if (entry === undefined) {
referencesMap.set(module, { connection, runtimeCondition });
continue;
}
entry.runtimeCondition = mergeRuntimeConditionNonFalse(
entry.runtimeCondition,
runtimeCondition,
runtime
);
}
return referencesMap.values();
2017-11-08 18:32:05 +08:00
};
2017-08-07 17:53:07 +08:00
/**
* @param {ModuleGraphConnection} connection graph connection
* @param {RuntimeSpec | true} runtimeCondition runtime condition
* @returns {void}
*/
const enterModule = (connection, runtimeCondition) => {
const module = connection.module;
2018-02-25 09:00:20 +08:00
if (!module) return;
const existingEntry = existingEntries.get(module);
if (existingEntry === true) {
return;
}
2018-02-25 09:00:20 +08:00
if (modulesSet.has(module)) {
existingEntries.set(module, true);
if (runtimeCondition !== true) {
throw new Error(
`Cannot runtime-conditional concatenate a module (${module.identifier()} in ${this.rootModule.identifier()}, ${runtimeConditionToString(
runtimeCondition
)}). This should not happen.`
);
}
2017-08-07 17:53:07 +08:00
const imports = getConcatenatedImports(module);
for (const { connection, runtimeCondition } of imports) {
enterModule(connection, runtimeCondition);
}
2017-08-07 17:53:07 +08:00
list.push({
type: "concatenated",
module: connection.module,
runtimeCondition
2017-08-07 17:53:07 +08:00
});
} else {
if (existingEntry !== undefined) {
const reducedRuntimeCondition = subtractRuntimeCondition(
runtimeCondition,
existingEntry,
runtime
);
if (reducedRuntimeCondition === false) return;
runtimeCondition = reducedRuntimeCondition;
existingEntries.set(
connection.module,
mergeRuntimeConditionNonFalse(
existingEntry,
runtimeCondition,
runtime
)
);
} else {
existingEntries.set(connection.module, runtimeCondition);
}
if (list.length > 0) {
const lastItem = list[list.length - 1];
if (
lastItem.type === "external" &&
lastItem.module === connection.module
) {
lastItem.runtimeCondition = mergeRuntimeCondition(
lastItem.runtimeCondition,
runtimeCondition,
runtime
);
return;
}
}
2017-08-07 17:53:07 +08:00
list.push({
type: "external",
2017-08-07 19:56:50 +08:00
get module() {
// We need to use a getter here, because the module in the dependency
// could be replaced by some other process (i. e. also replaced with a
// concatenated module)
return connection.module;
},
runtimeCondition
2017-08-07 17:53:07 +08:00
});
}
2017-11-08 18:32:05 +08:00
};
2017-08-07 17:53:07 +08:00
existingEntries.set(rootModule, true);
const imports = getConcatenatedImports(rootModule);
for (const { connection, runtimeCondition } of imports) {
enterModule(connection, runtimeCondition);
}
list.push({
type: "concatenated",
module: rootModule,
runtimeCondition: true
});
2017-08-07 17:53:07 +08:00
return list;
2017-05-10 19:15:14 +08:00
}
/**
* @param {Module} rootModule the root module of the concatenation
* @param {Set<Module>} modules all modules in the concatenation (including the root module)
* @param {AssociatedObjectForCache=} associatedObjectForCache object for caching
* @param {string | HashConstructor=} hashFunction hash function to use
* @returns {string} the identifier
*/
static _createIdentifier(
rootModule,
modules,
associatedObjectForCache,
hashFunction = DEFAULTS.HASH_FUNCTION
) {
const cachedMakePathsRelative = makePathsRelative.bindContextCache(
2024-02-22 22:20:17 +08:00
/** @type {string} */ (rootModule.context),
2020-01-15 06:14:47 +08:00
associatedObjectForCache
);
2024-07-31 04:09:42 +08:00
const identifiers = [];
for (const module of modules) {
identifiers.push(cachedMakePathsRelative(module.identifier()));
}
identifiers.sort();
const hash = createHash(hashFunction);
hash.update(identifiers.join(" "));
2024-07-31 10:39:30 +08:00
return `${rootModule.identifier()}|${hash.digest("hex")}`;
}
/**
2025-08-28 18:34:30 +08:00
* @param {FileSystemDependencies} fileDependencies set where file dependencies are added to
* @param {FileSystemDependencies} contextDependencies set where context dependencies are added to
* @param {FileSystemDependencies} missingDependencies set where missing dependencies are added to
* @param {FileSystemDependencies} buildDependencies set where build dependencies are added to
*/
addCacheDependencies(
fileDependencies,
contextDependencies,
missingDependencies,
buildDependencies
) {
for (const module of this._modules) {
module.addCacheDependencies(
fileDependencies,
contextDependencies,
missingDependencies,
buildDependencies
);
}
}
2018-07-11 19:05:13 +08:00
/**
* @param {CodeGenerationContext} context context for code generation
* @returns {CodeGenerationResult} result
2018-07-11 19:05:13 +08:00
*/
codeGeneration({
dependencyTemplates,
runtimeTemplate,
moduleGraph,
chunkGraph,
2021-12-01 21:15:19 +08:00
runtime: generationRuntime,
codeGenerationResults
}) {
const { concatenatedModuleInfo } = ConcatenatedModule.getCompilationHooks(
2025-08-30 01:28:23 +08:00
this.compilation
);
2024-10-02 05:18:10 +08:00
/** @type {RuntimeRequirements} */
2019-10-09 17:49:48 +08:00
const runtimeRequirements = new Set();
const runtime = intersectRuntime(generationRuntime, this._runtime);
const requestShortener = runtimeTemplate.requestShortener;
2020-03-13 00:51:26 +08:00
// Meta info for each module
const [modulesWithInfo, moduleToInfoMap] = this._getModulesWithInfo(
moduleGraph,
runtime
);
2017-05-10 19:15:14 +08:00
// Set with modules that need a generated namespace object
2025-08-28 18:34:30 +08:00
/** @type {NeededNamespaceObjects} */
const neededNamespaceObjects = new Set();
// Default disallowed names
const allUsedNames = new Set(INITIAL_USED_NAMES);
const chunks = chunkGraph.getModuleChunks(this);
// Add names already used in the current chunk scope
for (const chunk of chunks) {
if (ConcatenationScope.chunkUsedNames.has(chunk)) {
for (const name of ConcatenationScope.chunkUsedNames.get(chunk) || []) {
allUsedNames.add(name);
}
}
}
2017-05-10 19:15:14 +08:00
// Generate source code and analyse scopes
// Prepare a ReplaceSource for the final source
for (const info of moduleToInfoMap.values()) {
this._analyseModule(
moduleToInfoMap,
info,
dependencyTemplates,
runtimeTemplate,
moduleGraph,
chunkGraph,
2021-12-01 21:15:19 +08:00
runtime,
2024-10-02 05:18:10 +08:00
/** @type {CodeGenerationResults} */
(codeGenerationResults),
allUsedNames
);
2018-01-22 20:52:43 +08:00
}
2017-05-10 19:15:14 +08:00
// Record the names registered by the current ConcatenatedModule into the chunk scope
if (INITIAL_USED_NAMES.size !== allUsedNames.size) {
for (const name of allUsedNames) {
if (INITIAL_USED_NAMES.has(name)) continue;
for (const chunk of chunks) {
if (!ConcatenationScope.chunkUsedNames.has(chunk)) {
ConcatenationScope.chunkUsedNames.set(chunk, new Set([name]));
} else {
/** @type {Set<string>} */ (
ConcatenationScope.chunkUsedNames.get(chunk)
).add(name);
}
}
}
}
// Updated Top level declarations are created by renaming
2025-08-28 18:34:30 +08:00
/** @type {TopLevelDeclarations} */
const topLevelDeclarations = new Set();
2017-05-10 19:15:14 +08:00
// List of additional names in scope for module references
/** @type {Map<string, ScopeInfo>} */
const usedNamesInScopeInfo = new Map();
// Set of already checked scopes
const ignoredScopes = new Set();
2017-05-10 19:15:14 +08:00
// get all global names
2018-02-25 09:00:20 +08:00
for (const info of modulesWithInfo) {
if (info.type === "concatenated") {
// ignore symbols from moduleScope
if (info.moduleScope) {
ignoredScopes.add(info.moduleScope);
}
// The super class expression in class scopes behaves weird
// We get ranges of all super class expressions to make
// renaming to work correctly
const superClassCache = new WeakMap();
2024-02-22 22:20:17 +08:00
/**
* @param {Scope} scope scope
* @returns {{ range: Range, variables: Variable[] }[]} result
2024-02-22 22:20:17 +08:00
*/
const getSuperClassExpressions = (scope) => {
const cacheEntry = superClassCache.get(scope);
if (cacheEntry !== undefined) return cacheEntry;
const superClassExpressions = [];
for (const childScope of scope.childScopes) {
if (childScope.type !== "class") continue;
2020-08-03 02:09:36 +08:00
const block = childScope.block;
if (
(block.type === "ClassDeclaration" ||
block.type === "ClassExpression") &&
block.superClass
) {
superClassExpressions.push({
range: /** @type {Range} */ (block.superClass.range),
2020-08-03 02:09:36 +08:00
variables: childScope.variables
});
}
}
superClassCache.set(scope, superClassExpressions);
return superClassExpressions;
};
// add global symbols
if (info.globalScope) {
for (const reference of info.globalScope.through) {
const name = reference.identifier.name;
2020-09-11 15:06:24 +08:00
if (ConcatenationScope.isModuleReference(name)) {
const match = ConcatenationScope.matchModuleReference(name);
2020-10-20 19:49:48 +08:00
if (!match) continue;
const referencedInfo = modulesWithInfo[match.index];
if (referencedInfo.type === "reference") {
throw new Error("Module reference can't point to a reference");
}
const binding = getFinalBinding(
moduleGraph,
referencedInfo,
match.ids,
moduleToInfoMap,
runtime,
requestShortener,
runtimeTemplate,
neededNamespaceObjects,
false,
match.deferredImport,
2024-02-22 22:20:17 +08:00
/** @type {BuildMeta} */
(info.module.buildMeta).strictHarmonyModule,
true
);
if (!binding.ids) continue;
2021-05-11 15:31:46 +08:00
const { usedNames, alreadyCheckedScopes } =
getUsedNamesInScopeInfo(
usedNamesInScopeInfo,
2021-05-11 15:31:46 +08:00
binding.info.module.identifier(),
"name" in binding ? binding.name : ""
);
for (const expr of getSuperClassExpressions(reference.from)) {
if (
2024-02-22 22:20:17 +08:00
expr.range[0] <=
/** @type {Range} */ (reference.identifier.range)[0] &&
expr.range[1] >=
/** @type {Range} */ (reference.identifier.range)[1]
) {
for (const variable of expr.variables) {
usedNames.add(variable.name);
}
}
}
addScopeSymbols(
reference.from,
usedNames,
alreadyCheckedScopes,
ignoredScopes
);
} else {
allUsedNames.add(name);
}
}
2018-01-22 20:52:43 +08:00
}
}
2018-01-22 20:52:43 +08:00
}
2017-05-10 19:15:14 +08:00
/**
* @param {string} name the name to find a new name for
* @param {ConcatenatedModuleInfo} info the info of the module
* @param {Reference[]} references the references to the name
* @returns {string|undefined} the new name or undefined if the name is not found
*/
const _findNewName = (name, info, references) => {
const { usedNames, alreadyCheckedScopes } = getUsedNamesInScopeInfo(
usedNamesInScopeInfo,
info.module.identifier(),
name
);
if (allUsedNames.has(name) || usedNames.has(name)) {
for (const ref of references) {
addScopeSymbols(
ref.from,
usedNames,
alreadyCheckedScopes,
ignoredScopes
);
}
const newName = findNewName(
name,
allUsedNames,
usedNames,
info.module.readableIdentifier(requestShortener)
);
allUsedNames.add(newName);
info.internalNames.set(name, newName);
topLevelDeclarations.add(newName);
return newName;
}
};
/**
* @param {string} name the name to find a new name for
* @param {ConcatenatedModuleInfo} info the info of the module
* @param {Reference[]} references the references to the name
* @returns {string|undefined} the new name or undefined if the name is not found
*/
const _findNewNameForSpecifier = (name, info, references) => {
const { usedNames: moduleUsedNames, alreadyCheckedScopes } =
getUsedNamesInScopeInfo(
usedNamesInScopeInfo,
info.module.identifier(),
name
);
const referencesUsedNames = new Set();
for (const ref of references) {
addScopeSymbols(
ref.from,
referencesUsedNames,
alreadyCheckedScopes,
ignoredScopes
);
}
if (moduleUsedNames.has(name) || referencesUsedNames.has(name)) {
const newName = findNewName(
name,
allUsedNames,
new Set([...moduleUsedNames, ...referencesUsedNames]),
info.module.readableIdentifier(requestShortener)
);
allUsedNames.add(newName);
topLevelDeclarations.add(newName);
return newName;
}
};
// generate names for symbols
for (const info of moduleToInfoMap.values()) {
const { usedNames: namespaceObjectUsedNames } = getUsedNamesInScopeInfo(
usedNamesInScopeInfo,
info.module.identifier(),
""
);
2018-02-25 09:00:20 +08:00
switch (info.type) {
case "concatenated": {
2024-10-25 02:13:59 +08:00
const variables = /** @type {Scope} */ (info.moduleScope).variables;
for (const variable of variables) {
2018-02-25 09:00:20 +08:00
const name = variable.name;
const references = getAllReferences(variable);
const newName = _findNewName(name, info, references);
if (newName) {
2024-10-25 02:13:59 +08:00
const source = /** @type {ReplaceSource} */ (info.source);
2025-07-03 17:06:45 +08:00
const allIdentifiers = new Set([
...references.map((r) => r.identifier),
2025-07-03 17:06:45 +08:00
...variable.identifiers
]);
2018-02-25 09:00:20 +08:00
for (const identifier of allIdentifiers) {
2024-02-22 22:20:17 +08:00
const r = /** @type {Range} */ (identifier.range);
2024-08-15 02:38:08 +08:00
const path = getPathInAst(
/** @type {NonNullable<ConcatenatedModuleInfo["ast"]>} */
(info.ast),
identifier
);
2021-02-20 02:32:23 +08:00
if (path && path.length > 1) {
const maybeProperty =
2021-02-24 19:54:17 +08:00
path[1].type === "AssignmentPattern" &&
path[1].left === path[0]
? path[2]
: path[1];
2021-02-20 02:32:23 +08:00
if (
maybeProperty.type === "Property" &&
maybeProperty.shorthand
) {
source.insert(r[1], `: ${newName}`);
continue;
}
}
2021-02-20 02:32:23 +08:00
source.replace(r[0], r[1] - 1, newName);
}
2018-02-25 09:00:20 +08:00
} else {
allUsedNames.add(name);
info.internalNames.set(name, name);
topLevelDeclarations.add(name);
2018-01-22 20:52:43 +08:00
}
2017-05-10 19:15:14 +08:00
}
let namespaceObjectName;
if (info.namespaceExportSymbol) {
namespaceObjectName = info.internalNames.get(
info.namespaceExportSymbol
);
} else {
namespaceObjectName = findNewName(
"namespaceObject",
allUsedNames,
namespaceObjectUsedNames,
info.module.readableIdentifier(requestShortener)
);
allUsedNames.add(namespaceObjectName);
}
2024-03-18 23:28:40 +08:00
info.namespaceObjectName =
/** @type {string} */
(namespaceObjectName);
2025-06-24 22:43:58 +08:00
topLevelDeclarations.add(
/** @type {string} */
(namespaceObjectName)
);
2018-02-25 09:00:20 +08:00
break;
}
case "external": {
const externalName = findNewName(
2018-02-25 09:00:20 +08:00
"",
allUsedNames,
namespaceObjectUsedNames,
2018-02-25 09:00:20 +08:00
info.module.readableIdentifier(requestShortener)
);
allUsedNames.add(externalName);
2020-10-20 19:49:48 +08:00
info.name = externalName;
topLevelDeclarations.add(externalName);
if (info.deferred) {
const externalName = findNewName(
"deferred",
allUsedNames,
namespaceObjectUsedNames,
info.module.readableIdentifier(requestShortener)
);
allUsedNames.add(externalName);
info.deferredName = externalName;
topLevelDeclarations.add(externalName);
const externalNameInterop = findNewName(
"deferredNamespaceObject",
allUsedNames,
namespaceObjectUsedNames,
info.module.readableIdentifier(requestShortener)
);
allUsedNames.add(externalNameInterop);
info.deferredNamespaceObjectName = externalNameInterop;
topLevelDeclarations.add(externalNameInterop);
}
2018-02-25 09:00:20 +08:00
break;
}
}
2024-02-22 22:20:17 +08:00
const buildMeta = /** @type {BuildMeta} */ (info.module.buildMeta);
if (buildMeta.exportsType !== "namespace") {
const externalNameInterop = findNewName(
"namespaceObject",
allUsedNames,
namespaceObjectUsedNames,
info.module.readableIdentifier(requestShortener)
);
allUsedNames.add(externalNameInterop);
info.interopNamespaceObjectName = externalNameInterop;
topLevelDeclarations.add(externalNameInterop);
}
if (
2024-02-22 22:20:17 +08:00
buildMeta.exportsType === "default" &&
buildMeta.defaultObject !== "redirect" &&
info.interopNamespaceObject2Used
) {
const externalNameInterop = findNewName(
"namespaceObject2",
allUsedNames,
namespaceObjectUsedNames,
info.module.readableIdentifier(requestShortener)
);
allUsedNames.add(externalNameInterop);
info.interopNamespaceObject2Name = externalNameInterop;
topLevelDeclarations.add(externalNameInterop);
}
2024-02-22 22:20:17 +08:00
if (buildMeta.exportsType === "dynamic" || !buildMeta.exportsType) {
const externalNameInterop = findNewName(
"default",
allUsedNames,
namespaceObjectUsedNames,
info.module.readableIdentifier(requestShortener)
);
allUsedNames.add(externalNameInterop);
info.interopDefaultAccessName = externalNameInterop;
topLevelDeclarations.add(externalNameInterop);
}
2018-01-22 20:52:43 +08:00
}
2017-05-10 19:15:14 +08:00
2020-09-11 15:06:24 +08:00
// Find and replace references to modules
for (const info of moduleToInfoMap.values()) {
2018-02-25 09:00:20 +08:00
if (info.type === "concatenated") {
2024-08-09 23:42:37 +08:00
const globalScope = /** @type {Scope} */ (info.globalScope);
// group references by name
const referencesByName = new Map();
2024-08-09 23:42:37 +08:00
for (const reference of globalScope.through) {
const name = reference.identifier.name;
if (!referencesByName.has(name)) {
referencesByName.set(name, []);
}
referencesByName.get(name).push(reference);
}
for (const [name, references] of referencesByName) {
const match = ConcatenationScope.matchModuleReference(name);
2018-02-25 09:00:20 +08:00
if (match) {
const referencedInfo = modulesWithInfo[match.index];
if (referencedInfo.type === "reference") {
throw new Error("Module reference can't point to a reference");
}
const concatenationScope = /** @type {ConcatenatedModuleInfo} */ (
referencedInfo
).concatenationScope;
const exportId = match.ids[0];
const specifier =
concatenationScope && concatenationScope.getRawExport(exportId);
if (specifier) {
const newName = _findNewNameForSpecifier(
specifier,
info,
references
);
const initFragmentChanged =
newName &&
concatenatedModuleInfo.call(
{
rawExportMap: new Map([
[exportId, /** @type {string} */ (newName)]
])
},
/** @type {ConcatenatedModuleInfo} */ (referencedInfo)
);
if (initFragmentChanged) {
concatenationScope.setRawExportMap(exportId, newName);
}
}
2018-02-25 09:00:20 +08:00
const finalName = getFinalName(
moduleGraph,
referencedInfo,
match.ids,
2018-02-25 09:00:20 +08:00
moduleToInfoMap,
runtime,
2018-02-25 09:00:20 +08:00
requestShortener,
runtimeTemplate,
neededNamespaceObjects,
match.call,
match.deferredImport,
!match.directImport,
2024-02-22 22:20:17 +08:00
/** @type {BuildMeta} */
(info.module.buildMeta).strictHarmonyModule,
match.asiSafe
2018-02-25 09:00:20 +08:00
);
for (const reference of references) {
const r = /** @type {Range} */ (reference.identifier.range);
const source = /** @type {ReplaceSource} */ (info.source);
// range is extended by 2 chars to cover the appended "._"
source.replace(r[0], r[1] + 1, finalName);
}
2017-05-10 19:15:14 +08:00
}
2018-01-22 20:52:43 +08:00
}
}
2018-01-22 20:52:43 +08:00
}
2017-05-10 19:15:14 +08:00
// Map with all root exposed used exports
/** @type {Map<string, (requestShortener: RequestShortener) => string>} */
const exportsMap = new Map();
// Set with all root exposed unused exports
/** @type {Set<string>} */
const unusedExports = new Set();
const rootInfo =
/** @type {ConcatenatedModuleInfo} */
(moduleToInfoMap.get(this.rootModule));
2024-02-22 22:20:17 +08:00
const strictHarmonyModule =
/** @type {BuildMeta} */
(rootInfo.module.buildMeta).strictHarmonyModule;
const exportsInfo = moduleGraph.getExportsInfo(rootInfo.module);
/** @type {Record<string, string>} */
const exportsFinalName = {};
for (const exportInfo of exportsInfo.orderedExports) {
const name = exportInfo.name;
if (exportInfo.provided === false) continue;
const used = exportInfo.getUsedName(undefined, runtime);
if (!used) {
unusedExports.add(name);
continue;
}
exportsMap.set(used, (requestShortener) => {
try {
const finalName = getFinalName(
moduleGraph,
rootInfo,
[name],
moduleToInfoMap,
runtime,
requestShortener,
runtimeTemplate,
neededNamespaceObjects,
false,
false,
false,
strictHarmonyModule,
true
);
exportsFinalName[used] = finalName;
return `/* ${
exportInfo.isReexport() ? "reexport" : "binding"
} */ ${finalName}`;
2024-07-31 15:37:05 +08:00
} catch (err) {
2024-02-22 22:20:17 +08:00
/** @type {Error} */
2024-07-31 15:37:05 +08:00
(err).message +=
2024-07-31 05:43:19 +08:00
`\nwhile generating the root export '${name}' (used name: '${used}')`;
2024-07-31 15:37:05 +08:00
throw err;
}
});
}
2017-05-10 19:15:14 +08:00
const result = new ConcatSource();
2017-08-07 20:12:31 +08:00
// add harmony compatibility flag (must be first because of possible circular dependencies)
let shouldAddHarmonyFlag = false;
const rootExportsInfo = moduleGraph.getExportsInfo(this);
if (
rootExportsInfo.otherExportsInfo.getUsed(runtime) !== UsageState.Unused ||
rootExportsInfo.getReadOnlyExportInfo("__esModule").getUsed(runtime) !==
UsageState.Unused
) {
shouldAddHarmonyFlag = true;
2017-05-10 19:15:14 +08:00
}
2017-08-07 20:12:31 +08:00
// define exports
if (exportsMap.size > 0) {
const definitions = [];
for (const [key, value] of exportsMap) {
definitions.push(
2023-05-05 11:41:15 +08:00
`\n ${propertyName(key)}: ${runtimeTemplate.returningFunction(
value(requestShortener)
)}`
);
}
2025-04-06 22:53:09 +08:00
const { onDemandExportsGeneration } =
2025-08-30 01:28:23 +08:00
ConcatenatedModule.getCompilationHooks(this.compilation);
runtimeRequirements.add(RuntimeGlobals.exports);
runtimeRequirements.add(RuntimeGlobals.definePropertyGetters);
if (shouldAddHarmonyFlag) {
result.add("// ESM COMPAT FLAG\n");
result.add(
runtimeTemplate.defineEsModuleFlagStatement({
exportsArgument: this.exportsArgument,
runtimeRequirements
})
);
}
if (onDemandExportsGeneration.call(this)) {
/** @type {BuildMeta} */ (this.buildMeta).factoryExportsBinding =
"\n// EXPORTS\n" +
`${RuntimeGlobals.definePropertyGetters}(${
this.exportsArgument
}, {${definitions.join(",")}\n});\n`;
/** @type {BuildMeta} */ (this.buildMeta).exportsFinalName =
exportsFinalName;
} else {
2024-07-31 12:23:44 +08:00
result.add("\n// EXPORTS\n");
result.add(
`${RuntimeGlobals.definePropertyGetters}(${
this.exportsArgument
}, {${definitions.join(",")}\n});\n`
);
}
}
// list unused exports
if (unusedExports.size > 0) {
result.add(
`\n// UNUSED EXPORTS: ${joinIterableWithComma(unusedExports)}\n`
);
}
// generate namespace objects
const namespaceObjectSources = new Map();
for (const info of neededNamespaceObjects) {
if (info.namespaceExportSymbol) continue;
const nsObj = [];
const exportsInfo = moduleGraph.getExportsInfo(info.module);
for (const exportInfo of exportsInfo.orderedExports) {
if (exportInfo.provided === false) continue;
const usedName = exportInfo.getUsedName(undefined, runtime);
if (usedName) {
const finalName = getFinalName(
moduleGraph,
info,
[exportInfo.name],
moduleToInfoMap,
runtime,
requestShortener,
runtimeTemplate,
neededNamespaceObjects,
false,
false,
undefined,
2024-02-22 22:20:17 +08:00
/** @type {BuildMeta} */
(info.module.buildMeta).strictHarmonyModule,
true
);
nsObj.push(
2023-05-05 11:41:15 +08:00
`\n ${propertyName(usedName)}: ${runtimeTemplate.returningFunction(
finalName
)}`
);
}
}
const name = info.namespaceObjectName;
const defineGetters =
nsObj.length > 0
? `${RuntimeGlobals.definePropertyGetters}(${name}, {${nsObj.join(
","
2024-07-31 05:43:19 +08:00
)}\n});\n`
: "";
if (nsObj.length > 0) {
runtimeRequirements.add(RuntimeGlobals.definePropertyGetters);
}
namespaceObjectSources.set(
info,
`
// NAMESPACE OBJECT: ${info.module.readableIdentifier(requestShortener)}
var ${name} = {};
${RuntimeGlobals.makeNamespaceObject}(${name});
${defineGetters}`
);
runtimeRequirements.add(RuntimeGlobals.makeNamespaceObject);
}
2017-08-07 20:12:31 +08:00
// define required namespace objects (must be before evaluation modules)
2018-02-25 09:00:20 +08:00
for (const info of modulesWithInfo) {
if (info.type === "concatenated") {
const source = namespaceObjectSources.get(info);
if (!source) continue;
result.add(source);
2017-08-07 20:12:31 +08:00
}
if (info.type === "external" && info.deferred) {
const moduleId = JSON.stringify(chunkGraph.getModuleId(info.module));
const loader = getOptimizedDeferredModule(
runtimeTemplate,
info.module.getExportsType(
moduleGraph,
/** @type {BuildMeta} */
(this.rootModule.buildMeta).strictHarmonyModule
),
moduleId,
// an async module will opt-out of the concat module optimization.
[]
);
runtimeRequirements.add(RuntimeGlobals.require);
result.add(
`\n// DEFERRED EXTERNAL MODULE: ${info.module.readableIdentifier(requestShortener)}\nvar ${info.deferredName} = ${loader};`
);
}
2018-01-22 20:52:43 +08:00
}
2017-08-07 20:12:31 +08:00
/** @type {InitFragment<ChunkRenderContext>[]} */
const chunkInitFragments = [];
2025-08-30 01:28:23 +08:00
const deferEnabled = this.compilation.options.experiments.deferImport;
2017-08-07 20:12:31 +08:00
// evaluate modules in order
for (const rawInfo of modulesWithInfo) {
let name;
let isConditional = false;
const info = rawInfo.type === "reference" ? rawInfo.target : rawInfo;
2018-02-25 09:00:20 +08:00
switch (info.type) {
case "concatenated": {
2018-02-25 09:00:20 +08:00
result.add(
`\n;// ${info.module.readableIdentifier(requestShortener)}\n`
2018-02-25 09:00:20 +08:00
);
// If a module is deferred in other places, but used as non-deferred here,
// the module itself will be emitted as mod_deferred (in the case "external"),
// we need to emit an extra import declaration to evaluate it in order.
if (deferEnabled) {
for (const dep of info.module.dependencies) {
if (
!(/** @type {HarmonyImportDependency} */ (dep).defer) &&
(dep instanceof HarmonyImportSideEffectDependency ||
dep instanceof HarmonyImportSpecifierDependency)
) {
const referredModule = moduleGraph.getModule(dep);
if (!referredModule) {
if (dep instanceof HarmonyImportSideEffectDependency) {
continue;
} else {
throw new Error(
"Deferred module used, but no module in the graph."
);
}
}
if (moduleGraph.isDeferred(referredModule)) {
const deferredModuleInfo = /** @type {ExternalModuleInfo} */ (
modulesWithInfo.find(
(i) =>
i.type === "external" && i.module === referredModule
)
);
if (!deferredModuleInfo) continue;
result.add(
`\n// non-deferred import to a deferred module (${referredModule.readableIdentifier(requestShortener)})\nvar ${deferredModuleInfo.name} = ${deferredModuleInfo.deferredName}.a;`
);
}
}
}
}
2024-10-25 02:13:59 +08:00
result.add(/** @type {ReplaceSource} */ (info.source));
if (info.chunkInitFragments) {
for (const f of info.chunkInitFragments) chunkInitFragments.push(f);
}
if (info.runtimeRequirements) {
for (const r of info.runtimeRequirements) {
runtimeRequirements.add(r);
}
}
name = info.namespaceObjectName;
break;
}
case "external": {
// deferred case is handled in the "const info of modulesWithInfo" loop above
if (!info.deferred) {
result.add(
`\n// EXTERNAL MODULE: ${info.module.readableIdentifier(
requestShortener
)}\n`
);
runtimeRequirements.add(RuntimeGlobals.require);
const { runtimeCondition } =
/** @type {ExternalModuleInfo | ReferenceToModuleInfo} */
(rawInfo);
const condition = runtimeTemplate.runtimeConditionExpression({
chunkGraph,
runtimeCondition,
runtime,
runtimeRequirements
});
if (condition !== "true") {
isConditional = true;
result.add(`if (${condition}) {\n`);
}
const moduleId = JSON.stringify(
chunkGraph.getModuleId(info.module)
);
result.add(`var ${info.name} = __webpack_require__(${moduleId});`);
name = info.name;
}
2017-08-07 17:53:07 +08:00
break;
}
2017-08-07 17:53:07 +08:00
default:
2020-03-30 23:56:37 +08:00
// @ts-expect-error never is expected here
throw new Error(`Unsupported concatenation entry type ${info.type}`);
2017-05-10 19:15:14 +08:00
}
if (info.type === "external" && info.deferredNamespaceObjectUsed) {
runtimeRequirements.add(RuntimeGlobals.makeDeferredNamespaceObject);
result.add(
`\nvar ${info.deferredNamespaceObjectName} = /*#__PURE__*/${
RuntimeGlobals.makeDeferredNamespaceObject
}(${JSON.stringify(
chunkGraph.getModuleId(info.module)
)}, ${getMakeDeferredNamespaceModeFromExportsType(
info.module.getExportsType(moduleGraph, strictHarmonyModule)
)});`
);
}
if (info.interopNamespaceObjectUsed) {
runtimeRequirements.add(RuntimeGlobals.createFakeNamespaceObject);
result.add(
`\nvar ${info.interopNamespaceObjectName} = /*#__PURE__*/${RuntimeGlobals.createFakeNamespaceObject}(${name}, 2);`
);
}
if (info.interopNamespaceObject2Used) {
runtimeRequirements.add(RuntimeGlobals.createFakeNamespaceObject);
result.add(
`\nvar ${info.interopNamespaceObject2Name} = /*#__PURE__*/${RuntimeGlobals.createFakeNamespaceObject}(${name});`
);
}
if (info.interopDefaultAccessUsed) {
runtimeRequirements.add(RuntimeGlobals.compatGetDefaultExport);
result.add(
`\nvar ${info.interopDefaultAccessName} = /*#__PURE__*/${RuntimeGlobals.compatGetDefaultExport}(${name});`
);
}
if (isConditional) {
result.add("\n}");
}
2018-01-22 20:52:43 +08:00
}
2017-05-10 19:15:14 +08:00
const data = new Map();
if (chunkInitFragments.length > 0) {
data.set("chunkInitFragments", chunkInitFragments);
}
data.set("topLevelDeclarations", topLevelDeclarations);
2019-12-11 05:58:26 +08:00
/** @type {CodeGenerationResult} */
const resultEntry = {
sources: new Map([["javascript", new CachedSource(result)]]),
data,
runtimeRequirements
};
2019-12-11 05:58:26 +08:00
return resultEntry;
2017-05-10 19:15:14 +08:00
}
/**
2025-08-28 18:34:30 +08:00
* @param {ModuleToInfoMap} modulesMap modulesMap
* @param {ModuleInfo} info info
* @param {DependencyTemplates} dependencyTemplates dependencyTemplates
* @param {RuntimeTemplate} runtimeTemplate runtimeTemplate
* @param {ModuleGraph} moduleGraph moduleGraph
* @param {ChunkGraph} chunkGraph chunkGraph
* @param {RuntimeSpec} runtime runtime
2021-12-01 21:15:19 +08:00
* @param {CodeGenerationResults} codeGenerationResults codeGenerationResults
* @param {Set<string>} usedNames used names
*/
_analyseModule(
modulesMap,
info,
dependencyTemplates,
runtimeTemplate,
moduleGraph,
chunkGraph,
2021-12-01 21:15:19 +08:00
runtime,
codeGenerationResults,
usedNames
) {
if (info.type === "concatenated") {
const m = info.module;
try {
2020-09-11 15:06:24 +08:00
// Create a concatenation scope to track and capture information
const concatenationScope = new ConcatenationScope(
modulesMap,
info,
usedNames
);
2020-09-11 15:06:24 +08:00
// TODO cache codeGeneration results
const codeGenResult = m.codeGeneration({
dependencyTemplates,
runtimeTemplate,
moduleGraph,
chunkGraph,
2020-09-11 15:06:24 +08:00
runtime,
2021-12-01 21:15:19 +08:00
concatenationScope,
codeGenerationResults,
2024-11-01 04:19:07 +08:00
sourceTypes: JS_TYPES
});
const source =
/** @type {Source} */
(codeGenResult.sources.get("javascript"));
const data = codeGenResult.data;
const chunkInitFragments = data && data.get("chunkInitFragments");
const code = source.source().toString();
let ast;
try {
ast = JavascriptParser._parse(code, {
sourceType: "module"
});
2024-08-15 02:38:08 +08:00
} catch (_err) {
2025-04-26 01:43:01 +08:00
const err =
/** @type {Error & { loc?: { line: number, column: number } }} */
(_err);
if (
err.loc &&
typeof err.loc === "object" &&
typeof err.loc.line === "number"
) {
const lineNumber = err.loc.line;
const lines = code.split("\n");
2024-07-31 10:39:30 +08:00
err.message += `\n| ${lines
.slice(Math.max(0, lineNumber - 3), lineNumber + 2)
.join("\n| ")}`;
}
throw err;
}
const scopeManager = eslintScope.analyze(ast, {
ecmaVersion: 6,
sourceType: "module",
optimistic: true,
ignoreEval: true,
impliedStrict: true
});
2024-02-22 22:20:17 +08:00
const globalScope = /** @type {Scope} */ (scopeManager.acquire(ast));
const moduleScope = globalScope.childScopes[0];
const resultSource = new ReplaceSource(source);
2024-10-02 05:18:10 +08:00
info.runtimeRequirements =
/** @type {ReadOnlyRuntimeRequirements} */
(codeGenResult.runtimeRequirements);
info.ast = ast;
info.internalSource = source;
info.source = resultSource;
info.chunkInitFragments = chunkInitFragments;
info.globalScope = globalScope;
info.moduleScope = moduleScope;
info.concatenationScope = concatenationScope;
} catch (err) {
2024-02-22 22:20:17 +08:00
/** @type {Error} */
2024-07-31 05:43:19 +08:00
(err).message +=
`\nwhile analyzing module ${m.identifier()} for concatenation`;
throw err;
}
}
}
/**
* @param {ModuleGraph} moduleGraph the module graph
* @param {RuntimeSpec} runtime the runtime
2025-08-28 18:34:30 +08:00
* @returns {[ModuleInfoOrReference[], ModuleToInfoMap]} module info items
*/
_getModulesWithInfo(moduleGraph, runtime) {
const orderedConcatenationList = this._createConcatenationList(
this.rootModule,
this._modules,
runtime,
moduleGraph
);
2025-08-28 18:34:30 +08:00
/** @type {ModuleToInfoMap} */
const map = new Map();
const list = orderedConcatenationList.map((info, index) => {
let item = map.get(info.module);
if (item === undefined) {
switch (info.type) {
case "concatenated":
item = {
type: "concatenated",
module: info.module,
index,
ast: undefined,
internalSource: undefined,
runtimeRequirements: undefined,
source: undefined,
globalScope: undefined,
moduleScope: undefined,
internalNames: new Map(),
exportMap: undefined,
rawExportMap: undefined,
namespaceExportSymbol: undefined,
namespaceObjectName: undefined,
interopNamespaceObjectUsed: false,
interopNamespaceObjectName: undefined,
interopNamespaceObject2Used: false,
interopNamespaceObject2Name: undefined,
interopDefaultAccessUsed: false,
interopDefaultAccessName: undefined,
concatenationScope: undefined
};
break;
case "external":
item = {
type: "external",
module: info.module,
runtimeCondition: info.runtimeCondition,
index,
name: undefined,
deferredName: undefined,
interopNamespaceObjectUsed: false,
interopNamespaceObjectName: undefined,
interopNamespaceObject2Used: false,
interopNamespaceObject2Name: undefined,
interopDefaultAccessUsed: false,
interopDefaultAccessName: undefined,
deferred: moduleGraph.isDeferred(info.module),
deferredNamespaceObjectName: undefined,
deferredNamespaceObjectUsed: false
};
break;
default:
throw new Error(
`Unsupported concatenation entry type ${info.type}`
);
}
2024-03-18 23:28:40 +08:00
map.set(
/** @type {ModuleInfo} */ (item).module,
/** @type {ModuleInfo} */ (item)
);
2024-08-06 11:08:48 +08:00
return /** @type {ModuleInfo} */ (item);
}
2024-07-31 04:21:27 +08:00
/** @type {ReferenceToModuleInfo} */
const ref = {
type: "reference",
runtimeCondition: info.runtimeCondition,
target: item
};
return ref;
});
return [list, map];
}
/**
* @param {Hash} hash the hash used to track dependencies
* @param {UpdateHashContext} context context
* @returns {void}
*/
updateHash(hash, context) {
const { chunkGraph, runtime } = context;
for (const info of this._createConcatenationList(
this.rootModule,
this._modules,
intersectRuntime(runtime, this._runtime),
chunkGraph.moduleGraph
)) {
2018-02-25 09:00:20 +08:00
switch (info.type) {
2017-08-07 17:53:07 +08:00
case "concatenated":
info.module.updateHash(hash, context);
2017-08-07 17:53:07 +08:00
break;
case "external":
hash.update(`${chunkGraph.getModuleId(info.module)}`);
// TODO runtimeCondition
2017-08-07 17:53:07 +08:00
break;
}
}
super.updateHash(hash, context);
}
2019-12-11 05:58:26 +08:00
2024-03-18 23:28:40 +08:00
/**
* @param {ObjectDeserializerContext} context context
* @returns {ConcatenatedModule} ConcatenatedModule
*/
2019-12-11 05:58:26 +08:00
static deserialize(context) {
const obj = new ConcatenatedModule({
2024-10-24 11:02:20 +08:00
identifier: /** @type {EXPECTED_ANY} */ (undefined),
rootModule: /** @type {EXPECTED_ANY} */ (undefined),
modules: /** @type {EXPECTED_ANY} */ (undefined),
runtime: undefined,
2024-10-24 11:02:20 +08:00
compilation: /** @type {EXPECTED_ANY} */ (undefined)
});
2019-12-11 05:58:26 +08:00
obj.deserialize(context);
return obj;
}
2017-05-10 19:15:14 +08:00
}
2019-12-11 05:58:26 +08:00
makeSerializable(ConcatenatedModule, "webpack/lib/optimize/ConcatenatedModule");
2017-05-10 19:15:14 +08:00
module.exports = ConcatenatedModule;