Actualizacion maquina principal
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-prettier / node_modules / eslint / lib / rules / keyword-spacing.js
1 /**
2  * @fileoverview Rule to enforce spacing before and after keywords.
3  * @author Toru Nagashima
4  */
5
6 "use strict";
7
8 //------------------------------------------------------------------------------
9 // Requirements
10 //------------------------------------------------------------------------------
11
12 const astUtils = require("./utils/ast-utils"),
13     keywords = require("./utils/keywords");
14
15 //------------------------------------------------------------------------------
16 // Constants
17 //------------------------------------------------------------------------------
18
19 const PREV_TOKEN = /^[)\]}>]$/u;
20 const NEXT_TOKEN = /^(?:[([{<~!]|\+\+?|--?)$/u;
21 const PREV_TOKEN_M = /^[)\]}>*]$/u;
22 const NEXT_TOKEN_M = /^[{*]$/u;
23 const TEMPLATE_OPEN_PAREN = /\$\{$/u;
24 const TEMPLATE_CLOSE_PAREN = /^\}/u;
25 const CHECK_TYPE = /^(?:JSXElement|RegularExpression|String|Template)$/u;
26 const KEYS = keywords.concat(["as", "async", "await", "from", "get", "let", "of", "set", "yield"]);
27
28 // check duplications.
29 (function() {
30     KEYS.sort();
31     for (let i = 1; i < KEYS.length; ++i) {
32         if (KEYS[i] === KEYS[i - 1]) {
33             throw new Error(`Duplication was found in the keyword list: ${KEYS[i]}`);
34         }
35     }
36 }());
37
38 //------------------------------------------------------------------------------
39 // Helpers
40 //------------------------------------------------------------------------------
41
42 /**
43  * Checks whether or not a given token is a "Template" token ends with "${".
44  * @param {Token} token A token to check.
45  * @returns {boolean} `true` if the token is a "Template" token ends with "${".
46  */
47 function isOpenParenOfTemplate(token) {
48     return token.type === "Template" && TEMPLATE_OPEN_PAREN.test(token.value);
49 }
50
51 /**
52  * Checks whether or not a given token is a "Template" token starts with "}".
53  * @param {Token} token A token to check.
54  * @returns {boolean} `true` if the token is a "Template" token starts with "}".
55  */
56 function isCloseParenOfTemplate(token) {
57     return token.type === "Template" && TEMPLATE_CLOSE_PAREN.test(token.value);
58 }
59
60 //------------------------------------------------------------------------------
61 // Rule Definition
62 //------------------------------------------------------------------------------
63
64 module.exports = {
65     meta: {
66         type: "layout",
67
68         docs: {
69             description: "enforce consistent spacing before and after keywords",
70             category: "Stylistic Issues",
71             recommended: false,
72             url: "https://eslint.org/docs/rules/keyword-spacing"
73         },
74
75         fixable: "whitespace",
76
77         schema: [
78             {
79                 type: "object",
80                 properties: {
81                     before: { type: "boolean", default: true },
82                     after: { type: "boolean", default: true },
83                     overrides: {
84                         type: "object",
85                         properties: KEYS.reduce((retv, key) => {
86                             retv[key] = {
87                                 type: "object",
88                                 properties: {
89                                     before: { type: "boolean" },
90                                     after: { type: "boolean" }
91                                 },
92                                 additionalProperties: false
93                             };
94                             return retv;
95                         }, {}),
96                         additionalProperties: false
97                     }
98                 },
99                 additionalProperties: false
100             }
101         ],
102         messages: {
103             expectedBefore: "Expected space(s) before \"{{value}}\".",
104             expectedAfter: "Expected space(s) after \"{{value}}\".",
105             unexpectedBefore: "Unexpected space(s) before \"{{value}}\".",
106             unexpectedAfter: "Unexpected space(s) after \"{{value}}\"."
107         }
108     },
109
110     create(context) {
111         const sourceCode = context.getSourceCode();
112
113         /**
114          * Reports a given token if there are not space(s) before the token.
115          * @param {Token} token A token to report.
116          * @param {RegExp} pattern A pattern of the previous token to check.
117          * @returns {void}
118          */
119         function expectSpaceBefore(token, pattern) {
120             const prevToken = sourceCode.getTokenBefore(token);
121
122             if (prevToken &&
123                 (CHECK_TYPE.test(prevToken.type) || pattern.test(prevToken.value)) &&
124                 !isOpenParenOfTemplate(prevToken) &&
125                 astUtils.isTokenOnSameLine(prevToken, token) &&
126                 !sourceCode.isSpaceBetweenTokens(prevToken, token)
127             ) {
128                 context.report({
129                     loc: token.loc.start,
130                     messageId: "expectedBefore",
131                     data: token,
132                     fix(fixer) {
133                         return fixer.insertTextBefore(token, " ");
134                     }
135                 });
136             }
137         }
138
139         /**
140          * Reports a given token if there are space(s) before the token.
141          * @param {Token} token A token to report.
142          * @param {RegExp} pattern A pattern of the previous token to check.
143          * @returns {void}
144          */
145         function unexpectSpaceBefore(token, pattern) {
146             const prevToken = sourceCode.getTokenBefore(token);
147
148             if (prevToken &&
149                 (CHECK_TYPE.test(prevToken.type) || pattern.test(prevToken.value)) &&
150                 !isOpenParenOfTemplate(prevToken) &&
151                 astUtils.isTokenOnSameLine(prevToken, token) &&
152                 sourceCode.isSpaceBetweenTokens(prevToken, token)
153             ) {
154                 context.report({
155                     loc: token.loc.start,
156                     messageId: "unexpectedBefore",
157                     data: token,
158                     fix(fixer) {
159                         return fixer.removeRange([prevToken.range[1], token.range[0]]);
160                     }
161                 });
162             }
163         }
164
165         /**
166          * Reports a given token if there are not space(s) after the token.
167          * @param {Token} token A token to report.
168          * @param {RegExp} pattern A pattern of the next token to check.
169          * @returns {void}
170          */
171         function expectSpaceAfter(token, pattern) {
172             const nextToken = sourceCode.getTokenAfter(token);
173
174             if (nextToken &&
175                 (CHECK_TYPE.test(nextToken.type) || pattern.test(nextToken.value)) &&
176                 !isCloseParenOfTemplate(nextToken) &&
177                 astUtils.isTokenOnSameLine(token, nextToken) &&
178                 !sourceCode.isSpaceBetweenTokens(token, nextToken)
179             ) {
180                 context.report({
181                     loc: token.loc.start,
182                     messageId: "expectedAfter",
183                     data: token,
184                     fix(fixer) {
185                         return fixer.insertTextAfter(token, " ");
186                     }
187                 });
188             }
189         }
190
191         /**
192          * Reports a given token if there are space(s) after the token.
193          * @param {Token} token A token to report.
194          * @param {RegExp} pattern A pattern of the next token to check.
195          * @returns {void}
196          */
197         function unexpectSpaceAfter(token, pattern) {
198             const nextToken = sourceCode.getTokenAfter(token);
199
200             if (nextToken &&
201                 (CHECK_TYPE.test(nextToken.type) || pattern.test(nextToken.value)) &&
202                 !isCloseParenOfTemplate(nextToken) &&
203                 astUtils.isTokenOnSameLine(token, nextToken) &&
204                 sourceCode.isSpaceBetweenTokens(token, nextToken)
205             ) {
206                 context.report({
207                     loc: token.loc.start,
208                     messageId: "unexpectedAfter",
209                     data: token,
210                     fix(fixer) {
211                         return fixer.removeRange([token.range[1], nextToken.range[0]]);
212                     }
213                 });
214             }
215         }
216
217         /**
218          * Parses the option object and determines check methods for each keyword.
219          * @param {Object|undefined} options The option object to parse.
220          * @returns {Object} - Normalized option object.
221          *      Keys are keywords (there are for every keyword).
222          *      Values are instances of `{"before": function, "after": function}`.
223          */
224         function parseOptions(options = {}) {
225             const before = options.before !== false;
226             const after = options.after !== false;
227             const defaultValue = {
228                 before: before ? expectSpaceBefore : unexpectSpaceBefore,
229                 after: after ? expectSpaceAfter : unexpectSpaceAfter
230             };
231             const overrides = (options && options.overrides) || {};
232             const retv = Object.create(null);
233
234             for (let i = 0; i < KEYS.length; ++i) {
235                 const key = KEYS[i];
236                 const override = overrides[key];
237
238                 if (override) {
239                     const thisBefore = ("before" in override) ? override.before : before;
240                     const thisAfter = ("after" in override) ? override.after : after;
241
242                     retv[key] = {
243                         before: thisBefore ? expectSpaceBefore : unexpectSpaceBefore,
244                         after: thisAfter ? expectSpaceAfter : unexpectSpaceAfter
245                     };
246                 } else {
247                     retv[key] = defaultValue;
248                 }
249             }
250
251             return retv;
252         }
253
254         const checkMethodMap = parseOptions(context.options[0]);
255
256         /**
257          * Reports a given token if usage of spacing followed by the token is
258          * invalid.
259          * @param {Token} token A token to report.
260          * @param {RegExp} [pattern] Optional. A pattern of the previous
261          *      token to check.
262          * @returns {void}
263          */
264         function checkSpacingBefore(token, pattern) {
265             checkMethodMap[token.value].before(token, pattern || PREV_TOKEN);
266         }
267
268         /**
269          * Reports a given token if usage of spacing preceded by the token is
270          * invalid.
271          * @param {Token} token A token to report.
272          * @param {RegExp} [pattern] Optional. A pattern of the next
273          *      token to check.
274          * @returns {void}
275          */
276         function checkSpacingAfter(token, pattern) {
277             checkMethodMap[token.value].after(token, pattern || NEXT_TOKEN);
278         }
279
280         /**
281          * Reports a given token if usage of spacing around the token is invalid.
282          * @param {Token} token A token to report.
283          * @returns {void}
284          */
285         function checkSpacingAround(token) {
286             checkSpacingBefore(token);
287             checkSpacingAfter(token);
288         }
289
290         /**
291          * Reports the first token of a given node if the first token is a keyword
292          * and usage of spacing around the token is invalid.
293          * @param {ASTNode|null} node A node to report.
294          * @returns {void}
295          */
296         function checkSpacingAroundFirstToken(node) {
297             const firstToken = node && sourceCode.getFirstToken(node);
298
299             if (firstToken && firstToken.type === "Keyword") {
300                 checkSpacingAround(firstToken);
301             }
302         }
303
304         /**
305          * Reports the first token of a given node if the first token is a keyword
306          * and usage of spacing followed by the token is invalid.
307          *
308          * This is used for unary operators (e.g. `typeof`), `function`, and `super`.
309          * Other rules are handling usage of spacing preceded by those keywords.
310          * @param {ASTNode|null} node A node to report.
311          * @returns {void}
312          */
313         function checkSpacingBeforeFirstToken(node) {
314             const firstToken = node && sourceCode.getFirstToken(node);
315
316             if (firstToken && firstToken.type === "Keyword") {
317                 checkSpacingBefore(firstToken);
318             }
319         }
320
321         /**
322          * Reports the previous token of a given node if the token is a keyword and
323          * usage of spacing around the token is invalid.
324          * @param {ASTNode|null} node A node to report.
325          * @returns {void}
326          */
327         function checkSpacingAroundTokenBefore(node) {
328             if (node) {
329                 const token = sourceCode.getTokenBefore(node, astUtils.isKeywordToken);
330
331                 checkSpacingAround(token);
332             }
333         }
334
335         /**
336          * Reports `async` or `function` keywords of a given node if usage of
337          * spacing around those keywords is invalid.
338          * @param {ASTNode} node A node to report.
339          * @returns {void}
340          */
341         function checkSpacingForFunction(node) {
342             const firstToken = node && sourceCode.getFirstToken(node);
343
344             if (firstToken &&
345                 ((firstToken.type === "Keyword" && firstToken.value === "function") ||
346                 firstToken.value === "async")
347             ) {
348                 checkSpacingBefore(firstToken);
349             }
350         }
351
352         /**
353          * Reports `class` and `extends` keywords of a given node if usage of
354          * spacing around those keywords is invalid.
355          * @param {ASTNode} node A node to report.
356          * @returns {void}
357          */
358         function checkSpacingForClass(node) {
359             checkSpacingAroundFirstToken(node);
360             checkSpacingAroundTokenBefore(node.superClass);
361         }
362
363         /**
364          * Reports `if` and `else` keywords of a given node if usage of spacing
365          * around those keywords is invalid.
366          * @param {ASTNode} node A node to report.
367          * @returns {void}
368          */
369         function checkSpacingForIfStatement(node) {
370             checkSpacingAroundFirstToken(node);
371             checkSpacingAroundTokenBefore(node.alternate);
372         }
373
374         /**
375          * Reports `try`, `catch`, and `finally` keywords of a given node if usage
376          * of spacing around those keywords is invalid.
377          * @param {ASTNode} node A node to report.
378          * @returns {void}
379          */
380         function checkSpacingForTryStatement(node) {
381             checkSpacingAroundFirstToken(node);
382             checkSpacingAroundFirstToken(node.handler);
383             checkSpacingAroundTokenBefore(node.finalizer);
384         }
385
386         /**
387          * Reports `do` and `while` keywords of a given node if usage of spacing
388          * around those keywords is invalid.
389          * @param {ASTNode} node A node to report.
390          * @returns {void}
391          */
392         function checkSpacingForDoWhileStatement(node) {
393             checkSpacingAroundFirstToken(node);
394             checkSpacingAroundTokenBefore(node.test);
395         }
396
397         /**
398          * Reports `for` and `in` keywords of a given node if usage of spacing
399          * around those keywords is invalid.
400          * @param {ASTNode} node A node to report.
401          * @returns {void}
402          */
403         function checkSpacingForForInStatement(node) {
404             checkSpacingAroundFirstToken(node);
405             checkSpacingAroundTokenBefore(node.right);
406         }
407
408         /**
409          * Reports `for` and `of` keywords of a given node if usage of spacing
410          * around those keywords is invalid.
411          * @param {ASTNode} node A node to report.
412          * @returns {void}
413          */
414         function checkSpacingForForOfStatement(node) {
415             if (node.await) {
416                 checkSpacingBefore(sourceCode.getFirstToken(node, 0));
417                 checkSpacingAfter(sourceCode.getFirstToken(node, 1));
418             } else {
419                 checkSpacingAroundFirstToken(node);
420             }
421             checkSpacingAround(sourceCode.getTokenBefore(node.right, astUtils.isNotOpeningParenToken));
422         }
423
424         /**
425          * Reports `import`, `export`, `as`, and `from` keywords of a given node if
426          * usage of spacing around those keywords is invalid.
427          *
428          * This rule handles the `*` token in module declarations.
429          *
430          *     import*as A from "./a"; /*error Expected space(s) after "import".
431          *                               error Expected space(s) before "as".
432          * @param {ASTNode} node A node to report.
433          * @returns {void}
434          */
435         function checkSpacingForModuleDeclaration(node) {
436             const firstToken = sourceCode.getFirstToken(node);
437
438             checkSpacingBefore(firstToken, PREV_TOKEN_M);
439             checkSpacingAfter(firstToken, NEXT_TOKEN_M);
440
441             if (node.type === "ExportDefaultDeclaration") {
442                 checkSpacingAround(sourceCode.getTokenAfter(firstToken));
443             }
444
445             if (node.source) {
446                 const fromToken = sourceCode.getTokenBefore(node.source);
447
448                 checkSpacingBefore(fromToken, PREV_TOKEN_M);
449                 checkSpacingAfter(fromToken, NEXT_TOKEN_M);
450             }
451         }
452
453         /**
454          * Reports `as` keyword of a given node if usage of spacing around this
455          * keyword is invalid.
456          * @param {ASTNode} node A node to report.
457          * @returns {void}
458          */
459         function checkSpacingForImportNamespaceSpecifier(node) {
460             const asToken = sourceCode.getFirstToken(node, 1);
461
462             checkSpacingBefore(asToken, PREV_TOKEN_M);
463         }
464
465         /**
466          * Reports `static`, `get`, and `set` keywords of a given node if usage of
467          * spacing around those keywords is invalid.
468          * @param {ASTNode} node A node to report.
469          * @returns {void}
470          */
471         function checkSpacingForProperty(node) {
472             if (node.static) {
473                 checkSpacingAroundFirstToken(node);
474             }
475             if (node.kind === "get" ||
476                 node.kind === "set" ||
477                 (
478                     (node.method || node.type === "MethodDefinition") &&
479                     node.value.async
480                 )
481             ) {
482                 const token = sourceCode.getTokenBefore(
483                     node.key,
484                     tok => {
485                         switch (tok.value) {
486                             case "get":
487                             case "set":
488                             case "async":
489                                 return true;
490                             default:
491                                 return false;
492                         }
493                     }
494                 );
495
496                 if (!token) {
497                     throw new Error("Failed to find token get, set, or async beside method name");
498                 }
499
500
501                 checkSpacingAround(token);
502             }
503         }
504
505         /**
506          * Reports `await` keyword of a given node if usage of spacing before
507          * this keyword is invalid.
508          * @param {ASTNode} node A node to report.
509          * @returns {void}
510          */
511         function checkSpacingForAwaitExpression(node) {
512             checkSpacingBefore(sourceCode.getFirstToken(node));
513         }
514
515         return {
516
517             // Statements
518             DebuggerStatement: checkSpacingAroundFirstToken,
519             WithStatement: checkSpacingAroundFirstToken,
520
521             // Statements - Control flow
522             BreakStatement: checkSpacingAroundFirstToken,
523             ContinueStatement: checkSpacingAroundFirstToken,
524             ReturnStatement: checkSpacingAroundFirstToken,
525             ThrowStatement: checkSpacingAroundFirstToken,
526             TryStatement: checkSpacingForTryStatement,
527
528             // Statements - Choice
529             IfStatement: checkSpacingForIfStatement,
530             SwitchStatement: checkSpacingAroundFirstToken,
531             SwitchCase: checkSpacingAroundFirstToken,
532
533             // Statements - Loops
534             DoWhileStatement: checkSpacingForDoWhileStatement,
535             ForInStatement: checkSpacingForForInStatement,
536             ForOfStatement: checkSpacingForForOfStatement,
537             ForStatement: checkSpacingAroundFirstToken,
538             WhileStatement: checkSpacingAroundFirstToken,
539
540             // Statements - Declarations
541             ClassDeclaration: checkSpacingForClass,
542             ExportNamedDeclaration: checkSpacingForModuleDeclaration,
543             ExportDefaultDeclaration: checkSpacingForModuleDeclaration,
544             ExportAllDeclaration: checkSpacingForModuleDeclaration,
545             FunctionDeclaration: checkSpacingForFunction,
546             ImportDeclaration: checkSpacingForModuleDeclaration,
547             VariableDeclaration: checkSpacingAroundFirstToken,
548
549             // Expressions
550             ArrowFunctionExpression: checkSpacingForFunction,
551             AwaitExpression: checkSpacingForAwaitExpression,
552             ClassExpression: checkSpacingForClass,
553             FunctionExpression: checkSpacingForFunction,
554             NewExpression: checkSpacingBeforeFirstToken,
555             Super: checkSpacingBeforeFirstToken,
556             ThisExpression: checkSpacingBeforeFirstToken,
557             UnaryExpression: checkSpacingBeforeFirstToken,
558             YieldExpression: checkSpacingBeforeFirstToken,
559
560             // Others
561             ImportNamespaceSpecifier: checkSpacingForImportNamespaceSpecifier,
562             MethodDefinition: checkSpacingForProperty,
563             Property: checkSpacingForProperty
564         };
565     }
566 };