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 |
|
109 | var assert = require('assert');
|
110 |
|
111 | function 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 | */
|
126 | function 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 |
|
142 | function 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 |
|
153 | module.exports = function() {};
|
154 |
|
155 | module.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 | };
|