minor adjustment to readme
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-prettier / node_modules / eslint / lib / rules / camelcase.js
1 /**
2  * @fileoverview Rule to flag non-camelcased identifiers
3  * @author Nicholas C. Zakas
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: "enforce camelcase naming convention",
18             category: "Stylistic Issues",
19             recommended: false,
20             url: "https://eslint.org/docs/rules/camelcase"
21         },
22
23         schema: [
24             {
25                 type: "object",
26                 properties: {
27                     ignoreDestructuring: {
28                         type: "boolean",
29                         default: false
30                     },
31                     ignoreImports: {
32                         type: "boolean",
33                         default: false
34                     },
35                     properties: {
36                         enum: ["always", "never"]
37                     },
38                     allow: {
39                         type: "array",
40                         items: [
41                             {
42                                 type: "string"
43                             }
44                         ],
45                         minItems: 0,
46                         uniqueItems: true
47                     }
48                 },
49                 additionalProperties: false
50             }
51         ],
52
53         messages: {
54             notCamelCase: "Identifier '{{name}}' is not in camel case."
55         }
56     },
57
58     create(context) {
59
60         const options = context.options[0] || {};
61         let properties = options.properties || "";
62         const ignoreDestructuring = options.ignoreDestructuring;
63         const ignoreImports = options.ignoreImports;
64         const allow = options.allow || [];
65
66         if (properties !== "always" && properties !== "never") {
67             properties = "always";
68         }
69
70         //--------------------------------------------------------------------------
71         // Helpers
72         //--------------------------------------------------------------------------
73
74         // contains reported nodes to avoid reporting twice on destructuring with shorthand notation
75         const reported = [];
76         const ALLOWED_PARENT_TYPES = new Set(["CallExpression", "NewExpression"]);
77
78         /**
79          * Checks if a string contains an underscore and isn't all upper-case
80          * @param {string} name The string to check.
81          * @returns {boolean} if the string is underscored
82          * @private
83          */
84         function isUnderscored(name) {
85
86             // if there's an underscore, it might be A_CONSTANT, which is okay
87             return name.includes("_") && name !== name.toUpperCase();
88         }
89
90         /**
91          * Checks if a string match the ignore list
92          * @param {string} name The string to check.
93          * @returns {boolean} if the string is ignored
94          * @private
95          */
96         function isAllowed(name) {
97             return allow.some(
98                 entry => name === entry || name.match(new RegExp(entry, "u"))
99             );
100         }
101
102         /**
103          * Checks if a parent of a node is an ObjectPattern.
104          * @param {ASTNode} node The node to check.
105          * @returns {boolean} if the node is inside an ObjectPattern
106          * @private
107          */
108         function isInsideObjectPattern(node) {
109             let current = node;
110
111             while (current) {
112                 const parent = current.parent;
113
114                 if (parent && parent.type === "Property" && parent.computed && parent.key === current) {
115                     return false;
116                 }
117
118                 if (current.type === "ObjectPattern") {
119                     return true;
120                 }
121
122                 current = parent;
123             }
124
125             return false;
126         }
127
128         /**
129          * Reports an AST node as a rule violation.
130          * @param {ASTNode} node The node to report.
131          * @returns {void}
132          * @private
133          */
134         function report(node) {
135             if (!reported.includes(node)) {
136                 reported.push(node);
137                 context.report({ node, messageId: "notCamelCase", data: { name: node.name } });
138             }
139         }
140
141         return {
142
143             Identifier(node) {
144
145                 /*
146                  * Leading and trailing underscores are commonly used to flag
147                  * private/protected identifiers, strip them before checking if underscored
148                  */
149                 const name = node.name,
150                     nameIsUnderscored = isUnderscored(name.replace(/^_+|_+$/gu, "")),
151                     effectiveParent = (node.parent.type === "MemberExpression") ? node.parent.parent : node.parent;
152
153                 // First, we ignore the node if it match the ignore list
154                 if (isAllowed(name)) {
155                     return;
156                 }
157
158                 // MemberExpressions get special rules
159                 if (node.parent.type === "MemberExpression") {
160
161                     // "never" check properties
162                     if (properties === "never") {
163                         return;
164                     }
165
166                     // Always report underscored object names
167                     if (node.parent.object.type === "Identifier" && node.parent.object.name === node.name && nameIsUnderscored) {
168                         report(node);
169
170                     // Report AssignmentExpressions only if they are the left side of the assignment
171                     } else if (effectiveParent.type === "AssignmentExpression" && nameIsUnderscored && (effectiveParent.right.type !== "MemberExpression" || effectiveParent.left.type === "MemberExpression" && effectiveParent.left.property.name === node.name)) {
172                         report(node);
173                     }
174
175                 /*
176                  * Properties have their own rules, and
177                  * AssignmentPattern nodes can be treated like Properties:
178                  * e.g.: const { no_camelcased = false } = bar;
179                  */
180                 } else if (node.parent.type === "Property" || node.parent.type === "AssignmentPattern") {
181
182                     if (node.parent.parent && node.parent.parent.type === "ObjectPattern") {
183                         if (node.parent.shorthand && node.parent.value.left && nameIsUnderscored) {
184                             report(node);
185                         }
186
187                         const assignmentKeyEqualsValue = node.parent.key.name === node.parent.value.name;
188
189                         if (isUnderscored(name) && node.parent.computed) {
190                             report(node);
191                         }
192
193                         // prevent checking righthand side of destructured object
194                         if (node.parent.key === node && node.parent.value !== node) {
195                             return;
196                         }
197
198                         const valueIsUnderscored = node.parent.value.name && nameIsUnderscored;
199
200                         // ignore destructuring if the option is set, unless a new identifier is created
201                         if (valueIsUnderscored && !(assignmentKeyEqualsValue && ignoreDestructuring)) {
202                             report(node);
203                         }
204                     }
205
206                     // "never" check properties or always ignore destructuring
207                     if (properties === "never" || (ignoreDestructuring && isInsideObjectPattern(node))) {
208                         return;
209                     }
210
211                     // don't check right hand side of AssignmentExpression to prevent duplicate warnings
212                     if (nameIsUnderscored && !ALLOWED_PARENT_TYPES.has(effectiveParent.type) && !(node.parent.right === node)) {
213                         report(node);
214                     }
215
216                 // Check if it's an import specifier
217                 } else if (["ImportSpecifier", "ImportNamespaceSpecifier", "ImportDefaultSpecifier"].includes(node.parent.type)) {
218
219                     if (node.parent.type === "ImportSpecifier" && ignoreImports) {
220                         return;
221                     }
222
223                     // Report only if the local imported identifier is underscored
224                     if (
225                         node.parent.local &&
226                         node.parent.local.name === node.name &&
227                         nameIsUnderscored
228                     ) {
229                         report(node);
230                     }
231
232                 // Report anything that is underscored that isn't a CallExpression
233                 } else if (nameIsUnderscored && !ALLOWED_PARENT_TYPES.has(effectiveParent.type)) {
234                     report(node);
235                 }
236             }
237
238         };
239
240     }
241 };