UNPKG

8.18 kBJavaScriptView Raw
1/**
2 * @fileoverview Rule to flag block statements that do not use the one true brace style
3 * @author Ian Christian Myers
4 */
5
6"use strict";
7
8//------------------------------------------------------------------------------
9// Rule Definition
10//------------------------------------------------------------------------------
11
12module.exports = function(context) {
13 var style = context.options[0] || "1tbs",
14 params = context.options[1] || {},
15 sourceCode = context.getSourceCode();
16
17 var OPEN_MESSAGE = "Opening curly brace does not appear on the same line as controlling statement.",
18 OPEN_MESSAGE_ALLMAN = "Opening curly brace appears on the same line as controlling statement.",
19 BODY_MESSAGE = "Statement inside of curly braces should be on next line.",
20 CLOSE_MESSAGE = "Closing curly brace does not appear on the same line as the subsequent block.",
21 CLOSE_MESSAGE_SINGLE = "Closing curly brace should be on the same line as opening curly brace or on the line after the previous block.",
22 CLOSE_MESSAGE_STROUSTRUP_ALLMAN = "Closing curly brace appears on the same line as the subsequent block.";
23
24 //--------------------------------------------------------------------------
25 // Helpers
26 //--------------------------------------------------------------------------
27
28 /**
29 * Determines if a given node is a block statement.
30 * @param {ASTNode} node The node to check.
31 * @returns {boolean} True if the node is a block statement, false if not.
32 * @private
33 */
34 function isBlock(node) {
35 return node && node.type === "BlockStatement";
36 }
37
38 /**
39 * Check if the token is an punctuator with a value of curly brace
40 * @param {object} token - Token to check
41 * @returns {boolean} true if its a curly punctuator
42 * @private
43 */
44 function isCurlyPunctuator(token) {
45 return token.value === "{" || token.value === "}";
46 }
47
48 /**
49 * Binds a list of properties to a function that verifies that the opening
50 * curly brace is on the same line as its controlling statement of a given
51 * node.
52 * @param {...string} The properties to check on the node.
53 * @returns {Function} A function that will perform the check on a node
54 * @private
55 */
56 function checkBlock() {
57 var blockProperties = arguments;
58
59 return function(node) {
60 [].forEach.call(blockProperties, function(blockProp) {
61 var block = node[blockProp], previousToken, curlyToken, curlyTokenEnd, curlyTokensOnSameLine;
62
63 if (!isBlock(block)) {
64 return;
65 }
66
67 previousToken = sourceCode.getTokenBefore(block);
68 curlyToken = sourceCode.getFirstToken(block);
69 curlyTokenEnd = sourceCode.getLastToken(block);
70 curlyTokensOnSameLine = curlyToken.loc.start.line === curlyTokenEnd.loc.start.line;
71
72 if (style !== "allman" && previousToken.loc.start.line !== curlyToken.loc.start.line) {
73 context.report(node, OPEN_MESSAGE);
74 } else if (style === "allman" && previousToken.loc.start.line === curlyToken.loc.start.line && !params.allowSingleLine) {
75 context.report(node, OPEN_MESSAGE_ALLMAN);
76 }
77
78 if (!block.body.length || curlyTokensOnSameLine && params.allowSingleLine) {
79 return;
80 }
81
82 if (curlyToken.loc.start.line === block.body[0].loc.start.line) {
83 context.report(block.body[0], BODY_MESSAGE);
84 } else if (curlyTokenEnd.loc.start.line === block.body[block.body.length - 1].loc.start.line) {
85 context.report(block.body[block.body.length - 1], CLOSE_MESSAGE_SINGLE);
86 }
87 });
88 };
89 }
90
91 /**
92 * Enforces the configured brace style on IfStatements
93 * @param {ASTNode} node An IfStatement node.
94 * @returns {void}
95 * @private
96 */
97 function checkIfStatement(node) {
98 var tokens;
99
100 checkBlock("consequent", "alternate")(node);
101
102 if (node.alternate) {
103
104 tokens = sourceCode.getTokensBefore(node.alternate, 2);
105
106 if (style === "1tbs") {
107 if (tokens[0].loc.start.line !== tokens[1].loc.start.line &&
108 node.consequent.type === "BlockStatement" &&
109 isCurlyPunctuator(tokens[0]) ) {
110 context.report(node.alternate, CLOSE_MESSAGE);
111 }
112 } else if (tokens[0].loc.start.line === tokens[1].loc.start.line) {
113 context.report(node.alternate, CLOSE_MESSAGE_STROUSTRUP_ALLMAN);
114 }
115
116 }
117 }
118
119 /**
120 * Enforces the configured brace style on TryStatements
121 * @param {ASTNode} node A TryStatement node.
122 * @returns {void}
123 * @private
124 */
125 function checkTryStatement(node) {
126 var tokens;
127
128 checkBlock("block", "finalizer")(node);
129
130 if (isBlock(node.finalizer)) {
131 tokens = sourceCode.getTokensBefore(node.finalizer, 2);
132 if (style === "1tbs") {
133 if (tokens[0].loc.start.line !== tokens[1].loc.start.line) {
134 context.report(node.finalizer, CLOSE_MESSAGE);
135 }
136 } else if (tokens[0].loc.start.line === tokens[1].loc.start.line) {
137 context.report(node.finalizer, CLOSE_MESSAGE_STROUSTRUP_ALLMAN);
138 }
139 }
140 }
141
142 /**
143 * Enforces the configured brace style on CatchClauses
144 * @param {ASTNode} node A CatchClause node.
145 * @returns {void}
146 * @private
147 */
148 function checkCatchClause(node) {
149 var previousToken = sourceCode.getTokenBefore(node),
150 firstToken = sourceCode.getFirstToken(node);
151
152 checkBlock("body")(node);
153
154 if (isBlock(node.body)) {
155 if (style === "1tbs") {
156 if (previousToken.loc.start.line !== firstToken.loc.start.line) {
157 context.report(node, CLOSE_MESSAGE);
158 }
159 } else {
160 if (previousToken.loc.start.line === firstToken.loc.start.line) {
161 context.report(node, CLOSE_MESSAGE_STROUSTRUP_ALLMAN);
162 }
163 }
164 }
165 }
166
167 /**
168 * Enforces the configured brace style on SwitchStatements
169 * @param {ASTNode} node A SwitchStatement node.
170 * @returns {void}
171 * @private
172 */
173 function checkSwitchStatement(node) {
174 var tokens;
175 if (node.cases && node.cases.length) {
176 tokens = sourceCode.getTokensBefore(node.cases[0], 2);
177 } else {
178 tokens = sourceCode.getLastTokens(node, 3);
179 }
180
181 if (style !== "allman" && tokens[0].loc.start.line !== tokens[1].loc.start.line) {
182 context.report(node, OPEN_MESSAGE);
183 } else if (style === "allman" && tokens[0].loc.start.line === tokens[1].loc.start.line) {
184 context.report(node, OPEN_MESSAGE_ALLMAN);
185 }
186 }
187
188 //--------------------------------------------------------------------------
189 // Public API
190 //--------------------------------------------------------------------------
191
192 return {
193 "FunctionDeclaration": checkBlock("body"),
194 "FunctionExpression": checkBlock("body"),
195 "ArrowFunctionExpression": checkBlock("body"),
196 "IfStatement": checkIfStatement,
197 "TryStatement": checkTryStatement,
198 "CatchClause": checkCatchClause,
199 "DoWhileStatement": checkBlock("body"),
200 "WhileStatement": checkBlock("body"),
201 "WithStatement": checkBlock("body"),
202 "ForStatement": checkBlock("body"),
203 "ForInStatement": checkBlock("body"),
204 "ForOfStatement": checkBlock("body"),
205 "SwitchStatement": checkSwitchStatement
206 };
207
208};
209
210module.exports.schema = [
211 {
212 "enum": ["1tbs", "stroustrup", "allman"]
213 },
214 {
215 "type": "object",
216 "properties": {
217 "allowSingleLine": {
218 "type": "boolean"
219 }
220 },
221 "additionalProperties": false
222 }
223];