| /** | 
|  * @fileoverview A rule to ensure whitespace before blocks. | 
|  * @author Mathias Schreck <https://github.com/lo1tuma> | 
|  */ | 
|   | 
| "use strict"; | 
|   | 
| const astUtils = require("./utils/ast-utils"); | 
|   | 
| //------------------------------------------------------------------------------ | 
| // Rule Definition | 
| //------------------------------------------------------------------------------ | 
|   | 
| module.exports = { | 
|     meta: { | 
|         type: "layout", | 
|   | 
|         docs: { | 
|             description: "enforce consistent spacing before blocks", | 
|             category: "Stylistic Issues", | 
|             recommended: false, | 
|             url: "https://eslint.org/docs/rules/space-before-blocks" | 
|         }, | 
|   | 
|         fixable: "whitespace", | 
|   | 
|         schema: [ | 
|             { | 
|                 oneOf: [ | 
|                     { | 
|                         enum: ["always", "never"] | 
|                     }, | 
|                     { | 
|                         type: "object", | 
|                         properties: { | 
|                             keywords: { | 
|                                 enum: ["always", "never", "off"] | 
|                             }, | 
|                             functions: { | 
|                                 enum: ["always", "never", "off"] | 
|                             }, | 
|                             classes: { | 
|                                 enum: ["always", "never", "off"] | 
|                             } | 
|                         }, | 
|                         additionalProperties: false | 
|                     } | 
|                 ] | 
|             } | 
|         ] | 
|     }, | 
|   | 
|     create(context) { | 
|         const config = context.options[0], | 
|             sourceCode = context.getSourceCode(); | 
|         let alwaysFunctions = true, | 
|             alwaysKeywords = true, | 
|             alwaysClasses = true, | 
|             neverFunctions = false, | 
|             neverKeywords = false, | 
|             neverClasses = false; | 
|   | 
|         if (typeof config === "object") { | 
|             alwaysFunctions = config.functions === "always"; | 
|             alwaysKeywords = config.keywords === "always"; | 
|             alwaysClasses = config.classes === "always"; | 
|             neverFunctions = config.functions === "never"; | 
|             neverKeywords = config.keywords === "never"; | 
|             neverClasses = config.classes === "never"; | 
|         } else if (config === "never") { | 
|             alwaysFunctions = false; | 
|             alwaysKeywords = false; | 
|             alwaysClasses = false; | 
|             neverFunctions = true; | 
|             neverKeywords = true; | 
|             neverClasses = true; | 
|         } | 
|   | 
|         /** | 
|          * Checks whether or not a given token is an arrow operator (=>) or a keyword | 
|          * in order to avoid to conflict with `arrow-spacing` and `keyword-spacing`. | 
|          * | 
|          * @param {Token} token - A token to check. | 
|          * @returns {boolean} `true` if the token is an arrow operator. | 
|          */ | 
|         function isConflicted(token) { | 
|             return (token.type === "Punctuator" && token.value === "=>") || token.type === "Keyword"; | 
|         } | 
|   | 
|         /** | 
|          * Checks the given BlockStatement node has a preceding space if it doesn’t start on a new line. | 
|          * @param {ASTNode|Token} node The AST node of a BlockStatement. | 
|          * @returns {void} undefined. | 
|          */ | 
|         function checkPrecedingSpace(node) { | 
|             const precedingToken = sourceCode.getTokenBefore(node); | 
|   | 
|             if (precedingToken && !isConflicted(precedingToken) && astUtils.isTokenOnSameLine(precedingToken, node)) { | 
|                 const hasSpace = sourceCode.isSpaceBetweenTokens(precedingToken, node); | 
|                 const parent = context.getAncestors().pop(); | 
|                 let requireSpace; | 
|                 let requireNoSpace; | 
|   | 
|                 if (parent.type === "FunctionExpression" || parent.type === "FunctionDeclaration") { | 
|                     requireSpace = alwaysFunctions; | 
|                     requireNoSpace = neverFunctions; | 
|                 } else if (node.type === "ClassBody") { | 
|                     requireSpace = alwaysClasses; | 
|                     requireNoSpace = neverClasses; | 
|                 } else { | 
|                     requireSpace = alwaysKeywords; | 
|                     requireNoSpace = neverKeywords; | 
|                 } | 
|   | 
|                 if (requireSpace && !hasSpace) { | 
|                     context.report({ | 
|                         node, | 
|                         message: "Missing space before opening brace.", | 
|                         fix(fixer) { | 
|                             return fixer.insertTextBefore(node, " "); | 
|                         } | 
|                     }); | 
|                 } else if (requireNoSpace && hasSpace) { | 
|                     context.report({ | 
|                         node, | 
|                         message: "Unexpected space before opening brace.", | 
|                         fix(fixer) { | 
|                             return fixer.removeRange([precedingToken.range[1], node.range[0]]); | 
|                         } | 
|                     }); | 
|                 } | 
|             } | 
|         } | 
|   | 
|         /** | 
|          * Checks if the CaseBlock of an given SwitchStatement node has a preceding space. | 
|          * @param {ASTNode} node The node of a SwitchStatement. | 
|          * @returns {void} undefined. | 
|          */ | 
|         function checkSpaceBeforeCaseBlock(node) { | 
|             const cases = node.cases; | 
|             let openingBrace; | 
|   | 
|             if (cases.length > 0) { | 
|                 openingBrace = sourceCode.getTokenBefore(cases[0]); | 
|             } else { | 
|                 openingBrace = sourceCode.getLastToken(node, 1); | 
|             } | 
|   | 
|             checkPrecedingSpace(openingBrace); | 
|         } | 
|   | 
|         return { | 
|             BlockStatement: checkPrecedingSpace, | 
|             ClassBody: checkPrecedingSpace, | 
|             SwitchStatement: checkSpaceBeforeCaseBlock | 
|         }; | 
|   | 
|     } | 
| }; |