123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143 |
- /**
- * @fileoverview Rule to flag use of duplicate keys in an object.
- * @author Ian Christian Myers
- */
-
- "use strict";
-
- //------------------------------------------------------------------------------
- // Requirements
- //------------------------------------------------------------------------------
-
- const astUtils = require("./utils/ast-utils");
-
- //------------------------------------------------------------------------------
- // Helpers
- //------------------------------------------------------------------------------
-
- const GET_KIND = /^(?:init|get)$/u;
- const SET_KIND = /^(?:init|set)$/u;
-
- /**
- * The class which stores properties' information of an object.
- */
- class ObjectInfo {
-
- // eslint-disable-next-line jsdoc/require-description
- /**
- * @param {ObjectInfo|null} upper The information of the outer object.
- * @param {ASTNode} node The ObjectExpression node of this information.
- */
- constructor(upper, node) {
- this.upper = upper;
- this.node = node;
- this.properties = new Map();
- }
-
- /**
- * Gets the information of the given Property node.
- * @param {ASTNode} node The Property node to get.
- * @returns {{get: boolean, set: boolean}} The information of the property.
- */
- getPropertyInfo(node) {
- const name = astUtils.getStaticPropertyName(node);
-
- if (!this.properties.has(name)) {
- this.properties.set(name, { get: false, set: false });
- }
- return this.properties.get(name);
- }
-
- /**
- * Checks whether the given property has been defined already or not.
- * @param {ASTNode} node The Property node to check.
- * @returns {boolean} `true` if the property has been defined.
- */
- isPropertyDefined(node) {
- const entry = this.getPropertyInfo(node);
-
- return (
- (GET_KIND.test(node.kind) && entry.get) ||
- (SET_KIND.test(node.kind) && entry.set)
- );
- }
-
- /**
- * Defines the given property.
- * @param {ASTNode} node The Property node to define.
- * @returns {void}
- */
- defineProperty(node) {
- const entry = this.getPropertyInfo(node);
-
- if (GET_KIND.test(node.kind)) {
- entry.get = true;
- }
- if (SET_KIND.test(node.kind)) {
- entry.set = true;
- }
- }
- }
-
- //------------------------------------------------------------------------------
- // Rule Definition
- //------------------------------------------------------------------------------
-
- module.exports = {
- meta: {
- type: "problem",
-
- docs: {
- description: "disallow duplicate keys in object literals",
- category: "Possible Errors",
- recommended: true,
- url: "https://eslint.org/docs/rules/no-dupe-keys"
- },
-
- schema: [],
-
- messages: {
- unexpected: "Duplicate key '{{name}}'."
- }
- },
-
- create(context) {
- let info = null;
-
- return {
- ObjectExpression(node) {
- info = new ObjectInfo(info, node);
- },
- "ObjectExpression:exit"() {
- info = info.upper;
- },
-
- Property(node) {
- const name = astUtils.getStaticPropertyName(node);
-
- // Skip destructuring.
- if (node.parent.type !== "ObjectExpression") {
- return;
- }
-
- // Skip if the name is not static.
- if (name === null) {
- return;
- }
-
- // Reports if the name is defined already.
- if (info.isPropertyDefined(node)) {
- context.report({
- node: info.node,
- loc: node.key.loc,
- messageId: "unexpected",
- data: { name }
- });
- }
-
- // Update info.
- info.defineProperty(node);
- }
- };
- }
- };
|