UNPKG

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