UNPKG

6.65 kBJavaScriptView Raw
1/**
2 * Disallows unused params in function expression and function declaration.
3 *
4 * Types: `Boolean`
5 *
6 * Values: `true`
7 *
8 * #### Example
9 *
10 * ```js
11 * "disallowUnusedParams": true
12 * ```
13 *
14 * ##### Valid
15 *
16 * ```js
17 * function x(test) {
18 * return test;
19 * }
20 *
21 * var x = function(test) {
22 * return test;
23 * }
24 * ```
25 *
26 * ##### Invalid
27 *
28 * ```js
29 * function x(test) {
30 * }
31 *
32 * var x = function(test) {
33 * }
34 * ```
35 */
36
37var assert = require('assert');
38
39function getUnusedNodes(node, variableMap, groupIfPossible) {
40 if (node.type === 'AssignmentPattern') {
41 return getUnusedNodes(node.left, variableMap, groupIfPossible);
42 }
43
44 if (node.type === 'Identifier') {
45 var variable = variableMap[node.name];
46
47 var hasUsages = variable.getReferences().some(function(reference) {
48 return !variable.getDefinitions().some(function(definition) {
49 return reference.node === definition.node;
50 });
51 });
52
53 if (hasUsages) {
54 return [];
55 }
56
57 return [node];
58 }
59
60 if (node.type === 'RestElement') {
61 var restUnusedNodes = getUnusedNodes(node.argument, variableMap, groupIfPossible);
62 if (groupIfPossible && restUnusedNodes.length === 1 && node.argument === restUnusedNodes[0]) {
63 return [node];
64 }
65 return restUnusedNodes;
66 }
67
68 if (node.type === 'ArrayPattern') {
69 var unusedArrayNodes = [];
70 node.elements.forEach(function(element) {
71 if (element) {
72 unusedArrayNodes = unusedArrayNodes.concat(getUnusedNodes(element, variableMap, groupIfPossible));
73 return;
74 }
75 unusedArrayNodes.push(null);
76 });
77 if (groupIfPossible && unusedArrayNodes.length === node.elements.length) {
78 if (node.elements.every(function(element, index) {
79 return unusedArrayNodes[index] === element;
80 })) {
81 return [node];
82 }
83 }
84 return unusedArrayNodes.filter(function(node) { return node; });
85 }
86
87 if (node.type === 'ObjectPattern') {
88 var unusedObjectNodes = [];
89 node.properties.forEach(function(property) {
90 unusedObjectNodes = unusedObjectNodes.concat(getUnusedNodes(property.value, variableMap, groupIfPossible));
91 });
92 if (groupIfPossible && unusedObjectNodes.length === node.properties.length) {
93 if (node.properties.every(function(property, index) {
94 return unusedObjectNodes[index] === property.value;
95 })) {
96 return [node];
97 }
98 }
99 return unusedObjectNodes;
100 }
101
102 return [];
103}
104
105function isComma(token) {
106 return token.type === 'Punctuator' && token.value === ',';
107}
108
109function removeWithCommaIfNecessary(node, removeForwards) {
110 var previousCodeToken = node.getPreviousCodeToken();
111 if (isComma(previousCodeToken)) {
112 node.parentElement.removeChildren(previousCodeToken, node);
113 return;
114 }
115
116 var nextCodeToken = node.getNextCodeToken();
117 if (removeForwards && isComma(nextCodeToken)) {
118 node.parentElement.removeChildren(node, nextCodeToken);
119 return;
120 }
121
122 var previousNode = node;
123 while (previousNode.previousSibling.isToken && !previousNode.previousSibling.isCode) {
124 previousNode = previousNode.previousSibling;
125 }
126 var nextNode = node;
127 while (nextNode.nextSibling.isToken && !nextNode.nextSibling.isCode) {
128 nextNode = nextNode.nextSibling;
129 }
130
131 node.parentElement.removeChild(previousNode, nextNode);
132}
133
134module.exports = function() {};
135
136module.exports.prototype = {
137 configure: function(options) {
138 assert(
139 options === true,
140 this.getOptionName() + ' option requires a true value or should be removed'
141 );
142 },
143
144 getOptionName: function() {
145 return 'disallowUnusedParams';
146 },
147
148 check: function(file, errors) {
149
150 function reportError(node) {
151 if (node.type === 'Identifier') {
152 errors.add('Param `' + node.name + '` is not used', node);
153 } else {
154 errors.add('Pattern is not used', node);
155 }
156 }
157
158 file.iterateNodesByType(
159 ['FunctionDeclaration', 'FunctionExpression', 'ArrowFunctionExpression'],
160 function(node) {
161 var variableMap = file.getScopes().acquire(node).getVariables()
162 .filter(function(variable) {
163 return variable.type === 'Parameter';
164 })
165 .reduce(function(obj, variable) {
166 obj[variable.name] = variable;
167 return obj;
168 }, {});
169
170 var params = node.params;
171 var reportUnusedDirectParams = true;
172
173 for (var i = params.length - 1; i >= 0; i--) {
174 var param = params[i];
175
176 if (!reportUnusedDirectParams && param.type === 'Identifier') {
177 continue;
178 }
179
180 var unusedNodes = getUnusedNodes(param, variableMap, reportUnusedDirectParams);
181
182 unusedNodes.forEach(reportError);
183
184 if (unusedNodes.length !== 1 || unusedNodes[0] !== param) {
185 reportUnusedDirectParams = false;
186 }
187 }
188 }
189 );
190 },
191
192 _fix: function(file, error) {
193 var node = error.element;
194
195 var parentElement = node.parentElement;
196 if (!parentElement) {
197 return;
198 }
199
200 if (
201 parentElement.type === 'FunctionExpression' ||
202 parentElement.type === 'FunctionDeclaration' ||
203 parentElement.type === 'ArrowFunctionExpression'
204 ) {
205 removeWithCommaIfNecessary(node, false);
206 return;
207 }
208
209 if (parentElement.type === 'ObjectProperty') {
210 removeWithCommaIfNecessary(parentElement, true);
211 return;
212 }
213
214 if (parentElement.type === 'ArrayPattern') {
215 removeWithCommaIfNecessary(node, false);
216 if (
217 parentElement.elements.length > 0 &&
218 parentElement.elements.every(function(element) {
219 return !element;
220 })
221 ) {
222 parentElement.removeChildren(
223 parentElement.firstChild.nextSibling,
224 parentElement.lastChild.previousSibling
225 );
226 }
227 }
228 }
229};