UNPKG

tslint-clean-code

Version:
226 lines 9.35 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 Utils_1 = require("./utils/Utils"); var FAILURE_STRING = 'The cohesion of this class is too low. Consider splitting this class into multiple cohesive classes: '; var Rule = (function (_super) { __extends(Rule, _super); function Rule() { return _super !== null && _super.apply(this, arguments) || this; } Rule.prototype.apply = function (sourceFile) { return this.applyWithWalker(new MinClassCohesionRuleWalker(sourceFile, this.getOptions())); }; Rule.metadata = { ruleName: 'min-class-cohesion', type: 'maintainability', description: 'High cohesion means the methods and variables of the class are co-dependent and hang together as a logical whole.', options: null, optionsDescription: '', typescriptOnly: true, issueClass: 'Non-SDL', issueType: 'Warning', severity: 'Important', level: 'Opportunity for Excellence', group: 'Correctness', recommendation: '[true, 0.5],', commonWeaknessEnumeration: '', }; return Rule; }(Lint.Rules.AbstractRule)); exports.Rule = Rule; var MinClassCohesionRuleWalker = (function (_super) { __extends(MinClassCohesionRuleWalker, _super); function MinClassCohesionRuleWalker(sourceFile, options) { var _this = _super.call(this, sourceFile, options) || this; _this.minClassCohesion = 0.5; _this.parseOptions(); return _this; } MinClassCohesionRuleWalker.prototype.visitClassDeclaration = function (node) { if (!this.isClassCohesive(node)) { var className = node.name == null ? '<unknown>' : node.name.text; this.addFailureAt(node.getStart(), node.getWidth(), FAILURE_STRING + className); } _super.prototype.visitClassDeclaration.call(this, node); }; MinClassCohesionRuleWalker.prototype.isClassCohesive = function (node) { var classNode = new ClassDeclarationHelper(node); if (classNode.extendsSomething) { return true; } var cohesionScore = classNode.cohesionScore; return cohesionScore >= this.minClassCohesion; }; MinClassCohesionRuleWalker.prototype.parseOptions = function () { var _this = this; this.getOptions().forEach(function (opt) { if (typeof opt === 'boolean') { return; } if (typeof opt === 'number') { _this.minClassCohesion = opt; return; } throw new Error("Rule min-class-cohesion only supports option of type number, not " + typeof opt + "."); }); }; return MinClassCohesionRuleWalker; }(ErrorTolerantWalker_1.ErrorTolerantWalker)); var ClassDeclarationHelper = (function () { function ClassDeclarationHelper(node) { this.node = node; } Object.defineProperty(ClassDeclarationHelper.prototype, "cohesionScore", { get: function () { var _this = this; var _a = this, fieldNames = _a.fieldNames, methods = _a.methods; if (methods.length === 0) { return 1.0; } var numFields = fieldNames.length; if (numFields === 0) { return 0.0; } var methodScores = methods.map(function (method) { var used = _this.numberOfFieldsUsedByMethod(fieldNames, method); return used / numFields; }); var sumScores = methodScores.reduce(function (total, current) { return total + current; }, 0); return sumScores / methods.length; }, enumerable: true, configurable: true }); Object.defineProperty(ClassDeclarationHelper.prototype, "fieldNames", { get: function () { var parameterNames = this.constructorParameterNames; var instanceFields = this.instanceFieldNames; return parameterNames.concat(instanceFields); }, enumerable: true, configurable: true }); Object.defineProperty(ClassDeclarationHelper.prototype, "methods", { get: function () { return this.node.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; } }); }, enumerable: true, configurable: true }); ClassDeclarationHelper.prototype.numberOfFieldsUsedByMethod = function (fieldNames, method) { var fields = ClassDeclarationHelper.fieldsUsedByMethod(method); return fieldNames.reduce(function (count, fieldName) { if (fields[fieldName]) { return count + 1; } return count; }, 0); }; Object.defineProperty(ClassDeclarationHelper.prototype, "constructorParameterNames", { get: function () { return this.constructorParameters.map(function (param) { return param.name.getText(); }); }, enumerable: true, configurable: true }); Object.defineProperty(ClassDeclarationHelper.prototype, "constructorParameters", { get: function () { var ctor = this.constructorDeclaration; if (ctor) { return ctor.parameters.filter(function (param) { return (AstUtils_1.AstUtils.hasModifier(param.modifiers, ts.SyntaxKind.PublicKeyword) || AstUtils_1.AstUtils.hasModifier(param.modifiers, ts.SyntaxKind.PrivateKeyword) || AstUtils_1.AstUtils.hasModifier(param.modifiers, ts.SyntaxKind.ProtectedKeyword) || AstUtils_1.AstUtils.hasModifier(param.modifiers, ts.SyntaxKind.ReadonlyKeyword)); }); } return []; }, enumerable: true, configurable: true }); Object.defineProperty(ClassDeclarationHelper.prototype, "constructorDeclaration", { get: function () { return (this.node.members.find(function (element) { return element.kind === ts.SyntaxKind.Constructor; })); }, enumerable: true, configurable: true }); Object.defineProperty(ClassDeclarationHelper.prototype, "instanceFieldNames", { get: function () { return this.instanceFields.map(function (param) { return param.name.getText(); }); }, enumerable: true, configurable: true }); Object.defineProperty(ClassDeclarationHelper.prototype, "instanceFields", { get: function () { return (this.node.members.filter(function (classElement) { return classElement.kind === ts.SyntaxKind.PropertyDeclaration; })); }, enumerable: true, configurable: true }); ClassDeclarationHelper.fieldsUsedByMethod = function (method) { var walker = new ClassMethodWalker(); walker.walk(method); return walker.fieldsUsed; }; Object.defineProperty(ClassDeclarationHelper.prototype, "extendsSomething", { get: function () { return Utils_1.Utils.exists(this.node.heritageClauses, function (clause) { return clause.token === ts.SyntaxKind.ExtendsKeyword; }); }, enumerable: true, configurable: true }); Object.defineProperty(ClassDeclarationHelper.prototype, "name", { get: function () { return this.node.name == null ? '<unknown>' : this.node.name.text; }, enumerable: true, configurable: true }); return ClassDeclarationHelper; }()); var ClassMethodWalker = (function (_super) { __extends(ClassMethodWalker, _super); function ClassMethodWalker() { var _this = _super !== null && _super.apply(this, arguments) || this; _this.fieldsUsed = {}; return _this; } ClassMethodWalker.prototype.visitPropertyAccessExpression = function (node) { var isOnThis = node.expression.kind === ts.SyntaxKind.ThisKeyword; if (isOnThis) { var field = node.name.text; this.fieldsUsed[field] = true; } _super.prototype.visitPropertyAccessExpression.call(this, node); }; return ClassMethodWalker; }(Lint.SyntaxWalker)); //# sourceMappingURL=minClassCohesionRule.js.map