fix: should avoid through variables in inlined module

This commit is contained in:
fi3ework 2024-06-06 17:36:17 +08:00
parent 65bce6826b
commit bb10e4b5f0
8 changed files with 336 additions and 284 deletions

View File

@ -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

View File

@ -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;

View File

@ -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

227
lib/util/concatenate.js Normal file
View File

@ -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
};

View File

@ -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 };

View File

@ -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.
}) })

View File

@ -0,0 +1 @@
module.exports = typeof Symbol

5
types.d.ts vendored
View File

@ -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;