concatenate.js 6.8 KB

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