no-dupe-keys.js 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. /**
  2. * @fileoverview Rule to flag use of duplicate keys in an object.
  3. * @author Ian Christian Myers
  4. */
  5. "use strict";
  6. //------------------------------------------------------------------------------
  7. // Requirements
  8. //------------------------------------------------------------------------------
  9. const astUtils = require("./utils/ast-utils");
  10. //------------------------------------------------------------------------------
  11. // Helpers
  12. //------------------------------------------------------------------------------
  13. const GET_KIND = /^(?:init|get)$/u;
  14. const SET_KIND = /^(?:init|set)$/u;
  15. /**
  16. * The class which stores properties' information of an object.
  17. */
  18. class ObjectInfo {
  19. // eslint-disable-next-line jsdoc/require-description
  20. /**
  21. * @param {ObjectInfo|null} upper The information of the outer object.
  22. * @param {ASTNode} node The ObjectExpression node of this information.
  23. */
  24. constructor(upper, node) {
  25. this.upper = upper;
  26. this.node = node;
  27. this.properties = new Map();
  28. }
  29. /**
  30. * Gets the information of the given Property node.
  31. * @param {ASTNode} node The Property node to get.
  32. * @returns {{get: boolean, set: boolean}} The information of the property.
  33. */
  34. getPropertyInfo(node) {
  35. const name = astUtils.getStaticPropertyName(node);
  36. if (!this.properties.has(name)) {
  37. this.properties.set(name, { get: false, set: false });
  38. }
  39. return this.properties.get(name);
  40. }
  41. /**
  42. * Checks whether the given property has been defined already or not.
  43. * @param {ASTNode} node The Property node to check.
  44. * @returns {boolean} `true` if the property has been defined.
  45. */
  46. isPropertyDefined(node) {
  47. const entry = this.getPropertyInfo(node);
  48. return (
  49. (GET_KIND.test(node.kind) && entry.get) ||
  50. (SET_KIND.test(node.kind) && entry.set)
  51. );
  52. }
  53. /**
  54. * Defines the given property.
  55. * @param {ASTNode} node The Property node to define.
  56. * @returns {void}
  57. */
  58. defineProperty(node) {
  59. const entry = this.getPropertyInfo(node);
  60. if (GET_KIND.test(node.kind)) {
  61. entry.get = true;
  62. }
  63. if (SET_KIND.test(node.kind)) {
  64. entry.set = true;
  65. }
  66. }
  67. }
  68. //------------------------------------------------------------------------------
  69. // Rule Definition
  70. //------------------------------------------------------------------------------
  71. module.exports = {
  72. meta: {
  73. type: "problem",
  74. docs: {
  75. description: "disallow duplicate keys in object literals",
  76. category: "Possible Errors",
  77. recommended: true,
  78. url: "https://eslint.org/docs/rules/no-dupe-keys"
  79. },
  80. schema: [],
  81. messages: {
  82. unexpected: "Duplicate key '{{name}}'."
  83. }
  84. },
  85. create(context) {
  86. let info = null;
  87. return {
  88. ObjectExpression(node) {
  89. info = new ObjectInfo(info, node);
  90. },
  91. "ObjectExpression:exit"() {
  92. info = info.upper;
  93. },
  94. Property(node) {
  95. const name = astUtils.getStaticPropertyName(node);
  96. // Skip destructuring.
  97. if (node.parent.type !== "ObjectExpression") {
  98. return;
  99. }
  100. // Skip if the name is not static.
  101. if (name === null) {
  102. return;
  103. }
  104. // Reports if the name is defined already.
  105. if (info.isPropertyDefined(node)) {
  106. context.report({
  107. node: info.node,
  108. loc: node.key.loc,
  109. messageId: "unexpected",
  110. data: { name }
  111. });
  112. }
  113. // Update info.
  114. info.defineProperty(node);
  115. }
  116. };
  117. }
  118. };