]> git.proxmox.com Git - pve-eslint.git/blob - eslint/lib/rules/space-before-blocks.js
import 8.3.0 source
[pve-eslint.git] / eslint / lib / rules / space-before-blocks.js
1 /**
2 * @fileoverview A rule to ensure whitespace before blocks.
3 * @author Mathias Schreck <https://github.com/lo1tuma>
4 */
5
6 "use strict";
7
8 //------------------------------------------------------------------------------
9 // Requirements
10 //------------------------------------------------------------------------------
11
12 const astUtils = require("./utils/ast-utils");
13
14 //------------------------------------------------------------------------------
15 // Helpers
16 //------------------------------------------------------------------------------
17
18 /**
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.
22 */
23 function isFunctionBody(node) {
24 const parent = node.parent;
25
26 return (
27 node.type === "BlockStatement" &&
28 astUtils.isFunction(parent) &&
29 parent.body === node
30 );
31 }
32
33 //------------------------------------------------------------------------------
34 // Rule Definition
35 //------------------------------------------------------------------------------
36
37 module.exports = {
38 meta: {
39 type: "layout",
40
41 docs: {
42 description: "enforce consistent spacing before blocks",
43 recommended: false,
44 url: "https://eslint.org/docs/rules/space-before-blocks"
45 },
46
47 fixable: "whitespace",
48
49 schema: [
50 {
51 oneOf: [
52 {
53 enum: ["always", "never"]
54 },
55 {
56 type: "object",
57 properties: {
58 keywords: {
59 enum: ["always", "never", "off"]
60 },
61 functions: {
62 enum: ["always", "never", "off"]
63 },
64 classes: {
65 enum: ["always", "never", "off"]
66 }
67 },
68 additionalProperties: false
69 }
70 ]
71 }
72 ],
73
74 messages: {
75 unexpectedSpace: "Unexpected space before opening brace.",
76 missingSpace: "Missing space before opening brace."
77 }
78 },
79
80 create(context) {
81 const config = context.options[0],
82 sourceCode = context.getSourceCode();
83 let alwaysFunctions = true,
84 alwaysKeywords = true,
85 alwaysClasses = true,
86 neverFunctions = false,
87 neverKeywords = false,
88 neverClasses = false;
89
90 if (typeof config === "object") {
91 alwaysFunctions = config.functions === "always";
92 alwaysKeywords = config.keywords === "always";
93 alwaysClasses = config.classes === "always";
94 neverFunctions = config.functions === "never";
95 neverKeywords = config.keywords === "never";
96 neverClasses = config.classes === "never";
97 } else if (config === "never") {
98 alwaysFunctions = false;
99 alwaysKeywords = false;
100 alwaysClasses = false;
101 neverFunctions = true;
102 neverKeywords = true;
103 neverClasses = true;
104 }
105
106 /**
107 * Checks whether the spacing before the given block is already controlled by another rule:
108 * - `arrow-spacing` checks spaces after `=>`.
109 * - `keyword-spacing` checks spaces after keywords in certain contexts.
110 * - `switch-colon-spacing` checks spaces after `:` of switch cases.
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.
114 */
115 function isConflicted(precedingToken, node) {
116 return (
117 astUtils.isArrowToken(precedingToken) ||
118 (
119 astUtils.isKeywordToken(precedingToken) &&
120 !isFunctionBody(node)
121 ) ||
122 (
123 astUtils.isColonToken(precedingToken) &&
124 node.parent &&
125 node.parent.type === "SwitchCase" &&
126 precedingToken === astUtils.getSwitchCaseColonToken(node.parent, sourceCode)
127 )
128 );
129 }
130
131 /**
132 * Checks the given BlockStatement node has a preceding space if it doesn’t start on a new line.
133 * @param {ASTNode|Token} node The AST node of a BlockStatement.
134 * @returns {void} undefined.
135 */
136 function checkPrecedingSpace(node) {
137 const precedingToken = sourceCode.getTokenBefore(node);
138
139 if (precedingToken && !isConflicted(precedingToken, node) && astUtils.isTokenOnSameLine(precedingToken, node)) {
140 const hasSpace = sourceCode.isSpaceBetweenTokens(precedingToken, node);
141 let requireSpace;
142 let requireNoSpace;
143
144 if (isFunctionBody(node)) {
145 requireSpace = alwaysFunctions;
146 requireNoSpace = neverFunctions;
147 } else if (node.type === "ClassBody") {
148 requireSpace = alwaysClasses;
149 requireNoSpace = neverClasses;
150 } else {
151 requireSpace = alwaysKeywords;
152 requireNoSpace = neverKeywords;
153 }
154
155 if (requireSpace && !hasSpace) {
156 context.report({
157 node,
158 messageId: "missingSpace",
159 fix(fixer) {
160 return fixer.insertTextBefore(node, " ");
161 }
162 });
163 } else if (requireNoSpace && hasSpace) {
164 context.report({
165 node,
166 messageId: "unexpectedSpace",
167 fix(fixer) {
168 return fixer.removeRange([precedingToken.range[1], node.range[0]]);
169 }
170 });
171 }
172 }
173 }
174
175 /**
176 * Checks if the CaseBlock of an given SwitchStatement node has a preceding space.
177 * @param {ASTNode} node The node of a SwitchStatement.
178 * @returns {void} undefined.
179 */
180 function checkSpaceBeforeCaseBlock(node) {
181 const cases = node.cases;
182 let openingBrace;
183
184 if (cases.length > 0) {
185 openingBrace = sourceCode.getTokenBefore(cases[0]);
186 } else {
187 openingBrace = sourceCode.getLastToken(node, 1);
188 }
189
190 checkPrecedingSpace(openingBrace);
191 }
192
193 return {
194 BlockStatement: checkPrecedingSpace,
195 ClassBody: checkPrecedingSpace,
196 SwitchStatement: checkSpaceBeforeCaseBlock
197 };
198
199 }
200 };