webpack/lib/optimize/ConcatenatedModule.js

1602 lines
44 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");
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");
const RuntimeGlobals = require("../RuntimeGlobals");
2018-07-30 23:08:51 +08:00
const Template = require("../Template");
2017-05-10 19:15:14 +08:00
const HarmonyImportDependency = require("../dependencies/HarmonyImportDependency");
2019-10-11 21:46:57 +08:00
const JavascriptParser = require("../javascript/JavascriptParser");
const { equals } = require("../util/ArrayHelpers");
const LazySet = require("../util/LazySet");
const { concatComparators, keepOriginalOrder } = require("../util/comparators");
const createHash = require("../util/createHash");
const contextify = require("../util/identifier").contextify;
2019-12-11 05:58:26 +08:00
const makeSerializable = require("../util/makeSerializable");
const propertyAccess = require("../util/propertyAccess");
2017-05-10 19:15:14 +08:00
2020-08-03 02:09:36 +08:00
/** @typedef {import("eslint-scope").Scope} Scope */
2018-07-30 23:08:51 +08:00
/** @typedef {import("webpack-sources").Source} Source */
/** @typedef {import("../../declarations/WebpackOptions").WebpackOptionsNormalized} WebpackOptions */
/** @typedef {import("../ChunkGraph")} ChunkGraph */
/** @typedef {import("../Compilation")} Compilation */
2018-07-30 23:08:51 +08:00
/** @typedef {import("../Dependency")} Dependency */
/** @typedef {import("../Dependency").UpdateHashContext} UpdateHashContext */
/** @typedef {import("../DependencyTemplate").DependencyTemplateContext} DependencyTemplateContext */
2018-07-11 19:05:13 +08:00
/** @typedef {import("../DependencyTemplates")} DependencyTemplates */
/** @typedef {import("../ExportsInfo").ExportInfo} ExportInfo */
/** @typedef {import("../Module").CodeGenerationContext} CodeGenerationContext */
/** @typedef {import("../Module").CodeGenerationResult} CodeGenerationResult */
2018-07-20 22:24:35 +08:00
/** @typedef {import("../Module").LibIdentOptions} LibIdentOptions */
/** @typedef {import("../ModuleGraph")} ModuleGraph */
/** @typedef {import("../ModuleGraphConnection")} ModuleGraphConnection */
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("../WebpackError")} WebpackError */
2019-07-17 22:02:33 +08:00
/** @typedef {import("../util/Hash")} Hash */
/** @typedef {import("../util/fs").InputFileSystem} InputFileSystem */
/** @typedef {import("../util/runtime").RuntimeSpec} RuntimeSpec */
/**
* @typedef {Object} ReexportInfo
* @property {Module} module
* @property {string[]} export
*/
/** @typedef {RawBinding | SymbolBinding} Binding */
/**
* @typedef {Object} RawBinding
* @property {ModuleInfo} info
* @property {string} rawName
* @property {string=} comment
* @property {string[]} ids
* @property {string[]} exportName
*/
/**
* @typedef {Object} SymbolBinding
* @property {ConcatenatedModuleInfo} info
* @property {string} name
* @property {string=} comment
* @property {string[]} ids
* @property {string[]} exportName
*/
/** @typedef {ConcatenatedModuleInfo | ExternalModuleInfo } ModuleInfo */
/**
* @typedef {Object} ConcatenatedModuleInfo
* @property {"concatenated"} type
* @property {Module} module
* @property {number} index
* @property {Object} ast
* @property {Source} internalSource
* @property {ReplaceSource} source
* @property {Iterable<string>} runtimeRequirements
2020-08-03 02:09:36 +08:00
* @property {Scope} globalScope
* @property {Scope} moduleScope
* @property {Map<string, string>} internalNames
* @property {Map<string, string>} exportMap
* @property {string=} namespaceExportSymbol
* @property {string} namespaceObjectName
* @property {boolean} interopNamespaceObjectUsed
* @property {string} interopNamespaceObjectName
* @property {boolean} interopDefaultAccessUsed
* @property {string} interopDefaultAccessName
*/
/**
* @typedef {Object} ExternalModuleInfo
* @property {"external"} type
* @property {Module} module
* @property {number} index
* @property {string} name
* @property {boolean} interopNamespaceObjectUsed
* @property {string} interopNamespaceObjectName
* @property {boolean} interopDefaultAccessUsed
* @property {string} interopDefaultAccessName
*/
2018-11-16 22:02:17 +08:00
const RESERVED_NAMES = [
// internal names (should always be renamed)
ConcatenationScope.DEFAULT_EXPORT,
ConcatenationScope.NAMESPACE_OBJECT_EXPORT,
2018-11-16 22:02:17 +08:00
// keywords
"abstract,arguments,async,await,boolean,break,byte,case,catch,char,class,const,continue",
"debugger,default,delete,do,double,else,enum,eval,export,extends,false,final,finally,float",
"for,function,goto,if,implements,import,in,instanceof,int,interface,let,long,native,new,null",
"package,private,protected,public,return,short,static,super,switch,synchronized,this,throw",
"throws,transient,true,try,typeof,var,void,volatile,while,with,yield",
// commonjs
"module,__dirname,__filename,exports",
// js globals
"Array,Date,eval,function,hasOwnProperty,Infinity,isFinite,isNaN,isPrototypeOf,length,Math",
"NaN,name,Number,Object,prototype,String,toString,undefined,valueOf",
// browser globals
"alert,all,anchor,anchors,area,assign,blur,button,checkbox,clearInterval,clearTimeout",
"clientInformation,close,closed,confirm,constructor,crypto,decodeURI,decodeURIComponent",
"defaultStatus,document,element,elements,embed,embeds,encodeURI,encodeURIComponent,escape",
"event,fileUpload,focus,form,forms,frame,innerHeight,innerWidth,layer,layers,link,location",
"mimeTypes,navigate,navigator,frames,frameRate,hidden,history,image,images,offscreenBuffering",
"open,opener,option,outerHeight,outerWidth,packages,pageXOffset,pageYOffset,parent,parseFloat",
"parseInt,password,pkcs11,plugin,prompt,propertyIsEnum,radio,reset,screenX,screenY,scroll",
"secure,select,self,setInterval,setTimeout,status,submit,taint,text,textarea,top,unescape",
"untaint,window",
// window events
"onblur,onclick,onerror,onfocus,onkeydown,onkeypress,onkeyup,onmouseover,onload,onmouseup,onmousedown,onsubmit"
]
.join(",")
.split(",");
const bySourceOrder = (a, b) => {
const aOrder = a.sourceOrder;
const bOrder = b.sourceOrder;
if (isNaN(aOrder)) {
if (!isNaN(bOrder)) {
return 1;
}
} else {
if (isNaN(bOrder)) {
return -1;
}
if (aOrder !== bOrder) {
return aOrder < bOrder ? -1 : 1;
}
}
return 0;
};
const joinIterableWithComma = iterable => {
// This is more performant than Array.from().join(", ")
// as it doesn't create an array
let str = "";
let first = true;
for (const item of iterable) {
if (first) {
first = false;
} else {
str += ", ";
}
str += item;
}
return str;
};
/**
* @typedef {Object} ConcatenationEntry
* @property {"concatenated" | "external"} type
* @property {Module} module
*/
/**
* @param {ModuleGraph} moduleGraph the module graph
* @param {ModuleInfo} info module info
* @param {string[]} exportName exportName
* @param {Map<Module, ModuleInfo>} moduleToInfoMap moduleToInfoMap
* @param {RuntimeSpec} runtime for which runtime
* @param {RequestShortener} requestShortener the request shortener
* @param {RuntimeTemplate} runtimeTemplate the runtime template
* @param {Set<ConcatenatedModuleInfo>} neededNamespaceObjects modules for which a namespace object should be generated
* @param {boolean} asCall asCall
* @param {boolean} 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,
strictHarmonyModule,
asiSafe,
alreadyVisited = new Set()
2018-02-25 09:00:20 +08:00
) => {
const exportsType = info.module.getExportsType(
moduleGraph,
strictHarmonyModule
);
if (exportName.length === 0) {
switch (exportsType) {
case "default-only":
case "default-with-named":
2018-01-06 05:35:54 +08:00
info.interopNamespaceObjectUsed = true;
return {
info,
rawName: 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":
if (exportName[0] === "default") {
exportName = exportName.slice(1);
}
break;
case "default-only": {
const exportId = exportName[0];
exportName = exportName.slice(1);
if (exportId !== "default") {
return {
info,
rawName:
"/* non-default import from default-exporting module */undefined",
ids: exportName,
exportName
};
}
break;
}
case "dynamic":
if (exportName[0] === "default") {
exportName = exportName.slice(1);
info.interopDefaultAccessUsed = true;
const defaultExport = asCall
? `${info.interopDefaultAccessName}()`
: asiSafe
? `(${info.interopDefaultAccessName}())`
2020-08-29 22:00:03 +08:00
: asiSafe === false
? `;(${info.interopDefaultAccessName}())`
: `${info.interopDefaultAccessName}.a`;
return {
info,
rawName: defaultExport,
ids: exportName,
exportName
};
}
break;
default:
throw new Error(`Unexpected exportsType ${exportsType}`);
}
}
if (exportName.length === 0) {
switch (info.type) {
case "concatenated":
neededNamespaceObjects.add(info);
return {
info,
rawName: info.namespaceObjectName,
ids: exportName,
exportName
};
case "external":
return { info, rawName: info.name, ids: exportName, exportName };
}
}
const exportsInfo = moduleGraph.getExportsInfo(info.module);
const exportInfo = exportsInfo.getExportInfo(exportName[0]);
if (alreadyVisited.has(exportInfo)) {
return {
info,
rawName: "/* circular reexport */ Object(function x() { x() }())",
ids: [],
exportName
};
}
alreadyVisited.add(exportInfo);
if (exportInfo.getUsed(runtime) === UsageState.Unused) {
return {
info,
rawName: "/* unused export */ undefined",
ids: exportName.slice(1),
exportName
};
}
if (exportInfo.provided === false) {
return {
info,
rawName: "/* not provided export */ undefined",
ids: exportName.slice(1),
exportName
};
}
switch (info.type) {
case "concatenated": {
const exportId = exportName[0];
const directExport = info.exportMap.get(exportId);
if (directExport) {
const usedName = /** @type {string[]} */ (exportsInfo.getUsedName(
exportName,
runtime
));
return {
info,
name: directExport,
ids: usedName.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`
);
}
if (reexport) {
const refInfo = moduleToInfoMap.get(reexport.module);
return getFinalBinding(
moduleGraph,
refInfo,
reexport.export
? [...reexport.export, ...exportName.slice(1)]
: exportName.slice(1),
moduleToInfoMap,
runtime,
requestShortener,
runtimeTemplate,
neededNamespaceObjects,
asCall,
info.module.buildMeta.strictHarmonyModule,
asiSafe,
alreadyVisited
);
}
if (info.namespaceExportSymbol) {
const usedName = /** @type {string[]} */ (exportsInfo.getUsedName(
exportName,
runtime
));
return {
info,
rawName: info.namespaceObjectName,
ids: usedName,
exportName
};
}
throw new Error(
`Cannot get final name for export '${exportName.join(
"."
)}' of ${info.module.readableIdentifier(requestShortener)}`
);
}
case "external": {
const used = /** @type {string[]} */ (exportsInfo.getUsedName(
exportName,
runtime
));
const comment = equals(used, exportName)
? ""
: Template.toNormalComment(`${exportName.join(".")}`);
return { info, rawName: info.name + comment, ids: used, exportName };
}
}
};
/**
* @param {ModuleGraph} moduleGraph the module graph
* @param {ModuleInfo} info module info
* @param {string[]} exportName exportName
* @param {Map<Module, ModuleInfo>} moduleToInfoMap moduleToInfoMap
* @param {RuntimeSpec} runtime for which runtime
* @param {RequestShortener} requestShortener the request shortener
* @param {RuntimeTemplate} runtimeTemplate the runtime template
* @param {Set<ConcatenatedModuleInfo>} neededNamespaceObjects modules for which a namespace object should be generated
* @param {boolean} asCall asCall
* @param {boolean} callContext callContext
* @param {boolean} 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,
callContext,
strictHarmonyModule,
asiSafe
2018-02-25 09:00:20 +08:00
) => {
const binding = getFinalBinding(
moduleGraph,
info,
exportName,
moduleToInfoMap,
runtime,
requestShortener,
runtimeTemplate,
neededNamespaceObjects,
asCall,
strictHarmonyModule,
asiSafe
);
{
const { ids, comment } = binding;
let reference;
let isPropertyAccess;
if ("rawName" in binding) {
reference = `${binding.rawName}${comment || ""}${propertyAccess(ids)}`;
isPropertyAccess = ids.length > 0;
} else {
const { info, name: exportId } = binding;
const name = info.internalNames.get(exportId);
if (!name) {
throw new Error(
`The export "${exportId}" in "${info.module.readableIdentifier(
requestShortener
)}" has no internal name (existing names: ${
Array.from(
info.internalNames,
([name, symbol]) => `${name}: ${symbol}`
).join(", ") || "none"
})`
);
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
? `;(0,${reference})`
: `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
const addScopeSymbols = (s, nameSet, scopeSet1, scopeSet2) => {
let scope = s;
while (scope) {
if (scopeSet1.has(scope)) break;
if (scopeSet2.has(scope)) break;
scopeSet1.add(scope);
for (const variable of scope.variables) {
nameSet.add(variable.name);
2018-01-22 20:52:43 +08:00
}
scope = scope.upper;
}
2017-11-08 18:32:05 +08:00
};
2017-11-08 18:32:05 +08:00
const getAllReferences = variable => {
2017-06-06 17:18:34 +08:00
let set = variable.references;
// Look for inner scope variables too (like in class Foo { t() { Foo } })
const identifiers = new Set(variable.identifiers);
2018-02-25 09:00:20 +08:00
for (const scope of variable.scope.childScopes) {
for (const innerVar of scope.variables) {
if (innerVar.identifiers.some(id => identifiers.has(id))) {
2017-06-06 17:18:34 +08:00
set = set.concat(innerVar.references);
break;
}
}
}
return set;
2017-11-08 18:32:05 +08:00
};
2017-06-06 17:18:34 +08:00
2017-11-08 18:32:05 +08:00
const getPathInAst = (ast, node) => {
2018-02-25 09:00:20 +08:00
if (ast === node) {
return [];
}
2017-11-08 18:32:05 +08:00
const nr = node.range;
2017-11-08 18:32:05 +08:00
const enterNode = n => {
2018-02-25 09:00:20 +08:00
if (!n) return undefined;
2017-11-08 18:32:05 +08:00
const r = n.range;
2018-02-25 09:00:20 +08:00
if (r) {
if (r[0] <= nr[0] && r[1] >= nr[1]) {
2017-11-08 18:32:05 +08:00
const path = getPathInAst(n, node);
2018-02-25 09:00:20 +08:00
if (path) {
2017-11-08 18:32:05 +08:00
path.push(n);
return path;
}
}
}
return undefined;
};
2018-02-25 09:00:20 +08:00
if (Array.isArray(ast)) {
for (let i = 0; i < ast.length; i++) {
const enterResult = enterNode(ast[i]);
2018-08-21 08:26:50 +08:00
if (enterResult !== undefined) return enterResult;
}
2018-02-25 09:00:20 +08:00
} else if (ast && typeof ast === "object") {
const keys = Object.keys(ast);
for (let i = 0; i < keys.length; i++) {
const value = ast[keys[i]];
2018-02-25 09:00:20 +08:00
if (Array.isArray(value)) {
const pathResult = getPathInAst(value, node);
2018-08-21 08:26:50 +08:00
if (pathResult !== undefined) return pathResult;
2018-02-25 09:00:20 +08:00
} else if (value && typeof value === "object") {
const enterResult = enterNode(value);
2018-08-21 08:26:50 +08:00
if (enterResult !== undefined) return enterResult;
}
}
}
2017-11-08 18:32:05 +08:00
};
/**
* @param {ModuleInfo[]} modulesWithInfo modules
* @returns {Map<Module, ModuleInfo>} mapping
*/
const modulesWithInfoToMap = modulesWithInfo => {
const moduleToInfoMap = new Map();
for (const m of modulesWithInfo) {
moduleToInfoMap.set(m.module, m);
}
return moduleToInfoMap;
};
const TYPES = new Set(["javascript"]);
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 {Object=} associatedObjectForCache object for caching
* @returns {ConcatenatedModule} the module
*/
static create(rootModule, modules, associatedObjectForCache) {
const identifier = ConcatenatedModule._createIdentifier(
rootModule,
modules,
associatedObjectForCache
);
return new ConcatenatedModule({
identifier,
rootModule,
modules
});
}
/**
* @param {Object} options options
* @param {string} options.identifier the identifier of the module
* @param {Module=} options.rootModule the root module of the concatenation
* @param {Set<Module>=} options.modules all concatenated modules
*/
constructor({ identifier, rootModule, modules }) {
2018-01-31 04:40:44 +08:00
super("javascript/esm", null);
// 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.factoryMeta = rootModule && rootModule.factoryMeta;
// Caching
// TODO
}
/**
* 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) {
super.updateCacheModule(module);
const m = /** @type {ConcatenatedModule} */ (module);
this._identifier = m._identifier;
this.rootModule = m.rootModule;
this._modules = m._modules;
}
/**
2020-03-10 09:59:46 +08:00
* @returns {Set<string>} types available (do not mutate)
*/
getSourceTypes() {
return TYPES;
}
get modules() {
return Array.from(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) {
return (
this.rootModule.readableIdentifier(requestShortener) +
` + ${this._modules.size - 1} modules`
);
}
/**
* @param {LibIdentOptions} options options
* @returns {string | null} an identifier for library inclusion
*/
libIdent(options) {
return this.rootModule.libIdent(options);
}
/**
* @returns {string | null} absolute path which should be used for condition matching (usually the resource path)
*/
nameForCondition() {
return this.rootModule.nameForCondition();
}
/**
* @param {WebpackOptions} options webpack options
* @param {Compilation} compilation the compilation
* @param {ResolverWithOptions} resolver the resolver
* @param {InputFileSystem} fs the file system
* @param {function(WebpackError=): void} callback callback function
* @returns {void}
*/
build(options, compilation, resolver, fs, callback) {
const { rootModule } = this;
this.buildInfo = {
strict: true,
cacheable: true,
moduleArgument: rootModule.buildInfo.moduleArgument,
exportsArgument: rootModule.buildInfo.exportsArgument,
fileDependencies: new LazySet(),
contextDependencies: new LazySet(),
missingDependencies: new LazySet(),
assets: undefined
};
this.buildMeta = rootModule.buildMeta;
this.clearDependenciesAndBlocks();
this.clearWarningsAndErrors();
for (const m of this._modules) {
// populate cacheable
if (!m.buildInfo.cacheable) {
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) ||
!this._modules.has(compilation.moduleGraph.getModule(dep))
)) {
this.dependencies.push(d);
}
// populate blocks
for (const d of m.blocks) {
this.blocks.push(d);
}
// populate warnings
const warnings = m.getWarnings();
if (warnings !== undefined) {
for (const warning of warnings) {
this.addWarning(warning);
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);
}
}
// populate assets
if (m.buildInfo.assets) {
if (this.buildInfo.assets === undefined) {
this.buildInfo.assets = Object.create(null);
}
Object.assign(this.buildInfo.assets, m.buildInfo.assets);
}
if (m.buildInfo.assetsInfo) {
if (this.buildInfo.assetsInfo === undefined) {
this.buildInfo.assetsInfo = new Map();
2017-11-06 23:41:26 +08:00
}
for (const [key, value] of m.buildInfo.assetsInfo) {
this.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 {Set<Module>} */
const existingEntries = new Set();
2017-08-07 17:53:07 +08:00
/**
* @param {Module} module a module
* @returns {ModuleGraphConnection[]} imported modules in order
*/
2017-11-08 18:32:05 +08:00
const getConcatenatedImports = module => {
let connections = Array.from(moduleGraph.getOutgoingConnections(module));
if (module === rootModule) {
for (const c of moduleGraph.getOutgoingConnections(this))
connections.push(c);
}
const references = connections
.filter(connection => {
if (!(connection.dependency instanceof HarmonyImportDependency))
return false;
return (
connection && connection.module && connection.isActive(runtime)
);
})
.map(connection => ({
connection,
2020-03-29 06:10:15 +08:00
sourceOrder: /** @type {HarmonyImportDependency} */ (connection.dependency)
.sourceOrder
}));
references.sort(
concatComparators(bySourceOrder, keepOriginalOrder(references))
);
return references.map(({ connection }) => connection);
2017-11-08 18:32:05 +08:00
};
2017-08-07 17:53:07 +08:00
/**
* @param {ModuleGraphConnection} connection graph connection
* @returns {void}
*/
const enterModule = connection => {
const module = connection.module;
2018-02-25 09:00:20 +08:00
if (!module) return;
if (existingEntries.has(module)) {
return;
}
2018-02-25 09:00:20 +08:00
if (modulesSet.has(module)) {
existingEntries.add(module);
2017-08-07 17:53:07 +08:00
const imports = getConcatenatedImports(module);
imports.forEach(enterModule);
list.push({
type: "concatenated",
module: connection.module
2017-08-07 17:53:07 +08:00
});
} else {
existingEntries.add(connection.module);
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;
2017-08-07 19:56:50 +08:00
}
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.add(rootModule);
const imports = getConcatenatedImports(rootModule);
imports.forEach(enterModule);
list.push({
type: "concatenated",
module: rootModule
});
2017-08-07 17:53:07 +08:00
return list;
2017-05-10 19:15:14 +08:00
}
static _createIdentifier(rootModule, modules, associatedObjectForCache) {
2020-01-15 06:14:47 +08:00
const cachedContextify = contextify.bindContextCache(
rootModule.context,
associatedObjectForCache
);
let identifiers = [];
for (const module of modules) {
identifiers.push(cachedContextify(module.identifier()));
}
identifiers.sort();
2018-02-22 18:27:11 +08:00
const hash = createHash("md4");
hash.update(identifiers.join(" "));
return rootModule.identifier() + "|" + hash.digest("hex");
}
/**
* @param {LazySet<string>} fileDependencies set where file dependencies are added to
* @param {LazySet<string>} contextDependencies set where context dependencies are added to
* @param {LazySet<string>} missingDependencies set where missing dependencies are added to
* @param {LazySet<string>} buildDependencies set where build dependencies are added to
*/
addCacheDependencies(
fileDependencies,
contextDependencies,
missingDependencies,
buildDependencies
) {
for (const module of this._modules) {
module.addCacheDependencies(
fileDependencies,
contextDependencies,
missingDependencies,
buildDependencies
);
}
}
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,
runtime
}) {
/** @type {Set<string>} */
2019-10-09 17:49:48 +08:00
const runtimeRequirements = new Set();
const requestShortener = runtimeTemplate.requestShortener;
2020-03-13 00:51:26 +08:00
// Meta info for each module
const modulesWithInfo = this._getModulesWithInfo(moduleGraph, runtime);
2017-05-10 19:15:14 +08:00
// Create mapping from module to info
const moduleToInfoMap = modulesWithInfoToMap(modulesWithInfo);
2017-05-10 19:15:14 +08:00
// Set with modules that need a generated namespace object
/** @type {Set<ConcatenatedModuleInfo>} */
const neededNamespaceObjects = new Set();
2017-05-10 19:15:14 +08:00
// Generate source code and analyse scopes
// Prepare a ReplaceSource for the final source
2018-02-25 09:00:20 +08:00
for (const info of modulesWithInfo) {
this._analyseModule(
2020-09-11 15:06:24 +08:00
modulesWithInfo,
info,
dependencyTemplates,
runtimeTemplate,
moduleGraph,
chunkGraph,
runtime
);
2018-01-22 20:52:43 +08:00
}
2017-05-10 19:15:14 +08:00
// List of all used names to avoid conflicts
2018-11-16 22:02:17 +08:00
const allUsedNames = new Set(RESERVED_NAMES);
2017-05-10 19:15:14 +08:00
// List of additional names in scope for module references
/** @type {Map<string, { usedNames: Set<string>, alreadyCheckedScopes: Set<TODO> }>} */
const usedNamesInScopeInfo = new Map();
/**
* @param {string} module module identifier
* @param {string} id export id
* @returns {{ usedNames: Set<string>, alreadyCheckedScopes: Set<TODO> }} info
*/
const getUsedNamesInScopeInfo = (module, id) => {
const key = `${module}-${id}`;
let info = usedNamesInScopeInfo.get(key);
if (info === undefined) {
info = {
usedNames: new Set(),
alreadyCheckedScopes: new Set()
};
usedNamesInScopeInfo.set(key, info);
}
return info;
};
// Set of already checked scopes
const ignoredScopes = new Set();
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") {
const superClassExpressions = [];
// ignore symbols from moduleScope
if (info.moduleScope) {
ignoredScopes.add(info.moduleScope);
// The super class expression in class scopes behaves weird
// We store ranges of all super class expressions to make
// renaming to work correctly
for (const childScope of info.moduleScope.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: block.superClass.range,
variables: childScope.variables
});
}
}
}
// 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);
if (!match || match.ids.length < 1) continue;
const binding = getFinalBinding(
moduleGraph,
modulesWithInfo[match.index],
match.ids,
moduleToInfoMap,
runtime,
requestShortener,
runtimeTemplate,
neededNamespaceObjects,
false,
info.module.buildMeta.strictHarmonyModule,
true
);
if (!binding.ids) continue;
const {
usedNames,
alreadyCheckedScopes
} = getUsedNamesInScopeInfo(
binding.info.module.identifier(),
"name" in binding ? binding.name : ""
);
for (const expr of superClassExpressions) {
if (
expr.range[0] <= reference.identifier.range[0] &&
expr.range[1] >= 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
// generate names for symbols
2018-02-25 09:00:20 +08:00
for (const info of modulesWithInfo) {
const { usedNames: namespaceObjectUsedNames } = getUsedNamesInScopeInfo(
info.module.identifier(),
""
);
2018-02-25 09:00:20 +08:00
switch (info.type) {
case "concatenated": {
for (const variable of info.moduleScope.variables) {
const name = variable.name;
const { usedNames, alreadyCheckedScopes } = getUsedNamesInScopeInfo(
info.module.identifier(),
name
);
if (allUsedNames.has(name) || usedNames.has(name)) {
2018-02-25 09:00:20 +08:00
const references = getAllReferences(variable);
for (const ref of references) {
addScopeSymbols(
ref.from,
usedNames,
alreadyCheckedScopes,
ignoredScopes
);
}
2018-02-25 09:00:20 +08:00
const newName = this.findNewName(
name,
allUsedNames,
usedNames,
2018-02-25 09:00:20 +08:00
info.module.readableIdentifier(requestShortener)
);
allUsedNames.add(newName);
info.internalNames.set(name, newName);
const source = info.source;
const allIdentifiers = new Set(
references.map(r => r.identifier).concat(variable.identifiers)
);
for (const identifier of allIdentifiers) {
const r = identifier.range;
const path = getPathInAst(info.ast, identifier);
if (
path &&
path.length > 1 &&
path[1].type === "Property" &&
path[1].shorthand
) {
source.insert(r[1], `: ${newName}`);
} else {
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);
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 = this.findNewName(
"namespaceObject",
allUsedNames,
namespaceObjectUsedNames,
info.module.readableIdentifier(requestShortener)
);
allUsedNames.add(namespaceObjectName);
}
info.namespaceObjectName = namespaceObjectName;
2018-02-25 09:00:20 +08:00
break;
}
case "external": {
const externalName = this.findNewName(
"",
allUsedNames,
namespaceObjectUsedNames,
2018-02-25 09:00:20 +08:00
info.module.readableIdentifier(requestShortener)
);
allUsedNames.add(externalName);
info.name = externalName;
break;
}
}
if (
info.module.buildMeta.exportsType === "default" ||
info.module.buildMeta.exportsType === "flagged" ||
info.module.buildMeta.exportsType === "dynamic" ||
!info.module.buildMeta.exportsType
) {
const externalNameInterop = this.findNewName(
"namespaceObject",
allUsedNames,
namespaceObjectUsedNames,
info.module.readableIdentifier(requestShortener)
);
allUsedNames.add(externalNameInterop);
info.interopNamespaceObjectName = externalNameInterop;
}
if (
info.module.buildMeta.exportsType === "dynamic" ||
!info.module.buildMeta.exportsType
) {
const externalNameInterop = this.findNewName(
"default",
allUsedNames,
namespaceObjectUsedNames,
info.module.readableIdentifier(requestShortener)
);
allUsedNames.add(externalNameInterop);
info.interopDefaultAccessName = 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
2018-02-25 09:00:20 +08:00
for (const info of modulesWithInfo) {
if (info.type === "concatenated") {
for (const reference of info.globalScope.through) {
const name = reference.identifier.name;
const match = ConcatenationScope.matchModuleReference(name);
2018-02-25 09:00:20 +08:00
if (match) {
const finalName = getFinalName(
moduleGraph,
modulesWithInfo[match.index],
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.directImport,
info.module.buildMeta.strictHarmonyModule,
match.asiSafe
2018-02-25 09:00:20 +08:00
);
const r = reference.identifier.range;
const source = info.source;
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, function(RequestShortener): string>} */
const exportsMap = new Map();
// Set with all root exposed unused exports
/** @type {Set<string>} */
const unusedExports = new Set();
const rootInfo = /** @type {ConcatenatedModuleInfo} */ (moduleToInfoMap.get(
this.rootModule
));
const strictHarmonyModule = rootInfo.module.buildMeta.strictHarmonyModule;
const exportsInfo = moduleGraph.getExportsInfo(rootInfo.module);
for (const exportInfo of exportsInfo.orderedExports) {
const name = exportInfo.name;
if (exportInfo.provided === false) continue;
const used = exportInfo.getUsedName(undefined, runtime);
if (!used) {
unusedExports.add(name);
continue;
}
exportsMap.set(used, requestShortener => {
const finalName = getFinalName(
moduleGraph,
rootInfo,
[name],
moduleToInfoMap,
runtime,
requestShortener,
runtimeTemplate,
neededNamespaceObjects,
false,
false,
strictHarmonyModule,
true
);
return `/* ${
exportInfo.isReexport() ? "reexport" : "binding"
} */ ${finalName}`;
});
}
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)
if (
moduleGraph.getExportsInfo(this).otherExportsInfo.getUsed(runtime) !==
UsageState.Unused
) {
result.add(`// ESM COMPAT FLAG\n`);
2018-02-25 09:00:20 +08:00
result.add(
runtimeTemplate.defineEsModuleFlagStatement({
2018-11-17 01:18:44 +08:00
exportsArgument: this.exportsArgument,
runtimeRequirements
2018-02-25 09:00:20 +08:00
})
);
2017-05-10 19:15:14 +08:00
}
2017-08-07 20:12:31 +08:00
// define exports
if (exportsMap.size > 0) {
runtimeRequirements.add(RuntimeGlobals.exports);
runtimeRequirements.add(RuntimeGlobals.definePropertyGetters);
const definitions = [];
for (const [key, value] of exportsMap) {
definitions.push(
`\n ${JSON.stringify(key)}: ${runtimeTemplate.returningFunction(
value(requestShortener)
)}`
);
}
result.add(`\n// EXPORTS\n`);
result.add(
`${RuntimeGlobals.definePropertyGetters}(${
this.exportsArgument
}, {${definitions.join(",")}\n});\n`
);
}
// list unused exports
if (unusedExports.size > 0) {
result.add(
`\n// UNUSED EXPORTS: ${joinIterableWithComma(unusedExports)}\n`
);
}
// generate namespace objects
const namespaceObjectSources = new Map();
for (const info of neededNamespaceObjects) {
if (info.namespaceExportSymbol) continue;
const nsObj = [];
const exportsInfo = moduleGraph.getExportsInfo(info.module);
for (const exportInfo of exportsInfo.orderedExports) {
const usedName = exportInfo.getUsedName(undefined, runtime);
if (usedName) {
const finalName = getFinalName(
moduleGraph,
info,
[exportInfo.name],
moduleToInfoMap,
runtime,
requestShortener,
runtimeTemplate,
neededNamespaceObjects,
false,
undefined,
info.module.buildMeta.strictHarmonyModule,
true
);
nsObj.push(
`\n ${JSON.stringify(
usedName
)}: ${runtimeTemplate.returningFunction(finalName)}`
);
}
}
const name = info.namespaceObjectName;
namespaceObjectSources.set(
info,
`\n// NAMESPACE OBJECT: ${info.module.readableIdentifier(
requestShortener
)}\nvar ${name} = {};\n${
RuntimeGlobals.makeNamespaceObject
}(${name});\n${
RuntimeGlobals.definePropertyGetters
}(${name}, {${nsObj.join(",")}\n});\n`
);
runtimeRequirements.add(RuntimeGlobals.makeNamespaceObject);
runtimeRequirements.add(RuntimeGlobals.definePropertyGetters);
}
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
}
2018-01-22 20:52:43 +08:00
}
2017-08-07 20:12:31 +08:00
// evaluate modules in order
2018-02-25 09:00:20 +08:00
for (const info of modulesWithInfo) {
let name;
2018-02-25 09:00:20 +08:00
switch (info.type) {
case "concatenated": {
2018-02-25 09:00:20 +08:00
result.add(
`\n// CONCATENATED MODULE: ${info.module.readableIdentifier(
requestShortener
)}\n`
);
result.add(info.source);
if (info.runtimeRequirements) {
for (const r of info.runtimeRequirements) {
runtimeRequirements.add(r);
}
}
name = info.namespaceObjectName;
break;
}
2017-08-07 17:53:07 +08:00
case "external":
2018-02-25 09:00:20 +08:00
result.add(
`\n// EXTERNAL MODULE: ${info.module.readableIdentifier(
requestShortener
)}\n`
);
runtimeRequirements.add(RuntimeGlobals.require);
2018-02-25 09:00:20 +08:00
result.add(
`var ${info.name} = __webpack_require__(${JSON.stringify(
chunkGraph.getModuleId(info.module)
)});`
2018-02-25 09:00:20 +08:00
);
name = info.name;
2017-08-07 17:53:07 +08:00
break;
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.interopNamespaceObjectUsed) {
if (info.module.buildMeta.exportsType === "default") {
runtimeRequirements.add(RuntimeGlobals.createFakeNamespaceObject);
result.add(
`\nvar ${info.interopNamespaceObjectName} = /*#__PURE__*/${RuntimeGlobals.createFakeNamespaceObject}(${name}, 2);\n`
);
} else if (
info.module.buildMeta.exportsType === "flagged" ||
info.module.buildMeta.exportsType === "dynamic" ||
!info.module.buildMeta.exportsType
) {
runtimeRequirements.add(RuntimeGlobals.createFakeNamespaceObject);
result.add(
`\nvar ${info.interopNamespaceObjectName} = /*#__PURE__*/${RuntimeGlobals.createFakeNamespaceObject}(${name});\n`
);
}
}
if (info.interopDefaultAccessUsed) {
runtimeRequirements.add(RuntimeGlobals.compatGetDefaultExport);
result.add(
`\nvar ${info.interopDefaultAccessName} = /*#__PURE__*/${RuntimeGlobals.compatGetDefaultExport}(${name});\n`
);
}
2018-01-22 20:52:43 +08:00
}
2017-05-10 19:15:14 +08:00
2019-12-11 05:58:26 +08:00
/** @type {CodeGenerationResult} */
const resultEntry = {
sources: new Map([["javascript", new CachedSource(result)]]),
runtimeRequirements
};
2019-12-11 05:58:26 +08:00
return resultEntry;
2017-05-10 19:15:14 +08:00
}
/**
2020-09-11 15:06:24 +08:00
* @param {ModuleInfo[]} modulesWithInfo modulesWithInfo
* @param {ModuleInfo} info info
* @param {DependencyTemplates} dependencyTemplates dependencyTemplates
* @param {RuntimeTemplate} runtimeTemplate runtimeTemplate
* @param {ModuleGraph} moduleGraph moduleGraph
* @param {ChunkGraph} chunkGraph chunkGraph
* @param {RuntimeSpec} runtime runtime
*/
_analyseModule(
2020-09-11 15:06:24 +08:00
modulesWithInfo,
info,
dependencyTemplates,
runtimeTemplate,
moduleGraph,
chunkGraph,
runtime
) {
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(
modulesWithInfo,
info
);
// TODO cache codeGeneration results
const codeGenResult = m.codeGeneration({
dependencyTemplates,
runtimeTemplate,
moduleGraph,
chunkGraph,
2020-09-11 15:06:24 +08:00
runtime,
concatenationScope
});
const source = codeGenResult.sources.get("javascript");
const code = source.source().toString();
let ast;
try {
ast = JavascriptParser._parse(code, {
sourceType: "module"
});
} catch (err) {
if (
err.loc &&
typeof err.loc === "object" &&
typeof err.loc.line === "number"
) {
const lineNumber = err.loc.line;
const lines = code.split("\n");
err.message +=
"\n| " +
lines
.slice(Math.max(0, lineNumber - 3), lineNumber + 2)
.join("\n| ");
}
throw err;
}
const scopeManager = eslintScope.analyze(ast, {
ecmaVersion: 6,
sourceType: "module",
optimistic: true,
ignoreEval: true,
impliedStrict: true
});
const globalScope = scopeManager.acquire(ast);
const moduleScope = globalScope.childScopes[0];
const resultSource = new ReplaceSource(source);
info.runtimeRequirements = codeGenResult.runtimeRequirements;
info.ast = ast;
info.internalSource = source;
info.source = resultSource;
info.globalScope = globalScope;
info.moduleScope = moduleScope;
} catch (err) {
err.message += `\nwhile analysing module ${m.identifier()} for concatenation`;
throw err;
}
}
}
/**
* @param {ChunkGraph} chunkGraph the chunk graph
* @param {DependencyTemplates} dependencyTemplates dependency templates
* @param {RuntimeSpec} runtime the runtime
* @returns {string} hash
*/
_getHashDigest(chunkGraph, dependencyTemplates, runtime) {
const hash = chunkGraph.getModuleHash(this, runtime);
const dtHash = dependencyTemplates.getHash();
return `${hash}-${dtHash}`;
}
/**
* @param {ModuleGraph} moduleGraph the module graph
* @param {RuntimeSpec} runtime the runtime
* @returns {ModuleInfo[]} module info items
*/
_getModulesWithInfo(moduleGraph, runtime) {
const orderedConcatenationList = this._createConcatenationList(
this.rootModule,
this._modules,
undefined,
moduleGraph
);
return orderedConcatenationList.map((info, index) => {
switch (info.type) {
case "concatenated": {
return {
type: "concatenated",
module: info.module,
index,
ast: undefined,
internalSource: undefined,
runtimeRequirements: undefined,
source: undefined,
globalScope: undefined,
moduleScope: undefined,
internalNames: new Map(),
exportMap: new Map(),
namespaceExportSymbol: undefined,
namespaceObjectName: undefined,
interopNamespaceObjectUsed: false,
interopNamespaceObjectName: undefined,
interopDefaultAccessUsed: false,
interopDefaultAccessName: undefined
};
}
case "external":
return {
type: "external",
module: info.module,
index,
name: undefined,
interopNamespaceObjectUsed: false,
interopNamespaceObjectName: undefined,
interopDefaultAccessUsed: false,
interopDefaultAccessName: undefined
};
default:
throw new Error(`Unsupported concatenation entry type ${info.type}`);
}
});
}
findNewName(oldName, usedNamed1, usedNamed2, extraInfo) {
2017-05-10 19:15:14 +08:00
let name = oldName;
if (name === ConcatenationScope.DEFAULT_EXPORT) {
name = "";
}
if (name === ConcatenationScope.NAMESPACE_OBJECT_EXPORT) {
name = "namespaceObject";
}
2017-05-10 19:15:14 +08:00
2017-06-07 18:57:15 +08:00
// Remove uncool stuff
2018-02-25 09:00:20 +08:00
extraInfo = extraInfo.replace(
/\.+\/|(\/index)?\.([a-zA-Z0-9]{1,4})($|\s|\?)|\s*\+\s*\d+\s*modules/g,
""
);
2017-06-07 18:57:15 +08:00
2017-05-10 19:15:14 +08:00
const splittedInfo = extraInfo.split("/");
2018-02-25 09:00:20 +08:00
while (splittedInfo.length) {
2017-08-07 20:38:54 +08:00
name = splittedInfo.pop() + (name ? "_" + name : "");
const nameIdent = Template.toIdentifier(name);
2018-02-25 09:00:20 +08:00
if (
!usedNamed1.has(nameIdent) &&
(!usedNamed2 || !usedNamed2.has(nameIdent))
)
return nameIdent;
2017-05-10 19:15:14 +08:00
}
2017-08-07 22:15:49 +08:00
let i = 0;
let nameWithNumber = Template.toIdentifier(`${name}_${i}`);
2018-02-25 09:00:20 +08:00
while (
usedNamed1.has(nameWithNumber) ||
(usedNamed2 && usedNamed2.has(nameWithNumber))
) {
i++;
nameWithNumber = Template.toIdentifier(`${name}_${i}`);
2017-08-07 22:15:49 +08:00
}
return nameWithNumber;
2017-05-10 19:15:14 +08:00
}
/**
* @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,
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)}`);
2017-08-07 17:53:07 +08:00
break;
}
}
super.updateHash(hash, context);
}
2019-12-11 05:58:26 +08:00
static deserialize(context) {
const obj = new ConcatenatedModule({
identifier: undefined,
rootModule: undefined,
modules: 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;