| 1 | /*
|
|---|
| 2 | MIT License http://www.opensource.org/licenses/mit-license.php
|
|---|
| 3 | Author Tobias Koppers @sokra
|
|---|
| 4 | */
|
|---|
| 5 |
|
|---|
| 6 | "use strict";
|
|---|
| 7 |
|
|---|
| 8 | const Template = require("../Template");
|
|---|
| 9 |
|
|---|
| 10 | /** @typedef {import("estree").Node} Node */
|
|---|
| 11 | /** @typedef {import("../javascript/JavascriptModulesPlugin").Scope} Scope */
|
|---|
| 12 | /** @typedef {import("../javascript/JavascriptModulesPlugin").Reference} Reference */
|
|---|
| 13 | /** @typedef {import("../javascript/JavascriptModulesPlugin").Variable} Variable */
|
|---|
| 14 | /** @typedef {import("../javascript/JavascriptParser").Range} Range */
|
|---|
| 15 | /** @typedef {Set<string>} UsedNames */
|
|---|
| 16 |
|
|---|
| 17 | const DEFAULT_EXPORT = "__WEBPACK_DEFAULT_EXPORT__";
|
|---|
| 18 | const NAMESPACE_OBJECT_EXPORT = "__WEBPACK_NAMESPACE_OBJECT__";
|
|---|
| 19 |
|
|---|
| 20 | /**
|
|---|
| 21 | * Gets all references.
|
|---|
| 22 | * @param {Variable} variable variable
|
|---|
| 23 | * @returns {Reference[]} references
|
|---|
| 24 | */
|
|---|
| 25 | const getAllReferences = (variable) => {
|
|---|
| 26 | let set = variable.references;
|
|---|
| 27 | // Look for inner scope variables too (like in class Foo { t() { Foo } })
|
|---|
| 28 | const identifiers = new Set(variable.identifiers);
|
|---|
| 29 | for (const scope of variable.scope.childScopes) {
|
|---|
| 30 | for (const innerVar of scope.variables) {
|
|---|
| 31 | if (innerVar.identifiers.some((id) => identifiers.has(id))) {
|
|---|
| 32 | set = [...set, ...innerVar.references];
|
|---|
| 33 | break;
|
|---|
| 34 | }
|
|---|
| 35 | }
|
|---|
| 36 | }
|
|---|
| 37 | return set;
|
|---|
| 38 | };
|
|---|
| 39 |
|
|---|
| 40 | /**
|
|---|
| 41 | * Returns result.
|
|---|
| 42 | * @param {Node | Node[]} ast ast
|
|---|
| 43 | * @param {Node} node node
|
|---|
| 44 | * @returns {undefined | Node[]} result
|
|---|
| 45 | */
|
|---|
| 46 | const getPathInAst = (ast, node) => {
|
|---|
| 47 | if (ast === node) {
|
|---|
| 48 | return [];
|
|---|
| 49 | }
|
|---|
| 50 |
|
|---|
| 51 | const nr = /** @type {Range} */ (node.range);
|
|---|
| 52 |
|
|---|
| 53 | /**
|
|---|
| 54 | * Returns result.
|
|---|
| 55 | * @param {Node} n node
|
|---|
| 56 | * @returns {Node[] | undefined} result
|
|---|
| 57 | */
|
|---|
| 58 | const enterNode = (n) => {
|
|---|
| 59 | if (!n) return;
|
|---|
| 60 | const r = n.range;
|
|---|
| 61 | if (r && r[0] <= nr[0] && r[1] >= nr[1]) {
|
|---|
| 62 | const path = getPathInAst(n, node);
|
|---|
| 63 | if (path) {
|
|---|
| 64 | path.push(n);
|
|---|
| 65 | return path;
|
|---|
| 66 | }
|
|---|
| 67 | }
|
|---|
| 68 | };
|
|---|
| 69 |
|
|---|
| 70 | if (Array.isArray(ast)) {
|
|---|
| 71 | for (let i = 0; i < ast.length; i++) {
|
|---|
| 72 | const enterResult = enterNode(ast[i]);
|
|---|
| 73 | if (enterResult !== undefined) return enterResult;
|
|---|
| 74 | }
|
|---|
| 75 | } else if (ast && typeof ast === "object") {
|
|---|
| 76 | const keys =
|
|---|
| 77 | /** @type {(keyof Node)[]} */
|
|---|
| 78 | (Object.keys(ast));
|
|---|
| 79 | for (let i = 0; i < keys.length; i++) {
|
|---|
| 80 | // We are making the faster check in `enterNode` using `n.range`
|
|---|
| 81 | const value =
|
|---|
| 82 | ast[
|
|---|
| 83 | /** @type {Exclude<keyof Node, "range" | "loc" | "leadingComments" | "trailingComments">} */
|
|---|
| 84 | (keys[i])
|
|---|
| 85 | ];
|
|---|
| 86 | if (Array.isArray(value)) {
|
|---|
| 87 | const pathResult = getPathInAst(value, node);
|
|---|
| 88 | if (pathResult !== undefined) return pathResult;
|
|---|
| 89 | } else if (value && typeof value === "object") {
|
|---|
| 90 | const enterResult = enterNode(value);
|
|---|
| 91 | if (enterResult !== undefined) return enterResult;
|
|---|
| 92 | }
|
|---|
| 93 | }
|
|---|
| 94 | }
|
|---|
| 95 | };
|
|---|
| 96 |
|
|---|
| 97 | /**
|
|---|
| 98 | * Returns found new name.
|
|---|
| 99 | * @param {string} oldName old name
|
|---|
| 100 | * @param {UsedNames} usedNamed1 used named 1
|
|---|
| 101 | * @param {UsedNames} usedNamed2 used named 2
|
|---|
| 102 | * @param {string} extraInfo extra info
|
|---|
| 103 | * @returns {string} found new name
|
|---|
| 104 | */
|
|---|
| 105 | function findNewName(oldName, usedNamed1, usedNamed2, extraInfo) {
|
|---|
| 106 | let name = oldName;
|
|---|
| 107 |
|
|---|
| 108 | if (name === DEFAULT_EXPORT) {
|
|---|
| 109 | name = "";
|
|---|
| 110 | }
|
|---|
| 111 | if (name === NAMESPACE_OBJECT_EXPORT) {
|
|---|
| 112 | name = "namespaceObject";
|
|---|
| 113 | }
|
|---|
| 114 |
|
|---|
| 115 | // Remove uncool stuff
|
|---|
| 116 | extraInfo = extraInfo.replace(
|
|---|
| 117 | /\.+\/|(?:\/index)?\.[a-zA-Z0-9]{1,4}(?:$|\s|\?)|\s*\+\s*\d+\s*modules/g,
|
|---|
| 118 | ""
|
|---|
| 119 | );
|
|---|
| 120 |
|
|---|
| 121 | const splittedInfo = extraInfo.split("/");
|
|---|
| 122 | while (splittedInfo.length) {
|
|---|
| 123 | name = splittedInfo.pop() + (name ? `_${name}` : "");
|
|---|
| 124 | const nameIdent = Template.toIdentifier(name);
|
|---|
| 125 | if (
|
|---|
| 126 | !usedNamed1.has(nameIdent) &&
|
|---|
| 127 | (!usedNamed2 || !usedNamed2.has(nameIdent))
|
|---|
| 128 | ) {
|
|---|
| 129 | return nameIdent;
|
|---|
| 130 | }
|
|---|
| 131 | }
|
|---|
| 132 |
|
|---|
| 133 | let i = 0;
|
|---|
| 134 | let nameWithNumber = Template.toIdentifier(`${name}_${i}`);
|
|---|
| 135 | while (
|
|---|
| 136 | usedNamed1.has(nameWithNumber) ||
|
|---|
| 137 | // eslint-disable-next-line no-unmodified-loop-condition
|
|---|
| 138 | (usedNamed2 && usedNamed2.has(nameWithNumber))
|
|---|
| 139 | ) {
|
|---|
| 140 | i++;
|
|---|
| 141 | nameWithNumber = Template.toIdentifier(`${name}_${i}`);
|
|---|
| 142 | }
|
|---|
| 143 | return nameWithNumber;
|
|---|
| 144 | }
|
|---|
| 145 |
|
|---|
| 146 | /** @typedef {Set<Scope>} ScopeSet */
|
|---|
| 147 |
|
|---|
| 148 | /**
|
|---|
| 149 | * Adds scope symbols.
|
|---|
| 150 | * @param {Scope | null} s scope
|
|---|
| 151 | * @param {UsedNames} nameSet name set
|
|---|
| 152 | * @param {ScopeSet} scopeSet1 scope set 1
|
|---|
| 153 | * @param {ScopeSet} scopeSet2 scope set 2
|
|---|
| 154 | */
|
|---|
| 155 | const addScopeSymbols = (s, nameSet, scopeSet1, scopeSet2) => {
|
|---|
| 156 | let scope = s;
|
|---|
| 157 | while (scope) {
|
|---|
| 158 | if (scopeSet1.has(scope)) break;
|
|---|
| 159 | if (scopeSet2.has(scope)) break;
|
|---|
| 160 | scopeSet1.add(scope);
|
|---|
| 161 | for (const variable of scope.variables) {
|
|---|
| 162 | nameSet.add(variable.name);
|
|---|
| 163 | }
|
|---|
| 164 | scope = scope.upper;
|
|---|
| 165 | }
|
|---|
| 166 | };
|
|---|
| 167 |
|
|---|
| 168 | const RESERVED_NAMES = new Set(
|
|---|
| 169 | [
|
|---|
| 170 | // internal names (should always be renamed)
|
|---|
| 171 | DEFAULT_EXPORT,
|
|---|
| 172 | NAMESPACE_OBJECT_EXPORT,
|
|---|
| 173 |
|
|---|
| 174 | // keywords
|
|---|
| 175 | "abstract,arguments,async,await,boolean,break,byte,case,catch,char,class,const,continue",
|
|---|
| 176 | "debugger,default,delete,do,double,else,enum,eval,export,extends,false,final,finally,float",
|
|---|
| 177 | "for,function,goto,if,implements,import,in,instanceof,int,interface,let,long,native,new,null",
|
|---|
| 178 | "package,private,protected,public,return,short,static,super,switch,synchronized,this,throw",
|
|---|
| 179 | "throws,transient,true,try,typeof,var,void,volatile,while,with,yield",
|
|---|
| 180 |
|
|---|
| 181 | // commonjs/amd
|
|---|
| 182 | "module,__dirname,__filename,exports,require,define",
|
|---|
| 183 |
|
|---|
| 184 | // js globals
|
|---|
| 185 | "Array,Date,eval,function,hasOwnProperty,Infinity,isFinite,isNaN,isPrototypeOf,length,Math",
|
|---|
| 186 | "NaN,name,Number,Object,prototype,String,Symbol,toString,undefined,valueOf",
|
|---|
| 187 |
|
|---|
| 188 | // browser globals
|
|---|
| 189 | "alert,all,anchor,anchors,area,assign,blur,button,checkbox,clearInterval,clearTimeout",
|
|---|
| 190 | "clientInformation,close,closed,confirm,constructor,crypto,decodeURI,decodeURIComponent",
|
|---|
| 191 | "defaultStatus,document,element,elements,embed,embeds,encodeURI,encodeURIComponent,escape",
|
|---|
| 192 | "event,fileUpload,focus,form,forms,frame,innerHeight,innerWidth,layer,layers,link,location",
|
|---|
| 193 | "mimeTypes,navigate,navigator,frames,frameRate,hidden,history,image,images,offscreenBuffering",
|
|---|
| 194 | "open,opener,option,outerHeight,outerWidth,packages,pageXOffset,pageYOffset,parent,parseFloat",
|
|---|
| 195 | "parseInt,password,pkcs11,plugin,prompt,propertyIsEnum,radio,reset,screenX,screenY,scroll",
|
|---|
| 196 | "secure,select,self,setInterval,setTimeout,status,submit,taint,text,textarea,top,unescape",
|
|---|
| 197 | "untaint,window",
|
|---|
| 198 |
|
|---|
| 199 | // window events
|
|---|
| 200 | "onblur,onclick,onerror,onfocus,onkeydown,onkeypress,onkeyup,onmouseover,onload,onmouseup,onmousedown,onsubmit"
|
|---|
| 201 | ]
|
|---|
| 202 | .join(",")
|
|---|
| 203 | .split(",")
|
|---|
| 204 | );
|
|---|
| 205 |
|
|---|
| 206 | /** @typedef {{ usedNames: UsedNames, alreadyCheckedScopes: ScopeSet }} ScopeInfo */
|
|---|
| 207 | /** @typedef {Map<string, ScopeInfo>} UsedNamesInScopeInfo */
|
|---|
| 208 |
|
|---|
| 209 | /**
|
|---|
| 210 | * Gets used names in scope info.
|
|---|
| 211 | * @param {UsedNamesInScopeInfo} usedNamesInScopeInfo used names in scope info
|
|---|
| 212 | * @param {string} module module identifier
|
|---|
| 213 | * @param {string} id export id
|
|---|
| 214 | * @returns {ScopeInfo} info
|
|---|
| 215 | */
|
|---|
| 216 | const getUsedNamesInScopeInfo = (usedNamesInScopeInfo, module, id) => {
|
|---|
| 217 | const key = `${module}-${id}`;
|
|---|
| 218 | let info = usedNamesInScopeInfo.get(key);
|
|---|
| 219 | if (info === undefined) {
|
|---|
| 220 | info = {
|
|---|
| 221 | usedNames: new Set(),
|
|---|
| 222 | alreadyCheckedScopes: new Set()
|
|---|
| 223 | };
|
|---|
| 224 | usedNamesInScopeInfo.set(key, info);
|
|---|
| 225 | }
|
|---|
| 226 | return info;
|
|---|
| 227 | };
|
|---|
| 228 |
|
|---|
| 229 | module.exports = {
|
|---|
| 230 | DEFAULT_EXPORT,
|
|---|
| 231 | NAMESPACE_OBJECT_EXPORT,
|
|---|
| 232 | RESERVED_NAMES,
|
|---|
| 233 | addScopeSymbols,
|
|---|
| 234 | findNewName,
|
|---|
| 235 | getAllReferences,
|
|---|
| 236 | getPathInAst,
|
|---|
| 237 | getUsedNamesInScopeInfo
|
|---|
| 238 | };
|
|---|