| /** |
| * @fileoverview A rule to disallow duplicate name in class members. |
| * @author Toru Nagashima |
| */ |
| |
| "use strict"; |
| |
| //------------------------------------------------------------------------------ |
| // Rule Definition |
| //------------------------------------------------------------------------------ |
| |
| module.exports = { |
| meta: { |
| type: "problem", |
| |
| docs: { |
| description: "disallow duplicate class members", |
| category: "ECMAScript 6", |
| recommended: true, |
| url: "https://eslint.org/docs/rules/no-dupe-class-members" |
| }, |
| |
| schema: [], |
| |
| messages: { |
| unexpected: "Duplicate name '{{name}}'." |
| } |
| }, |
| |
| create(context) { |
| let stack = []; |
| |
| /** |
| * Gets state of a given member name. |
| * @param {string} name - A name of a member. |
| * @param {boolean} isStatic - A flag which specifies that is a static member. |
| * @returns {Object} A state of a given member name. |
| * - retv.init {boolean} A flag which shows the name is declared as normal member. |
| * - retv.get {boolean} A flag which shows the name is declared as getter. |
| * - retv.set {boolean} A flag which shows the name is declared as setter. |
| */ |
| function getState(name, isStatic) { |
| const stateMap = stack[stack.length - 1]; |
| const key = `$${name}`; // to avoid "__proto__". |
| |
| if (!stateMap[key]) { |
| stateMap[key] = { |
| nonStatic: { init: false, get: false, set: false }, |
| static: { init: false, get: false, set: false } |
| }; |
| } |
| |
| return stateMap[key][isStatic ? "static" : "nonStatic"]; |
| } |
| |
| /** |
| * Gets the name text of a given node. |
| * |
| * @param {ASTNode} node - A node to get the name. |
| * @returns {string} The name text of the node. |
| */ |
| function getName(node) { |
| switch (node.type) { |
| case "Identifier": return node.name; |
| case "Literal": return String(node.value); |
| |
| /* istanbul ignore next: syntax error */ |
| default: return ""; |
| } |
| } |
| |
| return { |
| |
| // Initializes the stack of state of member declarations. |
| Program() { |
| stack = []; |
| }, |
| |
| // Initializes state of member declarations for the class. |
| ClassBody() { |
| stack.push(Object.create(null)); |
| }, |
| |
| // Disposes the state for the class. |
| "ClassBody:exit"() { |
| stack.pop(); |
| }, |
| |
| // Reports the node if its name has been declared already. |
| MethodDefinition(node) { |
| if (node.computed) { |
| return; |
| } |
| |
| const name = getName(node.key); |
| const state = getState(name, node.static); |
| let isDuplicate = false; |
| |
| if (node.kind === "get") { |
| isDuplicate = (state.init || state.get); |
| state.get = true; |
| } else if (node.kind === "set") { |
| isDuplicate = (state.init || state.set); |
| state.set = true; |
| } else { |
| isDuplicate = (state.init || state.get || state.set); |
| state.init = true; |
| } |
| |
| if (isDuplicate) { |
| context.report({ node, messageId: "unexpected", data: { name } }); |
| } |
| } |
| }; |
| } |
| }; |