From 7c5f3a4b3555cd5e0e6c8b30567b2743c9ea6bf5 Mon Sep 17 00:00:00 2001 From: Noah Chen Date: Fri, 18 Nov 2016 21:28:56 -0500 Subject: [PATCH 01/23] Format develop/custom-rules/index.md --- docs/develop/custom-rules/index.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/develop/custom-rules/index.md b/docs/develop/custom-rules/index.md index c9d45c7308f..8cc3cded4f7 100644 --- a/docs/develop/custom-rules/index.md +++ b/docs/develop/custom-rules/index.md @@ -58,12 +58,12 @@ Now that you're written a rule to detect problems, let's modify it to *fix* them Instantiate a `Fix` object and pass it in as an argument to `addFailure`. This snippet replaces the offending import statement with an empty string: ```typescript - // create a fixer for this failure - const replacement = new Lint.Replacement(node.getStart(), node.getWidth(), ""); - const fix = new Lint.Fix("no-imports", [replacement]); +// create a fixer for this failure +const replacement = new Lint.Replacement(node.getStart(), node.getWidth(), ""); +const fix = new Lint.Fix("no-imports", [replacement]); - // create a failure at the current position - this.addFailure(this.createFailure(node.getStart(), node.getWidth(), Rule.FAILURE_STRING, fix)); +// create a failure at the current position +this.addFailure(this.createFailure(node.getStart(), node.getWidth(), Rule.FAILURE_STRING, fix)); ``` --- Final notes: From 104bba2564e90114efa90bb0794eeb5b41f04e91 Mon Sep 17 00:00:00 2001 From: Noah Chen Date: Fri, 18 Nov 2016 22:48:24 -0500 Subject: [PATCH 02/23] Fix linting errors (#1744) --- CHANGELOG.md | 8 +++++ README.md | 6 ++-- docs/develop/custom-formatters/index.md | 2 +- src/configs/recommended.ts | 2 +- src/enableDisableRules.ts | 4 +-- src/linter.ts | 4 +-- src/rules/adjacentOverloadSignaturesRule.ts | 12 ++++---- src/rules/alignRule.ts | 2 +- src/rules/banRule.ts | 4 +-- src/rules/commentFormatRule.ts | 4 +-- src/rules/completedDocsRule.ts | 2 +- src/rules/curlyRule.ts | 4 +-- src/rules/cyclomaticComplexityRule.ts | 13 +++++---- src/rules/linebreakStyleRule.ts | 2 +- src/rules/maxClassesPerFileRule.ts | 4 +-- src/rules/maxFileLineCountRule.ts | 4 +-- src/rules/maxLineLengthRule.ts | 2 +- src/rules/memberAccessRule.ts | 2 +- src/rules/noDuplicateVariableRule.ts | 4 ++- src/rules/noEmptyRule.ts | 2 +- src/rules/noInferrableTypesRule.ts | 4 ++- src/rules/noParameterPropertiesRule.ts | 4 ++- src/rules/noShadowedVariableRule.ts | 4 ++- src/rules/noSwitchCaseFallThroughRule.ts | 2 +- src/rules/noUnsafeFinallyRule.ts | 12 ++++---- src/rules/noUnusedExpressionRule.ts | 2 +- src/rules/noUnusedVariableRule.ts | 11 +++---- src/rules/objectLiteralKeyQuotesRule.ts | 29 +++++++++++++++---- src/rules/objectLiteralShorthandRule.ts | 2 +- src/rules/objectLiteralSortKeysRule.ts | 4 ++- src/rules/orderedImportsRule.ts | 4 +-- src/rules/preferForOfRule.ts | 2 +- src/rules/quotemarkRule.ts | 2 +- src/rules/restrictPlusOperandsRule.ts | 4 ++- src/rules/trailingCommaRule.ts | 6 ++-- src/rules/typedefWhitespaceRule.ts | 10 +++---- src/test.ts | 2 +- src/test/parse.ts | 6 ++-- src/tslint-cli.ts | 26 ++++++++--------- src/utils.ts | 2 +- test/configurationTests.ts | 2 +- test/executable/executableTests.ts | 2 +- .../rules/ruleOneRule.js | 2 +- .../rules/ruleThreeRule.js | 2 +- .../rules/ruleTwoRule.js | 2 +- test/files/custom-rules-2/noFailRule.js | 2 +- test/files/custom-rules/alwaysFailRule.js | 2 +- test/ruleLoaderTests.ts | 18 ++++++------ .../default/false-positives.ts.lint | 10 +++++++ .../no-unused-variable/default/import.ts.fix | 4 +++ .../no-unused-variable/default/import.ts.lint | 4 +++ test/rules/semicolon/always/test.ts.fix | 2 ++ test/rules/semicolon/always/test.ts.lint | 2 ++ tslint.json | 1 + 54 files changed, 171 insertions(+), 109 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3593c9aa05c..21ff0dce1f6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Change Log === +v4.0.0-dev.2 +--- +* Include latest v4.0.0 changes + v4.0.0 --- * **BREAKING CHANGES** @@ -193,6 +197,10 @@ Thanks to our contributors! * @janaagaard75 * @mprobst +v3.12.0-dev.2 +--- +* [enhancement] Support TypeScript v2.0.0-dev builds + v3.12.1 --- * Stable release containing changes from the last dev release (v3.12.0-dev.1) diff --git a/README.md b/README.md index 01c8c313af2..d89e42499ee 100644 --- a/README.md +++ b/README.md @@ -355,7 +355,7 @@ tsc --noImplicitAny noImportsRule.ts Then, if using the CLI, provide the directory that contains this rule as an option to `--rules-dir`. If using TSLint as a library or via `grunt-tslint`, the `options` hash must contain `"rulesDirectory": "..."`. If you run the linter, you'll see that we have now successfully banned all import statements via TSLint! -Finally, enable each custom rule in your [`tslint.json` config file][0] config file. +Finally, enable each custom rule in your [`tslint.json` config file](https://palantir.github.io/tslint/usage/tslint-json/) config file. Final notes: @@ -370,7 +370,7 @@ Just like rules, additional formatters can also be supplied to TSLint via `--for ```typescript import * as ts from "typescript"; -import * as Lint from "tslint/lib/lint"; +import * as Lint from "tslint"; export class Formatter extends Lint.Formatters.AbstractFormatter { public format(failures: Lint.RuleFailure[]): string { @@ -412,5 +412,3 @@ Creating a new release 4. Commit with message `Prepare release ` 5. Run `npm publish` 6. Create a git tag for the new release and push it ([see existing tags here](https://github.com/palantir/tslint/tags)) - -[0]: {{site.baseurl | append: "/usage/tslint-json/"}} diff --git a/docs/develop/custom-formatters/index.md b/docs/develop/custom-formatters/index.md index a88daad73ca..173c8e3f868 100644 --- a/docs/develop/custom-formatters/index.md +++ b/docs/develop/custom-formatters/index.md @@ -8,7 +8,7 @@ Just like [custom rules][0], additional formatters can also be supplied to TSLin ```ts import * as ts from "typescript"; -import * as Lint from "tslint/lib/lint"; +import * as Lint from "tslint"; export class Formatter extends Lint.Formatters.AbstractFormatter { public format(failures: Lint.RuleFailure[]): string { diff --git a/src/configs/recommended.ts b/src/configs/recommended.ts index 75fe9cc7c06..77acc19835a 100644 --- a/src/configs/recommended.ts +++ b/src/configs/recommended.ts @@ -36,13 +36,13 @@ export const rules = { "interface-name": [true, "always-prefix"], "jsdoc-format": true, "label-position": true, + "max-classes-per-file": [true, 1], "max-line-length": [true, 120], "member-access": true, "member-ordering": [true, { "order": "statics-first" }, ], "new-parens": true, - "max-classes-per-file": [true, 1], "no-any": false, "no-arg": true, "no-bitwise": true, diff --git a/src/enableDisableRules.ts b/src/enableDisableRules.ts index b67ca86b43a..ae9f42dec9e 100644 --- a/src/enableDisableRules.ts +++ b/src/enableDisableRules.ts @@ -48,7 +48,7 @@ export class EnableDisableRulesWalker extends SkippableTokenAwareRuleWalker { private getStartOfLinePosition(node: ts.SourceFile, position: number, lineOffset = 0) { return node.getPositionOfLineAndCharacter( - node.getLineAndCharacterOfPosition(position).line + lineOffset, 0 + node.getLineAndCharacterOfPosition(position).line + lineOffset, 0, ); } @@ -73,7 +73,7 @@ export class EnableDisableRulesWalker extends SkippableTokenAwareRuleWalker { rulesList = commentTextParts[1].split(/\s+/).slice(1); // remove empty items and potential comment end. - rulesList = rulesList.filter(item => !!item && item.indexOf("*/") === -1); + rulesList = rulesList.filter((item) => !!item && item.indexOf("*/") === -1); // potentially there were no items, so default to `all`. rulesList = rulesList.length > 0 ? rulesList : ["all"]; diff --git a/src/linter.ts b/src/linter.ts index f5d196d6c86..39c192e1627 100644 --- a/src/linter.ts +++ b/src/linter.ts @@ -20,11 +20,11 @@ import * as ts from "typescript"; import { DEFAULT_CONFIG, - IConfigurationFile, findConfiguration, findConfigurationPath, getRelativePath, getRulesDirectories, + IConfigurationFile, loadConfigurationFromPath, } from "./configuration"; import { EnableDisableRulesWalker } from "./enableDisableRules"; @@ -41,7 +41,7 @@ import { arrayify, dedent } from "./utils"; * Linter that can lint multiple files in consecutive runs. */ class Linter { - public static VERSION = "4.0.0"; + public static VERSION = "4.0.0-dev.2"; public static findConfiguration = findConfiguration; public static findConfigurationPath = findConfigurationPath; diff --git a/src/rules/adjacentOverloadSignaturesRule.ts b/src/rules/adjacentOverloadSignaturesRule.ts index 65e44933bbf..e95c1f303ed 100644 --- a/src/rules/adjacentOverloadSignaturesRule.ts +++ b/src/rules/adjacentOverloadSignaturesRule.ts @@ -34,7 +34,9 @@ export class Rule extends Lint.Rules.AbstractRule { }; /* tslint:enable:object-literal-sort-keys */ - public static FAILURE_STRING_FACTORY = (name: string) => `All '${name}' signatures should be adjacent`; + public static FAILURE_STRING_FACTORY = (name: string) => { + return `All '${name}' signatures should be adjacent`; + } public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { return this.applyWithWalker(new AdjacentOverloadSignaturesWalker(sourceFile, this.getOptions())); @@ -56,7 +58,7 @@ class AdjacentOverloadSignaturesWalker extends Lint.RuleWalker { } public visitInterfaceDeclaration(node: ts.InterfaceDeclaration): void { - this.checkOverloadsAdjacent(node.members, member => member.name && getTextOfPropertyName(member.name)); + this.checkOverloadsAdjacent(node.members, (member) => member.name && getTextOfPropertyName(member.name)); super.visitInterfaceDeclaration(node); } @@ -71,7 +73,7 @@ class AdjacentOverloadSignaturesWalker extends Lint.RuleWalker { } private visitStatements(statements: ts.Statement[]) { - this.checkOverloadsAdjacent(statements, statement => { + this.checkOverloadsAdjacent(statements, (statement) => { if (statement.kind === ts.SyntaxKind.FunctionDeclaration) { const name = (statement as ts.FunctionDeclaration).name; return name && name.text; @@ -81,8 +83,8 @@ class AdjacentOverloadSignaturesWalker extends Lint.RuleWalker { }); } - private visitMembers(members: (ts.TypeElement | ts.ClassElement)[]) { - this.checkOverloadsAdjacent(members, member => member.name && getTextOfPropertyName(member.name)); + private visitMembers(members: Array) { + this.checkOverloadsAdjacent(members, (member) => member.name && getTextOfPropertyName(member.name)); } /** 'getOverloadName' may return undefined for nodes that cannot be overloads, e.g. a `const` declaration. */ diff --git a/src/rules/alignRule.ts b/src/rules/alignRule.ts index 65141617e21..44bb0aa7256 100644 --- a/src/rules/alignRule.ts +++ b/src/rules/alignRule.ts @@ -61,7 +61,7 @@ export class Rule extends Lint.Rules.AbstractRule { type SourcePosition = { line: number; character: number; -} +}; class AlignWalker extends Lint.RuleWalker { public visitConstructorDeclaration(node: ts.ConstructorDeclaration) { diff --git a/src/rules/banRule.ts b/src/rules/banRule.ts index a621978f4be..341f7276026 100644 --- a/src/rules/banRule.ts +++ b/src/rules/banRule.ts @@ -45,7 +45,7 @@ export class Rule extends Lint.Rules.AbstractRule { public static FAILURE_STRING_FACTORY = (expression: string, messageAddition?: string) => { return `Calls to '${expression}' are not allowed.${messageAddition ? " " + messageAddition : ""}`; - }; + } public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { const options = this.getOptions(); @@ -102,7 +102,7 @@ export class BanFunctionWalker extends Lint.RuleWalker { const failure = this.createFailure( expression.getStart(), expression.getWidth(), - Rule.FAILURE_STRING_FACTORY(`${leftSideExpression}.${rightSideExpression}`, bannedFunction[2]) + Rule.FAILURE_STRING_FACTORY(`${leftSideExpression}.${rightSideExpression}`, bannedFunction[2]), ); this.addFailure(failure); } diff --git a/src/rules/commentFormatRule.ts b/src/rules/commentFormatRule.ts index 5b074c364e9..c51689d56d7 100644 --- a/src/rules/commentFormatRule.ts +++ b/src/rules/commentFormatRule.ts @@ -118,11 +118,11 @@ function startsWith(commentText: string, changeCase: (str: string) => string) { } function startsWithLowercase(commentText: string) { - return startsWith(commentText, c => c.toLowerCase()); + return startsWith(commentText, (c) => c.toLowerCase()); } function startsWithUppercase(commentText: string) { - return startsWith(commentText, c => c.toUpperCase()); + return startsWith(commentText, (c) => c.toUpperCase()); } function startsWithSpace(commentText: string) { diff --git a/src/rules/completedDocsRule.ts b/src/rules/completedDocsRule.ts index b5d2b265ebd..a43ad746ac2 100644 --- a/src/rules/completedDocsRule.ts +++ b/src/rules/completedDocsRule.ts @@ -106,7 +106,7 @@ export class CompletedDocsWalker extends Lint.ProgramAwareRuleWalker { const comments = this.getTypeChecker().getSymbolAtLocation(node.name).getDocumentationComment(); - if (comments.map(comment => comment.text).join("").trim() === "") { + if (comments.map((comment) => comment.text).join("").trim() === "") { this.addFailure(this.createDocumentationFailure(node, nodeToCheck)); } } diff --git a/src/rules/curlyRule.ts b/src/rules/curlyRule.ts index b86b74b7221..bcddb77b349 100644 --- a/src/rules/curlyRule.ts +++ b/src/rules/curlyRule.ts @@ -83,7 +83,7 @@ class CurlyWalker extends Lint.RuleWalker { this.addFailure(this.createFailure( node.getStart(), node.thenStatement.getEnd() - node.getStart(), - Rule.IF_FAILURE_STRING + Rule.IF_FAILURE_STRING, )); } @@ -97,7 +97,7 @@ class CurlyWalker extends Lint.RuleWalker { this.addFailure(this.createFailure( elseKeywordNode.getStart(), node.elseStatement.getEnd() - elseKeywordNode.getStart(), - Rule.ELSE_FAILURE_STRING + Rule.ELSE_FAILURE_STRING, )); } diff --git a/src/rules/cyclomaticComplexityRule.ts b/src/rules/cyclomaticComplexityRule.ts index 8f4ddc430ed..ddefdee516f 100644 --- a/src/rules/cyclomaticComplexityRule.ts +++ b/src/rules/cyclomaticComplexityRule.ts @@ -15,8 +15,8 @@ * limitations under the License. */ -import * as Lint from "../index"; import * as ts from "typescript"; +import * as Lint from "../index"; export class Rule extends Lint.Rules.AbstractRule { @@ -54,10 +54,13 @@ export class Rule extends Lint.Rules.AbstractRule { }; /* tslint:enable:object-literal-sort-keys */ - public static ANONYMOUS_FAILURE_STRING = (expected: number, actual: number) => - `The function has a cyclomatic complexity of ${actual} which is higher than the threshold of ${expected}`; - public static NAMED_FAILURE_STRING = (expected: number, actual: number, name: string) => - `The function ${name} has a cyclomatic complexity of ${actual} which is higher than the threshold of ${expected}`; + public static ANONYMOUS_FAILURE_STRING = (expected: number, actual: number) => { + return `The function has a cyclomatic complexity of ${actual} which is higher than the threshold of ${expected}`; + } + + public static NAMED_FAILURE_STRING = (expected: number, actual: number, name: string) => { + return `The function ${name} has a cyclomatic complexity of ${actual} which is higher than the threshold of ${expected}`; + } public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { return this.applyWithWalker(new CyclomaticComplexityWalker(sourceFile, this.getOptions(), this.threshold)); diff --git a/src/rules/linebreakStyleRule.ts b/src/rules/linebreakStyleRule.ts index 2fc8bf2a999..a8cff31c5f7 100644 --- a/src/rules/linebreakStyleRule.ts +++ b/src/rules/linebreakStyleRule.ts @@ -53,7 +53,7 @@ export class Rule extends Lint.Rules.AbstractRule { sourceFile.languageVersion, false, sourceFile.languageVariant, - sourceFile.getFullText() + sourceFile.getFullText(), ); const linebreakStyle = this.getOptions().ruleArguments[0] || OPTION_LINEBREAK_STYLE_LF; diff --git a/src/rules/maxClassesPerFileRule.ts b/src/rules/maxClassesPerFileRule.ts index cd7d112b10b..5bb613adaa4 100644 --- a/src/rules/maxClassesPerFileRule.ts +++ b/src/rules/maxClassesPerFileRule.ts @@ -1,5 +1,5 @@ -import * as Lint from "../index"; -import * as ts from "typescript"; +import * as ts from "typescript"; +import * as Lint from "../index"; export class Rule extends Lint.Rules.AbstractRule { diff --git a/src/rules/maxFileLineCountRule.ts b/src/rules/maxFileLineCountRule.ts index 7995c6d28d5..641800d2b11 100644 --- a/src/rules/maxFileLineCountRule.ts +++ b/src/rules/maxFileLineCountRule.ts @@ -14,8 +14,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import * as Lint from "../index"; import * as ts from "typescript"; +import * as Lint from "../index"; export class Rule extends Lint.Rules.AbstractRule { /* tslint:disable:object-literal-sort-keys */ @@ -40,7 +40,7 @@ export class Rule extends Lint.Rules.AbstractRule { let msg = `This file has ${lineCount} lines, which exceeds the maximum of ${lineLimit} lines allowed. `; msg += `Consider breaking this file up into smaller parts`; return msg; - }; + } public isEnabled(): boolean { if (super.isEnabled()) { diff --git a/src/rules/maxLineLengthRule.ts b/src/rules/maxLineLengthRule.ts index bf356a4bf8e..d46730c2624 100644 --- a/src/rules/maxLineLengthRule.ts +++ b/src/rules/maxLineLengthRule.ts @@ -41,7 +41,7 @@ export class Rule extends Lint.Rules.AbstractRule { public static FAILURE_STRING_FACTORY = (lineLimit: number) => { return `Exceeds maximum line length of ${lineLimit}`; - }; + } public isEnabled(): boolean { if (super.isEnabled()) { diff --git a/src/rules/memberAccessRule.ts b/src/rules/memberAccessRule.ts index 817b38dc2da..8fef76d20f3 100644 --- a/src/rules/memberAccessRule.ts +++ b/src/rules/memberAccessRule.ts @@ -101,7 +101,7 @@ export class MemberAccessWalker extends Lint.RuleWalker { node.modifiers, ts.SyntaxKind.PublicKeyword, ts.SyntaxKind.PrivateKeyword, - ts.SyntaxKind.ProtectedKeyword + ts.SyntaxKind.ProtectedKeyword, ); if (!hasAnyVisibilityModifiers) { diff --git a/src/rules/noDuplicateVariableRule.ts b/src/rules/noDuplicateVariableRule.ts index 5d636721831..7dc9473bfdb 100644 --- a/src/rules/noDuplicateVariableRule.ts +++ b/src/rules/noDuplicateVariableRule.ts @@ -38,7 +38,9 @@ export class Rule extends Lint.Rules.AbstractRule { }; /* tslint:enable:object-literal-sort-keys */ - public static FAILURE_STRING_FACTORY = (name: string) => `Duplicate variable: '${name}'`; + public static FAILURE_STRING_FACTORY = (name: string) => { + return `Duplicate variable: '${name}'`; + } public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { return this.applyWithWalker(new NoDuplicateVariableWalker(sourceFile, this.getOptions())); diff --git a/src/rules/noEmptyRule.ts b/src/rules/noEmptyRule.ts index fe3b216665c..d6dbbe1b695 100644 --- a/src/rules/noEmptyRule.ts +++ b/src/rules/noEmptyRule.ts @@ -69,7 +69,7 @@ class BlockWalker extends Lint.RuleWalker { ts.SyntaxKind.PrivateKeyword, ts.SyntaxKind.ProtectedKeyword, ts.SyntaxKind.PublicKeyword, - ts.SyntaxKind.ReadonlyKeyword + ts.SyntaxKind.ReadonlyKeyword, ); if (hasPropertyAccessModifier) { diff --git a/src/rules/noInferrableTypesRule.ts b/src/rules/noInferrableTypesRule.ts index 257030e7565..79c918d9cc8 100644 --- a/src/rules/noInferrableTypesRule.ts +++ b/src/rules/noInferrableTypesRule.ts @@ -47,7 +47,9 @@ export class Rule extends Lint.Rules.AbstractRule { }; /* tslint:enable:object-literal-sort-keys */ - public static FAILURE_STRING_FACTORY = (type: string) => `LHS type (${type}) inferred by RHS expression, remove type annotation`; + public static FAILURE_STRING_FACTORY = (type: string) => { + return `LHS type (${type}) inferred by RHS expression, remove type annotation`; + } public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { return this.applyWithWalker(new NoInferrableTypesWalker(sourceFile, this.getOptions())); diff --git a/src/rules/noParameterPropertiesRule.ts b/src/rules/noParameterPropertiesRule.ts index 9561d64fb00..d4ee70be800 100644 --- a/src/rules/noParameterPropertiesRule.ts +++ b/src/rules/noParameterPropertiesRule.ts @@ -35,7 +35,9 @@ export class Rule extends Lint.Rules.AbstractRule { }; /* tslint:enable:object-literal-sort-keys */ - public static FAILURE_STRING_FACTORY = (ident: string) => `Property '${ident}' cannot be declared in the constructor`; + public static FAILURE_STRING_FACTORY = (ident: string) => { + return `Property '${ident}' cannot be declared in the constructor`; + } public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { return this.applyWithWalker(new NoParameterPropertiesWalker(sourceFile, this.getOptions())); diff --git a/src/rules/noShadowedVariableRule.ts b/src/rules/noShadowedVariableRule.ts index e50c3b168df..6b2ffa0057e 100644 --- a/src/rules/noShadowedVariableRule.ts +++ b/src/rules/noShadowedVariableRule.ts @@ -33,7 +33,9 @@ export class Rule extends Lint.Rules.AbstractRule { }; /* tslint:enable:object-literal-sort-keys */ - public static FAILURE_STRING_FACTORY = (name: string) => `Shadowed variable: '${name}'`; + public static FAILURE_STRING_FACTORY = (name: string) => { + return `Shadowed variable: '${name}'`; + } public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { return this.applyWithWalker(new NoShadowedVariableWalker(sourceFile, this.getOptions())); diff --git a/src/rules/noSwitchCaseFallThroughRule.ts b/src/rules/noSwitchCaseFallThroughRule.ts index 218a6bd4dc0..29b8cc7b678 100644 --- a/src/rules/noSwitchCaseFallThroughRule.ts +++ b/src/rules/noSwitchCaseFallThroughRule.ts @@ -83,7 +83,7 @@ export class NoSwitchCaseFallThroughWalker extends Lint.RuleWalker { this.addFailure(this.createFailure( switchClauses[i + 1].getStart(), "case".length, - `${Rule.FAILURE_STRING_PART}'case'` + `${Rule.FAILURE_STRING_PART}'case'`, )); } } diff --git a/src/rules/noUnsafeFinallyRule.ts b/src/rules/noUnsafeFinallyRule.ts index 1a5664ba4f1..bfe8669a5a7 100644 --- a/src/rules/noUnsafeFinallyRule.ts +++ b/src/rules/noUnsafeFinallyRule.ts @@ -15,8 +15,8 @@ * limitations under the License. */ -import * as Lint from "../index"; import * as ts from "typescript"; +import * as Lint from "../index"; export class Rule extends Lint.Rules.AbstractRule { /* tslint:disable:object-literal-sort-keys */ @@ -40,14 +40,12 @@ export class Rule extends Lint.Rules.AbstractRule { /* tslint:enable:object-literal-sort-keys */ public static FAILURE_TYPE_BREAK = "break"; - public static FAILURE_TYPE_CONTINUE = "continue"; - public static FAILURE_TYPE_RETURN = "return"; - public static FAILURE_TYPE_THROW = "throw"; - - public static FAILURE_STRING_FACTORY = (name: string) => `${name} statements in finally blocks are forbidden.`; + public static FAILURE_STRING_FACTORY = (name: string) => { + return `${name} statements in finally blocks are forbidden.`; + } public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { return this.applyWithWalker(new NoReturnInFinallyScopeAwareWalker(sourceFile, this.getOptions())); @@ -81,7 +79,7 @@ interface IFinallyScope { /** * A collection of `break` or `continue` labels in this scope. */ - labels: Array; + labels: string[]; } /** diff --git a/src/rules/noUnusedExpressionRule.ts b/src/rules/noUnusedExpressionRule.ts index 83c51a222c9..4fa5285e7e3 100644 --- a/src/rules/noUnusedExpressionRule.ts +++ b/src/rules/noUnusedExpressionRule.ts @@ -70,7 +70,7 @@ export class NoUnusedExpressionWalker extends Lint.RuleWalker { if (checkPreviousSiblings) { const siblings: ts.Node[] = []; - ts.forEachChild(node.parent, child => { siblings.push(child); }); + ts.forEachChild(node.parent, (child) => { siblings.push(child); }); return siblings.slice(0, siblings.indexOf(node)).every((n) => NoUnusedExpressionWalker.isDirective(n, false)); } else { return true; diff --git a/src/rules/noUnusedVariableRule.ts b/src/rules/noUnusedVariableRule.ts index 280e0bc2a76..3e1dcb54485 100644 --- a/src/rules/noUnusedVariableRule.ts +++ b/src/rules/noUnusedVariableRule.ts @@ -74,8 +74,9 @@ export class Rule extends Lint.Rules.AbstractRule { public static FAILURE_TYPE_PARAM = "parameter"; public static FAILURE_TYPE_PROP = "property"; public static FAILURE_TYPE_VAR = "variable"; - - public static FAILURE_STRING_FACTORY = (type: string, name: string) => `Unused ${type}: '${name}'`; + public static FAILURE_STRING_FACTORY = (type: string, name: string) => { + return `Unused ${type}: '${name}'`; + } public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { const languageService = Lint.createLanguageService(sourceFile.fileName, sourceFile.getFullText()); @@ -196,7 +197,7 @@ class NoUnusedVariablesWalker extends Lint.RuleWalker { if (importClause.namedBindings != null) { if (importClause.namedBindings.kind === ts.SyntaxKind.NamedImports) { let imports = node.importClause.namedBindings as ts.NamedImports; - usedNamedImports = imports.elements.map(e => this.isUsed(e.name.text, e.name.getStart())); + usedNamedImports = imports.elements.map((e) => this.isUsed(e.name.text, e.name.getStart())); } // Avoid deleting the whole statement if there's an import * inside if (importClause.namedBindings.kind === ts.SyntaxKind.NamespaceImport) { @@ -205,7 +206,7 @@ class NoUnusedVariablesWalker extends Lint.RuleWalker { } // Delete the entire import statement if named and default imports all unused - if (!usesDefaultImport && usedNamedImports.every(e => !e)) { + if (!usesDefaultImport && usedNamedImports.every((e) => !e)) { this.fail(Rule.FAILURE_TYPE_IMPORT, node.getText(), node.getStart(), this.deleteImportStatement(node)); super.visitImportDeclaration(node); return; @@ -222,7 +223,7 @@ class NoUnusedVariablesWalker extends Lint.RuleWalker { if (importClause.namedBindings != null && importClause.namedBindings.kind === ts.SyntaxKind.NamedImports) { // Delete the entire named imports if all unused, including curly braces. - if (usedNamedImports.every(e => !e)) { + if (usedNamedImports.every((e) => !e)) { const start = importClause.name != null ? importClause.name.getEnd() : importClause.namedBindings.getStart(); this.fail(Rule.FAILURE_TYPE_IMPORT, importClause.namedBindings.getText(), importClause.namedBindings.getStart(), [ this.deleteText(start, importClause.namedBindings.getEnd() - start), diff --git a/src/rules/objectLiteralKeyQuotesRule.ts b/src/rules/objectLiteralKeyQuotesRule.ts index aab9d2ee470..46ba55f8caa 100644 --- a/src/rules/objectLiteralKeyQuotesRule.ts +++ b/src/rules/objectLiteralKeyQuotesRule.ts @@ -1,5 +1,22 @@ -import * as Lint from "../index"; +/** + * @license + * Copyright 2016 Palantir Technologies, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import * as ts from "typescript"; +import * as Lint from "../index"; export class Rule extends Lint.Rules.AbstractRule { /* tslint:disable:object-literal-sort-keys */ @@ -46,8 +63,12 @@ export class Rule extends Lint.Rules.AbstractRule { /* tslint:enable:object-literal-sort-keys */ public static INCONSISTENT_PROPERTY = `All property names in this object literal must be consistently quoted or unquoted.`; - public static UNNEEDED_QUOTES = (name: string) => `Unnecessarily quoted property '${name}' found.`; - public static UNQUOTED_PROPERTY = (name: string) => `Unquoted property '${name}' found.`; + public static UNNEEDED_QUOTES = (name: string) => { + return `Unnecessarily quoted property '${name}' found.`; + } + public static UNQUOTED_PROPERTY = (name: string) => { + return `Unquoted property '${name}' found.`; + } public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { const objectLiteralKeyQuotesWalker = new ObjectLiteralKeyQuotesWalker(sourceFile, this.getOptions()); @@ -57,9 +78,7 @@ export class Rule extends Lint.Rules.AbstractRule { // This is simplistic. See https://mothereff.in/js-properties for the gorey details. const IDENTIFIER_NAME_REGEX = /^(?:[\$A-Z_a-z])*$/; - const NUMBER_REGEX = /^[0-9]+$/; - type QuotesMode = "always" | "as-needed" | "consistent" | "consistent-as-needed"; interface IObjectLiteralState { diff --git a/src/rules/objectLiteralShorthandRule.ts b/src/rules/objectLiteralShorthandRule.ts index ed59daa3828..aefc93181a8 100644 --- a/src/rules/objectLiteralShorthandRule.ts +++ b/src/rules/objectLiteralShorthandRule.ts @@ -1,5 +1,5 @@ -import * as Lint from "../index"; import * as ts from "typescript"; +import * as Lint from "../index"; export class Rule extends Lint.Rules.AbstractRule { /* tslint:disable:object-literal-sort-keys */ diff --git a/src/rules/objectLiteralSortKeysRule.ts b/src/rules/objectLiteralSortKeysRule.ts index 48b9567e3ec..78dc7d96efa 100644 --- a/src/rules/objectLiteralSortKeysRule.ts +++ b/src/rules/objectLiteralSortKeysRule.ts @@ -33,7 +33,9 @@ export class Rule extends Lint.Rules.AbstractRule { }; /* tslint:enable:object-literal-sort-keys */ - public static FAILURE_STRING_FACTORY = (name: string) => `The key '${name}' is not sorted alphabetically`; + public static FAILURE_STRING_FACTORY = (name: string) => { + return `The key '${name}' is not sorted alphabetically`; + } public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { return this.applyWithWalker(new ObjectLiteralSortKeysWalker(sourceFile, this.getOptions())); diff --git a/src/rules/orderedImportsRule.ts b/src/rules/orderedImportsRule.ts index b241f212e21..a78e4fc26e2 100644 --- a/src/rules/orderedImportsRule.ts +++ b/src/rules/orderedImportsRule.ts @@ -89,7 +89,7 @@ export class Rule extends Lint.Rules.AbstractRule { // Convert aBcD --> AbCd function flipCase(x: string): string { - return x.split("").map(char => { + return x.split("").map((char) => { if (char >= "a" && char <= "z") { return char.toUpperCase(); } else if (char >= "A" && char <= "Z") { @@ -143,7 +143,7 @@ function sortByKey(xs: T[], getSortKey: (x: T) => string): T[] { // Transformations to apply to produce the desired ordering of imports. // The imports must be lexicographically sorted after applying the transform. const TRANSFORMS: {[ordering: string]: (x: string) => string} = { - any: () => "", + "any": () => "", "case-insensitive": (x: string) => x.toLowerCase(), "lowercase-first": flipCase, "lowercase-last": (x: string) => x, diff --git a/src/rules/preferForOfRule.ts b/src/rules/preferForOfRule.ts index 68183ea3c49..51edb4eca1f 100644 --- a/src/rules/preferForOfRule.ts +++ b/src/rules/preferForOfRule.ts @@ -15,8 +15,8 @@ * limitations under the License. */ -import * as Lint from "../index"; import * as ts from "typescript"; +import * as Lint from "../index"; export class Rule extends Lint.Rules.AbstractRule { /* tslint:disable:object-literal-sort-keys */ diff --git a/src/rules/quotemarkRule.ts b/src/rules/quotemarkRule.ts index ab159c96be5..ebfb7bda6f3 100644 --- a/src/rules/quotemarkRule.ts +++ b/src/rules/quotemarkRule.ts @@ -21,7 +21,7 @@ import * as Lint from "../index"; enum QuoteMark { SINGLE_QUOTES, - DOUBLE_QUOTES + DOUBLE_QUOTES, } export class Rule extends Lint.Rules.AbstractRule { diff --git a/src/rules/restrictPlusOperandsRule.ts b/src/rules/restrictPlusOperandsRule.ts index d5fe764fad6..05becd841fa 100644 --- a/src/rules/restrictPlusOperandsRule.ts +++ b/src/rules/restrictPlusOperandsRule.ts @@ -34,7 +34,9 @@ export class Rule extends Lint.Rules.TypedRule { /* tslint:enable:object-literal-sort-keys */ public static MISMATCHED_TYPES_FAILURE = "Types of values used in '+' operation must match"; - public static UNSUPPORTED_TYPE_FAILURE_FACTORY = (type: string) => `cannot add type ${type}`; + public static UNSUPPORTED_TYPE_FAILURE_FACTORY = (type: string) => { + return `cannot add type ${type}`; + } public applyWithProgram(sourceFile: ts.SourceFile, program: ts.Program): Lint.RuleFailure[] { return this.applyWithWalker(new RestrictPlusOperandsWalker(sourceFile, this.getOptions(), program)); diff --git a/src/rules/trailingCommaRule.ts b/src/rules/trailingCommaRule.ts index 35d50dd61bd..845cf4be0f7 100644 --- a/src/rules/trailingCommaRule.ts +++ b/src/rules/trailingCommaRule.ts @@ -66,7 +66,7 @@ export class Rule extends Lint.Rules.AbstractRule { } class TrailingCommaWalker extends Lint.RuleWalker { - private static SYNTAX_LIST_WRAPPER_TOKENS: [ts.SyntaxKind, ts.SyntaxKind][] = [ + private static SYNTAX_LIST_WRAPPER_TOKENS: Array<[ts.SyntaxKind, ts.SyntaxKind]> = [ [ts.SyntaxKind.OpenBraceToken, ts.SyntaxKind.CloseBraceToken], [ts.SyntaxKind.OpenBracketToken, ts.SyntaxKind.CloseBracketToken], [ts.SyntaxKind.OpenParenToken, ts.SyntaxKind.CloseParenToken], @@ -189,8 +189,8 @@ class TrailingCommaWalker extends Lint.RuleWalker { // as opposed to optionals alongside it. So instead of children[i + 1] having // [ PropertySignature, Semicolon, PropertySignature, Semicolon ], the AST is // [ PropertySignature, PropertySignature], where the Semicolons are under PropertySignature - const hasSemicolon = grandChildren.some(grandChild => { - return grandChild.getChildren().some(ggc => ggc.kind === ts.SyntaxKind.SemicolonToken); + const hasSemicolon = grandChildren.some((grandChild) => { + return grandChild.getChildren().some((ggc) => ggc.kind === ts.SyntaxKind.SemicolonToken); }); if (!hasSemicolon) { diff --git a/src/rules/typedefWhitespaceRule.ts b/src/rules/typedefWhitespaceRule.ts index ea76e575ef5..c7cbb094262 100644 --- a/src/rules/typedefWhitespaceRule.ts +++ b/src/rules/typedefWhitespaceRule.ts @@ -30,7 +30,7 @@ const SPACE_OBJECT = { properties: { "call-signature": SPACE_OPTIONS, "index-signature": SPACE_OPTIONS, - parameter: SPACE_OPTIONS, + "parameter": SPACE_OPTIONS, "property-declaration": SPACE_OPTIONS, "variable-declaration": SPACE_OPTIONS, }, @@ -90,9 +90,7 @@ export class Rule extends Lint.Rules.AbstractRule { class TypedefWhitespaceWalker extends Lint.RuleWalker { private static getColonPosition(node: ts.Node) { - const colon = node.getChildren().filter((child) => - child.kind === ts.SyntaxKind.ColonToken - )[0]; + const colon = node.getChildren().filter((child) => child.kind === ts.SyntaxKind.ColonToken)[0]; return colon == null ? -1 : colon.getStart(); } @@ -241,7 +239,7 @@ class TypedefWhitespaceWalker extends Lint.RuleWalker { hasLeadingWhitespace, hasSeveralLeadingWhitespaces, colonPosition - 1, - message + message, ); } } @@ -283,7 +281,7 @@ class TypedefWhitespaceWalker extends Lint.RuleWalker { hasTrailingWhitespace, hasSeveralTrailingWhitespaces, colonPosition + 1, - message + message, ); } } diff --git a/src/test.ts b/src/test.ts index de9f71d133d..90c8e94cc75 100644 --- a/src/test.ts +++ b/src/test.ts @@ -119,7 +119,7 @@ export function runTest(testDirectory: string, rulesDirectory?: string | string[ const stat = fs.statSync(fixedFile); if (stat.isFile()) { fixedFileText = fs.readFileSync(fixedFile, "utf8"); - const fixes = failures.filter(f => f.hasFix()).map(f => f.getFix()); + const fixes = failures.filter((f) => f.hasFix()).map((f) => f.getFix()); newFileText = Fix.applyAll(fileTextWithoutMarkup, fixes); } } catch (e) { diff --git a/src/test/parse.ts b/src/test/parse.ts index 24366ecbc58..d0f8c8d2abc 100644 --- a/src/test/parse.ts +++ b/src/test/parse.ts @@ -24,7 +24,7 @@ import { parseLine, printLine, } from "./lines"; -import {LintError, errorComparator, lintSyntaxError} from "./lintError"; +import {errorComparator, LintError, lintSyntaxError} from "./lintError"; /** * Takes the full text of a .lint file and returns the contents of the file @@ -83,7 +83,7 @@ export function parseErrorsFromMarkup(text: string): LintError[] { for (let nextLineNo = lineNo + 1; ; ++nextLineNo) { if (!isValidErrorMarkupContinuation(errorLinesForCodeLines, nextLineNo)) { throw lintSyntaxError( - `Error mark starting at ${errorStartPos.line}:${errorStartPos.col} does not end correctly.` + `Error mark starting at ${errorStartPos.line}:${errorStartPos.col} does not end correctly.`, ); } else { const nextErrorLine = errorLinesForCodeLines[nextLineNo].shift(); @@ -122,7 +122,7 @@ export function createMarkupFromErrors(code: string, lintErrors: LintError[]) { errorLinesForCodeText[startPos.line].push(new EndErrorLine( startPos.col, endPos.col, - message + message, )); } else { // multiline error diff --git a/src/tslint-cli.ts b/src/tslint-cli.ts index ab8d3d259c1..7018a2e76fe 100644 --- a/src/tslint-cli.ts +++ b/src/tslint-cli.ts @@ -43,58 +43,58 @@ let processed = optimist } }) .options({ - c: { + "c": { alias: "config", describe: "configuration file", }, - e: { + "e": { alias: "exclude", describe: "exclude globs from path expansion", type: "string", }, - fix: { + "fix": { describe: "Fixes linting errors for select rules. This may overwrite linted files", type: "boolean", }, - force: { + "force": { describe: "return status code 0 even if there are lint errors", type: "boolean", }, - h: { + "h": { alias: "help", describe: "display detailed help", }, - i: { + "i": { alias: "init", describe: "generate a tslint.json config file in the current working directory", }, - o: { + "o": { alias: "out", describe: "output file", }, - project: { + "project": { describe: "tsconfig.json file", }, - r: { + "r": { alias: "rules-dir", describe: "rules directory", }, - s: { + "s": { alias: "formatters-dir", describe: "formatters directory", }, - t: { + "t": { alias: "format", default: "prose", describe: "output format (prose, json, stylish, verbose, pmd, msbuild, checkstyle, vso, fileslist)", }, - test: { + "test": { describe: "test that tslint produces the correct output for the specified directory", }, "type-check": { describe: "enable type checking when linting a project", }, - v: { + "v": { alias: "version", describe: "current version", }, diff --git a/src/utils.ts b/src/utils.ts index c8b58e36c79..7279b2a7396 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -55,7 +55,7 @@ export function dedent(strings: TemplateStringsArray, ...values: string[]) { } // find the smallest indent, we don't want to remove all leading whitespace - const indent = Math.min(...match.map(el => el.length)); + const indent = Math.min(...match.map((el) => el.length)); const regexp = new RegExp("^[ \\t]{" + indent + "}", "gm"); fullString = indent > 0 ? fullString.replace(regexp, "") : fullString; return fullString; diff --git a/test/configurationTests.ts b/test/configurationTests.ts index 1a4d28eda2a..11134abcacb 100644 --- a/test/configurationTests.ts +++ b/test/configurationTests.ts @@ -16,7 +16,7 @@ import * as fs from "fs"; -import { IConfigurationFile, extendConfigurationFile, loadConfigurationFromPath } from "../src/configuration"; +import { extendConfigurationFile, IConfigurationFile, loadConfigurationFromPath } from "../src/configuration"; import { createTempFile } from "./utils"; describe("Configuration", () => { diff --git a/test/executable/executableTests.ts b/test/executable/executableTests.ts index 08655c96b56..60a2f7262b1 100644 --- a/test/executable/executableTests.ts +++ b/test/executable/executableTests.ts @@ -14,11 +14,11 @@ * limitations under the License. */ -import { createTempFile, denormalizeWinPath } from "../utils"; import * as cp from "child_process"; import * as fs from "fs"; import * as os from "os"; import * as path from "path"; +import { createTempFile, denormalizeWinPath } from "../utils"; // when tests are run with mocha from npm scripts CWD points to project root const EXECUTABLE_DIR = path.resolve(process.cwd(), "test", "executable"); diff --git a/test/external/tslint-test-custom-rules/rules/ruleOneRule.js b/test/external/tslint-test-custom-rules/rules/ruleOneRule.js index 1d2ff62d1af..9b8b16e0fb8 100644 --- a/test/external/tslint-test-custom-rules/rules/ruleOneRule.js +++ b/test/external/tslint-test-custom-rules/rules/ruleOneRule.js @@ -3,7 +3,7 @@ var __extends = (this && this.__extends) || function (d, b) { function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; -var Lint = require("tslint/lib/lint"); +var Lint = require("tslint"); var Rule = (function (_super) { __extends(Rule, _super); function Rule() { diff --git a/test/external/tslint-test-custom-rules/rules/ruleThreeRule.js b/test/external/tslint-test-custom-rules/rules/ruleThreeRule.js index 1d2ff62d1af..9b8b16e0fb8 100644 --- a/test/external/tslint-test-custom-rules/rules/ruleThreeRule.js +++ b/test/external/tslint-test-custom-rules/rules/ruleThreeRule.js @@ -3,7 +3,7 @@ var __extends = (this && this.__extends) || function (d, b) { function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; -var Lint = require("tslint/lib/lint"); +var Lint = require("tslint"); var Rule = (function (_super) { __extends(Rule, _super); function Rule() { diff --git a/test/external/tslint-test-custom-rules/rules/ruleTwoRule.js b/test/external/tslint-test-custom-rules/rules/ruleTwoRule.js index 1d2ff62d1af..9b8b16e0fb8 100644 --- a/test/external/tslint-test-custom-rules/rules/ruleTwoRule.js +++ b/test/external/tslint-test-custom-rules/rules/ruleTwoRule.js @@ -3,7 +3,7 @@ var __extends = (this && this.__extends) || function (d, b) { function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; -var Lint = require("tslint/lib/lint"); +var Lint = require("tslint"); var Rule = (function (_super) { __extends(Rule, _super); function Rule() { diff --git a/test/files/custom-rules-2/noFailRule.js b/test/files/custom-rules-2/noFailRule.js index 1d2ff62d1af..9b8b16e0fb8 100644 --- a/test/files/custom-rules-2/noFailRule.js +++ b/test/files/custom-rules-2/noFailRule.js @@ -3,7 +3,7 @@ var __extends = (this && this.__extends) || function (d, b) { function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; -var Lint = require("tslint/lib/lint"); +var Lint = require("tslint"); var Rule = (function (_super) { __extends(Rule, _super); function Rule() { diff --git a/test/files/custom-rules/alwaysFailRule.js b/test/files/custom-rules/alwaysFailRule.js index ee3abbe634d..b6d3d25921c 100644 --- a/test/files/custom-rules/alwaysFailRule.js +++ b/test/files/custom-rules/alwaysFailRule.js @@ -3,7 +3,7 @@ var __extends = (this && this.__extends) || function (d, b) { function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; -var Lint = require("tslint/lib/lint"); +var Lint = require("tslint"); var Rule = (function (_super) { __extends(Rule, _super); function Rule() { diff --git a/test/ruleLoaderTests.ts b/test/ruleLoaderTests.ts index daa8b1f6422..f9bb790b35e 100644 --- a/test/ruleLoaderTests.ts +++ b/test/ruleLoaderTests.ts @@ -23,10 +23,10 @@ describe("Rule Loader", () => { it("loads core rules", () => { const validConfiguration: {[name: string]: any} = { "class-name": true, - eofline: true, - forin: false, + "eofline": true, + "forin": false, "no-debugger": true, - quotemark: "single", + "quotemark": "single", }; const rules = loadRules(validConfiguration, {}, RULES_DIRECTORY); @@ -48,8 +48,8 @@ describe("Rule Loader", () => { it("doesn't ignore leading or trailing underscores or dashes", () => { /* tslint:disable:object-literal-sort-keys */ const invalidConfiguration: {[name: string]: any} = { - _indent: 6, - forin_: true, + "_indent": 6, + "forin_": true, "-quotemark": "single", "eofline-": true, }; @@ -64,10 +64,10 @@ describe("Rule Loader", () => { it("works with rulesDirectory argument as an Array", () => { const validConfiguration: {[name: string]: any} = { "class-name": true, - eofline: true, - forin: false, + "eofline": true, + "forin": false, "no-debugger": true, - quotemark: "single", + "quotemark": "single", }; const rules = loadRules(validConfiguration, {}, [RULES_DIRECTORY]); @@ -90,7 +90,7 @@ describe("Rule Loader", () => { assert.throws( () => loadRules(invalidConfiguration, {}, RULES_DIRECTORY, true), - /array-type/ + /array-type/, ); }); }); diff --git a/test/rules/no-unused-variable/default/false-positives.ts.lint b/test/rules/no-unused-variable/default/false-positives.ts.lint index 30cd304ee5b..bfd2b0ac19d 100644 --- a/test/rules/no-unused-variable/default/false-positives.ts.lint +++ b/test/rules/no-unused-variable/default/false-positives.ts.lint @@ -40,3 +40,13 @@ interface MyDateTimeOpts extends DateTimeOpts { let opts: MyDateTimeOpts; console.log(opts.timezoneOffset - 1); + +import * as myLib from 'myLib'; +export { myLib }; + +import foo from 'foo'; +const bar = {foo}; +myFunc(bar); + +import a from "module"; +export { a }; diff --git a/test/rules/no-unused-variable/default/import.ts.fix b/test/rules/no-unused-variable/default/import.ts.fix index c8de8aeeee8..081f3e24ca0 100644 --- a/test/rules/no-unused-variable/default/import.ts.fix +++ b/test/rules/no-unused-variable/default/import.ts.fix @@ -37,3 +37,7 @@ baz(); namedExport(); import "jquery"; + +import abc = require('abc'); +import def = abc.someVar; +console.log(def); diff --git a/test/rules/no-unused-variable/default/import.ts.lint b/test/rules/no-unused-variable/default/import.ts.lint index a0f019f289b..539dde7c3e1 100644 --- a/test/rules/no-unused-variable/default/import.ts.lint +++ b/test/rules/no-unused-variable/default/import.ts.lint @@ -58,3 +58,7 @@ baz(); namedExport(); import "jquery"; + +import abc = require('abc'); +import def = abc.someVar; +console.log(def); diff --git a/test/rules/semicolon/always/test.ts.fix b/test/rules/semicolon/always/test.ts.fix index b4e5a93118d..049d84d358f 100644 --- a/test/rules/semicolon/always/test.ts.fix +++ b/test/rules/semicolon/always/test.ts.fix @@ -68,6 +68,8 @@ interface ITest { foo?: string; bar: number; baz: boolean; + + readonly raz: number; } import {Router} from 'aurelia-router'; diff --git a/test/rules/semicolon/always/test.ts.lint b/test/rules/semicolon/always/test.ts.lint index ff46b4c6637..e99a82a7461 100644 --- a/test/rules/semicolon/always/test.ts.lint +++ b/test/rules/semicolon/always/test.ts.lint @@ -92,6 +92,8 @@ interface ITest { bar: number ~nil [Missing semicolon] baz: boolean; + + readonly raz: number; } import {Router} from 'aurelia-router'; diff --git a/tslint.json b/tslint.json index f0eae7a1393..c496ae41946 100644 --- a/tslint.json +++ b/tslint.json @@ -2,6 +2,7 @@ "extends": "tslint:latest", "rules": { "interface-name": false, + "max-classes-per-file": false, "max-line-length": [true, 140], "member-ordering": [true, "public-before-private", From be864a190f9dd3e46a3f7f32e6e3bf6d34d8ec5f Mon Sep 17 00:00:00 2001 From: Noah Chen Date: Fri, 18 Nov 2016 22:57:19 -0500 Subject: [PATCH 03/23] Allow null keyword in a type position (originally #1277) (#1745) --- src/linter.ts | 2 +- src/rules/noNullKeywordRule.ts | 12 +++++++++++- test/rules/no-null-keyword/test.ts.lint | 5 +++++ 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/linter.ts b/src/linter.ts index 39c192e1627..7132b293b0c 100644 --- a/src/linter.ts +++ b/src/linter.ts @@ -41,7 +41,7 @@ import { arrayify, dedent } from "./utils"; * Linter that can lint multiple files in consecutive runs. */ class Linter { - public static VERSION = "4.0.0-dev.2"; + public static VERSION = "4.0.0"; public static findConfiguration = findConfiguration; public static findConfigurationPath = findConfigurationPath; diff --git a/src/rules/noNullKeywordRule.ts b/src/rules/noNullKeywordRule.ts index 40e2e1af9f6..6592c1640d1 100644 --- a/src/rules/noNullKeywordRule.ts +++ b/src/rules/noNullKeywordRule.ts @@ -47,8 +47,18 @@ export class Rule extends Lint.Rules.AbstractRule { class NullWalker extends Lint.RuleWalker { public visitNode(node: ts.Node) { super.visitNode(node); - if (node.kind === ts.SyntaxKind.NullKeyword) { + if (node.kind === ts.SyntaxKind.NullKeyword && !isPartOfType(node)) { this.addFailure(this.createFailure(node.getStart(), node.getWidth(), Rule.FAILURE_STRING)); } } } + +function isPartOfType({ parent }: ts.Node) { + while (parent != null) { + if (ts.SyntaxKind.FirstTypeNode <= parent.kind && parent.kind <= ts.SyntaxKind.LastTypeNode) { + return true; + } + parent = parent.parent; + } + return false; +} diff --git a/test/rules/no-null-keyword/test.ts.lint b/test/rules/no-null-keyword/test.ts.lint index 1cab307c4a1..5eb317ac348 100644 --- a/test/rules/no-null-keyword/test.ts.lint +++ b/test/rules/no-null-keyword/test.ts.lint @@ -2,3 +2,8 @@ var x = null; // error ~~~~ [Use 'undefined' instead of 'null'] console.log(null, x); // error ~~~~ [Use 'undefined' instead of 'null'] + +let match(): string | null; +interface foo { + bar: [number, null, string]; +} From fbe5fd47906336b1a104db7c023357492fea9142 Mon Sep 17 00:00:00 2001 From: Noah Chen Date: Fri, 18 Nov 2016 22:58:49 -0500 Subject: [PATCH 04/23] Fix TS 2.0 one-line rule bug (originally #1429) (#1746) --- src/rules/oneLineRule.ts | 2 +- test/rules/one-line/all/test.ts.lint | 3 +++ test/rules/one-line/none/test.ts.lint | 3 +++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/rules/oneLineRule.ts b/src/rules/oneLineRule.ts index 561c31d78c7..bad0235d9d4 100644 --- a/src/rules/oneLineRule.ts +++ b/src/rules/oneLineRule.ts @@ -196,7 +196,7 @@ class OneLineWalker extends Lint.RuleWalker { public visitModuleDeclaration(node: ts.ModuleDeclaration) { const nameNode = node.name; const body = node.body; - if (body.kind === ts.SyntaxKind.ModuleBlock) { + if (body != null && body.kind === ts.SyntaxKind.ModuleBlock) { const openBraceToken = body.getChildAt(0); this.handleOpeningBrace(nameNode, openBraceToken); } diff --git a/test/rules/one-line/all/test.ts.lint b/test/rules/one-line/all/test.ts.lint index 769f784e529..f33ea57e051 100644 --- a/test/rules/one-line/all/test.ts.lint +++ b/test/rules/one-line/all/test.ts.lint @@ -140,3 +140,6 @@ let geoConfig: { timeout: 5000, enableHighAccuracy: false }; + +declare module "*"; +declare module "someLibrary/*"; diff --git a/test/rules/one-line/none/test.ts.lint b/test/rules/one-line/none/test.ts.lint index 751e45661ff..a6a5ddca080 100644 --- a/test/rules/one-line/none/test.ts.lint +++ b/test/rules/one-line/none/test.ts.lint @@ -119,3 +119,6 @@ let geoConfig: { timeout: 5000, enableHighAccuracy: false }; + +declare module "*"; +declare module "someLibrary/*"; From 273f160ec0b18086be36a51ea21d566c47357985 Mon Sep 17 00:00:00 2001 From: Noah Chen Date: Fri, 18 Nov 2016 23:13:31 -0500 Subject: [PATCH 05/23] Clarify `no-unused-variable` deprecation message with "tsc" --- src/rules/noUnusedVariableRule.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rules/noUnusedVariableRule.ts b/src/rules/noUnusedVariableRule.ts index 3e1dcb54485..70c1703d0f3 100644 --- a/src/rules/noUnusedVariableRule.ts +++ b/src/rules/noUnusedVariableRule.ts @@ -31,7 +31,7 @@ export class Rule extends Lint.Rules.AbstractRule { /* tslint:disable:object-literal-sort-keys */ public static metadata: Lint.IRuleMetadata = { ruleName: "no-unused-variable", - deprecationMessage: "Use the compiler options --noUnusedParameters and --noUnusedLocals instead.", + deprecationMessage: "Use the tsc compiler options --noUnusedParameters and --noUnusedLocals instead.", description: "Disallows unused imports, variables, functions and private class members.", optionsDescription: Lint.Utils.dedent` Three optional arguments may be optionally provided: From c76559eebbec2b9ceb8471b04d17e4a6b1293644 Mon Sep 17 00:00:00 2001 From: Noah Chen Date: Sat, 19 Nov 2016 01:38:20 -0500 Subject: [PATCH 06/23] Clarify blog post text --- docs/_posts/2016-11-17-new-for-4.0.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/_posts/2016-11-17-new-for-4.0.md b/docs/_posts/2016-11-17-new-for-4.0.md index 29a1ca08595..b92a1284ecd 100644 --- a/docs/_posts/2016-11-17-new-for-4.0.md +++ b/docs/_posts/2016-11-17-new-for-4.0.md @@ -15,13 +15,15 @@ TSLint 4.0 has been released! With this release comes a few exciting [changes][0 * [semicolon][7] * [trailing-comma][8] + * **Linting `.js` files**. *A much-requested feature from our community*. Simplify your toolset by running the same rules you know and love on your .js and .jsx files. Just add a `jsRules` [section][9] to your `tslint.json` file, and TSLint will your JavaScript files. -* **TypeScript 2.0+ required**. This lets us deprecate/remove rules that are checked by the compiler. These rules now cause compilation errors: +* **TypeScript 2.0+ required**. This lets us deprecate/remove rules that are checked by the compiler. Problematic code that once violated these rules now cause compilation errors in `tsc`: * no-duplicate-key * no-unreachable * no-unused-variable + * **Node.js API Change**. [Moved and renamed][11] some things to make more sense. Get it all when you use `import * as TSLint from "tslint"`. * **[Recommended Rules Updated][12]** @@ -36,6 +38,7 @@ TSLint 4.0 has been released! With this release comes a few exciting [changes][0 * [ordered-imports][21] * [prefer-for-of][22] + * **Other rules you might find handy**: * [completed-docs][23] * [cyclomatic-complexity][24] From 5128ef5997727c8ccd1176a23313309ba97857d9 Mon Sep 17 00:00:00 2001 From: Klaus Meinhardt Date: Sun, 20 Nov 2016 01:06:32 +0100 Subject: [PATCH 07/23] Fix description of maxClassesPerFileRule (#1750) --- src/rules/maxClassesPerFileRule.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/rules/maxClassesPerFileRule.ts b/src/rules/maxClassesPerFileRule.ts index 5bb613adaa4..04b4a933ccc 100644 --- a/src/rules/maxClassesPerFileRule.ts +++ b/src/rules/maxClassesPerFileRule.ts @@ -7,8 +7,7 @@ export class Rule extends Lint.Rules.AbstractRule { public static metadata: Lint.IRuleMetadata = { ruleName: "max-classes-per-file", description: Lint.Utils.dedent` - A file may not contain more than the specified number of classes - if the file name does not match the "ignore-filename-pattern" option`, + A file may not contain more than the specified number of classes`, rationale: Lint.Utils.dedent` Ensures that files have a single responsibility so that that classes each exist in their own files`, optionsDescription: Lint.Utils.dedent` From 3b5e8939fe64a721b9e1a3081b62f8cba33537bc Mon Sep 17 00:00:00 2001 From: Adi Dahiya Date: Sat, 19 Nov 2016 19:19:18 -0500 Subject: [PATCH 08/23] Remove no-unused-variable from recommended config (#1754) Fixes #1748 --- src/configs/recommended.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/configs/recommended.ts b/src/configs/recommended.ts index 77acc19835a..a1f4dac224b 100644 --- a/src/configs/recommended.ts +++ b/src/configs/recommended.ts @@ -71,8 +71,6 @@ export const rules = { "no-unsafe-finally": true, "no-unused-expression": true, "no-unused-new": true, - // deprecated as of v4.0 - "no-unused-variable": false, // disable this rule as it is very heavy performance-wise and not that useful "no-use-before-declare": false, "no-var-keyword": true, From ac05171f547a1b0711df7ff7301fd9bd5f5a7265 Mon Sep 17 00:00:00 2001 From: Adi Dahiya Date: Sat, 19 Nov 2016 19:23:13 -0500 Subject: [PATCH 09/23] Prepare release v4.0.1 (#1755) --- CHANGELOG.md | 4 ++++ package.json | 2 +- src/linter.ts | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 21ff0dce1f6..ce21cbf741b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Change Log === +v4.0.1 +--- +* [bugfix] Removed `no-unused-variable` rule from recommended config, as it was causing spurious deprecation warnings. + v4.0.0-dev.2 --- * Include latest v4.0.0 changes diff --git a/package.json b/package.json index ede2e56f300..40ad22d92a6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tslint", - "version": "4.0.0", + "version": "4.0.1", "description": "An extensible static analysis linter for the TypeScript language", "bin": { "tslint": "./bin/tslint" diff --git a/src/linter.ts b/src/linter.ts index 7132b293b0c..928ab96443d 100644 --- a/src/linter.ts +++ b/src/linter.ts @@ -41,7 +41,7 @@ import { arrayify, dedent } from "./utils"; * Linter that can lint multiple files in consecutive runs. */ class Linter { - public static VERSION = "4.0.0"; + public static VERSION = "4.0.1"; public static findConfiguration = findConfiguration; public static findConfigurationPath = findConfigurationPath; From 84eeb10352350b0f12aa52416b541af120109d13 Mon Sep 17 00:00:00 2001 From: Adi Dahiya Date: Mon, 21 Nov 2016 12:58:52 -0500 Subject: [PATCH 10/23] Fix README code samples to match module APIs --- README.md | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index d89e42499ee..5ee5d18a85a 100644 --- a/README.md +++ b/README.md @@ -213,9 +213,9 @@ tslint accepts the following command-line options: #### Library -```javascript -const Linter = require("tslint"); -const fs = require("fs"); +```js +import { Linter } from "tslint"; +import * as fs from "fs"; const fileName = "Specify file name"; const configuration = { @@ -240,7 +240,7 @@ const result = linter.lint(); To enable rules that work with the type checker, a TypeScript program object must be passed to the linter when using the programmatic API. Helper functions are provided to create a program from a `tsconfig.json` file. A project directory can be specified if project files do not lie in the same directory as the `tsconfig.json` file. -```javascript +```js const program = Linter.createProgram("tsconfig.json", "projectDir/"); const files = Linter.getFileNames(program); const results = files.map(file => { @@ -321,20 +321,20 @@ __Important conventions__: Now, let us first write the rule in TypeScript: -```typescript +```ts import * as ts from "typescript"; -import * as Lint from "tslint"; +import { Rules, RuleFailure, RuleWalker } from "tslint"; -export class Rule extends Lint.Rules.AbstractRule { +export class Rule extends Rules.AbstractRule { public static FAILURE_STRING = "import statement forbidden"; - public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { + public apply(sourceFile: ts.SourceFile): RuleFailure[] { return this.applyWithWalker(new NoImportsWalker(sourceFile, this.getOptions())); } } // The walker takes care of all the work. -class NoImportsWalker extends Lint.RuleWalker { +class NoImportsWalker extends RuleWalker { public visitImportDeclaration(node: ts.ImportDeclaration) { // create a failure at the current position this.addFailure(this.createFailure(node.getStart(), node.getWidth(), Rule.FAILURE_STRING)); @@ -370,11 +370,11 @@ Just like rules, additional formatters can also be supplied to TSLint via `--for ```typescript import * as ts from "typescript"; -import * as Lint from "tslint"; +import { Formatters, RuleFailure } from "tslint"; -export class Formatter extends Lint.Formatters.AbstractFormatter { - public format(failures: Lint.RuleFailure[]): string { - var failuresJSON = failures.map((failure: Lint.RuleFailure) => failure.toJson()); +export class Formatter extends Formatters.AbstractFormatter { + public format(failures: RuleFailure[]): string { + var failuresJSON = failures.map((failure: RuleFailure) => failure.toJson()); return JSON.stringify(failuresJSON); } } From 9a7d7195c7a670e15a70947288d7ae9cd0032659 Mon Sep 17 00:00:00 2001 From: gustavderdrache Date: Mon, 21 Nov 2016 14:26:37 -0500 Subject: [PATCH 11/23] Don't flag "" as a JS property name. (#1762) --- src/rules/objectLiteralKeyQuotesRule.ts | 2 +- test/rules/object-literal-key-quotes/as-needed/test.js.lint | 1 + test/rules/object-literal-key-quotes/as-needed/test.ts.lint | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/rules/objectLiteralKeyQuotesRule.ts b/src/rules/objectLiteralKeyQuotesRule.ts index 46ba55f8caa..413581f298b 100644 --- a/src/rules/objectLiteralKeyQuotesRule.ts +++ b/src/rules/objectLiteralKeyQuotesRule.ts @@ -77,7 +77,7 @@ export class Rule extends Lint.Rules.AbstractRule { } // This is simplistic. See https://mothereff.in/js-properties for the gorey details. -const IDENTIFIER_NAME_REGEX = /^(?:[\$A-Z_a-z])*$/; +const IDENTIFIER_NAME_REGEX = /^(?:[\$A-Z_a-z])+$/; const NUMBER_REGEX = /^[0-9]+$/; type QuotesMode = "always" | "as-needed" | "consistent" | "consistent-as-needed"; diff --git a/test/rules/object-literal-key-quotes/as-needed/test.js.lint b/test/rules/object-literal-key-quotes/as-needed/test.js.lint index e6dc627b5d0..86c24145018 100644 --- a/test/rules/object-literal-key-quotes/as-needed/test.js.lint +++ b/test/rules/object-literal-key-quotes/as-needed/test.js.lint @@ -19,4 +19,5 @@ const o = { "0x0": 0, "true": 0, // failure ~~~~~~ [Unnecessarily quoted property 'true' found.] + '': 'always quote the empty string', }; diff --git a/test/rules/object-literal-key-quotes/as-needed/test.ts.lint b/test/rules/object-literal-key-quotes/as-needed/test.ts.lint index e6dc627b5d0..86c24145018 100644 --- a/test/rules/object-literal-key-quotes/as-needed/test.ts.lint +++ b/test/rules/object-literal-key-quotes/as-needed/test.ts.lint @@ -19,4 +19,5 @@ const o = { "0x0": 0, "true": 0, // failure ~~~~~~ [Unnecessarily quoted property 'true' found.] + '': 'always quote the empty string', }; From 169fc1b2b0f305c7023e211fb266e02d63abc4ab Mon Sep 17 00:00:00 2001 From: Noah Chen Date: Mon, 21 Nov 2016 14:54:05 -0500 Subject: [PATCH 12/23] Extend timeout for executableTests (#1766) --- docs/_data/rules.json | 2 +- docs/rules/max-classes-per-file/index.html | 3 +-- test/executable/executableTests.ts | 2 ++ 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/_data/rules.json b/docs/_data/rules.json index 249191ad953..29873e94fe6 100644 --- a/docs/_data/rules.json +++ b/docs/_data/rules.json @@ -296,7 +296,7 @@ }, { "ruleName": "max-classes-per-file", - "description": "\nA file may not contain more than the specified number of classes \nif the file name does not match the \"ignore-filename-pattern\" option", + "description": "\nA file may not contain more than the specified number of classes", "rationale": "\nEnsures that files have a single responsibility so that that classes each exist in their own files", "optionsDescription": "\nThe one required argument is an integer indicating the maximum number of classes that can appear in a file.", "options": { diff --git a/docs/rules/max-classes-per-file/index.html b/docs/rules/max-classes-per-file/index.html index 1827aab9dc8..985d4d6824a 100644 --- a/docs/rules/max-classes-per-file/index.html +++ b/docs/rules/max-classes-per-file/index.html @@ -2,8 +2,7 @@ ruleName: max-classes-per-file description: |- - A file may not contain more than the specified number of classes - if the file name does not match the "ignore-filename-pattern" option + A file may not contain more than the specified number of classes rationale: |- Ensures that files have a single responsibility so that that classes each exist in their own files diff --git a/test/executable/executableTests.ts b/test/executable/executableTests.ts index 60a2f7262b1..2b9288c827b 100644 --- a/test/executable/executableTests.ts +++ b/test/executable/executableTests.ts @@ -28,6 +28,8 @@ const TEMP_JSON_PATH = path.resolve(EXECUTABLE_DIR, "tslint.json"); /* tslint:disable:only-arrow-functions */ describe("Executable", function() { this.slow(3000); // the executable is JIT-ed each time it runs; avoid showing slowness warnings + this.timeout(4000); + describe("Files", () => { it("exits with code 1 if no arguments passed", (done) => { execCli([], (err, stdout, stderr) => { From 33ea88c21fff3ef547f2c45d23de8bec9fd93de3 Mon Sep 17 00:00:00 2001 From: Noah Chen Date: Mon, 21 Nov 2016 15:43:34 -0500 Subject: [PATCH 13/23] Was not reporting correct # of fixes (#1767) --- src/linter.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/linter.ts b/src/linter.ts index 928ab96443d..3a0837d0e46 100644 --- a/src/linter.ts +++ b/src/linter.ts @@ -102,7 +102,6 @@ class Linter { let fileFailures: RuleFailure[] = []; if (this.options.fix) { - this.fixes = []; for (let rule of enabledRules) { let ruleFailures = this.applyRule(rule, sourceFile); const fixes = ruleFailures.map((f) => f.getFix()).filter((f) => !!f); From 69a32e6600d471eeaa2992ef4e0e978cfa83f2c9 Mon Sep 17 00:00:00 2001 From: Noah Chen Date: Mon, 21 Nov 2016 16:02:23 -0500 Subject: [PATCH 14/23] Fatal errors from API should be unhandled and caught in tslint-cli (#1764) --- docs/_data/rules.json | 2 +- docs/rules/no-unused-variable/index.html | 2 +- src/configuration.ts | 15 ++------- src/error.ts | 40 ++++++++++++++++++++++++ src/test/lintError.ts | 2 +- src/tslint-cli.ts | 20 +++++++----- 6 files changed, 58 insertions(+), 23 deletions(-) create mode 100644 src/error.ts diff --git a/docs/_data/rules.json b/docs/_data/rules.json index 29873e94fe6..61d5c2d6c0b 100644 --- a/docs/_data/rules.json +++ b/docs/_data/rules.json @@ -850,7 +850,7 @@ }, { "ruleName": "no-unused-variable", - "deprecationMessage": "Use the compiler options --noUnusedParameters and --noUnusedLocals instead.", + "deprecationMessage": "Use the tsc compiler options --noUnusedParameters and --noUnusedLocals instead.", "description": "Disallows unused imports, variables, functions and private class members.", "optionsDescription": "\nThree optional arguments may be optionally provided:\n\n* `\"check-parameters\"` disallows unused function and constructor parameters.\n * NOTE: this option is experimental and does not work with classes\n that use abstract method declarations, among other things.\n* `\"react\"` relaxes the rule for a namespace import named `React`\n(from either the module `\"react\"` or `\"react/addons\"`).\nAny JSX expression in the file will be treated as a usage of `React`\n(because it expands to `React.createElement `).\n* `{\"ignore-pattern\": \"pattern\"}` where pattern is a case-sensitive regexp.\nVariable names that match the pattern will be ignored.", "options": { diff --git a/docs/rules/no-unused-variable/index.html b/docs/rules/no-unused-variable/index.html index 3487ff0e49d..abb1bd4e72a 100644 --- a/docs/rules/no-unused-variable/index.html +++ b/docs/rules/no-unused-variable/index.html @@ -1,6 +1,6 @@ --- ruleName: no-unused-variable -deprecationMessage: Use the compiler options --noUnusedParameters and --noUnusedLocals instead. +deprecationMessage: Use the tsc compiler options --noUnusedParameters and --noUnusedLocals instead. description: 'Disallows unused imports, variables, functions and private class members.' optionsDescription: |- diff --git a/src/configuration.ts b/src/configuration.ts index e295aeada39..1f699b587f4 100644 --- a/src/configuration.ts +++ b/src/configuration.ts @@ -19,6 +19,7 @@ import findup = require("findup-sync"); import * as fs from "fs"; import * as path from "path"; import * as resolve from "resolve"; +import { FatalError } from "./error"; import {arrayify, objectify, stripComments} from "./utils"; @@ -32,16 +33,7 @@ export interface IConfigurationFile { rules?: any; } -/** - * Define `Error` here to avoid using `Error` from @types/node. - * Using the `node` version causes a compilation error when this code is used as an npm library if @types/node is not already imported. - */ -export interface Error { - message: string; -} - export interface IConfigurationLoadResult { - error?: Error; path: string; results?: IConfigurationFile; } @@ -119,11 +111,10 @@ export function findConfiguration(configFile: string, inputFilePath: string): IC try { loadResult.results = loadConfigurationFromPath(path); + return loadResult; } catch (error) { - loadResult.error = error; + throw new FatalError(`Failed to load ${path}: ${error.message}`, error); } - - return loadResult; } /** diff --git a/src/error.ts b/src/error.ts new file mode 100644 index 00000000000..f18aacd485c --- /dev/null +++ b/src/error.ts @@ -0,0 +1,40 @@ +/** + * @license + * Copyright 2016 Palantir Technologies, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Generic error typing for EcmaScript errors + * Define `Error` here to avoid using `Error` from @types/node. + * Using the `node` version causes a compilation error when this code is used as an npm library if @types/node is not already imported. + */ +export declare class Error { + public name?: string; + public message: string; + public stack?: string; + constructor(message?: string); +} + +/** + * Used to exit the program and display a friendly message without the callstack. + */ +export class FatalError extends Error { + public static NAME = "FatalError"; + constructor(public message: string, public innerError?: Error) { + super(message); + this.name = FatalError.NAME; + this.stack = new Error().stack; + } +} diff --git a/src/test/lintError.ts b/src/test/lintError.ts index 46968be2599..5f90a4b3a81 100644 --- a/src/test/lintError.ts +++ b/src/test/lintError.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { Error } from "../configuration"; +import { Error } from "../error"; export interface PositionInFile { line: number; diff --git a/src/tslint-cli.ts b/src/tslint-cli.ts index 7018a2e76fe..dc722d1f9e0 100644 --- a/src/tslint-cli.ts +++ b/src/tslint-cli.ts @@ -26,6 +26,7 @@ import { DEFAULT_CONFIG, findConfiguration, } from "./configuration"; +import { FatalError } from "./error"; import * as Linter from "./linter"; import { consoleTestResultHandler, runTest } from "./test"; import { updateNotifierCheck } from "./updateNotifier"; @@ -259,13 +260,7 @@ const processFiles = (files: string[], program?: ts.Program) => { const contents = fs.readFileSync(file, "utf8"); const configLoad = findConfiguration(possibleConfigAbsolutePath, file); - - if (configLoad.results) { - linter.lint(file, contents, configLoad.results); - } else { - console.error(`Failed to load ${configLoad.path}: ${configLoad.error.message}`); - process.exit(1); - } + linter.lint(file, contents, configLoad.results); } const lintResult = linter.getResult(); @@ -332,4 +327,13 @@ files = files .map((file: string) => glob.sync(file, { ignore: ignorePatterns, nodir: true })) .reduce((a: string[], b: string[]) => a.concat(b)); -processFiles(files, program); +try { + processFiles(files, program); +} catch (error) { + if (error.name === FatalError.NAME) { + console.error(error.message); + process.exit(1); + } + // rethrow unhandled error + throw error; +} From 51e399084e9f8cf9bca97f91404a7d342178fc63 Mon Sep 17 00:00:00 2001 From: Noah Chen Date: Mon, 21 Nov 2016 19:17:54 -0500 Subject: [PATCH 15/23] Update doc - bullet points not formatted correctly --- docs/develop/custom-rules/index.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/develop/custom-rules/index.md b/docs/develop/custom-rules/index.md index 8cc3cded4f7..36806f85425 100644 --- a/docs/develop/custom-rules/index.md +++ b/docs/develop/custom-rules/index.md @@ -8,10 +8,10 @@ TSLint ships with a set of core rules that can be configured. However, users are Let us take the example of how to write a new rule to forbid all import statements (you know, *for science*). Let us name the rule file `noImportsRule.ts`. Rules are referenced in `tslint.json` with their kebab-cased identifer, so `"no-imports": true` would configure the rule. __Important conventions__: -* Rule identifiers are always kebab-cased. -* Rule files are always camel-cased (`camelCasedRule.ts`). -* Rule files *must* contain the suffix `Rule`. -* The exported class must always be named `Rule` and extend from `Lint.Rules.AbstractRule`. +- Rule identifiers are always kebab-cased. +- Rule files are always camel-cased (`camelCasedRule.ts`). +- Rule files *must* contain the suffix `Rule`. +- The exported class must always be named `Rule` and extend from `Lint.Rules.AbstractRule`. Now, let us first write the rule in TypeScript: From 539075224e2d4de48242511f29dbd808bed83984 Mon Sep 17 00:00:00 2001 From: Adi Dahiya Date: Tue, 22 Nov 2016 00:52:13 -0500 Subject: [PATCH 16/23] Update README features list to include auto fixing --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 5ee5d18a85a..35fc869fee5 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,7 @@ TSLint supports: - custom formatters (failure reporters) - inline disabling / enabling of rules - configuration presets (`tslint:latest`, `tslint-react`, etc.) & composition +- automatic fixing of formatting & style violations - integration with [msbuild](https://github.com/joshuakgoldberg/tslint.msbuild), [grunt](https://github.com/palantir/grunt-tslint), [gulp](https://github.com/panuhorsmalahti/gulp-tslint), [atom](https://github.com/AtomLinter/linter-tslint), [eclipse](https://github.com/palantir/eclipse-tslint), [emacs](http://flycheck.org), [sublime](https://packagecontrol.io/packages/SublimeLinter-contrib-tslint), [vim](https://github.com/scrooloose/syntastic), [visual studio](https://visualstudiogallery.msdn.microsoft.com/6edc26d4-47d8-4987-82ee-7c820d79be1d), [vscode](https://marketplace.visualstudio.com/items?itemName=eg2.tslint), [webstorm](https://www.jetbrains.com/webstorm/help/tslint.html), and more Table of Contents @@ -115,7 +116,7 @@ Options: ``` -c, --config configuration file -e, --exclude exclude globs from path expansion ---fix Fixes linting errors for select rules. This may overwrite linted files +--fix fixes linting errors for select rules (his may overwrite linted files) --force return status code 0 even if there are lint errors -h, --help display detailed help -i, --init generate a tslint.json config file in the current working directory From 1c0f16fc7f286ad6b84e5189262f16d3bf126f1e Mon Sep 17 00:00:00 2001 From: Noah Chen Date: Tue, 22 Nov 2016 14:58:34 -0500 Subject: [PATCH 17/23] Update 2016-11-17-new-for-4.0.md --- docs/_posts/2016-11-17-new-for-4.0.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/_posts/2016-11-17-new-for-4.0.md b/docs/_posts/2016-11-17-new-for-4.0.md index b92a1284ecd..2bbf2404c95 100644 --- a/docs/_posts/2016-11-17-new-for-4.0.md +++ b/docs/_posts/2016-11-17-new-for-4.0.md @@ -16,7 +16,7 @@ TSLint 4.0 has been released! With this release comes a few exciting [changes][0 * [trailing-comma][8] -* **Linting `.js` files**. *A much-requested feature from our community*. Simplify your toolset by running the same rules you know and love on your .js and .jsx files. Just add a `jsRules` [section][9] to your `tslint.json` file, and TSLint will your JavaScript files. +* **Linting `.js` files**. *A much-requested feature from our community*. Simplify your toolset by running the same rules you know and love on your .js and .jsx files. Just add a `jsRules` [section][9] to your `tslint.json` file, and TSLint will lint your JavaScript files. * **TypeScript 2.0+ required**. This lets us deprecate/remove rules that are checked by the compiler. Problematic code that once violated these rules now cause compilation errors in `tsc`: * no-duplicate-key From d29f4840436dd1a499d0a112a344d42b104cc487 Mon Sep 17 00:00:00 2001 From: Noah Chen Date: Tue, 22 Nov 2016 16:17:35 -0500 Subject: [PATCH 18/23] Rewrite `prefer-for-of` rule (#1758) --- src/rules/preferForOfRule.ts | 197 +++++++++++++++++--------- test/rules/prefer-for-of/test.ts.lint | 120 +++++++++------- 2 files changed, 201 insertions(+), 116 deletions(-) diff --git a/src/rules/preferForOfRule.ts b/src/rules/preferForOfRule.ts index 51edb4eca1f..7142a298ded 100644 --- a/src/rules/preferForOfRule.ts +++ b/src/rules/preferForOfRule.ts @@ -35,89 +35,158 @@ export class Rule extends Lint.Rules.AbstractRule { public static FAILURE_STRING = "Expected a 'for-of' loop instead of a 'for' loop with this simple iteration"; public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { - const languageService = Lint.createLanguageService(sourceFile.fileName, sourceFile.getFullText()); - return this.applyWithWalker(new PreferForOfWalker(sourceFile, this.getOptions(), languageService)); + return this.applyWithWalker(new PreferForOfWalker(sourceFile, this.getOptions())); } } +interface IIncrementorState { + arrayToken: ts.LeftHandSideExpression; + endIncrementPos: number; + onlyArrayAccess: boolean; +} + class PreferForOfWalker extends Lint.RuleWalker { - constructor(sourceFile: ts.SourceFile, options: Lint.IOptions, private languageService: ts.LanguageService) { + // a map of incrementors and whether or not they are only used to index into an array reference in the for loop + private incrementorMap: { [name: string]: IIncrementorState }; + + constructor(sourceFile: ts.SourceFile, options: Lint.IOptions) { super(sourceFile, options); + this.incrementorMap = {}; } public visitForStatement(node: ts.ForStatement) { - const arrayAccessNode = this.locateArrayNodeInForLoop(node); - - if (arrayAccessNode !== undefined) { - // Skip arrays thats just loop over a hard coded number - // If we are accessing the length of the array, then we are likely looping over it's values - if (arrayAccessNode.kind === ts.SyntaxKind.PropertyAccessExpression && arrayAccessNode.getLastToken().getText() === "length") { - let incrementorVariable = node.incrementor.getFirstToken(); - if (/\+|-/g.test(incrementorVariable.getText())) { - // If it's formatted as `++i` instead, we need to get the OTHER token - incrementorVariable = node.incrementor.getLastToken(); - } - const arrayToken = arrayAccessNode.getChildAt(0); - const loopSyntaxText = node.statement.getText(); - // Find all usages of the incrementor variable - const fileName = this.getSourceFile().fileName; - const highlights = this.languageService.getDocumentHighlights(fileName, incrementorVariable.getStart(), [fileName]); - - if (highlights && highlights.length > 0) { - // There are *usually* three usages when setting up the for loop, - // so remove those from the count to get the count inside the loop block - const incrementorCount = highlights[0].highlightSpans.length - 3; - - // Find `array[i]`-like usages by building up a regex - const arrayTokenForRegex = arrayToken.getText().replace(".", "\\."); - const incrementorForRegex = incrementorVariable.getText().replace(".", "\\."); - const regex = new RegExp(`${arrayTokenForRegex}\\[\\s*${incrementorForRegex}\\s*\\]`, "g"); - const accessMatches = loopSyntaxText.match(regex); - const matchCount = (accessMatches || []).length; - - // If there are more usages of the array item being access than the incrementor variable - // being used, then this loop could be replaced with a for-of loop instead. - // This means that the incrementor variable is not used on its own anywhere and is ONLY - // used to access the array item. - if (matchCount >= incrementorCount) { - const failure = this.createFailure(node.getStart(), node.getWidth(), Rule.FAILURE_STRING); - this.addFailure(failure); + const arrayNodeInfo = this.getForLoopHeaderInfo(node); + let indexVariableName: string; + if (arrayNodeInfo != null) { + const { indexVariable, arrayToken } = arrayNodeInfo; + indexVariableName = indexVariable.getText(); + + // store `for` loop state + this.incrementorMap[indexVariableName] = { + arrayToken, + endIncrementPos: node.incrementor.end, + onlyArrayAccess: true, + }; + } + + super.visitForStatement(node); + + if (indexVariableName != null) { + const incrementorState = this.incrementorMap[indexVariableName]; + if (incrementorState.onlyArrayAccess) { + const length = incrementorState.endIncrementPos - node.getStart() + 1; + const failure = this.createFailure(node.getStart(), length, Rule.FAILURE_STRING); + this.addFailure(failure); + } + + // remove current `for` loop state + delete this.incrementorMap[indexVariableName]; + } + } + + public visitIdentifier(node: ts.Identifier) { + const incrementorState = this.incrementorMap[node.text]; + + // check if the identifier is an iterator and is currently in the `for` loop body + if (incrementorState != null && incrementorState.arrayToken != null && incrementorState.endIncrementPos < node.getStart()) { + // mark `onlyArrayAccess` false if iterator is used on anything except the array in the `for` loop header + if (node.parent.kind !== ts.SyntaxKind.ElementAccessExpression + || incrementorState.arrayToken.getText() !== ( node.parent).expression.getText()) { + + incrementorState.onlyArrayAccess = false; + } + } + super.visitIdentifier(node); + } + + // returns the iterator and array of a `for` loop if the `for` loop is basic. Otherwise, `null` + private getForLoopHeaderInfo(forLoop: ts.ForStatement) { + let indexVariableName: string; + let indexVariable: ts.Identifier; + + // assign `indexVariableName` if initializer is simple and starts at 0 + if (forLoop.initializer != null && forLoop.initializer.kind === ts.SyntaxKind.VariableDeclarationList) { + const syntaxList = forLoop.initializer.getChildAt(1); + if (syntaxList.kind === ts.SyntaxKind.SyntaxList && syntaxList.getChildCount() === 1) { + const assignment = syntaxList.getChildAt(0); + if (assignment.kind === ts.SyntaxKind.VariableDeclaration) { + const value = assignment.getChildAt(2).getText(); + if (value === "0") { + indexVariable = assignment.getChildAt(0); + indexVariableName = indexVariable.getText(); } } } } - super.visitForStatement(node); + // ensure `for` condition + if (indexVariableName == null + || forLoop.condition == null + || forLoop.condition.kind !== ts.SyntaxKind.BinaryExpression + || forLoop.condition.getChildAt(0).getText() !== indexVariableName + || forLoop.condition.getChildAt(1).getText() !== "<") { + + return null; + } + + if (!this.isIncremented(forLoop.incrementor, indexVariableName)) { + return null; + } + + // ensure that the condition checks a `length` property + const conditionRight = forLoop.condition.getChildAt(2); + if (conditionRight.kind === ts.SyntaxKind.PropertyAccessExpression) { + const propertyAccess = conditionRight; + if (propertyAccess.name.getText() === "length") { + return { indexVariable, arrayToken: propertyAccess.expression }; + } + } + + return null; } - private locateArrayNodeInForLoop(forLoop: ts.ForStatement): ts.Node { - // Some oddly formatted (yet still valid!) `for` loops might not have children in the condition - // See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for - if (forLoop.condition !== undefined) { - let arrayAccessNode = forLoop.condition.getChildAt(2); - // If We haven't found it, maybe it's not a standard for loop, try looking in the initializer for the array - // Something like `for(var t=0, len=arr.length; t < len; t++)` - if (arrayAccessNode.kind !== ts.SyntaxKind.PropertyAccessExpression && forLoop.initializer !== undefined) { - for (let initNode of forLoop.initializer.getChildren()) { - // look in `var t=0, len=arr.length;` - if (initNode.kind === ts.SyntaxKind.SyntaxList) { - for (let initVar of initNode.getChildren()) { - // look in `t=0, len=arr.length;` - if (initVar.kind === ts.SyntaxKind.VariableDeclaration) { - for (let initVarPart of initVar.getChildren()) { - // look in `len=arr.length` - if (initVarPart.kind === ts.SyntaxKind.PropertyAccessExpression) { - arrayAccessNode = initVarPart; - } - } - } - } + private isIncremented(node: ts.Node, indexVariableName: string) { + if (node == null) { + return false; + } + + // ensure variable is incremented + if (node.kind === ts.SyntaxKind.PrefixUnaryExpression) { + const incrementor = node; + if (incrementor.operator === ts.SyntaxKind.PlusPlusToken && incrementor.operand.getText() === indexVariableName) { + // x++ + return true; + } + } else if (node.kind === ts.SyntaxKind.PostfixUnaryExpression) { + const incrementor = node; + if (incrementor.operator === ts.SyntaxKind.PlusPlusToken && incrementor.operand.getText() === indexVariableName) { + // ++x + return true; + } + } else if (node.kind === ts.SyntaxKind.BinaryExpression) { + const binaryExpression = node; + if (binaryExpression.operatorToken.getText() === "+=" + && binaryExpression.left.getText() === indexVariableName + && binaryExpression.right.getText() === "1") { + // x += 1 + return true; + } + if (binaryExpression.operatorToken.getText() === "=" + && binaryExpression.left.getText() === indexVariableName) { + const addExpression = binaryExpression.right; + if (addExpression.operatorToken.getText() === "+") { + if (addExpression.right.getText() === indexVariableName && addExpression.left.getText() === "1") { + // x = 1 + x + return true; + } else if (addExpression.left.getText() === indexVariableName && addExpression.right.getText() === "1") { + // x = x + 1 + return true; } } } - return arrayAccessNode; } else { - return undefined; + return false; } + return false; } } diff --git a/test/rules/prefer-for-of/test.ts.lint b/test/rules/prefer-for-of/test.ts.lint index 0457f270347..ac67a99dacb 100644 --- a/test/rules/prefer-for-of/test.ts.lint +++ b/test/rules/prefer-for-of/test.ts.lint @@ -1,70 +1,46 @@ function sampleFunc() { - //This loop only uses the iterator to access the array item, so we can recommend a for-of loop here - for (var a = 0; a <= obj.arr.length; a++) { - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // This loop only uses the iterator to access the array item, so we can recommend a for-of loop here + for (var a = 0; a < obj.arr.length; a++) { + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [0] console.log(obj.arr[a]); -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ } -~~~~~ [0] - //Same as above, but no curly braces - for (var b = 0; b <= obj.arr.length; b++) console.log(obj.arr[b]); - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [0] + // Same as above, but no curly braces + for (var b = 0; b < obj.arr.length; b++) console.log(obj.arr[b]); + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [0] - //the index is used by itself, so a normal for loop is allowed here - for (var c = 0; c <= arr.length; c++) { + // the index is used by itself, so a normal for loop is allowed here + for (var c = 0; c < arr.length; c++) { doMath(c); } - //Same as above, but no curly braces - for (var d = 0; d <= arr.length; d++) doMath(d); + // Same as above, but no curly braces + for (var d = 0; d < arr.length; d++) doMath(d); - //the index is used by itself, so a normal for loop is allowed here - for (var e = 0; e <= arr.length; e++) { + // the index is used by itself, so a normal for loop is allowed here + for (var e = 0; e < arr.length; e++) { if(e > 5) { doMath(e); } console.log(arr[e]); } - //This iterates off of a hard-coded number and should be allowed + // This iterates off of a hard-coded number and should be allowed for (var f = 0; f <= 40; f++) { doMath(f); } - //Same as above, but no curly braces + // Same as above, but no curly braces for (var g = 0; g <= 40; g++) doMath(g); - //Loop set up different, but uses the index alone - this is ok - for(var h=0, len=arr.length; h < len; h++) { - doMath(h); - } - - //Same as above, but no curly braces - for(var i=0, len=arr.length; i < len; i++) doMath(i); - - //Loop set up different, but uses the index alone - this is ok - for(var j=0, len=arr.length; j < len; j++){ - if(j > 5) { - doMath(j); - } - console.log(arr[j]); - } + // multiple operations in the initializer + for(var h=0, len=arr.length; h < len; h++) {} - //Loop set up different, only uses the index to access the array - this should fail - for(var k=0, len=arr.length; k < len; k++) { - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - console.log(arr[k]); -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - } -~~~~~ [0] - - //Same as above, but no curly braces - for(var l=0, len=arr.length; l < len; l++) console.log(arr[l]); - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [0] + // Same as above, but no curly braces + for(var i=0, len=arr.length; i < len; i++) arr[i]; - //Odd for loop setups + // Odd for loop setups var m = 0; for (;;) { if (m > 3) break; @@ -79,28 +55,68 @@ function sampleFunc() { var o = 0; for (; o < arr.length; o++) { - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ console.log(arr[o]); -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ } -~~~~~ [0] - //Prefix incrementor + // Prefix incrementor for(let p = 0; p < arr.length; ++p) { - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [0] arr[p].whatever(); -~~~~~~~~~~~~~~~~~~~~~~~~~~ } -~~~~~ [0] - //For in loops ARE allowed + // empty + for(let x = 0; x < arr.length; x++) {} + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [0] + + // missing + for(; x < arr.length; x++) {} + for(let x = 0;; x++) {} + for(let x = 0; x < arr.length;) {} + + // mismatch + for(let x = 0; NOTX < arr.length; x++) {} + for(let x = 0; x < arr.length; NOTX++) {} + for(let NOTX = 0; x < arr.length; x++) {} + + // decrement + for(let x = 0; x < arr.length; x--) {} + + // not `<` + for(let x = 0; x <= arr.length; x++) {} + + // wrong starting point + for(let x = 1; x < arr.length; x++) {} + + // not `length` property + for(let x = 0; x < arr.length(); x++) {} + + // alternate incrementor + for(let x = 0; x < arr.length; x+=1) {} + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [0] + for(let x = 0; x < arr.length; x=x+1) {} + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [0] + for(let x = 0; x < arr.length; x=1+x) {} + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [0] + + // adds too much + for(let x = 0; x < arr.length; x+=11) {} + for(let x = 0; x < arr.length; x=x+11) {} + + for(let x = 0; x < arr.length; x++) { + x++; + } + + // unexpected condition + for(let x = 0; true; x++) {} + + // For-in loops ARE allowed for (var q in obj) { if (obj.hasOwnProperty(q)) { console.log(q); } } - //For of loops ARE allowed + // For-of loops ARE allowed for (var r of arr) { console.log(r); } From 716e1e72e155db1a01933c47660af698a4865b17 Mon Sep 17 00:00:00 2001 From: Noah Chen Date: Tue, 22 Nov 2016 16:36:02 -0500 Subject: [PATCH 19/23] fix `adjacent-overload-signatures` to treat static as non-overload (#1772) --- src/rules/adjacentOverloadSignaturesRule.ts | 30 +++++++++++++------ .../adjacent-overload-signatures/test.ts.lint | 6 ++++ 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/src/rules/adjacentOverloadSignaturesRule.ts b/src/rules/adjacentOverloadSignaturesRule.ts index e95c1f303ed..918aab31031 100644 --- a/src/rules/adjacentOverloadSignaturesRule.ts +++ b/src/rules/adjacentOverloadSignaturesRule.ts @@ -58,7 +58,9 @@ class AdjacentOverloadSignaturesWalker extends Lint.RuleWalker { } public visitInterfaceDeclaration(node: ts.InterfaceDeclaration): void { - this.checkOverloadsAdjacent(node.members, (member) => member.name && getTextOfPropertyName(member.name)); + this.checkOverloadsAdjacent(node.members, (member) => { + return getTextOfPropertyName(member); + }); super.visitInterfaceDeclaration(node); } @@ -84,7 +86,9 @@ class AdjacentOverloadSignaturesWalker extends Lint.RuleWalker { } private visitMembers(members: Array) { - this.checkOverloadsAdjacent(members, (member) => member.name && getTextOfPropertyName(member.name)); + this.checkOverloadsAdjacent(members, (member) => { + return getTextOfPropertyName(member); + }); } /** 'getOverloadName' may return undefined for nodes that cannot be overloads, e.g. a `const` declaration. */ @@ -109,19 +113,27 @@ function isLiteralExpression(node: ts.Node): node is ts.LiteralExpression { return node.kind === ts.SyntaxKind.StringLiteral || node.kind === ts.SyntaxKind.NumericLiteral; } -function getTextOfPropertyName(name: ts.PropertyName): string { - switch (name.kind) { +function getTextOfPropertyName(node: ts.InterfaceDeclaration | ts.TypeElement | ts.ClassElement): string { + let nameText: string; + if (node.name == null) { + return null; + } + switch (node.name.kind) { case ts.SyntaxKind.Identifier: - return (name as ts.Identifier).text; + nameText = (node.name as ts.Identifier).text; + break; case ts.SyntaxKind.ComputedPropertyName: - const { expression } = (name as ts.ComputedPropertyName); + const { expression } = (node.name as ts.ComputedPropertyName); if (isLiteralExpression(expression)) { - return expression.text; + nameText = expression.text; } break; default: - if (isLiteralExpression(name)) { - return name.text; + if (isLiteralExpression(node.name)) { + nameText = ( node.name).text; } } + + const suffix = Lint.hasModifier(node.modifiers, ts.SyntaxKind.StaticKeyword) ? " __static__" : ""; + return nameText + suffix; } diff --git a/test/rules/adjacent-overload-signatures/test.ts.lint b/test/rules/adjacent-overload-signatures/test.ts.lint index 9b65ef3d1b2..de6a26b2f28 100644 --- a/test/rules/adjacent-overload-signatures/test.ts.lint +++ b/test/rules/adjacent-overload-signatures/test.ts.lint @@ -111,3 +111,9 @@ declare namespace N { export function a(x: number): void; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [All 'a' signatures should be adjacent] } + +class Foo { + public static bar() {} + constructor() {} + public bar() {} +} \ No newline at end of file From 353156312a8bcf15738e9331673974e1ad90aa77 Mon Sep 17 00:00:00 2001 From: Noah Chen Date: Tue, 22 Nov 2016 16:36:21 -0500 Subject: [PATCH 20/23] Don't exit on missing rules (#1771) --- src/ruleLoader.ts | 23 +++++++++++++---------- test/ruleLoaderTests.ts | 40 ++++++---------------------------------- 2 files changed, 19 insertions(+), 44 deletions(-) diff --git a/src/ruleLoader.ts b/src/ruleLoader.ts index 5a4e79860c7..7c6cebb5047 100644 --- a/src/ruleLoader.ts +++ b/src/ruleLoader.ts @@ -21,6 +21,7 @@ import {camelize} from "underscore.string"; import {getRulesDirectories} from "./configuration"; import {IDisabledInterval, IRule} from "./language/rule/rule"; +import {dedent} from "./utils"; const moduleDirectory = path.dirname(module.filename); const CORE_RULES_DIRECTORY = path.resolve(moduleDirectory, ".", "rules"); @@ -65,26 +66,28 @@ export function loadRules(ruleConfiguration: {[name: string]: any}, } if (notFoundRules.length > 0) { - const ERROR_MESSAGE = ` + const warning = dedent` Could not find implementations for the following rules specified in the configuration: - ${notFoundRules.join("\n")} + ${notFoundRules.join("\n ")} Try upgrading TSLint and/or ensuring that you have all necessary custom rules installed. If TSLint was recently upgraded, you may have old rules configured which need to be cleaned up. `; - throw new Error(ERROR_MESSAGE); - } else if (notAllowedInJsRules.length > 0) { - const JS_ERROR_MESSAGE = ` + console.warn(warning); + } + if (notAllowedInJsRules.length > 0) { + const warning = dedent` Following rules specified in configuration couldn't be applied to .js or .jsx files: - ${notAllowedInJsRules.join("\n")} - + ${notAllowedInJsRules.join("\n ")} Make sure to exclude them from "jsRules" section of your tslint.json. `; - throw new Error(JS_ERROR_MESSAGE); - } else { - return rules; + console.warn(warning); + } + if (rules.length === 0) { + console.warn("No valid rules have been specified"); } + return rules; } export function findRule(name: string, rulesDirectories?: string | string[]) { diff --git a/test/ruleLoaderTests.ts b/test/ruleLoaderTests.ts index f9bb790b35e..21f784254f4 100644 --- a/test/ruleLoaderTests.ts +++ b/test/ruleLoaderTests.ts @@ -33,32 +33,15 @@ describe("Rule Loader", () => { assert.equal(rules.length, 5); }); - it("throws if an invalid rule is found", () => { + it("ignores invalid rules", () => { const invalidConfiguration: {[name: string]: any} = { - invalidConfig1: true, - invalidConfig2: false, - }; - - assert.throws( - () => loadRules(invalidConfiguration, {}, RULES_DIRECTORY), - /invalidConfig1\ninvalidConfig2/, - ); - }); - - it("doesn't ignore leading or trailing underscores or dashes", () => { - /* tslint:disable:object-literal-sort-keys */ - const invalidConfiguration: {[name: string]: any} = { - "_indent": 6, - "forin_": true, - "-quotemark": "single", - "eofline-": true, + "class-name": true, + "invalidConfig1": true, + "invalidConfig2": false, }; - /* tslint:enable:object-literal-sort-keys */ - assert.throws( - () => loadRules(invalidConfiguration, {}, RULES_DIRECTORY), - /_indent\nforin_\n-quotemark\neofline-/, - ); + const rules = loadRules(invalidConfiguration, {}, [RULES_DIRECTORY]); + assert.equal(rules.length, 1); }); it("works with rulesDirectory argument as an Array", () => { @@ -82,15 +65,4 @@ describe("Rule Loader", () => { const rules = loadRules(validConfiguration, {}, RULES_DIRECTORY, true); assert.equal(rules.length, 1); }); - - it("throws if an invalid rule is adopted", () => { - const invalidConfiguration: {[name: string]: any} = { - "array-type": [true, "array"], - }; - - assert.throws( - () => loadRules(invalidConfiguration, {}, RULES_DIRECTORY, true), - /array-type/, - ); - }); }); From 750f7c582d6444dd77e82c24af40eeb7d6268a6e Mon Sep 17 00:00:00 2001 From: Noah Chen Date: Wed, 23 Nov 2016 11:25:56 -0500 Subject: [PATCH 21/23] Prepare version 4.0.2 (#1778) --- CHANGELOG.md | 12 ++++++++++++ README.md | 2 +- package.json | 2 +- src/linter.ts | 2 +- src/tslint-cli.ts | 2 +- 5 files changed, 16 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ce21cbf741b..7cf7707f374 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,18 @@ Change Log === +v4.0.2 +--- +* [enhancement] Don't exit when a rule can't be found. Print as a warning instead (#1771) +* [bugfix] Don't flag a property named as empty string as not needing quotes in an object literal (#1762) +* [bugfix] Report correct number of fixes done by --fix (#1767) +* [bugfix] Allow 3rd party apps to see exception when the config is invalid (#1764) +* [bugfix] Fix false positives and exceptions in `prefer-for-of` (#1758) +* [bugfix] Fix `adjacent-overload-signatures` false positive when a static function has the same name (#1772) + +Thanks to our contributors! +* @gustavderdrache + v4.0.1 --- * [bugfix] Removed `no-unused-variable` rule from recommended config, as it was causing spurious deprecation warnings. diff --git a/README.md b/README.md index 35fc869fee5..662d9c150e9 100644 --- a/README.md +++ b/README.md @@ -116,7 +116,7 @@ Options: ``` -c, --config configuration file -e, --exclude exclude globs from path expansion ---fix fixes linting errors for select rules (his may overwrite linted files) +--fix fixes linting errors for select rules (this may overwrite linted files) --force return status code 0 even if there are lint errors -h, --help display detailed help -i, --init generate a tslint.json config file in the current working directory diff --git a/package.json b/package.json index 40ad22d92a6..0a5f8b5775a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tslint", - "version": "4.0.1", + "version": "4.0.2", "description": "An extensible static analysis linter for the TypeScript language", "bin": { "tslint": "./bin/tslint" diff --git a/src/linter.ts b/src/linter.ts index 3a0837d0e46..e522288f358 100644 --- a/src/linter.ts +++ b/src/linter.ts @@ -41,7 +41,7 @@ import { arrayify, dedent } from "./utils"; * Linter that can lint multiple files in consecutive runs. */ class Linter { - public static VERSION = "4.0.1"; + public static VERSION = "4.0.2"; public static findConfiguration = findConfiguration; public static findConfigurationPath = findConfigurationPath; diff --git a/src/tslint-cli.ts b/src/tslint-cli.ts index dc722d1f9e0..c8f2dc3e9a6 100644 --- a/src/tslint-cli.ts +++ b/src/tslint-cli.ts @@ -54,7 +54,7 @@ let processed = optimist type: "string", }, "fix": { - describe: "Fixes linting errors for select rules. This may overwrite linted files", + describe: "fixes linting errors for select rules (this may overwrite linted files)", type: "boolean", }, "force": { From 65ae6454c1706831c3637f46ba30dde4bb90fec7 Mon Sep 17 00:00:00 2001 From: Noah Chen Date: Wed, 23 Nov 2016 11:47:13 -0500 Subject: [PATCH 22/23] Prepare v4.0.0-dev.3 --- package.json | 2 +- src/linter.ts | 2 +- src/rules/noVarKeywordRule.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 8665ed97a30..c2771cfc3be 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tslint", - "version": "4.0.0-dev.2", + "version": "4.0.0-dev.3", "description": "An extensible static analysis linter for the TypeScript language", "bin": { "tslint": "./bin/tslint" diff --git a/src/linter.ts b/src/linter.ts index c98807cc8f0..39512e0659b 100644 --- a/src/linter.ts +++ b/src/linter.ts @@ -41,7 +41,7 @@ import { arrayify, dedent } from "./utils"; * Linter that can lint multiple files in consecutive runs. */ class Linter { - public static VERSION = "4.0.0-dev.2"; + public static VERSION = "4.0.0-dev.3"; public static findConfiguration = findConfiguration; public static findConfigurationPath = findConfigurationPath; diff --git a/src/rules/noVarKeywordRule.ts b/src/rules/noVarKeywordRule.ts index ae1bb2fa8e0..d022c85cf96 100644 --- a/src/rules/noVarKeywordRule.ts +++ b/src/rules/noVarKeywordRule.ts @@ -77,5 +77,5 @@ class NoVarKeywordWalker extends Lint.RuleWalker { private fix = (node: ts.Node) => new Lint.Fix(Rule.metadata.ruleName, [ this.deleteText(node.getStart(), "var".length), this.appendText(node.getStart(), "let"), - ]) + ]); } From 96e1bef7d712de8b833e5a112f9c986e3cec69e5 Mon Sep 17 00:00:00 2001 From: Noah Chen Date: Wed, 23 Nov 2016 11:51:51 -0500 Subject: [PATCH 23/23] Update CHANGELOG.md --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7cf7707f374..83bf2d6dca9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Change Log === +v4.0.0-dev.3 +--- +* Include latest v4.0.2 changes + v4.0.2 --- * [enhancement] Don't exit when a rule can't be found. Print as a warning instead (#1771)