Ohm-Management - Projektarbeit B-ME
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.

index.js 45KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408
  1. /*! @author Toru Nagashima <https://github.com/mysticatea> */
  2. 'use strict';
  3. Object.defineProperty(exports, '__esModule', { value: true });
  4. /**
  5. * Get the innermost scope which contains a given location.
  6. * @param {Scope} initialScope The initial scope to search.
  7. * @param {Node} node The location to search.
  8. * @returns {Scope} The innermost scope.
  9. */
  10. function getInnermostScope(initialScope, node) {
  11. const location = node.range[0];
  12. let scope = initialScope;
  13. let found = false;
  14. do {
  15. found = false;
  16. for (const childScope of scope.childScopes) {
  17. const range = childScope.block.range;
  18. if (range[0] <= location && location < range[1]) {
  19. scope = childScope;
  20. found = true;
  21. break
  22. }
  23. }
  24. } while (found)
  25. return scope
  26. }
  27. /**
  28. * Find the variable of a given name.
  29. * @param {Scope} initialScope The scope to start finding.
  30. * @param {string|Node} nameOrNode The variable name to find. If this is a Node object then it should be an Identifier node.
  31. * @returns {Variable|null} The found variable or null.
  32. */
  33. function findVariable(initialScope, nameOrNode) {
  34. let name = "";
  35. let scope = initialScope;
  36. if (typeof nameOrNode === "string") {
  37. name = nameOrNode;
  38. } else {
  39. name = nameOrNode.name;
  40. scope = getInnermostScope(scope, nameOrNode);
  41. }
  42. while (scope != null) {
  43. const variable = scope.set.get(name);
  44. if (variable != null) {
  45. return variable
  46. }
  47. scope = scope.upper;
  48. }
  49. return null
  50. }
  51. /**
  52. * Negate the result of `this` calling.
  53. * @param {Token} token The token to check.
  54. * @returns {boolean} `true` if the result of `this(token)` is `false`.
  55. */
  56. function negate0(token) {
  57. return !this(token) //eslint-disable-line no-invalid-this
  58. }
  59. /**
  60. * Creates the negate function of the given function.
  61. * @param {function(Token):boolean} f - The function to negate.
  62. * @returns {function(Token):boolean} Negated function.
  63. */
  64. function negate(f) {
  65. return negate0.bind(f)
  66. }
  67. /**
  68. * Checks if the given token is an arrow token or not.
  69. * @param {Token} token - The token to check.
  70. * @returns {boolean} `true` if the token is an arrow token.
  71. */
  72. function isArrowToken(token) {
  73. return token.value === "=>" && token.type === "Punctuator"
  74. }
  75. /**
  76. * Checks if the given token is a comma token or not.
  77. * @param {Token} token - The token to check.
  78. * @returns {boolean} `true` if the token is a comma token.
  79. */
  80. function isCommaToken(token) {
  81. return token.value === "," && token.type === "Punctuator"
  82. }
  83. /**
  84. * Checks if the given token is a semicolon token or not.
  85. * @param {Token} token - The token to check.
  86. * @returns {boolean} `true` if the token is a semicolon token.
  87. */
  88. function isSemicolonToken(token) {
  89. return token.value === ";" && token.type === "Punctuator"
  90. }
  91. /**
  92. * Checks if the given token is a colon token or not.
  93. * @param {Token} token - The token to check.
  94. * @returns {boolean} `true` if the token is a colon token.
  95. */
  96. function isColonToken(token) {
  97. return token.value === ":" && token.type === "Punctuator"
  98. }
  99. /**
  100. * Checks if the given token is an opening parenthesis token or not.
  101. * @param {Token} token - The token to check.
  102. * @returns {boolean} `true` if the token is an opening parenthesis token.
  103. */
  104. function isOpeningParenToken(token) {
  105. return token.value === "(" && token.type === "Punctuator"
  106. }
  107. /**
  108. * Checks if the given token is a closing parenthesis token or not.
  109. * @param {Token} token - The token to check.
  110. * @returns {boolean} `true` if the token is a closing parenthesis token.
  111. */
  112. function isClosingParenToken(token) {
  113. return token.value === ")" && token.type === "Punctuator"
  114. }
  115. /**
  116. * Checks if the given token is an opening square bracket token or not.
  117. * @param {Token} token - The token to check.
  118. * @returns {boolean} `true` if the token is an opening square bracket token.
  119. */
  120. function isOpeningBracketToken(token) {
  121. return token.value === "[" && token.type === "Punctuator"
  122. }
  123. /**
  124. * Checks if the given token is a closing square bracket token or not.
  125. * @param {Token} token - The token to check.
  126. * @returns {boolean} `true` if the token is a closing square bracket token.
  127. */
  128. function isClosingBracketToken(token) {
  129. return token.value === "]" && token.type === "Punctuator"
  130. }
  131. /**
  132. * Checks if the given token is an opening brace token or not.
  133. * @param {Token} token - The token to check.
  134. * @returns {boolean} `true` if the token is an opening brace token.
  135. */
  136. function isOpeningBraceToken(token) {
  137. return token.value === "{" && token.type === "Punctuator"
  138. }
  139. /**
  140. * Checks if the given token is a closing brace token or not.
  141. * @param {Token} token - The token to check.
  142. * @returns {boolean} `true` if the token is a closing brace token.
  143. */
  144. function isClosingBraceToken(token) {
  145. return token.value === "}" && token.type === "Punctuator"
  146. }
  147. /**
  148. * Checks if the given token is a comment token or not.
  149. * @param {Token} token - The token to check.
  150. * @returns {boolean} `true` if the token is a comment token.
  151. */
  152. function isCommentToken(token) {
  153. return (
  154. token.type === "Line" ||
  155. token.type === "Block" ||
  156. token.type === "Shebang"
  157. )
  158. }
  159. const isNotArrowToken = negate(isArrowToken);
  160. const isNotCommaToken = negate(isCommaToken);
  161. const isNotSemicolonToken = negate(isSemicolonToken);
  162. const isNotColonToken = negate(isColonToken);
  163. const isNotOpeningParenToken = negate(isOpeningParenToken);
  164. const isNotClosingParenToken = negate(isClosingParenToken);
  165. const isNotOpeningBracketToken = negate(isOpeningBracketToken);
  166. const isNotClosingBracketToken = negate(isClosingBracketToken);
  167. const isNotOpeningBraceToken = negate(isOpeningBraceToken);
  168. const isNotClosingBraceToken = negate(isClosingBraceToken);
  169. const isNotCommentToken = negate(isCommentToken);
  170. /**
  171. * Get the `(` token of the given function node.
  172. * @param {Node} node - The function node to get.
  173. * @param {SourceCode} sourceCode - The source code object to get tokens.
  174. * @returns {Token} `(` token.
  175. */
  176. function getOpeningParenOfParams(node, sourceCode) {
  177. return node.id
  178. ? sourceCode.getTokenAfter(node.id, isOpeningParenToken)
  179. : sourceCode.getFirstToken(node, isOpeningParenToken)
  180. }
  181. /**
  182. * Get the location of the given function node for reporting.
  183. * @param {Node} node - The function node to get.
  184. * @param {SourceCode} sourceCode - The source code object to get tokens.
  185. * @returns {string} The location of the function node for reporting.
  186. */
  187. function getFunctionHeadLocation(node, sourceCode) {
  188. const parent = node.parent;
  189. let start = null;
  190. let end = null;
  191. if (node.type === "ArrowFunctionExpression") {
  192. const arrowToken = sourceCode.getTokenBefore(node.body, isArrowToken);
  193. start = arrowToken.loc.start;
  194. end = arrowToken.loc.end;
  195. } else if (
  196. parent.type === "Property" ||
  197. parent.type === "MethodDefinition"
  198. ) {
  199. start = parent.loc.start;
  200. end = getOpeningParenOfParams(node, sourceCode).loc.start;
  201. } else {
  202. start = node.loc.start;
  203. end = getOpeningParenOfParams(node, sourceCode).loc.start;
  204. }
  205. return {
  206. start: Object.assign({}, start),
  207. end: Object.assign({}, end),
  208. }
  209. }
  210. const builtinNames = Object.freeze(
  211. new Set([
  212. "Array",
  213. "ArrayBuffer",
  214. "Boolean",
  215. "DataView",
  216. "Date",
  217. "decodeURI",
  218. "decodeURIComponent",
  219. "encodeURI",
  220. "encodeURIComponent",
  221. "Error",
  222. "escape",
  223. "EvalError",
  224. "Float32Array",
  225. "Float64Array",
  226. "Function",
  227. "Infinity",
  228. "Int16Array",
  229. "Int32Array",
  230. "Int8Array",
  231. "isFinite",
  232. "isNaN",
  233. "isPrototypeOf",
  234. "JSON",
  235. "Map",
  236. "Math",
  237. "NaN",
  238. "Number",
  239. "Object",
  240. "parseFloat",
  241. "parseInt",
  242. "Promise",
  243. "Proxy",
  244. "RangeError",
  245. "ReferenceError",
  246. "Reflect",
  247. "RegExp",
  248. "Set",
  249. "String",
  250. "Symbol",
  251. "SyntaxError",
  252. "TypeError",
  253. "Uint16Array",
  254. "Uint32Array",
  255. "Uint8Array",
  256. "Uint8ClampedArray",
  257. "undefined",
  258. "unescape",
  259. "URIError",
  260. "WeakMap",
  261. "WeakSet",
  262. ])
  263. );
  264. /**
  265. * Get the element values of a given node list.
  266. * @param {Node[]} nodeList The node list to get values.
  267. * @param {Scope|undefined} initialScope The initial scope to find variables.
  268. * @returns {any[]|null} The value list if all nodes are constant. Otherwise, null.
  269. */
  270. function getElementValues(nodeList, initialScope) {
  271. const valueList = [];
  272. for (let i = 0; i < nodeList.length; ++i) {
  273. const elementNode = nodeList[i];
  274. if (elementNode == null) {
  275. valueList.length = i + 1;
  276. } else if (elementNode.type === "SpreadElement") {
  277. const argument = getStaticValueR(elementNode.argument, initialScope);
  278. if (argument == null) {
  279. return null
  280. }
  281. valueList.push(...argument.value);
  282. } else {
  283. const element = getStaticValueR(elementNode, initialScope);
  284. if (element == null) {
  285. return null
  286. }
  287. valueList.push(element.value);
  288. }
  289. }
  290. return valueList
  291. }
  292. const operations = Object.freeze({
  293. ArrayExpression(node, initialScope) {
  294. const elements = getElementValues(node.elements, initialScope);
  295. return elements != null ? { value: elements } : null
  296. },
  297. AssignmentExpression(node, initialScope) {
  298. if (node.operator === "=") {
  299. return getStaticValueR(node.right, initialScope)
  300. }
  301. return null
  302. },
  303. //eslint-disable-next-line complexity
  304. BinaryExpression(node, initialScope) {
  305. if (node.operator === "in" || node.operator === "instanceof") {
  306. // Not supported.
  307. return null
  308. }
  309. const left = getStaticValueR(node.left, initialScope);
  310. const right = getStaticValueR(node.right, initialScope);
  311. if (left != null && right != null) {
  312. switch (node.operator) {
  313. case "==":
  314. return { value: left.value == right.value } //eslint-disable-line eqeqeq
  315. case "!=":
  316. return { value: left.value != right.value } //eslint-disable-line eqeqeq
  317. case "===":
  318. return { value: left.value === right.value }
  319. case "!==":
  320. return { value: left.value !== right.value }
  321. case "<":
  322. return { value: left.value < right.value }
  323. case "<=":
  324. return { value: left.value <= right.value }
  325. case ">":
  326. return { value: left.value > right.value }
  327. case ">=":
  328. return { value: left.value >= right.value }
  329. case "<<":
  330. return { value: left.value << right.value }
  331. case ">>":
  332. return { value: left.value >> right.value }
  333. case ">>>":
  334. return { value: left.value >>> right.value }
  335. case "+":
  336. return { value: left.value + right.value }
  337. case "-":
  338. return { value: left.value - right.value }
  339. case "*":
  340. return { value: left.value * right.value }
  341. case "/":
  342. return { value: left.value / right.value }
  343. case "%":
  344. return { value: left.value % right.value }
  345. case "**":
  346. return { value: Math.pow(left.value, right.value) }
  347. case "|":
  348. return { value: left.value | right.value }
  349. case "^":
  350. return { value: left.value ^ right.value }
  351. case "&":
  352. return { value: left.value & right.value }
  353. // no default
  354. }
  355. }
  356. return null
  357. },
  358. CallExpression(node, initialScope) {
  359. const calleeNode = node.callee;
  360. const args = getElementValues(node.arguments, initialScope);
  361. if (args != null) {
  362. if (calleeNode.type === "MemberExpression") {
  363. const object = getStaticValueR(calleeNode.object, initialScope);
  364. const property = calleeNode.computed
  365. ? getStaticValueR(calleeNode.property, initialScope)
  366. : { value: calleeNode.property.name };
  367. if (object != null && property != null) {
  368. const receiver = object.value;
  369. const methodName = property.value;
  370. return { value: receiver[methodName](...args) }
  371. }
  372. } else {
  373. const callee = getStaticValueR(calleeNode, initialScope);
  374. if (callee != null) {
  375. const func = callee.value;
  376. return { value: func(...args) }
  377. }
  378. }
  379. }
  380. return null
  381. },
  382. ConditionalExpression(node, initialScope) {
  383. const test = getStaticValueR(node.test, initialScope);
  384. if (test != null) {
  385. return test.value
  386. ? getStaticValueR(node.consequent, initialScope)
  387. : getStaticValueR(node.alternate, initialScope)
  388. }
  389. return null
  390. },
  391. ExpressionStatement(node, initialScope) {
  392. return getStaticValueR(node.expression, initialScope)
  393. },
  394. Identifier(node, initialScope) {
  395. if (initialScope != null) {
  396. const variable = findVariable(initialScope, node);
  397. // Built-in globals.
  398. if (
  399. variable != null &&
  400. variable.defs.length === 0 &&
  401. builtinNames.has(variable.name) &&
  402. variable.name in global
  403. ) {
  404. return { value: global[variable.name] }
  405. }
  406. // Constants.
  407. if (variable != null && variable.defs.length === 1) {
  408. const def = variable.defs[0];
  409. if (
  410. def.parent &&
  411. def.parent.kind === "const" &&
  412. // TODO(mysticatea): don't support destructuring here.
  413. def.node.id.type === "Identifier"
  414. ) {
  415. return getStaticValueR(def.node.init, initialScope)
  416. }
  417. }
  418. }
  419. return null
  420. },
  421. Literal(node) {
  422. //istanbul ignore if : this is implementation-specific behavior.
  423. if (node.regex != null && node.value == null) {
  424. // It was a RegExp literal, but Node.js didn't support it.
  425. return null
  426. }
  427. return node
  428. },
  429. LogicalExpression(node, initialScope) {
  430. const left = getStaticValueR(node.left, initialScope);
  431. if (left != null) {
  432. if (
  433. (node.operator === "||" && Boolean(left.value) === true) ||
  434. (node.operator === "&&" && Boolean(left.value) === false)
  435. ) {
  436. return left
  437. }
  438. const right = getStaticValueR(node.right, initialScope);
  439. if (right != null) {
  440. return right
  441. }
  442. }
  443. return null
  444. },
  445. MemberExpression(node, initialScope) {
  446. const object = getStaticValueR(node.object, initialScope);
  447. const property = node.computed
  448. ? getStaticValueR(node.property, initialScope)
  449. : { value: node.property.name };
  450. if (object != null && property != null) {
  451. return { value: object.value[property.value] }
  452. }
  453. return null
  454. },
  455. NewExpression(node, initialScope) {
  456. const callee = getStaticValueR(node.callee, initialScope);
  457. const args = getElementValues(node.arguments, initialScope);
  458. if (callee != null && args != null) {
  459. const Func = callee.value;
  460. return { value: new Func(...args) }
  461. }
  462. return null
  463. },
  464. ObjectExpression(node, initialScope) {
  465. const object = {};
  466. for (const propertyNode of node.properties) {
  467. if (propertyNode.type === "Property") {
  468. if (propertyNode.kind !== "init") {
  469. return null
  470. }
  471. const key = propertyNode.computed
  472. ? getStaticValueR(propertyNode.key, initialScope)
  473. : { value: propertyNode.key.name };
  474. const value = getStaticValueR(propertyNode.value, initialScope);
  475. if (key == null || value == null) {
  476. return null
  477. }
  478. object[key.value] = value.value;
  479. } else if (
  480. propertyNode.type === "SpreadElement" ||
  481. propertyNode.type === "ExperimentalSpreadProperty"
  482. ) {
  483. const argument = getStaticValueR(
  484. propertyNode.argument,
  485. initialScope
  486. );
  487. if (argument == null) {
  488. return null
  489. }
  490. Object.assign(object, argument.value);
  491. } else {
  492. return null
  493. }
  494. }
  495. return { value: object }
  496. },
  497. SequenceExpression(node, initialScope) {
  498. const last = node.expressions[node.expressions.length - 1];
  499. return getStaticValueR(last, initialScope)
  500. },
  501. TaggedTemplateExpression(node, initialScope) {
  502. const tag = getStaticValueR(node.tag, initialScope);
  503. const expressions = getElementValues(
  504. node.quasi.expressions,
  505. initialScope
  506. );
  507. if (tag != null && expressions != null) {
  508. const func = tag.value;
  509. const strings = node.quasi.quasis.map(q => q.value.cooked);
  510. strings.raw = node.quasi.quasis.map(q => q.value.raw);
  511. return { value: func(strings, ...expressions) }
  512. }
  513. return null
  514. },
  515. TemplateLiteral(node, initialScope) {
  516. const expressions = getElementValues(node.expressions, initialScope);
  517. if (expressions != null) {
  518. let value = node.quasis[0].value.cooked;
  519. for (let i = 0; i < expressions.length; ++i) {
  520. value += expressions[i];
  521. value += node.quasis[i + 1].value.cooked;
  522. }
  523. return { value }
  524. }
  525. return null
  526. },
  527. UnaryExpression(node, initialScope) {
  528. if (node.operator === "delete") {
  529. // Not supported.
  530. return null
  531. }
  532. if (node.operator === "void") {
  533. return { value: undefined }
  534. }
  535. const arg = getStaticValueR(node.argument, initialScope);
  536. if (arg != null) {
  537. switch (node.operator) {
  538. case "-":
  539. return { value: -arg.value }
  540. case "+":
  541. return { value: +arg.value } //eslint-disable-line no-implicit-coercion
  542. case "!":
  543. return { value: !arg.value }
  544. case "~":
  545. return { value: ~arg.value }
  546. case "typeof":
  547. return { value: typeof arg.value }
  548. // no default
  549. }
  550. }
  551. return null
  552. },
  553. });
  554. /**
  555. * Get the value of a given node if it's a static value.
  556. * @param {Node} node The node to get.
  557. * @param {Scope|undefined} initialScope The scope to start finding variable.
  558. * @returns {{value:any}|null} The static value of the node, or `null`.
  559. */
  560. function getStaticValueR(node, initialScope) {
  561. if (node != null && Object.hasOwnProperty.call(operations, node.type)) {
  562. return operations[node.type](node, initialScope)
  563. }
  564. return null
  565. }
  566. /**
  567. * Get the value of a given node if it's a static value.
  568. * @param {Node} node The node to get.
  569. * @param {Scope} [initialScope] The scope to start finding variable. Optional. If this scope was given, this tries to resolve identifier references which are in the given node as much as possible.
  570. * @returns {{value:any}|null} The static value of the node, or `null`.
  571. */
  572. function getStaticValue(node, initialScope = null) {
  573. try {
  574. return getStaticValueR(node, initialScope)
  575. } catch (_error) {
  576. return null
  577. }
  578. }
  579. /**
  580. * Get the value of a given node if it's a literal or a template literal.
  581. * @param {Node} node The node to get.
  582. * @param {Scope} [initialScope] The scope to start finding variable. Optional. If the node is an Identifier node and this scope was given, this checks the variable of the identifier, and returns the value of it if the variable is a constant.
  583. * @returns {string|null} The value of the node, or `null`.
  584. */
  585. function getStringIfConstant(node, initialScope = null) {
  586. const evaluated = getStaticValue(node, initialScope);
  587. return evaluated && String(evaluated.value)
  588. }
  589. /**
  590. * Get the property name from a MemberExpression node or a Property node.
  591. * @param {Node} node The node to get.
  592. * @param {Scope} [initialScope] The scope to start finding variable. Optional. If the node is a computed property node and this scope was given, this checks the computed property name by the `getStringIfConstant` function with the scope, and returns the value of it.
  593. * @returns {string|null} The property name of the node.
  594. */
  595. function getPropertyName(node, initialScope) {
  596. switch (node.type) {
  597. case "MemberExpression":
  598. if (node.computed) {
  599. return getStringIfConstant(node.property, initialScope)
  600. }
  601. return node.property.name
  602. case "Property":
  603. case "MethodDefinition":
  604. if (node.computed) {
  605. return getStringIfConstant(node.key, initialScope)
  606. }
  607. if (node.key.type === "Literal") {
  608. return String(node.key.value)
  609. }
  610. return node.key.name
  611. // no default
  612. }
  613. return null
  614. }
  615. /**
  616. * Get the name and kind of the given function node.
  617. * @param {ASTNode} node - The function node to get.
  618. * @returns {string} The name and kind of the function node.
  619. */
  620. function getFunctionNameWithKind(node) {
  621. const parent = node.parent;
  622. const tokens = [];
  623. if (parent.type === "MethodDefinition" && parent.static) {
  624. tokens.push("static");
  625. }
  626. if (node.async) {
  627. tokens.push("async");
  628. }
  629. if (node.generator) {
  630. tokens.push("generator");
  631. }
  632. if (node.type === "ArrowFunctionExpression") {
  633. tokens.push("arrow", "function");
  634. } else if (
  635. parent.type === "Property" ||
  636. parent.type === "MethodDefinition"
  637. ) {
  638. if (parent.kind === "constructor") {
  639. return "constructor"
  640. }
  641. if (parent.kind === "get") {
  642. tokens.push("getter");
  643. } else if (parent.kind === "set") {
  644. tokens.push("setter");
  645. } else {
  646. tokens.push("method");
  647. }
  648. } else {
  649. tokens.push("function");
  650. }
  651. if (node.id) {
  652. tokens.push(`'${node.id.name}'`);
  653. } else {
  654. const name = getPropertyName(parent);
  655. if (name) {
  656. tokens.push(`'${name}'`);
  657. }
  658. }
  659. return tokens.join(" ")
  660. }
  661. /**
  662. * @author Toru Nagashima <https://github.com/mysticatea>
  663. * See LICENSE file in root directory for full license.
  664. */
  665. const placeholder = /\$(?:[$&`']|[1-9][0-9]?)/g;
  666. /** @type {WeakMap<PatternMatcher, {pattern:RegExp,escaped:boolean}>} */
  667. const internal = new WeakMap();
  668. /**
  669. * Check whether a given character is escaped or not.
  670. * @param {string} str The string to check.
  671. * @param {number} index The location of the character to check.
  672. * @returns {boolean} `true` if the character is escaped.
  673. */
  674. function isEscaped(str, index) {
  675. let escaped = false;
  676. for (let i = index - 1; i >= 0 && str.charCodeAt(i) === 0x5c; --i) {
  677. escaped = !escaped;
  678. }
  679. return escaped
  680. }
  681. /**
  682. * Replace a given string by a given matcher.
  683. * @param {PatternMatcher} matcher The pattern matcher.
  684. * @param {string} str The string to be replaced.
  685. * @param {string} replacement The new substring to replace each matched part.
  686. * @returns {string} The replaced string.
  687. */
  688. function replaceS(matcher, str, replacement) {
  689. const chunks = [];
  690. let index = 0;
  691. /** @type {RegExpExecArray} */
  692. let match = null;
  693. /**
  694. * @param {string} key The placeholder.
  695. * @returns {string} The replaced string.
  696. */
  697. function replacer(key) {
  698. switch (key) {
  699. case "$$":
  700. return "$"
  701. case "$&":
  702. return match[0]
  703. case "$`":
  704. return str.slice(0, match.index)
  705. case "$'":
  706. return str.slice(match.index + match[0].length)
  707. default: {
  708. const i = key.slice(1);
  709. if (i in match) {
  710. return match[i]
  711. }
  712. return key
  713. }
  714. }
  715. }
  716. for (match of matcher.execAll(str)) {
  717. chunks.push(str.slice(index, match.index));
  718. chunks.push(replacement.replace(placeholder, replacer));
  719. index = match.index + match[0].length;
  720. }
  721. chunks.push(str.slice(index));
  722. return chunks.join("")
  723. }
  724. //eslint-disable-next-line valid-jsdoc
  725. /**
  726. * Replace a given string by a given matcher.
  727. * @param {PatternMatcher} matcher The pattern matcher.
  728. * @param {string} str The string to be replaced.
  729. * @param {(...strs[])=>string} replace The function to replace each matched part.
  730. * @returns {string} The replaced string.
  731. */
  732. function replaceF(matcher, str, replace) {
  733. const chunks = [];
  734. let index = 0;
  735. for (const match of matcher.execAll(str)) {
  736. chunks.push(str.slice(index, match.index));
  737. chunks.push(String(replace(...match, match.index, match.input)));
  738. index = match.index + match[0].length;
  739. }
  740. chunks.push(str.slice(index));
  741. return chunks.join("")
  742. }
  743. /**
  744. * The class to find patterns as considering escape sequences.
  745. */
  746. class PatternMatcher {
  747. /**
  748. * Initialize this matcher.
  749. * @param {RegExp} pattern The pattern to match.
  750. * @param {{escaped:boolean}} options The options.
  751. */
  752. constructor(pattern, { escaped = false } = {}) {
  753. if (!(pattern instanceof RegExp)) {
  754. throw new TypeError("'pattern' should be a RegExp instance.")
  755. }
  756. if (!pattern.flags.includes("g")) {
  757. throw new Error("'pattern' should contains 'g' flag.")
  758. }
  759. internal.set(this, {
  760. pattern: new RegExp(pattern.source, pattern.flags),
  761. escaped: Boolean(escaped),
  762. });
  763. }
  764. /**
  765. * Find the pattern in a given string.
  766. * @param {string} str The string to find.
  767. * @returns {IterableIterator<RegExpExecArray>} The iterator which iterate the matched information.
  768. */
  769. *execAll(str) {
  770. const { pattern, escaped } = internal.get(this);
  771. let match = null;
  772. let lastIndex = 0;
  773. pattern.lastIndex = 0;
  774. while ((match = pattern.exec(str)) != null) {
  775. if (escaped || !isEscaped(str, match.index)) {
  776. lastIndex = pattern.lastIndex;
  777. yield match;
  778. pattern.lastIndex = lastIndex;
  779. }
  780. }
  781. }
  782. /**
  783. * Check whether the pattern is found in a given string.
  784. * @param {string} str The string to check.
  785. * @returns {boolean} `true` if the pattern was found in the string.
  786. */
  787. test(str) {
  788. const it = this.execAll(str);
  789. const ret = it.next();
  790. return !ret.done
  791. }
  792. //eslint-disable-next-line valid-jsdoc
  793. /**
  794. * Replace a given string.
  795. * @param {string} str The string to be replaced.
  796. * @param {(string|((...strs:string[])=>string))} replacer The string or function to replace. This is the same as the 2nd argument of `String.prototype.replace`.
  797. * @returns {string} The replaced string.
  798. */
  799. [Symbol.replace](str, replacer) {
  800. return typeof replacer === "function"
  801. ? replaceF(this, String(str), replacer)
  802. : replaceS(this, String(str), String(replacer))
  803. }
  804. }
  805. const SENTINEL_TYPE = /^(?:.+?Statement|.+?Declaration|(?:Array|ArrowFunction|Assignment|Call|Class|Function|Member|New|Object)Expression|AssignmentPattern|Program|VariableDeclarator)$/;
  806. const IMPORT_TYPE = /^(?:Import|Export(?:All|Default|Named))Declaration$/;
  807. const has = Function.call.bind(Object.hasOwnProperty);
  808. const READ = Symbol("read");
  809. const CALL = Symbol("call");
  810. const CONSTRUCT = Symbol("construct");
  811. const ESM = Symbol("esm");
  812. const requireCall = { require: { [CALL]: true } };
  813. /**
  814. * Check whether a given variable is modified or not.
  815. * @param {Variable} variable The variable to check.
  816. * @returns {boolean} `true` if the variable is modified.
  817. */
  818. function isModifiedGlobal(variable) {
  819. return (
  820. variable == null ||
  821. variable.defs.length !== 0 ||
  822. variable.references.some(r => r.isWrite())
  823. )
  824. }
  825. /**
  826. * The reference tracker.
  827. */
  828. class ReferenceTracker {
  829. /**
  830. * Initialize this tracker.
  831. * @param {Scope} globalScope The global scope.
  832. * @param {object} [options] The options.
  833. * @param {"legacy"|"strict"} [options.mode="strict"] The mode to determine the ImportDeclaration's behavior for CJS modules.
  834. * @param {string[]} [options.globalObjectNames=["global","self","window"]] The variable names for Global Object.
  835. */
  836. constructor(
  837. globalScope,
  838. {
  839. mode = "strict",
  840. globalObjectNames = ["global", "self", "window"],
  841. } = {}
  842. ) {
  843. this.variableStack = [];
  844. this.globalScope = globalScope;
  845. this.mode = mode;
  846. this.globalObjectNames = globalObjectNames.slice(0);
  847. }
  848. /**
  849. * Iterate the references of global variables.
  850. * @param {object} traceMap The trace map.
  851. * @returns {IterableIterator<{node:Node,path:string[],type:symbol,info:any}>} The iterator to iterate references.
  852. */
  853. *iterateGlobalReferences(traceMap) {
  854. for (const key of Object.keys(traceMap)) {
  855. const nextTraceMap = traceMap[key];
  856. const path = [key];
  857. const variable = this.globalScope.set.get(key);
  858. if (isModifiedGlobal(variable)) {
  859. continue
  860. }
  861. yield* this._iterateVariableReferences(
  862. variable,
  863. path,
  864. nextTraceMap,
  865. true
  866. );
  867. }
  868. for (const key of this.globalObjectNames) {
  869. const path = [];
  870. const variable = this.globalScope.set.get(key);
  871. if (isModifiedGlobal(variable)) {
  872. continue
  873. }
  874. yield* this._iterateVariableReferences(
  875. variable,
  876. path,
  877. traceMap,
  878. false
  879. );
  880. }
  881. }
  882. /**
  883. * Iterate the references of CommonJS modules.
  884. * @param {object} traceMap The trace map.
  885. * @returns {IterableIterator<{node:Node,path:string[],type:symbol,info:any}>} The iterator to iterate references.
  886. */
  887. *iterateCjsReferences(traceMap) {
  888. for (const { node } of this.iterateGlobalReferences(requireCall)) {
  889. const key = getStringIfConstant(node.arguments[0]);
  890. if (key == null || !has(traceMap, key)) {
  891. continue
  892. }
  893. const nextTraceMap = traceMap[key];
  894. const path = [key];
  895. if (nextTraceMap[READ]) {
  896. yield {
  897. node,
  898. path,
  899. type: READ,
  900. info: nextTraceMap[READ],
  901. };
  902. }
  903. yield* this._iteratePropertyReferences(node, path, nextTraceMap);
  904. }
  905. }
  906. /**
  907. * Iterate the references of ES modules.
  908. * @param {object} traceMap The trace map.
  909. * @returns {IterableIterator<{node:Node,path:string[],type:symbol,info:any}>} The iterator to iterate references.
  910. */
  911. *iterateEsmReferences(traceMap) {
  912. const programNode = this.globalScope.block;
  913. for (const node of programNode.body) {
  914. if (!IMPORT_TYPE.test(node.type) || node.source == null) {
  915. continue
  916. }
  917. const moduleId = node.source.value;
  918. if (!has(traceMap, moduleId)) {
  919. continue
  920. }
  921. const nextTraceMap = traceMap[moduleId];
  922. const path = [moduleId];
  923. if (nextTraceMap[READ]) {
  924. yield { node, path, type: READ, info: nextTraceMap[READ] };
  925. }
  926. if (node.type === "ExportAllDeclaration") {
  927. for (const key of Object.keys(nextTraceMap)) {
  928. const exportTraceMap = nextTraceMap[key];
  929. if (exportTraceMap[READ]) {
  930. yield {
  931. node,
  932. path: path.concat(key),
  933. type: READ,
  934. info: exportTraceMap[READ],
  935. };
  936. }
  937. }
  938. } else {
  939. for (const specifier of node.specifiers) {
  940. const esm = has(nextTraceMap, ESM);
  941. const it = this._iterateImportReferences(
  942. specifier,
  943. path,
  944. esm
  945. ? nextTraceMap
  946. : this.mode === "legacy"
  947. ? Object.assign(
  948. { default: nextTraceMap },
  949. nextTraceMap
  950. )
  951. : { default: nextTraceMap }
  952. );
  953. if (esm) {
  954. yield* it;
  955. } else {
  956. for (const report of it) {
  957. report.path = report.path.filter(exceptDefault);
  958. if (
  959. report.path.length >= 2 ||
  960. report.type !== READ
  961. ) {
  962. yield report;
  963. }
  964. }
  965. }
  966. }
  967. }
  968. }
  969. }
  970. /**
  971. * Iterate the references for a given variable.
  972. * @param {Variable} variable The variable to iterate that references.
  973. * @param {string[]} path The current path.
  974. * @param {object} traceMap The trace map.
  975. * @param {boolean} shouldReport = The flag to report those references.
  976. * @returns {IterableIterator<{node:Node,path:string[],type:symbol,info:any}>} The iterator to iterate references.
  977. */
  978. *_iterateVariableReferences(variable, path, traceMap, shouldReport) {
  979. if (this.variableStack.includes(variable)) {
  980. return
  981. }
  982. this.variableStack.push(variable);
  983. try {
  984. for (const reference of variable.references) {
  985. if (!reference.isRead()) {
  986. continue
  987. }
  988. const node = reference.identifier;
  989. if (shouldReport && traceMap[READ]) {
  990. yield { node, path, type: READ, info: traceMap[READ] };
  991. }
  992. yield* this._iteratePropertyReferences(node, path, traceMap);
  993. }
  994. } finally {
  995. this.variableStack.pop();
  996. }
  997. }
  998. /**
  999. * Iterate the references for a given AST node.
  1000. * @param rootNode The AST node to iterate references.
  1001. * @param {string[]} path The current path.
  1002. * @param {object} traceMap The trace map.
  1003. * @returns {IterableIterator<{node:Node,path:string[],type:symbol,info:any}>} The iterator to iterate references.
  1004. */
  1005. //eslint-disable-next-line complexity, require-jsdoc
  1006. *_iteratePropertyReferences(rootNode, path, traceMap) {
  1007. let node = rootNode;
  1008. while (!SENTINEL_TYPE.test(node.parent.type)) {
  1009. node = node.parent;
  1010. }
  1011. const parent = node.parent;
  1012. if (parent.type === "MemberExpression") {
  1013. if (parent.object === node) {
  1014. const key = getPropertyName(parent);
  1015. if (key == null || !has(traceMap, key)) {
  1016. return
  1017. }
  1018. path = path.concat(key); //eslint-disable-line no-param-reassign
  1019. const nextTraceMap = traceMap[key];
  1020. if (nextTraceMap[READ]) {
  1021. yield {
  1022. node: parent,
  1023. path,
  1024. type: READ,
  1025. info: nextTraceMap[READ],
  1026. };
  1027. }
  1028. yield* this._iteratePropertyReferences(
  1029. parent,
  1030. path,
  1031. nextTraceMap
  1032. );
  1033. }
  1034. return
  1035. }
  1036. if (parent.type === "CallExpression") {
  1037. if (parent.callee === node && traceMap[CALL]) {
  1038. yield { node: parent, path, type: CALL, info: traceMap[CALL] };
  1039. }
  1040. return
  1041. }
  1042. if (parent.type === "NewExpression") {
  1043. if (parent.callee === node && traceMap[CONSTRUCT]) {
  1044. yield {
  1045. node: parent,
  1046. path,
  1047. type: CONSTRUCT,
  1048. info: traceMap[CONSTRUCT],
  1049. };
  1050. }
  1051. return
  1052. }
  1053. if (parent.type === "AssignmentExpression") {
  1054. if (parent.right === node) {
  1055. yield* this._iterateLhsReferences(parent.left, path, traceMap);
  1056. yield* this._iteratePropertyReferences(parent, path, traceMap);
  1057. }
  1058. return
  1059. }
  1060. if (parent.type === "AssignmentPattern") {
  1061. if (parent.right === node) {
  1062. yield* this._iterateLhsReferences(parent.left, path, traceMap);
  1063. }
  1064. return
  1065. }
  1066. if (parent.type === "VariableDeclarator") {
  1067. if (parent.init === node) {
  1068. yield* this._iterateLhsReferences(parent.id, path, traceMap);
  1069. }
  1070. }
  1071. }
  1072. /**
  1073. * Iterate the references for a given Pattern node.
  1074. * @param {Node} patternNode The Pattern node to iterate references.
  1075. * @param {string[]} path The current path.
  1076. * @param {object} traceMap The trace map.
  1077. * @returns {IterableIterator<{node:Node,path:string[],type:symbol,info:any}>} The iterator to iterate references.
  1078. */
  1079. *_iterateLhsReferences(patternNode, path, traceMap) {
  1080. if (patternNode.type === "Identifier") {
  1081. const variable = findVariable(this.globalScope, patternNode);
  1082. if (variable != null) {
  1083. yield* this._iterateVariableReferences(
  1084. variable,
  1085. path,
  1086. traceMap,
  1087. false
  1088. );
  1089. }
  1090. return
  1091. }
  1092. if (patternNode.type === "ObjectPattern") {
  1093. for (const property of patternNode.properties) {
  1094. const key = getPropertyName(property);
  1095. if (key == null || !has(traceMap, key)) {
  1096. continue
  1097. }
  1098. const nextPath = path.concat(key);
  1099. const nextTraceMap = traceMap[key];
  1100. if (nextTraceMap[READ]) {
  1101. yield {
  1102. node: property,
  1103. path: nextPath,
  1104. type: READ,
  1105. info: nextTraceMap[READ],
  1106. };
  1107. }
  1108. yield* this._iterateLhsReferences(
  1109. property.value,
  1110. nextPath,
  1111. nextTraceMap
  1112. );
  1113. }
  1114. return
  1115. }
  1116. if (patternNode.type === "AssignmentPattern") {
  1117. yield* this._iterateLhsReferences(patternNode.left, path, traceMap);
  1118. }
  1119. }
  1120. /**
  1121. * Iterate the references for a given ModuleSpecifier node.
  1122. * @param {Node} specifierNode The ModuleSpecifier node to iterate references.
  1123. * @param {string[]} path The current path.
  1124. * @param {object} traceMap The trace map.
  1125. * @returns {IterableIterator<{node:Node,path:string[],type:symbol,info:any}>} The iterator to iterate references.
  1126. */
  1127. *_iterateImportReferences(specifierNode, path, traceMap) {
  1128. const type = specifierNode.type;
  1129. if (type === "ImportSpecifier" || type === "ImportDefaultSpecifier") {
  1130. const key =
  1131. type === "ImportDefaultSpecifier"
  1132. ? "default"
  1133. : specifierNode.imported.name;
  1134. if (!has(traceMap, key)) {
  1135. return
  1136. }
  1137. path = path.concat(key); //eslint-disable-line no-param-reassign
  1138. const nextTraceMap = traceMap[key];
  1139. if (nextTraceMap[READ]) {
  1140. yield {
  1141. node: specifierNode,
  1142. path,
  1143. type: READ,
  1144. info: nextTraceMap[READ],
  1145. };
  1146. }
  1147. yield* this._iterateVariableReferences(
  1148. findVariable(this.globalScope, specifierNode.local),
  1149. path,
  1150. nextTraceMap,
  1151. false
  1152. );
  1153. return
  1154. }
  1155. if (type === "ImportNamespaceSpecifier") {
  1156. yield* this._iterateVariableReferences(
  1157. findVariable(this.globalScope, specifierNode.local),
  1158. path,
  1159. traceMap,
  1160. false
  1161. );
  1162. return
  1163. }
  1164. if (type === "ExportSpecifier") {
  1165. const key = specifierNode.local.name;
  1166. if (!has(traceMap, key)) {
  1167. return
  1168. }
  1169. path = path.concat(key); //eslint-disable-line no-param-reassign
  1170. const nextTraceMap = traceMap[key];
  1171. if (nextTraceMap[READ]) {
  1172. yield {
  1173. node: specifierNode,
  1174. path,
  1175. type: READ,
  1176. info: nextTraceMap[READ],
  1177. };
  1178. }
  1179. }
  1180. }
  1181. }
  1182. ReferenceTracker.READ = READ;
  1183. ReferenceTracker.CALL = CALL;
  1184. ReferenceTracker.CONSTRUCT = CONSTRUCT;
  1185. ReferenceTracker.ESM = ESM;
  1186. /**
  1187. * This is a predicate function for Array#filter.
  1188. * @param {string} name A name part.
  1189. * @param {number} index The index of the name.
  1190. * @returns {boolean} `false` if it's default.
  1191. */
  1192. function exceptDefault(name, index) {
  1193. return !(index === 1 && name === "default")
  1194. }
  1195. var index = {
  1196. CALL,
  1197. CONSTRUCT,
  1198. ESM,
  1199. findVariable,
  1200. getFunctionHeadLocation,
  1201. getFunctionNameWithKind,
  1202. getInnermostScope,
  1203. getPropertyName,
  1204. getStaticValue,
  1205. getStringIfConstant,
  1206. isArrowToken,
  1207. isClosingBraceToken,
  1208. isClosingBracketToken,
  1209. isClosingParenToken,
  1210. isColonToken,
  1211. isCommaToken,
  1212. isCommentToken,
  1213. isNotArrowToken,
  1214. isNotClosingBraceToken,
  1215. isNotClosingBracketToken,
  1216. isNotClosingParenToken,
  1217. isNotColonToken,
  1218. isNotCommaToken,
  1219. isNotCommentToken,
  1220. isNotOpeningBraceToken,
  1221. isNotOpeningBracketToken,
  1222. isNotOpeningParenToken,
  1223. isNotSemicolonToken,
  1224. isOpeningBraceToken,
  1225. isOpeningBracketToken,
  1226. isOpeningParenToken,
  1227. isSemicolonToken,
  1228. PatternMatcher,
  1229. READ,
  1230. ReferenceTracker,
  1231. };
  1232. exports.default = index;
  1233. exports.CALL = CALL;
  1234. exports.CONSTRUCT = CONSTRUCT;
  1235. exports.ESM = ESM;
  1236. exports.findVariable = findVariable;
  1237. exports.getFunctionHeadLocation = getFunctionHeadLocation;
  1238. exports.getFunctionNameWithKind = getFunctionNameWithKind;
  1239. exports.getInnermostScope = getInnermostScope;
  1240. exports.getPropertyName = getPropertyName;
  1241. exports.getStaticValue = getStaticValue;
  1242. exports.getStringIfConstant = getStringIfConstant;
  1243. exports.isArrowToken = isArrowToken;
  1244. exports.isClosingBraceToken = isClosingBraceToken;
  1245. exports.isClosingBracketToken = isClosingBracketToken;
  1246. exports.isClosingParenToken = isClosingParenToken;
  1247. exports.isColonToken = isColonToken;
  1248. exports.isCommaToken = isCommaToken;
  1249. exports.isCommentToken = isCommentToken;
  1250. exports.isNotArrowToken = isNotArrowToken;
  1251. exports.isNotClosingBraceToken = isNotClosingBraceToken;
  1252. exports.isNotClosingBracketToken = isNotClosingBracketToken;
  1253. exports.isNotClosingParenToken = isNotClosingParenToken;
  1254. exports.isNotColonToken = isNotColonToken;
  1255. exports.isNotCommaToken = isNotCommaToken;
  1256. exports.isNotCommentToken = isNotCommentToken;
  1257. exports.isNotOpeningBraceToken = isNotOpeningBraceToken;
  1258. exports.isNotOpeningBracketToken = isNotOpeningBracketToken;
  1259. exports.isNotOpeningParenToken = isNotOpeningParenToken;
  1260. exports.isNotSemicolonToken = isNotSemicolonToken;
  1261. exports.isOpeningBraceToken = isOpeningBraceToken;
  1262. exports.isOpeningBracketToken = isOpeningBracketToken;
  1263. exports.isOpeningParenToken = isOpeningParenToken;
  1264. exports.isSemicolonToken = isSemicolonToken;
  1265. exports.PatternMatcher = PatternMatcher;
  1266. exports.READ = READ;
  1267. exports.ReferenceTracker = ReferenceTracker;
  1268. //# sourceMappingURL=index.js.map