2 * @fileoverview A rule to ensure whitespace before blocks.
3 * @author Mathias Schreck <https://github.com/lo1tuma>
8 //------------------------------------------------------------------------------
10 //------------------------------------------------------------------------------
12 const astUtils = require("./utils/ast-utils");
14 //------------------------------------------------------------------------------
16 //------------------------------------------------------------------------------
19 * Checks whether the given node represents the body of a function.
20 * @param {ASTNode} node the node to check.
21 * @returns {boolean} `true` if the node is function body.
23 function isFunctionBody(node) {
24 const parent = node.parent;
27 node.type === "BlockStatement" &&
28 astUtils.isFunction(parent) &&
33 //------------------------------------------------------------------------------
35 //------------------------------------------------------------------------------
42 description: "enforce consistent spacing before blocks",
43 category: "Stylistic Issues",
45 url: "https://eslint.org/docs/rules/space-before-blocks"
48 fixable: "whitespace",
54 enum: ["always", "never"]
60 enum: ["always", "never", "off"]
63 enum: ["always", "never", "off"]
66 enum: ["always", "never", "off"]
69 additionalProperties: false
76 unexpectedSpace: "Unexpected space before opening brace.",
77 missingSpace: "Missing space before opening brace."
82 const config = context.options[0],
83 sourceCode = context.getSourceCode();
84 let alwaysFunctions = true,
85 alwaysKeywords = true,
87 neverFunctions = false,
88 neverKeywords = false,
91 if (typeof config === "object") {
92 alwaysFunctions = config.functions === "always";
93 alwaysKeywords = config.keywords === "always";
94 alwaysClasses = config.classes === "always";
95 neverFunctions = config.functions === "never";
96 neverKeywords = config.keywords === "never";
97 neverClasses = config.classes === "never";
98 } else if (config === "never") {
99 alwaysFunctions = false;
100 alwaysKeywords = false;
101 alwaysClasses = false;
102 neverFunctions = true;
103 neverKeywords = true;
108 * Checks whether the spacing before the given block is already controlled by another rule:
109 * - `arrow-spacing` checks spaces after `=>`.
110 * - `keyword-spacing` checks spaces after keywords in certain contexts.
111 * @param {Token} precedingToken first token before the block.
112 * @param {ASTNode|Token} node `BlockStatement` node or `{` token of a `SwitchStatement` node.
113 * @returns {boolean} `true` if requiring or disallowing spaces before the given block could produce conflicts with other rules.
115 function isConflicted(precedingToken, node) {
116 return astUtils.isArrowToken(precedingToken) ||
117 astUtils.isKeywordToken(precedingToken) && !isFunctionBody(node);
121 * Checks the given BlockStatement node has a preceding space if it doesn’t start on a new line.
122 * @param {ASTNode|Token} node The AST node of a BlockStatement.
123 * @returns {void} undefined.
125 function checkPrecedingSpace(node) {
126 const precedingToken = sourceCode.getTokenBefore(node);
128 if (precedingToken && !isConflicted(precedingToken, node) && astUtils.isTokenOnSameLine(precedingToken, node)) {
129 const hasSpace = sourceCode.isSpaceBetweenTokens(precedingToken, node);
133 if (isFunctionBody(node)) {
134 requireSpace = alwaysFunctions;
135 requireNoSpace = neverFunctions;
136 } else if (node.type === "ClassBody") {
137 requireSpace = alwaysClasses;
138 requireNoSpace = neverClasses;
140 requireSpace = alwaysKeywords;
141 requireNoSpace = neverKeywords;
144 if (requireSpace && !hasSpace) {
147 messageId: "missingSpace",
149 return fixer.insertTextBefore(node, " ");
152 } else if (requireNoSpace && hasSpace) {
155 messageId: "unexpectedSpace",
157 return fixer.removeRange([precedingToken.range[1], node.range[0]]);
165 * Checks if the CaseBlock of an given SwitchStatement node has a preceding space.
166 * @param {ASTNode} node The node of a SwitchStatement.
167 * @returns {void} undefined.
169 function checkSpaceBeforeCaseBlock(node) {
170 const cases = node.cases;
173 if (cases.length > 0) {
174 openingBrace = sourceCode.getTokenBefore(cases[0]);
176 openingBrace = sourceCode.getLastToken(node, 1);
179 checkPrecedingSpace(openingBrace);
183 BlockStatement: checkPrecedingSpace,
184 ClassBody: checkPrecedingSpace,
185 SwitchStatement: checkSpaceBeforeCaseBlock