UNPKG

5.26 kBJavaScriptView Raw
1/**
2 * Disallows multiple `var` declaration (except for-loop).
3 *
4 * Types: `Boolean` or `Object`
5 *
6 * Values:
7 *
8 * - `true` disallows multiple variable declarations except within a for loop
9 * - `Object`:
10 * - `'strict'` disallows all multiple variable declarations
11 * - `'allExcept'` array of exceptions:
12 * - `'undefined'` allows declarations where all variables are not defined
13 * - `'require'` allows declarations where all variables are importing external modules with require
14 *
15 * #### Example
16 *
17 * ```js
18 * "disallowMultipleVarDecl": true
19 * ```
20 *
21 * ##### Valid for `true`
22 *
23 * ```js
24 * var x = 1;
25 * var y = 2;
26 *
27 * for (var i = 0, j = arr.length; i < j; i++) {}
28 * ```
29 *
30 * ##### Valid for `{ strict: true }`
31 *
32 * ```js
33 * var x = 1;
34 * var y = 2;
35 * ```
36 *
37 * ##### Valid for `{ allExcept: ['undefined'] }`
38 *
39 * ```js
40 * var a, b;
41 * var x = 1;
42 * var y = 2;
43 *
44 * for (var i = 0, j = arr.length; i < j; i++) {}
45 * ```
46 * ##### Valid for `{ allExcept: ['require'] }`
47 *
48 * ```js
49 * var a = require('a'),
50 * b = require('b');
51 *
52 * var x = 1;
53 * var y = 2;
54 *
55 * for (var i = 0, j = arr.length; i < j; i++) {}
56 * ```
57 *
58 * ##### Invalid
59 *
60 * ```js
61 * var x = 1,
62 * y = 2;
63 *
64 * var x, y = 2, z;
65 * ```
66 */
67
68var assert = require('assert');
69
70module.exports = function() {};
71
72module.exports.prototype = {
73
74 configure: function(options) {
75 // support for legacy options
76 if (typeof options !== 'object') {
77 assert(
78 options === true ||
79 options === 'strict' ||
80 options === 'exceptUndefined',
81 this.getOptionName() +
82 ' option requires a true value, "strict", "exceptUndefined", or an object'
83 );
84
85 var _options = {
86 strict: options === 'strict',
87 allExcept: []
88 };
89
90 if (options === 'exceptUndefined') {
91 _options.allExcept.push('undefined');
92 }
93
94 return this.configure(_options);
95 }
96
97 if (Array.isArray(options.allExcept)) {
98 this._exceptUndefined = options.allExcept.indexOf('undefined') > -1;
99 this._exceptRequire = options.allExcept.indexOf('require') > -1;
100 }
101
102 this._strictMode = options.strict === true;
103 },
104
105 getOptionName: function() {
106 return 'disallowMultipleVarDecl';
107 },
108
109 check: function(file, errors) {
110
111 function isSourcedFromRequire(node) {
112 // If this node is a CallExpression it has a callee,
113 // check if this is the `require` function
114 if (node.callee && node.callee.name === 'require') {
115 return true;
116 }
117
118 // If this CallExpression is not a `require` we keep looking for
119 // the `require` method up in the tree
120 if (node.callee && node.callee.object) {
121 return isSourcedFromRequire(node.callee.object);
122 }
123
124 // If there is no `callee` this might be a MemberExpression, keep
125 // look for the `require` method up in the tree.
126 if (node.object) {
127 return isSourcedFromRequire(node.object);
128 }
129
130 return false;
131 }
132
133 var inStrictMode = this._strictMode;
134 var exceptUndefined = this._exceptUndefined;
135 var exceptRequire = this._exceptRequire;
136
137 file.iterateNodesByType('VariableDeclaration', function(node) {
138 var definedVariables = node.declarations.filter(function(declaration) {
139 return !!declaration.init;
140 });
141 var hasDefinedVariables = definedVariables.length > 0;
142
143 var requireStatements = node.declarations.filter(function(declaration) {
144 var init = declaration.init;
145 return init && isSourcedFromRequire(init);
146 });
147 var allRequireStatements = requireStatements.length === node.declarations.length;
148
149 var isForStatement = node.parentElement.type === 'ForStatement';
150
151 // allow single var declarations
152 if (node.declarations.length === 1) {
153 return;
154 }
155
156 // allow multiple var declarations in for statement unless we're in strict mode
157 // for (var i = 0, j = myArray.length; i < j; i++) {}
158 if (!inStrictMode && isForStatement) {
159 return;
160 }
161
162 // allow multiple var declarations with all undefined variables in exceptUndefined mode
163 // var a, b, c
164 if (exceptUndefined && !hasDefinedVariables) {
165 return;
166 }
167
168 // allow multiple var declaration with all require
169 // var a = require("a"), b = require("b")
170 if (exceptRequire && allRequireStatements) {
171 return;
172 }
173
174 // allow multiple var declarations only with require && undefined
175 // var a = require("a"), b = require("b"), x, y
176 if (exceptUndefined && exceptRequire && definedVariables.length === requireStatements.length) {
177 return;
178 }
179
180 errors.add('Multiple var declaration', node);
181 });
182 }
183};