is-same-reference.js 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. 'use strict';
  2. const {getStaticValue} = require('eslint-utils');
  3. // Copied from https://github.com/eslint/eslint/blob/c3e9accce2f61b04ab699fd37c90703305281aa3/lib/rules/utils/ast-utils.js#L379
  4. /**
  5. Gets the property name of a given node.
  6. The node can be a MemberExpression, a Property, or a MethodDefinition.
  7. If the name is dynamic, this returns `null`.
  8. For examples:
  9. a.b // => "b"
  10. a["b"] // => "b"
  11. a['b'] // => "b"
  12. a[`b`] // => "b"
  13. a[100] // => "100"
  14. a[b] // => null
  15. a["a" + "b"] // => null
  16. a[tag`b`] // => null
  17. a[`${b}`] // => null
  18. let a = {b: 1} // => "b"
  19. let a = {["b"]: 1} // => "b"
  20. let a = {['b']: 1} // => "b"
  21. let a = {[`b`]: 1} // => "b"
  22. let a = {[100]: 1} // => "100"
  23. let a = {[b]: 1} // => null
  24. let a = {["a" + "b"]: 1} // => null
  25. let a = {[tag`b`]: 1} // => null
  26. let a = {[`${b}`]: 1} // => null
  27. @param {ASTNode} node The node to get.
  28. @returns {string|undefined} The property name if static. Otherwise, undefined.
  29. */
  30. function getStaticPropertyName(node) {
  31. let property;
  32. switch (node && node.type) {
  33. case 'MemberExpression':
  34. property = node.property;
  35. break;
  36. /* istanbul ignore next: Hard to test */
  37. case 'ChainExpression':
  38. return getStaticPropertyName(node.expression);
  39. /* istanbul ignore next: Only reachable when use this to get class/object member key */
  40. case 'Property':
  41. case 'MethodDefinition':
  42. /* istanbul ignore next */
  43. property = node.key;
  44. /* istanbul ignore next */
  45. break;
  46. // No default
  47. }
  48. if (property) {
  49. if (property.type === 'Identifier' && !node.computed) {
  50. return property.name;
  51. }
  52. const staticResult = getStaticValue(property);
  53. if (!staticResult) {
  54. return;
  55. }
  56. return String(staticResult.value);
  57. }
  58. }
  59. /**
  60. Check if two literal nodes are the same value.
  61. @param {ASTNode} left The Literal node to compare.
  62. @param {ASTNode} right The other Literal node to compare.
  63. @returns {boolean} `true` if the two literal nodes are the same value.
  64. */
  65. function equalLiteralValue(left, right) {
  66. // RegExp literal.
  67. if (left.regex || right.regex) {
  68. return Boolean(
  69. left.regex
  70. && right.regex
  71. && left.regex.pattern === right.regex.pattern
  72. && left.regex.flags === right.regex.flags,
  73. );
  74. }
  75. // BigInt literal.
  76. if (left.bigint || right.bigint) {
  77. return left.bigint === right.bigint;
  78. }
  79. return left.value === right.value;
  80. }
  81. /**
  82. Check if two expressions reference the same value. For example:
  83. a = a
  84. a.b = a.b
  85. a[0] = a[0]
  86. a['b'] = a['b']
  87. @param {ASTNode} left The left side of the comparison.
  88. @param {ASTNode} right The right side of the comparison.
  89. @returns {boolean} `true` if both sides match and reference the same value.
  90. */
  91. function isSameReference(left, right) {
  92. if (left.type !== right.type) {
  93. // Handle `a.b` and `a?.b` are samely.
  94. if (left.type === 'ChainExpression') {
  95. return isSameReference(left.expression, right);
  96. }
  97. if (right.type === 'ChainExpression') {
  98. return isSameReference(left, right.expression);
  99. }
  100. return false;
  101. }
  102. switch (left.type) {
  103. case 'Super':
  104. case 'ThisExpression':
  105. return true;
  106. case 'Identifier':
  107. return left.name === right.name;
  108. case 'Literal':
  109. return equalLiteralValue(left, right);
  110. case 'ChainExpression':
  111. return isSameReference(left.expression, right.expression);
  112. case 'MemberExpression': {
  113. const nameA = getStaticPropertyName(left);
  114. // X.y = x["y"]
  115. return (
  116. typeof nameA !== 'undefined'
  117. && isSameReference(left.object, right.object)
  118. && nameA === getStaticPropertyName(right)
  119. );
  120. }
  121. default:
  122. return false;
  123. }
  124. }
  125. module.exports = isSameReference;