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