1 | /**
|
2 | * Requires blocks to begin and end with 2 newlines
|
3 | *
|
4 | * Types: `Boolean`, `Integer`, `Object`
|
5 | *
|
6 | * Values:
|
7 | * - `true` validates all non-empty blocks
|
8 | * - `Integer` specifies a minimum number of lines containing elements in the block before validating
|
9 | * - `Object` (at least one of properties must be true):
|
10 | * - `'open'`
|
11 | * - `true` validates that there is a newline after the opening brace in a block
|
12 | * - `false` ignores the newline validation after the opening brace in a block
|
13 | * - `'close'`
|
14 | * - `true` validates that there is a newline before the closing brace in a block
|
15 | * - `false` ignores the newline validation before the closing brace in a block
|
16 | * - `'allExcept'` array of exceptions:
|
17 | * - `'conditionals'` ignores conditional (if, else if, else) blocks
|
18 | * - `'functions'` ignores function blocks
|
19 | *
|
20 | * #### Example
|
21 | *
|
22 | * ```js
|
23 | * "requirePaddingNewlinesInBlocks": true
|
24 | * "requirePaddingNewlinesInBlocks": 1
|
25 | * "requirePaddingNewlinesInBlocks": { "open": true, "close": false }
|
26 | * "requirePaddingNewlinesInBlocks": { "allExcept": [ "conditionals" ] }
|
27 | * "requirePaddingNewlinesInBlocks": { "open": true, "close": false, allExcept: ['conditionals'] }
|
28 | * ```
|
29 | *
|
30 | * ##### Valid for mode `true` or `{ "open": true, "close": true }`
|
31 | *
|
32 | * ```js
|
33 | * if (true) {
|
34 | *
|
35 | * doSomething();
|
36 | *
|
37 | * }
|
38 | * var abc = function() {};
|
39 | * ```
|
40 | *
|
41 | * ##### Invalid
|
42 | *
|
43 | * ```js
|
44 | * if (true) {doSomething();}
|
45 | * if (true) {
|
46 | * doSomething();
|
47 | * }
|
48 | * ```
|
49 | *
|
50 | * ##### Valid for mode `1`
|
51 | *
|
52 | * ```js
|
53 | * if (true) {
|
54 | *
|
55 | * doSomething();
|
56 | * doSomethingElse();
|
57 | *
|
58 | * }
|
59 | * if (true) {
|
60 | * doSomething();
|
61 | * }
|
62 | * if (true) { doSomething(); }
|
63 | * var abc = function() {};
|
64 | * ```
|
65 | *
|
66 | * ##### Invalid
|
67 | *
|
68 | * ```js
|
69 | * if (true) { doSomething(); doSomethingElse(); }
|
70 | * if (true) {
|
71 | * doSomething();
|
72 | * doSomethingElse();
|
73 | * }
|
74 | * ```
|
75 | *
|
76 | * ##### Valid for mode `{ "open": true, "close": false }`
|
77 | *
|
78 | * ```js
|
79 | * if (true) {
|
80 | *
|
81 | * doSomething();
|
82 | * }
|
83 | * var abc = function() {};
|
84 | * ```
|
85 | *
|
86 | * ##### Invalid
|
87 | *
|
88 | * ```js
|
89 | * if (true) {doSomething();}
|
90 | * if (true) {
|
91 | * doSomething();
|
92 | * }
|
93 | * if (true) {
|
94 | * doSomething();
|
95 | *
|
96 | * }
|
97 | * ```
|
98 | *
|
99 | * ##### Valid for `{ allExcept: ['conditionals'] }`
|
100 | *
|
101 | * ```js
|
102 | * if (true) {
|
103 | * doSomething();
|
104 | * }
|
105 | *
|
106 | * function (foo) {
|
107 | *
|
108 | * return bar;
|
109 | *
|
110 | * }
|
111 | * ```
|
112 | *
|
113 | * ##### Invalid
|
114 | *
|
115 | * ```js
|
116 | * function (foo) {
|
117 | * return bar;
|
118 | * }
|
119 | * ```
|
120 | *
|
121 | * ##### Valid for `{ "open": true, "close": false, allExcept: ['conditionals'] }`
|
122 | *
|
123 | * ```js
|
124 | * function (foo) {
|
125 | *
|
126 | * return bar;
|
127 | * }
|
128 | *
|
129 | * if (true) {
|
130 | * doSomething();
|
131 | * }
|
132 | * ```
|
133 | *
|
134 | * ##### Invalid
|
135 | *
|
136 | * ```js
|
137 | * function (foo) {
|
138 | * return bar;
|
139 | *
|
140 | * }
|
141 | * ```
|
142 | */
|
143 |
|
144 | var assert = require('assert');
|
145 |
|
146 | module.exports = function() {};
|
147 |
|
148 | module.exports.prototype = {
|
149 |
|
150 | configure: function(options) {
|
151 | var optionName = this.getOptionName();
|
152 |
|
153 | assert(
|
154 | options === true || typeof options === 'number' || typeof options === 'object',
|
155 | optionName + ' option requires the value true, an Integer or an object'
|
156 | );
|
157 |
|
158 | this._checkOpen = true;
|
159 | this._checkClose = true;
|
160 | this._minLines = 0;
|
161 |
|
162 | if (typeof options === 'object') {
|
163 | assert(options.allExcept || options.open || options.close,
|
164 | optionName + 'option requires either "open", "close", "allExcept"');
|
165 |
|
166 | if (options.allExcept) {
|
167 | assert(Array.isArray(options.allExcept), optionName + ' option requires "allExcept" to be an array');
|
168 | assert(options.allExcept.length > 0, optionName + ' option requires "allExcept" to have at least one ' +
|
169 | 'item or be set to `true`');
|
170 | this._exceptConditionals = options.allExcept.indexOf('conditionals') > -1;
|
171 | this._exceptFunctions = options.allExcept.indexOf('functions') > -1;
|
172 | }
|
173 |
|
174 | if (options.open || options.close) {
|
175 | assert(typeof options.open === 'boolean' && typeof options.close === 'boolean',
|
176 | this.getOptionName() + ' option requires the "open" and "close" ' +
|
177 | 'properties to be booleans');
|
178 |
|
179 | this._checkOpen = options.open;
|
180 | this._checkClose = options.close;
|
181 | }
|
182 | } else if (typeof options === 'number') {
|
183 | this._minLines = options;
|
184 | } else {
|
185 | assert(options === true, this.getOptionName() + ' option requires either a true value, or an object');
|
186 | }
|
187 | },
|
188 |
|
189 | getOptionName: function() {
|
190 | return 'requirePaddingNewlinesInBlocks';
|
191 | },
|
192 |
|
193 | check: function(file, errors) {
|
194 | var minLines = this._minLines;
|
195 | var exceptConditionals = this._exceptConditionals;
|
196 | var exceptFunctions = this._exceptFunctions;
|
197 | var checkOpen = this._checkOpen;
|
198 | var checkClose = this._checkClose;
|
199 |
|
200 | file.iterateNodesByType('BlockStatement', function(node) {
|
201 | var openingBracket;
|
202 | var closingBracket;
|
203 |
|
204 | if (node.body.length <= minLines) {
|
205 | return;
|
206 | }
|
207 |
|
208 | if (exceptConditionals && node.parentElement.type === 'IfStatement' ||
|
209 | exceptFunctions && (node.parentElement.type === 'FunctionExpression' ||
|
210 | node.parentElement.type === 'FunctionDeclaration')) {
|
211 | return;
|
212 | }
|
213 |
|
214 | if (checkOpen === true) {
|
215 | openingBracket = node.getFirstToken();
|
216 |
|
217 | errors.assert.linesBetween({
|
218 | token: openingBracket,
|
219 | nextToken: file.getNextToken(openingBracket, {includeComments: true}),
|
220 | atLeast: 2,
|
221 | message: 'Expected a padding newline after opening curly brace'
|
222 | });
|
223 | }
|
224 |
|
225 | if (checkClose === true) {
|
226 | closingBracket = file.getLastNodeToken(node);
|
227 |
|
228 | errors.assert.linesBetween({
|
229 | token: file.getPrevToken(closingBracket, {includeComments: true}),
|
230 | nextToken: closingBracket,
|
231 | atLeast: 2,
|
232 | message: 'Expected a padding newline before closing curly brace'
|
233 | });
|
234 | }
|
235 | });
|
236 | }
|
237 | };
|