webpack/lib/optimize/ConcatenatedModule.js

1492 lines
37 KiB
JavaScript
Raw Normal View History

2017-05-10 13:15:14 +02:00
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
const Module = require("../Module");
const Template = require("../Template");
2017-05-21 06:56:24 +02:00
const Parser = require("../Parser");
const eslintScope = require("eslint-scope");
const { ConcatSource, ReplaceSource } = require("webpack-sources");
const DependencyReference = require("../dependencies/DependencyReference");
2017-05-10 13:15:14 +02:00
const HarmonyImportDependency = require("../dependencies/HarmonyImportDependency");
const HarmonyImportSideEffectDependency = require("../dependencies/HarmonyImportSideEffectDependency");
2017-05-10 13:15:14 +02:00
const HarmonyImportSpecifierDependency = require("../dependencies/HarmonyImportSpecifierDependency");
const HarmonyExportSpecifierDependency = require("../dependencies/HarmonyExportSpecifierDependency");
const HarmonyExportExpressionDependency = require("../dependencies/HarmonyExportExpressionDependency");
const HarmonyExportImportedSpecifierDependency = require("../dependencies/HarmonyExportImportedSpecifierDependency");
const HarmonyCompatibilityDependency = require("../dependencies/HarmonyCompatibilityDependency");
const createHash = require("../util/createHash");
2017-05-10 13:15:14 +02:00
/** @typedef {import("../Dependency")} Dependency */
/** @typedef {import("../Compilation")} Compilation */
/** @typedef {import("../util/createHash").Hash} Hash */
/**
* @typedef {Object} ConcatenationEntry
* @property {"concatenated" | "external"} type
* @property {Module} module
*/
2018-02-25 02:00:20 +01:00
const ensureNsObjSource = (
info,
moduleToInfoMap,
requestShortener,
strictHarmonyModule
) => {
if (!info.hasNamespaceObject) {
2017-08-07 11:53:07 +02:00
info.hasNamespaceObject = true;
const name = info.exportMap.get(true);
const nsObj = [`var ${name} = {};`, `__webpack_require__.r(${name});`];
2018-02-25 02:00:20 +01:00
for (const exportName of info.module.buildMeta.providedExports) {
const finalName = getFinalName(
info,
exportName,
moduleToInfoMap,
requestShortener,
false,
strictHarmonyModule
);
nsObj.push(
`__webpack_require__.d(${name}, ${JSON.stringify(
exportName
)}, function() { return ${finalName}; });`
);
2017-08-07 11:53:07 +02:00
}
info.namespaceObjectSource = nsObj.join("\n") + "\n";
}
2017-11-08 11:32:05 +01:00
};
2017-08-07 11:53:07 +02:00
2018-02-25 02:00:20 +01:00
const getExternalImport = (
importedModule,
info,
exportName,
asCall,
strictHarmonyModule
) => {
const used = importedModule.isUsed(exportName);
2018-02-25 02:00:20 +01:00
if (!used) return "/* unused reexport */undefined";
const comment =
used !== exportName ? ` ${Template.toNormalComment(exportName)}` : "";
switch (importedModule.buildMeta.exportsType) {
case "named":
2018-02-25 02:00:20 +01:00
if (exportName === "default") {
return info.name;
2018-02-25 02:00:20 +01:00
} else if (exportName === true) {
2018-01-05 22:35:54 +01:00
info.interopNamespaceObjectUsed = true;
return info.interopNamespaceObjectName;
} else {
break;
}
case "namespace":
2018-02-25 02:00:20 +01:00
if (exportName === true) {
return info.name;
} else {
break;
}
default:
2018-02-25 02:00:20 +01:00
if (strictHarmonyModule) {
if (exportName === "default") {
return info.name;
2018-02-25 02:00:20 +01:00
} else if (exportName === true) {
2018-01-05 22:35:54 +01:00
info.interopNamespaceObjectUsed = true;
return info.interopNamespaceObjectName;
} else {
return "/* non-default import from non-esm module */undefined";
}
} else {
2018-02-25 02:00:20 +01:00
if (exportName === "default") {
2018-01-05 22:35:54 +01:00
info.interopDefaultAccessUsed = true;
2018-02-25 02:00:20 +01:00
return asCall
? `${info.interopDefaultAccessName}()`
: `${info.interopDefaultAccessName}.a`;
} else if (exportName === true) {
return info.name;
} else {
break;
}
}
}
const reference = `${info.name}[${JSON.stringify(used)}${comment}]`;
2018-02-25 02:00:20 +01:00
if (asCall) return `Object(${reference})`;
2017-08-07 14:38:54 +02:00
return reference;
2017-11-08 11:32:05 +01:00
};
2018-02-25 02:00:20 +01:00
const getFinalName = (
info,
exportName,
moduleToInfoMap,
requestShortener,
asCall,
strictHarmonyModule,
alreadyVisited = new Set()
2018-02-25 02:00:20 +01:00
) => {
switch (info.type) {
case "concatenated": {
const directExport = info.exportMap.get(exportName);
if (directExport) {
if (exportName === true) {
2018-02-25 02:00:20 +01:00
ensureNsObjSource(
info,
moduleToInfoMap,
requestShortener,
strictHarmonyModule
);
2018-06-23 15:27:10 +02:00
} else if (!info.module.isUsed(exportName)) {
return "/* unused export */ undefined";
}
if (info.globalExports.has(directExport)) {
return directExport;
}
2018-02-25 02:00:20 +01:00
const name = info.internalNames.get(directExport);
if (!name) {
2018-02-25 02:00:20 +01:00
throw new Error(
`The export "${directExport}" in "${info.module.readableIdentifier(
requestShortener
)}" has no internal name`
);
}
2018-02-25 02:00:20 +01:00
return name;
}
2018-02-25 02:00:20 +01:00
const reexport = info.reexportMap.get(exportName);
if (reexport) {
if (alreadyVisited.has(reexport)) {
throw new Error(
`Circular reexports ${Array.from(
alreadyVisited,
e =>
`"${e.module.readableIdentifier(requestShortener)}".${
e.exportName
}`
).join(
" --> "
)} -(circular)-> "${reexport.module.readableIdentifier(
requestShortener
)}".${reexport.exportName}`
);
}
alreadyVisited.add(reexport);
2018-02-25 02:00:20 +01:00
const refInfo = moduleToInfoMap.get(reexport.module);
if (refInfo) {
// module is in the concatenation
return getFinalName(
refInfo,
reexport.exportName,
moduleToInfoMap,
requestShortener,
asCall,
strictHarmonyModule,
alreadyVisited
2018-02-25 02:00:20 +01:00
);
}
2017-05-10 13:15:14 +02:00
}
2018-02-25 02:00:20 +01:00
const problem =
2018-03-26 16:56:10 +02:00
`Cannot get final name for export "${exportName}" in "${info.module.readableIdentifier(
requestShortener
)}"` +
2018-02-25 02:00:20 +01:00
` (known exports: ${Array.from(info.exportMap.keys())
.filter(name => name !== true)
.join(" ")}, ` +
`known reexports: ${Array.from(info.reexportMap.keys()).join(" ")})`;
return `${Template.toNormalComment(problem)} undefined`;
}
case "external": {
const importedModule = info.module;
return getExternalImport(
importedModule,
info,
exportName,
asCall,
strictHarmonyModule
);
}
2017-05-10 13:15:14 +02:00
}
2017-11-08 11:32:05 +01:00
};
2017-05-10 13:15:14 +02:00
const addScopeSymbols1 = (s, nameSet, scopeSet) => {
let scope = s;
2018-02-25 02:00:20 +01:00
while (scope) {
if (scopeSet.has(scope)) break;
scopeSet.add(scope);
2018-02-25 02:00:20 +01:00
for (const variable of scope.variables) {
nameSet.add(variable.name);
}
scope = scope.upper;
}
};
const addScopeSymbols2 = (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 13:52:43 +01:00
}
scope = scope.upper;
}
2017-11-08 11:32:05 +01:00
};
2017-11-08 11:32:05 +01:00
const getAllReferences = variable => {
2017-06-06 11:18:34 +02: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 02:00:20 +01: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 11:18:34 +02:00
set = set.concat(innerVar.references);
break;
}
}
}
return set;
2017-11-08 11:32:05 +01:00
};
2017-06-06 11:18:34 +02:00
2017-11-08 11:32:05 +01:00
const getPathInAst = (ast, node) => {
2018-02-25 02:00:20 +01:00
if (ast === node) {
return [];
}
2017-11-08 11:32:05 +01:00
const nr = node.range;
2017-11-08 11:32:05 +01:00
const enterNode = n => {
2018-02-25 02:00:20 +01:00
if (!n) return undefined;
2017-11-08 11:32:05 +01:00
const r = n.range;
2018-02-25 02:00:20 +01:00
if (r) {
if (r[0] <= nr[0] && r[1] >= nr[1]) {
2017-11-08 11:32:05 +01:00
const path = getPathInAst(n, node);
2018-02-25 02:00:20 +01:00
if (path) {
2017-11-08 11:32:05 +01:00
path.push(n);
return path;
}
}
}
return undefined;
};
var i;
2018-02-25 02:00:20 +01:00
if (Array.isArray(ast)) {
for (i = 0; i < ast.length; i++) {
const enterResult = enterNode(ast[i]);
2018-08-21 02:26:50 +02:00
if (enterResult !== undefined) return enterResult;
}
2018-02-25 02:00:20 +01:00
} else if (ast && typeof ast === "object") {
const keys = Object.keys(ast);
2018-02-25 02:00:20 +01:00
for (i = 0; i < keys.length; i++) {
const value = ast[keys[i]];
2018-02-25 02:00:20 +01:00
if (Array.isArray(value)) {
const pathResult = getPathInAst(value, node);
2018-08-21 02:26:50 +02:00
if (pathResult !== undefined) return pathResult;
2018-02-25 02:00:20 +01:00
} else if (value && typeof value === "object") {
const enterResult = enterNode(value);
2018-08-21 02:26:50 +02:00
if (enterResult !== undefined) return enterResult;
}
}
}
2017-11-08 11:32:05 +01:00
};
2017-05-10 13:15:14 +02:00
class ConcatenatedModule extends Module {
constructor(rootModule, modules, concatenationList) {
2018-01-30 21:40:44 +01:00
super("javascript/esm", null);
super.setChunks(rootModule._chunks);
// Info from Factory
2017-05-10 13:15:14 +02:00
this.rootModule = rootModule;
2018-06-26 15:27:44 +02:00
this.factoryMeta = rootModule.factoryMeta;
// Info from Compilation
2017-05-10 13:15:14 +02:00
this.index = rootModule.index;
this.index2 = rootModule.index2;
this.depth = rootModule.depth;
// Info from Optimization
this.used = rootModule.used;
this.usedExports = rootModule.usedExports;
// Info from Build
this.buildInfo = {
strict: true,
cacheable: modules.every(m => m.buildInfo.cacheable),
moduleArgument: rootModule.buildInfo.moduleArgument,
exportsArgument: rootModule.buildInfo.exportsArgument,
fileDependencies: new Set(),
contextDependencies: new Set(),
assets: undefined
};
2017-05-10 13:15:14 +02:00
this.built = modules.some(m => m.built);
this.buildMeta = rootModule.buildMeta;
// Caching
2017-08-07 11:53:07 +02:00
this._numberOfConcatenatedModules = modules.length;
// Graph
const modulesSet = new Set(modules);
2018-02-25 02:00:20 +01:00
this.reasons = rootModule.reasons.filter(
reason =>
!(reason.dependency instanceof HarmonyImportDependency) ||
!modulesSet.has(reason.module)
);
this.dependencies = [];
this.warnings = [];
this.errors = [];
this._orderedConcatenationList =
concatenationList ||
ConcatenatedModule.createConcatenationList(rootModule, modulesSet, null);
2018-02-25 02:00:20 +01:00
for (const info of this._orderedConcatenationList) {
if (info.type === "concatenated") {
2017-08-07 11:53:07 +02:00
const m = info.module;
// populate dependencies
2018-02-25 02:00:20 +01:00
for (const d of m.dependencies.filter(
dep =>
!(dep instanceof HarmonyImportDependency) ||
!modulesSet.has(dep._module)
2018-02-25 02:00:20 +01:00
)) {
2018-01-22 13:52:43 +01:00
this.dependencies.push(d);
}
2017-08-07 11:53:07 +02:00
// populate file dependencies
2018-02-25 02:00:20 +01:00
if (m.buildInfo.fileDependencies) {
for (const file of m.buildInfo.fileDependencies) {
2018-01-22 13:52:43 +01:00
this.buildInfo.fileDependencies.add(file);
}
}
2017-08-07 11:53:07 +02:00
// populate context dependencies
2018-02-25 02:00:20 +01:00
if (m.buildInfo.contextDependencies) {
for (const context of m.buildInfo.contextDependencies) {
2018-01-22 13:52:43 +01:00
this.buildInfo.contextDependencies.add(context);
}
}
2017-08-07 11:53:07 +02:00
// populate warnings
for (const warning of m.warnings) {
this.warnings.push(warning);
}
2017-08-07 11:53:07 +02:00
// populate errors
for (const error of m.errors) {
this.errors.push(error);
}
2017-08-07 11:53:07 +02:00
2018-02-25 02:00:20 +01:00
if (m.buildInfo.assets) {
if (this.buildInfo.assets === undefined) {
this.buildInfo.assets = Object.create(null);
}
Object.assign(this.buildInfo.assets, m.buildInfo.assets);
2017-11-06 16:41:26 +01:00
}
2017-08-07 11:53:07 +02:00
}
}
this._identifier = this._createIdentifier();
2017-05-10 13:15:14 +02:00
}
get modules() {
return this._orderedConcatenationList
.filter(info => info.type === "concatenated")
.map(info => info.module);
}
2017-05-10 13:15:14 +02:00
identifier() {
return this._identifier;
2017-05-10 13:15:14 +02:00
}
readableIdentifier(requestShortener) {
2018-02-25 02:00:20 +01:00
return (
this.rootModule.readableIdentifier(requestShortener) +
` + ${this._numberOfConcatenatedModules - 1} modules`
);
2017-05-10 13:15:14 +02:00
}
libIdent(options) {
2017-06-05 14:01:19 +02:00
return this.rootModule.libIdent(options);
2017-05-10 13:15:14 +02:00
}
nameForCondition() {
return this.rootModule.nameForCondition();
}
build(options, compilation, resolver, fs, callback) {
throw new Error("Cannot build this module. It should be already built.");
}
size() {
// Guess size from embedded modules
2017-08-07 11:53:07 +02:00
return this._orderedConcatenationList.reduce((sum, info) => {
2018-02-25 02:00:20 +01:00
switch (info.type) {
2017-08-07 11:53:07 +02:00
case "concatenated":
return sum + info.module.size();
case "external":
return sum + 5;
}
return sum;
}, 0);
}
/**
* @param {Module} rootModule the root of the concatenation
* @param {Set<Module>} modulesSet a set of modules which should be concatenated
* @param {Compilation} compilation the compilation context
* @returns {ConcatenationEntry[]} concatenation list
*/
static createConcatenationList(rootModule, modulesSet, compilation) {
2017-08-07 11:53:07 +02:00
const list = [];
const set = new Set();
2017-08-07 11:53:07 +02:00
/**
* @param {Module} module a module
* @returns {(function(): Module)[]} imported modules in order
*/
2017-11-08 11:32:05 +01:00
const getConcatenatedImports = module => {
/** @type {WeakMap<DependencyReference, Dependency>} */
const map = new WeakMap();
const references = module.dependencies
.filter(dep => dep instanceof HarmonyImportDependency)
.map(dep => {
const ref = compilation.getDependencyReference(module, dep);
if (ref) map.set(ref, dep);
return ref;
})
.filter(ref => ref);
DependencyReference.sort(references);
2018-06-22 17:35:48 +02:00
// TODO webpack 5: remove this hack, see also DependencyReference
return references.map(ref => {
const dep = map.get(ref);
return () => compilation.getDependencyReference(module, dep).module;
});
2017-11-08 11:32:05 +01:00
};
2017-08-07 11:53:07 +02:00
2017-11-08 11:32:05 +01:00
const enterModule = getModule => {
2017-08-07 13:56:50 +02:00
const module = getModule();
2018-02-25 02:00:20 +01:00
if (!module) return;
if (set.has(module)) return;
set.add(module);
2018-02-25 02:00:20 +01:00
if (modulesSet.has(module)) {
2017-08-07 11:53:07 +02:00
const imports = getConcatenatedImports(module);
imports.forEach(enterModule);
list.push({
type: "concatenated",
module
});
} else {
list.push({
type: "external",
2017-08-07 13:56:50 +02: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 getModule();
}
2017-08-07 11:53:07 +02:00
});
}
2017-11-08 11:32:05 +01:00
};
2017-08-07 11:53:07 +02:00
2017-08-07 13:56:50 +02:00
enterModule(() => rootModule);
2017-08-07 11:53:07 +02:00
return list;
2017-05-10 13:15:14 +02:00
}
_createIdentifier() {
let orderedConcatenationListIdentifiers = "";
2018-02-25 02:00:20 +01:00
for (let i = 0; i < this._orderedConcatenationList.length; i++) {
if (this._orderedConcatenationList[i].type === "concatenated") {
orderedConcatenationListIdentifiers += this._orderedConcatenationList[
i
].module.identifier();
orderedConcatenationListIdentifiers += " ";
}
}
2018-02-22 11:27:11 +01:00
const hash = createHash("md4");
hash.update(orderedConcatenationListIdentifiers);
return this.rootModule.identifier() + " " + hash.digest("hex");
}
source(dependencyTemplates, runtimeTemplate) {
const requestShortener = runtimeTemplate.requestShortener;
2017-05-10 13:15:14 +02:00
// Metainfo for each module
const modulesWithInfo = this._orderedConcatenationList.map((info, idx) => {
2018-02-25 02:00:20 +01:00
switch (info.type) {
case "concatenated": {
const exportMap = new Map();
const reexportMap = new Map();
for (const dep of info.module.dependencies) {
if (dep instanceof HarmonyExportSpecifierDependency) {
if (!exportMap.has(dep.name)) {
exportMap.set(dep.name, dep.id);
}
2018-02-25 02:00:20 +01:00
} else if (dep instanceof HarmonyExportExpressionDependency) {
if (!exportMap.has("default")) {
2018-02-25 02:00:20 +01:00
exportMap.set("default", "__WEBPACK_MODULE_DEFAULT_EXPORT__");
}
2018-02-25 02:00:20 +01:00
} else if (
dep instanceof HarmonyExportImportedSpecifierDependency
) {
const exportName = dep.name;
const importName = dep._id;
const importedModule = dep._module;
2018-02-25 02:00:20 +01:00
if (exportName && importName) {
if (!reexportMap.has(exportName)) {
reexportMap.set(exportName, {
module: importedModule,
exportName: importName,
dependency: dep
});
}
} else if (exportName) {
if (!reexportMap.has(exportName)) {
reexportMap.set(exportName, {
module: importedModule,
exportName: true,
dependency: dep
});
}
} else if (importedModule) {
for (const name of importedModule.buildMeta.providedExports) {
if (dep.activeExports.has(name) || name === "default") {
2018-02-25 02:00:20 +01:00
continue;
}
2018-02-25 02:00:20 +01:00
if (!reexportMap.has(name)) {
reexportMap.set(name, {
module: importedModule,
2018-02-25 02:00:20 +01:00
exportName: name,
dependency: dep
});
}
}
}
2018-01-22 13:52:43 +01:00
}
2017-05-10 13:15:14 +02:00
}
2018-02-25 02:00:20 +01:00
return {
type: "concatenated",
module: info.module,
index: idx,
ast: undefined,
internalSource: undefined,
source: undefined,
globalScope: undefined,
moduleScope: undefined,
internalNames: new Map(),
globalExports: new Set(),
2018-02-25 02:00:20 +01:00
exportMap: exportMap,
reexportMap: reexportMap,
hasNamespaceObject: false,
namespaceObjectSource: null
};
}
case "external":
return {
type: "external",
module: info.module,
index: idx,
name: undefined,
2018-01-05 22:35:54 +01:00
interopNamespaceObjectUsed: false,
interopNamespaceObjectName: undefined,
interopDefaultAccessUsed: false,
interopDefaultAccessName: undefined
};
default:
throw new Error(`Unsupported concatenation entry type ${info.type}`);
}
2017-05-10 13:15:14 +02:00
});
// Create mapping from module to info
const moduleToInfoMap = new Map();
2018-02-25 02:00:20 +01:00
for (const m of modulesWithInfo) {
2018-01-22 13:52:43 +01:00
moduleToInfoMap.set(m.module, m);
}
2017-05-10 13:15:14 +02:00
// Configure template decorators for dependencies
const innerDependencyTemplates = new Map(dependencyTemplates);
2018-02-25 02:00:20 +01:00
innerDependencyTemplates.set(
HarmonyImportSpecifierDependency,
new HarmonyImportSpecifierDependencyConcatenatedTemplate(
dependencyTemplates.get(HarmonyImportSpecifierDependency),
moduleToInfoMap
)
);
innerDependencyTemplates.set(
HarmonyImportSideEffectDependency,
new HarmonyImportSideEffectDependencyConcatenatedTemplate(
dependencyTemplates.get(HarmonyImportSideEffectDependency),
moduleToInfoMap
)
);
innerDependencyTemplates.set(
HarmonyExportSpecifierDependency,
new HarmonyExportSpecifierDependencyConcatenatedTemplate(
dependencyTemplates.get(HarmonyExportSpecifierDependency),
this.rootModule
)
);
innerDependencyTemplates.set(
HarmonyExportExpressionDependency,
new HarmonyExportExpressionDependencyConcatenatedTemplate(
dependencyTemplates.get(HarmonyExportExpressionDependency),
this.rootModule
2018-02-25 02:00:20 +01:00
)
);
innerDependencyTemplates.set(
HarmonyExportImportedSpecifierDependency,
new HarmonyExportImportedSpecifierDependencyConcatenatedTemplate(
dependencyTemplates.get(HarmonyExportImportedSpecifierDependency),
this.rootModule,
moduleToInfoMap
)
);
innerDependencyTemplates.set(
HarmonyCompatibilityDependency,
new HarmonyCompatibilityDependencyConcatenatedTemplate(
dependencyTemplates.get(HarmonyCompatibilityDependency),
this.rootModule,
moduleToInfoMap
)
);
// Must use full identifier in our cache here to ensure that the source
// is updated should our dependencies list change.
2018-06-25 16:18:21 +02:00
// TODO webpack 5 refactor
2018-02-25 02:00:20 +01:00
innerDependencyTemplates.set(
"hash",
innerDependencyTemplates.get("hash") + this.identifier()
2018-02-25 02:00:20 +01:00
);
2017-05-10 13:15:14 +02:00
// Generate source code and analyse scopes
// Prepare a ReplaceSource for the final source
2018-02-25 02:00:20 +01:00
for (const info of modulesWithInfo) {
if (info.type === "concatenated") {
const m = info.module;
const source = m.source(innerDependencyTemplates, runtimeTemplate);
const code = source.source();
let ast;
try {
ast = Parser.parse(code, {
2018-02-25 02:00:20 +01:00
sourceType: "module"
});
2018-02-25 02:00:20 +01:00
} catch (err) {
if (
err.loc &&
typeof err.loc === "object" &&
typeof err.loc.line === "number"
) {
const lineNumber = err.loc.line;
const lines = code.split("\n");
2018-02-25 02:00:20 +01: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
});
const globalScope = scopeManager.acquire(ast);
const moduleScope = globalScope.childScopes[0];
const resultSource = new ReplaceSource(source);
info.ast = ast;
info.internalSource = source;
info.source = resultSource;
info.globalScope = globalScope;
info.moduleScope = moduleScope;
}
2018-01-22 13:52:43 +01:00
}
2017-05-10 13:15:14 +02:00
// List of all used names to avoid conflicts
const allUsedNames = new Set([
"__WEBPACK_MODULE_DEFAULT_EXPORT__", // avoid using this internal name
2018-02-25 02:00:20 +01:00
"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",
"module",
"__dirname",
"__filename",
"exports",
"Array",
"Date",
"eval",
"function",
"hasOwnProperty",
"Infinity",
"isFinite",
"isNaN",
"isPrototypeOf",
"length",
"Math",
"NaN",
"name",
"Number",
"Object",
"prototype",
"String",
"toString",
"undefined",
"valueOf",
"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",
"onblur",
"onclick",
"onerror",
"onfocus",
"onkeydown",
"onkeypress",
"onkeyup",
"onmouseover",
"onload",
"onmouseup",
"onmousedown",
"onsubmit"
]);
2017-05-10 13:15:14 +02:00
// Set of already checked scopes
const alreadyCheckedScopes = new Set();
2017-05-10 13:15:14 +02:00
// get all global names
2018-02-25 02:00:20 +01:00
for (const info of modulesWithInfo) {
const superClassExpressions = [];
// ignore symbols from moduleScope
if (info.moduleScope) {
alreadyCheckedScopes.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;
if (!childScope.block.superClass) continue;
superClassExpressions.push({
range: childScope.block.superClass.range,
variables: childScope.variables
});
}
}
// add global symbols
2018-02-25 02:00:20 +01:00
if (info.globalScope) {
for (const reference of info.globalScope.through) {
const name = reference.identifier.name;
2018-02-25 02:00:20 +01:00
if (
/^__WEBPACK_MODULE_REFERENCE__\d+_([\da-f]+|ns)(_call)?(_strict)?__$/.test(
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) {
allUsedNames.add(variable.name);
}
}
}
addScopeSymbols1(
reference.from,
allUsedNames,
alreadyCheckedScopes
);
} else {
allUsedNames.add(name);
}
2018-01-22 13:52:43 +01:00
}
}
// add exported globals
if (info.type === "concatenated") {
const variables = new Set();
for (const variable of info.moduleScope.variables) {
variables.add(variable.name);
}
for (const [, variable] of info.exportMap) {
if (!variables.has(variable)) {
info.globalExports.add(variable);
}
}
}
2018-01-22 13:52:43 +01:00
}
2017-05-10 13:15:14 +02:00
// generate names for symbols
2018-02-25 02:00:20 +01:00
for (const info of modulesWithInfo) {
switch (info.type) {
case "concatenated": {
const namespaceObjectName = this.findNewName(
"namespaceObject",
allUsedNames,
null,
info.module.readableIdentifier(requestShortener)
);
allUsedNames.add(namespaceObjectName);
info.internalNames.set(namespaceObjectName, namespaceObjectName);
info.exportMap.set(true, namespaceObjectName);
for (const variable of info.moduleScope.variables) {
const name = variable.name;
if (allUsedNames.has(name)) {
const references = getAllReferences(variable);
const symbolsInReferences = new Set();
const alreadyCheckedInnerScopes = new Set();
for (const ref of references) {
addScopeSymbols2(
ref.from,
symbolsInReferences,
alreadyCheckedInnerScopes,
alreadyCheckedScopes
);
}
2018-02-25 02:00:20 +01:00
const newName = this.findNewName(
name,
allUsedNames,
symbolsInReferences,
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 02:00:20 +01:00
} else {
allUsedNames.add(name);
info.internalNames.set(name, name);
2018-01-22 13:52:43 +01:00
}
2017-05-10 13:15:14 +02:00
}
2018-02-25 02:00:20 +01:00
break;
}
case "external": {
const externalName = this.findNewName(
"",
allUsedNames,
null,
info.module.readableIdentifier(requestShortener)
);
allUsedNames.add(externalName);
info.name = externalName;
if (
info.module.buildMeta.exportsType === "named" ||
!info.module.buildMeta.exportsType
) {
const externalNameInterop = this.findNewName(
"namespaceObject",
allUsedNames,
null,
info.module.readableIdentifier(requestShortener)
);
allUsedNames.add(externalNameInterop);
info.interopNamespaceObjectName = externalNameInterop;
}
if (!info.module.buildMeta.exportsType) {
const externalNameInterop = this.findNewName(
"default",
allUsedNames,
null,
info.module.readableIdentifier(requestShortener)
);
allUsedNames.add(externalNameInterop);
info.interopDefaultAccessName = externalNameInterop;
}
2018-02-25 02:00:20 +01:00
break;
}
}
2018-01-22 13:52:43 +01:00
}
2017-05-10 13:15:14 +02:00
// Find and replace referenced to modules
2018-02-25 02:00:20 +01:00
for (const info of modulesWithInfo) {
if (info.type === "concatenated") {
for (const reference of info.globalScope.through) {
const name = reference.identifier.name;
2018-02-25 02:00:20 +01:00
const match = /^__WEBPACK_MODULE_REFERENCE__(\d+)_([\da-f]+|ns)(_call)?(_strict)?__$/.exec(
name
);
if (match) {
const referencedModule = modulesWithInfo[+match[1]];
let exportName;
2018-02-25 02:00:20 +01:00
if (match[2] === "ns") {
exportName = true;
} else {
2017-08-07 13:56:50 +02:00
const exportData = match[2];
2018-01-22 15:00:12 +01:00
exportName = Buffer.from(exportData, "hex").toString("utf-8");
}
const asCall = !!match[3];
const strictHarmonyModule = !!match[4];
2018-02-25 02:00:20 +01:00
const finalName = getFinalName(
referencedModule,
exportName,
moduleToInfoMap,
requestShortener,
asCall,
strictHarmonyModule
);
const r = reference.identifier.range;
const source = info.source;
source.replace(r[0], r[1] - 1, finalName);
2017-05-10 13:15:14 +02:00
}
2018-01-22 13:52:43 +01:00
}
}
2018-01-22 13:52:43 +01:00
}
2017-05-10 13:15:14 +02:00
const result = new ConcatSource();
2017-08-07 14:12:31 +02:00
// add harmony compatibility flag (must be first because of possible circular dependencies)
const usedExports = this.rootModule.usedExports;
2019-01-22 09:13:21 +01:00
if (usedExports === true || usedExports === null) {
2018-02-25 02:00:20 +01:00
result.add(
runtimeTemplate.defineEsModuleFlagStatement({
exportsArgument: this.exportsArgument
})
);
2017-05-10 13:15:14 +02:00
}
2017-08-07 14:12:31 +02:00
// define required namespace objects (must be before evaluation modules)
2018-02-25 02:00:20 +01:00
for (const info of modulesWithInfo) {
if (info.namespaceObjectSource) {
2017-08-07 14:12:31 +02:00
result.add(info.namespaceObjectSource);
}
2018-01-22 13:52:43 +01:00
}
2017-08-07 14:12:31 +02:00
// evaluate modules in order
2018-02-25 02:00:20 +01:00
for (const info of modulesWithInfo) {
switch (info.type) {
2017-08-07 11:53:07 +02:00
case "concatenated":
2018-02-25 02:00:20 +01:00
result.add(
`\n// CONCATENATED MODULE: ${info.module.readableIdentifier(
requestShortener
)}\n`
);
result.add(info.source);
break;
2017-08-07 11:53:07 +02:00
case "external":
2018-02-25 02:00:20 +01:00
result.add(
`\n// EXTERNAL MODULE: ${info.module.readableIdentifier(
requestShortener
)}\n`
);
result.add(
`var ${info.name} = __webpack_require__(${JSON.stringify(
info.module.id
)});\n`
);
if (info.interopNamespaceObjectUsed) {
if (info.module.buildMeta.exportsType === "named") {
result.add(
2019-06-09 11:23:42 +02:00
`var ${info.interopNamespaceObjectName} = /*#__PURE__*/__webpack_require__.t(${info.name}, 2);\n`
2018-02-25 02:00:20 +01:00
);
} else if (!info.module.buildMeta.exportsType) {
result.add(
2019-06-09 11:23:42 +02:00
`var ${info.interopNamespaceObjectName} = /*#__PURE__*/__webpack_require__.t(${info.name});\n`
2018-02-25 02:00:20 +01:00
);
}
}
2018-02-25 02:00:20 +01:00
if (info.interopDefaultAccessUsed) {
result.add(
2019-06-09 11:23:42 +02:00
`var ${info.interopDefaultAccessName} = /*#__PURE__*/__webpack_require__.n(${info.name});\n`
2018-02-25 02:00:20 +01:00
);
}
2017-08-07 11:53:07 +02:00
break;
default:
throw new Error(`Unsupported concatenation entry type ${info.type}`);
2017-05-10 13:15:14 +02:00
}
2018-01-22 13:52:43 +01:00
}
2017-05-10 13:15:14 +02:00
return result;
}
findNewName(oldName, usedNamed1, usedNamed2, extraInfo) {
2017-05-10 13:15:14 +02:00
let name = oldName;
2018-02-25 02:00:20 +01:00
if (name === "__WEBPACK_MODULE_DEFAULT_EXPORT__") name = "";
2017-05-10 13:15:14 +02:00
2017-06-07 12:57:15 +02:00
// Remove uncool stuff
2018-02-25 02:00:20 +01:00
extraInfo = extraInfo.replace(
/\.+\/|(\/index)?\.([a-zA-Z0-9]{1,4})($|\s|\?)|\s*\+\s*\d+\s*modules/g,
""
);
2017-06-07 12:57:15 +02:00
2017-05-10 13:15:14 +02:00
const splittedInfo = extraInfo.split("/");
2018-02-25 02:00:20 +01:00
while (splittedInfo.length) {
2017-08-07 14:38:54 +02:00
name = splittedInfo.pop() + (name ? "_" + name : "");
const nameIdent = Template.toIdentifier(name);
2018-02-25 02:00:20 +01:00
if (
!usedNamed1.has(nameIdent) &&
(!usedNamed2 || !usedNamed2.has(nameIdent))
)
return nameIdent;
2017-05-10 13:15:14 +02:00
}
2017-08-07 16:15:49 +02:00
let i = 0;
let nameWithNumber = Template.toIdentifier(`${name}_${i}`);
2018-02-25 02:00:20 +01:00
while (
usedNamed1.has(nameWithNumber) ||
(usedNamed2 && usedNamed2.has(nameWithNumber))
) {
i++;
nameWithNumber = Template.toIdentifier(`${name}_${i}`);
2017-08-07 16:15:49 +02:00
}
return nameWithNumber;
2017-05-10 13:15:14 +02:00
}
/**
* @param {Hash} hash the hash used to track dependencies
* @returns {void}
*/
updateHash(hash) {
2018-02-25 02:00:20 +01:00
for (const info of this._orderedConcatenationList) {
switch (info.type) {
2017-08-07 11:53:07 +02:00
case "concatenated":
info.module.updateHash(hash);
break;
case "external":
hash.update(`${info.module.id}`);
break;
}
}
super.updateHash(hash);
}
2017-05-10 13:15:14 +02:00
}
class HarmonyImportSpecifierDependencyConcatenatedTemplate {
constructor(originalTemplate, modulesMap) {
this.originalTemplate = originalTemplate;
this.modulesMap = modulesMap;
}
getHarmonyInitOrder(dep) {
const module = dep._module;
const info = this.modulesMap.get(module);
2018-02-25 02:00:20 +01:00
if (!info) {
return this.originalTemplate.getHarmonyInitOrder(dep);
}
return NaN;
}
harmonyInit(dep, source, runtimeTemplate, dependencyTemplates) {
const module = dep._module;
const info = this.modulesMap.get(module);
2018-02-25 02:00:20 +01:00
if (!info) {
this.originalTemplate.harmonyInit(
dep,
source,
runtimeTemplate,
dependencyTemplates
);
return;
}
}
apply(dep, source, runtime, dependencyTemplates) {
const module = dep._module;
2017-05-10 13:15:14 +02:00
const info = this.modulesMap.get(module);
2018-02-25 02:00:20 +01:00
if (!info) {
this.originalTemplate.apply(dep, source, runtime, dependencyTemplates);
2017-05-10 13:15:14 +02:00
return;
}
let content;
const callFlag = dep.call ? "_call" : "";
2018-02-25 02:00:20 +01:00
const strictFlag = dep.originModule.buildMeta.strictHarmonyModule
? "_strict"
: "";
if (dep._id === null) {
content = `__WEBPACK_MODULE_REFERENCE__${info.index}_ns${strictFlag}__`;
2018-02-25 02:00:20 +01:00
} else if (dep.namespaceObjectAsContext) {
2018-03-26 16:56:10 +02:00
content = `__WEBPACK_MODULE_REFERENCE__${
info.index
}_ns${strictFlag}__[${JSON.stringify(dep._id)}]`;
2017-05-10 13:15:14 +02:00
} else {
const exportData = Buffer.from(dep._id, "utf-8").toString("hex");
2019-06-09 11:23:42 +02:00
content = `__WEBPACK_MODULE_REFERENCE__${info.index}_${exportData}${callFlag}${strictFlag}__`;
2017-05-10 13:15:14 +02:00
}
2018-02-25 02:00:20 +01:00
if (dep.shorthand) {
2017-05-10 13:15:14 +02:00
content = dep.name + ": " + content;
}
source.replace(dep.range[0], dep.range[1] - 1, content);
}
}
class HarmonyImportSideEffectDependencyConcatenatedTemplate {
2017-05-10 13:15:14 +02:00
constructor(originalTemplate, modulesMap) {
this.originalTemplate = originalTemplate;
this.modulesMap = modulesMap;
}
getHarmonyInitOrder(dep) {
const module = dep._module;
const info = this.modulesMap.get(module);
2018-02-25 02:00:20 +01:00
if (!info) {
return this.originalTemplate.getHarmonyInitOrder(dep);
}
return NaN;
}
harmonyInit(dep, source, runtime, dependencyTemplates) {
const module = dep._module;
const info = this.modulesMap.get(module);
2018-02-25 02:00:20 +01:00
if (!info) {
this.originalTemplate.harmonyInit(
dep,
source,
runtime,
dependencyTemplates
);
return;
}
}
apply(dep, source, runtime, dependencyTemplates) {
const module = dep._module;
2017-05-10 13:15:14 +02:00
const info = this.modulesMap.get(module);
2018-02-25 02:00:20 +01:00
if (!info) {
this.originalTemplate.apply(dep, source, runtime, dependencyTemplates);
2017-05-10 13:15:14 +02:00
return;
}
}
}
class HarmonyExportSpecifierDependencyConcatenatedTemplate {
constructor(originalTemplate, rootModule) {
this.originalTemplate = originalTemplate;
this.rootModule = rootModule;
}
getHarmonyInitOrder(dep) {
2018-02-25 02:00:20 +01:00
if (dep.originModule === this.rootModule) {
return this.originalTemplate.getHarmonyInitOrder(dep);
}
return NaN;
}
harmonyInit(dep, source, runtime, dependencyTemplates) {
2018-02-25 02:00:20 +01:00
if (dep.originModule === this.rootModule) {
this.originalTemplate.harmonyInit(
dep,
source,
runtime,
dependencyTemplates
);
return;
}
}
apply(dep, source, runtime, dependencyTemplates) {
2018-02-25 02:00:20 +01:00
if (dep.originModule === this.rootModule) {
this.originalTemplate.apply(dep, source, runtime, dependencyTemplates);
2017-05-10 13:15:14 +02:00
}
}
}
class HarmonyExportExpressionDependencyConcatenatedTemplate {
constructor(originalTemplate, rootModule) {
this.originalTemplate = originalTemplate;
this.rootModule = rootModule;
}
apply(dep, source, runtime, dependencyTemplates) {
2018-02-25 02:00:20 +01:00
let content =
"/* harmony default export */ var __WEBPACK_MODULE_DEFAULT_EXPORT__ = ";
if (dep.originModule === this.rootModule) {
const used = dep.originModule.isUsed("default");
const exportsName = dep.originModule.exportsArgument;
2018-02-25 02:00:20 +01:00
if (used) content += `${exportsName}[${JSON.stringify(used)}] = `;
}
2017-05-10 13:15:14 +02:00
2018-02-25 02:00:20 +01:00
if (dep.range) {
2018-09-25 21:52:03 +02:00
source.replace(
dep.rangeStatement[0],
dep.range[0] - 1,
content + "(" + dep.prefix
);
source.replace(dep.range[1], dep.rangeStatement[1] - 1, ");");
return;
2017-05-10 13:15:14 +02:00
}
2018-09-25 21:52:03 +02:00
source.replace(
dep.rangeStatement[0],
dep.rangeStatement[1] - 1,
content + dep.prefix
);
2017-05-10 13:15:14 +02:00
}
}
class HarmonyExportImportedSpecifierDependencyConcatenatedTemplate {
constructor(originalTemplate, rootModule, modulesMap) {
this.originalTemplate = originalTemplate;
this.rootModule = rootModule;
this.modulesMap = modulesMap;
}
getExports(dep) {
const importModule = dep._module;
if (dep._id) {
2017-05-10 13:15:14 +02:00
// export { named } from "module"
2018-02-25 02:00:20 +01:00
return [
{
name: dep.name,
id: dep._id,
2018-02-25 02:00:20 +01:00
module: importModule
}
];
2017-05-10 13:15:14 +02:00
}
2018-02-25 02:00:20 +01:00
if (dep.name) {
2017-05-10 13:15:14 +02:00
// export * as abc from "module"
2018-02-25 02:00:20 +01:00
return [
{
name: dep.name,
id: true,
module: importModule
}
];
2017-05-10 13:15:14 +02:00
}
// export * from "module"
2018-02-25 02:00:20 +01:00
return importModule.buildMeta.providedExports
.filter(exp => exp !== "default" && !dep.activeExports.has(exp))
.map(exp => {
return {
name: exp,
id: exp,
module: importModule
};
});
2017-05-10 13:15:14 +02:00
}
getHarmonyInitOrder(dep) {
const module = dep._module;
const info = this.modulesMap.get(module);
2018-02-25 02:00:20 +01:00
if (!info) {
return this.originalTemplate.getHarmonyInitOrder(dep);
}
return NaN;
}
harmonyInit(dep, source, runtime, dependencyTemplates) {
const module = dep._module;
const info = this.modulesMap.get(module);
2018-02-25 02:00:20 +01:00
if (!info) {
this.originalTemplate.harmonyInit(
dep,
source,
runtime,
dependencyTemplates
);
return;
}
}
apply(dep, source, runtime, dependencyTemplates) {
2018-02-25 02:00:20 +01:00
if (dep.originModule === this.rootModule) {
if (this.modulesMap.get(dep._module)) {
2017-05-10 13:15:14 +02:00
const exportDefs = this.getExports(dep);
2018-02-25 02:00:20 +01:00
for (const def of exportDefs) {
2017-05-10 13:15:14 +02:00
const info = this.modulesMap.get(def.module);
const used = dep.originModule.isUsed(def.name);
2018-02-25 02:00:20 +01:00
if (!used) {
source.insert(
-1,
2018-08-22 11:11:37 +02:00
`/* unused concated harmony import ${def.name} */\n`
2018-02-25 02:00:20 +01:00
);
2018-08-22 11:11:37 +02:00
continue;
2017-05-10 13:15:14 +02:00
}
let finalName;
2018-02-25 02:00:20 +01:00
const strictFlag = dep.originModule.buildMeta.strictHarmonyModule
? "_strict"
: "";
if (def.id === true) {
2019-06-09 11:23:42 +02:00
finalName = `__WEBPACK_MODULE_REFERENCE__${info.index}_ns${strictFlag}__`;
2017-05-10 13:15:14 +02:00
} else {
2018-01-22 15:00:12 +01:00
const exportData = Buffer.from(def.id, "utf-8").toString("hex");
2019-06-09 11:23:42 +02:00
finalName = `__WEBPACK_MODULE_REFERENCE__${info.index}_${exportData}${strictFlag}__`;
2017-05-10 13:15:14 +02:00
}
const exportsName = this.rootModule.exportsArgument;
2018-08-22 11:11:37 +02:00
const content =
`/* concated harmony reexport ${def.name} */` +
`__webpack_require__.d(${exportsName}, ` +
`${JSON.stringify(used)}, ` +
`function() { return ${finalName}; });\n`;
2017-05-10 13:15:14 +02:00
source.insert(-1, content);
2018-01-22 13:52:43 +01:00
}
2017-05-10 13:15:14 +02:00
} else {
this.originalTemplate.apply(dep, source, runtime, dependencyTemplates);
2017-05-10 13:15:14 +02:00
}
}
}
}
class HarmonyCompatibilityDependencyConcatenatedTemplate {
constructor(originalTemplate, rootModule, modulesMap) {
this.originalTemplate = originalTemplate;
this.rootModule = rootModule;
this.modulesMap = modulesMap;
}
apply(dep, source, runtime, dependencyTemplates) {
// do nothing
2017-05-10 13:15:14 +02:00
}
}
module.exports = ConcatenatedModule;