UNPKG

tslint-microsoft-contrib

Version:
324 lines 13.2 kB
"use strict"; var __extends = (this && this.__extends) || (function () { var extendStatics = function (d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; return extendStatics(d, b); } return function (d, b) { extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); Object.defineProperty(exports, "__esModule", { value: true }); var ts = require("typescript"); var Lint = require("tslint"); var tsutils = require("tsutils"); var Utils_1 = require("./utils/Utils"); var TypeGuard_1 = require("./utils/TypeGuard"); var PROPS_REGEX = 'props-interface-regex'; var STATE_REGEX = 'state-interface-regex'; var FAILURE_UNUSED_PROP = 'Unused React property defined in interface: '; var FAILURE_UNUSED_STATE = 'Unused React state defined in interface: '; var Rule = (function (_super) { __extends(Rule, _super); function Rule() { return _super !== null && _super.apply(this, arguments) || this; } Rule.prototype.apply = function (sourceFile) { if (sourceFile.languageVariant === ts.LanguageVariant.JSX) { return this.applyWithFunction(sourceFile, walk, this.parseOptions(this.getOptions())); } return []; }; Rule.prototype.parseOptions = function (options) { var _this = this; var parsed = { propsInterfaceRegex: /^Props$/, stateInterfaceRegex: /^State$/ }; options.ruleArguments.forEach(function (opt) { if (TypeGuard_1.isObject(opt)) { parsed.propsInterfaceRegex = _this.getOptionOrDefault(opt, PROPS_REGEX, parsed.propsInterfaceRegex); parsed.stateInterfaceRegex = _this.getOptionOrDefault(opt, STATE_REGEX, parsed.stateInterfaceRegex); } }); return parsed; }; Rule.prototype.getOptionOrDefault = function (option, key, defaultValue) { try { var value = option[key]; if (value !== undefined && typeof value === 'string') { return new RegExp(value); } } catch (e) { console.error('Could not read ' + key + ' within react-unused-props-and-state-name configuration'); } return defaultValue; }; Rule.metadata = { ruleName: 'react-unused-props-and-state', type: 'maintainability', description: 'Remove unneeded properties defined in React Props and State interfaces', options: null, optionsDescription: '', typescriptOnly: true, issueClass: 'Non-SDL', issueType: 'Warning', severity: 'Low', level: 'Opportunity for Excellence', group: 'Correctness', commonWeaknessEnumeration: '398' }; return Rule; }(Lint.Rules.AbstractRule)); exports.Rule = Rule; function walk(ctx) { var propNames = []; var propNodes = {}; var stateNames = []; var stateNodes = {}; var classDeclarations = []; var arrowFunctions = []; var functionComponents = []; var propsAlias; var stateAlias; function getTypeElementData(node) { var result = {}; node.members.forEach(function (typeElement) { if (typeElement.name !== undefined) { var text = typeElement.name.getText(); if (text !== undefined) { result[text] = typeElement; } } }); return result; } function getTypeLiteralData(node) { var result = {}; node.members.forEach(function (typeElement) { if (typeElement.name !== undefined) { var text = typeElement.name.getText(); if (text !== undefined) { result[text] = typeElement; } } }); return result; } function getObjectBindingData(node) { var result = {}; node.elements.forEach(function (element) { if (element.name !== undefined) { var text = element.name.getText(); if (text !== undefined) { result[text] = element; } } }); return result; } function isParentNodeSuperCall(node) { if (node.parent !== undefined && node.parent.kind === ts.SyntaxKind.CallExpression) { var call = node.parent; return call.expression.getText() === 'super'; } return false; } function inspectPropUsageInObjectBinding(name) { var bindingElements = getObjectBindingData(name); var foundPropNames = Object.keys(bindingElements); for (var _i = 0, foundPropNames_1 = foundPropNames; _i < foundPropNames_1.length; _i++) { var propName = foundPropNames_1[_i]; propNames = Utils_1.Utils.remove(propNames, propName); } } function lookForReactSpecificArrowFunction(node) { var nodeTypeText = node.typeName.getText(); var isReactFunctionComponentType = nodeTypeText === 'React.SFC' || nodeTypeText === 'SFC' || nodeTypeText === 'React.FC' || nodeTypeText === 'FC' || nodeTypeText === 'React.StatelessComponent' || nodeTypeText === 'StatelessComponent' || nodeTypeText === 'React.FunctionComponent' || nodeTypeText === 'FunctionComponent'; if (!isReactFunctionComponentType) { return; } if (!node.typeArguments || node.typeArguments.length !== 1) { return; } var typeArgument = node.typeArguments[0]; if (tsutils.isTypeLiteralNode(typeArgument)) { propNodes = getTypeLiteralData(typeArgument); propNames = Object.keys(propNodes); } else { } var arrowFunction = tsutils.getChildOfKind(node.parent, ts.SyntaxKind.ArrowFunction); if (!arrowFunction || !tsutils.isArrowFunction(arrowFunction)) { return; } lookForArrowFunction(arrowFunction); } function lookForArrowFunction(node) { var parameters = node.parameters; if (parameters.length !== 1) { return; } var firstParameter = parameters[0]; var name = firstParameter.name, type = firstParameter.type; if (type && tsutils.isTypeReferenceNode(type)) { var typeName = type.typeName.getText(); if (!ctx.options.propsInterfaceRegex.test(typeName)) { return; } } if (tsutils.isIdentifier(name)) { propsAlias = name.getText(); } else if (tsutils.isObjectBindingPattern(name)) { inspectPropUsageInObjectBinding(name); } arrowFunctions.push(node); } function lookForFunctionComponent(node) { if (!node.body) { return; } var parameters = node.parameters; if (parameters.length !== 1) { return; } var firstParameter = parameters[0]; var name = firstParameter.name, type = firstParameter.type; if (type && tsutils.isTypeReferenceNode(type)) { var typeName = type.typeName.getText(); if (!ctx.options.propsInterfaceRegex.test(typeName)) { return; } } if (tsutils.isIdentifier(name)) { propsAlias = name.getText(); } else if (tsutils.isObjectBindingPattern(name)) { inspectPropUsageInObjectBinding(name); } functionComponents.push(node.body); } function cb(node) { if (tsutils.isClassDeclaration(node)) { classDeclarations.push(node); return; } if (tsutils.isConstructorDeclaration(node)) { if (node.parameters.length > 0) { propsAlias = node.parameters[0].name.text; } ts.forEachChild(node, cb); propsAlias = undefined; return; } if (tsutils.isMethodDeclaration(node)) { var methodName = node.name.text; if (/componentWillReceiveProps|shouldComponentUpdate|componentWillUpdate|componentDidUpdate/.test(methodName) && node.parameters.length > 0) { propsAlias = node.parameters[0].name.text; } if (/shouldComponentUpdate|componentWillUpdate|componentDidUpdate/.test(methodName) && node.parameters.length > 1) { stateAlias = node.parameters[1].name.text; } ts.forEachChild(node, cb); propsAlias = undefined; stateAlias = undefined; return; } if (tsutils.isInterfaceDeclaration(node)) { if (ctx.options.propsInterfaceRegex.test(node.name.text)) { propNodes = getTypeElementData(node); propNames = Object.keys(propNodes); } if (ctx.options.stateInterfaceRegex.test(node.name.text)) { stateNodes = getTypeElementData(node); stateNames = Object.keys(stateNodes); } } else if (tsutils.isPropertyAccessExpression(node)) { var referencedPropertyName = node.getText(); if (/this\.props\..*/.test(referencedPropertyName)) { propNames = Utils_1.Utils.remove(propNames, referencedPropertyName.substring(11)); } else if (/this\.state\..*/.test(referencedPropertyName)) { stateNames = Utils_1.Utils.remove(stateNames, referencedPropertyName.substring(11)); } if (propsAlias !== undefined) { if (new RegExp(propsAlias + '\\..*').test(referencedPropertyName)) { propNames = Utils_1.Utils.remove(propNames, referencedPropertyName.substring(propsAlias.length + 1)); } } if (stateAlias !== undefined) { if (new RegExp(stateAlias + '\\..*').test(referencedPropertyName)) { stateNames = Utils_1.Utils.remove(stateNames, referencedPropertyName.substring(stateAlias.length + 1)); } } if (node.parent.kind !== ts.SyntaxKind.PropertyAccessExpression) { if (referencedPropertyName === 'this.props') { propNames = []; } else if (referencedPropertyName === 'this.state') { stateNames = []; } } } else if (tsutils.isIdentifier(node)) { if (propsAlias !== undefined) { if (node.text === propsAlias && node.parent.kind !== ts.SyntaxKind.PropertyAccessExpression && node.parent.kind !== ts.SyntaxKind.Parameter && isParentNodeSuperCall(node) === false) { propNames = []; } } if (stateAlias !== undefined) { if (node.text === stateAlias && node.parent.kind !== ts.SyntaxKind.PropertyAccessExpression && node.parent.kind !== ts.SyntaxKind.Parameter) { stateNames = []; } } } else if (tsutils.isTypeReferenceNode(node)) { lookForReactSpecificArrowFunction(node); } else if (tsutils.isArrowFunction(node)) { lookForArrowFunction(node); } else if (tsutils.isFunctionDeclaration(node)) { lookForFunctionComponent(node); } else if (tsutils.isFunctionExpression(node)) { lookForFunctionComponent(node); } return ts.forEachChild(node, cb); } ts.forEachChild(ctx.sourceFile, cb); if (propNames.length > 0 || stateNames.length > 0) { classDeclarations.forEach(function (c) { return ts.forEachChild(c, cb); }); arrowFunctions.forEach(function (c) { return ts.forEachChild(c.body, cb); }); functionComponents.forEach(function (f) { return ts.forEachChild(f, cb); }); } propNames.forEach(function (propName) { var typeElement = propNodes[propName]; ctx.addFailureAt(typeElement.getStart(), typeElement.getWidth(), FAILURE_UNUSED_PROP + propName); }); stateNames.forEach(function (stateName) { var typeElement = stateNodes[stateName]; ctx.addFailureAt(typeElement.getStart(), typeElement.getWidth(), FAILURE_UNUSED_STATE + stateName); }); } //# sourceMappingURL=reactUnusedPropsAndStateRule.js.map