| /** |
| * @fileoverview Rule to flag wrapping non-iife in parens |
| * @author Gyandeep Singh |
| */ |
| |
| "use strict"; |
| |
| //------------------------------------------------------------------------------ |
| // Helpers |
| //------------------------------------------------------------------------------ |
| |
| /** |
| * Checks whether or not a given node is an `Identifier` node which was named a given name. |
| * @param {ASTNode} node - A node to check. |
| * @param {string} name - An expected name of the node. |
| * @returns {boolean} `true` if the node is an `Identifier` node which was named as expected. |
| */ |
| function isIdentifier(node, name) { |
| return node.type === "Identifier" && node.name === name; |
| } |
| |
| /** |
| * Checks whether or not a given node is an argument of a specified method call. |
| * @param {ASTNode} node - A node to check. |
| * @param {number} index - An expected index of the node in arguments. |
| * @param {string} object - An expected name of the object of the method. |
| * @param {string} property - An expected name of the method. |
| * @returns {boolean} `true` if the node is an argument of the specified method call. |
| */ |
| function isArgumentOfMethodCall(node, index, object, property) { |
| const parent = node.parent; |
| |
| return ( |
| parent.type === "CallExpression" && |
| parent.callee.type === "MemberExpression" && |
| parent.callee.computed === false && |
| isIdentifier(parent.callee.object, object) && |
| isIdentifier(parent.callee.property, property) && |
| parent.arguments[index] === node |
| ); |
| } |
| |
| /** |
| * Checks whether or not a given node is a property descriptor. |
| * @param {ASTNode} node - A node to check. |
| * @returns {boolean} `true` if the node is a property descriptor. |
| */ |
| function isPropertyDescriptor(node) { |
| |
| // Object.defineProperty(obj, "foo", {set: ...}) |
| if (isArgumentOfMethodCall(node, 2, "Object", "defineProperty") || |
| isArgumentOfMethodCall(node, 2, "Reflect", "defineProperty") |
| ) { |
| return true; |
| } |
| |
| /* |
| * Object.defineProperties(obj, {foo: {set: ...}}) |
| * Object.create(proto, {foo: {set: ...}}) |
| */ |
| const grandparent = node.parent.parent; |
| |
| return grandparent.type === "ObjectExpression" && ( |
| isArgumentOfMethodCall(grandparent, 1, "Object", "create") || |
| isArgumentOfMethodCall(grandparent, 1, "Object", "defineProperties") |
| ); |
| } |
| |
| //------------------------------------------------------------------------------ |
| // Rule Definition |
| //------------------------------------------------------------------------------ |
| |
| module.exports = { |
| meta: { |
| type: "suggestion", |
| |
| docs: { |
| description: "enforce getter and setter pairs in objects", |
| category: "Best Practices", |
| recommended: false, |
| url: "https://eslint.org/docs/rules/accessor-pairs" |
| }, |
| |
| schema: [{ |
| type: "object", |
| properties: { |
| getWithoutSet: { |
| type: "boolean", |
| default: false |
| }, |
| setWithoutGet: { |
| type: "boolean", |
| default: true |
| } |
| }, |
| additionalProperties: false |
| }], |
| |
| messages: { |
| getter: "Getter is not present.", |
| setter: "Setter is not present." |
| } |
| }, |
| create(context) { |
| const config = context.options[0] || {}; |
| const checkGetWithoutSet = config.getWithoutSet === true; |
| const checkSetWithoutGet = config.setWithoutGet !== false; |
| |
| /** |
| * Checks a object expression to see if it has setter and getter both present or none. |
| * @param {ASTNode} node The node to check. |
| * @returns {void} |
| * @private |
| */ |
| function checkLonelySetGet(node) { |
| let isSetPresent = false; |
| let isGetPresent = false; |
| const isDescriptor = isPropertyDescriptor(node); |
| |
| for (let i = 0, end = node.properties.length; i < end; i++) { |
| const property = node.properties[i]; |
| |
| let propToCheck = ""; |
| |
| if (property.kind === "init") { |
| if (isDescriptor && !property.computed) { |
| propToCheck = property.key.name; |
| } |
| } else { |
| propToCheck = property.kind; |
| } |
| |
| switch (propToCheck) { |
| case "set": |
| isSetPresent = true; |
| break; |
| |
| case "get": |
| isGetPresent = true; |
| break; |
| |
| default: |
| |
| // Do nothing |
| } |
| |
| if (isSetPresent && isGetPresent) { |
| break; |
| } |
| } |
| |
| if (checkSetWithoutGet && isSetPresent && !isGetPresent) { |
| context.report({ node, messageId: "getter" }); |
| } else if (checkGetWithoutSet && isGetPresent && !isSetPresent) { |
| context.report({ node, messageId: "setter" }); |
| } |
| } |
| |
| return { |
| ObjectExpression(node) { |
| if (checkSetWithoutGet || checkGetWithoutSet) { |
| checkLonelySetGet(node); |
| } |
| } |
| }; |
| } |
| }; |