Actualizacion maquina principal
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-prettier / node_modules / eslint / lib / rules / quote-props.js
1 /**
2  * @fileoverview Rule to flag non-quoted property names in object literals.
3  * @author Mathias Bynens <http://mathiasbynens.be/>
4  */
5 "use strict";
6
7 //------------------------------------------------------------------------------
8 // Requirements
9 //------------------------------------------------------------------------------
10
11 const espree = require("espree"),
12     keywords = require("./utils/keywords");
13
14 //------------------------------------------------------------------------------
15 // Rule Definition
16 //------------------------------------------------------------------------------
17
18 module.exports = {
19     meta: {
20         type: "suggestion",
21
22         docs: {
23             description: "require quotes around object literal property names",
24             category: "Stylistic Issues",
25             recommended: false,
26             url: "https://eslint.org/docs/rules/quote-props"
27         },
28
29         schema: {
30             anyOf: [
31                 {
32                     type: "array",
33                     items: [
34                         {
35                             enum: ["always", "as-needed", "consistent", "consistent-as-needed"]
36                         }
37                     ],
38                     minItems: 0,
39                     maxItems: 1
40                 },
41                 {
42                     type: "array",
43                     items: [
44                         {
45                             enum: ["always", "as-needed", "consistent", "consistent-as-needed"]
46                         },
47                         {
48                             type: "object",
49                             properties: {
50                                 keywords: {
51                                     type: "boolean"
52                                 },
53                                 unnecessary: {
54                                     type: "boolean"
55                                 },
56                                 numbers: {
57                                     type: "boolean"
58                                 }
59                             },
60                             additionalProperties: false
61                         }
62                     ],
63                     minItems: 0,
64                     maxItems: 2
65                 }
66             ]
67         },
68
69         fixable: "code"
70     },
71
72     create(context) {
73
74         const MODE = context.options[0],
75             KEYWORDS = context.options[1] && context.options[1].keywords,
76             CHECK_UNNECESSARY = !context.options[1] || context.options[1].unnecessary !== false,
77             NUMBERS = context.options[1] && context.options[1].numbers,
78
79             MESSAGE_UNNECESSARY = "Unnecessarily quoted property '{{property}}' found.",
80             MESSAGE_UNQUOTED = "Unquoted property '{{property}}' found.",
81             MESSAGE_NUMERIC = "Unquoted number literal '{{property}}' used as key.",
82             MESSAGE_RESERVED = "Unquoted reserved word '{{property}}' used as key.",
83             sourceCode = context.getSourceCode();
84
85
86         /**
87          * Checks whether a certain string constitutes an ES3 token
88          * @param   {string} tokenStr The string to be checked.
89          * @returns {boolean} `true` if it is an ES3 token.
90          */
91         function isKeyword(tokenStr) {
92             return keywords.indexOf(tokenStr) >= 0;
93         }
94
95         /**
96          * Checks if an espree-tokenized key has redundant quotes (i.e. whether quotes are unnecessary)
97          * @param   {string} rawKey The raw key value from the source
98          * @param   {espreeTokens} tokens The espree-tokenized node key
99          * @param   {boolean} [skipNumberLiterals=false] Indicates whether number literals should be checked
100          * @returns {boolean} Whether or not a key has redundant quotes.
101          * @private
102          */
103         function areQuotesRedundant(rawKey, tokens, skipNumberLiterals) {
104             return tokens.length === 1 && tokens[0].start === 0 && tokens[0].end === rawKey.length &&
105                 (["Identifier", "Keyword", "Null", "Boolean"].indexOf(tokens[0].type) >= 0 ||
106                 (tokens[0].type === "Numeric" && !skipNumberLiterals && String(+tokens[0].value) === tokens[0].value));
107         }
108
109         /**
110          * Returns a string representation of a property node with quotes removed
111          * @param {ASTNode} key Key AST Node, which may or may not be quoted
112          * @returns {string} A replacement string for this property
113          */
114         function getUnquotedKey(key) {
115             return key.type === "Identifier" ? key.name : key.value;
116         }
117
118         /**
119          * Returns a string representation of a property node with quotes added
120          * @param {ASTNode} key Key AST Node, which may or may not be quoted
121          * @returns {string} A replacement string for this property
122          */
123         function getQuotedKey(key) {
124             if (key.type === "Literal" && typeof key.value === "string") {
125
126                 // If the key is already a string literal, don't replace the quotes with double quotes.
127                 return sourceCode.getText(key);
128             }
129
130             // Otherwise, the key is either an identifier or a number literal.
131             return `"${key.type === "Identifier" ? key.name : key.value}"`;
132         }
133
134         /**
135          * Ensures that a property's key is quoted only when necessary
136          * @param   {ASTNode} node Property AST node
137          * @returns {void}
138          */
139         function checkUnnecessaryQuotes(node) {
140             const key = node.key;
141
142             if (node.method || node.computed || node.shorthand) {
143                 return;
144             }
145
146             if (key.type === "Literal" && typeof key.value === "string") {
147                 let tokens;
148
149                 try {
150                     tokens = espree.tokenize(key.value);
151                 } catch (e) {
152                     return;
153                 }
154
155                 if (tokens.length !== 1) {
156                     return;
157                 }
158
159                 const isKeywordToken = isKeyword(tokens[0].value);
160
161                 if (isKeywordToken && KEYWORDS) {
162                     return;
163                 }
164
165                 if (CHECK_UNNECESSARY && areQuotesRedundant(key.value, tokens, NUMBERS)) {
166                     context.report({
167                         node,
168                         message: MESSAGE_UNNECESSARY,
169                         data: { property: key.value },
170                         fix: fixer => fixer.replaceText(key, getUnquotedKey(key))
171                     });
172                 }
173             } else if (KEYWORDS && key.type === "Identifier" && isKeyword(key.name)) {
174                 context.report({
175                     node,
176                     message: MESSAGE_RESERVED,
177                     data: { property: key.name },
178                     fix: fixer => fixer.replaceText(key, getQuotedKey(key))
179                 });
180             } else if (NUMBERS && key.type === "Literal" && typeof key.value === "number") {
181                 context.report({
182                     node,
183                     message: MESSAGE_NUMERIC,
184                     data: { property: key.value },
185                     fix: fixer => fixer.replaceText(key, getQuotedKey(key))
186                 });
187             }
188         }
189
190         /**
191          * Ensures that a property's key is quoted
192          * @param   {ASTNode} node Property AST node
193          * @returns {void}
194          */
195         function checkOmittedQuotes(node) {
196             const key = node.key;
197
198             if (!node.method && !node.computed && !node.shorthand && !(key.type === "Literal" && typeof key.value === "string")) {
199                 context.report({
200                     node,
201                     message: MESSAGE_UNQUOTED,
202                     data: { property: key.name || key.value },
203                     fix: fixer => fixer.replaceText(key, getQuotedKey(key))
204                 });
205             }
206         }
207
208         /**
209          * Ensures that an object's keys are consistently quoted, optionally checks for redundancy of quotes
210          * @param   {ASTNode} node Property AST node
211          * @param   {boolean} checkQuotesRedundancy Whether to check quotes' redundancy
212          * @returns {void}
213          */
214         function checkConsistency(node, checkQuotesRedundancy) {
215             const quotedProps = [],
216                 unquotedProps = [];
217             let keywordKeyName = null,
218                 necessaryQuotes = false;
219
220             node.properties.forEach(property => {
221                 const key = property.key;
222
223                 if (!key || property.method || property.computed || property.shorthand) {
224                     return;
225                 }
226
227                 if (key.type === "Literal" && typeof key.value === "string") {
228
229                     quotedProps.push(property);
230
231                     if (checkQuotesRedundancy) {
232                         let tokens;
233
234                         try {
235                             tokens = espree.tokenize(key.value);
236                         } catch (e) {
237                             necessaryQuotes = true;
238                             return;
239                         }
240
241                         necessaryQuotes = necessaryQuotes || !areQuotesRedundant(key.value, tokens) || KEYWORDS && isKeyword(tokens[0].value);
242                     }
243                 } else if (KEYWORDS && checkQuotesRedundancy && key.type === "Identifier" && isKeyword(key.name)) {
244                     unquotedProps.push(property);
245                     necessaryQuotes = true;
246                     keywordKeyName = key.name;
247                 } else {
248                     unquotedProps.push(property);
249                 }
250             });
251
252             if (checkQuotesRedundancy && quotedProps.length && !necessaryQuotes) {
253                 quotedProps.forEach(property => {
254                     context.report({
255                         node: property,
256                         message: "Properties shouldn't be quoted as all quotes are redundant.",
257                         fix: fixer => fixer.replaceText(property.key, getUnquotedKey(property.key))
258                     });
259                 });
260             } else if (unquotedProps.length && keywordKeyName) {
261                 unquotedProps.forEach(property => {
262                     context.report({
263                         node: property,
264                         message: "Properties should be quoted as '{{property}}' is a reserved word.",
265                         data: { property: keywordKeyName },
266                         fix: fixer => fixer.replaceText(property.key, getQuotedKey(property.key))
267                     });
268                 });
269             } else if (quotedProps.length && unquotedProps.length) {
270                 unquotedProps.forEach(property => {
271                     context.report({
272                         node: property,
273                         message: "Inconsistently quoted property '{{key}}' found.",
274                         data: { key: property.key.name || property.key.value },
275                         fix: fixer => fixer.replaceText(property.key, getQuotedKey(property.key))
276                     });
277                 });
278             }
279         }
280
281         return {
282             Property(node) {
283                 if (MODE === "always" || !MODE) {
284                     checkOmittedQuotes(node);
285                 }
286                 if (MODE === "as-needed") {
287                     checkUnnecessaryQuotes(node);
288                 }
289             },
290             ObjectExpression(node) {
291                 if (MODE === "consistent") {
292                     checkConsistency(node, false);
293                 }
294                 if (MODE === "consistent-as-needed") {
295                     checkConsistency(node, true);
296                 }
297             }
298         };
299
300     }
301 };