1 | /**
|
2 | * @fileoverview Rule to flag wrapping non-iife in parens
|
3 | * @author Gyandeep Singh
|
4 | */
|
5 |
|
6 | ;
|
7 |
|
8 | //------------------------------------------------------------------------------
|
9 | // Helpers
|
10 | //------------------------------------------------------------------------------
|
11 |
|
12 | /**
|
13 | * Checks whether or not a given node is an `Identifier` node which was named a given name.
|
14 | * @param {ASTNode} node - A node to check.
|
15 | * @param {string} name - An expected name of the node.
|
16 | * @returns {boolean} `true` if the node is an `Identifier` node which was named as expected.
|
17 | */
|
18 | function isIdentifier(node, name) {
|
19 | return node.type === "Identifier" && node.name === name;
|
20 | }
|
21 |
|
22 | /**
|
23 | * Checks whether or not a given node is an argument of a specified method call.
|
24 | * @param {ASTNode} node - A node to check.
|
25 | * @param {number} index - An expected index of the node in arguments.
|
26 | * @param {string} object - An expected name of the object of the method.
|
27 | * @param {string} property - An expected name of the method.
|
28 | * @returns {boolean} `true` if the node is an argument of the specified method call.
|
29 | */
|
30 | function isArgumentOfMethodCall(node, index, object, property) {
|
31 | const parent = node.parent;
|
32 |
|
33 | return (
|
34 | parent.type === "CallExpression" &&
|
35 | parent.callee.type === "MemberExpression" &&
|
36 | parent.callee.computed === false &&
|
37 | isIdentifier(parent.callee.object, object) &&
|
38 | isIdentifier(parent.callee.property, property) &&
|
39 | parent.arguments[index] === node
|
40 | );
|
41 | }
|
42 |
|
43 | /**
|
44 | * Checks whether or not a given node is a property descriptor.
|
45 | * @param {ASTNode} node - A node to check.
|
46 | * @returns {boolean} `true` if the node is a property descriptor.
|
47 | */
|
48 | function isPropertyDescriptor(node) {
|
49 |
|
50 | // Object.defineProperty(obj, "foo", {set: ...})
|
51 | if (isArgumentOfMethodCall(node, 2, "Object", "defineProperty") ||
|
52 | isArgumentOfMethodCall(node, 2, "Reflect", "defineProperty")
|
53 | ) {
|
54 | return true;
|
55 | }
|
56 |
|
57 | /*
|
58 | * Object.defineProperties(obj, {foo: {set: ...}})
|
59 | * Object.create(proto, {foo: {set: ...}})
|
60 | */
|
61 | const grandparent = node.parent.parent;
|
62 |
|
63 | return grandparent.type === "ObjectExpression" && (
|
64 | isArgumentOfMethodCall(grandparent, 1, "Object", "create") ||
|
65 | isArgumentOfMethodCall(grandparent, 1, "Object", "defineProperties")
|
66 | );
|
67 | }
|
68 |
|
69 | //------------------------------------------------------------------------------
|
70 | // Rule Definition
|
71 | //------------------------------------------------------------------------------
|
72 |
|
73 | module.exports = {
|
74 | meta: {
|
75 | docs: {
|
76 | description: "enforce getter and setter pairs in objects",
|
77 | category: "Best Practices",
|
78 | recommended: false,
|
79 | url: "https://eslint.org/docs/rules/accessor-pairs"
|
80 | },
|
81 | schema: [{
|
82 | type: "object",
|
83 | properties: {
|
84 | getWithoutSet: {
|
85 | type: "boolean"
|
86 | },
|
87 | setWithoutGet: {
|
88 | type: "boolean"
|
89 | }
|
90 | },
|
91 | additionalProperties: false
|
92 | }],
|
93 | messages: {
|
94 | getter: "Getter is not present.",
|
95 | setter: "Setter is not present."
|
96 | }
|
97 | },
|
98 | create(context) {
|
99 | const config = context.options[0] || {};
|
100 | const checkGetWithoutSet = config.getWithoutSet === true;
|
101 | const checkSetWithoutGet = config.setWithoutGet !== false;
|
102 |
|
103 | /**
|
104 | * Checks a object expression to see if it has setter and getter both present or none.
|
105 | * @param {ASTNode} node The node to check.
|
106 | * @returns {void}
|
107 | * @private
|
108 | */
|
109 | function checkLonelySetGet(node) {
|
110 | let isSetPresent = false;
|
111 | let isGetPresent = false;
|
112 | const isDescriptor = isPropertyDescriptor(node);
|
113 |
|
114 | for (let i = 0, end = node.properties.length; i < end; i++) {
|
115 | const property = node.properties[i];
|
116 |
|
117 | let propToCheck = "";
|
118 |
|
119 | if (property.kind === "init") {
|
120 | if (isDescriptor && !property.computed) {
|
121 | propToCheck = property.key.name;
|
122 | }
|
123 | } else {
|
124 | propToCheck = property.kind;
|
125 | }
|
126 |
|
127 | switch (propToCheck) {
|
128 | case "set":
|
129 | isSetPresent = true;
|
130 | break;
|
131 |
|
132 | case "get":
|
133 | isGetPresent = true;
|
134 | break;
|
135 |
|
136 | default:
|
137 |
|
138 | // Do nothing
|
139 | }
|
140 |
|
141 | if (isSetPresent && isGetPresent) {
|
142 | break;
|
143 | }
|
144 | }
|
145 |
|
146 | if (checkSetWithoutGet && isSetPresent && !isGetPresent) {
|
147 | context.report({ node, messageId: "getter" });
|
148 | } else if (checkGetWithoutSet && isGetPresent && !isSetPresent) {
|
149 | context.report({ node, messageId: "setter" });
|
150 | }
|
151 | }
|
152 |
|
153 | return {
|
154 | ObjectExpression(node) {
|
155 | if (checkSetWithoutGet || checkGetWithoutSet) {
|
156 | checkLonelySetGet(node);
|
157 | }
|
158 | }
|
159 | };
|
160 | }
|
161 | };
|