.gitignore added
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-prettier / node_modules / eslint / lib / rules / grouped-accessor-pairs.js
1 /**
2  * @fileoverview Rule to require grouped accessor pairs in object literals and classes
3  * @author Milos Djermanovic
4  */
5
6 "use strict";
7
8 //------------------------------------------------------------------------------
9 // Requirements
10 //------------------------------------------------------------------------------
11
12 const 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  */
42 function 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  */
65 function 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  */
85 function isAccessorKind(node) {
86     return node.kind === "get" || node.kind === "set";
87 }
88
89 //------------------------------------------------------------------------------
90 // Rule Definition
91 //------------------------------------------------------------------------------
92
93 module.exports = {
94     meta: {
95         type: "suggestion",
96
97         docs: {
98             description: "require grouped accessor pairs in object literals and classes",
99             category: "Best Practices",
100             recommended: false,
101             url: "https://eslint.org/docs/rules/grouped-accessor-pairs"
102         },
103
104         schema: [
105             {
106                 enum: ["anyOrder", "getBeforeSet", "setBeforeGet"]
107             }
108         ],
109
110         messages: {
111             notGrouped: "Accessor pair {{ formerName }} and {{ latterName }} should be grouped.",
112             invalidOrder: "Expected {{ latterName }} to be before {{ formerName }}."
113         }
114     },
115
116     create(context) {
117         const order = context.options[0] || "anyOrder";
118         const sourceCode = context.getSourceCode();
119
120         /**
121          * Reports the given accessor pair.
122          * @param {string} messageId messageId to report.
123          * @param {ASTNode} formerNode getter/setter node that is defined before `latterNode`.
124          * @param {ASTNode} latterNode getter/setter node that is defined after `formerNode`.
125          * @returns {void}
126          * @private
127          */
128         function report(messageId, formerNode, latterNode) {
129             context.report({
130                 node: latterNode,
131                 messageId,
132                 loc: astUtils.getFunctionHeadLoc(latterNode.value, sourceCode),
133                 data: {
134                     formerName: astUtils.getFunctionNameWithKind(formerNode.value),
135                     latterName: astUtils.getFunctionNameWithKind(latterNode.value)
136                 }
137             });
138         }
139
140         /**
141          * Creates a new `AccessorData` object for the given getter or setter node.
142          * @param {ASTNode} node A getter or setter node.
143          * @returns {AccessorData} New `AccessorData` object that contains the given node.
144          * @private
145          */
146         function createAccessorData(node) {
147             const name = astUtils.getStaticPropertyName(node);
148             const key = (name !== null) ? name : sourceCode.getTokens(node.key);
149
150             return {
151                 key,
152                 getters: node.kind === "get" ? [node] : [],
153                 setters: node.kind === "set" ? [node] : []
154             };
155         }
156
157         /**
158          * Merges the given `AccessorData` object into the given accessors list.
159          * @param {AccessorData[]} accessors The list to merge into.
160          * @param {AccessorData} accessorData The object to merge.
161          * @returns {AccessorData[]} The same instance with the merged object.
162          * @private
163          */
164         function mergeAccessorData(accessors, accessorData) {
165             const equalKeyElement = accessors.find(a => areEqualKeys(a.key, accessorData.key));
166
167             if (equalKeyElement) {
168                 equalKeyElement.getters.push(...accessorData.getters);
169                 equalKeyElement.setters.push(...accessorData.setters);
170             } else {
171                 accessors.push(accessorData);
172             }
173
174             return accessors;
175         }
176
177         /**
178          * Checks accessor pairs in the given list of nodes.
179          * @param {ASTNode[]} nodes The list to check.
180          * @param {Function} shouldCheck – Predicate that returns `true` if the node should be checked.
181          * @returns {void}
182          * @private
183          */
184         function checkList(nodes, shouldCheck) {
185             const accessors = nodes
186                 .filter(shouldCheck)
187                 .filter(isAccessorKind)
188                 .map(createAccessorData)
189                 .reduce(mergeAccessorData, []);
190
191             for (const { getters, setters } of accessors) {
192
193                 // Don't report accessor properties that have duplicate getters or setters.
194                 if (getters.length === 1 && setters.length === 1) {
195                     const [getter] = getters,
196                         [setter] = setters,
197                         getterIndex = nodes.indexOf(getter),
198                         setterIndex = nodes.indexOf(setter),
199                         formerNode = getterIndex < setterIndex ? getter : setter,
200                         latterNode = getterIndex < setterIndex ? setter : getter;
201
202                     if (Math.abs(getterIndex - setterIndex) > 1) {
203                         report("notGrouped", formerNode, latterNode);
204                     } else if (
205                         (order === "getBeforeSet" && getterIndex > setterIndex) ||
206                         (order === "setBeforeGet" && getterIndex < setterIndex)
207                     ) {
208                         report("invalidOrder", formerNode, latterNode);
209                     }
210                 }
211             }
212         }
213
214         return {
215             ObjectExpression(node) {
216                 checkList(node.properties, n => n.type === "Property");
217             },
218             ClassBody(node) {
219                 checkList(node.body, n => n.type === "MethodDefinition" && !n.static);
220                 checkList(node.body, n => n.type === "MethodDefinition" && n.static);
221             }
222         };
223     }
224 };