UNPKG

5.22 kBJavaScriptView Raw
1/**
2 * @fileoverview A rule to ensure whitespace before blocks.
3 * @author Mathias Schreck <https://github.com/lo1tuma>
4 */
5
6"use strict";
7
8const astUtils = require("../ast-utils");
9
10//------------------------------------------------------------------------------
11// Rule Definition
12//------------------------------------------------------------------------------
13
14module.exports = {
15 meta: {
16 docs: {
17 description: "enforce consistent spacing before blocks",
18 category: "Stylistic Issues",
19 recommended: false,
20 url: "https://eslint.org/docs/rules/space-before-blocks"
21 },
22
23 fixable: "whitespace",
24
25 schema: [
26 {
27 oneOf: [
28 {
29 enum: ["always", "never"]
30 },
31 {
32 type: "object",
33 properties: {
34 keywords: {
35 enum: ["always", "never"]
36 },
37 functions: {
38 enum: ["always", "never"]
39 },
40 classes: {
41 enum: ["always", "never"]
42 }
43 },
44 additionalProperties: false
45 }
46 ]
47 }
48 ]
49 },
50
51 create(context) {
52 const config = context.options[0],
53 sourceCode = context.getSourceCode();
54 let checkFunctions = true,
55 checkKeywords = true,
56 checkClasses = true;
57
58 if (typeof config === "object") {
59 checkFunctions = config.functions !== "never";
60 checkKeywords = config.keywords !== "never";
61 checkClasses = config.classes !== "never";
62 } else if (config === "never") {
63 checkFunctions = false;
64 checkKeywords = false;
65 checkClasses = false;
66 }
67
68 /**
69 * Checks whether or not a given token is an arrow operator (=>) or a keyword
70 * in order to avoid to conflict with `arrow-spacing` and `keyword-spacing`.
71 *
72 * @param {Token} token - A token to check.
73 * @returns {boolean} `true` if the token is an arrow operator.
74 */
75 function isConflicted(token) {
76 return (token.type === "Punctuator" && token.value === "=>") || token.type === "Keyword";
77 }
78
79 /**
80 * Checks the given BlockStatement node has a preceding space if it doesn’t start on a new line.
81 * @param {ASTNode|Token} node The AST node of a BlockStatement.
82 * @returns {void} undefined.
83 */
84 function checkPrecedingSpace(node) {
85 const precedingToken = sourceCode.getTokenBefore(node);
86
87 if (precedingToken && !isConflicted(precedingToken) && astUtils.isTokenOnSameLine(precedingToken, node)) {
88 const hasSpace = sourceCode.isSpaceBetweenTokens(precedingToken, node);
89 const parent = context.getAncestors().pop();
90 let requireSpace;
91
92 if (parent.type === "FunctionExpression" || parent.type === "FunctionDeclaration") {
93 requireSpace = checkFunctions;
94 } else if (node.type === "ClassBody") {
95 requireSpace = checkClasses;
96 } else {
97 requireSpace = checkKeywords;
98 }
99
100 if (requireSpace) {
101 if (!hasSpace) {
102 context.report({
103 node,
104 message: "Missing space before opening brace.",
105 fix(fixer) {
106 return fixer.insertTextBefore(node, " ");
107 }
108 });
109 }
110 } else {
111 if (hasSpace) {
112 context.report({
113 node,
114 message: "Unexpected space before opening brace.",
115 fix(fixer) {
116 return fixer.removeRange([precedingToken.range[1], node.range[0]]);
117 }
118 });
119 }
120 }
121 }
122 }
123
124 /**
125 * Checks if the CaseBlock of an given SwitchStatement node has a preceding space.
126 * @param {ASTNode} node The node of a SwitchStatement.
127 * @returns {void} undefined.
128 */
129 function checkSpaceBeforeCaseBlock(node) {
130 const cases = node.cases;
131 let openingBrace;
132
133 if (cases.length > 0) {
134 openingBrace = sourceCode.getTokenBefore(cases[0]);
135 } else {
136 openingBrace = sourceCode.getLastToken(node, 1);
137 }
138
139 checkPrecedingSpace(openingBrace);
140 }
141
142 return {
143 BlockStatement: checkPrecedingSpace,
144 ClassBody: checkPrecedingSpace,
145 SwitchStatement: checkSpaceBeforeCaseBlock
146 };
147
148 }
149};