mirror of https://github.com/webpack/webpack.git
fix: should avoid through variables in inlined module
This commit is contained in:
parent
65bce6826b
commit
bb10e4b5f0
|
@ -5,14 +5,16 @@
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
const {
|
||||||
|
DEFAULT_EXPORT,
|
||||||
|
NAMESPACE_OBJECT_EXPORT
|
||||||
|
} = require("./util/concatenate");
|
||||||
|
|
||||||
/** @typedef {import("./Module")} Module */
|
/** @typedef {import("./Module")} Module */
|
||||||
|
|
||||||
const MODULE_REFERENCE_REGEXP =
|
const MODULE_REFERENCE_REGEXP =
|
||||||
/^__WEBPACK_MODULE_REFERENCE__(\d+)_([\da-f]+|ns)(_call)?(_directImport)?(?:_asiSafe(\d))?__$/;
|
/^__WEBPACK_MODULE_REFERENCE__(\d+)_([\da-f]+|ns)(_call)?(_directImport)?(?:_asiSafe(\d))?__$/;
|
||||||
|
|
||||||
const DEFAULT_EXPORT = "__WEBPACK_DEFAULT_EXPORT__";
|
|
||||||
const NAMESPACE_OBJECT_EXPORT = "__WEBPACK_NAMESPACE_OBJECT__";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {object} ExternalModuleInfo
|
* @typedef {object} ExternalModuleInfo
|
||||||
* @property {number} index
|
* @property {number} index
|
||||||
|
|
|
@ -31,13 +31,21 @@ const Template = require("../Template");
|
||||||
const { last, someInIterable } = require("../util/IterableHelpers");
|
const { last, someInIterable } = require("../util/IterableHelpers");
|
||||||
const StringXor = require("../util/StringXor");
|
const StringXor = require("../util/StringXor");
|
||||||
const { compareModulesByIdentifier } = require("../util/comparators");
|
const { compareModulesByIdentifier } = require("../util/comparators");
|
||||||
|
const {
|
||||||
|
getPathInAst,
|
||||||
|
getAllReferences,
|
||||||
|
RESERVED_NAMES,
|
||||||
|
findNewName,
|
||||||
|
addScopeSymbols,
|
||||||
|
getUsedNamesInScopeInfo
|
||||||
|
} = require("../util/concatenate");
|
||||||
const createHash = require("../util/createHash");
|
const createHash = require("../util/createHash");
|
||||||
const { getPathInAst, getAllReferences } = require("../util/mergeScope");
|
|
||||||
const nonNumericOnlyHash = require("../util/nonNumericOnlyHash");
|
const nonNumericOnlyHash = require("../util/nonNumericOnlyHash");
|
||||||
const { intersectRuntime } = require("../util/runtime");
|
const { intersectRuntime } = require("../util/runtime");
|
||||||
const JavascriptGenerator = require("./JavascriptGenerator");
|
const JavascriptGenerator = require("./JavascriptGenerator");
|
||||||
const JavascriptParser = require("./JavascriptParser");
|
const JavascriptParser = require("./JavascriptParser");
|
||||||
|
|
||||||
|
/** @typedef {import("eslint-scope").Reference} Reference */
|
||||||
/** @typedef {import("eslint-scope").Scope} Scope */
|
/** @typedef {import("eslint-scope").Scope} Scope */
|
||||||
/** @typedef {import("eslint-scope").Variable} Variable */
|
/** @typedef {import("eslint-scope").Variable} Variable */
|
||||||
/** @typedef {import("webpack-sources").Source} Source */
|
/** @typedef {import("webpack-sources").Source} Source */
|
||||||
|
@ -1458,8 +1466,7 @@ class JavascriptModulesPlugin {
|
||||||
const renamedInlinedModules = new Map();
|
const renamedInlinedModules = new Map();
|
||||||
const { runtimeTemplate } = renderContext;
|
const { runtimeTemplate } = renderContext;
|
||||||
|
|
||||||
/** @typedef {{ source: Source, ast: any, variables: Set<Variable>, usedInNonInlined: Set<Variable>}} InlinedModulesInfo */
|
/** @type {Map<Module, { source: Source, module: Module, ast: any, variables: Set<Variable>, through: Set<Reference>, usedInNonInlined: Set<Variable>, moduleScope: Scope }>} */
|
||||||
/** @type {Map<Module, InlinedModulesInfo>} */
|
|
||||||
const inlinedModulesToInfo = new Map();
|
const inlinedModulesToInfo = new Map();
|
||||||
/** @type {Set<string>} */
|
/** @type {Set<string>} */
|
||||||
const nonInlinedModuleThroughIdentifiers = new Set();
|
const nonInlinedModuleThroughIdentifiers = new Set();
|
||||||
|
@ -1487,14 +1494,17 @@ class JavascriptModulesPlugin {
|
||||||
ignoreEval: true
|
ignoreEval: true
|
||||||
});
|
});
|
||||||
|
|
||||||
const globalScope = /** @type {Scope} */ (scopeManager.acquire(ast));
|
const globalScope = scopeManager.acquire(ast);
|
||||||
if (inlinedModules && inlinedModules.has(m)) {
|
if (inlinedModules && inlinedModules.has(m)) {
|
||||||
const moduleScope = globalScope.childScopes[0];
|
const moduleScope = globalScope.childScopes[0];
|
||||||
inlinedModulesToInfo.set(m, {
|
inlinedModulesToInfo.set(m, {
|
||||||
source: moduleSource,
|
source: moduleSource,
|
||||||
ast,
|
ast,
|
||||||
|
module: m,
|
||||||
variables: new Set(moduleScope.variables),
|
variables: new Set(moduleScope.variables),
|
||||||
usedInNonInlined: new Set()
|
through: new Set(moduleScope.through),
|
||||||
|
usedInNonInlined: new Set(),
|
||||||
|
moduleScope
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
for (const ref of globalScope.through) {
|
for (const ref of globalScope.through) {
|
||||||
|
@ -1505,7 +1515,10 @@ class JavascriptModulesPlugin {
|
||||||
|
|
||||||
for (const [, { variables, usedInNonInlined }] of inlinedModulesToInfo) {
|
for (const [, { variables, usedInNonInlined }] of inlinedModulesToInfo) {
|
||||||
for (const variable of variables) {
|
for (const variable of variables) {
|
||||||
if (nonInlinedModuleThroughIdentifiers.has(variable.name)) {
|
if (
|
||||||
|
nonInlinedModuleThroughIdentifiers.has(variable.name) ||
|
||||||
|
RESERVED_NAMES.has(variable.name)
|
||||||
|
) {
|
||||||
usedInNonInlined.add(variable);
|
usedInNonInlined.add(variable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1519,39 +1532,70 @@ class JavascriptModulesPlugin {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const usedNames = new Set(
|
const info = inlinedModulesToInfo.get(m);
|
||||||
Array.from(
|
const allUsedNames = new Set(
|
||||||
/** @type {InlinedModulesInfo} */
|
Array.from(info.through, v => v.identifier.name)
|
||||||
(inlinedModulesToInfo.get(m)).variables
|
|
||||||
).map(v => v.name)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
for (const variable of usedInNonInlined) {
|
for (const variable of usedInNonInlined) {
|
||||||
|
allUsedNames.add(variable.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const variable of info.variables) {
|
||||||
|
allUsedNames.add(variable.name);
|
||||||
const references = getAllReferences(variable);
|
const references = getAllReferences(variable);
|
||||||
const allIdentifiers = new Set(
|
const allIdentifiers = new Set(
|
||||||
references.map(r => r.identifier).concat(variable.identifiers)
|
references.map(r => r.identifier).concat(variable.identifiers)
|
||||||
);
|
);
|
||||||
|
|
||||||
const newName = this.findNewName(
|
const usedNamesInScopeInfo = new Map();
|
||||||
variable.name,
|
const ignoredScopes = new Set();
|
||||||
usedNames,
|
|
||||||
m.readableIdentifier(runtimeTemplate.requestShortener)
|
const name = variable.name;
|
||||||
|
const { usedNames, alreadyCheckedScopes } = getUsedNamesInScopeInfo(
|
||||||
|
usedNamesInScopeInfo,
|
||||||
|
info.module.identifier(),
|
||||||
|
name
|
||||||
);
|
);
|
||||||
usedNames.add(newName);
|
|
||||||
for (const identifier of allIdentifiers) {
|
if (allUsedNames.has(name) || usedNames.has(name)) {
|
||||||
const r = /** @type {Range} */ (identifier.range);
|
const references = getAllReferences(variable);
|
||||||
const path = getPathInAst(ast, identifier);
|
for (const ref of references) {
|
||||||
if (path && path.length > 1) {
|
addScopeSymbols(
|
||||||
const maybeProperty =
|
ref.from,
|
||||||
path[1].type === "AssignmentPattern" && path[1].left === path[0]
|
usedNames,
|
||||||
? path[2]
|
alreadyCheckedScopes,
|
||||||
: path[1];
|
ignoredScopes
|
||||||
if (maybeProperty.type === "Property" && maybeProperty.shorthand) {
|
);
|
||||||
source.insert(r[1], `: ${newName}`);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
source.replace(r[0], r[1] - 1, newName);
|
|
||||||
|
const newName = findNewName(
|
||||||
|
variable.name,
|
||||||
|
allUsedNames,
|
||||||
|
usedNames,
|
||||||
|
m.readableIdentifier(runtimeTemplate.requestShortener)
|
||||||
|
);
|
||||||
|
allUsedNames.add(newName);
|
||||||
|
for (const identifier of allIdentifiers) {
|
||||||
|
const r = identifier.range;
|
||||||
|
const path = getPathInAst(ast, identifier);
|
||||||
|
if (path && path.length > 1) {
|
||||||
|
const maybeProperty =
|
||||||
|
path[1].type === "AssignmentPattern" && path[1].left === path[0]
|
||||||
|
? path[2]
|
||||||
|
: path[1];
|
||||||
|
if (
|
||||||
|
maybeProperty.type === "Property" &&
|
||||||
|
maybeProperty.shorthand
|
||||||
|
) {
|
||||||
|
source.insert(r[1], `: ${newName}`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
source.replace(r[0], r[1] - 1, newName);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
allUsedNames.add(name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1560,38 +1604,6 @@ class JavascriptModulesPlugin {
|
||||||
|
|
||||||
return renamedInlinedModules;
|
return renamedInlinedModules;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string} oldName oldName
|
|
||||||
* @param {Set<string>} usedName usedName
|
|
||||||
* @param {string} extraInfo extraInfo
|
|
||||||
* @returns {string} extraInfo
|
|
||||||
*/
|
|
||||||
findNewName(oldName, usedName, extraInfo) {
|
|
||||||
let name = oldName;
|
|
||||||
|
|
||||||
// Remove uncool stuff
|
|
||||||
extraInfo = extraInfo.replace(
|
|
||||||
/\.+\/|(\/index)?\.([a-zA-Z0-9]{1,4})($|\s|\?)|\s*\+\s*\d+\s*modules/g,
|
|
||||||
""
|
|
||||||
);
|
|
||||||
const splittedInfo = extraInfo.split("/");
|
|
||||||
while (splittedInfo.length) {
|
|
||||||
name = splittedInfo.pop() + (name ? `_${name}` : "");
|
|
||||||
const nameIdent = Template.toIdentifier(name);
|
|
||||||
if (!usedName.has(nameIdent)) {
|
|
||||||
return nameIdent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let i = 0;
|
|
||||||
let nameWithNumber = Template.toIdentifier(`${name}_${i}`);
|
|
||||||
while (usedName.has(nameWithNumber)) {
|
|
||||||
i++;
|
|
||||||
nameWithNumber = Template.toIdentifier(`${name}_${i}`);
|
|
||||||
}
|
|
||||||
return nameWithNumber;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = JavascriptModulesPlugin;
|
module.exports = JavascriptModulesPlugin;
|
||||||
|
|
|
@ -24,10 +24,17 @@ const JavascriptParser = require("../javascript/JavascriptParser");
|
||||||
const { equals } = require("../util/ArrayHelpers");
|
const { equals } = require("../util/ArrayHelpers");
|
||||||
const LazySet = require("../util/LazySet");
|
const LazySet = require("../util/LazySet");
|
||||||
const { concatComparators } = require("../util/comparators");
|
const { concatComparators } = require("../util/comparators");
|
||||||
|
const {
|
||||||
|
RESERVED_NAMES,
|
||||||
|
findNewName,
|
||||||
|
addScopeSymbols,
|
||||||
|
getAllReferences,
|
||||||
|
getPathInAst,
|
||||||
|
getUsedNamesInScopeInfo
|
||||||
|
} = require("../util/concatenate");
|
||||||
const createHash = require("../util/createHash");
|
const createHash = require("../util/createHash");
|
||||||
const { makePathsRelative } = require("../util/identifier");
|
const { makePathsRelative } = require("../util/identifier");
|
||||||
const makeSerializable = require("../util/makeSerializable");
|
const makeSerializable = require("../util/makeSerializable");
|
||||||
const { getAllReferences, getPathInAst } = require("../util/mergeScope");
|
|
||||||
const propertyAccess = require("../util/propertyAccess");
|
const propertyAccess = require("../util/propertyAccess");
|
||||||
const { propertyName } = require("../util/propertyName");
|
const { propertyName } = require("../util/propertyName");
|
||||||
const {
|
const {
|
||||||
|
@ -74,6 +81,7 @@ const {
|
||||||
/** @typedef {import("../serialization/ObjectMiddleware").ObjectDeserializerContext} ObjectDeserializerContext */
|
/** @typedef {import("../serialization/ObjectMiddleware").ObjectDeserializerContext} ObjectDeserializerContext */
|
||||||
/** @typedef {import("../util/Hash")} Hash */
|
/** @typedef {import("../util/Hash")} Hash */
|
||||||
/** @typedef {typeof import("../util/Hash")} HashConstructor */
|
/** @typedef {typeof import("../util/Hash")} HashConstructor */
|
||||||
|
/** @typedef {import("../util/concatenate").UsedNames} UsedNames */
|
||||||
/** @typedef {import("../util/fs").InputFileSystem} InputFileSystem */
|
/** @typedef {import("../util/fs").InputFileSystem} InputFileSystem */
|
||||||
/** @typedef {import("../util/runtime").RuntimeSpec} RuntimeSpec */
|
/** @typedef {import("../util/runtime").RuntimeSpec} RuntimeSpec */
|
||||||
|
|
||||||
|
@ -171,52 +179,13 @@ if (!ReferencerClass.prototype.PropertyDefinition) {
|
||||||
* @property {ConcatenatedModuleInfo | ExternalModuleInfo} target
|
* @property {ConcatenatedModuleInfo | ExternalModuleInfo} target
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** @typedef {Set<string>} UsedNames */
|
|
||||||
|
|
||||||
const RESERVED_NAMES = new Set(
|
|
||||||
[
|
|
||||||
// internal names (should always be renamed)
|
|
||||||
ConcatenationScope.DEFAULT_EXPORT,
|
|
||||||
ConcatenationScope.NAMESPACE_OBJECT_EXPORT,
|
|
||||||
|
|
||||||
// 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/amd
|
|
||||||
"module,__dirname,__filename,exports,require,define",
|
|
||||||
|
|
||||||
// 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(",")
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @template T
|
* @template T
|
||||||
* @param {string} property property
|
* @param {string} property property
|
||||||
* @param {function(T[keyof T], T[keyof T]): 0 | 1 | -1} comparator comparator
|
* @param {function(T[keyof T], T[keyof T]): 0 | 1 | -1} comparator comparator
|
||||||
* @returns {Comparator<T>} comparator
|
* @returns {Comparator<T>} comparator
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const createComparator = (property, comparator) => (a, b) =>
|
const createComparator = (property, comparator) => (a, b) =>
|
||||||
comparator(
|
comparator(
|
||||||
a[/** @type {keyof T} */ (property)],
|
a[/** @type {keyof T} */ (property)],
|
||||||
|
@ -628,25 +597,6 @@ const getFinalName = (
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {Scope | null} s scope
|
|
||||||
* @param {UsedNames} nameSet name set
|
|
||||||
* @param {TODO} scopeSet1 scope set 1
|
|
||||||
* @param {TODO} scopeSet2 scope set 2
|
|
||||||
*/
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
scope = scope.upper;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const TYPES = new Set(["javascript"]);
|
const TYPES = new Set(["javascript"]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1193,18 +1143,6 @@ class ConcatenatedModule extends Module {
|
||||||
* @param {string} id export id
|
* @param {string} id export id
|
||||||
* @returns {{ usedNames: UsedNames, alreadyCheckedScopes: Set<TODO> }} info
|
* @returns {{ usedNames: UsedNames, 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
|
// Set of already checked scopes
|
||||||
const ignoredScopes = new Set();
|
const ignoredScopes = new Set();
|
||||||
|
@ -1274,6 +1212,7 @@ class ConcatenatedModule extends Module {
|
||||||
if (!binding.ids) continue;
|
if (!binding.ids) continue;
|
||||||
const { usedNames, alreadyCheckedScopes } =
|
const { usedNames, alreadyCheckedScopes } =
|
||||||
getUsedNamesInScopeInfo(
|
getUsedNamesInScopeInfo(
|
||||||
|
usedNamesInScopeInfo,
|
||||||
binding.info.module.identifier(),
|
binding.info.module.identifier(),
|
||||||
"name" in binding ? binding.name : ""
|
"name" in binding ? binding.name : ""
|
||||||
);
|
);
|
||||||
|
@ -1306,6 +1245,7 @@ class ConcatenatedModule extends Module {
|
||||||
// generate names for symbols
|
// generate names for symbols
|
||||||
for (const info of moduleToInfoMap.values()) {
|
for (const info of moduleToInfoMap.values()) {
|
||||||
const { usedNames: namespaceObjectUsedNames } = getUsedNamesInScopeInfo(
|
const { usedNames: namespaceObjectUsedNames } = getUsedNamesInScopeInfo(
|
||||||
|
usedNamesInScopeInfo,
|
||||||
info.module.identifier(),
|
info.module.identifier(),
|
||||||
""
|
""
|
||||||
);
|
);
|
||||||
|
@ -1314,6 +1254,7 @@ class ConcatenatedModule extends Module {
|
||||||
for (const variable of info.moduleScope.variables) {
|
for (const variable of info.moduleScope.variables) {
|
||||||
const name = variable.name;
|
const name = variable.name;
|
||||||
const { usedNames, alreadyCheckedScopes } = getUsedNamesInScopeInfo(
|
const { usedNames, alreadyCheckedScopes } = getUsedNamesInScopeInfo(
|
||||||
|
usedNamesInScopeInfo,
|
||||||
info.module.identifier(),
|
info.module.identifier(),
|
||||||
name
|
name
|
||||||
);
|
);
|
||||||
|
@ -1327,7 +1268,7 @@ class ConcatenatedModule extends Module {
|
||||||
ignoredScopes
|
ignoredScopes
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
const newName = this.findNewName(
|
const newName = findNewName(
|
||||||
name,
|
name,
|
||||||
allUsedNames,
|
allUsedNames,
|
||||||
usedNames,
|
usedNames,
|
||||||
|
@ -1375,7 +1316,7 @@ class ConcatenatedModule extends Module {
|
||||||
info.namespaceExportSymbol
|
info.namespaceExportSymbol
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
namespaceObjectName = this.findNewName(
|
namespaceObjectName = findNewName(
|
||||||
"namespaceObject",
|
"namespaceObject",
|
||||||
allUsedNames,
|
allUsedNames,
|
||||||
namespaceObjectUsedNames,
|
namespaceObjectUsedNames,
|
||||||
|
@ -1390,7 +1331,7 @@ class ConcatenatedModule extends Module {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "external": {
|
case "external": {
|
||||||
const externalName = this.findNewName(
|
const externalName = findNewName(
|
||||||
"",
|
"",
|
||||||
allUsedNames,
|
allUsedNames,
|
||||||
namespaceObjectUsedNames,
|
namespaceObjectUsedNames,
|
||||||
|
@ -1404,7 +1345,7 @@ class ConcatenatedModule extends Module {
|
||||||
}
|
}
|
||||||
const buildMeta = /** @type {BuildMeta} */ (info.module.buildMeta);
|
const buildMeta = /** @type {BuildMeta} */ (info.module.buildMeta);
|
||||||
if (buildMeta.exportsType !== "namespace") {
|
if (buildMeta.exportsType !== "namespace") {
|
||||||
const externalNameInterop = this.findNewName(
|
const externalNameInterop = findNewName(
|
||||||
"namespaceObject",
|
"namespaceObject",
|
||||||
allUsedNames,
|
allUsedNames,
|
||||||
namespaceObjectUsedNames,
|
namespaceObjectUsedNames,
|
||||||
|
@ -1418,7 +1359,7 @@ class ConcatenatedModule extends Module {
|
||||||
buildMeta.exportsType === "default" &&
|
buildMeta.exportsType === "default" &&
|
||||||
buildMeta.defaultObject !== "redirect"
|
buildMeta.defaultObject !== "redirect"
|
||||||
) {
|
) {
|
||||||
const externalNameInterop = this.findNewName(
|
const externalNameInterop = findNewName(
|
||||||
"namespaceObject2",
|
"namespaceObject2",
|
||||||
allUsedNames,
|
allUsedNames,
|
||||||
namespaceObjectUsedNames,
|
namespaceObjectUsedNames,
|
||||||
|
@ -1429,7 +1370,7 @@ class ConcatenatedModule extends Module {
|
||||||
topLevelDeclarations.add(externalNameInterop);
|
topLevelDeclarations.add(externalNameInterop);
|
||||||
}
|
}
|
||||||
if (buildMeta.exportsType === "dynamic" || !buildMeta.exportsType) {
|
if (buildMeta.exportsType === "dynamic" || !buildMeta.exportsType) {
|
||||||
const externalNameInterop = this.findNewName(
|
const externalNameInterop = findNewName(
|
||||||
"default",
|
"default",
|
||||||
allUsedNames,
|
allUsedNames,
|
||||||
namespaceObjectUsedNames,
|
namespaceObjectUsedNames,
|
||||||
|
@ -1915,53 +1856,6 @@ ${defineGetters}`
|
||||||
return [list, map];
|
return [list, map];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string} oldName old name
|
|
||||||
* @param {UsedNames} usedNamed1 used named 1
|
|
||||||
* @param {UsedNames} usedNamed2 used named 2
|
|
||||||
* @param {string} extraInfo extra info
|
|
||||||
* @returns {string} found new name
|
|
||||||
*/
|
|
||||||
findNewName(oldName, usedNamed1, usedNamed2, extraInfo) {
|
|
||||||
let name = oldName;
|
|
||||||
|
|
||||||
if (name === ConcatenationScope.DEFAULT_EXPORT) {
|
|
||||||
name = "";
|
|
||||||
}
|
|
||||||
if (name === ConcatenationScope.NAMESPACE_OBJECT_EXPORT) {
|
|
||||||
name = "namespaceObject";
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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) ||
|
|
||||||
// eslint-disable-next-line no-unmodified-loop-condition
|
|
||||||
(usedNamed2 && usedNamed2.has(nameWithNumber))
|
|
||||||
) {
|
|
||||||
i++;
|
|
||||||
nameWithNumber = Template.toIdentifier(`${name}_${i}`);
|
|
||||||
}
|
|
||||||
return nameWithNumber;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Hash} hash the hash used to track dependencies
|
* @param {Hash} hash the hash used to track dependencies
|
||||||
* @param {UpdateHashContext} context context
|
* @param {UpdateHashContext} context context
|
||||||
|
|
|
@ -0,0 +1,227 @@
|
||||||
|
/*
|
||||||
|
MIT License http://www.opensource.org/licenses/mit-license.php
|
||||||
|
Author Tobias Koppers @sokra
|
||||||
|
*/
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const Template = require("../Template");
|
||||||
|
|
||||||
|
/** @typedef {import("eslint-scope").Scope} Scope */
|
||||||
|
/** @typedef {import("eslint-scope").Reference} Reference */
|
||||||
|
/** @typedef {import("eslint-scope").Variable} Variable */
|
||||||
|
/** @typedef {import("estree").Node} Node */
|
||||||
|
/** @typedef {import("../javascript/JavascriptParser").Range} Range */
|
||||||
|
/** @typedef {import("../javascript/JavascriptParser").Program} Program */
|
||||||
|
/** @typedef {Set<string>} UsedNames */
|
||||||
|
|
||||||
|
const DEFAULT_EXPORT = "__WEBPACK_DEFAULT_EXPORT__";
|
||||||
|
const NAMESPACE_OBJECT_EXPORT = "__WEBPACK_NAMESPACE_OBJECT__";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Variable} variable variable
|
||||||
|
* @returns {Reference[]} references
|
||||||
|
*/
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Node | Node[]} ast ast
|
||||||
|
* @param {Node} node node
|
||||||
|
* @returns {undefined | Node[]} result
|
||||||
|
*/
|
||||||
|
const getPathInAst = (ast, node) => {
|
||||||
|
if (ast === node) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const nr = /** @type {Range} */ (node.range);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Node} n node
|
||||||
|
* @returns {Node[] | undefined} result
|
||||||
|
*/
|
||||||
|
const enterNode = n => {
|
||||||
|
if (!n) return;
|
||||||
|
const r = n.range;
|
||||||
|
if (r && r[0] <= nr[0] && r[1] >= nr[1]) {
|
||||||
|
const path = getPathInAst(n, node);
|
||||||
|
if (path) {
|
||||||
|
path.push(n);
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (Array.isArray(ast)) {
|
||||||
|
for (let i = 0; i < ast.length; i++) {
|
||||||
|
const enterResult = enterNode(ast[i]);
|
||||||
|
if (enterResult !== undefined) return enterResult;
|
||||||
|
}
|
||||||
|
} else if (ast && typeof ast === "object") {
|
||||||
|
const keys =
|
||||||
|
/** @type {Array<keyof Node>} */
|
||||||
|
(Object.keys(ast));
|
||||||
|
for (let i = 0; i < keys.length; i++) {
|
||||||
|
// We are making the faster check in `enterNode` using `n.range`
|
||||||
|
const value =
|
||||||
|
ast[
|
||||||
|
/** @type {Exclude<keyof Node, "range" | "loc" | "leadingComments" | "trailingComments">} */
|
||||||
|
(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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} oldName old name
|
||||||
|
* @param {UsedNames} usedNamed1 used named 1
|
||||||
|
* @param {UsedNames} usedNamed2 used named 2
|
||||||
|
* @param {string} extraInfo extra info
|
||||||
|
* @returns {string} found new name
|
||||||
|
*/
|
||||||
|
function findNewName(oldName, usedNamed1, usedNamed2, extraInfo) {
|
||||||
|
let name = oldName;
|
||||||
|
|
||||||
|
if (name === DEFAULT_EXPORT) {
|
||||||
|
name = "";
|
||||||
|
}
|
||||||
|
if (name === NAMESPACE_OBJECT_EXPORT) {
|
||||||
|
name = "namespaceObject";
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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) ||
|
||||||
|
// eslint-disable-next-line no-unmodified-loop-condition
|
||||||
|
(usedNamed2 && usedNamed2.has(nameWithNumber))
|
||||||
|
) {
|
||||||
|
i++;
|
||||||
|
nameWithNumber = Template.toIdentifier(`${name}_${i}`);
|
||||||
|
}
|
||||||
|
return nameWithNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Scope | null} s scope
|
||||||
|
* @param {UsedNames} nameSet name set
|
||||||
|
* @param {TODO} scopeSet1 scope set 1
|
||||||
|
* @param {TODO} scopeSet2 scope set 2
|
||||||
|
*/
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
scope = scope.upper;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const RESERVED_NAMES = new Set(
|
||||||
|
[
|
||||||
|
// internal names (should always be renamed)
|
||||||
|
DEFAULT_EXPORT,
|
||||||
|
NAMESPACE_OBJECT_EXPORT,
|
||||||
|
|
||||||
|
// 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/amd
|
||||||
|
"module,__dirname,__filename,exports,require,define",
|
||||||
|
|
||||||
|
// js globals
|
||||||
|
"Array,Date,eval,function,hasOwnProperty,Infinity,isFinite,isNaN,isPrototypeOf,length,Math",
|
||||||
|
"NaN,name,Number,Object,prototype,String,Symbol,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(",")
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Map<string, { usedNames: UsedNames, alreadyCheckedScopes: Set<TODO> }>} usedNamesInScopeInfo used names in scope info
|
||||||
|
* @param {string} module module identifier
|
||||||
|
* @param {string} id export id
|
||||||
|
* @returns {{ usedNames: UsedNames, alreadyCheckedScopes: Set<TODO> }} info
|
||||||
|
*/
|
||||||
|
const getUsedNamesInScopeInfo = (usedNamesInScopeInfo, 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;
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
getUsedNamesInScopeInfo,
|
||||||
|
findNewName,
|
||||||
|
getAllReferences,
|
||||||
|
getPathInAst,
|
||||||
|
NAMESPACE_OBJECT_EXPORT,
|
||||||
|
DEFAULT_EXPORT,
|
||||||
|
RESERVED_NAMES,
|
||||||
|
addScopeSymbols
|
||||||
|
};
|
|
@ -1,88 +0,0 @@
|
||||||
/*
|
|
||||||
MIT License http://www.opensource.org/licenses/mit-license.php
|
|
||||||
Author Tobias Koppers @sokra
|
|
||||||
*/
|
|
||||||
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
/** @typedef {import("eslint-scope").Reference} Reference */
|
|
||||||
/** @typedef {import("eslint-scope").Variable} Variable */
|
|
||||||
/** @typedef {import("estree").Node} Node */
|
|
||||||
/** @typedef {import("estree").Program} Program */
|
|
||||||
/** @typedef {import("../javascript/JavascriptParser").Range} Range */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {Variable} variable variable
|
|
||||||
* @returns {Reference[]} references
|
|
||||||
*/
|
|
||||||
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;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {Node | Node[]} ast ast
|
|
||||||
* @param {Node} node node
|
|
||||||
* @returns {undefined | Node[]} result
|
|
||||||
*/
|
|
||||||
const getPathInAst = (ast, node) => {
|
|
||||||
if (ast === node) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
const nr = /** @type {Range} */ (node.range);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {Node} n node
|
|
||||||
* @returns {Node[] | undefined} result
|
|
||||||
*/
|
|
||||||
const enterNode = n => {
|
|
||||||
if (!n) return;
|
|
||||||
const r = n.range;
|
|
||||||
if (r && r[0] <= nr[0] && r[1] >= nr[1]) {
|
|
||||||
const path = getPathInAst(n, node);
|
|
||||||
if (path) {
|
|
||||||
path.push(n);
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (Array.isArray(ast)) {
|
|
||||||
for (let i = 0; i < ast.length; i++) {
|
|
||||||
const enterResult = enterNode(ast[i]);
|
|
||||||
if (enterResult !== undefined) return enterResult;
|
|
||||||
}
|
|
||||||
} else if (ast && typeof ast === "object") {
|
|
||||||
const keys =
|
|
||||||
/** @type {Array<keyof Node>} */
|
|
||||||
(Object.keys(ast));
|
|
||||||
for (let i = 0; i < keys.length; i++) {
|
|
||||||
// We are making the faster check in `enterNode` using `n.range`
|
|
||||||
const value =
|
|
||||||
ast[
|
|
||||||
/** @type {Exclude<keyof Node, "range" | "loc" | "leadingComments" | "trailingComments">} */
|
|
||||||
(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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = { getAllReferences, getPathInAst };
|
|
|
@ -1,16 +1,25 @@
|
||||||
import { value as value1 } from './module1'
|
import { value as value1 } from './module1'
|
||||||
const value2 = require('./module2')
|
const value2 = require('./module2')
|
||||||
const value3 = require('./module3')
|
const value3 = require('./module3')
|
||||||
|
const value4 = require('./module4')
|
||||||
|
|
||||||
let value = 42
|
let value = 42
|
||||||
let src_value = 43
|
let src_value = 43
|
||||||
let src_src_value = 44
|
let src_src_value = 44
|
||||||
|
let Symbol = 'Symbol'
|
||||||
|
|
||||||
it('inlined module should not leak to non-inlined modules', () => {
|
it('inlined module should not leak to non-inlined modules', () => {
|
||||||
|
// The two variables are in nested scope and could be the candidate names for inline module during renaming.
|
||||||
|
// The renaming logic should detect them and bypass to avoid the collisions.
|
||||||
|
const index_src_value = -1
|
||||||
|
const index_src_value_0 = -1
|
||||||
|
|
||||||
expect(value1).toBe(undefined)
|
expect(value1).toBe(undefined)
|
||||||
expect(value).toBe(42)
|
expect(value).toBe(42)
|
||||||
expect(src_value).toBe(43)
|
expect(src_value).toBe(43)
|
||||||
expect(src_src_value).toBe(44)
|
expect(src_src_value).toBe(44)
|
||||||
expect(value2).toBe("undefined") // should not touch leaked `value` variable
|
expect(Symbol).toBe('Symbol')
|
||||||
expect(value3).toBe("undefined") // should not touch leaked `src_value` variable
|
expect(value2).toBe("undefined") // Should not touch `value` variable in inline module.
|
||||||
|
expect(value3).toBe("undefined") // Should not touch src_value` in inline module.
|
||||||
|
expect(value4).toBe("function") // Module variables in inline module should not shadowling global variables.
|
||||||
})
|
})
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
module.exports = typeof Symbol
|
|
@ -5628,11 +5628,6 @@ declare class JavascriptModulesPlugin {
|
||||||
allStrict: boolean,
|
allStrict: boolean,
|
||||||
hasChunkModules: boolean
|
hasChunkModules: boolean
|
||||||
): false | Map<Module, Source>;
|
): false | Map<Module, Source>;
|
||||||
findNewName(
|
|
||||||
oldName: string,
|
|
||||||
usedName: Set<string>,
|
|
||||||
extraInfo: string
|
|
||||||
): string;
|
|
||||||
static getCompilationHooks(
|
static getCompilationHooks(
|
||||||
compilation: Compilation
|
compilation: Compilation
|
||||||
): CompilationHooksJavascriptModulesPlugin;
|
): CompilationHooksJavascriptModulesPlugin;
|
||||||
|
|
Loading…
Reference in New Issue