]>
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 | ||
14 | module.exports = { | |
15 | meta: { | |
16 | type: "layout", | |
17 | ||
18 | docs: { | |
19 | description: "disallow or enforce spaces inside of blocks after opening block and before closing block", | |
20 | category: "Stylistic Issues", | |
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. | |
44 | * @param {ASTNode} node A BlockStatement/SwitchStatement node to get. | |
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 | } | |
54 | return sourceCode.getFirstToken(node); | |
55 | } | |
56 | ||
57 | /** | |
58 | * Checks whether or not: | |
59 | * - given tokens are on same line. | |
60 | * - there is/isn't a space between given tokens. | |
61 | * @param {Token} left A token to check. | |
62 | * @param {Token} right The token which is next to `left`. | |
63 | * @returns {boolean} | |
64 | * When the option is `"always"`, `true` if there are one or more spaces between given tokens. | |
65 | * When the option is `"never"`, `true` if there are not any spaces between given tokens. | |
66 | * If given tokens are not on same line, it's always `true`. | |
67 | */ | |
68 | function isValid(left, right) { | |
69 | return ( | |
70 | !util.isTokenOnSameLine(left, right) || | |
71 | sourceCode.isSpaceBetweenTokens(left, right) === always | |
72 | ); | |
73 | } | |
74 | ||
75 | /** | |
76 | * Reports invalid spacing style inside braces. | |
77 | * @param {ASTNode} node A BlockStatement/SwitchStatement node to get. | |
78 | * @returns {void} | |
79 | */ | |
80 | function checkSpacingInsideBraces(node) { | |
81 | ||
82 | // Gets braces and the first/last token of content. | |
83 | const openBrace = getOpenBrace(node); | |
84 | const closeBrace = sourceCode.getLastToken(node); | |
85 | const firstToken = sourceCode.getTokenAfter(openBrace, { includeComments: true }); | |
86 | const lastToken = sourceCode.getTokenBefore(closeBrace, { includeComments: true }); | |
87 | ||
88 | // Skip if the node is invalid or empty. | |
89 | if (openBrace.type !== "Punctuator" || | |
90 | openBrace.value !== "{" || | |
91 | closeBrace.type !== "Punctuator" || | |
92 | closeBrace.value !== "}" || | |
93 | firstToken === closeBrace | |
94 | ) { | |
95 | return; | |
96 | } | |
97 | ||
98 | // Skip line comments for option never | |
99 | if (!always && firstToken.type === "Line") { | |
100 | return; | |
101 | } | |
102 | ||
103 | // Check. | |
104 | if (!isValid(openBrace, firstToken)) { | |
105 | context.report({ | |
106 | node, | |
107 | loc: openBrace.loc.start, | |
108 | messageId, | |
109 | data: { | |
110 | location: "after", | |
111 | token: openBrace.value | |
112 | }, | |
113 | fix(fixer) { | |
114 | if (always) { | |
115 | return fixer.insertTextBefore(firstToken, " "); | |
116 | } | |
117 | ||
118 | return fixer.removeRange([openBrace.range[1], firstToken.range[0]]); | |
119 | } | |
120 | }); | |
121 | } | |
122 | if (!isValid(lastToken, closeBrace)) { | |
123 | context.report({ | |
124 | node, | |
125 | loc: closeBrace.loc.start, | |
126 | messageId, | |
127 | data: { | |
128 | location: "before", | |
129 | token: closeBrace.value | |
130 | }, | |
131 | fix(fixer) { | |
132 | if (always) { | |
133 | return fixer.insertTextAfter(lastToken, " "); | |
134 | } | |
135 | ||
136 | return fixer.removeRange([lastToken.range[1], closeBrace.range[0]]); | |
137 | } | |
138 | }); | |
139 | } | |
140 | } | |
141 | ||
142 | return { | |
143 | BlockStatement: checkSpacingInsideBraces, | |
144 | SwitchStatement: checkSpacingInsideBraces | |
145 | }; | |
146 | } | |
147 | }; |