source: imaps-frontend/node_modules/eslint/lib/rules/accessor-pairs.js

main
Last change on this file was d565449, checked in by stefan toskovski <stefantoska84@…>, 4 weeks ago

Update repo after prototype presentation

  • Property mode set to 100644
File size: 11.7 KB
Line 
1/**
2 * @fileoverview Rule to enforce getter and setter pairs in objects and classes.
3 * @author Gyandeep Singh
4 */
5
6"use strict";
7
8//------------------------------------------------------------------------------
9// Requirements
10//------------------------------------------------------------------------------
11
12const astUtils = require("./utils/ast-utils");
13
14//------------------------------------------------------------------------------
15// Typedefs
16//------------------------------------------------------------------------------
17
18/**
19 * Property name if it can be computed statically, otherwise the list of the tokens of the key node.
20 * @typedef {string|Token[]} Key
21 */
22
23/**
24 * Accessor nodes with the same key.
25 * @typedef {Object} AccessorData
26 * @property {Key} key Accessor's key
27 * @property {ASTNode[]} getters List of getter nodes.
28 * @property {ASTNode[]} setters List of setter nodes.
29 */
30
31//------------------------------------------------------------------------------
32// Helpers
33//------------------------------------------------------------------------------
34
35/**
36 * Checks whether or not the given lists represent the equal tokens in the same order.
37 * Tokens are compared by their properties, not by instance.
38 * @param {Token[]} left First list of tokens.
39 * @param {Token[]} right Second list of tokens.
40 * @returns {boolean} `true` if the lists have same tokens.
41 */
42function areEqualTokenLists(left, right) {
43 if (left.length !== right.length) {
44 return false;
45 }
46
47 for (let i = 0; i < left.length; i++) {
48 const leftToken = left[i],
49 rightToken = right[i];
50
51 if (leftToken.type !== rightToken.type || leftToken.value !== rightToken.value) {
52 return false;
53 }
54 }
55
56 return true;
57}
58
59/**
60 * Checks whether or not the given keys are equal.
61 * @param {Key} left First key.
62 * @param {Key} right Second key.
63 * @returns {boolean} `true` if the keys are equal.
64 */
65function areEqualKeys(left, right) {
66 if (typeof left === "string" && typeof right === "string") {
67
68 // Statically computed names.
69 return left === right;
70 }
71 if (Array.isArray(left) && Array.isArray(right)) {
72
73 // Token lists.
74 return areEqualTokenLists(left, right);
75 }
76
77 return false;
78}
79
80/**
81 * Checks whether or not a given node is of an accessor kind ('get' or 'set').
82 * @param {ASTNode} node A node to check.
83 * @returns {boolean} `true` if the node is of an accessor kind.
84 */
85function isAccessorKind(node) {
86 return node.kind === "get" || node.kind === "set";
87}
88
89/**
90 * Checks whether or not a given node is an argument of a specified method call.
91 * @param {ASTNode} node A node to check.
92 * @param {number} index An expected index of the node in arguments.
93 * @param {string} object An expected name of the object of the method.
94 * @param {string} property An expected name of the method.
95 * @returns {boolean} `true` if the node is an argument of the specified method call.
96 */
97function isArgumentOfMethodCall(node, index, object, property) {
98 const parent = node.parent;
99
100 return (
101 parent.type === "CallExpression" &&
102 astUtils.isSpecificMemberAccess(parent.callee, object, property) &&
103 parent.arguments[index] === node
104 );
105}
106
107/**
108 * Checks whether or not a given node is a property descriptor.
109 * @param {ASTNode} node A node to check.
110 * @returns {boolean} `true` if the node is a property descriptor.
111 */
112function isPropertyDescriptor(node) {
113
114 // Object.defineProperty(obj, "foo", {set: ...})
115 if (isArgumentOfMethodCall(node, 2, "Object", "defineProperty") ||
116 isArgumentOfMethodCall(node, 2, "Reflect", "defineProperty")
117 ) {
118 return true;
119 }
120
121 /*
122 * Object.defineProperties(obj, {foo: {set: ...}})
123 * Object.create(proto, {foo: {set: ...}})
124 */
125 const grandparent = node.parent.parent;
126
127 return grandparent.type === "ObjectExpression" && (
128 isArgumentOfMethodCall(grandparent, 1, "Object", "create") ||
129 isArgumentOfMethodCall(grandparent, 1, "Object", "defineProperties")
130 );
131}
132
133//------------------------------------------------------------------------------
134// Rule Definition
135//------------------------------------------------------------------------------
136
137/** @type {import('../shared/types').Rule} */
138module.exports = {
139 meta: {
140 type: "suggestion",
141
142 docs: {
143 description: "Enforce getter and setter pairs in objects and classes",
144 recommended: false,
145 url: "https://eslint.org/docs/latest/rules/accessor-pairs"
146 },
147
148 schema: [{
149 type: "object",
150 properties: {
151 getWithoutSet: {
152 type: "boolean",
153 default: false
154 },
155 setWithoutGet: {
156 type: "boolean",
157 default: true
158 },
159 enforceForClassMembers: {
160 type: "boolean",
161 default: true
162 }
163 },
164 additionalProperties: false
165 }],
166
167 messages: {
168 missingGetterInPropertyDescriptor: "Getter is not present in property descriptor.",
169 missingSetterInPropertyDescriptor: "Setter is not present in property descriptor.",
170 missingGetterInObjectLiteral: "Getter is not present for {{ name }}.",
171 missingSetterInObjectLiteral: "Setter is not present for {{ name }}.",
172 missingGetterInClass: "Getter is not present for class {{ name }}.",
173 missingSetterInClass: "Setter is not present for class {{ name }}."
174 }
175 },
176 create(context) {
177 const config = context.options[0] || {};
178 const checkGetWithoutSet = config.getWithoutSet === true;
179 const checkSetWithoutGet = config.setWithoutGet !== false;
180 const enforceForClassMembers = config.enforceForClassMembers !== false;
181 const sourceCode = context.sourceCode;
182
183 /**
184 * Reports the given node.
185 * @param {ASTNode} node The node to report.
186 * @param {string} messageKind "missingGetter" or "missingSetter".
187 * @returns {void}
188 * @private
189 */
190 function report(node, messageKind) {
191 if (node.type === "Property") {
192 context.report({
193 node,
194 messageId: `${messageKind}InObjectLiteral`,
195 loc: astUtils.getFunctionHeadLoc(node.value, sourceCode),
196 data: { name: astUtils.getFunctionNameWithKind(node.value) }
197 });
198 } else if (node.type === "MethodDefinition") {
199 context.report({
200 node,
201 messageId: `${messageKind}InClass`,
202 loc: astUtils.getFunctionHeadLoc(node.value, sourceCode),
203 data: { name: astUtils.getFunctionNameWithKind(node.value) }
204 });
205 } else {
206 context.report({
207 node,
208 messageId: `${messageKind}InPropertyDescriptor`
209 });
210 }
211 }
212
213 /**
214 * Reports each of the nodes in the given list using the same messageId.
215 * @param {ASTNode[]} nodes Nodes to report.
216 * @param {string} messageKind "missingGetter" or "missingSetter".
217 * @returns {void}
218 * @private
219 */
220 function reportList(nodes, messageKind) {
221 for (const node of nodes) {
222 report(node, messageKind);
223 }
224 }
225
226 /**
227 * Checks accessor pairs in the given list of nodes.
228 * @param {ASTNode[]} nodes The list to check.
229 * @returns {void}
230 * @private
231 */
232 function checkList(nodes) {
233 const accessors = [];
234 let found = false;
235
236 for (let i = 0; i < nodes.length; i++) {
237 const node = nodes[i];
238
239 if (isAccessorKind(node)) {
240
241 // Creates a new `AccessorData` object for the given getter or setter node.
242 const name = astUtils.getStaticPropertyName(node);
243 const key = (name !== null) ? name : sourceCode.getTokens(node.key);
244
245 // Merges the given `AccessorData` object into the given accessors list.
246 for (let j = 0; j < accessors.length; j++) {
247 const accessor = accessors[j];
248
249 if (areEqualKeys(accessor.key, key)) {
250 accessor.getters.push(...node.kind === "get" ? [node] : []);
251 accessor.setters.push(...node.kind === "set" ? [node] : []);
252 found = true;
253 break;
254 }
255 }
256 if (!found) {
257 accessors.push({
258 key,
259 getters: node.kind === "get" ? [node] : [],
260 setters: node.kind === "set" ? [node] : []
261 });
262 }
263 found = false;
264 }
265 }
266
267 for (const { getters, setters } of accessors) {
268 if (checkSetWithoutGet && setters.length && !getters.length) {
269 reportList(setters, "missingGetter");
270 }
271 if (checkGetWithoutSet && getters.length && !setters.length) {
272 reportList(getters, "missingSetter");
273 }
274 }
275 }
276
277 /**
278 * Checks accessor pairs in an object literal.
279 * @param {ASTNode} node `ObjectExpression` node to check.
280 * @returns {void}
281 * @private
282 */
283 function checkObjectLiteral(node) {
284 checkList(node.properties.filter(p => p.type === "Property"));
285 }
286
287 /**
288 * Checks accessor pairs in a property descriptor.
289 * @param {ASTNode} node Property descriptor `ObjectExpression` node to check.
290 * @returns {void}
291 * @private
292 */
293 function checkPropertyDescriptor(node) {
294 const namesToCheck = new Set(node.properties
295 .filter(p => p.type === "Property" && p.kind === "init" && !p.computed)
296 .map(({ key }) => key.name));
297
298 const hasGetter = namesToCheck.has("get");
299 const hasSetter = namesToCheck.has("set");
300
301 if (checkSetWithoutGet && hasSetter && !hasGetter) {
302 report(node, "missingGetter");
303 }
304 if (checkGetWithoutSet && hasGetter && !hasSetter) {
305 report(node, "missingSetter");
306 }
307 }
308
309 /**
310 * Checks the given object expression as an object literal and as a possible property descriptor.
311 * @param {ASTNode} node `ObjectExpression` node to check.
312 * @returns {void}
313 * @private
314 */
315 function checkObjectExpression(node) {
316 checkObjectLiteral(node);
317 if (isPropertyDescriptor(node)) {
318 checkPropertyDescriptor(node);
319 }
320 }
321
322 /**
323 * Checks the given class body.
324 * @param {ASTNode} node `ClassBody` node to check.
325 * @returns {void}
326 * @private
327 */
328 function checkClassBody(node) {
329 const methodDefinitions = node.body.filter(m => m.type === "MethodDefinition");
330
331 checkList(methodDefinitions.filter(m => m.static));
332 checkList(methodDefinitions.filter(m => !m.static));
333 }
334
335 const listeners = {};
336
337 if (checkSetWithoutGet || checkGetWithoutSet) {
338 listeners.ObjectExpression = checkObjectExpression;
339 if (enforceForClassMembers) {
340 listeners.ClassBody = checkClassBody;
341 }
342 }
343
344 return listeners;
345 }
346};
Note: See TracBrowser for help on using the repository browser.