UNPKG

tslint-clean-code

Version:
252 lines 9.73 kB
"use strict"; var __extends = (this && this.__extends) || (function () { var 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 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 ErrorTolerantWalker_1 = require("./utils/ErrorTolerantWalker"); var AstUtils_1 = require("./utils/AstUtils"); var Rule = (function (_super) { __extends(Rule, _super); function Rule() { return _super !== null && _super.apply(this, arguments) || this; } Rule.FAILURE_STRING = function (feature) { var methodName = feature.methodName, className = feature.className, otherClassName = feature.otherClassName; var failureMessage = "Method \"" + methodName + "\" uses \"" + otherClassName + "\" more than its own class \"" + className + "\"."; var recommendation = "Extract or Move Method from \"" + methodName + "\" into \"" + otherClassName + "\"."; return failureMessage + " " + recommendation; }; Rule.prototype.apply = function (sourceFile) { return this.applyWithWalker(new NoFeatureEnvyRuleWalker(sourceFile, this.getOptions())); }; Rule.metadata = { ruleName: 'no-feature-envy', type: 'maintainability', description: 'A method accesses the data of another object more than its own data.', options: null, optionsDescription: '', optionExamples: [], recommendation: '[true, 1, ["_"]],', typescriptOnly: false, issueClass: 'Non-SDL', issueType: 'Warning', severity: 'Moderate', level: 'Opportunity for Excellence', group: 'Clarity', commonWeaknessEnumeration: '', }; return Rule; }(Lint.Rules.AbstractRule)); exports.Rule = Rule; var NoFeatureEnvyRuleWalker = (function (_super) { __extends(NoFeatureEnvyRuleWalker, _super); function NoFeatureEnvyRuleWalker(sourceFile, options) { var _this = _super.call(this, sourceFile, options) || this; _this.threshold = 0; _this.exclude = []; _this.parseOptions(); return _this; } NoFeatureEnvyRuleWalker.prototype.visitClassDeclaration = function (node) { this.checkAndReport(node); _super.prototype.visitClassDeclaration.call(this, node); }; NoFeatureEnvyRuleWalker.prototype.checkAndReport = function (node) { var _this = this; this.getFeatureMethodsForClass(node).forEach(function (feature) { var failureMessage = Rule.FAILURE_STRING(feature); _this.addFailureAtNode(feature.methodNode, failureMessage); }); }; NoFeatureEnvyRuleWalker.prototype.getFeatureMethodsForClass = function (classNode) { var _this = this; var methods = this.methodsForClass(classNode); return methods .map(function (method) { var walker = new ClassMethodWalker(classNode, method); return walker.features(); }) .map(function (features) { return _this.getTopFeature(features); }) .filter(function (feature) { return feature !== undefined; }); }; NoFeatureEnvyRuleWalker.prototype.getTopFeature = function (features) { var filteredFeatures = this.filterFeatures(features); return filteredFeatures.reduce(function (best, current) { if (!best) { return current; } if (current.featureEnvy() > best.featureEnvy()) { return current; } return best; }, undefined); }; NoFeatureEnvyRuleWalker.prototype.filterFeatures = function (features) { var _this = this; return features.filter(function (feature) { var isExcluded = _this.exclude.indexOf(feature.otherClassName) !== -1; if (isExcluded) { return false; } return feature.featureEnvy() > _this.threshold; }); }; NoFeatureEnvyRuleWalker.prototype.methodsForClass = function (classNode) { return classNode.members.filter(function (classElement) { switch (classElement.kind) { case ts.SyntaxKind.MethodDeclaration: case ts.SyntaxKind.GetAccessor: case ts.SyntaxKind.SetAccessor: return !AstUtils_1.AstUtils.isStatic(classElement); default: return false; } }); }; NoFeatureEnvyRuleWalker.prototype.parseOptions = function () { var _this = this; this.getOptions().forEach(function (opt) { if (typeof opt === 'boolean') { return; } if (typeof opt === 'number') { _this.threshold = opt; return; } if (Array.isArray(opt)) { _this.exclude = opt; return; } }); }; return NoFeatureEnvyRuleWalker; }(ErrorTolerantWalker_1.ErrorTolerantWalker)); var ClassMethodWalker = (function (_super) { __extends(ClassMethodWalker, _super); function ClassMethodWalker(classNode, methodNode) { var _this = _super.call(this) || this; _this.classNode = classNode; _this.methodNode = methodNode; _this.featureEnvyMap = {}; _this.walk(_this.methodNode); return _this; } ClassMethodWalker.prototype.features = function () { var _this = this; var thisClassAccesses = this.getCountForClass('this'); return this.classesUsed.map(function (className) { var otherClassAccesses = _this.getCountForClass(className); return new MethodFeature({ classNode: _this.classNode, methodNode: _this.methodNode, otherClassName: className, thisClassAccesses: thisClassAccesses, otherClassAccesses: otherClassAccesses, }); }); }; ClassMethodWalker.prototype.getCountForClass = function (className) { return this.featureEnvyMap[className] || 0; }; Object.defineProperty(ClassMethodWalker.prototype, "classesUsed", { get: function () { return Object.keys(this.featureEnvyMap).filter(function (className) { return className !== 'this'; }); }, enumerable: true, configurable: true }); ClassMethodWalker.prototype.visitPropertyAccessExpression = function (node) { if (this.isTopPropertyAccess(node)) { var className = this.classNameForPropertyAccess(node); this.incrementCountForClass(className); } _super.prototype.visitPropertyAccessExpression.call(this, node); }; ClassMethodWalker.prototype.incrementCountForClass = function (className) { if (this.featureEnvyMap[className] !== undefined) { this.featureEnvyMap[className] += 1; } else { this.featureEnvyMap[className] = 1; } }; ClassMethodWalker.prototype.isTopPropertyAccess = function (node) { switch (node.expression.kind) { case ts.SyntaxKind.Identifier: case ts.SyntaxKind.ThisKeyword: case ts.SyntaxKind.SuperKeyword: return true; } return false; }; ClassMethodWalker.prototype.classNameForPropertyAccess = function (node) { var expression = node.expression; if (ts.isThisTypeNode(node)) { return 'this'; } if (expression.kind === ts.SyntaxKind.SuperKeyword) { return 'this'; } if (this.classNode.name.getText() === expression.getText()) { return 'this'; } return expression.getText(); }; return ClassMethodWalker; }(Lint.SyntaxWalker)); var MethodFeature = (function () { function MethodFeature(data) { this.data = data; } Object.defineProperty(MethodFeature.prototype, "className", { get: function () { return this.classNode.name.text; }, enumerable: true, configurable: true }); Object.defineProperty(MethodFeature.prototype, "classNode", { get: function () { return this.data.classNode; }, enumerable: true, configurable: true }); Object.defineProperty(MethodFeature.prototype, "methodName", { get: function () { return this.methodNode.name.getText(); }, enumerable: true, configurable: true }); Object.defineProperty(MethodFeature.prototype, "methodNode", { get: function () { return this.data.methodNode; }, enumerable: true, configurable: true }); MethodFeature.prototype.featureEnvy = function () { var _a = this.data, thisClassAccesses = _a.thisClassAccesses, otherClassAccesses = _a.otherClassAccesses; return otherClassAccesses - thisClassAccesses; }; Object.defineProperty(MethodFeature.prototype, "otherClassName", { get: function () { return this.data.otherClassName; }, enumerable: true, configurable: true }); return MethodFeature; }()); exports.MethodFeature = MethodFeature; //# sourceMappingURL=noFeatureEnvyRule.js.map