.gitignore added
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-prettier / node_modules / eslint / lib / rules / id-match.js
1 /**
2  * @fileoverview Rule to flag non-matching identifiers
3  * @author Matthieu Larcher
4  */
5
6 "use strict";
7
8 //------------------------------------------------------------------------------
9 // Rule Definition
10 //------------------------------------------------------------------------------
11
12 module.exports = {
13     meta: {
14         type: "suggestion",
15
16         docs: {
17             description: "require identifiers to match a specified regular expression",
18             category: "Stylistic Issues",
19             recommended: false,
20             url: "https://eslint.org/docs/rules/id-match"
21         },
22
23         schema: [
24             {
25                 type: "string"
26             },
27             {
28                 type: "object",
29                 properties: {
30                     properties: {
31                         type: "boolean",
32                         default: false
33                     },
34                     onlyDeclarations: {
35                         type: "boolean",
36                         default: false
37                     },
38                     ignoreDestructuring: {
39                         type: "boolean",
40                         default: false
41                     }
42                 },
43                 additionalProperties: false
44             }
45         ],
46         messages: {
47             notMatch: "Identifier '{{name}}' does not match the pattern '{{pattern}}'."
48         }
49     },
50
51     create(context) {
52
53         //--------------------------------------------------------------------------
54         // Options
55         //--------------------------------------------------------------------------
56         const pattern = context.options[0] || "^.+$",
57             regexp = new RegExp(pattern, "u");
58
59         const options = context.options[1] || {},
60             properties = !!options.properties,
61             onlyDeclarations = !!options.onlyDeclarations,
62             ignoreDestructuring = !!options.ignoreDestructuring;
63
64         //--------------------------------------------------------------------------
65         // Helpers
66         //--------------------------------------------------------------------------
67
68         // contains reported nodes to avoid reporting twice on destructuring with shorthand notation
69         const reported = new Map();
70         const ALLOWED_PARENT_TYPES = new Set(["CallExpression", "NewExpression"]);
71         const DECLARATION_TYPES = new Set(["FunctionDeclaration", "VariableDeclarator"]);
72         const IMPORT_TYPES = new Set(["ImportSpecifier", "ImportNamespaceSpecifier", "ImportDefaultSpecifier"]);
73
74         /**
75          * Checks if a string matches the provided pattern
76          * @param {string} name The string to check.
77          * @returns {boolean} if the string is a match
78          * @private
79          */
80         function isInvalid(name) {
81             return !regexp.test(name);
82         }
83
84         /**
85          * Checks if a parent of a node is an ObjectPattern.
86          * @param {ASTNode} node The node to check.
87          * @returns {boolean} if the node is inside an ObjectPattern
88          * @private
89          */
90         function isInsideObjectPattern(node) {
91             let { parent } = node;
92
93             while (parent) {
94                 if (parent.type === "ObjectPattern") {
95                     return true;
96                 }
97
98                 parent = parent.parent;
99             }
100
101             return false;
102         }
103
104         /**
105          * Verifies if we should report an error or not based on the effective
106          * parent node and the identifier name.
107          * @param {ASTNode} effectiveParent The effective parent node of the node to be reported
108          * @param {string} name The identifier name of the identifier node
109          * @returns {boolean} whether an error should be reported or not
110          */
111         function shouldReport(effectiveParent, name) {
112             return (!onlyDeclarations || DECLARATION_TYPES.has(effectiveParent.type)) &&
113                 !ALLOWED_PARENT_TYPES.has(effectiveParent.type) && isInvalid(name);
114         }
115
116         /**
117          * Reports an AST node as a rule violation.
118          * @param {ASTNode} node The node to report.
119          * @returns {void}
120          * @private
121          */
122         function report(node) {
123             if (!reported.has(node)) {
124                 context.report({
125                     node,
126                     messageId: "notMatch",
127                     data: {
128                         name: node.name,
129                         pattern
130                     }
131                 });
132                 reported.set(node, true);
133             }
134         }
135
136         return {
137
138             Identifier(node) {
139                 const name = node.name,
140                     parent = node.parent,
141                     effectiveParent = (parent.type === "MemberExpression") ? parent.parent : parent;
142
143                 if (parent.type === "MemberExpression") {
144
145                     if (!properties) {
146                         return;
147                     }
148
149                     // Always check object names
150                     if (parent.object.type === "Identifier" &&
151                         parent.object.name === name) {
152                         if (isInvalid(name)) {
153                             report(node);
154                         }
155
156                     // Report AssignmentExpressions left side's assigned variable id
157                     } else if (effectiveParent.type === "AssignmentExpression" &&
158                         effectiveParent.left.type === "MemberExpression" &&
159                         effectiveParent.left.property.name === node.name) {
160                         if (isInvalid(name)) {
161                             report(node);
162                         }
163
164                     // Report AssignmentExpressions only if they are the left side of the assignment
165                     } else if (effectiveParent.type === "AssignmentExpression" && effectiveParent.right.type !== "MemberExpression") {
166                         if (isInvalid(name)) {
167                             report(node);
168                         }
169                     }
170
171                 /*
172                  * Properties have their own rules, and
173                  * AssignmentPattern nodes can be treated like Properties:
174                  * e.g.: const { no_camelcased = false } = bar;
175                  */
176                 } else if (parent.type === "Property" || parent.type === "AssignmentPattern") {
177
178                     if (parent.parent && parent.parent.type === "ObjectPattern") {
179                         if (parent.shorthand && parent.value.left && isInvalid(name)) {
180
181                             report(node);
182                         }
183
184                         const assignmentKeyEqualsValue = parent.key.name === parent.value.name;
185
186                         // prevent checking righthand side of destructured object
187                         if (!assignmentKeyEqualsValue && parent.key === node) {
188                             return;
189                         }
190
191                         const valueIsInvalid = parent.value.name && isInvalid(name);
192
193                         // ignore destructuring if the option is set, unless a new identifier is created
194                         if (valueIsInvalid && !(assignmentKeyEqualsValue && ignoreDestructuring)) {
195                             report(node);
196                         }
197                     }
198
199                     // never check properties or always ignore destructuring
200                     if (!properties || (ignoreDestructuring && isInsideObjectPattern(node))) {
201                         return;
202                     }
203
204                     // don't check right hand side of AssignmentExpression to prevent duplicate warnings
205                     if (parent.right !== node && shouldReport(effectiveParent, name)) {
206                         report(node);
207                     }
208
209                 // Check if it's an import specifier
210                 } else if (IMPORT_TYPES.has(parent.type)) {
211
212                     // Report only if the local imported identifier is invalid
213                     if (parent.local && parent.local.name === node.name && isInvalid(name)) {
214                         report(node);
215                     }
216
217                 // Report anything that is invalid that isn't a CallExpression
218                 } else if (shouldReport(effectiveParent, name)) {
219                     report(node);
220                 }
221             }
222
223         };
224
225     }
226 };