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 //------------------------------------------------------------------------------
37 /** @type {import('../shared/types').Rule} */
43 description
: "enforce consistent spacing before blocks",
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 * - `switch-colon-spacing` checks spaces after `:` of switch cases.
112 * @param {Token} precedingToken first token before the block.
113 * @param {ASTNode|Token} node `BlockStatement` node or `{` token of a `SwitchStatement` node.
114 * @returns {boolean} `true` if requiring or disallowing spaces before the given block could produce conflicts with other rules.
116 function isConflicted(precedingToken
, node
) {
118 astUtils
.isArrowToken(precedingToken
) ||
120 astUtils
.isKeywordToken(precedingToken
) &&
121 !isFunctionBody(node
)
124 astUtils
.isColonToken(precedingToken
) &&
126 node
.parent
.type
=== "SwitchCase" &&
127 precedingToken
=== astUtils
.getSwitchCaseColonToken(node
.parent
, sourceCode
)
133 * Checks the given BlockStatement node has a preceding space if it doesn’t start on a new line.
134 * @param {ASTNode|Token} node The AST node of a BlockStatement.
135 * @returns {void} undefined.
137 function checkPrecedingSpace(node
) {
138 const precedingToken
= sourceCode
.getTokenBefore(node
);
140 if (precedingToken
&& !isConflicted(precedingToken
, node
) && astUtils
.isTokenOnSameLine(precedingToken
, node
)) {
141 const hasSpace
= sourceCode
.isSpaceBetweenTokens(precedingToken
, node
);
145 if (isFunctionBody(node
)) {
146 requireSpace
= alwaysFunctions
;
147 requireNoSpace
= neverFunctions
;
148 } else if (node
.type
=== "ClassBody") {
149 requireSpace
= alwaysClasses
;
150 requireNoSpace
= neverClasses
;
152 requireSpace
= alwaysKeywords
;
153 requireNoSpace
= neverKeywords
;
156 if (requireSpace
&& !hasSpace
) {
159 messageId
: "missingSpace",
161 return fixer
.insertTextBefore(node
, " ");
164 } else if (requireNoSpace
&& hasSpace
) {
167 messageId
: "unexpectedSpace",
169 return fixer
.removeRange([precedingToken
.range
[1], node
.range
[0]]);
177 * Checks if the CaseBlock of an given SwitchStatement node has a preceding space.
178 * @param {ASTNode} node The node of a SwitchStatement.
179 * @returns {void} undefined.
181 function checkSpaceBeforeCaseBlock(node
) {
182 const cases
= node
.cases
;
185 if (cases
.length
> 0) {
186 openingBrace
= sourceCode
.getTokenBefore(cases
[0]);
188 openingBrace
= sourceCode
.getLastToken(node
, 1);
191 checkPrecedingSpace(openingBrace
);
195 BlockStatement
: checkPrecedingSpace
,
196 ClassBody
: checkPrecedingSpace
,
197 SwitchStatement
: checkSpaceBeforeCaseBlock