UNPKG

5.17 kBJavaScriptView Raw
1/**
2 * Requires curly braces after statements.
3 *
4 * Types: `Array` or `Boolean` or `Object`
5 *
6 * Values:
7 * - Array of quoted keywords
8 * - `true` to require curly braces after the following keywords
9 * - `Object`
10 * - `'keywords'`
11 * - Array of quoted keywords
12 * - `'allExcept'`
13 * - Array of keywords inside of the block that would allow curly braces
14 * - Ex: ["return" , "continue", "break"]
15 *
16 * JSHint: [`curly`](http://jshint.com/docs/options/#curly)
17 *
18 * #### Example
19 *
20 * ```js
21 * "requireCurlyBraces": [
22 * "if",
23 * "else",
24 * "for",
25 * "while",
26 * "do",
27 * "try",
28 * "catch",
29 * "case",
30 * "default"
31 * ]
32 * ```
33 *
34 * ##### Valid
35 *
36 * ```js
37 * if (x) {
38 * x++;
39 * }
40 * ```
41 *
42 * ##### Invalid
43 *
44 * ```js
45 * if (x) x++;
46 * ```
47 */
48
49var assert = require('assert');
50var defaultKeywords = require('../utils').curlyBracedKeywords;
51
52module.exports = function() {};
53
54module.exports.prototype = {
55
56 configure: function(options) {
57 assert(
58 Array.isArray(options) || options === true || typeof options === 'object',
59 this.getOptionName() + ' option requires array, true value, or object'
60 );
61
62 var keywordMap = {
63 'return': 'ReturnStatement',
64 'break': 'BreakStatement',
65 'continue': 'ContinueStatement'
66 };
67
68 if (options === true) {
69 options = defaultKeywords;
70 }
71
72 if (!Array.isArray(options)) {
73 assert(
74 Array.isArray(options.allExcept),
75 this.getOptionName() + '.allExcept ' +
76 'property requires an array value'
77 );
78 assert(
79 Array.isArray(options.keywords) || options.keywords === true,
80 this.getOptionName() + '.keywords ' +
81 'property requires an array value or a value of true'
82 );
83
84 if (options.keywords === true) {
85 options.keywords = defaultKeywords;
86 }
87
88 this._exceptions = options.allExcept.map(function(statementType) {
89 return keywordMap[statementType];
90 });
91 options = options.keywords;
92 }
93
94 this._typeIndex = {};
95 for (var i = 0, l = options.length; i < l; i++) {
96 this._typeIndex[options[i]] = true;
97 }
98 },
99
100 getOptionName: function() {
101 return 'requireCurlyBraces';
102 },
103
104 check: function(file, errors) {
105 var typeIndex = this._typeIndex;
106 var exceptions = this._exceptions;
107
108 function isNotABlockStatement(node) {
109 return node && node.type !== 'BlockStatement';
110 }
111
112 function addError(typeString, entity) {
113 errors.add(
114 typeString + ' statement without curly braces',
115 entity
116 );
117 }
118
119 function checkBody(type, typeString) {
120 file.iterateNodesByType(type, function(node) {
121 if (isNotABlockStatement(node.body)) {
122 addError(typeString, node);
123 }
124 });
125 }
126
127 if (typeIndex.if || typeIndex.else) {
128 file.iterateNodesByType('IfStatement', function(node) {
129 if (typeIndex.if && isNotABlockStatement(node.consequent) &&
130 // check exceptions for if and else
131 !(exceptions && exceptions.indexOf(node.consequent.type) !== -1)) {
132 // console.log(node.firstChild.getSourceCode());
133 addError('If', node.firstChild);
134 }
135 if (typeIndex.else && isNotABlockStatement(node.alternate) &&
136 node.alternate.type !== 'IfStatement' &&
137 // check exceptions for if and else
138 !(exceptions && exceptions.indexOf(node.consequent.type) !== -1)) {
139 addError('Else', node.alternate.getPreviousCodeToken());
140 }
141 });
142 }
143
144 if (typeIndex.case || typeIndex.default) {
145 file.iterateNodesByType('SwitchCase', function(node) {
146 // empty case statement
147 if (node.consequent.length === 0) {
148 return;
149 }
150
151 if (node.consequent.length === 1 && node.consequent[0].type === 'BlockStatement') {
152 return;
153 }
154
155 if (node.test === null && typeIndex.default) {
156 addError('Default', node);
157 }
158
159 if (node.test !== null && typeIndex.case) {
160 addError('Case', node);
161 }
162 });
163 }
164
165 if (typeIndex.while) {
166 checkBody('WhileStatement', 'While');
167 }
168
169 if (typeIndex.for) {
170 checkBody('ForStatement', 'For');
171 checkBody('ForInStatement', 'For in');
172 checkBody('ForOfStatement', 'For of');
173 }
174
175 if (typeIndex.do) {
176 checkBody('DoWhileStatement', 'Do while');
177 }
178
179 if (typeIndex.with) {
180 checkBody('WithStatement', 'With');
181 }
182 }
183
184};