UNPKG

13.3 kBJavaScriptView Raw
1"use strict";
2var __extends = (this && this.__extends) || (function () {
3 var extendStatics = function (d, b) {
4 extendStatics = Object.setPrototypeOf ||
5 ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
6 function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
7 return extendStatics(d, b);
8 }
9 return function (d, b) {
10 extendStatics(d, b);
11 function __() { this.constructor = d; }
12 d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
13 };
14})();
15Object.defineProperty(exports, "__esModule", { value: true });
16var ts = require("typescript");
17var Lint = require("tslint");
18var tsutils = require("tsutils");
19var Utils_1 = require("./utils/Utils");
20var TypeGuard_1 = require("./utils/TypeGuard");
21var PROPS_REGEX = 'props-interface-regex';
22var STATE_REGEX = 'state-interface-regex';
23var FAILURE_UNUSED_PROP = 'Unused React property defined in interface: ';
24var FAILURE_UNUSED_STATE = 'Unused React state defined in interface: ';
25var Rule = (function (_super) {
26 __extends(Rule, _super);
27 function Rule() {
28 return _super !== null && _super.apply(this, arguments) || this;
29 }
30 Rule.prototype.apply = function (sourceFile) {
31 if (sourceFile.languageVariant === ts.LanguageVariant.JSX) {
32 return this.applyWithFunction(sourceFile, walk, this.parseOptions(this.getOptions()));
33 }
34 else {
35 return [];
36 }
37 };
38 Rule.prototype.parseOptions = function (options) {
39 var _this = this;
40 var parsed = {
41 propsInterfaceRegex: /^Props$/,
42 stateInterfaceRegex: /^State$/
43 };
44 options.ruleArguments.forEach(function (opt) {
45 if (TypeGuard_1.isObject(opt)) {
46 parsed.propsInterfaceRegex = _this.getOptionOrDefault(opt, PROPS_REGEX, parsed.propsInterfaceRegex);
47 parsed.stateInterfaceRegex = _this.getOptionOrDefault(opt, STATE_REGEX, parsed.stateInterfaceRegex);
48 }
49 });
50 return parsed;
51 };
52 Rule.prototype.getOptionOrDefault = function (option, key, defaultValue) {
53 try {
54 var value = option[key];
55 if (value !== undefined && typeof value === 'string') {
56 return new RegExp(value);
57 }
58 }
59 catch (e) {
60 console.error('Could not read ' + key + ' within react-unused-props-and-state-name configuration');
61 }
62 return defaultValue;
63 };
64 Rule.metadata = {
65 ruleName: 'react-unused-props-and-state',
66 type: 'maintainability',
67 description: 'Remove unneeded properties defined in React Props and State interfaces',
68 options: null,
69 optionsDescription: '',
70 typescriptOnly: true,
71 issueClass: 'Non-SDL',
72 issueType: 'Warning',
73 severity: 'Low',
74 level: 'Opportunity for Excellence',
75 group: 'Correctness',
76 commonWeaknessEnumeration: '398'
77 };
78 return Rule;
79}(Lint.Rules.AbstractRule));
80exports.Rule = Rule;
81function walk(ctx) {
82 var propNames = [];
83 var propNodes = {};
84 var stateNames = [];
85 var stateNodes = {};
86 var classDeclarations = [];
87 var arrowFunctions = [];
88 var functionComponents = [];
89 var propsAlias;
90 var stateAlias;
91 function getTypeElementData(node) {
92 var result = {};
93 node.members.forEach(function (typeElement) {
94 if (typeElement.name !== undefined) {
95 var text = typeElement.name.getText();
96 if (text !== undefined) {
97 result[text] = typeElement;
98 }
99 }
100 });
101 return result;
102 }
103 function getTypeLiteralData(node) {
104 var result = {};
105 node.members.forEach(function (typeElement) {
106 if (typeElement.name !== undefined) {
107 var text = typeElement.name.getText();
108 if (text !== undefined) {
109 result[text] = typeElement;
110 }
111 }
112 });
113 return result;
114 }
115 function getObjectBindingData(node) {
116 var result = {};
117 node.elements.forEach(function (element) {
118 if (element.name !== undefined) {
119 var text = element.name.getText();
120 if (text !== undefined) {
121 result[text] = element;
122 }
123 }
124 });
125 return result;
126 }
127 function isParentNodeSuperCall(node) {
128 if (node.parent !== undefined && node.parent.kind === ts.SyntaxKind.CallExpression) {
129 var call = node.parent;
130 return call.expression.getText() === 'super';
131 }
132 return false;
133 }
134 function inspectPropUsageInObjectBinding(name) {
135 var bindingElements = getObjectBindingData(name);
136 var foundPropNames = Object.keys(bindingElements);
137 for (var _i = 0, foundPropNames_1 = foundPropNames; _i < foundPropNames_1.length; _i++) {
138 var propName = foundPropNames_1[_i];
139 propNames = Utils_1.Utils.remove(propNames, propName);
140 }
141 }
142 function lookForReactSpecificArrowFunction(node) {
143 var nodeTypeText = node.typeName.getText();
144 var isReactFunctionComponentType = nodeTypeText === 'React.SFC' ||
145 nodeTypeText === 'SFC' ||
146 nodeTypeText === 'React.FC' ||
147 nodeTypeText === 'FC' ||
148 nodeTypeText === 'React.StatelessComponent' ||
149 nodeTypeText === 'StatelessComponent' ||
150 nodeTypeText === 'React.FunctionComponent' ||
151 nodeTypeText === 'FunctionComponent';
152 if (!isReactFunctionComponentType) {
153 return;
154 }
155 if (!node.typeArguments || node.typeArguments.length !== 1) {
156 return;
157 }
158 var typeArgument = node.typeArguments[0];
159 if (tsutils.isTypeLiteralNode(typeArgument)) {
160 propNodes = getTypeLiteralData(typeArgument);
161 propNames = Object.keys(propNodes);
162 }
163 else {
164 }
165 var arrowFunction = tsutils.getChildOfKind(node.parent, ts.SyntaxKind.ArrowFunction);
166 if (!arrowFunction || !tsutils.isArrowFunction(arrowFunction)) {
167 return;
168 }
169 lookForArrowFunction(arrowFunction);
170 }
171 function lookForArrowFunction(node) {
172 var parameters = node.parameters;
173 if (parameters.length !== 1) {
174 return;
175 }
176 var firstParameter = parameters[0];
177 var name = firstParameter.name, type = firstParameter.type;
178 if (type && tsutils.isTypeReferenceNode(type)) {
179 var typeName = type.typeName.getText();
180 if (!ctx.options.propsInterfaceRegex.test(typeName)) {
181 return;
182 }
183 }
184 if (tsutils.isIdentifier(name)) {
185 propsAlias = name.getText();
186 }
187 else if (tsutils.isObjectBindingPattern(name)) {
188 inspectPropUsageInObjectBinding(name);
189 }
190 arrowFunctions.push(node);
191 }
192 function lookForFunctionComponent(node) {
193 if (!node.body) {
194 return;
195 }
196 var parameters = node.parameters;
197 if (parameters.length !== 1) {
198 return;
199 }
200 var firstParameter = parameters[0];
201 var name = firstParameter.name, type = firstParameter.type;
202 if (type && tsutils.isTypeReferenceNode(type)) {
203 var typeName = type.typeName.getText();
204 if (!ctx.options.propsInterfaceRegex.test(typeName)) {
205 return;
206 }
207 }
208 if (tsutils.isIdentifier(name)) {
209 propsAlias = name.getText();
210 }
211 else if (tsutils.isObjectBindingPattern(name)) {
212 inspectPropUsageInObjectBinding(name);
213 }
214 functionComponents.push(node.body);
215 }
216 function cb(node) {
217 if (tsutils.isClassDeclaration(node)) {
218 classDeclarations.push(node);
219 return;
220 }
221 if (tsutils.isConstructorDeclaration(node)) {
222 if (node.parameters.length > 0) {
223 propsAlias = node.parameters[0].name.text;
224 }
225 ts.forEachChild(node, cb);
226 propsAlias = undefined;
227 return;
228 }
229 if (tsutils.isMethodDeclaration(node)) {
230 var methodName = node.name.text;
231 if (/componentWillReceiveProps|shouldComponentUpdate|componentWillUpdate|componentDidUpdate/.test(methodName) &&
232 node.parameters.length > 0) {
233 propsAlias = node.parameters[0].name.text;
234 }
235 if (/shouldComponentUpdate|componentWillUpdate|componentDidUpdate/.test(methodName) && node.parameters.length > 1) {
236 stateAlias = node.parameters[1].name.text;
237 }
238 ts.forEachChild(node, cb);
239 propsAlias = undefined;
240 stateAlias = undefined;
241 return;
242 }
243 if (tsutils.isInterfaceDeclaration(node)) {
244 if (ctx.options.propsInterfaceRegex.test(node.name.text)) {
245 propNodes = getTypeElementData(node);
246 propNames = Object.keys(propNodes);
247 }
248 if (ctx.options.stateInterfaceRegex.test(node.name.text)) {
249 stateNodes = getTypeElementData(node);
250 stateNames = Object.keys(stateNodes);
251 }
252 }
253 else if (tsutils.isPropertyAccessExpression(node)) {
254 var referencedPropertyName = node.getText();
255 if (/this\.props\..*/.test(referencedPropertyName)) {
256 propNames = Utils_1.Utils.remove(propNames, referencedPropertyName.substring(11));
257 }
258 else if (/this\.state\..*/.test(referencedPropertyName)) {
259 stateNames = Utils_1.Utils.remove(stateNames, referencedPropertyName.substring(11));
260 }
261 if (propsAlias !== undefined) {
262 if (new RegExp(propsAlias + '\\..*').test(referencedPropertyName)) {
263 propNames = Utils_1.Utils.remove(propNames, referencedPropertyName.substring(propsAlias.length + 1));
264 }
265 }
266 if (stateAlias !== undefined) {
267 if (new RegExp(stateAlias + '\\..*').test(referencedPropertyName)) {
268 stateNames = Utils_1.Utils.remove(stateNames, referencedPropertyName.substring(stateAlias.length + 1));
269 }
270 }
271 if (node.parent.kind !== ts.SyntaxKind.PropertyAccessExpression) {
272 if (referencedPropertyName === 'this.props') {
273 propNames = [];
274 }
275 else if (referencedPropertyName === 'this.state') {
276 stateNames = [];
277 }
278 }
279 }
280 else if (tsutils.isIdentifier(node)) {
281 if (propsAlias !== undefined) {
282 if (node.text === propsAlias &&
283 node.parent.kind !== ts.SyntaxKind.PropertyAccessExpression &&
284 node.parent.kind !== ts.SyntaxKind.Parameter &&
285 isParentNodeSuperCall(node) === false) {
286 propNames = [];
287 }
288 }
289 if (stateAlias !== undefined) {
290 if (node.text === stateAlias &&
291 node.parent.kind !== ts.SyntaxKind.PropertyAccessExpression &&
292 node.parent.kind !== ts.SyntaxKind.Parameter) {
293 stateNames = [];
294 }
295 }
296 }
297 else if (tsutils.isTypeReferenceNode(node)) {
298 lookForReactSpecificArrowFunction(node);
299 }
300 else if (tsutils.isArrowFunction(node)) {
301 lookForArrowFunction(node);
302 }
303 else if (tsutils.isFunctionDeclaration(node)) {
304 lookForFunctionComponent(node);
305 }
306 else if (tsutils.isFunctionExpression(node)) {
307 lookForFunctionComponent(node);
308 }
309 return ts.forEachChild(node, cb);
310 }
311 ts.forEachChild(ctx.sourceFile, cb);
312 if (propNames.length > 0 || stateNames.length > 0) {
313 classDeclarations.forEach(function (c) { return ts.forEachChild(c, cb); });
314 arrowFunctions.forEach(function (c) { return ts.forEachChild(c.body, cb); });
315 functionComponents.forEach(function (f) { return ts.forEachChild(f, cb); });
316 }
317 propNames.forEach(function (propName) {
318 var typeElement = propNodes[propName];
319 ctx.addFailureAt(typeElement.getStart(), typeElement.getWidth(), FAILURE_UNUSED_PROP + propName);
320 });
321 stateNames.forEach(function (stateName) {
322 var typeElement = stateNodes[stateName];
323 ctx.addFailureAt(typeElement.getStart(), typeElement.getWidth(), FAILURE_UNUSED_STATE + stateName);
324 });
325}
326//# sourceMappingURL=reactUnusedPropsAndStateRule.js.map
\No newline at end of file