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 | type: "suggestion",
|
76 |
|
77 | docs: {
|
78 | description: "enforce getter and setter pairs in objects",
|
79 | category: "Best Practices",
|
80 | recommended: false,
|
81 | url: "https://eslint.org/docs/rules/accessor-pairs"
|
82 | },
|
83 |
|
84 | schema: [{
|
85 | type: "object",
|
86 | properties: {
|
87 | getWithoutSet: {
|
88 | type: "boolean",
|
89 | default: false
|
90 | },
|
91 | setWithoutGet: {
|
92 | type: "boolean",
|
93 | default: true
|
94 | }
|
95 | },
|
96 | additionalProperties: false
|
97 | }],
|
98 |
|
99 | messages: {
|
100 | getter: "Getter is not present.",
|
101 | setter: "Setter is not present."
|
102 | }
|
103 | },
|
104 | create(context) {
|
105 | const config = context.options[0] || {};
|
106 | const checkGetWithoutSet = config.getWithoutSet === true;
|
107 | const checkSetWithoutGet = config.setWithoutGet !== false;
|
108 |
|
109 | /**
|
110 | * Checks a object expression to see if it has setter and getter both present or none.
|
111 | * @param {ASTNode} node The node to check.
|
112 | * @returns {void}
|
113 | * @private
|
114 | */
|
115 | function checkLonelySetGet(node) {
|
116 | let isSetPresent = false;
|
117 | let isGetPresent = false;
|
118 | const isDescriptor = isPropertyDescriptor(node);
|
119 |
|
120 | for (let i = 0, end = node.properties.length; i < end; i++) {
|
121 | const property = node.properties[i];
|
122 |
|
123 | let propToCheck = "";
|
124 |
|
125 | if (property.kind === "init") {
|
126 | if (isDescriptor && !property.computed) {
|
127 | propToCheck = property.key.name;
|
128 | }
|
129 | } else {
|
130 | propToCheck = property.kind;
|
131 | }
|
132 |
|
133 | switch (propToCheck) {
|
134 | case "set":
|
135 | isSetPresent = true;
|
136 | break;
|
137 |
|
138 | case "get":
|
139 | isGetPresent = true;
|
140 | break;
|
141 |
|
142 | default:
|
143 |
|
144 | // Do nothing
|
145 | }
|
146 |
|
147 | if (isSetPresent && isGetPresent) {
|
148 | break;
|
149 | }
|
150 | }
|
151 |
|
152 | if (checkSetWithoutGet && isSetPresent && !isGetPresent) {
|
153 | context.report({ node, messageId: "getter" });
|
154 | } else if (checkGetWithoutSet && isGetPresent && !isSetPresent) {
|
155 | context.report({ node, messageId: "setter" });
|
156 | }
|
157 | }
|
158 |
|
159 | return {
|
160 | ObjectExpression(node) {
|
161 | if (checkSetWithoutGet || checkGetWithoutSet) {
|
162 | checkLonelySetGet(node);
|
163 | }
|
164 | }
|
165 | };
|
166 | }
|
167 | };
|