You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1478 lines
37 KiB
1478 lines
37 KiB
/*
|
|
MIT License http://www.opensource.org/licenses/mit-license.php
|
|
Author Tobias Koppers @sokra
|
|
*/
|
|
"use strict";
|
|
|
|
const Module = require("../Module");
|
|
const Template = require("../Template");
|
|
const Parser = require("../Parser");
|
|
const eslintScope = require("eslint-scope");
|
|
const { ConcatSource, ReplaceSource } = require("webpack-sources");
|
|
const DependencyReference = require("../dependencies/DependencyReference");
|
|
const HarmonyImportDependency = require("../dependencies/HarmonyImportDependency");
|
|
const HarmonyImportSideEffectDependency = require("../dependencies/HarmonyImportSideEffectDependency");
|
|
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");
|
|
|
|
/** @typedef {import("../Dependency")} Dependency */
|
|
/** @typedef {import("../Compilation")} Compilation */
|
|
/** @typedef {import("../util/createHash").Hash} Hash */
|
|
/** @typedef {import("../RequestShortener")} RequestShortener */
|
|
|
|
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
|
|
*/
|
|
|
|
const ensureNsObjSource = (
|
|
info,
|
|
moduleToInfoMap,
|
|
requestShortener,
|
|
strictHarmonyModule
|
|
) => {
|
|
if (!info.hasNamespaceObject) {
|
|
info.hasNamespaceObject = true;
|
|
const name = info.exportMap.get(true);
|
|
const nsObj = [`var ${name} = {};`, `__webpack_require__.r(${name});`];
|
|
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}; });`
|
|
);
|
|
}
|
|
info.namespaceObjectSource = nsObj.join("\n") + "\n";
|
|
}
|
|
};
|
|
|
|
const getExternalImport = (
|
|
importedModule,
|
|
info,
|
|
exportName,
|
|
asCall,
|
|
strictHarmonyModule
|
|
) => {
|
|
const used = importedModule.isUsed(exportName);
|
|
if (!used) return "/* unused reexport */undefined";
|
|
const comment =
|
|
used !== exportName ? ` ${Template.toNormalComment(exportName)}` : "";
|
|
switch (importedModule.buildMeta.exportsType) {
|
|
case "named":
|
|
if (exportName === "default") {
|
|
return info.name;
|
|
} else if (exportName === true) {
|
|
info.interopNamespaceObjectUsed = true;
|
|
return info.interopNamespaceObjectName;
|
|
} else {
|
|
break;
|
|
}
|
|
case "namespace":
|
|
if (exportName === true) {
|
|
return info.name;
|
|
} else {
|
|
break;
|
|
}
|
|
default:
|
|
if (strictHarmonyModule) {
|
|
if (exportName === "default") {
|
|
return info.name;
|
|
} else if (exportName === true) {
|
|
info.interopNamespaceObjectUsed = true;
|
|
return info.interopNamespaceObjectName;
|
|
} else {
|
|
return "/* non-default import from non-esm module */undefined";
|
|
}
|
|
} else {
|
|
if (exportName === "default") {
|
|
info.interopDefaultAccessUsed = true;
|
|
return asCall
|
|
? `${info.interopDefaultAccessName}()`
|
|
: `${info.interopDefaultAccessName}.a`;
|
|
} else if (exportName === true) {
|
|
return info.name;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
const reference = `${info.name}[${JSON.stringify(used)}${comment}]`;
|
|
if (asCall) return `Object(${reference})`;
|
|
return reference;
|
|
};
|
|
|
|
const getFinalName = (
|
|
info,
|
|
exportName,
|
|
moduleToInfoMap,
|
|
requestShortener,
|
|
asCall,
|
|
strictHarmonyModule,
|
|
alreadyVisited = new Set()
|
|
) => {
|
|
switch (info.type) {
|
|
case "concatenated": {
|
|
const directExport = info.exportMap.get(exportName);
|
|
if (directExport) {
|
|
if (exportName === true) {
|
|
ensureNsObjSource(
|
|
info,
|
|
moduleToInfoMap,
|
|
requestShortener,
|
|
strictHarmonyModule
|
|
);
|
|
} else if (!info.module.isUsed(exportName)) {
|
|
return "/* unused export */ undefined";
|
|
}
|
|
if (info.globalExports.has(directExport)) {
|
|
return directExport;
|
|
}
|
|
const name = info.internalNames.get(directExport);
|
|
if (!name) {
|
|
throw new Error(
|
|
`The export "${directExport}" in "${info.module.readableIdentifier(
|
|
requestShortener
|
|
)}" has no internal name`
|
|
);
|
|
}
|
|
return name;
|
|
}
|
|
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);
|
|
const refInfo = moduleToInfoMap.get(reexport.module);
|
|
if (refInfo) {
|
|
// module is in the concatenation
|
|
return getFinalName(
|
|
refInfo,
|
|
reexport.exportName,
|
|
moduleToInfoMap,
|
|
requestShortener,
|
|
asCall,
|
|
strictHarmonyModule,
|
|
alreadyVisited
|
|
);
|
|
}
|
|
}
|
|
const problem =
|
|
`Cannot get final name for export "${exportName}" in "${info.module.readableIdentifier(
|
|
requestShortener
|
|
)}"` +
|
|
` (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
|
|
);
|
|
}
|
|
}
|
|
};
|
|
|
|
const addScopeSymbols1 = (s, nameSet, scopeSet) => {
|
|
let scope = s;
|
|
while (scope) {
|
|
if (scopeSet.has(scope)) break;
|
|
scopeSet.add(scope);
|
|
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);
|
|
}
|
|
scope = scope.upper;
|
|
}
|
|
};
|
|
|
|
const getAllReferences = variable => {
|
|
let set = variable.references;
|
|
// Look for inner scope variables too (like in class Foo { t() { Foo } })
|
|
const identifiers = new Set(variable.identifiers);
|
|
for (const scope of variable.scope.childScopes) {
|
|
for (const innerVar of scope.variables) {
|
|
if (innerVar.identifiers.some(id => identifiers.has(id))) {
|
|
set = set.concat(innerVar.references);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return set;
|
|
};
|
|
|
|
const getPathInAst = (ast, node) => {
|
|
if (ast === node) {
|
|
return [];
|
|
}
|
|
|
|
const nr = node.range;
|
|
|
|
const enterNode = n => {
|
|
if (!n) return undefined;
|
|
const r = n.range;
|
|
if (r) {
|
|
if (r[0] <= nr[0] && r[1] >= nr[1]) {
|
|
const path = getPathInAst(n, node);
|
|
if (path) {
|
|
path.push(n);
|
|
return path;
|
|
}
|
|
}
|
|
}
|
|
return undefined;
|
|
};
|
|
|
|
var i;
|
|
if (Array.isArray(ast)) {
|
|
for (i = 0; i < ast.length; i++) {
|
|
const enterResult = enterNode(ast[i]);
|
|
if (enterResult !== undefined) return enterResult;
|
|
}
|
|
} else if (ast && typeof ast === "object") {
|
|
const keys = Object.keys(ast);
|
|
for (i = 0; i < keys.length; i++) {
|
|
const value = ast[keys[i]];
|
|
if (Array.isArray(value)) {
|
|
const pathResult = getPathInAst(value, node);
|
|
if (pathResult !== undefined) return pathResult;
|
|
} else if (value && typeof value === "object") {
|
|
const enterResult = enterNode(value);
|
|
if (enterResult !== undefined) return enterResult;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
const getHarmonyExportImportedSpecifierDependencyExports = dep => {
|
|
const importModule = dep._module;
|
|
if (!importModule) return [];
|
|
if (dep._id) {
|
|
// export { named } from "module"
|
|
return [
|
|
{
|
|
name: dep.name,
|
|
id: dep._id,
|
|
module: importModule
|
|
}
|
|
];
|
|
}
|
|
if (dep.name) {
|
|
// export * as abc from "module"
|
|
return [
|
|
{
|
|
name: dep.name,
|
|
id: true,
|
|
module: importModule
|
|
}
|
|
];
|
|
}
|
|
// export * from "module"
|
|
return importModule.buildMeta.providedExports
|
|
.filter(exp => exp !== "default" && !dep.activeExports.has(exp))
|
|
.map(exp => {
|
|
return {
|
|
name: exp,
|
|
id: exp,
|
|
module: importModule
|
|
};
|
|
});
|
|
};
|
|
|
|
class ConcatenatedModule extends Module {
|
|
constructor(rootModule, modules, concatenationList) {
|
|
super("javascript/esm", null);
|
|
super.setChunks(rootModule._chunks);
|
|
|
|
// Info from Factory
|
|
this.rootModule = rootModule;
|
|
this.factoryMeta = rootModule.factoryMeta;
|
|
|
|
// Info from Compilation
|
|
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
|
|
};
|
|
this.built = modules.some(m => m.built);
|
|
this.buildMeta = rootModule.buildMeta;
|
|
|
|
// Caching
|
|
this._numberOfConcatenatedModules = modules.length;
|
|
|
|
// Graph
|
|
const modulesSet = new Set(modules);
|
|
this.reasons = rootModule.reasons.filter(
|
|
reason =>
|
|
!(reason.dependency instanceof HarmonyImportDependency) ||
|
|
!modulesSet.has(reason.module)
|
|
);
|
|
|
|
this.dependencies = [];
|
|
this.blocks = [];
|
|
|
|
this.warnings = [];
|
|
this.errors = [];
|
|
this._orderedConcatenationList =
|
|
concatenationList ||
|
|
ConcatenatedModule.createConcatenationList(rootModule, modulesSet, null);
|
|
for (const info of this._orderedConcatenationList) {
|
|
if (info.type === "concatenated") {
|
|
const m = info.module;
|
|
|
|
// populate dependencies
|
|
for (const d of m.dependencies.filter(
|
|
dep =>
|
|
!(dep instanceof HarmonyImportDependency) ||
|
|
!modulesSet.has(dep._module)
|
|
)) {
|
|
this.dependencies.push(d);
|
|
}
|
|
// populate blocks
|
|
for (const d of m.blocks) {
|
|
this.blocks.push(d);
|
|
}
|
|
// populate file dependencies
|
|
if (m.buildInfo.fileDependencies) {
|
|
for (const file of m.buildInfo.fileDependencies) {
|
|
this.buildInfo.fileDependencies.add(file);
|
|
}
|
|
}
|
|
// populate context dependencies
|
|
if (m.buildInfo.contextDependencies) {
|
|
for (const context of m.buildInfo.contextDependencies) {
|
|
this.buildInfo.contextDependencies.add(context);
|
|
}
|
|
}
|
|
// populate warnings
|
|
for (const warning of m.warnings) {
|
|
this.warnings.push(warning);
|
|
}
|
|
// populate errors
|
|
for (const error of m.errors) {
|
|
this.errors.push(error);
|
|
}
|
|
|
|
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();
|
|
}
|
|
for (const [key, value] of m.buildInfo.assetsInfo) {
|
|
this.buildInfo.assetsInfo.set(key, value);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
this._identifier = this._createIdentifier();
|
|
}
|
|
|
|
get modules() {
|
|
return this._orderedConcatenationList
|
|
.filter(info => info.type === "concatenated")
|
|
.map(info => info.module);
|
|
}
|
|
|
|
identifier() {
|
|
return this._identifier;
|
|
}
|
|
|
|
readableIdentifier(requestShortener) {
|
|
return (
|
|
this.rootModule.readableIdentifier(requestShortener) +
|
|
` + ${this._numberOfConcatenatedModules - 1} modules`
|
|
);
|
|
}
|
|
|
|
libIdent(options) {
|
|
return this.rootModule.libIdent(options);
|
|
}
|
|
|
|
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
|
|
return this._orderedConcatenationList.reduce((sum, info) => {
|
|
switch (info.type) {
|
|
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) {
|
|
const list = [];
|
|
const set = new Set();
|
|
|
|
/**
|
|
* @param {Module} module a module
|
|
* @returns {(function(): Module)[]} imported modules in order
|
|
*/
|
|
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);
|
|
// TODO webpack 5: remove this hack, see also DependencyReference
|
|
return references.map(ref => {
|
|
const dep = map.get(ref);
|
|
return () => compilation.getDependencyReference(module, dep).module;
|
|
});
|
|
};
|
|
|
|
const enterModule = getModule => {
|
|
const module = getModule();
|
|
if (!module) return;
|
|
if (set.has(module)) return;
|
|
set.add(module);
|
|
if (modulesSet.has(module)) {
|
|
const imports = getConcatenatedImports(module);
|
|
imports.forEach(enterModule);
|
|
list.push({
|
|
type: "concatenated",
|
|
module
|
|
});
|
|
} else {
|
|
list.push({
|
|
type: "external",
|
|
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();
|
|
}
|
|
});
|
|
}
|
|
};
|
|
|
|
enterModule(() => rootModule);
|
|
|
|
return list;
|
|
}
|
|
|
|
_createIdentifier() {
|
|
let orderedConcatenationListIdentifiers = "";
|
|
for (let i = 0; i < this._orderedConcatenationList.length; i++) {
|
|
if (this._orderedConcatenationList[i].type === "concatenated") {
|
|
orderedConcatenationListIdentifiers += this._orderedConcatenationList[
|
|
i
|
|
].module.identifier();
|
|
orderedConcatenationListIdentifiers += " ";
|
|
}
|
|
}
|
|
const hash = createHash("md4");
|
|
hash.update(orderedConcatenationListIdentifiers);
|
|
return this.rootModule.identifier() + " " + hash.digest("hex");
|
|
}
|
|
|
|
source(dependencyTemplates, runtimeTemplate) {
|
|
const requestShortener = runtimeTemplate.requestShortener;
|
|
// Metainfo for each module
|
|
const modulesWithInfo = this._orderedConcatenationList.map((info, idx) => {
|
|
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);
|
|
}
|
|
} else if (dep instanceof HarmonyExportExpressionDependency) {
|
|
if (!exportMap.has("default")) {
|
|
exportMap.set("default", "__WEBPACK_MODULE_DEFAULT_EXPORT__");
|
|
}
|
|
} else if (
|
|
dep instanceof HarmonyExportImportedSpecifierDependency
|
|
) {
|
|
const exportName = dep.name;
|
|
const importName = dep._id;
|
|
const importedModule = dep._module;
|
|
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") {
|
|
continue;
|
|
}
|
|
if (!reexportMap.has(name)) {
|
|
reexportMap.set(name, {
|
|
module: importedModule,
|
|
exportName: name,
|
|
dependency: dep
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return {
|
|
type: "concatenated",
|
|
module: info.module,
|
|
index: idx,
|
|
ast: undefined,
|
|
internalSource: undefined,
|
|
source: undefined,
|
|
globalScope: undefined,
|
|
moduleScope: undefined,
|
|
internalNames: new Map(),
|
|
globalExports: new Set(),
|
|
exportMap: exportMap,
|
|
reexportMap: reexportMap,
|
|
hasNamespaceObject: false,
|
|
namespaceObjectSource: null
|
|
};
|
|
}
|
|
case "external":
|
|
return {
|
|
type: "external",
|
|
module: info.module,
|
|
index: idx,
|
|
name: undefined,
|
|
interopNamespaceObjectUsed: false,
|
|
interopNamespaceObjectName: undefined,
|
|
interopDefaultAccessUsed: false,
|
|
interopDefaultAccessName: undefined
|
|
};
|
|
default:
|
|
throw new Error(`Unsupported concatenation entry type ${info.type}`);
|
|
}
|
|
});
|
|
|
|
// Create mapping from module to info
|
|
const moduleToInfoMap = new Map();
|
|
for (const m of modulesWithInfo) {
|
|
moduleToInfoMap.set(m.module, m);
|
|
}
|
|
|
|
// Configure template decorators for dependencies
|
|
const innerDependencyTemplates = new Map(dependencyTemplates);
|
|
|
|
innerDependencyTemplates.set(
|
|
HarmonyImportSpecifierDependency,
|
|
new HarmonyImportSpecifierDependencyConcatenatedTemplate(
|
|
dependencyTemplates.get(HarmonyImportSpecifierDependency),
|
|
moduleToInfoMap
|
|
)
|
|
);
|
|
innerDependencyTemplates.set(
|
|
HarmonyImportSideEffectDependency,
|
|
new HarmonyImportSideEffectDependencyConcatenatedTemplate(
|
|
dependencyTemplates.get(HarmonyImportSideEffectDependency),
|
|
moduleToInfoMap
|
|
)
|
|
);
|
|
innerDependencyTemplates.set(
|
|
HarmonyExportSpecifierDependency,
|
|
new NullTemplate()
|
|
);
|
|
innerDependencyTemplates.set(
|
|
HarmonyExportExpressionDependency,
|
|
new HarmonyExportExpressionDependencyConcatenatedTemplate(
|
|
dependencyTemplates.get(HarmonyExportExpressionDependency),
|
|
this.rootModule
|
|
)
|
|
);
|
|
innerDependencyTemplates.set(
|
|
HarmonyExportImportedSpecifierDependency,
|
|
new NullTemplate()
|
|
);
|
|
innerDependencyTemplates.set(
|
|
HarmonyCompatibilityDependency,
|
|
new NullTemplate()
|
|
);
|
|
|
|
// Must use full identifier in our cache here to ensure that the source
|
|
// is updated should our dependencies list change.
|
|
// TODO webpack 5 refactor
|
|
innerDependencyTemplates.set(
|
|
"hash",
|
|
innerDependencyTemplates.get("hash") + this.identifier()
|
|
);
|
|
|
|
// Generate source code and analyse scopes
|
|
// Prepare a ReplaceSource for the final source
|
|
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, {
|
|
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.ast = ast;
|
|
info.internalSource = source;
|
|
info.source = resultSource;
|
|
info.globalScope = globalScope;
|
|
info.moduleScope = moduleScope;
|
|
}
|
|
}
|
|
|
|
// List of all used names to avoid conflicts
|
|
const allUsedNames = new Set([
|
|
"__WEBPACK_MODULE_DEFAULT_EXPORT__", // avoid using this internal name
|
|
|
|
"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"
|
|
]);
|
|
|
|
// Set of already checked scopes
|
|
const alreadyCheckedScopes = new Set();
|
|
|
|
// get all global names
|
|
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
|
|
if (info.globalScope) {
|
|
for (const reference of info.globalScope.through) {
|
|
const name = reference.identifier.name;
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// generate names for symbols
|
|
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
|
|
);
|
|
}
|
|
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);
|
|
}
|
|
}
|
|
} else {
|
|
allUsedNames.add(name);
|
|
info.internalNames.set(name, name);
|
|
}
|
|
}
|
|
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;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Find and replace referenced to modules
|
|
for (const info of modulesWithInfo) {
|
|
if (info.type === "concatenated") {
|
|
for (const reference of info.globalScope.through) {
|
|
const name = reference.identifier.name;
|
|
const match = /^__WEBPACK_MODULE_REFERENCE__(\d+)_([\da-f]+|ns)(_call)?(_strict)?__$/.exec(
|
|
name
|
|
);
|
|
if (match) {
|
|
const referencedModule = modulesWithInfo[+match[1]];
|
|
let exportName;
|
|
if (match[2] === "ns") {
|
|
exportName = true;
|
|
} else {
|
|
const exportData = match[2];
|
|
exportName = Buffer.from(exportData, "hex").toString("utf-8");
|
|
}
|
|
const asCall = !!match[3];
|
|
const strictHarmonyModule = !!match[4];
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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();
|
|
|
|
for (const dep of this.rootModule.dependencies) {
|
|
if (dep instanceof HarmonyExportSpecifierDependency) {
|
|
const used = this.rootModule.isUsed(dep.name);
|
|
if (used) {
|
|
const info = moduleToInfoMap.get(this.rootModule);
|
|
if (!exportsMap.has(used)) {
|
|
exportsMap.set(
|
|
used,
|
|
() => `/* binding */ ${info.internalNames.get(dep.id)}`
|
|
);
|
|
}
|
|
} else {
|
|
unusedExports.add(dep.name || "namespace");
|
|
}
|
|
} else if (dep instanceof HarmonyExportImportedSpecifierDependency) {
|
|
const exportDefs = getHarmonyExportImportedSpecifierDependencyExports(
|
|
dep
|
|
);
|
|
for (const def of exportDefs) {
|
|
const info = moduleToInfoMap.get(def.module);
|
|
const used = dep.originModule.isUsed(def.name);
|
|
if (used) {
|
|
if (!exportsMap.has(used)) {
|
|
exportsMap.set(used, requestShortener => {
|
|
const finalName = getFinalName(
|
|
info,
|
|
def.id,
|
|
moduleToInfoMap,
|
|
requestShortener,
|
|
false,
|
|
this.rootModule.buildMeta.strictHarmonyModule
|
|
);
|
|
return `/* reexport */ ${finalName}`;
|
|
});
|
|
}
|
|
} else {
|
|
unusedExports.add(def.name);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
const result = new ConcatSource();
|
|
|
|
// add harmony compatibility flag (must be first because of possible circular dependencies)
|
|
const usedExports = this.rootModule.usedExports;
|
|
if (usedExports === true || usedExports === null) {
|
|
result.add(`// ESM COMPAT FLAG\n`);
|
|
result.add(
|
|
runtimeTemplate.defineEsModuleFlagStatement({
|
|
exportsArgument: this.exportsArgument
|
|
})
|
|
);
|
|
}
|
|
|
|
// define exports
|
|
if (exportsMap.size > 0) {
|
|
result.add(`\n// EXPORTS\n`);
|
|
for (const [key, value] of exportsMap) {
|
|
result.add(
|
|
`__webpack_require__.d(${this.exportsArgument}, ${JSON.stringify(
|
|
key
|
|
)}, function() { return ${value(requestShortener)}; });\n`
|
|
);
|
|
}
|
|
}
|
|
|
|
// list unused exports
|
|
if (unusedExports.size > 0) {
|
|
result.add(
|
|
`\n// UNUSED EXPORTS: ${joinIterableWithComma(unusedExports)}\n`
|
|
);
|
|
}
|
|
|
|
// define required namespace objects (must be before evaluation modules)
|
|
for (const info of modulesWithInfo) {
|
|
if (info.namespaceObjectSource) {
|
|
result.add(
|
|
`\n// NAMESPACE OBJECT: ${info.module.readableIdentifier(
|
|
requestShortener
|
|
)}\n`
|
|
);
|
|
result.add(info.namespaceObjectSource);
|
|
}
|
|
}
|
|
|
|
// evaluate modules in order
|
|
for (const info of modulesWithInfo) {
|
|
switch (info.type) {
|
|
case "concatenated":
|
|
result.add(
|
|
`\n// CONCATENATED MODULE: ${info.module.readableIdentifier(
|
|
requestShortener
|
|
)}\n`
|
|
);
|
|
result.add(info.source);
|
|
break;
|
|
case "external":
|
|
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(
|
|
`var ${info.interopNamespaceObjectName} = /*#__PURE__*/__webpack_require__.t(${info.name}, 2);\n`
|
|
);
|
|
} else if (!info.module.buildMeta.exportsType) {
|
|
result.add(
|
|
`var ${info.interopNamespaceObjectName} = /*#__PURE__*/__webpack_require__.t(${info.name});\n`
|
|
);
|
|
}
|
|
}
|
|
if (info.interopDefaultAccessUsed) {
|
|
result.add(
|
|
`var ${info.interopDefaultAccessName} = /*#__PURE__*/__webpack_require__.n(${info.name});\n`
|
|
);
|
|
}
|
|
break;
|
|
default:
|
|
throw new Error(`Unsupported concatenation entry type ${info.type}`);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
findNewName(oldName, usedNamed1, usedNamed2, extraInfo) {
|
|
let name = oldName;
|
|
|
|
if (name === "__WEBPACK_MODULE_DEFAULT_EXPORT__") name = "";
|
|
|
|
// Remove uncool stuff
|
|
extraInfo = extraInfo.replace(
|
|
/\.+\/|(\/index)?\.([a-zA-Z0-9]{1,4})($|\s|\?)|\s*\+\s*\d+\s*modules/g,
|
|
""
|
|
);
|
|
|
|
const splittedInfo = extraInfo.split("/");
|
|
while (splittedInfo.length) {
|
|
name = splittedInfo.pop() + (name ? "_" + name : "");
|
|
const nameIdent = Template.toIdentifier(name);
|
|
if (
|
|
!usedNamed1.has(nameIdent) &&
|
|
(!usedNamed2 || !usedNamed2.has(nameIdent))
|
|
)
|
|
return nameIdent;
|
|
}
|
|
|
|
let i = 0;
|
|
let nameWithNumber = Template.toIdentifier(`${name}_${i}`);
|
|
while (
|
|
usedNamed1.has(nameWithNumber) ||
|
|
(usedNamed2 && usedNamed2.has(nameWithNumber))
|
|
) {
|
|
i++;
|
|
nameWithNumber = Template.toIdentifier(`${name}_${i}`);
|
|
}
|
|
return nameWithNumber;
|
|
}
|
|
|
|
/**
|
|
* @param {Hash} hash the hash used to track dependencies
|
|
* @returns {void}
|
|
*/
|
|
updateHash(hash) {
|
|
for (const info of this._orderedConcatenationList) {
|
|
switch (info.type) {
|
|
case "concatenated":
|
|
info.module.updateHash(hash);
|
|
break;
|
|
case "external":
|
|
hash.update(`${info.module.id}`);
|
|
break;
|
|
}
|
|
}
|
|
super.updateHash(hash);
|
|
}
|
|
}
|
|
|
|
class HarmonyImportSpecifierDependencyConcatenatedTemplate {
|
|
constructor(originalTemplate, modulesMap) {
|
|
this.originalTemplate = originalTemplate;
|
|
this.modulesMap = modulesMap;
|
|
}
|
|
|
|
getHarmonyInitOrder(dep) {
|
|
const module = dep._module;
|
|
const info = this.modulesMap.get(module);
|
|
if (!info) {
|
|
return this.originalTemplate.getHarmonyInitOrder(dep);
|
|
}
|
|
return NaN;
|
|
}
|
|
|
|
harmonyInit(dep, source, runtimeTemplate, dependencyTemplates) {
|
|
const module = dep._module;
|
|
const info = this.modulesMap.get(module);
|
|
if (!info) {
|
|
this.originalTemplate.harmonyInit(
|
|
dep,
|
|
source,
|
|
runtimeTemplate,
|
|
dependencyTemplates
|
|
);
|
|
return;
|
|
}
|
|
}
|
|
|
|
apply(dep, source, runtime, dependencyTemplates) {
|
|
const module = dep._module;
|
|
const info = this.modulesMap.get(module);
|
|
if (!info) {
|
|
this.originalTemplate.apply(dep, source, runtime, dependencyTemplates);
|
|
return;
|
|
}
|
|
let content;
|
|
const callFlag = dep.call ? "_call" : "";
|
|
const strictFlag = dep.originModule.buildMeta.strictHarmonyModule
|
|
? "_strict"
|
|
: "";
|
|
if (dep._id === null) {
|
|
content = `__WEBPACK_MODULE_REFERENCE__${info.index}_ns${strictFlag}__`;
|
|
} else if (dep.namespaceObjectAsContext) {
|
|
content = `__WEBPACK_MODULE_REFERENCE__${
|
|
info.index
|
|
}_ns${strictFlag}__[${JSON.stringify(dep._id)}]`;
|
|
} else {
|
|
const exportData = Buffer.from(dep._id, "utf-8").toString("hex");
|
|
content = `__WEBPACK_MODULE_REFERENCE__${info.index}_${exportData}${callFlag}${strictFlag}__`;
|
|
}
|
|
if (dep.shorthand) {
|
|
content = dep.name + ": " + content;
|
|
}
|
|
source.replace(dep.range[0], dep.range[1] - 1, content);
|
|
}
|
|
}
|
|
|
|
class HarmonyImportSideEffectDependencyConcatenatedTemplate {
|
|
constructor(originalTemplate, modulesMap) {
|
|
this.originalTemplate = originalTemplate;
|
|
this.modulesMap = modulesMap;
|
|
}
|
|
|
|
getHarmonyInitOrder(dep) {
|
|
const module = dep._module;
|
|
const info = this.modulesMap.get(module);
|
|
if (!info) {
|
|
return this.originalTemplate.getHarmonyInitOrder(dep);
|
|
}
|
|
return NaN;
|
|
}
|
|
|
|
harmonyInit(dep, source, runtime, dependencyTemplates) {
|
|
const module = dep._module;
|
|
const info = this.modulesMap.get(module);
|
|
if (!info) {
|
|
this.originalTemplate.harmonyInit(
|
|
dep,
|
|
source,
|
|
runtime,
|
|
dependencyTemplates
|
|
);
|
|
return;
|
|
}
|
|
}
|
|
|
|
apply(dep, source, runtime, dependencyTemplates) {
|
|
const module = dep._module;
|
|
const info = this.modulesMap.get(module);
|
|
if (!info) {
|
|
this.originalTemplate.apply(dep, source, runtime, dependencyTemplates);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
class HarmonyExportExpressionDependencyConcatenatedTemplate {
|
|
constructor(originalTemplate, rootModule) {
|
|
this.originalTemplate = originalTemplate;
|
|
this.rootModule = rootModule;
|
|
}
|
|
|
|
apply(dep, source, runtime, dependencyTemplates) {
|
|
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;
|
|
if (used) content += `${exportsName}[${JSON.stringify(used)}] = `;
|
|
}
|
|
|
|
if (dep.range) {
|
|
source.replace(
|
|
dep.rangeStatement[0],
|
|
dep.range[0] - 1,
|
|
content + "(" + dep.prefix
|
|
);
|
|
source.replace(dep.range[1], dep.rangeStatement[1] - 1, ");");
|
|
return;
|
|
}
|
|
|
|
source.replace(
|
|
dep.rangeStatement[0],
|
|
dep.rangeStatement[1] - 1,
|
|
content + dep.prefix
|
|
);
|
|
}
|
|
}
|
|
|
|
class NullTemplate {
|
|
apply() {}
|
|
}
|
|
|
|
module.exports = ConcatenatedModule;
|