UNPKG

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