From 3699b1fe1812d876a319f375b3a3bad484ad7133 Mon Sep 17 00:00:00 2001 From: Chris Barr Date: Thu, 16 Mar 2017 08:16:16 -0400 Subject: [PATCH] add new rule: no-useless-files --- README.md | 3 +- recommended_ruleset.js | 1 + src/noUselessFilesRule.ts | 44 ++++++++++++++++ src/tests/NoUselessFilesRuleTests.ts | 75 ++++++++++++++++++++++++++++ tslint-warnings.csv | 1 + tslint.json | 3 +- 6 files changed, 125 insertions(+), 2 deletions(-) create mode 100644 src/noUselessFilesRule.ts create mode 100644 src/tests/NoUselessFilesRuleTests.ts diff --git a/README.md b/README.md index dd4f02fb4..0c6a4cce0 100644 --- a/README.md +++ b/README.md @@ -61,7 +61,7 @@ The tslint.json file does not change format when using this package. Just add ou ##### Which Rules Should I Turn On? There certainly are a lot of options! Here are some links to get you started. -* Easiest Option - Our recommended ruleset is here: [recommended_ruleset.js](recommended_ruleset.js). You can also easily extend the ruleset by adding `"extends": "tslint-microsoft-contrib"` to your configuration. Please note, the default rules require the `--type-check` and `--project` TSLint options. +* Easiest Option - Our recommended ruleset is here: [recommended_ruleset.js](recommended_ruleset.js). You can also easily extend the ruleset by adding `"extends": "tslint-microsoft-contrib"` to your configuration. Please note, the default rules require the `--type-check` and `--project` TSLint options. * A nice blog post on the MSDN secure development blog can be found here: [Automating Secure Development Lifecycle Checks in TypeScript with TSLint](https://blogs.msdn.microsoft.com/secdevblog/2016/05/11/automating-secure-development-lifecycle-checks-in-typescript-with-tslint/) * A wiki briefly describing the SDL and related rules is here: [TSLint and the Microsoft Security Development Lifecycle](https://github.com/Microsoft/tslint-microsoft-contrib/wiki/TSLint-and-the-Microsoft-Security-Development-Lifecycle) * And our configuration file with all options is available here: [tslint.json](tslint.json) @@ -129,6 +129,7 @@ Rule Name | Description | Since `no-unnecessary-semicolons` | Remove unnecessary semicolons | 0.0.1 `no-unsupported-browser-code` | Avoid writing browser-specific code for unsupported browser versions. Browser versions are specified in the rule configuration options, eg: `[true, [ "IE 11", "Firefox > 40", "Chrome >= 45" ] ]`. Browser-specific blocks of code can then be designated with a single-line comment, like so: `// Browser specific: IE 10`, or with a jsdoc like this: `@browserspecific chrome 40`. | 2.0.10 `no-unused-imports` | Deprecated - This rule is now covered by TSLint's no-unused-variables rule. However, it can still be useful to enable this rule and pair it with the fix-no-unused-imports formatter. | 0.0.1 +`no-useless-files` | Avoid keeping files around that only contain commented out code, are completely empty, or only contain whitespace characters | 4.0.2 `no-var-self` | Do not use `var self = this`; instead, manage scope with arrow functions/lambdas. Self variables are a common practice in JavaScript but can be avoided in TypeScript. By default the rule bans any assignments of the `this` reference. If you want to enforce a naming convention or allow some usages then configure the rule with a regex. By default the rule is configured with `(?!)` which matches nothing. You can pass `^self$` to allow variables named self or pass `^(?!self$)` to allow anything other than self, for example| 2.0.8 `no-with-statement` | Do not use with statements. Assign the item to a new variable instead | 0.0.1 `non-literal-require` | Detect `require()` function calls for something that is not a string literal. For security reasons, it is best to only require() string literals. Otherwise, it is perhaps possible for an attacker to somehow change the value and download arbitrary Javascript into your page. | 2.0.14 diff --git a/recommended_ruleset.js b/recommended_ruleset.js index 8e5cc377e..528410967 100644 --- a/recommended_ruleset.js +++ b/recommended_ruleset.js @@ -133,6 +133,7 @@ module.exports = { "no-unnecessary-local-variable": true, "no-unnecessary-qualifier": true, "no-unsupported-browser-code": true, + "no-useless-files": true, "no-var-keyword": true, "no-var-requires": true, "no-var-self": true, diff --git a/src/noUselessFilesRule.ts b/src/noUselessFilesRule.ts new file mode 100644 index 000000000..7633085a0 --- /dev/null +++ b/src/noUselessFilesRule.ts @@ -0,0 +1,44 @@ +import * as ts from 'typescript'; +import * as Lint from 'tslint'; + +import {ExtendedMetadata} from './utils/ExtendedMetadata'; + +const FAILURE_STRING_EMPTY: string = 'This file is empty and should be deleted.'; +const FAILURE_STRING_COMMENTS: string = 'This file only contains comments and should be deleted.'; + +/** + * Implementation of the no-useless-files rule. + */ +export class Rule extends Lint.Rules.AbstractRule { + + public static metadata: ExtendedMetadata = { + ruleName: 'no-useless-files', + type: 'maintainability', + description: 'Locates files that only contain commented out code, whitespace characters, or have no content', + options: null, + optionsDescription: '', + typescriptOnly: false, + issueClass: 'Non-SDL', + issueType: 'Warning', + severity: 'Low', + level: 'Opportunity for Excellence', + group: 'Clarity', + commonWeaknessEnumeration: '398' //Indicator of Poor Code Quality + }; + + public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { + const ruleFailures: Lint.RuleFailure[] = []; + + const fileContent = sourceFile.getFullText().trim(); + const fileContentNoComments = sourceFile.getText().trim(); + + if (fileContent.length === 0) { + //This file only contains whitespace characters, a totally empty & useless file + ruleFailures.push(new Lint.RuleFailure(sourceFile, 0, 0, FAILURE_STRING_EMPTY, this.getOptions().ruleName)); + } else if (fileContentNoComments.length === 0) { + //This file only contains code comments, not empty but completely useless + ruleFailures.push(new Lint.RuleFailure(sourceFile, 0, 0, FAILURE_STRING_COMMENTS, this.getOptions().ruleName)); + } + return ruleFailures; + } +} diff --git a/src/tests/NoUselessFilesRuleTests.ts b/src/tests/NoUselessFilesRuleTests.ts new file mode 100644 index 000000000..662ba9422 --- /dev/null +++ b/src/tests/NoUselessFilesRuleTests.ts @@ -0,0 +1,75 @@ +import {TestHelper} from './TestHelper'; + +/** + * Unit tests. + */ +describe('noUselessFilesRule', () : void => { + + const ruleName : string = 'no-useless-files'; + + it('should pass on a normal file that contains code', () : void => { + const script : string = ` + export class MyClass { + constructor () { + console.log("foo"); + } + }`; + + TestHelper.assertViolations(ruleName, script, [ ]); + }); + + it('should fail on a file that only contains single-line comments', () : void => { + const script : string = `// This is the only comment in this file`; + + TestHelper.assertViolations(ruleName, script, [{ + 'failure': 'This file only contains comments and should be deleted.', + 'name': 'file.ts', + 'ruleName': ruleName, + 'startPosition': { 'character': 1, 'line': 1 } + }]); + }); + + it('should fail on a file that only contains multi-line comments', () : void => { + const script : string = `/* + export class MyClass { + constructor () { + console.log("foo"); + } + } + */`; + + TestHelper.assertViolations(ruleName, script, [{ + 'failure': 'This file only contains comments and should be deleted.', + 'name': 'file.ts', + 'ruleName': ruleName, + 'startPosition': { 'character': 1, 'line': 1 } + }]); + }); + + it('should fail on a file that only contains whitespace', () : void => { + //The below string contains spaces, tabs, and linebreaks + const script : string = ` + + + `; + + TestHelper.assertViolations(ruleName, script, [{ + 'failure': 'This file is empty and should be deleted.', + 'name': 'file.ts', + 'ruleName': ruleName, + 'startPosition': { 'character': 1, 'line': 1 } + }]); + }); + + it('should fail on a file that has no content', () : void => { + const script : string = ``; + + TestHelper.assertViolations(ruleName, script, [{ + 'failure': 'This file is empty and should be deleted.', + 'name': 'file.ts', + 'ruleName': ruleName, + 'startPosition': { 'character': 1, 'line': 1 } + }]); + }); + +}); diff --git a/tslint-warnings.csv b/tslint-warnings.csv index b591f31c6..aa42ff91b 100644 --- a/tslint-warnings.csv +++ b/tslint-warnings.csv @@ -180,6 +180,7 @@ CWE 710 - Coding Standards Violation" no-use-before-declare,Disallows usage of variables before their declaration.,TSLINTUU9UNF,tslint,Non-SDL,Error,Critical,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,"398, 456, 710","CWE 398 - Indicator of Poor Code Quality CWE 456 - Missing Initialization of a Variable CWE 710 - Coding Standards Violation" +no-useless-files,"Locates files that only contain commented out code, whitespace characters, or have no content",TSLINTGOUON3,tslint,Non-SDL,Warning,Low,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,398,"CWE 398 - Indicator of Poor Code Quality" no-var-keyword,Disallows usage of the `var` keyword.,TSLINT15BQNA,tslint,Non-SDL,Warning,Important,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,"398, 705, 710","CWE 398 - Indicator of Poor Code Quality CWE 705 - Incorrect Control Flow Scoping CWE 710 - Coding Standards Violation" diff --git a/tslint.json b/tslint.json index 7796833de..8110537cb 100644 --- a/tslint.json +++ b/tslint.json @@ -285,6 +285,7 @@ "check-operator", "check-separator", "check-type" - ] + ], + "no-useless-files": true } } \ No newline at end of file