]> git.proxmox.com Git - pve-eslint.git/blob - eslint/lib/rules/semi-spacing.js
e5e2ae25cc7365f125d3850fdc3cf7ca0438da0b
[pve-eslint.git] / eslint / lib / rules / semi-spacing.js
1 /**
2 * @fileoverview Validates spacing before and after semicolon
3 * @author Mathias Schreck
4 */
5
6 "use strict";
7
8 const astUtils = require("./utils/ast-utils");
9
10 //------------------------------------------------------------------------------
11 // Rule Definition
12 //------------------------------------------------------------------------------
13
14 module.exports = {
15 meta: {
16 type: "layout",
17
18 docs: {
19 description: "enforce consistent spacing before and after semicolons",
20 recommended: false,
21 url: "https://eslint.org/docs/rules/semi-spacing"
22 },
23
24 fixable: "whitespace",
25
26 schema: [
27 {
28 type: "object",
29 properties: {
30 before: {
31 type: "boolean",
32 default: false
33 },
34 after: {
35 type: "boolean",
36 default: true
37 }
38 },
39 additionalProperties: false
40 }
41 ],
42
43 messages: {
44 unexpectedWhitespaceBefore: "Unexpected whitespace before semicolon.",
45 unexpectedWhitespaceAfter: "Unexpected whitespace after semicolon.",
46 missingWhitespaceBefore: "Missing whitespace before semicolon.",
47 missingWhitespaceAfter: "Missing whitespace after semicolon."
48 }
49 },
50
51 create(context) {
52
53 const config = context.options[0],
54 sourceCode = context.getSourceCode();
55 let requireSpaceBefore = false,
56 requireSpaceAfter = true;
57
58 if (typeof config === "object") {
59 requireSpaceBefore = config.before;
60 requireSpaceAfter = config.after;
61 }
62
63 /**
64 * Checks if a given token has leading whitespace.
65 * @param {Object} token The token to check.
66 * @returns {boolean} True if the given token has leading space, false if not.
67 */
68 function hasLeadingSpace(token) {
69 const tokenBefore = sourceCode.getTokenBefore(token);
70
71 return tokenBefore && astUtils.isTokenOnSameLine(tokenBefore, token) && sourceCode.isSpaceBetweenTokens(tokenBefore, token);
72 }
73
74 /**
75 * Checks if a given token has trailing whitespace.
76 * @param {Object} token The token to check.
77 * @returns {boolean} True if the given token has trailing space, false if not.
78 */
79 function hasTrailingSpace(token) {
80 const tokenAfter = sourceCode.getTokenAfter(token);
81
82 return tokenAfter && astUtils.isTokenOnSameLine(token, tokenAfter) && sourceCode.isSpaceBetweenTokens(token, tokenAfter);
83 }
84
85 /**
86 * Checks if the given token is the last token in its line.
87 * @param {Token} token The token to check.
88 * @returns {boolean} Whether or not the token is the last in its line.
89 */
90 function isLastTokenInCurrentLine(token) {
91 const tokenAfter = sourceCode.getTokenAfter(token);
92
93 return !(tokenAfter && astUtils.isTokenOnSameLine(token, tokenAfter));
94 }
95
96 /**
97 * Checks if the given token is the first token in its line
98 * @param {Token} token The token to check.
99 * @returns {boolean} Whether or not the token is the first in its line.
100 */
101 function isFirstTokenInCurrentLine(token) {
102 const tokenBefore = sourceCode.getTokenBefore(token);
103
104 return !(tokenBefore && astUtils.isTokenOnSameLine(token, tokenBefore));
105 }
106
107 /**
108 * Checks if the next token of a given token is a closing parenthesis.
109 * @param {Token} token The token to check.
110 * @returns {boolean} Whether or not the next token of a given token is a closing parenthesis.
111 */
112 function isBeforeClosingParen(token) {
113 const nextToken = sourceCode.getTokenAfter(token);
114
115 return (nextToken && astUtils.isClosingBraceToken(nextToken) || astUtils.isClosingParenToken(nextToken));
116 }
117
118 /**
119 * Report location example :
120 *
121 * for unexpected space `before`
122 *
123 * var a = 'b' ;
124 * ^^^
125 *
126 * for unexpected space `after`
127 *
128 * var a = 'b'; c = 10;
129 * ^^
130 *
131 * Reports if the given token has invalid spacing.
132 * @param {Token} token The semicolon token to check.
133 * @param {ASTNode} node The corresponding node of the token.
134 * @returns {void}
135 */
136 function checkSemicolonSpacing(token, node) {
137 if (astUtils.isSemicolonToken(token)) {
138 if (hasLeadingSpace(token)) {
139 if (!requireSpaceBefore) {
140 const tokenBefore = sourceCode.getTokenBefore(token);
141 const loc = {
142 start: tokenBefore.loc.end,
143 end: token.loc.start
144 };
145
146 context.report({
147 node,
148 loc,
149 messageId: "unexpectedWhitespaceBefore",
150 fix(fixer) {
151
152 return fixer.removeRange([tokenBefore.range[1], token.range[0]]);
153 }
154 });
155 }
156 } else {
157 if (requireSpaceBefore) {
158 const loc = token.loc;
159
160 context.report({
161 node,
162 loc,
163 messageId: "missingWhitespaceBefore",
164 fix(fixer) {
165 return fixer.insertTextBefore(token, " ");
166 }
167 });
168 }
169 }
170
171 if (!isFirstTokenInCurrentLine(token) && !isLastTokenInCurrentLine(token) && !isBeforeClosingParen(token)) {
172 if (hasTrailingSpace(token)) {
173 if (!requireSpaceAfter) {
174 const tokenAfter = sourceCode.getTokenAfter(token);
175 const loc = {
176 start: token.loc.end,
177 end: tokenAfter.loc.start
178 };
179
180 context.report({
181 node,
182 loc,
183 messageId: "unexpectedWhitespaceAfter",
184 fix(fixer) {
185
186 return fixer.removeRange([token.range[1], tokenAfter.range[0]]);
187 }
188 });
189 }
190 } else {
191 if (requireSpaceAfter) {
192 const loc = token.loc;
193
194 context.report({
195 node,
196 loc,
197 messageId: "missingWhitespaceAfter",
198 fix(fixer) {
199 return fixer.insertTextAfter(token, " ");
200 }
201 });
202 }
203 }
204 }
205 }
206 }
207
208 /**
209 * Checks the spacing of the semicolon with the assumption that the last token is the semicolon.
210 * @param {ASTNode} node The node to check.
211 * @returns {void}
212 */
213 function checkNode(node) {
214 const token = sourceCode.getLastToken(node);
215
216 checkSemicolonSpacing(token, node);
217 }
218
219 return {
220 VariableDeclaration: checkNode,
221 ExpressionStatement: checkNode,
222 BreakStatement: checkNode,
223 ContinueStatement: checkNode,
224 DebuggerStatement: checkNode,
225 DoWhileStatement: checkNode,
226 ReturnStatement: checkNode,
227 ThrowStatement: checkNode,
228 ImportDeclaration: checkNode,
229 ExportNamedDeclaration: checkNode,
230 ExportAllDeclaration: checkNode,
231 ExportDefaultDeclaration: checkNode,
232 ForStatement(node) {
233 if (node.init) {
234 checkSemicolonSpacing(sourceCode.getTokenAfter(node.init), node);
235 }
236
237 if (node.test) {
238 checkSemicolonSpacing(sourceCode.getTokenAfter(node.test), node);
239 }
240 },
241 PropertyDefinition: checkNode
242 };
243 }
244 };