UNPKG

3.42 kBJavaScriptView Raw
1/**
2 * Requires `var` declaration to be on the top of an enclosing scope
3 *
4 * Types: `Boolean`
5 *
6 * Values:
7 *
8 * - `true` specifies that `var` declarations must occur the top of a function scope.
9 *
10 * #### Example
11 *
12 * ```js
13 * "requireVarDeclFirst": true
14 * ```
15 *
16 * ##### Valid for mode `true`
17 *
18 * ```js
19 * var x = 1,
20 * y = 2;
21 * ```
22 * ```js
23 * 'use strict;'
24 * var x = 1,
25 * y = 2;
26 * ```
27 * ```js
28 * var x = 1;
29 * var y = 2;
30 * ```
31 * ```js
32 * var x = 1;
33 * // comments
34 * var y = 2;
35 * ```
36 * ```js
37 * var x = 1;
38 * // comments
39 * // comments 2
40 * var y = 2;
41 * ```
42 * ```js
43 * const a = 1;
44 * const b = 2;
45 * ```
46 * ```js
47 * var x = 1;
48 * function y() {var z;};
49 * ```
50 * ```js
51 * var x = 1;
52 * var y = function () {var z;};
53 * ```
54 * ```js
55 * var w = 1;
56 * function x() {
57 * var y;
58 * // comments
59 * // comments 2
60 * var z;
61 * };
62 * ```
63 * ```js
64 * var w = 1;
65 * function x() {
66 * "use strict";
67 * var y;
68 * };
69 * ```
70 * ```js
71 * var x = 1;
72 * var y;
73 * for (y = 0; y < 10; y++) {};
74 * ```
75 *
76 * ##### Invalid
77 *
78 * ```js
79 * var x;
80 * x = 1;
81 * var y = 2;
82 * ```
83 * ```js
84 * var w = 1;
85 * function x() {var y;};
86 * var z = 2;
87 * ```
88 * ```js
89 * var w = 1;
90 * function x() {
91 * var y;
92 * y = 2;
93 * var z;
94 * };
95 * ```
96 * ```js
97 * var a;
98 * for(var count=0;count < 10;count++){}
99 * ```
100 * ```js
101 * var x;
102 * for(var count=0;count < 10;count++){
103 * var y;
104 * }
105 * ```
106 *
107 */
108
109var assert = require('assert');
110
111function isScopeElement(elem) {
112 return elem.type === 'Program' ||
113 elem.type === 'BlockStatement' && (
114 elem.parentElement.type === 'FunctionExpression' ||
115 elem.parentElement.type === 'FunctionDeclaration' ||
116 elem.parentElement.type === 'ArrowFunctionExpression'
117 );
118}
119
120/**
121 * Checks for allowed elements before variable declaration.
122 *
123 * @param {Object} elem
124 * @returns {Boolean}
125 */
126function isRelevantElement(elem) {
127 // Allow comments and whitespaces.
128 var itIs = elem.isComment || elem.isWhitespace;
129
130 // Variable declaration itself.
131 itIs = itIs || elem.type === 'VariableDeclaration';
132
133 // For '{' in `function() { var a; }`.
134 itIs = itIs || elem.type === 'Punctuator';
135
136 // Skip 'use strict' statements at the beginning.
137 itIs = itIs || elem.type === 'Directive';
138
139 return itIs;
140}
141
142function isVarDeclFirst(varDecl) {
143 var elem = varDecl;
144
145 // All elements should be relevant.
146 while (elem && isRelevantElement(elem)) {
147 elem = elem.previousSibling;
148 }
149
150 return elem === null;
151}
152
153module.exports = function() {};
154
155module.exports.prototype = {
156 configure: function(options) {
157 assert(
158 options === true,
159 this.getOptionName() + ' option requires a true value'
160 );
161 },
162
163 getOptionName: function() {
164 return 'requireVarDeclFirst';
165 },
166
167 check: function(file, errors) {
168 file.iterateNodesByType(['VariableDeclaration'], function(varDecl) {
169 // Ignore let and const for now #1783
170 if (varDecl.kind !== 'var') {
171 return;
172 }
173
174 // Checking scope to not allow vars inside block statements.
175 if (isScopeElement(varDecl.parentElement) && isVarDeclFirst(varDecl)) {
176 return;
177 }
178
179 errors.add('Variable declarations must be the first statements of a function scope.',
180 varDecl);
181 });
182 }
183};