UNPKG

5.08 kBJavaScriptView Raw
1/**
2 * @fileoverview A rule to disallow or enforce spaces inside of single line blocks.
3 * @author Toru Nagashima
4 */
5
6"use strict";
7
8const util = require("../ast-utils");
9
10//------------------------------------------------------------------------------
11// Rule Definition
12//------------------------------------------------------------------------------
13
14module.exports = {
15 meta: {
16 docs: {
17 description: "disallow or enforce spaces inside of blocks after opening block and before closing block",
18 category: "Stylistic Issues",
19 recommended: false,
20 url: "https://eslint.org/docs/rules/block-spacing"
21 },
22
23 fixable: "whitespace",
24
25 schema: [
26 { enum: ["always", "never"] }
27 ],
28
29 messages: {
30 missing: "Requires a space {{location}} '{{token}}'",
31 extra: "Unexpected space(s) {{location}} '{{token}}'"
32 }
33 },
34
35 create(context) {
36 const always = (context.options[0] !== "never"),
37 messageId = always ? "missing" : "extra",
38 sourceCode = context.getSourceCode();
39
40 /**
41 * Gets the open brace token from a given node.
42 * @param {ASTNode} node - A BlockStatement/SwitchStatement node to get.
43 * @returns {Token} The token of the open brace.
44 */
45 function getOpenBrace(node) {
46 if (node.type === "SwitchStatement") {
47 if (node.cases.length > 0) {
48 return sourceCode.getTokenBefore(node.cases[0]);
49 }
50 return sourceCode.getLastToken(node, 1);
51 }
52 return sourceCode.getFirstToken(node);
53 }
54
55 /**
56 * Checks whether or not:
57 * - given tokens are on same line.
58 * - there is/isn't a space between given tokens.
59 * @param {Token} left - A token to check.
60 * @param {Token} right - The token which is next to `left`.
61 * @returns {boolean}
62 * When the option is `"always"`, `true` if there are one or more spaces between given tokens.
63 * When the option is `"never"`, `true` if there are not any spaces between given tokens.
64 * If given tokens are not on same line, it's always `true`.
65 */
66 function isValid(left, right) {
67 return (
68 !util.isTokenOnSameLine(left, right) ||
69 sourceCode.isSpaceBetweenTokens(left, right) === always
70 );
71 }
72
73 /**
74 * Reports invalid spacing style inside braces.
75 * @param {ASTNode} node - A BlockStatement/SwitchStatement node to get.
76 * @returns {void}
77 */
78 function checkSpacingInsideBraces(node) {
79
80 // Gets braces and the first/last token of content.
81 const openBrace = getOpenBrace(node);
82 const closeBrace = sourceCode.getLastToken(node);
83 const firstToken = sourceCode.getTokenAfter(openBrace, { includeComments: true });
84 const lastToken = sourceCode.getTokenBefore(closeBrace, { includeComments: true });
85
86 // Skip if the node is invalid or empty.
87 if (openBrace.type !== "Punctuator" ||
88 openBrace.value !== "{" ||
89 closeBrace.type !== "Punctuator" ||
90 closeBrace.value !== "}" ||
91 firstToken === closeBrace
92 ) {
93 return;
94 }
95
96 // Skip line comments for option never
97 if (!always && firstToken.type === "Line") {
98 return;
99 }
100
101 // Check.
102 if (!isValid(openBrace, firstToken)) {
103 context.report({
104 node,
105 loc: openBrace.loc.start,
106 messageId,
107 data: {
108 location: "after",
109 token: openBrace.value
110 },
111 fix(fixer) {
112 if (always) {
113 return fixer.insertTextBefore(firstToken, " ");
114 }
115
116 return fixer.removeRange([openBrace.range[1], firstToken.range[0]]);
117 }
118 });
119 }
120 if (!isValid(lastToken, closeBrace)) {
121 context.report({
122 node,
123 loc: closeBrace.loc.start,
124 messageId,
125 data: {
126 location: "before",
127 token: closeBrace.value
128 },
129 fix(fixer) {
130 if (always) {
131 return fixer.insertTextAfter(lastToken, " ");
132 }
133
134 return fixer.removeRange([lastToken.range[1], closeBrace.range[0]]);
135 }
136 });
137 }
138 }
139
140 return {
141 BlockStatement: checkSpacingInsideBraces,
142 SwitchStatement: checkSpacingInsideBraces
143 };
144 }
145};