]> git.proxmox.com Git - pve-eslint.git/blame - eslint/lib/rules/space-before-blocks.js
import 8.23.1 source
[pve-eslint.git] / eslint / lib / rules / space-before-blocks.js
CommitLineData
eb39fafa
DC
1/**
2 * @fileoverview A rule to ensure whitespace before blocks.
3 * @author Mathias Schreck <https://github.com/lo1tuma>
4 */
5
6"use strict";
7
6f036462
TL
8//------------------------------------------------------------------------------
9// Requirements
10//------------------------------------------------------------------------------
11
eb39fafa
DC
12const astUtils = require("./utils/ast-utils");
13
6f036462
TL
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 */
23function 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
eb39fafa
DC
33//------------------------------------------------------------------------------
34// Rule Definition
35//------------------------------------------------------------------------------
36
34eeec05 37/** @type {import('../shared/types').Rule} */
eb39fafa
DC
38module.exports = {
39 meta: {
40 type: "layout",
41
42 docs: {
8f9d1d4d 43 description: "Enforce consistent spacing before blocks",
eb39fafa
DC
44 recommended: false,
45 url: "https://eslint.org/docs/rules/space-before-blocks"
46 },
47
48 fixable: "whitespace",
49
50 schema: [
51 {
52 oneOf: [
53 {
54 enum: ["always", "never"]
55 },
56 {
57 type: "object",
58 properties: {
59 keywords: {
60 enum: ["always", "never", "off"]
61 },
62 functions: {
63 enum: ["always", "never", "off"]
64 },
65 classes: {
66 enum: ["always", "never", "off"]
67 }
68 },
69 additionalProperties: false
70 }
71 ]
72 }
73 ],
74
75 messages: {
76 unexpectedSpace: "Unexpected space before opening brace.",
77 missingSpace: "Missing space before opening brace."
78 }
79 },
80
81 create(context) {
82 const config = context.options[0],
83 sourceCode = context.getSourceCode();
84 let alwaysFunctions = true,
85 alwaysKeywords = true,
86 alwaysClasses = true,
87 neverFunctions = false,
88 neverKeywords = false,
89 neverClasses = false;
90
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;
104 neverClasses = true;
105 }
106
107 /**
6f036462
TL
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.
609c276f 111 * - `switch-colon-spacing` checks spaces after `:` of switch cases.
6f036462
TL
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.
eb39fafa 115 */
6f036462 116 function isConflicted(precedingToken, node) {
609c276f
TL
117 return (
118 astUtils.isArrowToken(precedingToken) ||
119 (
120 astUtils.isKeywordToken(precedingToken) &&
121 !isFunctionBody(node)
122 ) ||
123 (
124 astUtils.isColonToken(precedingToken) &&
125 node.parent &&
126 node.parent.type === "SwitchCase" &&
127 precedingToken === astUtils.getSwitchCaseColonToken(node.parent, sourceCode)
128 )
129 );
eb39fafa
DC
130 }
131
132 /**
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.
136 */
137 function checkPrecedingSpace(node) {
138 const precedingToken = sourceCode.getTokenBefore(node);
139
6f036462 140 if (precedingToken && !isConflicted(precedingToken, node) && astUtils.isTokenOnSameLine(precedingToken, node)) {
eb39fafa 141 const hasSpace = sourceCode.isSpaceBetweenTokens(precedingToken, node);
eb39fafa
DC
142 let requireSpace;
143 let requireNoSpace;
144
6f036462 145 if (isFunctionBody(node)) {
eb39fafa
DC
146 requireSpace = alwaysFunctions;
147 requireNoSpace = neverFunctions;
148 } else if (node.type === "ClassBody") {
149 requireSpace = alwaysClasses;
150 requireNoSpace = neverClasses;
151 } else {
152 requireSpace = alwaysKeywords;
153 requireNoSpace = neverKeywords;
154 }
155
156 if (requireSpace && !hasSpace) {
157 context.report({
158 node,
159 messageId: "missingSpace",
160 fix(fixer) {
161 return fixer.insertTextBefore(node, " ");
162 }
163 });
164 } else if (requireNoSpace && hasSpace) {
165 context.report({
166 node,
167 messageId: "unexpectedSpace",
168 fix(fixer) {
169 return fixer.removeRange([precedingToken.range[1], node.range[0]]);
170 }
171 });
172 }
173 }
174 }
175
176 /**
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.
180 */
181 function checkSpaceBeforeCaseBlock(node) {
182 const cases = node.cases;
183 let openingBrace;
184
185 if (cases.length > 0) {
186 openingBrace = sourceCode.getTokenBefore(cases[0]);
187 } else {
188 openingBrace = sourceCode.getLastToken(node, 1);
189 }
190
191 checkPrecedingSpace(openingBrace);
192 }
193
194 return {
195 BlockStatement: checkPrecedingSpace,
196 ClassBody: checkPrecedingSpace,
197 SwitchStatement: checkSpaceBeforeCaseBlock
198 };
199
200 }
201};