From 3c902a1d98e75af4b9b59398dfb5c24b6d74ca96 Mon Sep 17 00:00:00 2001 From: Niles Date: Thu, 8 Aug 2019 10:40:04 -0500 Subject: [PATCH 01/28] fix(eslint-plugin): add `Literal` to `RuleListener` types (#824) --- packages/experimental-utils/src/ts-eslint/Rule.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/experimental-utils/src/ts-eslint/Rule.ts b/packages/experimental-utils/src/ts-eslint/Rule.ts index 0175309d752..89ac7b53288 100644 --- a/packages/experimental-utils/src/ts-eslint/Rule.ts +++ b/packages/experimental-utils/src/ts-eslint/Rule.ts @@ -270,6 +270,7 @@ interface RuleListener { JSXSpreadChild?: RuleFunction; JSXText?: RuleFunction; LabeledStatement?: RuleFunction; + Literal?: RuleFunction; LogicalExpression?: RuleFunction; MemberExpression?: RuleFunction; MetaProperty?: RuleFunction; From 42b3013ab846669fd730628f5cb0b043cfedabba Mon Sep 17 00:00:00 2001 From: James Henry Date: Fri, 9 Aug 2019 17:19:19 +0100 Subject: [PATCH 02/28] chore: misc package.json updates related to v2 (#832) --- package.json | 2 +- packages/eslint-plugin-tslint/package.json | 2 +- packages/eslint-plugin/package.json | 4 ++-- packages/experimental-utils/package.json | 2 +- packages/parser/package.json | 2 +- packages/typescript-estree/package.json | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index 98a6c27fc2d..d0c730ff1b4 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,7 @@ ] }, "engines": { - "node": ">=6.14.0" + "node": "^8.10.0 || ^10.13.0 || >=11.10.1" }, "devDependencies": { "@commitlint/cli": "^8.1.0", diff --git a/packages/eslint-plugin-tslint/package.json b/packages/eslint-plugin-tslint/package.json index bed7e2fd46f..bf40809b2db 100644 --- a/packages/eslint-plugin-tslint/package.json +++ b/packages/eslint-plugin-tslint/package.json @@ -11,7 +11,7 @@ "tslint" ], "engines": { - "node": "^6.14.0 || ^8.10.0 || >=9.10.0" + "node": "^8.10.0 || ^10.13.0 || >=11.10.1" }, "repository": { "type": "git", diff --git a/packages/eslint-plugin/package.json b/packages/eslint-plugin/package.json index 99183122281..516ee6b4f98 100644 --- a/packages/eslint-plugin/package.json +++ b/packages/eslint-plugin/package.json @@ -9,7 +9,7 @@ "typescript" ], "engines": { - "node": "^6.14.0 || ^8.10.0 || >=9.10.0" + "node": "^8.10.0 || ^10.13.0 || >=11.10.1" }, "files": [ "dist", @@ -54,7 +54,7 @@ "typescript": "*" }, "peerDependencies": { - "@typescript-eslint/parser": "^1.9.0", + "@typescript-eslint/parser": "^2.0.0-alpha.0", "eslint": "^5.0.0 || ^6.0.0" } } diff --git a/packages/experimental-utils/package.json b/packages/experimental-utils/package.json index 5cbedf3e6cb..4574f3129d1 100644 --- a/packages/experimental-utils/package.json +++ b/packages/experimental-utils/package.json @@ -8,7 +8,7 @@ "estree" ], "engines": { - "node": "^6.14.0 || ^8.10.0 || >=9.10.0" + "node": "^8.10.0 || ^10.13.0 || >=11.10.1" }, "files": [ "dist", diff --git a/packages/parser/package.json b/packages/parser/package.json index c11e9cb790a..bc3f3cf29b0 100644 --- a/packages/parser/package.json +++ b/packages/parser/package.json @@ -9,7 +9,7 @@ "LICENSE" ], "engines": { - "node": "^6.14.0 || ^8.10.0 || >=9.10.0" + "node": "^8.10.0 || ^10.13.0 || >=11.10.1" }, "repository": { "type": "git", diff --git a/packages/typescript-estree/package.json b/packages/typescript-estree/package.json index fabdc79810c..e0a0e661882 100644 --- a/packages/typescript-estree/package.json +++ b/packages/typescript-estree/package.json @@ -10,7 +10,7 @@ "LICENSE" ], "engines": { - "node": ">=6.14.0" + "node": "^8.10.0 || ^10.13.0 || >=11.10.1" }, "repository": { "type": "git", From ebbcc010c546b5777c14f0b33ead851b620184e0 Mon Sep 17 00:00:00 2001 From: Torleif Berger Date: Sat, 10 Aug 2019 02:32:16 +0200 Subject: [PATCH 03/28] fix(eslint-plugin): [efrt] flag default export w/allowExpressions (#831) BREAKING: the rule didn't previously flag default exports with this option --- .../rules/explicit-function-return-type.md | 20 +++++------ .../rules/explicit-function-return-type.ts | 3 +- .../explicit-function-return-type.test.ts | 33 +++++++++++++++++++ 3 files changed, 45 insertions(+), 11 deletions(-) diff --git a/packages/eslint-plugin/docs/rules/explicit-function-return-type.md b/packages/eslint-plugin/docs/rules/explicit-function-return-type.md index b1ac9af14ff..f31d6f42b8b 100644 --- a/packages/eslint-plugin/docs/rules/explicit-function-return-type.md +++ b/packages/eslint-plugin/docs/rules/explicit-function-return-type.md @@ -84,6 +84,10 @@ Examples of **incorrect** code for this rule with `{ allowExpressions: true }`: ```ts function test() {} + +const fn = () => {}; + +export default () => {}; ``` Examples of **correct** code for this rule with `{ allowExpressions: true }`: @@ -155,24 +159,20 @@ functionWithObjectArg({ Examples of **incorrect** code for this rule with `{ allowHigherOrderFunctions: true }`: ```ts -var arrowFn = (x: number) => (y: number) => x + y; +var arrowFn = () => () => {}; -function fn(x: number) { - return function(y: number) { - return x + y; - }; +function fn() { + return function() {}; } ``` Examples of **correct** code for this rule with `{ allowHigherOrderFunctions: true }`: ```ts -var arrowFn = (x: number) => (y: number): number => x + y; +var arrowFn = () => (): void => {}; -function fn(x: number) { - return function(y: number): number { - return x + y; - }; +function fn() { + return function(): void {}; } ``` diff --git a/packages/eslint-plugin/src/rules/explicit-function-return-type.ts b/packages/eslint-plugin/src/rules/explicit-function-return-type.ts index b431fe7675c..44ea2d03c8e 100644 --- a/packages/eslint-plugin/src/rules/explicit-function-return-type.ts +++ b/packages/eslint-plugin/src/rules/explicit-function-return-type.ts @@ -256,7 +256,8 @@ export default util.createRule({ if ( options.allowExpressions && node.parent.type !== AST_NODE_TYPES.VariableDeclarator && - node.parent.type !== AST_NODE_TYPES.MethodDefinition + node.parent.type !== AST_NODE_TYPES.MethodDefinition && + node.parent.type !== AST_NODE_TYPES.ExportDefaultDeclaration ) { return; } diff --git a/packages/eslint-plugin/tests/rules/explicit-function-return-type.test.ts b/packages/eslint-plugin/tests/rules/explicit-function-return-type.test.ts index 72d64d2abe0..32f23792591 100644 --- a/packages/eslint-plugin/tests/rules/explicit-function-return-type.test.ts +++ b/packages/eslint-plugin/tests/rules/explicit-function-return-type.test.ts @@ -90,6 +90,15 @@ class Test { }, ], }, + { + filename: 'test.ts', + code: `export default (): void => {}`, + options: [ + { + allowExpressions: true, + }, + ], + }, { filename: 'test.ts', code: ` @@ -417,6 +426,30 @@ function test() { }, ], }, + { + filename: 'test.ts', + code: 'export default () => {};', + options: [{ allowExpressions: true }], + errors: [ + { + messageId: 'missingReturnType', + line: 1, + column: 16, + }, + ], + }, + { + filename: 'test.ts', + code: 'export default function() {};', + options: [{ allowExpressions: true }], + errors: [ + { + messageId: 'missingReturnType', + line: 1, + column: 16, + }, + ], + }, { filename: 'test.ts', code: "var arrowFn = () => 'test';", From de6cc1d51a7b908ab2a731c5ce3c1d537062645f Mon Sep 17 00:00:00 2001 From: Ken <2770219+ken0x0a@users.noreply.github.com> Date: Tue, 13 Aug 2019 00:59:14 +0900 Subject: [PATCH 04/28] docs(eslint-plugin): [no-useless-constructor] add example setup (#837) --- .../eslint-plugin/docs/rules/no-useless-constructor.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/packages/eslint-plugin/docs/rules/no-useless-constructor.md b/packages/eslint-plugin/docs/rules/no-useless-constructor.md index 821f6a5d105..0aa1f4a5c98 100644 --- a/packages/eslint-plugin/docs/rules/no-useless-constructor.md +++ b/packages/eslint-plugin/docs/rules/no-useless-constructor.md @@ -71,6 +71,16 @@ class A extends B { } ``` +## Rule Changes + +```cjson +{ + // note you must disable the base rule as it can report incorrect errors + "no-useless-constructor": "off", + "@typescript-eslint/no-useless-constructor": "error", +} +``` + ## When Not To Use It If you don't want to be notified about unnecessary constructors, you can safely disable this rule. From 428567d7cc0985b1da754f092289212df3fe1bda Mon Sep 17 00:00:00 2001 From: Brad Zacher Date: Tue, 13 Aug 2019 02:50:38 -0700 Subject: [PATCH 05/28] feat(eslint-plugin)!: change recommended config (#729) BREAKING CHANGE: recommended config changes are considered breaking --- .eslintrc.js | 25 +++-------- .prettierrc => .prettierrc.json | 0 .../eslint-plugin-tslint/src/custom-linter.ts | 4 +- packages/eslint-plugin/README.md | 38 ++++++++-------- .../docs/rules/no-inferrable-types.md | 4 +- packages/eslint-plugin/package.json | 2 + packages/eslint-plugin/src/configs/all.json | 2 + packages/eslint-plugin/src/configs/base.json | 8 +--- .../src/configs/recommended.json | 32 +++++++++----- .../eslint-plugin/src/rules/array-type.ts | 7 +-- .../eslint-plugin/src/rules/await-thenable.ts | 4 +- .../eslint-plugin/src/rules/ban-ts-ignore.ts | 2 +- packages/eslint-plugin/src/rules/ban-types.ts | 5 ++- packages/eslint-plugin/src/rules/camelcase.ts | 2 +- .../src/rules/class-name-casing.ts | 4 +- .../src/rules/consistent-type-assertions.ts | 10 ++--- .../src/rules/consistent-type-definitions.ts | 5 ++- .../rules/explicit-function-return-type.ts | 4 +- .../rules/explicit-member-accessibility.ts | 9 ++-- .../src/rules/generic-type-naming.ts | 3 +- .../indent-new-do-not-use/OffsetStorage.ts | 12 +++-- .../src/rules/indent-new-do-not-use/index.ts | 15 +++---- packages/eslint-plugin/src/rules/indent.ts | 3 +- .../src/rules/interface-name-prefix.ts | 5 ++- .../src/rules/member-ordering.ts | 8 ++-- .../src/rules/no-empty-function.ts | 8 ++-- .../src/rules/no-empty-interface.ts | 2 +- .../src/rules/no-explicit-any.ts | 5 ++- .../src/rules/no-extra-parens.ts | 8 ++-- .../src/rules/no-extraneous-class.ts | 2 +- .../src/rules/no-floating-promises.ts | 2 +- .../src/rules/no-for-in-array.ts | 4 +- .../src/rules/no-inferrable-types.ts | 16 ++++--- .../src/rules/no-magic-numbers.ts | 2 +- .../eslint-plugin/src/rules/no-misused-new.ts | 8 ++-- .../src/rules/no-misused-promises.ts | 19 +++++--- .../eslint-plugin/src/rules/no-namespace.ts | 2 +- .../src/rules/no-non-null-assertion.ts | 4 +- .../src/rules/no-parameter-properties.ts | 5 ++- .../src/rules/no-require-imports.ts | 6 ++- .../eslint-plugin/src/rules/no-this-alias.ts | 6 +-- .../eslint-plugin/src/rules/no-type-alias.ts | 5 ++- .../src/rules/no-unnecessary-qualifier.ts | 2 +- .../rules/no-unnecessary-type-arguments.ts | 4 +- .../rules/no-unnecessary-type-assertion.ts | 4 +- .../src/rules/no-use-before-define.ts | 2 +- .../src/rules/no-useless-constructor.ts | 2 +- .../src/rules/no-var-requires.ts | 2 +- .../eslint-plugin/src/rules/prefer-for-of.ts | 2 +- .../src/rules/prefer-function-type.ts | 11 +++-- .../src/rules/prefer-includes.ts | 2 +- .../src/rules/prefer-namespace-keyword.ts | 2 +- .../src/rules/prefer-readonly.ts | 38 ++++++++-------- .../src/rules/prefer-regexp-exec.ts | 4 +- .../rules/prefer-string-starts-ends-with.ts | 2 +- .../src/rules/promise-function-async.ts | 12 +++-- .../src/rules/require-array-sort-compare.ts | 2 +- .../eslint-plugin/src/rules/require-await.ts | 8 ++-- .../src/rules/restrict-plus-operands.ts | 2 +- packages/eslint-plugin/src/rules/semi.ts | 3 +- .../src/rules/triple-slash-reference.ts | 10 ++--- .../src/rules/type-annotation-spacing.ts | 4 +- packages/eslint-plugin/src/rules/typedef.ts | 22 +++++----- .../eslint-plugin/src/rules/unbound-method.ts | 6 +-- .../src/rules/unified-signatures.ts | 21 ++++----- packages/eslint-plugin/src/util/misc.ts | 4 +- packages/eslint-plugin/src/util/types.ts | 7 ++- packages/eslint-plugin/tests/RuleTester.ts | 2 +- .../eslint-plugin/tests/rules/indent/utils.ts | 3 +- .../tests/rules/no-this-alias.test.ts | 10 +++++ .../tests/rules/no-unused-vars.test.ts | 8 ++-- .../eslint-plugin/tools/generate-configs.ts | 44 ++++++++++++++----- .../tools/validate-configs/checkConfigAll.ts | 4 +- .../checkConfigRecommended.ts | 4 +- .../validate-docs/validate-table-structure.ts | 3 +- .../src/eslint-utils/RuleCreator.ts | 2 +- .../src/eslint-utils/deepMerge.ts | 5 ++- packages/parser/src/analyze-scope.ts | 18 ++++---- packages/parser/src/parser.ts | 7 ++- packages/parser/src/scope/scope-manager.ts | 6 ++- packages/parser/src/simple-traverse.ts | 4 +- packages/parser/tests/lib/basics.ts | 2 +- packages/parser/tests/lib/jsx.ts | 2 +- packages/parser/tests/tools/scope-analysis.ts | 21 +++++---- packages/parser/tests/tools/test-utils.ts | 17 +++---- .../typescript-estree/src/ast-converter.ts | 5 ++- packages/typescript-estree/src/convert.ts | 17 ++++--- packages/typescript-estree/src/node-utils.ts | 11 ++++- packages/typescript-estree/src/parser.ts | 22 +++++++--- .../typescript-estree/src/tsconfig-parser.ts | 25 +++++++---- .../tests/ast-alignment/fixtures-to-test.ts | 4 +- .../tests/ast-alignment/parse.ts | 19 +++++--- .../tests/ast-alignment/utils.ts | 10 ++--- .../typescript-estree/tests/lib/convert.ts | 8 ++-- packages/typescript-estree/tests/lib/jsx.ts | 7 +-- .../tests/lib/semanticInfo.ts | 4 +- .../typescript-estree/tools/test-utils.ts | 10 ++--- .../utils/jest-snapshot-resolver.js | 1 + tools/generate-contributors.ts | 4 +- yarn.lock | 7 ++- 100 files changed, 470 insertions(+), 341 deletions(-) rename .prettierrc => .prettierrc.json (100%) diff --git a/.eslintrc.js b/.eslintrc.js index 45426c37acf..32a476969d3 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -21,23 +21,12 @@ module.exports = { // our plugin :D // - '@typescript-eslint/ban-ts-ignore': 'error', - '@typescript-eslint/consistent-type-definitions': 'error', - '@typescript-eslint/explicit-function-return-type': 'off', - '@typescript-eslint/explicit-member-accessibility': 'off', - '@typescript-eslint/indent': 'off', - '@typescript-eslint/no-explicit-any': 'warn', - '@typescript-eslint/no-inferrable-types': 'error', - '@typescript-eslint/no-misused-promises': 'error', + '@typescript-eslint/consistent-type-definitions': ['error', 'interface'], + '@typescript-eslint/no-explicit-any': 'error', '@typescript-eslint/no-non-null-assertion': 'off', - '@typescript-eslint/no-object-literal-type-assertion': 'off', - '@typescript-eslint/no-parameter-properties': 'off', - '@typescript-eslint/no-unnecessary-type-assertion': 'error', '@typescript-eslint/no-use-before-define': 'off', '@typescript-eslint/no-var-requires': 'off', - '@typescript-eslint/prefer-includes': 'error', - '@typescript-eslint/prefer-regexp-exec': 'error', - '@typescript-eslint/prefer-string-starts-ends-with': 'error', + '@typescript-eslint/unbound-method': 'off', // // eslint base @@ -110,12 +99,12 @@ module.exports = { 'import/no-mutable-exports': 'error', // Prevent importing the default as if it were named 'import/no-named-default': 'error', - // Prohibit named exports // we want everything to be a named export - 'import/no-named-export': 'off', + // Prohibit named exports + 'import/no-named-export': 'off', // we want everything to be a named export // Forbid a module from importing itself 'import/no-self-import': 'error', - // Require modules with a single export to use a default export // we want everything to be named - 'import/prefer-default-export': 'off', + // Require modules with a single export to use a default export + 'import/prefer-default-export': 'off', // we want everything to be named }, parserOptions: { sourceType: 'module', diff --git a/.prettierrc b/.prettierrc.json similarity index 100% rename from .prettierrc rename to .prettierrc.json diff --git a/packages/eslint-plugin-tslint/src/custom-linter.ts b/packages/eslint-plugin-tslint/src/custom-linter.ts index 5de914e5f8f..eb8527b99d7 100644 --- a/packages/eslint-plugin-tslint/src/custom-linter.ts +++ b/packages/eslint-plugin-tslint/src/custom-linter.ts @@ -1,5 +1,5 @@ import { ILinterOptions, Linter, LintResult } from 'tslint'; -import { Program } from 'typescript'; +import { Program, SourceFile } from 'typescript'; // We need to access the program, but Linter has private program already // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -14,7 +14,7 @@ export class CustomLinter extends TSLintLinter { return super.getResult(); } - getSourceFile(fileName: string) { + getSourceFile(fileName: string): SourceFile | undefined { return this.program.getSourceFile(fileName); } } diff --git a/packages/eslint-plugin/README.md b/packages/eslint-plugin/README.md index a6db1f9ec46..516f9c48dae 100644 --- a/packages/eslint-plugin/README.md +++ b/packages/eslint-plugin/README.md @@ -124,65 +124,65 @@ Then you should add `airbnb` (or `airbnb-base`) to your `extends` section of `.e | Name | Description | :heavy_check_mark: | :wrench: | :thought_balloon: | | --------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------ | -------- | ----------------- | | [`@typescript-eslint/adjacent-overload-signatures`](./docs/rules/adjacent-overload-signatures.md) | Require that member overloads be consecutive | :heavy_check_mark: | | | -| [`@typescript-eslint/array-type`](./docs/rules/array-type.md) | Requires using either `T[]` or `Array` for arrays | :heavy_check_mark: | :wrench: | | -| [`@typescript-eslint/await-thenable`](./docs/rules/await-thenable.md) | Disallows awaiting a value that is not a Thenable | | | :thought_balloon: | -| [`@typescript-eslint/ban-ts-ignore`](./docs/rules/ban-ts-ignore.md) | Bans “// @ts-ignore” comments from being used | | | | -| [`@typescript-eslint/ban-types`](./docs/rules/ban-types.md) | Bans specific types from being used | :heavy_check_mark: | :wrench: | | +| [`@typescript-eslint/array-type`](./docs/rules/array-type.md) | Requires using either `T[]` or `Array` for arrays | | :wrench: | | +| [`@typescript-eslint/await-thenable`](./docs/rules/await-thenable.md) | Disallows awaiting a value that is not a Thenable | :heavy_check_mark: | | :thought_balloon: | +| [`@typescript-eslint/ban-ts-ignore`](./docs/rules/ban-ts-ignore.md) | Bans “// @ts-ignore” comments from being used | :heavy_check_mark: | | | +| [`@typescript-eslint/ban-types`](./docs/rules/ban-types.md) | Bans specific types from being used | :heavy_check_mark: | :wrench: | | | [`@typescript-eslint/camelcase`](./docs/rules/camelcase.md) | Enforce camelCase naming convention | :heavy_check_mark: | | | | [`@typescript-eslint/class-name-casing`](./docs/rules/class-name-casing.md) | Require PascalCased class and interface names | :heavy_check_mark: | | | | [`@typescript-eslint/consistent-type-assertions`](./docs/rules/consistent-type-assertions.md) | Enforces consistent usage of type assertions. | :heavy_check_mark: | | | | [`@typescript-eslint/consistent-type-definitions`](./docs/rules/consistent-type-definitions.md) | Consistent with type definition either `interface` or `type` | | :wrench: | | | [`@typescript-eslint/explicit-function-return-type`](./docs/rules/explicit-function-return-type.md) | Require explicit return types on functions and class methods | :heavy_check_mark: | | | -| [`@typescript-eslint/explicit-member-accessibility`](./docs/rules/explicit-member-accessibility.md) | Require explicit accessibility modifiers on class properties and methods | :heavy_check_mark: | | | +| [`@typescript-eslint/explicit-member-accessibility`](./docs/rules/explicit-member-accessibility.md) | Require explicit accessibility modifiers on class properties and methods | | | | | [`@typescript-eslint/func-call-spacing`](./docs/rules/func-call-spacing.md) | Require or disallow spacing between function identifiers and their invocations | | :wrench: | | | [`@typescript-eslint/generic-type-naming`](./docs/rules/generic-type-naming.md) | Enforces naming of generic type variables | | | | -| [`@typescript-eslint/indent`](./docs/rules/indent.md) | Enforce consistent indentation | :heavy_check_mark: | :wrench: | | -| [`@typescript-eslint/interface-name-prefix`](./docs/rules/interface-name-prefix.md) | Require that interface names be prefixed with `I` | :heavy_check_mark: | | | +| [`@typescript-eslint/indent`](./docs/rules/indent.md) | Enforce consistent indentation | | :wrench: | | +| [`@typescript-eslint/interface-name-prefix`](./docs/rules/interface-name-prefix.md) | Require that interface names should or should not prefixed with `I` | :heavy_check_mark: | | | | [`@typescript-eslint/member-delimiter-style`](./docs/rules/member-delimiter-style.md) | Require a specific member delimiter style for interfaces and type literals | :heavy_check_mark: | :wrench: | | | [`@typescript-eslint/member-naming`](./docs/rules/member-naming.md) | Enforces naming conventions for class members by visibility | | | | | [`@typescript-eslint/member-ordering`](./docs/rules/member-ordering.md) | Require a consistent member declaration order | | | | | [`@typescript-eslint/no-array-constructor`](./docs/rules/no-array-constructor.md) | Disallow generic `Array` constructors | :heavy_check_mark: | :wrench: | | -| [`@typescript-eslint/no-empty-function`](./docs/rules/no-empty-function.md) | Disallow empty functions | | | | +| [`@typescript-eslint/no-empty-function`](./docs/rules/no-empty-function.md) | Disallow empty functions | :heavy_check_mark: | | | | [`@typescript-eslint/no-empty-interface`](./docs/rules/no-empty-interface.md) | Disallow the declaration of empty interfaces | :heavy_check_mark: | | | | [`@typescript-eslint/no-explicit-any`](./docs/rules/no-explicit-any.md) | Disallow usage of the `any` type | :heavy_check_mark: | :wrench: | | | [`@typescript-eslint/no-extra-parens`](./docs/rules/no-extra-parens.md) | Disallow unnecessary parentheses | | :wrench: | | | [`@typescript-eslint/no-extraneous-class`](./docs/rules/no-extraneous-class.md) | Forbids the use of classes as namespaces | | | | | [`@typescript-eslint/no-floating-promises`](./docs/rules/no-floating-promises.md) | Requires Promise-like values to be handled appropriately. | | | :thought_balloon: | -| [`@typescript-eslint/no-for-in-array`](./docs/rules/no-for-in-array.md) | Disallow iterating over an array with a for-in loop | | | :thought_balloon: | +| [`@typescript-eslint/no-for-in-array`](./docs/rules/no-for-in-array.md) | Disallow iterating over an array with a for-in loop | :heavy_check_mark: | | :thought_balloon: | | [`@typescript-eslint/no-inferrable-types`](./docs/rules/no-inferrable-types.md) | Disallows explicit type declarations for variables or parameters initialized to a number, string, or boolean | :heavy_check_mark: | :wrench: | | | [`@typescript-eslint/no-magic-numbers`](./docs/rules/no-magic-numbers.md) | Disallows magic numbers | | | | | [`@typescript-eslint/no-misused-new`](./docs/rules/no-misused-new.md) | Enforce valid definition of `new` and `constructor` | :heavy_check_mark: | | | -| [`@typescript-eslint/no-misused-promises`](./docs/rules/no-misused-promises.md) | Avoid using promises in places not designed to handle them | | | :thought_balloon: | +| [`@typescript-eslint/no-misused-promises`](./docs/rules/no-misused-promises.md) | Avoid using promises in places not designed to handle them | :heavy_check_mark: | | :thought_balloon: | | [`@typescript-eslint/no-namespace`](./docs/rules/no-namespace.md) | Disallow the use of custom TypeScript modules and namespaces | :heavy_check_mark: | | | | [`@typescript-eslint/no-non-null-assertion`](./docs/rules/no-non-null-assertion.md) | Disallows non-null assertions using the `!` postfix operator | :heavy_check_mark: | | | -| [`@typescript-eslint/no-parameter-properties`](./docs/rules/no-parameter-properties.md) | Disallow the use of parameter properties in class constructors | :heavy_check_mark: | | | +| [`@typescript-eslint/no-parameter-properties`](./docs/rules/no-parameter-properties.md) | Disallow the use of parameter properties in class constructors | | | | | [`@typescript-eslint/no-require-imports`](./docs/rules/no-require-imports.md) | Disallows invocation of `require()` | | | | -| [`@typescript-eslint/no-this-alias`](./docs/rules/no-this-alias.md) | Disallow aliasing `this` | | | | +| [`@typescript-eslint/no-this-alias`](./docs/rules/no-this-alias.md) | Disallow aliasing `this` | :heavy_check_mark: | | | | [`@typescript-eslint/no-type-alias`](./docs/rules/no-type-alias.md) | Disallow the use of type aliases | | | | | [`@typescript-eslint/no-unnecessary-qualifier`](./docs/rules/no-unnecessary-qualifier.md) | Warns when a namespace qualifier is unnecessary | | :wrench: | :thought_balloon: | | [`@typescript-eslint/no-unnecessary-type-arguments`](./docs/rules/no-unnecessary-type-arguments.md) | Warns if an explicitly specified type argument is the default for that type parameter | | :wrench: | :thought_balloon: | -| [`@typescript-eslint/no-unnecessary-type-assertion`](./docs/rules/no-unnecessary-type-assertion.md) | Warns if a type assertion does not change the type of an expression | | :wrench: | :thought_balloon: | +| [`@typescript-eslint/no-unnecessary-type-assertion`](./docs/rules/no-unnecessary-type-assertion.md) | Warns if a type assertion does not change the type of an expression | :heavy_check_mark: | :wrench: | :thought_balloon: | | [`@typescript-eslint/no-unused-vars`](./docs/rules/no-unused-vars.md) | Disallow unused variables | :heavy_check_mark: | | | | [`@typescript-eslint/no-use-before-define`](./docs/rules/no-use-before-define.md) | Disallow the use of variables before they are defined | :heavy_check_mark: | | | | [`@typescript-eslint/no-useless-constructor`](./docs/rules/no-useless-constructor.md) | Disallow unnecessary constructors | | | | | [`@typescript-eslint/no-var-requires`](./docs/rules/no-var-requires.md) | Disallows the use of require statements except in import statements | :heavy_check_mark: | | | | [`@typescript-eslint/prefer-for-of`](./docs/rules/prefer-for-of.md) | Prefer a ‘for-of’ loop over a standard ‘for’ loop if the index is only used to access the array being iterated | | | | | [`@typescript-eslint/prefer-function-type`](./docs/rules/prefer-function-type.md) | Use function types instead of interfaces with call signatures | | :wrench: | | -| [`@typescript-eslint/prefer-includes`](./docs/rules/prefer-includes.md) | Enforce `includes` method over `indexOf` method | | :wrench: | :thought_balloon: | +| [`@typescript-eslint/prefer-includes`](./docs/rules/prefer-includes.md) | Enforce `includes` method over `indexOf` method | :heavy_check_mark: | :wrench: | :thought_balloon: | | [`@typescript-eslint/prefer-namespace-keyword`](./docs/rules/prefer-namespace-keyword.md) | Require the use of the `namespace` keyword instead of the `module` keyword to declare custom TypeScript modules | :heavy_check_mark: | :wrench: | | | [`@typescript-eslint/prefer-readonly`](./docs/rules/prefer-readonly.md) | Requires that private members are marked as `readonly` if they're never modified outside of the constructor | | :wrench: | :thought_balloon: | -| [`@typescript-eslint/prefer-regexp-exec`](./docs/rules/prefer-regexp-exec.md) | Prefer RegExp#exec() over String#match() if no global flag is provided | | | :thought_balloon: | -| [`@typescript-eslint/prefer-string-starts-ends-with`](./docs/rules/prefer-string-starts-ends-with.md) | Enforce the use of `String#startsWith` and `String#endsWith` instead of other equivalent methods of checking substrings | | :wrench: | :thought_balloon: | +| [`@typescript-eslint/prefer-regexp-exec`](./docs/rules/prefer-regexp-exec.md) | Prefer RegExp#exec() over String#match() if no global flag is provided | :heavy_check_mark: | | :thought_balloon: | +| [`@typescript-eslint/prefer-string-starts-ends-with`](./docs/rules/prefer-string-starts-ends-with.md) | Enforce the use of `String#startsWith` and `String#endsWith` instead of other equivalent methods of checking substrings | :heavy_check_mark: | :wrench: | :thought_balloon: | | [`@typescript-eslint/promise-function-async`](./docs/rules/promise-function-async.md) | Requires any function or method that returns a Promise to be marked async | | | :thought_balloon: | | [`@typescript-eslint/require-array-sort-compare`](./docs/rules/require-array-sort-compare.md) | Enforce giving `compare` argument to `Array#sort` | | | :thought_balloon: | -| [`@typescript-eslint/require-await`](./docs/rules/require-await.md) | Disallow async functions which have no `await` expression | | | :thought_balloon: | +| [`@typescript-eslint/require-await`](./docs/rules/require-await.md) | Disallow async functions which have no `await` expression | :heavy_check_mark: | | :thought_balloon: | | [`@typescript-eslint/restrict-plus-operands`](./docs/rules/restrict-plus-operands.md) | When adding two variables, operands must both be of type number or of type string | | | :thought_balloon: | | [`@typescript-eslint/semi`](./docs/rules/semi.md) | Require or disallow semicolons instead of ASI | | :wrench: | | | [`@typescript-eslint/strict-boolean-expressions`](./docs/rules/strict-boolean-expressions.md) | Restricts the types allowed in boolean expressions | | | :thought_balloon: | -| [`@typescript-eslint/triple-slash-reference`](./docs/rules/triple-slash-reference.md) | Sets preference level for triple slash directives versus ES6-style import declarations | | | | +| [`@typescript-eslint/triple-slash-reference`](./docs/rules/triple-slash-reference.md) | Sets preference level for triple slash directives versus ES6-style import declarations | :heavy_check_mark: | | | | [`@typescript-eslint/type-annotation-spacing`](./docs/rules/type-annotation-spacing.md) | Require consistent spacing around type annotations | :heavy_check_mark: | :wrench: | | | [`@typescript-eslint/typedef`](./docs/rules/typedef.md) | Requires type annotations to exist | | | | -| [`@typescript-eslint/unbound-method`](./docs/rules/unbound-method.md) | Enforces unbound methods are called with their expected scope | | | :thought_balloon: | +| [`@typescript-eslint/unbound-method`](./docs/rules/unbound-method.md) | Enforces unbound methods are called with their expected scope | :heavy_check_mark: | | :thought_balloon: | | [`@typescript-eslint/unified-signatures`](./docs/rules/unified-signatures.md) | Warns for any two overloads that could be unified into one by using a union or an optional/rest parameter | | | | diff --git a/packages/eslint-plugin/docs/rules/no-inferrable-types.md b/packages/eslint-plugin/docs/rules/no-inferrable-types.md index 9dbabe276c3..7dd159b02e5 100644 --- a/packages/eslint-plugin/docs/rules/no-inferrable-types.md +++ b/packages/eslint-plugin/docs/rules/no-inferrable-types.md @@ -24,8 +24,8 @@ The default options are: ```JSON { - "ignoreParameters": true, - "ignoreProperties": true, + "ignoreParameters": false, + "ignoreProperties": false, } ``` diff --git a/packages/eslint-plugin/package.json b/packages/eslint-plugin/package.json index 516ee6b4f98..14b417ac5c7 100644 --- a/packages/eslint-plugin/package.json +++ b/packages/eslint-plugin/package.json @@ -49,8 +49,10 @@ "devDependencies": { "@types/json-schema": "^7.0.3", "@types/marked": "^0.6.5", + "@types/prettier": "^1.18.0", "chalk": "^2.4.2", "marked": "^0.7.0", + "prettier": "*", "typescript": "*" }, "peerDependencies": { diff --git a/packages/eslint-plugin/src/configs/all.json b/packages/eslint-plugin/src/configs/all.json index 0c17ccb0426..30a7cafafb5 100644 --- a/packages/eslint-plugin/src/configs/all.json +++ b/packages/eslint-plugin/src/configs/all.json @@ -24,6 +24,7 @@ "@typescript-eslint/member-ordering": "error", "no-array-constructor": "off", "@typescript-eslint/no-array-constructor": "error", + "no-empty-function": "off", "@typescript-eslint/no-empty-function": "error", "@typescript-eslint/no-empty-interface": "error", "@typescript-eslint/no-explicit-any": "error", @@ -62,6 +63,7 @@ "@typescript-eslint/prefer-string-starts-ends-with": "error", "@typescript-eslint/promise-function-async": "error", "@typescript-eslint/require-array-sort-compare": "error", + "require-await": "off", "@typescript-eslint/require-await": "error", "@typescript-eslint/restrict-plus-operands": "error", "semi": "off", diff --git a/packages/eslint-plugin/src/configs/base.json b/packages/eslint-plugin/src/configs/base.json index 6f56100a6ae..9580ed622cf 100644 --- a/packages/eslint-plugin/src/configs/base.json +++ b/packages/eslint-plugin/src/configs/base.json @@ -1,9 +1,5 @@ { "parser": "@typescript-eslint/parser", - "parserOptions": { - "sourceType": "module" - }, - "plugins": [ - "@typescript-eslint" - ] + "parserOptions": { "sourceType": "module" }, + "plugins": ["@typescript-eslint"] } diff --git a/packages/eslint-plugin/src/configs/recommended.json b/packages/eslint-plugin/src/configs/recommended.json index 121b85bf17a..95d18847686 100644 --- a/packages/eslint-plugin/src/configs/recommended.json +++ b/packages/eslint-plugin/src/configs/recommended.json @@ -1,38 +1,48 @@ { "extends": "./configs/base.json", "rules": { - "no-var": "error", - "prefer-const": "error", - "prefer-rest-params": "error", - "prefer-spread": "error", "@typescript-eslint/adjacent-overload-signatures": "error", - "@typescript-eslint/array-type": "error", + "@typescript-eslint/await-thenable": "error", + "@typescript-eslint/ban-ts-ignore": "error", "@typescript-eslint/ban-types": "error", "camelcase": "off", "@typescript-eslint/camelcase": "error", "@typescript-eslint/class-name-casing": "error", "@typescript-eslint/consistent-type-assertions": "error", "@typescript-eslint/explicit-function-return-type": "warn", - "@typescript-eslint/explicit-member-accessibility": "error", - "indent": "off", - "@typescript-eslint/indent": "error", "@typescript-eslint/interface-name-prefix": "error", "@typescript-eslint/member-delimiter-style": "error", "no-array-constructor": "off", "@typescript-eslint/no-array-constructor": "error", + "no-empty-function": "off", + "@typescript-eslint/no-empty-function": "error", "@typescript-eslint/no-empty-interface": "error", "@typescript-eslint/no-explicit-any": "warn", + "@typescript-eslint/no-for-in-array": "error", "@typescript-eslint/no-inferrable-types": "error", "@typescript-eslint/no-misused-new": "error", + "@typescript-eslint/no-misused-promises": "error", "@typescript-eslint/no-namespace": "error", - "@typescript-eslint/no-non-null-assertion": "error", - "@typescript-eslint/no-parameter-properties": "error", + "@typescript-eslint/no-non-null-assertion": "warn", + "@typescript-eslint/no-this-alias": "error", + "@typescript-eslint/no-unnecessary-type-assertion": "error", "no-unused-vars": "off", "@typescript-eslint/no-unused-vars": "warn", "no-use-before-define": "off", "@typescript-eslint/no-use-before-define": "error", "@typescript-eslint/no-var-requires": "error", + "@typescript-eslint/prefer-includes": "error", "@typescript-eslint/prefer-namespace-keyword": "error", - "@typescript-eslint/type-annotation-spacing": "error" + "@typescript-eslint/prefer-regexp-exec": "error", + "@typescript-eslint/prefer-string-starts-ends-with": "error", + "require-await": "off", + "@typescript-eslint/require-await": "error", + "@typescript-eslint/triple-slash-reference": "error", + "@typescript-eslint/type-annotation-spacing": "error", + "@typescript-eslint/unbound-method": "error", + "no-var": "error", + "prefer-const": "error", + "prefer-rest-params": "error", + "prefer-spread": "error" } } diff --git a/packages/eslint-plugin/src/rules/array-type.ts b/packages/eslint-plugin/src/rules/array-type.ts index 8fe28bc3217..b9977618c40 100644 --- a/packages/eslint-plugin/src/rules/array-type.ts +++ b/packages/eslint-plugin/src/rules/array-type.ts @@ -94,7 +94,8 @@ export default util.createRule({ docs: { description: 'Requires using either `T[]` or `Array` for arrays', category: 'Stylistic Issues', - recommended: 'error', + // too opinionated to be recommended + recommended: false, }, fixable: 'code', messages: { @@ -168,7 +169,7 @@ export default util.createRule({ } return { - TSArrayType(node: TSESTree.TSArrayType) { + TSArrayType(node): void { if ( isArrayOption || (isArraySimpleOption && isSimpleType(node.elementType)) @@ -241,7 +242,7 @@ export default util.createRule({ }); }, - TSTypeReference(node: TSESTree.TSTypeReference) { + TSTypeReference(node): void { if ( isGenericOption || node.typeName.type !== AST_NODE_TYPES.Identifier diff --git a/packages/eslint-plugin/src/rules/await-thenable.ts b/packages/eslint-plugin/src/rules/await-thenable.ts index 7a5db98db9c..304321001f7 100644 --- a/packages/eslint-plugin/src/rules/await-thenable.ts +++ b/packages/eslint-plugin/src/rules/await-thenable.ts @@ -9,7 +9,7 @@ export default util.createRule({ docs: { description: 'Disallows awaiting a value that is not a Thenable', category: 'Best Practices', - recommended: false, + recommended: 'error', }, messages: { await: 'Unexpected `await` of a non-Promise (non-"Thenable") value.', @@ -24,7 +24,7 @@ export default util.createRule({ const checker = parserServices.program.getTypeChecker(); return { - AwaitExpression(node) { + AwaitExpression(node): void { const originalNode = parserServices.esTreeNodeToTSNodeMap.get< ts.AwaitExpression >(node); diff --git a/packages/eslint-plugin/src/rules/ban-ts-ignore.ts b/packages/eslint-plugin/src/rules/ban-ts-ignore.ts index 87af895627d..7d7a32e6cfc 100644 --- a/packages/eslint-plugin/src/rules/ban-ts-ignore.ts +++ b/packages/eslint-plugin/src/rules/ban-ts-ignore.ts @@ -7,7 +7,7 @@ export default util.createRule({ docs: { description: 'Bans “// @ts-ignore” comments from being used', category: 'Best Practices', - recommended: false, + recommended: 'error', }, schema: [], messages: { diff --git a/packages/eslint-plugin/src/rules/ban-types.ts b/packages/eslint-plugin/src/rules/ban-types.ts index bd3d8bf1b93..a8c2cec6c06 100644 --- a/packages/eslint-plugin/src/rules/ban-types.ts +++ b/packages/eslint-plugin/src/rules/ban-types.ts @@ -25,7 +25,7 @@ function stringifyTypeName( function getCustomMessage( bannedType: null | string | { message?: string; fixWith?: string }, -) { +): string { if (bannedType === null) { return ''; } @@ -108,7 +108,7 @@ export default util.createRule({ ], create(context, [{ types: bannedTypes }]) { return { - TSTypeReference({ typeName }) { + TSTypeReference({ typeName }): void { const name = stringifyTypeName(typeName, context.getSourceCode()); if (name in bannedTypes) { @@ -124,6 +124,7 @@ export default util.createRule({ name: name, customMessage, }, + // eslint-disable-next-line @typescript-eslint/explicit-function-return-type fix: fixWith ? fixer => fixer.replaceText(typeName, fixWith) : null, }); } diff --git a/packages/eslint-plugin/src/rules/camelcase.ts b/packages/eslint-plugin/src/rules/camelcase.ts index ffa5a62ed89..4bb614cba9d 100644 --- a/packages/eslint-plugin/src/rules/camelcase.ts +++ b/packages/eslint-plugin/src/rules/camelcase.ts @@ -90,7 +90,7 @@ export default util.createRule({ } return { - Identifier(node) { + Identifier(node): void { /* * Leading and trailing underscores are commonly used to flag * private/protected identifiers, strip them diff --git a/packages/eslint-plugin/src/rules/class-name-casing.ts b/packages/eslint-plugin/src/rules/class-name-casing.ts index 1c76edd118b..cb238f87ac7 100644 --- a/packages/eslint-plugin/src/rules/class-name-casing.ts +++ b/packages/eslint-plugin/src/rules/class-name-casing.ts @@ -64,7 +64,7 @@ export default util.createRule({ | TSESTree.ClassDeclaration | TSESTree.TSInterfaceDeclaration | TSESTree.ClassExpression, - ) { + ): void { // class expressions (i.e. export default class {}) are OK if (node.id && !isPascalCase(node.id.name)) { report(node, node.id); @@ -72,7 +72,7 @@ export default util.createRule({ }, "VariableDeclarator[init.type='ClassExpression']"( node: TSESTree.VariableDeclarator, - ) { + ): void { if ( node.id.type === AST_NODE_TYPES.ArrayPattern || node.id.type === AST_NODE_TYPES.ObjectPattern diff --git a/packages/eslint-plugin/src/rules/consistent-type-assertions.ts b/packages/eslint-plugin/src/rules/consistent-type-assertions.ts index 6380f9f0003..a5f87250da0 100644 --- a/packages/eslint-plugin/src/rules/consistent-type-assertions.ts +++ b/packages/eslint-plugin/src/rules/consistent-type-assertions.ts @@ -77,7 +77,7 @@ export default util.createRule({ function reportIncorrectAssertionType( node: TSESTree.TSTypeAssertion | TSESTree.TSAsExpression, - ) { + ): void { const messageId = options.assertionStyle; context.report({ node, @@ -89,7 +89,7 @@ export default util.createRule({ }); } - function checkType(node: TSESTree.TypeNode) { + function checkType(node: TSESTree.TypeNode): boolean { switch (node.type) { case AST_NODE_TYPES.TSAnyKeyword: case AST_NODE_TYPES.TSUnknownKeyword: @@ -107,7 +107,7 @@ export default util.createRule({ function checkExpression( node: TSESTree.TSTypeAssertion | TSESTree.TSAsExpression, - ) { + ): void { if ( options.assertionStyle === 'never' || options.objectLiteralTypeAssertions === 'allow' || @@ -137,7 +137,7 @@ export default util.createRule({ } return { - TSTypeAssertion(node) { + TSTypeAssertion(node): void { if (options.assertionStyle !== 'angle-bracket') { reportIncorrectAssertionType(node); return; @@ -145,7 +145,7 @@ export default util.createRule({ checkExpression(node); }, - TSAsExpression(node) { + TSAsExpression(node): void { if (options.assertionStyle !== 'as') { reportIncorrectAssertionType(node); return; diff --git a/packages/eslint-plugin/src/rules/consistent-type-definitions.ts b/packages/eslint-plugin/src/rules/consistent-type-definitions.ts index 01fa92dd3d0..cf9e851c81c 100644 --- a/packages/eslint-plugin/src/rules/consistent-type-definitions.ts +++ b/packages/eslint-plugin/src/rules/consistent-type-definitions.ts @@ -9,6 +9,7 @@ export default util.createRule({ description: 'Consistent with type definition either `interface` or `type`', category: 'Stylistic Issues', + // too opinionated to be recommended recommended: false, }, messages: { @@ -29,7 +30,7 @@ export default util.createRule({ return { "TSTypeAliasDeclaration[typeAnnotation.type='TSTypeLiteral']"( node: TSESTree.TSTypeAliasDeclaration, - ) { + ): void { if (option === 'interface') { context.report({ node: node.id, @@ -63,7 +64,7 @@ export default util.createRule({ }); } }, - TSInterfaceDeclaration(node) { + TSInterfaceDeclaration(node): void { if (option === 'type') { context.report({ node: node.id, diff --git a/packages/eslint-plugin/src/rules/explicit-function-return-type.ts b/packages/eslint-plugin/src/rules/explicit-function-return-type.ts index 44ea2d03c8e..67692c20fc0 100644 --- a/packages/eslint-plugin/src/rules/explicit-function-return-type.ts +++ b/packages/eslint-plugin/src/rules/explicit-function-return-type.ts @@ -47,8 +47,8 @@ export default util.createRule({ defaultOptions: [ { allowExpressions: false, - allowTypedFunctionExpressions: false, - allowHigherOrderFunctions: false, + allowTypedFunctionExpressions: true, + allowHigherOrderFunctions: true, }, ], create(context, [options]) { diff --git a/packages/eslint-plugin/src/rules/explicit-member-accessibility.ts b/packages/eslint-plugin/src/rules/explicit-member-accessibility.ts index 8d2b590ffad..21a5a8cda18 100644 --- a/packages/eslint-plugin/src/rules/explicit-member-accessibility.ts +++ b/packages/eslint-plugin/src/rules/explicit-member-accessibility.ts @@ -33,8 +33,9 @@ export default util.createRule({ docs: { description: 'Require explicit accessibility modifiers on class properties and methods', - category: 'Best Practices', - recommended: 'error', + category: 'Stylistic Issues', + // too opinionated to be recommended + recommended: false, }, messages: { missingAccessibility: @@ -82,7 +83,7 @@ export default util.createRule({ nodeType: string, node: TSESTree.Node, nodeName: string, - ) { + ): void { context.report({ node: node, messageId: messageId, @@ -179,7 +180,7 @@ export default util.createRule({ */ function checkParameterPropertyAccessibilityModifier( node: TSESTree.TSParameterProperty, - ) { + ): void { const nodeType = 'parameter property'; // HAS to be an identifier or assignment or TSC will throw if ( diff --git a/packages/eslint-plugin/src/rules/generic-type-naming.ts b/packages/eslint-plugin/src/rules/generic-type-naming.ts index 784a2fc3b0c..95516174448 100644 --- a/packages/eslint-plugin/src/rules/generic-type-naming.ts +++ b/packages/eslint-plugin/src/rules/generic-type-naming.ts @@ -10,6 +10,7 @@ export default util.createRule({ docs: { description: 'Enforces naming of generic type variables', category: 'Stylistic Issues', + // too opinionated to be recommended recommended: false, }, messages: { @@ -30,7 +31,7 @@ export default util.createRule({ const regex = new RegExp(rule!); return { - TSTypeParameter(node) { + TSTypeParameter(node): void { const name = node.name.name; if (name && !regex.test(name)) { diff --git a/packages/eslint-plugin/src/rules/indent-new-do-not-use/OffsetStorage.ts b/packages/eslint-plugin/src/rules/indent-new-do-not-use/OffsetStorage.ts index 475e3c35d7b..082ca05129d 100644 --- a/packages/eslint-plugin/src/rules/indent-new-do-not-use/OffsetStorage.ts +++ b/packages/eslint-plugin/src/rules/indent-new-do-not-use/OffsetStorage.ts @@ -2,7 +2,11 @@ // License: https://github.com/eslint/eslint/blob/48700fc8408f394887cdedd071b22b757700fdcb/LICENSE import { TSESTree } from '@typescript-eslint/experimental-utils'; -import { BinarySearchTree, TokenOrComment } from './BinarySearchTree'; +import { + BinarySearchTree, + TokenOrComment, + TreeValue, +} from './BinarySearchTree'; import { TokenInfo } from './TokenInfo'; /** @@ -34,7 +38,7 @@ export class OffsetStorage { this.ignoredTokens = new WeakSet(); } - private getOffsetDescriptor(token: TokenOrComment) { + private getOffsetDescriptor(token: TokenOrComment): TreeValue { return this.tree.findLe(token.range[0]).value; } @@ -151,8 +155,8 @@ export class OffsetStorage { public setDesiredOffsets( range: [number, number], fromToken: TokenOrComment | null, - offset: number = 0, - force: boolean = false, + offset = 0, + force = false, ): void { /* * Offset ranges are stored as a collection of nodes, where each node maps a numeric key to an offset diff --git a/packages/eslint-plugin/src/rules/indent-new-do-not-use/index.ts b/packages/eslint-plugin/src/rules/indent-new-do-not-use/index.ts index 3549da45d74..56909f15fa7 100644 --- a/packages/eslint-plugin/src/rules/indent-new-do-not-use/index.ts +++ b/packages/eslint-plugin/src/rules/indent-new-do-not-use/index.ts @@ -25,15 +25,13 @@ import { OffsetStorage } from './OffsetStorage'; import { TokenInfo } from './TokenInfo'; import { createRule, ExcludeKeys, RequireKeys } from '../../util'; -function createGlobalLinebreakMatcher() { - return /\r\n|[\r\n\u2028\u2029]/gu; -} +const GLOBAL_LINEBREAK_REGEX = /\r\n|[\r\n\u2028\u2029]/gu; +const WHITESPACE_REGEX = /\s*$/u; //------------------------------------------------------------------------------ // Rule Definition //------------------------------------------------------------------------------ -const WHITESPACE_REGEX = /\s*$/u; const KNOWN_NODES = new Set([ AST_NODE_TYPES.AssignmentExpression, AST_NODE_TYPES.AssignmentPattern, @@ -440,7 +438,7 @@ export default createRule({ expectedAmount: number, actualSpaces: number, actualTabs: number, - ) { + ): { expected: string; actual: string | number } { const expectedStatement = `${expectedAmount} ${indentType}${ expectedAmount === 1 ? '' : 's' }`; // e.g. "2 tabs" @@ -568,9 +566,7 @@ export default createRule({ */ function countTrailingLinebreaks(str: string): number { const trailingWhitespace = WHITESPACE_REGEX.exec(str)![0]; - const linebreakMatches = createGlobalLinebreakMatcher().exec( - trailingWhitespace, - ); + const linebreakMatches = GLOBAL_LINEBREAK_REGEX.exec(trailingWhitespace); return linebreakMatches === null ? 0 : linebreakMatches.length; } @@ -587,7 +583,7 @@ export default createRule({ startToken: TSESTree.Token, endToken: TSESTree.Token, offset: number | string, - ) { + ): void { /** * Gets the first token of a given element, including surrounding parentheses. * @param element A node in the `elements` list @@ -1589,6 +1585,7 @@ export default createRule({ const listener = baseOffsetListeners[key] as TSESLint.RuleFunction< TSESTree.Node >; + // eslint-disable-next-line @typescript-eslint/explicit-function-return-type acc[key] = node => listenerCallQueue.push({ listener, node }); return acc; diff --git a/packages/eslint-plugin/src/rules/indent.ts b/packages/eslint-plugin/src/rules/indent.ts index 76d1edf1cdb..aca492d4b4d 100644 --- a/packages/eslint-plugin/src/rules/indent.ts +++ b/packages/eslint-plugin/src/rules/indent.ts @@ -90,7 +90,8 @@ export default util.createRule({ docs: { description: 'Enforce consistent indentation', category: 'Stylistic Issues', - recommended: 'error', + // too opinionated to be recommended + recommended: false, }, fixable: 'whitespace', schema: baseRule.meta.schema, diff --git a/packages/eslint-plugin/src/rules/interface-name-prefix.ts b/packages/eslint-plugin/src/rules/interface-name-prefix.ts index 1af78f79a53..11976552221 100644 --- a/packages/eslint-plugin/src/rules/interface-name-prefix.ts +++ b/packages/eslint-plugin/src/rules/interface-name-prefix.ts @@ -8,8 +8,11 @@ export default util.createRule({ meta: { type: 'suggestion', docs: { - description: 'Require that interface names be prefixed with `I`', + description: + 'Require that interface names should or should not prefixed with `I`', category: 'Stylistic Issues', + // this will always be recommended as there's no reason to use this convention + // https://github.com/typescript-eslint/typescript-eslint/issues/374 recommended: 'error', }, messages: { diff --git a/packages/eslint-plugin/src/rules/member-ordering.ts b/packages/eslint-plugin/src/rules/member-ordering.ts index d2becd1655f..72020afc600 100644 --- a/packages/eslint-plugin/src/rules/member-ordering.ts +++ b/packages/eslint-plugin/src/rules/member-ordering.ts @@ -364,28 +364,28 @@ export default util.createRule({ } return { - ClassDeclaration(node) { + ClassDeclaration(node): void { validateMembersOrder( node.body.body, options.classes || options.default!, true, ); }, - ClassExpression(node) { + ClassExpression(node): void { validateMembersOrder( node.body.body, options.classExpressions || options.default!, true, ); }, - TSInterfaceDeclaration(node) { + TSInterfaceDeclaration(node): void { validateMembersOrder( node.body.body, options.interfaces || options.default!, false, ); }, - TSTypeLiteral(node) { + TSTypeLiteral(node): void { validateMembersOrder( node.members, options.typeLiterals || options.default!, diff --git a/packages/eslint-plugin/src/rules/no-empty-function.ts b/packages/eslint-plugin/src/rules/no-empty-function.ts index d64568a874f..31aa5d8bb63 100644 --- a/packages/eslint-plugin/src/rules/no-empty-function.ts +++ b/packages/eslint-plugin/src/rules/no-empty-function.ts @@ -15,7 +15,7 @@ export default util.createRule({ docs: { description: 'Disallow empty functions', category: 'Best Practices', - recommended: false, + recommended: 'error', }, schema: baseRule.meta.schema, messages: baseRule.meta.messages, @@ -81,7 +81,7 @@ export default util.createRule({ */ function isConciseConstructor( node: TSESTree.FunctionDeclaration | TSESTree.FunctionExpression, - ) { + ): boolean { // Check TypeScript specific nodes return ( isConstructor(node) && isBodyEmpty(node) && hasParameterProperties(node) @@ -89,12 +89,12 @@ export default util.createRule({ } return { - FunctionDeclaration(node: TSESTree.FunctionDeclaration) { + FunctionDeclaration(node): void { if (!isConciseConstructor(node)) { rules.FunctionDeclaration(node); } }, - FunctionExpression(node: TSESTree.FunctionExpression) { + FunctionExpression(node): void { if (!isConciseConstructor(node)) { rules.FunctionExpression(node); } diff --git a/packages/eslint-plugin/src/rules/no-empty-interface.ts b/packages/eslint-plugin/src/rules/no-empty-interface.ts index 4d2a0a5a488..55fbdb5337e 100644 --- a/packages/eslint-plugin/src/rules/no-empty-interface.ts +++ b/packages/eslint-plugin/src/rules/no-empty-interface.ts @@ -40,7 +40,7 @@ export default util.createRule({ ], create(context, [{ allowSingleExtends }]) { return { - TSInterfaceDeclaration(node) { + TSInterfaceDeclaration(node): void { if (node.body.body.length !== 0) { // interface contains members --> Nothing to report return; diff --git a/packages/eslint-plugin/src/rules/no-explicit-any.ts b/packages/eslint-plugin/src/rules/no-explicit-any.ts index 43f81b8607e..249265a7683 100644 --- a/packages/eslint-plugin/src/rules/no-explicit-any.ts +++ b/packages/eslint-plugin/src/rules/no-explicit-any.ts @@ -167,7 +167,7 @@ export default util.createRule({ } return { - TSAnyKeyword(node) { + TSAnyKeyword(node): void { if (ignoreRestArgs && isNodeDescendantOfRestElementInFunction(node)) { return; } @@ -175,7 +175,8 @@ export default util.createRule({ let fix: TSESLint.ReportFixFunction | null = null; if (fixToUnknown) { - fix = fixer => fixer.replaceText(node, 'unknown'); + fix = (fixer => + fixer.replaceText(node, 'unknown')) as TSESLint.ReportFixFunction; } context.report({ diff --git a/packages/eslint-plugin/src/rules/no-extra-parens.ts b/packages/eslint-plugin/src/rules/no-extra-parens.ts index 28e69478d7e..7b8d091807c 100644 --- a/packages/eslint-plugin/src/rules/no-extra-parens.ts +++ b/packages/eslint-plugin/src/rules/no-extra-parens.ts @@ -31,7 +31,7 @@ export default util.createRule({ function binaryExp( node: TSESTree.BinaryExpression | TSESTree.LogicalExpression, - ) { + ): void { const rule = rules.BinaryExpression as (n: typeof node) => void; // makes the rule think it should skip the left or right @@ -56,7 +56,9 @@ export default util.createRule({ return rule(node); } - function callExp(node: TSESTree.CallExpression | TSESTree.NewExpression) { + function callExp( + node: TSESTree.CallExpression | TSESTree.NewExpression, + ): void { const rule = rules.CallExpression as (n: typeof node) => void; if (node.callee.type === AST_NODE_TYPES.TSAsExpression) { @@ -74,7 +76,7 @@ export default util.createRule({ } function unaryUpdateExpression( node: TSESTree.UnaryExpression | TSESTree.UpdateExpression, - ) { + ): void { const rule = rules.UnaryExpression as (n: typeof node) => void; if (node.argument.type === AST_NODE_TYPES.TSAsExpression) { diff --git a/packages/eslint-plugin/src/rules/no-extraneous-class.ts b/packages/eslint-plugin/src/rules/no-extraneous-class.ts index 846416866f2..4756b5928f3 100644 --- a/packages/eslint-plugin/src/rules/no-extraneous-class.ts +++ b/packages/eslint-plugin/src/rules/no-extraneous-class.ts @@ -54,7 +54,7 @@ export default util.createRule({ ], create(context, [{ allowConstructorOnly, allowEmpty, allowStaticOnly }]) { return { - ClassBody(node) { + ClassBody(node): void { const parent = node.parent as | TSESTree.ClassDeclaration | TSESTree.ClassExpression diff --git a/packages/eslint-plugin/src/rules/no-floating-promises.ts b/packages/eslint-plugin/src/rules/no-floating-promises.ts index 04d6664525c..5bc2f75adaa 100644 --- a/packages/eslint-plugin/src/rules/no-floating-promises.ts +++ b/packages/eslint-plugin/src/rules/no-floating-promises.ts @@ -24,7 +24,7 @@ export default util.createRule({ const checker = parserServices.program.getTypeChecker(); return { - ExpressionStatement(node) { + ExpressionStatement(node): void { const { expression } = parserServices.esTreeNodeToTSNodeMap.get< ts.ExpressionStatement >(node); diff --git a/packages/eslint-plugin/src/rules/no-for-in-array.ts b/packages/eslint-plugin/src/rules/no-for-in-array.ts index 970c621887e..03171e7fdf7 100644 --- a/packages/eslint-plugin/src/rules/no-for-in-array.ts +++ b/packages/eslint-plugin/src/rules/no-for-in-array.ts @@ -7,7 +7,7 @@ export default util.createRule({ docs: { description: 'Disallow iterating over an array with a for-in loop', category: 'Best Practices', - recommended: false, + recommended: 'error', }, messages: { forInViolation: @@ -19,7 +19,7 @@ export default util.createRule({ defaultOptions: [], create(context) { return { - ForInStatement(node) { + ForInStatement(node): void { const parserServices = util.getParserServices(context); const checker = parserServices.program.getTypeChecker(); const originalNode = parserServices.esTreeNodeToTSNodeMap.get< diff --git a/packages/eslint-plugin/src/rules/no-inferrable-types.ts b/packages/eslint-plugin/src/rules/no-inferrable-types.ts index f3b44e512fa..40fc6a60c7e 100644 --- a/packages/eslint-plugin/src/rules/no-inferrable-types.ts +++ b/packages/eslint-plugin/src/rules/no-inferrable-types.ts @@ -44,24 +44,30 @@ export default util.createRule({ }, defaultOptions: [ { - ignoreParameters: true, - ignoreProperties: true, + ignoreParameters: false, + ignoreProperties: false, }, ], create(context, [{ ignoreParameters, ignoreProperties }]) { - function isFunctionCall(init: TSESTree.Expression, callName: string) { + function isFunctionCall( + init: TSESTree.Expression, + callName: string, + ): boolean { return ( init.type === AST_NODE_TYPES.CallExpression && init.callee.type === AST_NODE_TYPES.Identifier && init.callee.name === callName ); } - function isLiteral(init: TSESTree.Expression, typeName: string) { + function isLiteral(init: TSESTree.Expression, typeName: string): boolean { return ( init.type === AST_NODE_TYPES.Literal && typeof init.value === typeName ); } - function isIdentifier(init: TSESTree.Expression, ...names: string[]) { + function isIdentifier( + init: TSESTree.Expression, + ...names: string[] + ): boolean { return ( init.type === AST_NODE_TYPES.Identifier && names.includes(init.name) ); diff --git a/packages/eslint-plugin/src/rules/no-magic-numbers.ts b/packages/eslint-plugin/src/rules/no-magic-numbers.ts index 8a50fa87c39..005bae9eb0a 100644 --- a/packages/eslint-plugin/src/rules/no-magic-numbers.ts +++ b/packages/eslint-plugin/src/rules/no-magic-numbers.ts @@ -150,7 +150,7 @@ export default util.createRule({ } return { - Literal(node) { + Literal(node): void { // Check if the node is a TypeScript enum declaration if (options.ignoreEnums && isParentTSEnumDeclaration(node)) { return; diff --git a/packages/eslint-plugin/src/rules/no-misused-new.ts b/packages/eslint-plugin/src/rules/no-misused-new.ts index aa9d0cb366c..67d00b131f0 100644 --- a/packages/eslint-plugin/src/rules/no-misused-new.ts +++ b/packages/eslint-plugin/src/rules/no-misused-new.ts @@ -16,7 +16,7 @@ export default util.createRule({ schema: [], messages: { errorMessageInterface: 'Interfaces cannot be constructed, only classes.', - errorMessageClass: 'Class cannon have method named `new`.', + errorMessageClass: 'Class cannot have method named `new`.', }, }, defaultOptions: [], @@ -69,7 +69,7 @@ export default util.createRule({ return { 'TSInterfaceBody > TSConstructSignatureDeclaration'( node: TSESTree.TSConstructSignatureDeclaration, - ) { + ): void { if ( isMatchingParentType( node.parent!.parent as TSESTree.TSInterfaceDeclaration, @@ -85,7 +85,7 @@ export default util.createRule({ }, "TSMethodSignature[key.name='constructor']"( node: TSESTree.TSMethodSignature, - ) { + ): void { context.report({ node, messageId: 'errorMessageInterface', @@ -93,7 +93,7 @@ export default util.createRule({ }, "ClassBody > MethodDefinition[key.name='new']"( node: TSESTree.MethodDefinition, - ) { + ): void { if (node.value.type === AST_NODE_TYPES.TSEmptyBodyFunctionExpression) { if ( node.parent && diff --git a/packages/eslint-plugin/src/rules/no-misused-promises.ts b/packages/eslint-plugin/src/rules/no-misused-promises.ts index b36046352d7..6d06ef8807a 100644 --- a/packages/eslint-plugin/src/rules/no-misused-promises.ts +++ b/packages/eslint-plugin/src/rules/no-misused-promises.ts @@ -17,7 +17,7 @@ export default util.createRule({ docs: { description: 'Avoid using promises in places not designed to handle them', category: 'Best Practices', - recommended: false, + recommended: 'error', }, messages: { voidReturn: @@ -73,13 +73,15 @@ export default util.createRule({ NewExpression: checkArguments, }; - function checkTestConditional(node: { test: TSESTree.Expression | null }) { + function checkTestConditional(node: { + test: TSESTree.Expression | null; + }): void { if (node.test) { checkConditional(node.test); } } - function checkConditional(node: TSESTree.Expression) { + function checkConditional(node: TSESTree.Expression): void { const tsNode = parserServices.esTreeNodeToTSNodeMap.get(node); if (isAlwaysThenable(checker, tsNode)) { context.report({ @@ -91,7 +93,7 @@ export default util.createRule({ function checkArguments( node: TSESTree.CallExpression | TSESTree.NewExpression, - ) { + ): void { const tsNode = parserServices.esTreeNodeToTSNodeMap.get< ts.CallExpression | ts.NewExpression >(node); @@ -126,7 +128,7 @@ export default util.createRule({ // alternates in a union) to be thenable. Otherwise, you might be trying to // check if something is defined or undefined and get caught because one of the // branches is thenable. -function isAlwaysThenable(checker: ts.TypeChecker, node: ts.Node) { +function isAlwaysThenable(checker: ts.TypeChecker, node: ts.Node): boolean { const type = checker.getTypeAtLocation(node); for (const subType of tsutils.unionTypeParts(checker.getApparentType(type))) { @@ -195,7 +197,7 @@ function isFunctionParam( function voidFunctionParams( checker: ts.TypeChecker, node: ts.CallExpression | ts.NewExpression, -) { +): Set { const voidReturnIndices = new Set(); const thenableReturnIndices = new Set(); const type = checker.getTypeAtLocation(node.expression); @@ -237,7 +239,10 @@ function voidFunctionParams( } // Returns true if the expression is a function that returns a thenable -function returnsThenable(checker: ts.TypeChecker, node: ts.Expression) { +function returnsThenable( + checker: ts.TypeChecker, + node: ts.Expression, +): boolean { const type = checker.getApparentType(checker.getTypeAtLocation(node)); for (const subType of tsutils.unionTypeParts(type)) { diff --git a/packages/eslint-plugin/src/rules/no-namespace.ts b/packages/eslint-plugin/src/rules/no-namespace.ts index ca68fcf647b..3698b4f941b 100644 --- a/packages/eslint-plugin/src/rules/no-namespace.ts +++ b/packages/eslint-plugin/src/rules/no-namespace.ts @@ -53,7 +53,7 @@ export default util.createRule({ return { "TSModuleDeclaration[global!=true][id.type='Identifier']"( node: TSESTree.TSModuleDeclaration, - ) { + ): void { if ( (node.parent && node.parent.type === AST_NODE_TYPES.TSModuleDeclaration) || diff --git a/packages/eslint-plugin/src/rules/no-non-null-assertion.ts b/packages/eslint-plugin/src/rules/no-non-null-assertion.ts index 6ed336e4d2d..4e888c58bb6 100644 --- a/packages/eslint-plugin/src/rules/no-non-null-assertion.ts +++ b/packages/eslint-plugin/src/rules/no-non-null-assertion.ts @@ -8,7 +8,7 @@ export default util.createRule({ description: 'Disallows non-null assertions using the `!` postfix operator', category: 'Stylistic Issues', - recommended: 'error', + recommended: 'warn', }, messages: { noNonNull: 'Forbidden non-null assertion.', @@ -18,7 +18,7 @@ export default util.createRule({ defaultOptions: [], create(context) { return { - TSNonNullExpression(node) { + TSNonNullExpression(node): void { context.report({ node, messageId: 'noNonNull', diff --git a/packages/eslint-plugin/src/rules/no-parameter-properties.ts b/packages/eslint-plugin/src/rules/no-parameter-properties.ts index a7727a33602..9b6bb31e654 100644 --- a/packages/eslint-plugin/src/rules/no-parameter-properties.ts +++ b/packages/eslint-plugin/src/rules/no-parameter-properties.ts @@ -27,7 +27,8 @@ export default util.createRule({ description: 'Disallow the use of parameter properties in class constructors', category: 'Stylistic Issues', - recommended: 'error', + // too opinionated to be recommended + recommended: false, }, messages: { noParamProp: @@ -81,7 +82,7 @@ export default util.createRule({ } return { - TSParameterProperty(node) { + TSParameterProperty(node): void { const modifiers = getModifiers(node); if (!allows.includes(modifiers)) { diff --git a/packages/eslint-plugin/src/rules/no-require-imports.ts b/packages/eslint-plugin/src/rules/no-require-imports.ts index afb0b4a3848..91d81e0d8c5 100644 --- a/packages/eslint-plugin/src/rules/no-require-imports.ts +++ b/packages/eslint-plugin/src/rules/no-require-imports.ts @@ -18,13 +18,15 @@ export default util.createRule({ defaultOptions: [], create(context) { return { - 'CallExpression > Identifier[name="require"]'(node: TSESTree.Identifier) { + 'CallExpression > Identifier[name="require"]'( + node: TSESTree.Identifier, + ): void { context.report({ node: node.parent!, messageId: 'noRequireImports', }); }, - TSExternalModuleReference(node) { + TSExternalModuleReference(node): void { context.report({ node, messageId: 'noRequireImports', diff --git a/packages/eslint-plugin/src/rules/no-this-alias.ts b/packages/eslint-plugin/src/rules/no-this-alias.ts index e156cbd24e2..8865fad2c3f 100644 --- a/packages/eslint-plugin/src/rules/no-this-alias.ts +++ b/packages/eslint-plugin/src/rules/no-this-alias.ts @@ -19,7 +19,7 @@ export default util.createRule({ docs: { description: 'Disallow aliasing `this`', category: 'Best Practices', - recommended: false, + recommended: 'error', }, schema: [ { @@ -46,7 +46,7 @@ export default util.createRule({ }, defaultOptions: [ { - allowDestructuring: false, + allowDestructuring: true, allowedNames: [], }, ], @@ -54,7 +54,7 @@ export default util.createRule({ return { "VariableDeclarator[init.type='ThisExpression']"( node: TSESTree.VariableDeclarator, - ) { + ): void { const { id } = node; if (allowDestructuring && id.type !== AST_NODE_TYPES.Identifier) { diff --git a/packages/eslint-plugin/src/rules/no-type-alias.ts b/packages/eslint-plugin/src/rules/no-type-alias.ts index f260d124133..1648b89f0df 100644 --- a/packages/eslint-plugin/src/rules/no-type-alias.ts +++ b/packages/eslint-plugin/src/rules/no-type-alias.ts @@ -44,6 +44,7 @@ export default util.createRule({ docs: { description: 'Disallow the use of type aliases', category: 'Stylistic Issues', + // too opinionated to be recommended recommended: false, }, messages: { @@ -188,7 +189,7 @@ export default util.createRule({ */ function validateTypeAliases( type: TypeWithLabel, - isTopLevel: boolean = false, + isTopLevel = false, ): void { if (type.node.type === AST_NODE_TYPES.TSFunctionType) { // callback @@ -268,7 +269,7 @@ export default util.createRule({ } return { - TSTypeAliasDeclaration(node) { + TSTypeAliasDeclaration(node): void { const types = getTypes(node.typeAnnotation); if (types.length === 1) { // is a top level type annotation diff --git a/packages/eslint-plugin/src/rules/no-unnecessary-qualifier.ts b/packages/eslint-plugin/src/rules/no-unnecessary-qualifier.ts index 23a98cb9bef..64f58848d79 100644 --- a/packages/eslint-plugin/src/rules/no-unnecessary-qualifier.ts +++ b/packages/eslint-plugin/src/rules/no-unnecessary-qualifier.ts @@ -132,7 +132,7 @@ export default util.createRule({ namespacesInScope.push(esTreeNodeToTSNodeMap.get(node)); } - function exitDeclaration() { + function exitDeclaration(): void { namespacesInScope.pop(); } diff --git a/packages/eslint-plugin/src/rules/no-unnecessary-type-arguments.ts b/packages/eslint-plugin/src/rules/no-unnecessary-type-arguments.ts index d93e205c7bb..100a9d261c0 100644 --- a/packages/eslint-plugin/src/rules/no-unnecessary-type-arguments.ts +++ b/packages/eslint-plugin/src/rules/no-unnecessary-type-arguments.ts @@ -73,7 +73,7 @@ export default util.createRule<[], MessageIds>({ } return { - TSTypeParameterInstantiation(node) { + TSTypeParameterInstantiation(node): void { const parentDeclaration = parserServices.esTreeNodeToTSNodeMap.get< ExtendingClassLikeDeclaration | ParameterCapableTSNode >(node.parent!); @@ -104,7 +104,7 @@ function getArgsAndParameters( function getTypeParametersFromNode( node: ParameterCapableTSNode, checker: ts.TypeChecker, -) { +): readonly ts.TypeParameterDeclaration[] | undefined { if (ts.isExpressionWithTypeArguments(node)) { return getTypeParametersFromType(node.expression, checker); } diff --git a/packages/eslint-plugin/src/rules/no-unnecessary-type-assertion.ts b/packages/eslint-plugin/src/rules/no-unnecessary-type-assertion.ts index c91c3d678c7..11da74d88c9 100644 --- a/packages/eslint-plugin/src/rules/no-unnecessary-type-assertion.ts +++ b/packages/eslint-plugin/src/rules/no-unnecessary-type-assertion.ts @@ -27,7 +27,7 @@ export default util.createRule({ description: 'Warns if a type assertion does not change the type of an expression', category: 'Best Practices', - recommended: false, + recommended: 'error', }, fixable: 'code', messages: { @@ -166,7 +166,7 @@ export default util.createRule({ } return { - TSNonNullExpression(node) { + TSNonNullExpression(node): void { const originalNode = parserServices.esTreeNodeToTSNodeMap.get< ts.NonNullExpression >(node); diff --git a/packages/eslint-plugin/src/rules/no-use-before-define.ts b/packages/eslint-plugin/src/rules/no-use-before-define.ts index 20ed4edc77a..e40da703893 100644 --- a/packages/eslint-plugin/src/rules/no-use-before-define.ts +++ b/packages/eslint-plugin/src/rules/no-use-before-define.ts @@ -255,7 +255,7 @@ export default util.createRule({ } return { - Program() { + Program(): void { findVariablesInScope(context.getScope()); }, }; diff --git a/packages/eslint-plugin/src/rules/no-useless-constructor.ts b/packages/eslint-plugin/src/rules/no-useless-constructor.ts index 6410fa5fa43..3587c795311 100644 --- a/packages/eslint-plugin/src/rules/no-useless-constructor.ts +++ b/packages/eslint-plugin/src/rules/no-useless-constructor.ts @@ -59,7 +59,7 @@ export default util.createRule({ create(context) { const rules = baseRule.create(context); return { - MethodDefinition(node) { + MethodDefinition(node): void { if ( node.value && node.value.type === AST_NODE_TYPES.FunctionExpression && diff --git a/packages/eslint-plugin/src/rules/no-var-requires.ts b/packages/eslint-plugin/src/rules/no-var-requires.ts index 29b1c77dd34..274cf2c1ceb 100644 --- a/packages/eslint-plugin/src/rules/no-var-requires.ts +++ b/packages/eslint-plugin/src/rules/no-var-requires.ts @@ -22,7 +22,7 @@ export default util.createRule({ defaultOptions: [], create(context) { return { - CallExpression(node) { + CallExpression(node): void { if ( node.callee.type === AST_NODE_TYPES.Identifier && node.callee.name === 'require' && diff --git a/packages/eslint-plugin/src/rules/prefer-for-of.ts b/packages/eslint-plugin/src/rules/prefer-for-of.ts index 89f6c540303..5aef0845a53 100644 --- a/packages/eslint-plugin/src/rules/prefer-for-of.ts +++ b/packages/eslint-plugin/src/rules/prefer-for-of.ts @@ -181,7 +181,7 @@ export default util.createRule({ } return { - 'ForStatement:exit'(node: TSESTree.ForStatement) { + 'ForStatement:exit'(node: TSESTree.ForStatement): void { if (!isSingleVariableDeclaration(node.init)) { return; } diff --git a/packages/eslint-plugin/src/rules/prefer-function-type.ts b/packages/eslint-plugin/src/rules/prefer-function-type.ts index f375b1bb8f2..dae4fae3295 100644 --- a/packages/eslint-plugin/src/rules/prefer-function-type.ts +++ b/packages/eslint-plugin/src/rules/prefer-function-type.ts @@ -72,7 +72,7 @@ export default util.createRule({ | TSESTree.TSCallSignatureDeclaration | TSESTree.TSConstructSignatureDeclaration, parent: TSESTree.Node, - ) { + ): string { const start = call.range[0]; const colonPos = call.returnType!.range[0] - start; const text = sourceCode.getText().slice(start, call.range[1]); @@ -102,7 +102,10 @@ export default util.createRule({ * @param member The TypeElement being checked * @param node The parent of member being checked */ - function checkMember(member: TSESTree.TypeElement, node: TSESTree.Node) { + function checkMember( + member: TSESTree.TypeElement, + node: TSESTree.Node, + ): void { if ( (member.type === AST_NODE_TYPES.TSCallSignatureDeclaration || member.type === AST_NODE_TYPES.TSConstructSignatureDeclaration) && @@ -141,12 +144,12 @@ export default util.createRule({ } return { - TSInterfaceDeclaration(node) { + TSInterfaceDeclaration(node): void { if (!hasOneSupertype(node) && node.body.body.length === 1) { checkMember(node.body.body[0], node); } }, - 'TSTypeLiteral[members.length = 1]'(node: TSESTree.TSTypeLiteral) { + 'TSTypeLiteral[members.length = 1]'(node: TSESTree.TSTypeLiteral): void { checkMember(node.members[0], node); }, }; diff --git a/packages/eslint-plugin/src/rules/prefer-includes.ts b/packages/eslint-plugin/src/rules/prefer-includes.ts index 2db17e11d1d..384edf01859 100644 --- a/packages/eslint-plugin/src/rules/prefer-includes.ts +++ b/packages/eslint-plugin/src/rules/prefer-includes.ts @@ -13,7 +13,7 @@ export default createRule({ docs: { description: 'Enforce `includes` method over `indexOf` method', category: 'Best Practices', - recommended: false, + recommended: 'error', }, fixable: 'code', messages: { diff --git a/packages/eslint-plugin/src/rules/prefer-namespace-keyword.ts b/packages/eslint-plugin/src/rules/prefer-namespace-keyword.ts index 6059a731a94..9137fe912aa 100644 --- a/packages/eslint-plugin/src/rules/prefer-namespace-keyword.ts +++ b/packages/eslint-plugin/src/rules/prefer-namespace-keyword.ts @@ -26,7 +26,7 @@ export default util.createRule({ const sourceCode = context.getSourceCode(); return { - TSModuleDeclaration(node) { + TSModuleDeclaration(node): void { // Do nothing if the name is a string. if (!node.id || node.id.type === AST_NODE_TYPES.Literal) { return; diff --git a/packages/eslint-plugin/src/rules/prefer-readonly.ts b/packages/eslint-plugin/src/rules/prefer-readonly.ts index 01f4ea320ba..22c85fd0343 100644 --- a/packages/eslint-plugin/src/rules/prefer-readonly.ts +++ b/packages/eslint-plugin/src/rules/prefer-readonly.ts @@ -61,7 +61,7 @@ export default util.createRule({ node: ts.PropertyAccessExpression, parent: ts.Node, classScope: ClassScope, - ) { + ): void { if (ts.isBinaryExpression(parent)) { handleParentBinaryExpression(node, parent, classScope); return; @@ -84,7 +84,7 @@ export default util.createRule({ node: ts.PropertyAccessExpression, parent: ts.BinaryExpression, classScope: ClassScope, - ) { + ): void { if ( parent.left === node && tsutils.isAssignmentKind(parent.operatorToken.kind) @@ -96,7 +96,7 @@ export default util.createRule({ function handleParentPostfixOrPrefixUnaryExpression( node: ts.PostfixUnaryExpression | ts.PrefixUnaryExpression, classScope: ClassScope, - ) { + ): void { if ( node.operator === ts.SyntaxKind.PlusPlusToken || node.operator === ts.SyntaxKind.MinusMinusToken @@ -107,14 +107,16 @@ export default util.createRule({ } } - function isConstructor(node: TSESTree.Node) { + function isConstructor(node: TSESTree.Node): boolean { return ( node.type === AST_NODE_TYPES.MethodDefinition && node.kind === 'constructor' ); } - function isFunctionScopeBoundaryInStack(node: TSESTree.Node) { + function isFunctionScopeBoundaryInStack( + node: TSESTree.Node, + ): boolean | tsutils.ScopeBoundary { if (classScopeStack.length === 0) { return false; } @@ -129,7 +131,7 @@ export default util.createRule({ function getEsNodesFromViolatingNode( violatingNode: ParameterOrPropertyDeclaration, - ) { + ): { esNode: TSESTree.Node; nameNode: TSESTree.Node } { if (ts.isParameterPropertyDeclaration(violatingNode)) { return { esNode: parserServices.tsNodeToESTreeNodeMap.get(violatingNode.name), @@ -148,7 +150,7 @@ export default util.createRule({ return { 'ClassDeclaration, ClassExpression'( node: TSESTree.ClassDeclaration | TSESTree.ClassExpression, - ) { + ): void { classScopeStack.push( new ClassScope( checker, @@ -157,7 +159,7 @@ export default util.createRule({ ), ); }, - 'ClassDeclaration, ClassExpression:exit'() { + 'ClassDeclaration, ClassExpression:exit'(): void { const finalizedClassScope = classScopeStack.pop()!; const sourceCode = context.getSourceCode(); @@ -175,7 +177,7 @@ export default util.createRule({ }); } }, - MemberExpression(node) { + MemberExpression(node): void { const tsNode = parserServices.esTreeNodeToTSNodeMap.get< ts.PropertyAccessExpression >(node); @@ -187,7 +189,7 @@ export default util.createRule({ ); } }, - [functionScopeBoundaries](node: TSESTree.Node) { + [functionScopeBoundaries](node: TSESTree.Node): void { if (isConstructor(node)) { classScopeStack[classScopeStack.length - 1].enterConstructor( parserServices.esTreeNodeToTSNodeMap.get( @@ -198,7 +200,7 @@ export default util.createRule({ classScopeStack[classScopeStack.length - 1].enterNonConstructor(); } }, - [`${functionScopeBoundaries}:exit`](node: TSESTree.Node) { + [`${functionScopeBoundaries}:exit`](node: TSESTree.Node): void { if (isConstructor(node)) { classScopeStack[classScopeStack.length - 1].exitConstructor(); } else if (isFunctionScopeBoundaryInStack(node)) { @@ -247,7 +249,7 @@ class ClassScope { } } - public addDeclaredVariable(node: ParameterOrPropertyDeclaration) { + public addDeclaredVariable(node: ParameterOrPropertyDeclaration): void { if ( !tsutils.isModifierFlagSet(node, ts.ModifierFlags.Private) || tsutils.isModifierFlagSet(node, ts.ModifierFlags.Readonly) || @@ -270,7 +272,7 @@ class ClassScope { ).set(node.name.getText(), node); } - public addVariableModification(node: ts.PropertyAccessExpression) { + public addVariableModification(node: ts.PropertyAccessExpression): void { const modifierType = this.checker.getTypeAtLocation(node.expression); if ( modifierType.symbol === undefined || @@ -295,7 +297,7 @@ class ClassScope { ).add(node.name.text); } - public enterConstructor(node: ts.ConstructorDeclaration) { + public enterConstructor(node: ts.ConstructorDeclaration): void { this.constructorScopeDepth = DIRECTLY_INSIDE_CONSTRUCTOR; for (const parameter of node.parameters) { @@ -305,23 +307,23 @@ class ClassScope { } } - public exitConstructor() { + public exitConstructor(): void { this.constructorScopeDepth = OUTSIDE_CONSTRUCTOR; } - public enterNonConstructor() { + public enterNonConstructor(): void { if (this.constructorScopeDepth !== OUTSIDE_CONSTRUCTOR) { this.constructorScopeDepth += 1; } } - public exitNonConstructor() { + public exitNonConstructor(): void { if (this.constructorScopeDepth !== OUTSIDE_CONSTRUCTOR) { this.constructorScopeDepth -= 1; } } - public finalizeUnmodifiedPrivateNonReadonlys() { + public finalizeUnmodifiedPrivateNonReadonlys(): ParameterOrPropertyDeclaration[] { this.memberVariableModifications.forEach(variableName => { this.privateModifiableMembers.delete(variableName); }); diff --git a/packages/eslint-plugin/src/rules/prefer-regexp-exec.ts b/packages/eslint-plugin/src/rules/prefer-regexp-exec.ts index c48e2351985..bd1a48fcb61 100644 --- a/packages/eslint-plugin/src/rules/prefer-regexp-exec.ts +++ b/packages/eslint-plugin/src/rules/prefer-regexp-exec.ts @@ -12,7 +12,7 @@ export default createRule({ description: 'Prefer RegExp#exec() over String#match() if no global flag is provided', category: 'Best Practices', - recommended: false, + recommended: 'error', }, messages: { regExpExecOverStringMatch: 'Use the `RegExp#exec()` method instead.', @@ -39,7 +39,7 @@ export default createRule({ return { "CallExpression[arguments.length=1] > MemberExpression.callee[property.name='match'][computed=false]"( node: TSESTree.MemberExpression, - ) { + ): void { const callNode = node.parent as TSESTree.CallExpression; const arg = callNode.arguments[0]; const evaluated = getStaticValue(arg, globalScope); diff --git a/packages/eslint-plugin/src/rules/prefer-string-starts-ends-with.ts b/packages/eslint-plugin/src/rules/prefer-string-starts-ends-with.ts index 4e902dfd584..2ffac6728ac 100644 --- a/packages/eslint-plugin/src/rules/prefer-string-starts-ends-with.ts +++ b/packages/eslint-plugin/src/rules/prefer-string-starts-ends-with.ts @@ -20,7 +20,7 @@ export default createRule({ description: 'Enforce the use of `String#startsWith` and `String#endsWith` instead of other equivalent methods of checking substrings', category: 'Best Practices', - recommended: false, + recommended: 'error', }, messages: { preferStartsWith: "Use 'String#startsWith' method instead.", diff --git a/packages/eslint-plugin/src/rules/promise-function-async.ts b/packages/eslint-plugin/src/rules/promise-function-async.ts index 48d63cf0f54..faf55f95fa7 100644 --- a/packages/eslint-plugin/src/rules/promise-function-async.ts +++ b/packages/eslint-plugin/src/rules/promise-function-async.ts @@ -86,7 +86,7 @@ export default util.createRule({ const parserServices = util.getParserServices(context); const checker = parserServices.program.getTypeChecker(); - function validateNode(node: TSESTree.Node) { + function validateNode(node: TSESTree.Node): void { const originalNode = parserServices.esTreeNodeToTSNodeMap.get(node); const signatures = checker .getTypeAtLocation(originalNode) @@ -111,17 +111,21 @@ export default util.createRule({ return { 'ArrowFunctionExpression[async = false]'( node: TSESTree.ArrowFunctionExpression, - ) { + ): void { if (checkArrowFunctions) { validateNode(node); } }, - 'FunctionDeclaration[async = false]'(node: TSESTree.FunctionDeclaration) { + 'FunctionDeclaration[async = false]'( + node: TSESTree.FunctionDeclaration, + ): void { if (checkFunctionDeclarations) { validateNode(node); } }, - 'FunctionExpression[async = false]'(node: TSESTree.FunctionExpression) { + 'FunctionExpression[async = false]'( + node: TSESTree.FunctionExpression, + ): void { if ( node.parent && 'kind' in node.parent && diff --git a/packages/eslint-plugin/src/rules/require-array-sort-compare.ts b/packages/eslint-plugin/src/rules/require-array-sort-compare.ts index a9745be16e0..5b3f430420f 100644 --- a/packages/eslint-plugin/src/rules/require-array-sort-compare.ts +++ b/packages/eslint-plugin/src/rules/require-array-sort-compare.ts @@ -26,7 +26,7 @@ export default util.createRule({ return { "CallExpression[arguments.length=0] > MemberExpression[property.name='sort'][computed=false]"( node: TSESTree.MemberExpression, - ) { + ): void { // Get the symbol of the `sort` method. const tsNode = service.esTreeNodeToTSNodeMap.get(node); const sortSymbol = checker.getSymbolAtLocation(tsNode); diff --git a/packages/eslint-plugin/src/rules/require-await.ts b/packages/eslint-plugin/src/rules/require-await.ts index cbdd08791d9..ef1092b46db 100644 --- a/packages/eslint-plugin/src/rules/require-await.ts +++ b/packages/eslint-plugin/src/rules/require-await.ts @@ -23,7 +23,7 @@ export default util.createRule({ docs: { description: 'Disallow async functions which have no `await` expression', category: 'Best Practices', - recommended: false, + recommended: 'error', }, schema: baseRule.meta.schema, messages: baseRule.meta.messages, @@ -46,7 +46,7 @@ export default util.createRule({ | TSESTree.FunctionDeclaration | TSESTree.FunctionExpression | TSESTree.ArrowFunctionExpression, - ) { + ): void { scopeInfo = { upper: scopeInfo, returnsPromise: false, @@ -79,7 +79,7 @@ export default util.createRule({ | TSESTree.FunctionDeclaration | TSESTree.FunctionExpression | TSESTree.ArrowFunctionExpression, - ) { + ): void { if (scopeInfo) { if (!scopeInfo.returnsPromise) { switch (node.type) { @@ -109,7 +109,7 @@ export default util.createRule({ 'FunctionExpression[async = true]:exit': exitFunction, 'ArrowFunctionExpression[async = true]:exit': exitFunction, - ReturnStatement(node: TSESTree.ReturnStatement) { + ReturnStatement(node): void { if (!scopeInfo) { return; } diff --git a/packages/eslint-plugin/src/rules/restrict-plus-operands.ts b/packages/eslint-plugin/src/rules/restrict-plus-operands.ts index 6d68d5f7dfc..df523033bf7 100644 --- a/packages/eslint-plugin/src/rules/restrict-plus-operands.ts +++ b/packages/eslint-plugin/src/rules/restrict-plus-operands.ts @@ -83,7 +83,7 @@ export default util.createRule({ } return { - "BinaryExpression[operator='+']"(node: TSESTree.BinaryExpression) { + "BinaryExpression[operator='+']"(node: TSESTree.BinaryExpression): void { const leftType = getNodeType(node.left); const rightType = getNodeType(node.right); diff --git a/packages/eslint-plugin/src/rules/semi.ts b/packages/eslint-plugin/src/rules/semi.ts index 8130088eb30..8d1ada467e3 100644 --- a/packages/eslint-plugin/src/rules/semi.ts +++ b/packages/eslint-plugin/src/rules/semi.ts @@ -16,6 +16,7 @@ export default util.createRule({ docs: { description: 'Require or disallow semicolons instead of ASI', category: 'Stylistic Issues', + // too opinionated to be recommended recommended: false, }, fixable: 'code', @@ -59,7 +60,7 @@ export default util.createRule({ return { ...rules, ...nodesToCheck, - ExportDefaultDeclaration(node) { + ExportDefaultDeclaration(node): void { if (node.declaration.type !== AST_NODE_TYPES.TSInterfaceDeclaration) { rules.ExportDefaultDeclaration(node); } diff --git a/packages/eslint-plugin/src/rules/triple-slash-reference.ts b/packages/eslint-plugin/src/rules/triple-slash-reference.ts index 7d74ab0c603..c5ab5772523 100644 --- a/packages/eslint-plugin/src/rules/triple-slash-reference.ts +++ b/packages/eslint-plugin/src/rules/triple-slash-reference.ts @@ -18,7 +18,7 @@ export default util.createRule({ description: 'Sets preference level for triple slash directives versus ES6-style import declarations', category: 'Best Practices', - recommended: false, + recommended: 'error', }, messages: { tripleSlashReference: @@ -57,7 +57,7 @@ export default util.createRule({ importName: string; })[] = []; - function hasMatchingReference(source: TSESTree.Literal) { + function hasMatchingReference(source: TSESTree.Literal): void { references.forEach(reference => { if (reference.importName === source.value) { context.report({ @@ -71,20 +71,20 @@ export default util.createRule({ }); } return { - ImportDeclaration(node) { + ImportDeclaration(node): void { if (programNode) { const source = node.source as TSESTree.Literal; hasMatchingReference(source); } }, - TSImportEqualsDeclaration(node) { + TSImportEqualsDeclaration(node): void { if (programNode) { const source = (node.moduleReference as TSESTree.TSExternalModuleReference) .expression as TSESTree.Literal; hasMatchingReference(source); } }, - Program(node) { + Program(node): void { if (lib === 'always' && path === 'always' && types == 'always') { return; } diff --git a/packages/eslint-plugin/src/rules/type-annotation-spacing.ts b/packages/eslint-plugin/src/rules/type-annotation-spacing.ts index f5e11f51fb0..d0f1c5b45a4 100644 --- a/packages/eslint-plugin/src/rules/type-annotation-spacing.ts +++ b/packages/eslint-plugin/src/rules/type-annotation-spacing.ts @@ -185,12 +185,12 @@ export default util.createRule({ } return { - TSMappedType(node) { + TSMappedType(node): void { if (node.typeAnnotation) { checkTypeAnnotationSpacing(node.typeAnnotation); } }, - TSTypeAnnotation(node) { + TSTypeAnnotation(node): void { checkTypeAnnotationSpacing(node.typeAnnotation); }, }; diff --git a/packages/eslint-plugin/src/rules/typedef.ts b/packages/eslint-plugin/src/rules/typedef.ts index 3c535467f2c..182568e5748 100644 --- a/packages/eslint-plugin/src/rules/typedef.ts +++ b/packages/eslint-plugin/src/rules/typedef.ts @@ -55,7 +55,7 @@ export default util.createRule<[Options], MessageIds>({ }, ], create(context, [options]) { - function report(location: TSESTree.Node, name?: string) { + function report(location: TSESTree.Node, name?: string): void { context.report({ node: location, messageId: name ? 'expectedTypedefNamed' : 'expectedTypedef', @@ -63,11 +63,13 @@ export default util.createRule<[Options], MessageIds>({ }); } - function getNodeName(node: TSESTree.Parameter | TSESTree.PropertyName) { + function getNodeName( + node: TSESTree.Parameter | TSESTree.PropertyName, + ): string | undefined { return node.type === AST_NODE_TYPES.Identifier ? node.name : undefined; } - function checkParameters(params: TSESTree.Parameter[]) { + function checkParameters(params: TSESTree.Parameter[]): void { for (const param of params) { let annotationNode: TSESTree.Node | undefined; @@ -90,17 +92,17 @@ export default util.createRule<[Options], MessageIds>({ } return { - ArrayPattern(node) { + ArrayPattern(node): void { if (options[OptionKeys.ArrayDestructuring] && !node.typeAnnotation) { report(node); } }, - ArrowFunctionExpression(node) { + ArrowFunctionExpression(node): void { if (options[OptionKeys.ArrowParameter]) { checkParameters(node.params); } }, - ClassProperty(node) { + ClassProperty(node): void { if ( options[OptionKeys.MemberVariableDeclaration] && !node.typeAnnotation @@ -115,19 +117,19 @@ export default util.createRule<[Options], MessageIds>({ }, 'FunctionDeclaration, FunctionExpression'( node: TSESTree.FunctionDeclaration | TSESTree.FunctionExpression, - ) { + ): void { if (options[OptionKeys.Parameter]) { checkParameters(node.params); } }, - ObjectPattern(node) { + ObjectPattern(node): void { if (options[OptionKeys.ObjectDestructuring] && !node.typeAnnotation) { report(node); } }, 'TSIndexSignature, TSPropertySignature'( node: TSESTree.TSIndexSignature | TSESTree.TSPropertySignature, - ) { + ): void { if (options[OptionKeys.PropertyDeclaration] && !node.typeAnnotation) { report( node, @@ -137,7 +139,7 @@ export default util.createRule<[Options], MessageIds>({ ); } }, - VariableDeclarator(node) { + VariableDeclarator(node): void { if ( options[OptionKeys.VariableDeclaration] && !node.id.typeAnnotation diff --git a/packages/eslint-plugin/src/rules/unbound-method.ts b/packages/eslint-plugin/src/rules/unbound-method.ts index 88892b63417..0db82b2c770 100644 --- a/packages/eslint-plugin/src/rules/unbound-method.ts +++ b/packages/eslint-plugin/src/rules/unbound-method.ts @@ -25,7 +25,7 @@ export default util.createRule({ category: 'Best Practices', description: 'Enforces unbound methods are called with their expected scope', - recommended: false, + recommended: 'error', }, messages: { unbound: @@ -54,7 +54,7 @@ export default util.createRule({ const checker = parserServices.program.getTypeChecker(); return { - [AST_NODE_TYPES.MemberExpression](node: TSESTree.MemberExpression) { + MemberExpression(node): void { if (isSafeUse(node)) { return; } @@ -73,7 +73,7 @@ export default util.createRule({ }, }); -function isDangerousMethod(symbol: ts.Symbol, ignoreStatic: boolean) { +function isDangerousMethod(symbol: ts.Symbol, ignoreStatic: boolean): boolean { const { valueDeclaration } = symbol; if (!valueDeclaration) { // working around https://github.com/microsoft/TypeScript/issues/31294 diff --git a/packages/eslint-plugin/src/rules/unified-signatures.ts b/packages/eslint-plugin/src/rules/unified-signatures.ts index 04695fcc302..1c3f747bc51 100644 --- a/packages/eslint-plugin/src/rules/unified-signatures.ts +++ b/packages/eslint-plugin/src/rules/unified-signatures.ts @@ -55,6 +55,7 @@ export default util.createRule({ description: 'Warns for any two overloads that could be unified into one by using a union or an optional/rest parameter', category: 'Variables', + // too opinionated to be recommended recommended: false, }, type: 'suggestion', @@ -307,14 +308,14 @@ export default util.createRule({ typeParameters?: TSESTree.TSTypeParameterDeclaration, ): IsTypeParameter { if (typeParameters === undefined) { - return () => false; + return (() => false) as IsTypeParameter; } const set = new Set(); for (const t of typeParameters.params) { set.add(t.name.name); } - return typeName => set.has(typeName); + return (typeName => set.has(typeName)) as IsTypeParameter; } /** True if any of the outer type parameters are used in a signature. */ @@ -476,7 +477,7 @@ export default util.createRule({ function createScope( parent: ScopeNode, typeParameters?: TSESTree.TSTypeParameterDeclaration, - ) { + ): void { currentScope && scopes.push(currentScope); currentScope = { overloads: new Map(), @@ -485,7 +486,7 @@ export default util.createRule({ }; } - function checkScope() { + function checkScope(): void { const failures = checkOverloads( Array.from(currentScope.overloads.values()), currentScope.typeParameters, @@ -494,7 +495,7 @@ export default util.createRule({ currentScope = scopes.pop()!; } - function addOverload(signature: OverloadNode, key?: string) { + function addOverload(signature: OverloadNode, key?: string): void { key = key || getOverloadKey(signature); if (currentScope && signature.parent === currentScope.parent && key) { const overloads = currentScope.overloads.get(key); @@ -513,15 +514,15 @@ export default util.createRule({ return { Program: createScope, TSModuleBlock: createScope, - TSInterfaceDeclaration(node) { + TSInterfaceDeclaration(node): void { createScope(node.body, node.typeParameters); }, - ClassDeclaration(node) { + ClassDeclaration(node): void { createScope(node.body, node.typeParameters); }, TSTypeLiteral: createScope, // collect overloads - TSDeclareFunction(node) { + TSDeclareFunction(node): void { if (node.id && !node.body) { addOverload(node, node.id.name); } @@ -529,12 +530,12 @@ export default util.createRule({ TSCallSignatureDeclaration: addOverload, TSConstructSignatureDeclaration: addOverload, TSMethodSignature: addOverload, - TSAbstractMethodDefinition(node) { + TSAbstractMethodDefinition(node): void { if (!node.value.body) { addOverload(node); } }, - MethodDefinition(node) { + MethodDefinition(node): void { if (!node.value.body) { addOverload(node); } diff --git a/packages/eslint-plugin/src/util/misc.ts b/packages/eslint-plugin/src/util/misc.ts index 30587b336ed..cae887c229f 100644 --- a/packages/eslint-plugin/src/util/misc.ts +++ b/packages/eslint-plugin/src/util/misc.ts @@ -11,14 +11,14 @@ import { /** * Check if the context file name is *.d.ts or *.d.tsx */ -export function isDefinitionFile(fileName: string) { +export function isDefinitionFile(fileName: string): boolean { return /\.d\.tsx?$/i.test(fileName || ''); } /** * Upper cases the first character or the string */ -export function upperCaseFirst(str: string) { +export function upperCaseFirst(str: string): string { return str[0].toUpperCase() + str.slice(1); } diff --git a/packages/eslint-plugin/src/util/types.ts b/packages/eslint-plugin/src/util/types.ts index 83a8bbabf57..6f769d7f950 100644 --- a/packages/eslint-plugin/src/util/types.ts +++ b/packages/eslint-plugin/src/util/types.ts @@ -186,7 +186,10 @@ export function isTypeFlagSet( /** * @returns Whether a type is an instance of the parent type, including for the parent's base types. */ -export const typeIsOrHasBaseType = (type: ts.Type, parentType: ts.Type) => { +export function typeIsOrHasBaseType( + type: ts.Type, + parentType: ts.Type, +): boolean { if (type.symbol === undefined || parentType.symbol === undefined) { return false; } @@ -208,4 +211,4 @@ export const typeIsOrHasBaseType = (type: ts.Type, parentType: ts.Type) => { } return false; -}; +} diff --git a/packages/eslint-plugin/tests/RuleTester.ts b/packages/eslint-plugin/tests/RuleTester.ts index fe3d06d4fc8..978adc10966 100644 --- a/packages/eslint-plugin/tests/RuleTester.ts +++ b/packages/eslint-plugin/tests/RuleTester.ts @@ -68,7 +68,7 @@ class RuleTester extends TSESLint.RuleTester { } } -function getFixturesRootDir() { +function getFixturesRootDir(): string { return path.join(process.cwd(), 'tests/fixtures/'); } diff --git a/packages/eslint-plugin/tests/rules/indent/utils.ts b/packages/eslint-plugin/tests/rules/indent/utils.ts index 72cedd3ea64..badfbc1c3c6 100644 --- a/packages/eslint-plugin/tests/rules/indent/utils.ts +++ b/packages/eslint-plugin/tests/rules/indent/utils.ts @@ -17,6 +17,7 @@ type MessageIds = InferMessageIdsTypeFromRule; * @returns The template literal, with spaces removed from all lines */ export function unIndent(strings: TemplateStringsArray): string { + const WHITESPACE_REGEX = / */u; const templateValue = strings[0]; const lines = templateValue .replace(/^\n/u, '') @@ -24,7 +25,7 @@ export function unIndent(strings: TemplateStringsArray): string { .split('\n'); const lineIndents = lines .filter(line => line.trim()) - .map(line => / */u.exec(line)![0].length); + .map(line => WHITESPACE_REGEX.exec(line)![0].length); const minLineIndent = Math.min(...lineIndents); return lines.map(line => line.slice(minLineIndent)).join('\n'); diff --git a/packages/eslint-plugin/tests/rules/no-this-alias.test.ts b/packages/eslint-plugin/tests/rules/no-this-alias.test.ts index c1ddce12186..03723eb9515 100644 --- a/packages/eslint-plugin/tests/rules/no-this-alias.test.ts +++ b/packages/eslint-plugin/tests/rules/no-this-alias.test.ts @@ -68,6 +68,11 @@ declare module 'foo' { }, { code: 'const { props, state } = this;', + options: [ + { + allowDestructuring: false, + }, + ], errors: [destructureError], }, { @@ -104,6 +109,11 @@ class TestClass { } } `, + options: [ + { + allowDestructuring: false, + }, + ], errors: [ idError, idError, diff --git a/packages/eslint-plugin/tests/rules/no-unused-vars.test.ts b/packages/eslint-plugin/tests/rules/no-unused-vars.test.ts index d09724e8eee..45bb8c80db1 100644 --- a/packages/eslint-plugin/tests/rules/no-unused-vars.test.ts +++ b/packages/eslint-plugin/tests/rules/no-unused-vars.test.ts @@ -10,10 +10,12 @@ const ruleTester = new RuleTester({ parser: '@typescript-eslint/parser', }); -function error(messages: { message: string; line: number; column: number }[]) { - // the base rule doesn't have messageIds +// the base rule doesn't have messageIds +function error( + messages: { message: string; line: number; column: number }[], // eslint-disable-next-line @typescript-eslint/no-explicit-any - return messages as any[]; +): any[] { + return messages; } ruleTester.run('no-unused-vars', rule, { diff --git a/packages/eslint-plugin/tools/generate-configs.ts b/packages/eslint-plugin/tools/generate-configs.ts index 5e293018eb4..d7f5fb29afd 100644 --- a/packages/eslint-plugin/tools/generate-configs.ts +++ b/packages/eslint-plugin/tools/generate-configs.ts @@ -2,8 +2,11 @@ import { TSESLint } from '@typescript-eslint/experimental-utils'; import chalk from 'chalk'; import fs from 'fs'; import path from 'path'; +import { format, resolveConfig } from 'prettier'; import rules from '../src/rules'; +const prettierConfig = resolveConfig(__dirname); + interface LinterConfigRules { [name: string]: | TSESLint.Linter.RuleLevel @@ -23,29 +26,40 @@ const BASE_RULES_TO_BE_OVERRIDDEN = new Set([ 'func-call-spacing', 'indent', 'no-array-constructor', + 'no-empty-function', 'no-extra-parens', 'no-magic-numbers', 'no-unused-vars', 'no-use-before-define', 'no-useless-constructor', + 'require-await', 'semi', ]); +// list of rules from the base plugin that we think should be turned on for typescript code +const BASE_RULES_THAT_ARE_RECOMMENDED = new Set([ + 'no-var', + 'prefer-const', + 'prefer-rest-params', + 'prefer-spread', +]); -const ruleEntries = Object.entries(rules); +const ruleEntries = Object.entries(rules).sort((a, b) => + a[0].localeCompare(b[0]), +); /** * Helper function reduces records to key - value pairs. * @param config * @param entry */ -const reducer = ( +function reducer( config: LinterConfigRules, entry: [string, TSESLint.RuleModule], settings: { errorLevel?: 'error' | 'warn'; filterDeprecated: boolean; }, -) => { +): LinterConfigRules { const key = entry[0]; const value = entry[1]; @@ -81,13 +95,17 @@ const reducer = ( config[ruleName] = usedSetting; return config; -}; +} /** * Helper function writes configuration. */ function writeConfig(config: LinterConfig, filePath: string): void { - fs.writeFileSync(filePath, `${JSON.stringify(config, null, 2)}\n`); + const configStr = format(JSON.stringify(config), { + parser: 'json', + ...prettierConfig, + }); + fs.writeFileSync(filePath, configStr); } const baseConfig: LinterConfig = { @@ -117,14 +135,18 @@ console.log(); console.log( '------------------------------ recommended.json ------------------------------', ); +const recommendedRules = ruleEntries + .filter(entry => !!entry[1].meta.docs.recommended) + .reduce( + (config, entry) => reducer(config, entry, { filterDeprecated: false }), + {}, + ); +BASE_RULES_THAT_ARE_RECOMMENDED.forEach(ruleName => { + recommendedRules[ruleName] = 'error'; +}); const recommendedConfig: LinterConfig = { extends: './configs/base.json', - rules: ruleEntries - .filter(entry => !!entry[1].meta.docs.recommended) - .reduce( - (config, entry) => reducer(config, entry, { filterDeprecated: false }), - {}, - ), + rules: recommendedRules, }; writeConfig( recommendedConfig, diff --git a/packages/eslint-plugin/tools/validate-configs/checkConfigAll.ts b/packages/eslint-plugin/tools/validate-configs/checkConfigAll.ts index 84981b8816e..fab57fe20cf 100644 --- a/packages/eslint-plugin/tools/validate-configs/checkConfigAll.ts +++ b/packages/eslint-plugin/tools/validate-configs/checkConfigAll.ts @@ -3,13 +3,13 @@ import { logRule } from '../log'; const prefix = '@typescript-eslint/'; -function checkConfigAll() { +function checkConfigAll(): boolean { const { rules } = plugin; const all = plugin.configs.all.rules; const allNames = new Set(Object.keys(all)); - return Object.entries(rules).reduce((acc, [ruleName, rule]) => { + return Object.entries(rules).reduce((acc, [ruleName, rule]) => { if (!rule.meta.deprecated) { const prefixed = `${prefix}${ruleName}` as keyof typeof all; if (allNames.has(prefixed)) { diff --git a/packages/eslint-plugin/tools/validate-configs/checkConfigRecommended.ts b/packages/eslint-plugin/tools/validate-configs/checkConfigRecommended.ts index 21f2faa876d..ae256143112 100644 --- a/packages/eslint-plugin/tools/validate-configs/checkConfigRecommended.ts +++ b/packages/eslint-plugin/tools/validate-configs/checkConfigRecommended.ts @@ -3,13 +3,13 @@ import { logRule } from '../log'; const prefix = '@typescript-eslint/'; -function checkConfigRecommended() { +function checkConfigRecommended(): boolean { const { rules } = plugin; const recommended = plugin.configs.recommended.rules; const recommendedNames = new Set(Object.keys(recommended)); - return Object.entries(rules).reduce((acc, [ruleName, rule]) => { + return Object.entries(rules).reduce((acc, [ruleName, rule]) => { if (!rule.meta.deprecated && rule.meta.docs.recommended !== false) { const prefixed = `${prefix}${ruleName}` as keyof typeof recommended; if (recommendedNames.has(prefixed)) { diff --git a/packages/eslint-plugin/tools/validate-docs/validate-table-structure.ts b/packages/eslint-plugin/tools/validate-docs/validate-table-structure.ts index e9d339c10c2..ed6a8131edb 100644 --- a/packages/eslint-plugin/tools/validate-docs/validate-table-structure.ts +++ b/packages/eslint-plugin/tools/validate-docs/validate-table-structure.ts @@ -3,6 +3,7 @@ import chalk from 'chalk'; import marked from 'marked'; import { logError } from '../log'; +const RULE_LINK_REGEX = /\[`@typescript-eslint\/(.+)`\]/; function validateTableStructure( rules: Record>>, rulesTable: marked.Tokens.Table, @@ -13,7 +14,7 @@ function validateTableStructure( let hasErrors = false; rulesTable.cells.forEach((row, rowIndex) => { - const match = /\[`@typescript-eslint\/(.+)`\]/.exec(row[0]); + const match = RULE_LINK_REGEX.exec(row[0]); if (!match) { logError(chalk.bold(`Unable to parse link in row ${rowIndex}:`), row[0]); hasErrors = true; diff --git a/packages/experimental-utils/src/eslint-utils/RuleCreator.ts b/packages/experimental-utils/src/eslint-utils/RuleCreator.ts index e14242fca72..3edb71e5a55 100644 --- a/packages/experimental-utils/src/eslint-utils/RuleCreator.ts +++ b/packages/experimental-utils/src/eslint-utils/RuleCreator.ts @@ -42,7 +42,7 @@ export function RuleCreator(urlCreator: (ruleName: string) => string) { url: urlCreator(name), }, }, - create(context) { + create(context): TRuleListener { const optionsWithDefault = applyDefault( defaultOptions, context.options, diff --git a/packages/experimental-utils/src/eslint-utils/deepMerge.ts b/packages/experimental-utils/src/eslint-utils/deepMerge.ts index 966a0db0821..3db67a59407 100644 --- a/packages/experimental-utils/src/eslint-utils/deepMerge.ts +++ b/packages/experimental-utils/src/eslint-utils/deepMerge.ts @@ -18,7 +18,10 @@ export function isObjectNotArray( * @param second The second object * @returns a new object */ -export function deepMerge(first: ObjectLike = {}, second: ObjectLike = {}) { +export function deepMerge( + first: ObjectLike = {}, + second: ObjectLike = {}, +): Record { // get the unique set of keys across both objects const keys = new Set(Object.keys(first).concat(Object.keys(second))); diff --git a/packages/parser/src/analyze-scope.ts b/packages/parser/src/analyze-scope.ts index 6c7e5a38428..1b98c064db0 100644 --- a/packages/parser/src/analyze-scope.ts +++ b/packages/parser/src/analyze-scope.ts @@ -21,7 +21,7 @@ function overrideDefine( this: TSESLintScope.Scope, node: TSESTree.Node, definition: TSESLintScope.Definition, - ) { + ): void { define.call(this, node, definition); // Set `variable.eslintUsed` to tell ESLint that the variable is exported. @@ -147,7 +147,7 @@ class Referencer extends TSESLintScope.Referencer { const upperScope = this.currentScope(); // Process the name. - if (type === 'FunctionDeclaration' && id) { + if (type === AST_NODE_TYPES.FunctionDeclaration && id) { upperScope.__define( id, new TSESLintScope.Definition( @@ -166,14 +166,14 @@ class Referencer extends TSESLintScope.Referencer { const def = defs[i]; if ( def.type === 'FunctionName' && - def.node.type === 'TSDeclareFunction' + def.node.type === AST_NODE_TYPES.TSDeclareFunction ) { defs.splice(i, 1); identifiers.splice(i, 1); break; } } - } else if (type === 'FunctionExpression' && id) { + } else if (type === AST_NODE_TYPES.FunctionExpression && id) { scopeManager.__nestFunctionExpressionNameScope(node); } @@ -213,7 +213,7 @@ class Referencer extends TSESLintScope.Referencer { this.visit(returnType); // Process the body. - if (body && body.type === 'BlockStatement') { + if (body && body.type === AST_NODE_TYPES.BlockStatement) { this.visitChildren(body); } else { this.visit(body); @@ -732,7 +732,7 @@ class Referencer extends TSESLintScope.Referencer { return; } - if (id && id.type === 'Identifier') { + if (id && id.type === AST_NODE_TYPES.Identifier) { scope.__define( id, new TSESLintScope.Definition( @@ -777,7 +777,7 @@ class Referencer extends TSESLintScope.Referencer { */ TSImportEqualsDeclaration(node: TSESTree.TSImportEqualsDeclaration): void { const { id, moduleReference } = node; - if (id && id.type === 'Identifier') { + if (id && id.type === AST_NODE_TYPES.Identifier) { this.currentScope().__define( id, new TSESLintScope.Definition( @@ -809,7 +809,7 @@ class Referencer extends TSESLintScope.Referencer { scopeManager.__currentScope = globalScope; // Skip TSModuleBlock to avoid to create that block scope. - if (node.body && node.body.type === 'TSModuleBlock') { + if (node.body && node.body.type === AST_NODE_TYPES.TSModuleBlock) { node.body.body.forEach(this.visit, this); } @@ -845,7 +845,7 @@ class Referencer extends TSESLintScope.Referencer { export function analyzeScope( ast: TSESTree.Program, parserOptions: ParserOptions, -) { +): ScopeManager { const options = { ignoreEval: true, optimistic: false, diff --git a/packages/parser/src/parser.ts b/packages/parser/src/parser.ts index fb5142f9b37..830a282da1e 100644 --- a/packages/parser/src/parser.ts +++ b/packages/parser/src/parser.ts @@ -28,7 +28,7 @@ interface ParseForESLintResult { function validateBoolean( value: boolean | undefined, - fallback: boolean = false, + fallback = false, ): boolean { if (typeof value !== 'boolean') { return fallback; @@ -44,7 +44,10 @@ export const version = packageJSON.version; export const Syntax = Object.freeze(AST_NODE_TYPES); -export function parse(code: string, options?: ParserOptions) { +export function parse( + code: string, + options?: ParserOptions, +): ParseForESLintResult['ast'] { return parseForESLint(code, options).ast; } diff --git a/packages/parser/src/scope/scope-manager.ts b/packages/parser/src/scope/scope-manager.ts index 648e24b77f8..c179bd512d3 100644 --- a/packages/parser/src/scope/scope-manager.ts +++ b/packages/parser/src/scope/scope-manager.ts @@ -13,12 +13,14 @@ export class ScopeManager extends TSESLintScope.ScopeManager { } /** @internal */ - __nestEnumScope(node: TSESTree.TSEnumDeclaration) { + __nestEnumScope(node: TSESTree.TSEnumDeclaration): TSESLintScope.Scope { return this.__nestScope(new EnumScope(this, this.__currentScope, node)); } /** @internal */ - __nestEmptyFunctionScope(node: TSESTree.TSDeclareFunction) { + __nestEmptyFunctionScope( + node: TSESTree.TSDeclareFunction, + ): TSESLintScope.Scope { return this.__nestScope( new EmptyFunctionScope(this, this.__currentScope, node), ); diff --git a/packages/parser/src/simple-traverse.ts b/packages/parser/src/simple-traverse.ts index 10f58918799..a616f239a7b 100644 --- a/packages/parser/src/simple-traverse.ts +++ b/packages/parser/src/simple-traverse.ts @@ -26,7 +26,7 @@ class SimpleTraverser { this.enter = enter; } - traverse(node: unknown, parent: TSESTree.Node | undefined) { + traverse(node: unknown, parent: TSESTree.Node | undefined): void { if (!isValidNode(node)) { return; } @@ -54,6 +54,6 @@ class SimpleTraverser { export function simpleTraverse( startingNode: TSESTree.Node, options: SimpleTraverseOptions, -) { +): void { new SimpleTraverser(options).traverse(startingNode, undefined); } diff --git a/packages/parser/tests/lib/basics.ts b/packages/parser/tests/lib/basics.ts index d526745e341..c06e8e57be1 100644 --- a/packages/parser/tests/lib/basics.ts +++ b/packages/parser/tests/lib/basics.ts @@ -39,7 +39,7 @@ export const Price: React.SFC = function Price(props) {} linter.defineRule('test', { create(context) { return { - TSTypeReference(node) { + TSTypeReference(node): void { const name = context.getSourceCode().getText(node.typeName); context.report({ node, diff --git a/packages/parser/tests/lib/jsx.ts b/packages/parser/tests/lib/jsx.ts index 4d7f6a3f9bf..530b26c444b 100644 --- a/packages/parser/tests/lib/jsx.ts +++ b/packages/parser/tests/lib/jsx.ts @@ -27,7 +27,7 @@ describe('JSX', () => { * Test each fixture file */ function testFixture(fixturesDir: string, useJSXTextNode: boolean) { - return (filename: string) => { + return (filename: string): void => { const code = fs.readFileSync(filename, 'utf8'); const config = { useJSXTextNode, diff --git a/packages/parser/tests/tools/scope-analysis.ts b/packages/parser/tests/tools/scope-analysis.ts index e7aab1d9951..373b863be81 100644 --- a/packages/parser/tests/tools/scope-analysis.ts +++ b/packages/parser/tests/tools/scope-analysis.ts @@ -8,20 +8,20 @@ export class ReferenceResolver { this.map = new Map(); } - resolve(obj: any, properties: any) { + resolve(obj: any, properties: any): any { const resolved = Object.assign({ $id: this.map.size }, properties); this.map.set(obj, resolved); return resolved; } - ref(obj: any) { + ref(obj: any): any { if (typeof obj !== 'object' || obj === null) { return obj; } - const { map } = this; + const map = this.map; return { - get $ref() { + get $ref(): any { return map.get(obj).$id; }, }; @@ -34,7 +34,7 @@ export class ReferenceResolver { * @param {ASTNode} node The AST node object. * @returns {Object} The object that can be used for JSON.stringify. */ -export function nodeToJSON(node: any) { +export function nodeToJSON(node: any): any { if (!node) { return node; } @@ -52,7 +52,7 @@ export function nodeToJSON(node: any) { * @param {ReferenceResolver} resolver The reference resolver. * @returns {Object} The object that can be used for JSON.stringify. */ -export function variableToJSON(variable: any, resolver: any) { +export function variableToJSON(variable: any, resolver: any): any { const { name, eslintUsed } = variable; const defs = variable.defs.map((d: any) => ({ type: d.type, @@ -80,7 +80,7 @@ export function variableToJSON(variable: any, resolver: any) { * @param {ReferenceResolver} resolver The reference resolver. * @returns {Object} The object that can be used for JSON.stringify. */ -export function referenceToJSON(reference: any, resolver: any) { +export function referenceToJSON(reference: any, resolver: any): any { const kind = `${reference.isRead() ? 'r' : ''}${ reference.isWrite() ? 'w' : '' }`; @@ -104,7 +104,10 @@ export function referenceToJSON(reference: any, resolver: any) { * @param {ReferenceResolver} resolver The reference resolver. * @returns {Object} The object that can be used for JSON.stringify. */ -export function scopeToJSON(scope: any, resolver = new ReferenceResolver()) { +export function scopeToJSON( + scope: any, + resolver = new ReferenceResolver(), +): any { const { type, functionExpressionScope, isStrict } = scope; const block = nodeToJSON(scope.block); const variables = scope.variables.map((v: any) => @@ -142,7 +145,7 @@ export function scopeToJSON(scope: any, resolver = new ReferenceResolver()) { }); } -export function getScopeTree(scopeManager: any) { +export function getScopeTree(scopeManager: any): any { const { globalScope } = scopeManager; // Do the postprocess to test. diff --git a/packages/parser/tests/tools/test-utils.ts b/packages/parser/tests/tools/test-utils.ts index 1ce769a521b..28dd40d2e07 100644 --- a/packages/parser/tests/tools/test-utils.ts +++ b/packages/parser/tests/tools/test-utils.ts @@ -18,7 +18,7 @@ const defaultConfig = { * @param ast the AST object * @returns copy of the AST object */ -function getRaw(ast: TSESTree.Program) { +function getRaw(ast: TSESTree.Program): TSESTree.Program { return JSON.parse( JSON.stringify(ast, (key, value) => { if ((key === 'start' || key === 'end') && typeof value === 'number') { @@ -39,18 +39,18 @@ function getRaw(ast: TSESTree.Program) { export function createSnapshotTestBlock( code: string, config: ParserOptions = {}, -) { +): () => void { config = Object.assign({}, defaultConfig, config); /** * @returns {Object} the AST object */ - function parse() { + function parse(): TSESTree.Program { const ast = parser.parseForESLint(code, config).ast; return getRaw(ast); } - return () => { + return (): void => { try { const result = parse(); expect(result).toMatchSnapshot(); @@ -77,18 +77,19 @@ export function createSnapshotTestBlock( export function createScopeSnapshotTestBlock( code: string, config: ParserOptions = {}, -) { +): () => void { config = Object.assign({}, defaultConfig, config); /** * @returns {Object} the AST object */ - function parse() { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + function parse(): any { const result = parser.parseForESLint(code, config); return getScopeTree(result.scopeManager); } - return () => { + return (): void => { try { const result = parse(); expect(result).toMatchSnapshot(); @@ -123,7 +124,7 @@ export function formatSnapshotName( filename: string, fixturesDir: string, fileExtension = '.js', -) { +): string { return `fixtures/${filename .replace(fixturesDir + '/', '') .replace(fileExtension, '')}`; diff --git a/packages/typescript-estree/src/ast-converter.ts b/packages/typescript-estree/src/ast-converter.ts index 9cb2bdc974a..fffb9ea15cf 100644 --- a/packages/typescript-estree/src/ast-converter.ts +++ b/packages/typescript-estree/src/ast-converter.ts @@ -1,14 +1,15 @@ import { SourceFile } from 'typescript'; -import { convertError, Converter } from './convert'; +import { convertError, Converter, ASTMaps } from './convert'; import { convertComments } from './convert-comments'; import { convertTokens } from './node-utils'; import { Extra } from './parser-options'; +import { TSESTree } from './ts-estree'; export function astConverter( ast: SourceFile, extra: Extra, shouldPreserveNodeMaps: boolean, -) { +): { estree: TSESTree.Program; astMaps: ASTMaps | undefined } { /** * The TypeScript compiler produced fundamental parse errors when parsing the * source. diff --git a/packages/typescript-estree/src/convert.ts b/packages/typescript-estree/src/convert.ts index dedae68dd17..3a7d2de99dc 100644 --- a/packages/typescript-estree/src/convert.ts +++ b/packages/typescript-estree/src/convert.ts @@ -19,8 +19,10 @@ import { isESTreeClassMember, isOptional, unescapeStringLiteralText, + TSError, } from './node-utils'; import { AST_NODE_TYPES, TSESTree, TSNode } from './ts-estree'; +import { ParserWeakMap } from './parser-options'; const SyntaxKind = ts.SyntaxKind; @@ -35,7 +37,7 @@ interface ConverterOptions { * @param error the error object * @returns converted error object */ -export function convertError(error: any) { +export function convertError(error: any): TSError { return createError( error.file, error.start, @@ -43,14 +45,19 @@ export function convertError(error: any) { ); } +export interface ASTMaps { + esTreeNodeToTSNodeMap: ParserWeakMap; + tsNodeToESTreeNodeMap: ParserWeakMap; +} + export class Converter { private readonly ast: ts.SourceFile; private readonly options: ConverterOptions; private readonly esTreeNodeToTSNodeMap = new WeakMap(); private readonly tsNodeToESTreeNodeMap = new WeakMap(); - private allowPattern: boolean = false; - private inTypeMode: boolean = false; + private allowPattern = false; + private inTypeMode = false; /** * Converts a TypeScript node into an ESTree node @@ -63,7 +70,7 @@ export class Converter { this.options = options; } - getASTMaps() { + getASTMaps(): ASTMaps { return { esTreeNodeToTSNodeMap: this.esTreeNodeToTSNodeMap, tsNodeToESTreeNodeMap: this.tsNodeToESTreeNodeMap, @@ -168,7 +175,7 @@ export class Converter { private registerTSNodeInNodeMap( node: ts.Node, result: TSESTree.BaseNode | null, - ) { + ): void { if (result && this.options.shouldPreserveNodeMaps) { if (!this.tsNodeToESTreeNodeMap.has(node)) { this.tsNodeToESTreeNodeMap.set(node, result); diff --git a/packages/typescript-estree/src/node-utils.ts b/packages/typescript-estree/src/node-utils.ts index f1666635b11..c23daf6e317 100644 --- a/packages/typescript-estree/src/node-utils.ts +++ b/packages/typescript-estree/src/node-utils.ts @@ -644,6 +644,13 @@ export function getNodeContainer( return container!; } +export interface TSError { + index: number; + lineNumber: number; + column: number; + message: string; +} + /** * @param ast the AST object * @param start the index at which the error starts @@ -654,7 +661,7 @@ export function createError( ast: ts.SourceFile, start: number, message: string, -) { +): TSError { const loc = ast.getLineAndCharacterOfPosition(start); return { index: start, @@ -668,7 +675,7 @@ export function createError( * @param n the TSNode * @param ast the TS AST */ -export function nodeHasTokens(n: ts.Node, ast: ts.SourceFile) { +export function nodeHasTokens(n: ts.Node, ast: ts.SourceFile): boolean { // If we have a token or node that has a non-zero width, it must have tokens. // Note: getWidth() does not take trivia into account. return n.kind === SyntaxKind.EndOfFileToken diff --git a/packages/typescript-estree/src/parser.ts b/packages/typescript-estree/src/parser.ts index 564114d7ccb..f215fa44492 100644 --- a/packages/typescript-estree/src/parser.ts +++ b/packages/typescript-estree/src/parser.ts @@ -33,7 +33,7 @@ let warnedAboutTSVersion = false; * * @param options Parser options */ -function getFileName({ jsx }: { jsx?: boolean }) { +function getFileName({ jsx }: { jsx?: boolean }): string { return jsx ? 'estree.tsx' : 'estree.ts'; } @@ -62,6 +62,11 @@ function resetExtra(): void { }; } +interface ASTAndProgram { + ast: ts.SourceFile; + program: ts.Program | undefined; +} + /** * @param code The code of the file being linted * @param options The config object @@ -71,7 +76,7 @@ function getASTFromProject( code: string, options: TSESTreeOptions, createDefaultProgram: boolean, -) { +): ASTAndProgram | undefined { const filePath = options.filePath || getFileName(options); const astAndProgram = firstDefined( calculateProjectParserOptions(code, filePath, extra), @@ -95,7 +100,10 @@ function getASTFromProject( * @param options The config object * @returns If found, returns the source file corresponding to the code and the containing program */ -function getASTAndDefaultProject(code: string, options: TSESTreeOptions) { +function getASTAndDefaultProject( + code: string, + options: TSESTreeOptions, +): ASTAndProgram | undefined { const fileName = options.filePath || getFileName(options); const program = createProgram(code, fileName, extra); const ast = program && program.getSourceFile(fileName); @@ -106,7 +114,7 @@ function getASTAndDefaultProject(code: string, options: TSESTreeOptions) { * @param code The code of the file being linted * @returns Returns a new source file and program corresponding to the linted code */ -function createNewProgram(code: string) { +function createNewProgram(code: string): ASTAndProgram { const FILENAME = getFileName(extra); const compilerHost: ts.CompilerHost = { @@ -170,7 +178,7 @@ function getProgramAndAST( options: TSESTreeOptions, shouldProvideParserServices: boolean, createDefaultProgram: boolean, -) { +): ASTAndProgram | undefined { return ( (shouldProvideParserServices && getASTFromProject(code, options, createDefaultProgram)) || @@ -293,7 +301,7 @@ function warnAboutTSVersion(): void { // Parser //------------------------------------------------------------------------------ -type AST = TSESTree.Program & +export type AST = TSESTree.Program & (T['range'] extends true ? { range: [number, number] } : {}) & (T['tokens'] extends true ? { tokens: TSESTree.Token[] } : {}) & (T['comment'] extends true ? { comments: TSESTree.Comment[] } : {}); @@ -403,7 +411,7 @@ export function parseAndGenerateServices< options, shouldProvideParserServices, extra.createDefaultProgram, - ); + )!; /** * Determine whether or not two-way maps of converted AST nodes should be preserved * during the conversion process diff --git a/packages/typescript-estree/src/tsconfig-parser.ts b/packages/typescript-estree/src/tsconfig-parser.ts index 09199053349..88c63171545 100644 --- a/packages/typescript-estree/src/tsconfig-parser.ts +++ b/packages/typescript-estree/src/tsconfig-parser.ts @@ -34,7 +34,7 @@ const parsedFilesSeen = new Set(); * Clear tsconfig caches. * Primarily used for testing. */ -export function clearCaches() { +export function clearCaches(): void { knownWatchProgramMap.clear(); watchCallbackTrackingMap.clear(); parsedFilesSeen.clear(); @@ -58,7 +58,7 @@ function diagnosticReporter(diagnostic: ts.Diagnostic): void { ); } -const noopFileWatcher = { close: () => {} }; +const noopFileWatcher = { close: (): void => {} }; function getTsconfigPath(tsconfigPath: string, extra: Extra): string { return path.isAbsolute(tsconfigPath) @@ -118,7 +118,7 @@ export function calculateProjectParserOptions( // ensure readFile reads the code being linted instead of the copy on disk const oldReadFile = watchCompilerHost.readFile; - watchCompilerHost.readFile = (filePath, encoding) => + watchCompilerHost.readFile = (filePath, encoding): string | undefined => path.normalize(filePath) === path.normalize(currentLintOperationState.filePath) ? currentLintOperationState.code @@ -128,7 +128,7 @@ export function calculateProjectParserOptions( watchCompilerHost.onUnRecoverableConfigFileDiagnostic = diagnosticReporter; // ensure process doesn't emit programs - watchCompilerHost.afterProgramCreate = program => { + watchCompilerHost.afterProgramCreate = (program): void => { // report error if there are any errors in the config file const configFileDiagnostics = program .getConfigFileParsingDiagnostics() @@ -143,18 +143,20 @@ export function calculateProjectParserOptions( }; // register callbacks to trigger program updates without using fileWatchers + // eslint-disable-next-line @typescript-eslint/explicit-function-return-type watchCompilerHost.watchFile = (fileName, callback) => { const normalizedFileName = path.normalize(fileName); watchCallbackTrackingMap.set(normalizedFileName, callback); return { - close: () => { + close: (): void => { watchCallbackTrackingMap.delete(normalizedFileName); }, }; }; // ensure fileWatchers aren't created for directories - watchCompilerHost.watchDirectory = () => noopFileWatcher; + watchCompilerHost.watchDirectory = (): typeof noopFileWatcher => + noopFileWatcher; // we're using internal typescript APIs which aren't on the types /* eslint-disable @typescript-eslint/no-explicit-any */ @@ -163,8 +165,9 @@ export function calculateProjectParserOptions( .onCachedDirectoryStructureHostCreate; (watchCompilerHost as any).onCachedDirectoryStructureHostCreate = ( host: any, - ) => { + ): void => { const oldReadDirectory = host.readDirectory; + // eslint-disable-next-line @typescript-eslint/explicit-function-return-type host.readDirectory = ( path: string, extensions?: readonly string[], @@ -206,7 +209,11 @@ export function calculateProjectParserOptions( * @param extra.project Provided tsconfig paths * @returns The program containing just the file being linted and associated library files */ -export function createProgram(code: string, filePath: string, extra: Extra) { +export function createProgram( + code: string, + filePath: string, + extra: Extra, +): ts.Program | undefined { if (!extra.projects || extra.projects.length !== 1) { return undefined; } @@ -225,7 +232,7 @@ export function createProgram(code: string, filePath: string, extra: Extra) { const compilerHost = ts.createCompilerHost(commandLine.options, true); const oldReadFile = compilerHost.readFile; - compilerHost.readFile = (fileName: string) => + compilerHost.readFile = (fileName: string): string | undefined => path.normalize(fileName) === path.normalize(filePath) ? code : oldReadFile(fileName); diff --git a/packages/typescript-estree/tests/ast-alignment/fixtures-to-test.ts b/packages/typescript-estree/tests/ast-alignment/fixtures-to-test.ts index 4b8e05d166b..64f2453fc60 100644 --- a/packages/typescript-estree/tests/ast-alignment/fixtures-to-test.ts +++ b/packages/typescript-estree/tests/ast-alignment/fixtures-to-test.ts @@ -33,8 +33,6 @@ const sharedFixturesDirPath = path.join( class FixturesTester { protected fixtures: FixturePatternConfig[] = []; - constructor() {} - /** * Utility to generate a FixturePatternConfig object containing the glob pattern for specific subsections of the fixtures/ directory, * including the capability to ignore specific nested patterns. @@ -45,7 +43,7 @@ class FixturesTester { public addFixturePatternConfig( fixturesSubPath: string, config: CreateFixturePatternConfig = {}, - ) { + ): void { let _fixturesDirPath = fixturesDirPath; if (!fs.existsSync(path.join(fixturesDirPath, fixturesSubPath))) { _fixturesDirPath = sharedFixturesDirPath; diff --git a/packages/typescript-estree/tests/ast-alignment/parse.ts b/packages/typescript-estree/tests/ast-alignment/parse.ts index 43c23ce44d7..7fba488c78d 100644 --- a/packages/typescript-estree/tests/ast-alignment/parse.ts +++ b/packages/typescript-estree/tests/ast-alignment/parse.ts @@ -1,12 +1,17 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ + import { ParserPlugin } from '@babel/parser'; import codeFrame from 'babel-code-frame'; import * as parser from '../../src/parser'; import * as parseUtils from './utils'; -function createError(message: string, line: number, column: number) { +function createError( + message: string, + line: number, + column: number, +): SyntaxError { // Construct an error similar to the ones thrown by Babylon. const error = new SyntaxError(`${message} (${line}:${column})`); - // eslint-disable-next-line @typescript-eslint/no-explicit-any (error as any).loc = { line, column, @@ -14,7 +19,7 @@ function createError(message: string, line: number, column: number) { return error; } -function parseWithBabelParser(text: string, jsx: boolean = true) { +function parseWithBabelParser(text: string, jsx = true): any { const babel = require('@babel/parser'); const plugins: ParserPlugin[] = [ 'typescript', @@ -40,7 +45,7 @@ function parseWithBabelParser(text: string, jsx: boolean = true) { }); } -function parseWithTypeScriptESTree(text: string, jsx: boolean = true) { +function parseWithTypeScriptESTree(text: string, jsx = true): parser.AST { try { const result = parser.parseAndGenerateServices(text, { loc: true, @@ -69,12 +74,14 @@ interface ASTComparisonParseOptions { jsx?: boolean; } -export function parse(text: string, opts: ASTComparisonParseOptions) { +export function parse( + text: string, + opts: ASTComparisonParseOptions, +): { parseError: any | null; ast: any | null } { /** * Always return a consistent interface, there will be times when we expect both * parsers to fail to parse the invalid source. */ - // eslint-disable-next-line @typescript-eslint/no-explicit-any const result: { parseError: any | null; ast: any | null } = { parseError: null, ast: null, diff --git a/packages/typescript-estree/tests/ast-alignment/utils.ts b/packages/typescript-estree/tests/ast-alignment/utils.ts index 43be2143e78..563fd19cc30 100644 --- a/packages/typescript-estree/tests/ast-alignment/utils.ts +++ b/packages/typescript-estree/tests/ast-alignment/utils.ts @@ -26,7 +26,7 @@ export function omitDeep( root: any, keysToOmit: { key: string; predicate: Function }[], nodes: Record void> = {}, -) { +): any { function shouldOmit(keyName: string, val: any): boolean { if (keysToOmit && keysToOmit.length) { return keysToOmit.some( @@ -36,7 +36,7 @@ export function omitDeep( return false; } - function visit(node: any, parent: any) { + function visit(node: any, parent: any): void { if (!node) { return; } @@ -72,8 +72,8 @@ export function omitDeep( /** * Common predicates for Babylon AST preprocessing */ -const always = () => true; -const ifNumber = (val: any) => typeof val === 'number'; +const always = (): boolean => true; +const ifNumber = (val: any): boolean => typeof val === 'number'; /** * - Babylon wraps the "Program" node in an extra "File" node, normalize this for simplicity for now... @@ -290,7 +290,7 @@ export function preprocessBabylonAST(ast: any): any { export function removeLocationDataAndSourceTypeFromProgramNode( ast: any, ignoreSourceType: boolean, -) { +): any { delete ast.loc; delete ast.range; if (ignoreSourceType) { diff --git a/packages/typescript-estree/tests/lib/convert.ts b/packages/typescript-estree/tests/lib/convert.ts index 4a5ac51be55..2a275c53fb4 100644 --- a/packages/typescript-estree/tests/lib/convert.ts +++ b/packages/typescript-estree/tests/lib/convert.ts @@ -100,7 +100,7 @@ describe('convert', () => { instance.convertProgram(); const maps = instance.getASTMaps(); - function checkMaps(child: any) { + function checkMaps(child: any): void { child.forEachChild((node: any) => { if ( node.kind !== ts.SyntaxKind.EndOfFileToken && @@ -134,7 +134,7 @@ describe('convert', () => { instance.convertProgram(); const maps = instance.getASTMaps(); - function checkMaps(child: any) { + function checkMaps(child: any): void { child.forEachChild((node: any) => { if ( node.kind !== ts.SyntaxKind.EndOfFileToken && @@ -167,7 +167,7 @@ describe('convert', () => { const program = instance.convertProgram(); const maps = instance.getASTMaps(); - function checkMaps(child: any) { + function checkMaps(child: any): void { child.forEachChild((node: any) => { if (node.kind !== ts.SyntaxKind.EndOfFileToken) { expect(ast).toBe( @@ -184,7 +184,7 @@ describe('convert', () => { expect(maps.esTreeNodeToTSNodeMap.get(program.body[0])).toBeDefined(); expect(program.body[0]).not.toBe( - maps.tsNodeToESTreeNodeMap.get(ast.statements[0]), + maps.tsNodeToESTreeNodeMap.get(ast.statements[0] as any), ); checkMaps(ast); }); diff --git a/packages/typescript-estree/tests/lib/jsx.ts b/packages/typescript-estree/tests/lib/jsx.ts index 52b6debdf6b..a3e0148907c 100644 --- a/packages/typescript-estree/tests/lib/jsx.ts +++ b/packages/typescript-estree/tests/lib/jsx.ts @@ -23,11 +23,8 @@ describe('JSX', () => { /** * Test each fixture file */ - function testFixture( - fixturesDir: string, - useJSXTextNode: boolean, - ): (filename: string) => void { - return filename => { + function testFixture(fixturesDir: string, useJSXTextNode: boolean) { + return (filename: string): void => { const code = readFileSync(filename, 'utf8'); const config: TSESTreeOptions = { loc: true, diff --git a/packages/typescript-estree/tests/lib/semanticInfo.ts b/packages/typescript-estree/tests/lib/semanticInfo.ts index 7e5c634db9d..1afab0c5e44 100644 --- a/packages/typescript-estree/tests/lib/semanticInfo.ts +++ b/packages/typescript-estree/tests/lib/semanticInfo.ts @@ -280,7 +280,7 @@ describe('semanticInfo', () => { function testIsolatedFile( parseResult: ParseAndGenerateServicesResult, -) { +): void { // get type checker expect(parseResult).toHaveProperty('services.program.getTypeChecker'); const checker = parseResult.services.program!.getTypeChecker(); @@ -331,7 +331,7 @@ function testIsolatedFile( * @param {ts.TypeChecker} checker * @param {ts.Node} tsNode */ -function checkNumberArrayType(checker: ts.TypeChecker, tsNode: ts.Node) { +function checkNumberArrayType(checker: ts.TypeChecker, tsNode: ts.Node): void { const nodeType = checker.getTypeAtLocation(tsNode); expect(nodeType.flags).toBe(ts.TypeFlags.Object); expect((nodeType as ts.ObjectType).objectFlags).toBe( diff --git a/packages/typescript-estree/tools/test-utils.ts b/packages/typescript-estree/tools/test-utils.ts index 16210df104f..b9fcb51cab6 100644 --- a/packages/typescript-estree/tools/test-utils.ts +++ b/packages/typescript-estree/tools/test-utils.ts @@ -6,7 +6,7 @@ import { TSESTreeOptions } from '../src/parser-options'; * @param {Object} ast the AST object * @returns {Object} copy of the AST object */ -export function getRaw(ast: parser.TSESTree.Program) { +export function getRaw(ast: parser.TSESTree.Program): parser.TSESTree.Program { return JSON.parse( JSON.stringify(ast, (key, value) => { if ((key === 'start' || key === 'end') && typeof value === 'number') { @@ -20,7 +20,7 @@ export function getRaw(ast: parser.TSESTree.Program) { export function parseCodeAndGenerateServices( code: string, config: TSESTreeOptions, -) { +): parser.ParseAndGenerateServicesResult { return parser.parseAndGenerateServices(code, config); } @@ -36,18 +36,18 @@ export function createSnapshotTestBlock( code: string, config: TSESTreeOptions, generateServices?: true, -) { +): () => void { /** * @returns {Object} the AST object */ - function parse() { + function parse(): parser.TSESTree.Program { const ast = generateServices ? parser.parseAndGenerateServices(code, config).ast : parser.parse(code, config); return getRaw(ast); } - return () => { + return (): void => { try { const result = parse(); expect(result).toMatchSnapshot(); diff --git a/tests/integration/utils/jest-snapshot-resolver.js b/tests/integration/utils/jest-snapshot-resolver.js index 3032ef5d575..366a9611839 100644 --- a/tests/integration/utils/jest-snapshot-resolver.js +++ b/tests/integration/utils/jest-snapshot-resolver.js @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/explicit-function-return-type */ /** * Use a jest snapshotResolver to map the test snapshot output back to the * linked volume. This means that even though we are running our tests inside diff --git a/tools/generate-contributors.ts b/tools/generate-contributors.ts index 34bf65ac52f..11b23765615 100644 --- a/tools/generate-contributors.ts +++ b/tools/generate-contributors.ts @@ -35,7 +35,7 @@ interface AllContributorsUser { contributions: string[]; } -async function* fetchUsers(page = 1) { +async function* fetchUsers(page = 1): AsyncIterableIterator { let lastLength = 0; do { const response = await fetch(`${contributorsApiUrl}&page=${page}`, { @@ -61,7 +61,7 @@ async function* fetchUsers(page = 1) { ); } -async function main() { +async function main(): Promise { const githubContributors: Contributor[] = []; // fetch all of the contributor info diff --git a/yarn.lock b/yarn.lock index 5924927ec6d..c86527946ff 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1428,6 +1428,11 @@ resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz#e486d0d97396d79beedd0a6e33f4534ff6b4973e" integrity sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA== +"@types/prettier@^1.18.0": + version "1.18.0" + resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-1.18.0.tgz#d2dbe4d5f76b455138f13a2d881278e2c06a733d" + integrity sha512-5N6WK/XXs9PLPpge2KOmOSaIym2vIo32GsrxM5YOFs7uZ8R9L/acg+hQzWsfwoHEpasqQkH0+3LzLTbiF1GFLQ== + "@types/semver@^6.0.1": version "6.0.1" resolved "https://registry.yarnpkg.com/@types/semver/-/semver-6.0.1.tgz#a984b405c702fa5a7ec6abc56b37f2ba35ef5af6" @@ -6988,7 +6993,7 @@ prelude-ls@~1.1.2: resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= -prettier@^1.18.2: +prettier@*, prettier@^1.18.2: version "1.18.2" resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.18.2.tgz#6823e7c5900017b4bd3acf46fe9ac4b4d7bda9ea" integrity sha512-OeHeMc0JhFE9idD4ZdtNibzY0+TPHSpSSb9h8FqtP+YnoZZ1sl8Vc9b1sasjfymH3SonAF4QcA2+mzHPhMvIiw== From 90b36ddac2f6de006fd59f2a9234df1eb2d1606e Mon Sep 17 00:00:00 2001 From: Taeheon Kim Date: Wed, 14 Aug 2019 01:39:11 +0900 Subject: [PATCH 06/28] docs(eslint-plugin): update ROADMAP.md (#844) --- packages/eslint-plugin/ROADMAP.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/eslint-plugin/ROADMAP.md b/packages/eslint-plugin/ROADMAP.md index 58580935012..65b5dcd46ba 100644 --- a/packages/eslint-plugin/ROADMAP.md +++ b/packages/eslint-plugin/ROADMAP.md @@ -22,7 +22,7 @@ | [`no-import-side-effect`] | 🔌 | [`import/no-unassigned-import`] | | [`no-inferrable-types`] | ✅ | [`@typescript-eslint/no-inferrable-types`] | | [`no-internal-module`] | ✅ | [`@typescript-eslint/prefer-namespace-keyword`] | -| [`no-magic-numbers`] | 🌟 | [`no-magic-numbers`][no-magic-numbers] | +| [`no-magic-numbers`] | ✅ | [`@typescript-eslint/no-magic-numbers`] | | [`no-namespace`] | ✅ | [`@typescript-eslint/no-namespace`] | | [`no-non-null-assertion`] | ✅ | [`@typescript-eslint/no-non-null-assertion`] | | [`no-parameter-reassignment`] | ✅ | [`no-param-reassign`][no-param-reassign] | @@ -620,6 +620,7 @@ Relevant plugins: [`chai-expect-keywords`](https://github.com/gavinaiken/eslint- [`@typescript-eslint/no-unnecessary-type-arguments`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-unnecessary-type-arguments.md [`@typescript-eslint/semi`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/semi.md [`@typescript-eslint/no-floating-promises`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-floating-promises.md +[`@typescript-eslint/no-magic-numbers`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-magic-numbers.md From d3470c963eb436d9e5128301d4579fb2b251de7c Mon Sep 17 00:00:00 2001 From: James Henry Date: Tue, 13 Aug 2019 20:37:21 +0100 Subject: [PATCH 07/28] feat(eslint-plugin)!: recommended-requiring-type-checking config (#846) BREAKING CHANGE: removed some rules from recommended config --- .eslintrc.js | 1 + packages/eslint-plugin/README.md | 21 ++++++- packages/eslint-plugin/package.json | 6 +- .../recommended-requiring-type-checking.json | 19 +++++++ .../src/configs/recommended.json | 10 ---- packages/eslint-plugin/src/index.ts | 2 + .../eslint-plugin/src/rules/await-thenable.ts | 1 + .../src/rules/no-floating-promises.ts | 1 + .../src/rules/no-for-in-array.ts | 1 + .../src/rules/no-misused-promises.ts | 1 + .../src/rules/no-unnecessary-qualifier.ts | 1 + .../rules/no-unnecessary-type-arguments.ts | 1 + .../rules/no-unnecessary-type-assertion.ts | 1 + .../src/rules/prefer-includes.ts | 1 + .../src/rules/prefer-readonly.ts | 1 + .../src/rules/prefer-regexp-exec.ts | 1 + .../rules/prefer-string-starts-ends-with.ts | 1 + .../src/rules/promise-function-async.ts | 1 + .../src/rules/require-array-sort-compare.ts | 1 + .../eslint-plugin/src/rules/require-await.ts | 1 + .../src/rules/restrict-plus-operands.ts | 1 + .../src/rules/strict-boolean-expressions.ts | 1 + .../eslint-plugin/src/rules/unbound-method.ts | 1 + .../eslint-plugin/tools/generate-configs.ts | 56 ++++++++++++++++++- .../checkConfigRecommended.ts | 6 +- ...kConfigRecommendedRequiringTypeChecking.ts | 45 +++++++++++++++ .../tools/validate-configs/index.ts | 6 ++ .../validate-docs/validate-table-rules.ts | 24 ++++++++ .../experimental-utils/src/ts-eslint/Rule.ts | 5 ++ tests/integration/docker-compose.yml | 17 ++++++ .../.eslintrc.yml | 17 ++++++ .../Dockerfile | 17 ++++++ .../index.ts | 1 + .../test.js.snap | 44 +++++++++++++++ .../test.sh | 19 +++++++ tests/integration/run-all-tests.sh | 3 + 36 files changed, 317 insertions(+), 19 deletions(-) create mode 100644 packages/eslint-plugin/src/configs/recommended-requiring-type-checking.json create mode 100644 packages/eslint-plugin/tools/validate-configs/checkConfigRecommendedRequiringTypeChecking.ts create mode 100644 tests/integration/fixtures/recommended-does-not-require-program/.eslintrc.yml create mode 100644 tests/integration/fixtures/recommended-does-not-require-program/Dockerfile create mode 100644 tests/integration/fixtures/recommended-does-not-require-program/index.ts create mode 100644 tests/integration/fixtures/recommended-does-not-require-program/test.js.snap create mode 100755 tests/integration/fixtures/recommended-does-not-require-program/test.sh diff --git a/.eslintrc.js b/.eslintrc.js index 32a476969d3..ef3d195354e 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -15,6 +15,7 @@ module.exports = { 'eslint:recommended', 'plugin:@typescript-eslint/eslint-recommended', 'plugin:@typescript-eslint/recommended', + 'plugin:@typescript-eslint/recommended-requiring-type-checking', ], rules: { // diff --git a/packages/eslint-plugin/README.md b/packages/eslint-plugin/README.md index 516f9c48dae..1ecc3e86cf9 100644 --- a/packages/eslint-plugin/README.md +++ b/packages/eslint-plugin/README.md @@ -51,7 +51,7 @@ You can also enable all the recommended rules for our plugin. Add `plugin:@types } ``` -You can also use [eslint:recommended](https://eslint.org/docs/rules/) with this plugin. Add both `eslint:recommended` and `plugin:@typescript-eslint/eslint-recommended`: +You can also use [eslint:recommended](https://eslint.org/docs/rules/) (the set of rules which are recommended for all projects by the ESLint Team) with this plugin. As noted in the root README, not all eslint core rules are compatible with TypeScript, so you need to add both `eslint:recommended` and `plugin:@typescript-eslint/eslint-recommended` (which will adjust the one from eslint appropriately for TypeScript) to your config: ```json { @@ -63,7 +63,24 @@ You can also use [eslint:recommended](https://eslint.org/docs/rules/) with this } ``` -If you want to use rules which require type information, you will need to specify a path to your tsconfig.json file in the "project" property of "parserOptions". +As of version 2 of this plugin, _by design_, none of the rules in the main `recommended` config require type-checking in order to run. This means that they are more lightweight and faster to run. + +Some highly valuable rules simply require type-checking in order to be implemented correctly, however, so we provide an additional config you can extend from called `recommended-requiring-type-checking`. You wou apply this _in addition_ to the recommended configs previously mentioned, e.g.: + +```json +{ + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/eslint-recommended", + "plugin:@typescript-eslint/recommended", + "plugin:@typescript-eslint/recommended-requiring-type-checking" + ] +} +``` + +Pro Tip: For larger codebases you may want to consider splitting our linting into two separate stages: 1. fast feedback rules which operate purely based on syntax (no type-checking), 2. rules which are based on semantics (type-checking). + +NOTE: If you want to use rules which require type information, you will need to specify a path to your tsconfig.json file in the "project" property of "parserOptions". If you do not do this, you will get a runtime error which explains this. ```json { diff --git a/packages/eslint-plugin/package.json b/packages/eslint-plugin/package.json index 14b417ac5c7..e0d1c05a9fe 100644 --- a/packages/eslint-plugin/package.json +++ b/packages/eslint-plugin/package.json @@ -30,11 +30,11 @@ "main": "dist/index.js", "scripts": { "build": "tsc -p tsconfig.build.json", - "check:docs": "ts-node --files ./tools/validate-docs/index.ts", - "check:configs": "ts-node --files ./tools/validate-configs/index.ts", + "check:docs": "../../node_modules/.bin/ts-node --files ./tools/validate-docs/index.ts", + "check:configs": "../../node_modules/.bin/ts-node --files ./tools/validate-configs/index.ts", "clean": "rimraf dist/", "format": "prettier --write \"./**/*.{ts,js,json,md}\" --ignore-path ../../.prettierignore", - "generate:configs": "ts-node --files tools/generate-configs.ts", + "generate:configs": "../../node_modules/.bin/ts-node --files tools/generate-configs.ts", "prebuild": "npm run clean", "test": "jest --coverage", "typecheck": "tsc --noEmit" diff --git a/packages/eslint-plugin/src/configs/recommended-requiring-type-checking.json b/packages/eslint-plugin/src/configs/recommended-requiring-type-checking.json new file mode 100644 index 00000000000..68867b53248 --- /dev/null +++ b/packages/eslint-plugin/src/configs/recommended-requiring-type-checking.json @@ -0,0 +1,19 @@ +{ + "extends": "./configs/base.json", + "rules": { + "@typescript-eslint/await-thenable": "error", + "@typescript-eslint/no-for-in-array": "error", + "@typescript-eslint/no-misused-promises": "error", + "@typescript-eslint/no-unnecessary-type-assertion": "error", + "@typescript-eslint/prefer-includes": "error", + "@typescript-eslint/prefer-regexp-exec": "error", + "@typescript-eslint/prefer-string-starts-ends-with": "error", + "require-await": "off", + "@typescript-eslint/require-await": "error", + "@typescript-eslint/unbound-method": "error", + "no-var": "error", + "prefer-const": "error", + "prefer-rest-params": "error", + "prefer-spread": "error" + } +} diff --git a/packages/eslint-plugin/src/configs/recommended.json b/packages/eslint-plugin/src/configs/recommended.json index 95d18847686..7d7a5628c9d 100644 --- a/packages/eslint-plugin/src/configs/recommended.json +++ b/packages/eslint-plugin/src/configs/recommended.json @@ -2,7 +2,6 @@ "extends": "./configs/base.json", "rules": { "@typescript-eslint/adjacent-overload-signatures": "error", - "@typescript-eslint/await-thenable": "error", "@typescript-eslint/ban-ts-ignore": "error", "@typescript-eslint/ban-types": "error", "camelcase": "off", @@ -18,28 +17,19 @@ "@typescript-eslint/no-empty-function": "error", "@typescript-eslint/no-empty-interface": "error", "@typescript-eslint/no-explicit-any": "warn", - "@typescript-eslint/no-for-in-array": "error", "@typescript-eslint/no-inferrable-types": "error", "@typescript-eslint/no-misused-new": "error", - "@typescript-eslint/no-misused-promises": "error", "@typescript-eslint/no-namespace": "error", "@typescript-eslint/no-non-null-assertion": "warn", "@typescript-eslint/no-this-alias": "error", - "@typescript-eslint/no-unnecessary-type-assertion": "error", "no-unused-vars": "off", "@typescript-eslint/no-unused-vars": "warn", "no-use-before-define": "off", "@typescript-eslint/no-use-before-define": "error", "@typescript-eslint/no-var-requires": "error", - "@typescript-eslint/prefer-includes": "error", "@typescript-eslint/prefer-namespace-keyword": "error", - "@typescript-eslint/prefer-regexp-exec": "error", - "@typescript-eslint/prefer-string-starts-ends-with": "error", - "require-await": "off", - "@typescript-eslint/require-await": "error", "@typescript-eslint/triple-slash-reference": "error", "@typescript-eslint/type-annotation-spacing": "error", - "@typescript-eslint/unbound-method": "error", "no-var": "error", "prefer-const": "error", "prefer-rest-params": "error", diff --git a/packages/eslint-plugin/src/index.ts b/packages/eslint-plugin/src/index.ts index 0fb5516ba72..d8e55844e1d 100644 --- a/packages/eslint-plugin/src/index.ts +++ b/packages/eslint-plugin/src/index.ts @@ -3,6 +3,7 @@ import rules from './rules'; import all from './configs/all.json'; import base from './configs/base.json'; import recommended from './configs/recommended.json'; +import recommendedRequiringTypeChecking from './configs/recommended-requiring-type-checking.json'; import eslintRecommended from './configs/eslint-recommended'; export = { @@ -12,5 +13,6 @@ export = { base, recommended, 'eslint-recommended': eslintRecommended, + 'recommended-requiring-type-checking': recommendedRequiringTypeChecking, }, }; diff --git a/packages/eslint-plugin/src/rules/await-thenable.ts b/packages/eslint-plugin/src/rules/await-thenable.ts index 304321001f7..94dc75cf78d 100644 --- a/packages/eslint-plugin/src/rules/await-thenable.ts +++ b/packages/eslint-plugin/src/rules/await-thenable.ts @@ -10,6 +10,7 @@ export default util.createRule({ description: 'Disallows awaiting a value that is not a Thenable', category: 'Best Practices', recommended: 'error', + requiresTypeChecking: true, }, messages: { await: 'Unexpected `await` of a non-Promise (non-"Thenable") value.', diff --git a/packages/eslint-plugin/src/rules/no-floating-promises.ts b/packages/eslint-plugin/src/rules/no-floating-promises.ts index 5bc2f75adaa..816cc084670 100644 --- a/packages/eslint-plugin/src/rules/no-floating-promises.ts +++ b/packages/eslint-plugin/src/rules/no-floating-promises.ts @@ -10,6 +10,7 @@ export default util.createRule({ description: 'Requires Promise-like values to be handled appropriately.', category: 'Best Practices', recommended: false, + requiresTypeChecking: true, }, messages: { floating: 'Promises must be handled appropriately', diff --git a/packages/eslint-plugin/src/rules/no-for-in-array.ts b/packages/eslint-plugin/src/rules/no-for-in-array.ts index 03171e7fdf7..db15d310457 100644 --- a/packages/eslint-plugin/src/rules/no-for-in-array.ts +++ b/packages/eslint-plugin/src/rules/no-for-in-array.ts @@ -8,6 +8,7 @@ export default util.createRule({ description: 'Disallow iterating over an array with a for-in loop', category: 'Best Practices', recommended: 'error', + requiresTypeChecking: true, }, messages: { forInViolation: diff --git a/packages/eslint-plugin/src/rules/no-misused-promises.ts b/packages/eslint-plugin/src/rules/no-misused-promises.ts index 6d06ef8807a..63d0f0bf483 100644 --- a/packages/eslint-plugin/src/rules/no-misused-promises.ts +++ b/packages/eslint-plugin/src/rules/no-misused-promises.ts @@ -18,6 +18,7 @@ export default util.createRule({ description: 'Avoid using promises in places not designed to handle them', category: 'Best Practices', recommended: 'error', + requiresTypeChecking: true, }, messages: { voidReturn: diff --git a/packages/eslint-plugin/src/rules/no-unnecessary-qualifier.ts b/packages/eslint-plugin/src/rules/no-unnecessary-qualifier.ts index 64f58848d79..1017be32222 100644 --- a/packages/eslint-plugin/src/rules/no-unnecessary-qualifier.ts +++ b/packages/eslint-plugin/src/rules/no-unnecessary-qualifier.ts @@ -10,6 +10,7 @@ export default util.createRule({ category: 'Best Practices', description: 'Warns when a namespace qualifier is unnecessary', recommended: false, + requiresTypeChecking: true, }, fixable: 'code', messages: { diff --git a/packages/eslint-plugin/src/rules/no-unnecessary-type-arguments.ts b/packages/eslint-plugin/src/rules/no-unnecessary-type-arguments.ts index 100a9d261c0..040650ea05d 100644 --- a/packages/eslint-plugin/src/rules/no-unnecessary-type-arguments.ts +++ b/packages/eslint-plugin/src/rules/no-unnecessary-type-arguments.ts @@ -29,6 +29,7 @@ export default util.createRule<[], MessageIds>({ 'Warns if an explicitly specified type argument is the default for that type parameter', category: 'Best Practices', recommended: false, + requiresTypeChecking: true, }, fixable: 'code', messages: { diff --git a/packages/eslint-plugin/src/rules/no-unnecessary-type-assertion.ts b/packages/eslint-plugin/src/rules/no-unnecessary-type-assertion.ts index 11da74d88c9..61583878bae 100644 --- a/packages/eslint-plugin/src/rules/no-unnecessary-type-assertion.ts +++ b/packages/eslint-plugin/src/rules/no-unnecessary-type-assertion.ts @@ -28,6 +28,7 @@ export default util.createRule({ 'Warns if a type assertion does not change the type of an expression', category: 'Best Practices', recommended: 'error', + requiresTypeChecking: true, }, fixable: 'code', messages: { diff --git a/packages/eslint-plugin/src/rules/prefer-includes.ts b/packages/eslint-plugin/src/rules/prefer-includes.ts index 384edf01859..fcb5dfca4f5 100644 --- a/packages/eslint-plugin/src/rules/prefer-includes.ts +++ b/packages/eslint-plugin/src/rules/prefer-includes.ts @@ -14,6 +14,7 @@ export default createRule({ description: 'Enforce `includes` method over `indexOf` method', category: 'Best Practices', recommended: 'error', + requiresTypeChecking: true, }, fixable: 'code', messages: { diff --git a/packages/eslint-plugin/src/rules/prefer-readonly.ts b/packages/eslint-plugin/src/rules/prefer-readonly.ts index 22c85fd0343..2c8ab3671be 100644 --- a/packages/eslint-plugin/src/rules/prefer-readonly.ts +++ b/packages/eslint-plugin/src/rules/prefer-readonly.ts @@ -32,6 +32,7 @@ export default util.createRule({ "Requires that private members are marked as `readonly` if they're never modified outside of the constructor", category: 'Best Practices', recommended: false, + requiresTypeChecking: true, }, fixable: 'code', messages: { diff --git a/packages/eslint-plugin/src/rules/prefer-regexp-exec.ts b/packages/eslint-plugin/src/rules/prefer-regexp-exec.ts index bd1a48fcb61..fea08554c3a 100644 --- a/packages/eslint-plugin/src/rules/prefer-regexp-exec.ts +++ b/packages/eslint-plugin/src/rules/prefer-regexp-exec.ts @@ -13,6 +13,7 @@ export default createRule({ 'Prefer RegExp#exec() over String#match() if no global flag is provided', category: 'Best Practices', recommended: 'error', + requiresTypeChecking: true, }, messages: { regExpExecOverStringMatch: 'Use the `RegExp#exec()` method instead.', diff --git a/packages/eslint-plugin/src/rules/prefer-string-starts-ends-with.ts b/packages/eslint-plugin/src/rules/prefer-string-starts-ends-with.ts index 2ffac6728ac..56344783b34 100644 --- a/packages/eslint-plugin/src/rules/prefer-string-starts-ends-with.ts +++ b/packages/eslint-plugin/src/rules/prefer-string-starts-ends-with.ts @@ -21,6 +21,7 @@ export default createRule({ 'Enforce the use of `String#startsWith` and `String#endsWith` instead of other equivalent methods of checking substrings', category: 'Best Practices', recommended: 'error', + requiresTypeChecking: true, }, messages: { preferStartsWith: "Use 'String#startsWith' method instead.", diff --git a/packages/eslint-plugin/src/rules/promise-function-async.ts b/packages/eslint-plugin/src/rules/promise-function-async.ts index faf55f95fa7..225b2bd8367 100644 --- a/packages/eslint-plugin/src/rules/promise-function-async.ts +++ b/packages/eslint-plugin/src/rules/promise-function-async.ts @@ -22,6 +22,7 @@ export default util.createRule({ 'Requires any function or method that returns a Promise to be marked async', category: 'Best Practices', recommended: false, + requiresTypeChecking: true, }, messages: { missingAsync: 'Functions that return promises must be async.', diff --git a/packages/eslint-plugin/src/rules/require-array-sort-compare.ts b/packages/eslint-plugin/src/rules/require-array-sort-compare.ts index 5b3f430420f..e88f2791f8f 100644 --- a/packages/eslint-plugin/src/rules/require-array-sort-compare.ts +++ b/packages/eslint-plugin/src/rules/require-array-sort-compare.ts @@ -12,6 +12,7 @@ export default util.createRule({ description: 'Enforce giving `compare` argument to `Array#sort`', category: 'Best Practices', recommended: false, + requiresTypeChecking: true, }, messages: { requireCompare: "Require 'compare' argument.", diff --git a/packages/eslint-plugin/src/rules/require-await.ts b/packages/eslint-plugin/src/rules/require-await.ts index ef1092b46db..7441b464896 100644 --- a/packages/eslint-plugin/src/rules/require-await.ts +++ b/packages/eslint-plugin/src/rules/require-await.ts @@ -24,6 +24,7 @@ export default util.createRule({ description: 'Disallow async functions which have no `await` expression', category: 'Best Practices', recommended: 'error', + requiresTypeChecking: true, }, schema: baseRule.meta.schema, messages: baseRule.meta.messages, diff --git a/packages/eslint-plugin/src/rules/restrict-plus-operands.ts b/packages/eslint-plugin/src/rules/restrict-plus-operands.ts index df523033bf7..1f715f1f902 100644 --- a/packages/eslint-plugin/src/rules/restrict-plus-operands.ts +++ b/packages/eslint-plugin/src/rules/restrict-plus-operands.ts @@ -11,6 +11,7 @@ export default util.createRule({ 'When adding two variables, operands must both be of type number or of type string', category: 'Best Practices', recommended: false, + requiresTypeChecking: true, }, messages: { notNumbers: diff --git a/packages/eslint-plugin/src/rules/strict-boolean-expressions.ts b/packages/eslint-plugin/src/rules/strict-boolean-expressions.ts index d9077bf3154..9c3c5b6ea98 100644 --- a/packages/eslint-plugin/src/rules/strict-boolean-expressions.ts +++ b/packages/eslint-plugin/src/rules/strict-boolean-expressions.ts @@ -27,6 +27,7 @@ export default util.createRule({ description: 'Restricts the types allowed in boolean expressions', category: 'Best Practices', recommended: false, + requiresTypeChecking: true, }, schema: [ { diff --git a/packages/eslint-plugin/src/rules/unbound-method.ts b/packages/eslint-plugin/src/rules/unbound-method.ts index 0db82b2c770..c76ac76f9d0 100644 --- a/packages/eslint-plugin/src/rules/unbound-method.ts +++ b/packages/eslint-plugin/src/rules/unbound-method.ts @@ -26,6 +26,7 @@ export default util.createRule({ description: 'Enforces unbound methods are called with their expected scope', recommended: 'error', + requiresTypeChecking: true, }, messages: { unbound: diff --git a/packages/eslint-plugin/tools/generate-configs.ts b/packages/eslint-plugin/tools/generate-configs.ts index d7f5fb29afd..cda1b0771ea 100644 --- a/packages/eslint-plugin/tools/generate-configs.ts +++ b/packages/eslint-plugin/tools/generate-configs.ts @@ -58,6 +58,7 @@ function reducer( settings: { errorLevel?: 'error' | 'warn'; filterDeprecated: boolean; + filterRequiresTypeChecking?: 'include' | 'exclude'; }, ): LinterConfigRules { const key = entry[0]; @@ -67,6 +68,22 @@ function reducer( return config; } + // Explicitly exclude rules requiring type-checking + if ( + settings.filterRequiresTypeChecking === 'exclude' && + value.meta.docs.requiresTypeChecking === true + ) { + return config; + } + + // Explicitly include rules requiring type-checking + if ( + settings.filterRequiresTypeChecking === 'include' && + value.meta.docs.requiresTypeChecking !== true + ) { + return config; + } + const ruleName = `${RULE_NAME_PREFIX}${key}`; const recommendation = value.meta.docs.recommended; const usedSetting = settings.errorLevel @@ -119,7 +136,7 @@ writeConfig(baseConfig, path.resolve(__dirname, '../src/configs/base.json')); console.log(); console.log( - '---------------------------------- all.json ----------------------------------', + '------------------------------------------------ all.json ------------------------------------------------', ); const allConfig: LinterConfig = { extends: './configs/base.json', @@ -133,12 +150,16 @@ writeConfig(allConfig, path.resolve(__dirname, '../src/configs/all.json')); console.log(); console.log( - '------------------------------ recommended.json ------------------------------', + '------------------------------ recommended.json (should not require program) ------------------------------', ); const recommendedRules = ruleEntries .filter(entry => !!entry[1].meta.docs.recommended) .reduce( - (config, entry) => reducer(config, entry, { filterDeprecated: false }), + (config, entry) => + reducer(config, entry, { + filterDeprecated: false, + filterRequiresTypeChecking: 'exclude', + }), {}, ); BASE_RULES_THAT_ARE_RECOMMENDED.forEach(ruleName => { @@ -152,3 +173,32 @@ writeConfig( recommendedConfig, path.resolve(__dirname, '../src/configs/recommended.json'), ); + +console.log(); +console.log( + '--------------------------------- recommended-requiring-type-checking.json ---------------------------------', +); +const recommendedRulesRequiringProgram = ruleEntries + .filter(entry => !!entry[1].meta.docs.recommended) + .reduce( + (config, entry) => + reducer(config, entry, { + filterDeprecated: false, + filterRequiresTypeChecking: 'include', + }), + {}, + ); +BASE_RULES_THAT_ARE_RECOMMENDED.forEach(ruleName => { + recommendedRulesRequiringProgram[ruleName] = 'error'; +}); +const recommendedRequiringTypeCheckingConfig: LinterConfig = { + extends: './configs/base.json', + rules: recommendedRulesRequiringProgram, +}; +writeConfig( + recommendedRequiringTypeCheckingConfig, + path.resolve( + __dirname, + '../src/configs/recommended-requiring-type-checking.json', + ), +); diff --git a/packages/eslint-plugin/tools/validate-configs/checkConfigRecommended.ts b/packages/eslint-plugin/tools/validate-configs/checkConfigRecommended.ts index ae256143112..28f0c7b21a5 100644 --- a/packages/eslint-plugin/tools/validate-configs/checkConfigRecommended.ts +++ b/packages/eslint-plugin/tools/validate-configs/checkConfigRecommended.ts @@ -10,7 +10,11 @@ function checkConfigRecommended(): boolean { const recommendedNames = new Set(Object.keys(recommended)); return Object.entries(rules).reduce((acc, [ruleName, rule]) => { - if (!rule.meta.deprecated && rule.meta.docs.recommended !== false) { + if ( + !rule.meta.deprecated && + rule.meta.docs.recommended !== false && + rule.meta.docs.requiresTypeChecking !== true + ) { const prefixed = `${prefix}${ruleName}` as keyof typeof recommended; if (recommendedNames.has(prefixed)) { if (recommended[prefixed] !== rule.meta.docs.recommended) { diff --git a/packages/eslint-plugin/tools/validate-configs/checkConfigRecommendedRequiringTypeChecking.ts b/packages/eslint-plugin/tools/validate-configs/checkConfigRecommendedRequiringTypeChecking.ts new file mode 100644 index 00000000000..a63f5e42c23 --- /dev/null +++ b/packages/eslint-plugin/tools/validate-configs/checkConfigRecommendedRequiringTypeChecking.ts @@ -0,0 +1,45 @@ +import plugin from '../../src/index'; +import { logRule } from '../log'; + +const prefix = '@typescript-eslint/'; + +function checkConfigRecommendedRequiringTypeChecking(): boolean { + const { rules } = plugin; + + const recommendedRequiringTypeChecking = + plugin.configs['recommended-requiring-type-checking'].rules; + const recommendedNames = new Set( + Object.keys(recommendedRequiringTypeChecking), + ); + + return Object.entries(rules).reduce((acc, [ruleName, rule]) => { + if ( + !rule.meta.deprecated && + rule.meta.docs.recommended !== false && + rule.meta.docs.requiresTypeChecking === true + ) { + const prefixed = `${prefix}${ruleName}` as keyof typeof recommendedRequiringTypeChecking; + if (recommendedNames.has(prefixed)) { + if ( + recommendedRequiringTypeChecking[prefixed] !== + rule.meta.docs.recommended + ) { + logRule( + false, + ruleName, + 'incorrect setting compared to the rule meta.', + ); + return true; + } + } else { + logRule(false, ruleName, 'missing in the config.'); + return true; + } + } + + logRule(true, ruleName); + return acc; + }, false); +} + +export { checkConfigRecommendedRequiringTypeChecking }; diff --git a/packages/eslint-plugin/tools/validate-configs/index.ts b/packages/eslint-plugin/tools/validate-configs/index.ts index cb64df53648..1c05196a677 100644 --- a/packages/eslint-plugin/tools/validate-configs/index.ts +++ b/packages/eslint-plugin/tools/validate-configs/index.ts @@ -1,11 +1,17 @@ import chalk from 'chalk'; import { checkConfigRecommended } from './checkConfigRecommended'; +import { checkConfigRecommendedRequiringTypeChecking } from './checkConfigRecommendedRequiringTypeChecking'; import { checkConfigAll } from './checkConfigAll'; let hasErrors = false; console.log(chalk.underline('Checking config "recommended"')); hasErrors = checkConfigRecommended() || hasErrors; +console.log( + chalk.underline('Checking config "recommended-requiring-type-checking"'), +); +hasErrors = checkConfigRecommendedRequiringTypeChecking() || hasErrors; + console.log(); console.log(chalk.underline('Checking config "all"')); hasErrors = checkConfigAll() || hasErrors; diff --git a/packages/eslint-plugin/tools/validate-docs/validate-table-rules.ts b/packages/eslint-plugin/tools/validate-docs/validate-table-rules.ts index c3e712ca525..b85172d0fdc 100644 --- a/packages/eslint-plugin/tools/validate-docs/validate-table-rules.ts +++ b/packages/eslint-plugin/tools/validate-docs/validate-table-rules.ts @@ -105,7 +105,31 @@ function validateTableRules( const ruleFileContents = fs.readFileSync( path.resolve(__dirname, `../../src/rules/${ruleName}.ts`), ); + const usesTypeInformation = ruleFileContents.includes('getParserServices'); + const tableRowHasThoughtBalloon = !!rowNeedsTypeInfo; + if (rule.meta.docs.requiresTypeChecking === true) { + if (!usesTypeInformation) { + errors.push( + 'Rule has `requiresTypeChecking` set in its meta, but it does not actually use type information - fix by removing `meta.docs.requiresTypeChecking`', + ); + } else if (!tableRowHasThoughtBalloon) { + errors.push( + 'Rule was documented as not using type information, when it actually does - fix by updating the plugin README.md', + ); + } + } else { + if (usesTypeInformation) { + errors.push( + 'Rule does not have `requiresTypeChecking` set in its meta, despite using type information - fix by setting `meta.docs.requiresTypeChecking: true` in the rule', + ); + } else if (tableRowHasThoughtBalloon) { + errors.push( + `Rule was documented as using type information, when it actually doesn't - fix by updating the plugin README.md`, + ); + } + } + validateTableBoolean( usesTypeInformation, rowNeedsTypeInfo, diff --git a/packages/experimental-utils/src/ts-eslint/Rule.ts b/packages/experimental-utils/src/ts-eslint/Rule.ts index 89ac7b53288..75acea5988c 100644 --- a/packages/experimental-utils/src/ts-eslint/Rule.ts +++ b/packages/experimental-utils/src/ts-eslint/Rule.ts @@ -32,6 +32,11 @@ interface RuleMetaDataDocs { * The URL of the rule's docs */ url: string; + /** + * Does the rule require us to create a full TypeScript Program in order for it + * to type-check code. This is only used for documentation purposes. + */ + requiresTypeChecking?: boolean; } interface RuleMetaData { /** diff --git a/tests/integration/docker-compose.yml b/tests/integration/docker-compose.yml index 5d10d766177..74bfb63dab2 100644 --- a/tests/integration/docker-compose.yml +++ b/tests/integration/docker-compose.yml @@ -36,3 +36,20 @@ services: - /usr/eslint-plugin/tests # Runtime link to all the specific integration test files, so that most updates don't require a rebuild. - ./fixtures/vue-sfc:/usr/linked + + recommended-does-not-require-program: + build: ./fixtures/recommended-does-not-require-program + container_name: "recommended-does-not-require-program" + volumes: + # Runtime link to the relevant built @typescript-eslint packages and integration test utils, + # but apply an empty volume for the package tests, we don't need those. + - ../../package.json/:/usr/root-package.json + - ./utils/:/usr/utils + - ../../packages/parser/:/usr/parser + - /usr/parser/tests + - ../../packages/typescript-estree/:/usr/typescript-estree + - /usr/typescript-estree/tests + - ../../packages/eslint-plugin/:/usr/eslint-plugin + - /usr/eslint-plugin/tests + # Runtime link to all the specific integration test files, so that most updates don't require a rebuild. + - ./fixtures/recommended-does-not-require-program:/usr/linked diff --git a/tests/integration/fixtures/recommended-does-not-require-program/.eslintrc.yml b/tests/integration/fixtures/recommended-does-not-require-program/.eslintrc.yml new file mode 100644 index 00000000000..75de006c68d --- /dev/null +++ b/tests/integration/fixtures/recommended-does-not-require-program/.eslintrc.yml @@ -0,0 +1,17 @@ +# This integration test exists to make sure that the recommended config does +# not require a program to be specified to ensure a fast and simple initial +# setup. Users can add on one of our other configs if they want to opt in to +# more expensive checks. +root: true + +# Local version of @typescript-eslint/parser +parser: '@typescript-eslint/parser' + +extends: +- 'eslint:recommended' +- 'plugin:@typescript-eslint/eslint-recommended' +- 'plugin:@typescript-eslint/recommended' + +plugins: +# Local version of @typescript-eslint/eslint-plugin +- '@typescript-eslint' diff --git a/tests/integration/fixtures/recommended-does-not-require-program/Dockerfile b/tests/integration/fixtures/recommended-does-not-require-program/Dockerfile new file mode 100644 index 00000000000..3b281e624c8 --- /dev/null +++ b/tests/integration/fixtures/recommended-does-not-require-program/Dockerfile @@ -0,0 +1,17 @@ +FROM node:carbon + +# Copy the test.sh into the container. Every other file will be linked, rather +# than copied to allow for changes without rebuilds wherever possible +WORKDIR /usr +COPY ./test.sh /usr/ + +# Create file which will be executed by jest +# to assert that the lint output is what we expect +RUN echo "const actualLintOutput = require('./lint-output.json');\n" \ + "\n" \ + "test('it should produce the expected lint ouput', () => {\n" \ + " expect(actualLintOutput).toMatchSnapshot();\n" \ + "});\n" > test.js + +# Run the integration test +CMD [ "./test.sh" ] diff --git a/tests/integration/fixtures/recommended-does-not-require-program/index.ts b/tests/integration/fixtures/recommended-does-not-require-program/index.ts new file mode 100644 index 00000000000..838b04b9475 --- /dev/null +++ b/tests/integration/fixtures/recommended-does-not-require-program/index.ts @@ -0,0 +1 @@ +var foo = true diff --git a/tests/integration/fixtures/recommended-does-not-require-program/test.js.snap b/tests/integration/fixtures/recommended-does-not-require-program/test.js.snap new file mode 100644 index 00000000000..36d059733d5 --- /dev/null +++ b/tests/integration/fixtures/recommended-does-not-require-program/test.js.snap @@ -0,0 +1,44 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`it should produce the expected lint ouput 1`] = ` +Array [ + Object { + "errorCount": 1, + "filePath": "/usr/linked/index.ts", + "fixableErrorCount": 1, + "fixableWarningCount": 0, + "messages": Array [ + Object { + "column": 1, + "endColumn": 15, + "endLine": 1, + "fix": Object { + "range": Array [ + 0, + 3, + ], + "text": "let", + }, + "line": 1, + "message": "Unexpected var, use let or const instead.", + "nodeType": "VariableDeclaration", + "ruleId": "no-var", + "severity": 2, + }, + Object { + "column": 5, + "endColumn": 8, + "endLine": 1, + "line": 1, + "message": "'foo' is assigned a value but never used.", + "nodeType": "Identifier", + "ruleId": "@typescript-eslint/no-unused-vars", + "severity": 1, + }, + ], + "source": "var foo = true +", + "warningCount": 1, + }, +] +`; diff --git a/tests/integration/fixtures/recommended-does-not-require-program/test.sh b/tests/integration/fixtures/recommended-does-not-require-program/test.sh new file mode 100755 index 00000000000..1fa77f5cbdf --- /dev/null +++ b/tests/integration/fixtures/recommended-does-not-require-program/test.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +# Generate the package.json to use +node /usr/utils/generate-package-json.js + +# Install dependencies +npm install + +# Use the local volumes for our own packages +npm install $(npm pack /usr/typescript-estree | tail -1) +npm install $(npm pack /usr/parser | tail -1) +npm install $(npm pack /usr/eslint-plugin | tail -1) + +# Run the linting +# (the "|| true" helps make sure that we run our tests on failed linting runs as well) +npx eslint --format json --output-file /usr/lint-output.json --config /usr/linked/.eslintrc.yml /usr/linked/**/*.ts || true + +# Run our assertions against the linting output +npx jest /usr/test.js --snapshotResolver=/usr/utils/jest-snapshot-resolver.js diff --git a/tests/integration/run-all-tests.sh b/tests/integration/run-all-tests.sh index 5b43af06246..4506da77349 100755 --- a/tests/integration/run-all-tests.sh +++ b/tests/integration/run-all-tests.sh @@ -9,3 +9,6 @@ docker-compose -f tests/integration/docker-compose.yml up --build --abort-on-con # vue-sfc docker-compose -f tests/integration/docker-compose.yml up --build --abort-on-container-exit vue-sfc + +# recommended-does-not-require-program +docker-compose -f tests/integration/docker-compose.yml up --build --abort-on-container-exit recommended-does-not-require-program From 0c4f474ccba2fd329cb43ae2309e786b51889a81 Mon Sep 17 00:00:00 2001 From: Pete Gonzalez <4673363+octogonz@users.noreply.github.com> Date: Tue, 13 Aug 2019 12:52:28 -0700 Subject: [PATCH 08/28] feat(eslint-plugin): [interface-name-prefix, class-name-casing] Add allowUnderscorePrefix option to support private declarations (#790) --- .../docs/rules/class-name-casing.md | 16 +++ .../docs/rules/interface-name-prefix.md | 85 +++++++++++-- .../src/rules/class-name-casing.ts | 32 ++++- .../src/rules/interface-name-prefix.ts | 113 ++++++++++++++++-- .../tests/rules/class-name-casing.test.ts | 18 +++ .../tests/rules/interface-name-prefix.test.ts | 63 +++++++++- 6 files changed, 302 insertions(+), 25 deletions(-) diff --git a/packages/eslint-plugin/docs/rules/class-name-casing.md b/packages/eslint-plugin/docs/rules/class-name-casing.md index a1fba58e323..f594a9854f1 100644 --- a/packages/eslint-plugin/docs/rules/class-name-casing.md +++ b/packages/eslint-plugin/docs/rules/class-name-casing.md @@ -5,6 +5,17 @@ This rule enforces PascalCased names for classes and interfaces. ## Rule Details This rule aims to make it easy to differentiate classes from regular variables at a glance. +The `_` prefix is sometimes used to designate a private declaration, so the rule also supports a name +that might be `_Example` instead of `Example`. + +## Options + +This rule has an object option: + +- `"allowUnderscorePrefix": false`: (default) does not allow the name to have an underscore prefix +- `"allowUnderscorePrefix": true`: allows the name to optionally have an underscore prefix + +## Examples Examples of **incorrect** code for this rule: @@ -16,6 +27,8 @@ class Another_Invalid_Class_Name {} var bar = class invalidName {}; interface someInterface {} + +class _InternalClass {} ``` Examples of **correct** code for this rule: @@ -28,6 +41,9 @@ export default class {} var foo = class {}; interface SomeInterface {} + +/* eslint @typescript-eslint/class-name-casing: { "allowUnderscorePrefix": true } */ +class _InternalClass {} ``` ## When Not To Use It diff --git a/packages/eslint-plugin/docs/rules/interface-name-prefix.md b/packages/eslint-plugin/docs/rules/interface-name-prefix.md index c6d907568a1..309dcbe4f24 100644 --- a/packages/eslint-plugin/docs/rules/interface-name-prefix.md +++ b/packages/eslint-plugin/docs/rules/interface-name-prefix.md @@ -1,22 +1,35 @@ # Require that interface names be prefixed with `I` (interface-name-prefix) -It can be hard to differentiate between classes and interfaces. -Prefixing interfaces with "I" can help telling them apart at a glance. +Interfaces often represent important software contracts, so it can be helpful to prefix their names with `I`. +The unprefixed name is then available for a class that provides a standard implementation of the interface. +Alternatively, the contributor guidelines for the TypeScript repo suggest +[never prefixing](https://github.com/Microsoft/TypeScript/wiki/Coding-guidelines#names) interfaces with `I`. ## Rule Details -This rule enforces consistency of interface naming prefix conventions. +This rule enforces whether or not the `I` prefix is required for interface names. +The `_` prefix is sometimes used to designate a private declaration, so the rule also supports a private interface +that might be named `_IAnimal` instead of `IAnimal`. ## Options -This rule has a string option. +This rule has an object option: -- `"never"` (default) disallows all interfaces being prefixed with `"I"` -- `"always"` requires all interfaces be prefixed with `"I"` +- `{ "prefixWithI": "never" }`: (default) disallows all interfaces being prefixed with `"I"` or `"_I"` +- `{ "prefixWithI": "always" }`: requires all interfaces be prefixed with `"I"` (but does not allow `"_I"`) +- `{ "prefixWithI": "always", "allowUnderscorePrefix": true }`: requires all interfaces be prefixed with + either `"I"` or `"_I"` + +For backwards compatibility, this rule supports a string option instead: + +- `"never"`: Equivalent to `{ "prefixWithI": "never" }` +- `"always"`: Equivalent to `{ "prefixWithI": "always" }` + +## Examples ### never -TypeScript suggests [never prefixing](https://github.com/Microsoft/TypeScript/wiki/Coding-guidelines#names) interfaces with "I". +**Configuration:** `{ "prefixWithI": "never" }` The following patterns are considered warnings: @@ -24,6 +37,14 @@ The following patterns are considered warnings: interface IAnimal { name: string; } + +interface IIguana { + name: string; +} + +interface _IAnimal { + name: string; +} ``` The following patterns are not warnings: @@ -32,16 +53,30 @@ The following patterns are not warnings: interface Animal { name: string; } + +interface Iguana { + name: string; +} ``` ### always +**Configuration:** `{ "prefixWithI": "always" }` + The following patterns are considered warnings: ```ts interface Animal { name: string; } + +interface Iguana { + name: string; +} + +interface _IAnimal { + name: string; +} ``` The following patterns are not warnings: @@ -50,6 +85,42 @@ The following patterns are not warnings: interface IAnimal { name: string; } + +interface IIguana { + name: string; +} +``` + +### always and allowing underscores + +**Configuration:** `{ "prefixWithI": "always", "allowUnderscorePrefix": true }` + +The following patterns are considered warnings: + +```ts +interface Animal { + name: string; +} + +interface Iguana { + name: string; +} +``` + +The following patterns are not warnings: + +```ts +interface IAnimal { + name: string; +} + +interface IIguana { + name: string; +} + +interface _IAnimal { + name: string; +} ``` ## When Not To Use It diff --git a/packages/eslint-plugin/src/rules/class-name-casing.ts b/packages/eslint-plugin/src/rules/class-name-casing.ts index cb238f87ac7..c71a39fd2ac 100644 --- a/packages/eslint-plugin/src/rules/class-name-casing.ts +++ b/packages/eslint-plugin/src/rules/class-name-casing.ts @@ -4,7 +4,14 @@ import { } from '@typescript-eslint/experimental-utils'; import * as util from '../util'; -export default util.createRule({ +type Options = [ + { + allowUnderscorePrefix?: boolean; + }, +]; +type MessageIds = 'notPascalCased'; + +export default util.createRule({ name: 'class-name-casing', meta: { type: 'suggestion', @@ -16,16 +23,31 @@ export default util.createRule({ messages: { notPascalCased: "{{friendlyName}} '{{name}}' must be PascalCased.", }, - schema: [], + schema: [ + { + type: 'object', + properties: { + allowUnderscorePrefix: { + type: 'boolean', + default: false, + }, + }, + additionalProperties: false, + }, + ], }, - defaultOptions: [], - create(context) { + defaultOptions: [{ allowUnderscorePrefix: false }], + create(context, [options]) { /** * Determine if the identifier name is PascalCased * @param name The identifier name */ function isPascalCase(name: string): boolean { - return /^[A-Z][0-9A-Za-z]*$/.test(name); + if (options.allowUnderscorePrefix) { + return /^_?[A-Z][0-9A-Za-z]*$/.test(name); + } else { + return /^[A-Z][0-9A-Za-z]*$/.test(name); + } } /** diff --git a/packages/eslint-plugin/src/rules/interface-name-prefix.ts b/packages/eslint-plugin/src/rules/interface-name-prefix.ts index 11976552221..13284fc2aa3 100644 --- a/packages/eslint-plugin/src/rules/interface-name-prefix.ts +++ b/packages/eslint-plugin/src/rules/interface-name-prefix.ts @@ -1,8 +1,43 @@ import * as util from '../util'; -type Options = ['never' | 'always']; +type ParsedOptions = + | { + prefixWithI: 'never'; + } + | { + prefixWithI: 'always'; + allowUnderscorePrefix: boolean; + }; +type Options = [ + + | 'never' + | 'always' + | { + prefixWithI?: 'never'; + } + | { + prefixWithI: 'always'; + allowUnderscorePrefix?: boolean; + }, +]; type MessageIds = 'noPrefix' | 'alwaysPrefix'; +/** + * Parses a given value as options. + */ +export function parseOptions([options]: Options): ParsedOptions { + if (options === 'always') { + return { prefixWithI: 'always', allowUnderscorePrefix: false }; + } + if (options !== 'never' && options.prefixWithI === 'always') { + return { + prefixWithI: 'always', + allowUnderscorePrefix: !!options.allowUnderscorePrefix, + }; + } + return { prefixWithI: 'never' }; +} + export default util.createRule({ name: 'interface-name-prefix', meta: { @@ -21,13 +56,46 @@ export default util.createRule({ }, schema: [ { - enum: ['never', 'always'], + oneOf: [ + { + enum: [ + // Deprecated, equivalent to: { prefixWithI: 'never' } + 'never', + // Deprecated, equivalent to: { prefixWithI: 'always', allowUnderscorePrefix: false } + 'always', + ], + }, + { + type: 'object', + properties: { + prefixWithI: { + type: 'string', + enum: ['never'], + }, + }, + additionalProperties: false, + }, + { + type: 'object', + properties: { + prefixWithI: { + type: 'string', + enum: ['always'], + }, + allowUnderscorePrefix: { + type: 'boolean', + }, + }, + required: ['prefixWithI'], // required to select this "oneOf" alternative + additionalProperties: false, + }, + ], }, ], }, - defaultOptions: ['never'], - create(context, [option]) { - const never = option !== 'always'; + defaultOptions: [{ prefixWithI: 'never' }], + create(context, [options]) { + const parsedOptions = parseOptions([options]); /** * Checks if a string is prefixed with "I". @@ -41,21 +109,42 @@ export default util.createRule({ return /^I[A-Z]/.test(name); } + /** + * Checks if a string is prefixed with "I" or "_I". + * @param name The string to check + */ + function isPrefixedWithIOrUnderscoreI(name: string): boolean { + if (typeof name !== 'string') { + return false; + } + + return /^_?I[A-Z]/.test(name); + } + return { TSInterfaceDeclaration(node): void { - if (never) { - if (isPrefixedWithI(node.id.name)) { + if (parsedOptions.prefixWithI === 'never') { + if (isPrefixedWithIOrUnderscoreI(node.id.name)) { context.report({ node: node.id, messageId: 'noPrefix', }); } } else { - if (!isPrefixedWithI(node.id.name)) { - context.report({ - node: node.id, - messageId: 'alwaysPrefix', - }); + if (parsedOptions.allowUnderscorePrefix) { + if (!isPrefixedWithIOrUnderscoreI(node.id.name)) { + context.report({ + node: node.id, + messageId: 'alwaysPrefix', + }); + } + } else { + if (!isPrefixedWithI(node.id.name)) { + context.report({ + node: node.id, + messageId: 'alwaysPrefix', + }); + } } } }, diff --git a/packages/eslint-plugin/tests/rules/class-name-casing.test.ts b/packages/eslint-plugin/tests/rules/class-name-casing.test.ts index af85e24472f..7409fd927e6 100644 --- a/packages/eslint-plugin/tests/rules/class-name-casing.test.ts +++ b/packages/eslint-plugin/tests/rules/class-name-casing.test.ts @@ -14,6 +14,10 @@ ruleTester.run('class-name-casing', rule, { sourceType: 'module', }, }, + { + code: 'class _NameWithUnderscore {}', + options: [{ allowUnderscorePrefix: true }], + }, 'var Foo = class {};', 'interface SomeInterface {}', 'class ClassNameWithDigit2 {}', @@ -50,6 +54,20 @@ ruleTester.run('class-name-casing', rule, { }, ], }, + { + code: 'class _NameWithUnderscore {}', + errors: [ + { + messageId: 'notPascalCased', + data: { + friendlyName: 'Class', + name: '_NameWithUnderscore', + }, + line: 1, + column: 7, + }, + ], + }, { code: 'var foo = class {};', errors: [ diff --git a/packages/eslint-plugin/tests/rules/interface-name-prefix.test.ts b/packages/eslint-plugin/tests/rules/interface-name-prefix.test.ts index 09cf548c7ac..337a96a368f 100644 --- a/packages/eslint-plugin/tests/rules/interface-name-prefix.test.ts +++ b/packages/eslint-plugin/tests/rules/interface-name-prefix.test.ts @@ -1,6 +1,29 @@ -import rule from '../../src/rules/interface-name-prefix'; +import assert from 'assert'; +import rule, { parseOptions } from '../../src/rules/interface-name-prefix'; import { RuleTester } from '../RuleTester'; +describe('interface-name-prefix', () => { + it('parseOptions', () => { + assert.deepEqual(parseOptions(['never']), { prefixWithI: 'never' }); + assert.deepEqual(parseOptions(['always']), { + prefixWithI: 'always', + allowUnderscorePrefix: false, + }); + assert.deepEqual(parseOptions([{}]), { prefixWithI: 'never' }); + assert.deepEqual(parseOptions([{ prefixWithI: 'never' }]), { + prefixWithI: 'never', + }); + assert.deepEqual(parseOptions([{ prefixWithI: 'always' }]), { + prefixWithI: 'always', + allowUnderscorePrefix: false, + }); + assert.deepEqual( + parseOptions([{ prefixWithI: 'always', allowUnderscorePrefix: true }]), + { prefixWithI: 'always', allowUnderscorePrefix: true }, + ); + }); +}); + const ruleTester = new RuleTester({ parser: '@typescript-eslint/parser', }); @@ -22,6 +45,14 @@ interface IAnimal { }, { code: ` +interface _IAnimal { + name: string; +} + `, + options: [{ prefixWithI: 'always', allowUnderscorePrefix: true }], + }, + { + code: ` interface IIguana { name: string; } @@ -85,6 +116,21 @@ interface Animal { }, { code: ` +interface Animal { + name: string; +} + `, + options: [{ prefixWithI: 'always', allowUnderscorePrefix: true }], + errors: [ + { + messageId: 'alwaysPrefix', + line: 2, + column: 11, + }, + ], + }, + { + code: ` interface Iguana { name: string; } @@ -117,6 +163,21 @@ interface IIguana { code: ` interface IAnimal { name: string; +} + `, + options: ['never'], + errors: [ + { + messageId: 'noPrefix', + line: 2, + column: 11, + }, + ], + }, + { + code: ` +interface _IAnimal { + name: string; } `, options: ['never'], From 0cfc48e1e8a2222a542006361005aa57824c4a4f Mon Sep 17 00:00:00 2001 From: Nathan Date: Tue, 13 Aug 2019 16:30:42 -0400 Subject: [PATCH 09/28] fix(typescript-estree): jsx comment parsing (#703) --- .../tests/lib/__snapshots__/comments.ts.snap | 790 ++++++++++++++++++ .../jsx-tag-comment-after-prop.src.js | 11 + .../typescript-estree/src/convert-comments.ts | 4 +- .../tests/lib/__snapshots__/comments.ts.snap | 790 ++++++++++++++++++ .../semantic-diagnostics-enabled.ts.snap | 2 + 5 files changed, 1596 insertions(+), 1 deletion(-) create mode 100644 packages/shared-fixtures/fixtures/comments/jsx-tag-comment-after-prop.src.js diff --git a/packages/parser/tests/lib/__snapshots__/comments.ts.snap b/packages/parser/tests/lib/__snapshots__/comments.ts.snap index d04a7d39a32..92affb66677 100644 --- a/packages/parser/tests/lib/__snapshots__/comments.ts.snap +++ b/packages/parser/tests/lib/__snapshots__/comments.ts.snap @@ -7282,6 +7282,796 @@ Object { } `; +exports[`Comments fixtures/jsx-tag-comment-after-prop.src 1`] = ` +Object { + "body": Array [ + Object { + "declarations": Array [ + Object { + "id": Object { + "loc": Object { + "end": Object { + "column": 10, + "line": 1, + }, + "start": Object { + "column": 6, + "line": 1, + }, + }, + "name": "pure", + "range": Array [ + 6, + 10, + ], + "type": "Identifier", + }, + "init": Object { + "async": false, + "body": Object { + "body": Array [ + Object { + "argument": Object { + "children": Array [], + "closingElement": null, + "loc": Object { + "end": Object { + "column": 8, + "line": 8, + }, + "start": Object { + "column": 6, + "line": 3, + }, + }, + "openingElement": Object { + "attributes": Array [ + Object { + "loc": Object { + "end": Object { + "column": 17, + "line": 5, + }, + "start": Object { + "column": 8, + "line": 5, + }, + }, + "name": Object { + "loc": Object { + "end": Object { + "column": 11, + "line": 5, + }, + "start": Object { + "column": 8, + "line": 5, + }, + }, + "name": "foo", + "range": Array [ + 66, + 69, + ], + "type": "JSXIdentifier", + }, + "range": Array [ + 66, + 75, + ], + "type": "JSXAttribute", + "value": Object { + "expression": Object { + "loc": Object { + "end": Object { + "column": 16, + "line": 5, + }, + "start": Object { + "column": 13, + "line": 5, + }, + }, + "range": Array [ + 71, + 74, + ], + "raw": "123", + "type": "Literal", + "value": 123, + }, + "loc": Object { + "end": Object { + "column": 17, + "line": 5, + }, + "start": Object { + "column": 12, + "line": 5, + }, + }, + "range": Array [ + 70, + 75, + ], + "type": "JSXExpressionContainer", + }, + }, + Object { + "loc": Object { + "end": Object { + "column": 18, + "line": 7, + }, + "start": Object { + "column": 8, + "line": 7, + }, + }, + "name": Object { + "loc": Object { + "end": Object { + "column": 11, + "line": 7, + }, + "start": Object { + "column": 8, + "line": 7, + }, + }, + "name": "bar", + "range": Array [ + 99, + 102, + ], + "type": "JSXIdentifier", + }, + "range": Array [ + 99, + 109, + ], + "type": "JSXAttribute", + "value": Object { + "loc": Object { + "end": Object { + "column": 18, + "line": 7, + }, + "start": Object { + "column": 12, + "line": 7, + }, + }, + "range": Array [ + 103, + 109, + ], + "raw": "\\"woof\\"", + "type": "Literal", + "value": "woof", + }, + }, + ], + "loc": Object { + "end": Object { + "column": 8, + "line": 8, + }, + "start": Object { + "column": 6, + "line": 3, + }, + }, + "name": Object { + "loc": Object { + "end": Object { + "column": 10, + "line": 3, + }, + "start": Object { + "column": 7, + "line": 3, + }, + }, + "name": "Foo", + "range": Array [ + 39, + 42, + ], + "type": "JSXIdentifier", + }, + "range": Array [ + 38, + 118, + ], + "selfClosing": true, + "type": "JSXOpeningElement", + }, + "range": Array [ + 38, + 118, + ], + "type": "JSXElement", + }, + "loc": Object { + "end": Object { + "column": 4, + "line": 9, + }, + "start": Object { + "column": 2, + "line": 2, + }, + }, + "range": Array [ + 23, + 123, + ], + "type": "ReturnStatement", + }, + ], + "loc": Object { + "end": Object { + "column": 1, + "line": 10, + }, + "start": Object { + "column": 19, + "line": 1, + }, + }, + "range": Array [ + 19, + 125, + ], + "type": "BlockStatement", + }, + "expression": false, + "generator": false, + "id": null, + "loc": Object { + "end": Object { + "column": 1, + "line": 10, + }, + "start": Object { + "column": 13, + "line": 1, + }, + }, + "params": Array [], + "range": Array [ + 13, + 125, + ], + "type": "ArrowFunctionExpression", + }, + "loc": Object { + "end": Object { + "column": 1, + "line": 10, + }, + "start": Object { + "column": 6, + "line": 1, + }, + }, + "range": Array [ + 6, + 125, + ], + "type": "VariableDeclarator", + }, + ], + "kind": "const", + "loc": Object { + "end": Object { + "column": 1, + "line": 10, + }, + "start": Object { + "column": 0, + "line": 1, + }, + }, + "range": Array [ + 0, + 125, + ], + "type": "VariableDeclaration", + }, + ], + "comments": Array [ + Object { + "loc": Object { + "end": Object { + "column": 14, + "line": 4, + }, + "start": Object { + "column": 8, + "line": 4, + }, + }, + "range": Array [ + 51, + 57, + ], + "type": "Line", + "value": " one", + }, + Object { + "loc": Object { + "end": Object { + "column": 14, + "line": 6, + }, + "start": Object { + "column": 8, + "line": 6, + }, + }, + "range": Array [ + 84, + 90, + ], + "type": "Line", + "value": " two", + }, + ], + "loc": Object { + "end": Object { + "column": 0, + "line": 12, + }, + "start": Object { + "column": 0, + "line": 1, + }, + }, + "range": Array [ + 0, + 127, + ], + "sourceType": "module", + "tokens": Array [ + Object { + "loc": Object { + "end": Object { + "column": 5, + "line": 1, + }, + "start": Object { + "column": 0, + "line": 1, + }, + }, + "range": Array [ + 0, + 5, + ], + "type": "Keyword", + "value": "const", + }, + Object { + "loc": Object { + "end": Object { + "column": 10, + "line": 1, + }, + "start": Object { + "column": 6, + "line": 1, + }, + }, + "range": Array [ + 6, + 10, + ], + "type": "Identifier", + "value": "pure", + }, + Object { + "loc": Object { + "end": Object { + "column": 12, + "line": 1, + }, + "start": Object { + "column": 11, + "line": 1, + }, + }, + "range": Array [ + 11, + 12, + ], + "type": "Punctuator", + "value": "=", + }, + Object { + "loc": Object { + "end": Object { + "column": 14, + "line": 1, + }, + "start": Object { + "column": 13, + "line": 1, + }, + }, + "range": Array [ + 13, + 14, + ], + "type": "Punctuator", + "value": "(", + }, + Object { + "loc": Object { + "end": Object { + "column": 15, + "line": 1, + }, + "start": Object { + "column": 14, + "line": 1, + }, + }, + "range": Array [ + 14, + 15, + ], + "type": "Punctuator", + "value": ")", + }, + Object { + "loc": Object { + "end": Object { + "column": 18, + "line": 1, + }, + "start": Object { + "column": 16, + "line": 1, + }, + }, + "range": Array [ + 16, + 18, + ], + "type": "Punctuator", + "value": "=>", + }, + Object { + "loc": Object { + "end": Object { + "column": 20, + "line": 1, + }, + "start": Object { + "column": 19, + "line": 1, + }, + }, + "range": Array [ + 19, + 20, + ], + "type": "Punctuator", + "value": "{", + }, + Object { + "loc": Object { + "end": Object { + "column": 8, + "line": 2, + }, + "start": Object { + "column": 2, + "line": 2, + }, + }, + "range": Array [ + 23, + 29, + ], + "type": "Keyword", + "value": "return", + }, + Object { + "loc": Object { + "end": Object { + "column": 10, + "line": 2, + }, + "start": Object { + "column": 9, + "line": 2, + }, + }, + "range": Array [ + 30, + 31, + ], + "type": "Punctuator", + "value": "(", + }, + Object { + "loc": Object { + "end": Object { + "column": 7, + "line": 3, + }, + "start": Object { + "column": 6, + "line": 3, + }, + }, + "range": Array [ + 38, + 39, + ], + "type": "Punctuator", + "value": "<", + }, + Object { + "loc": Object { + "end": Object { + "column": 10, + "line": 3, + }, + "start": Object { + "column": 7, + "line": 3, + }, + }, + "range": Array [ + 39, + 42, + ], + "type": "JSXIdentifier", + "value": "Foo", + }, + Object { + "loc": Object { + "end": Object { + "column": 11, + "line": 5, + }, + "start": Object { + "column": 8, + "line": 5, + }, + }, + "range": Array [ + 66, + 69, + ], + "type": "JSXIdentifier", + "value": "foo", + }, + Object { + "loc": Object { + "end": Object { + "column": 12, + "line": 5, + }, + "start": Object { + "column": 11, + "line": 5, + }, + }, + "range": Array [ + 69, + 70, + ], + "type": "Punctuator", + "value": "=", + }, + Object { + "loc": Object { + "end": Object { + "column": 13, + "line": 5, + }, + "start": Object { + "column": 12, + "line": 5, + }, + }, + "range": Array [ + 70, + 71, + ], + "type": "Punctuator", + "value": "{", + }, + Object { + "loc": Object { + "end": Object { + "column": 16, + "line": 5, + }, + "start": Object { + "column": 13, + "line": 5, + }, + }, + "range": Array [ + 71, + 74, + ], + "type": "Numeric", + "value": "123", + }, + Object { + "loc": Object { + "end": Object { + "column": 17, + "line": 5, + }, + "start": Object { + "column": 16, + "line": 5, + }, + }, + "range": Array [ + 74, + 75, + ], + "type": "Punctuator", + "value": "}", + }, + Object { + "loc": Object { + "end": Object { + "column": 11, + "line": 7, + }, + "start": Object { + "column": 8, + "line": 7, + }, + }, + "range": Array [ + 99, + 102, + ], + "type": "JSXIdentifier", + "value": "bar", + }, + Object { + "loc": Object { + "end": Object { + "column": 12, + "line": 7, + }, + "start": Object { + "column": 11, + "line": 7, + }, + }, + "range": Array [ + 102, + 103, + ], + "type": "Punctuator", + "value": "=", + }, + Object { + "loc": Object { + "end": Object { + "column": 18, + "line": 7, + }, + "start": Object { + "column": 12, + "line": 7, + }, + }, + "range": Array [ + 103, + 109, + ], + "type": "JSXText", + "value": "\\"woof\\"", + }, + Object { + "loc": Object { + "end": Object { + "column": 7, + "line": 8, + }, + "start": Object { + "column": 6, + "line": 8, + }, + }, + "range": Array [ + 116, + 117, + ], + "type": "Punctuator", + "value": "/", + }, + Object { + "loc": Object { + "end": Object { + "column": 8, + "line": 8, + }, + "start": Object { + "column": 7, + "line": 8, + }, + }, + "range": Array [ + 117, + 118, + ], + "type": "Punctuator", + "value": ">", + }, + Object { + "loc": Object { + "end": Object { + "column": 3, + "line": 9, + }, + "start": Object { + "column": 2, + "line": 9, + }, + }, + "range": Array [ + 121, + 122, + ], + "type": "Punctuator", + "value": ")", + }, + Object { + "loc": Object { + "end": Object { + "column": 4, + "line": 9, + }, + "start": Object { + "column": 3, + "line": 9, + }, + }, + "range": Array [ + 122, + 123, + ], + "type": "Punctuator", + "value": ";", + }, + Object { + "loc": Object { + "end": Object { + "column": 1, + "line": 10, + }, + "start": Object { + "column": 0, + "line": 10, + }, + }, + "range": Array [ + 124, + 125, + ], + "type": "Punctuator", + "value": "}", + }, + ], + "type": "Program", +} +`; + exports[`Comments fixtures/jsx-tag-comments.src 1`] = ` Object { "body": Array [ diff --git a/packages/shared-fixtures/fixtures/comments/jsx-tag-comment-after-prop.src.js b/packages/shared-fixtures/fixtures/comments/jsx-tag-comment-after-prop.src.js new file mode 100644 index 00000000000..f6321a0a0c2 --- /dev/null +++ b/packages/shared-fixtures/fixtures/comments/jsx-tag-comment-after-prop.src.js @@ -0,0 +1,11 @@ +const pure = () => { + return ( + + ); +} + diff --git a/packages/typescript-estree/src/convert-comments.ts b/packages/typescript-estree/src/convert-comments.ts index 159c24cf3ef..d3738774cd9 100644 --- a/packages/typescript-estree/src/convert-comments.ts +++ b/packages/typescript-estree/src/convert-comments.ts @@ -147,7 +147,9 @@ export function convertComments( // Rescan after a JSX expression if ( container.parent && - container.parent.kind === ts.SyntaxKind.JsxExpression + container.parent.kind === ts.SyntaxKind.JsxExpression && + container.parent.parent && + container.parent.parent.kind === ts.SyntaxKind.JsxElement ) { kind = triviaScanner.reScanJsxToken(); continue; diff --git a/packages/typescript-estree/tests/lib/__snapshots__/comments.ts.snap b/packages/typescript-estree/tests/lib/__snapshots__/comments.ts.snap index e143fdb8524..b90f5eb4548 100644 --- a/packages/typescript-estree/tests/lib/__snapshots__/comments.ts.snap +++ b/packages/typescript-estree/tests/lib/__snapshots__/comments.ts.snap @@ -7282,6 +7282,796 @@ Object { } `; +exports[`Comments fixtures/jsx-tag-comment-after-prop.src 1`] = ` +Object { + "body": Array [ + Object { + "declarations": Array [ + Object { + "id": Object { + "loc": Object { + "end": Object { + "column": 10, + "line": 1, + }, + "start": Object { + "column": 6, + "line": 1, + }, + }, + "name": "pure", + "range": Array [ + 6, + 10, + ], + "type": "Identifier", + }, + "init": Object { + "async": false, + "body": Object { + "body": Array [ + Object { + "argument": Object { + "children": Array [], + "closingElement": null, + "loc": Object { + "end": Object { + "column": 8, + "line": 8, + }, + "start": Object { + "column": 6, + "line": 3, + }, + }, + "openingElement": Object { + "attributes": Array [ + Object { + "loc": Object { + "end": Object { + "column": 17, + "line": 5, + }, + "start": Object { + "column": 8, + "line": 5, + }, + }, + "name": Object { + "loc": Object { + "end": Object { + "column": 11, + "line": 5, + }, + "start": Object { + "column": 8, + "line": 5, + }, + }, + "name": "foo", + "range": Array [ + 66, + 69, + ], + "type": "JSXIdentifier", + }, + "range": Array [ + 66, + 75, + ], + "type": "JSXAttribute", + "value": Object { + "expression": Object { + "loc": Object { + "end": Object { + "column": 16, + "line": 5, + }, + "start": Object { + "column": 13, + "line": 5, + }, + }, + "range": Array [ + 71, + 74, + ], + "raw": "123", + "type": "Literal", + "value": 123, + }, + "loc": Object { + "end": Object { + "column": 17, + "line": 5, + }, + "start": Object { + "column": 12, + "line": 5, + }, + }, + "range": Array [ + 70, + 75, + ], + "type": "JSXExpressionContainer", + }, + }, + Object { + "loc": Object { + "end": Object { + "column": 18, + "line": 7, + }, + "start": Object { + "column": 8, + "line": 7, + }, + }, + "name": Object { + "loc": Object { + "end": Object { + "column": 11, + "line": 7, + }, + "start": Object { + "column": 8, + "line": 7, + }, + }, + "name": "bar", + "range": Array [ + 99, + 102, + ], + "type": "JSXIdentifier", + }, + "range": Array [ + 99, + 109, + ], + "type": "JSXAttribute", + "value": Object { + "loc": Object { + "end": Object { + "column": 18, + "line": 7, + }, + "start": Object { + "column": 12, + "line": 7, + }, + }, + "range": Array [ + 103, + 109, + ], + "raw": "\\"woof\\"", + "type": "Literal", + "value": "woof", + }, + }, + ], + "loc": Object { + "end": Object { + "column": 8, + "line": 8, + }, + "start": Object { + "column": 6, + "line": 3, + }, + }, + "name": Object { + "loc": Object { + "end": Object { + "column": 10, + "line": 3, + }, + "start": Object { + "column": 7, + "line": 3, + }, + }, + "name": "Foo", + "range": Array [ + 39, + 42, + ], + "type": "JSXIdentifier", + }, + "range": Array [ + 38, + 118, + ], + "selfClosing": true, + "type": "JSXOpeningElement", + }, + "range": Array [ + 38, + 118, + ], + "type": "JSXElement", + }, + "loc": Object { + "end": Object { + "column": 4, + "line": 9, + }, + "start": Object { + "column": 2, + "line": 2, + }, + }, + "range": Array [ + 23, + 123, + ], + "type": "ReturnStatement", + }, + ], + "loc": Object { + "end": Object { + "column": 1, + "line": 10, + }, + "start": Object { + "column": 19, + "line": 1, + }, + }, + "range": Array [ + 19, + 125, + ], + "type": "BlockStatement", + }, + "expression": false, + "generator": false, + "id": null, + "loc": Object { + "end": Object { + "column": 1, + "line": 10, + }, + "start": Object { + "column": 13, + "line": 1, + }, + }, + "params": Array [], + "range": Array [ + 13, + 125, + ], + "type": "ArrowFunctionExpression", + }, + "loc": Object { + "end": Object { + "column": 1, + "line": 10, + }, + "start": Object { + "column": 6, + "line": 1, + }, + }, + "range": Array [ + 6, + 125, + ], + "type": "VariableDeclarator", + }, + ], + "kind": "const", + "loc": Object { + "end": Object { + "column": 1, + "line": 10, + }, + "start": Object { + "column": 0, + "line": 1, + }, + }, + "range": Array [ + 0, + 125, + ], + "type": "VariableDeclaration", + }, + ], + "comments": Array [ + Object { + "loc": Object { + "end": Object { + "column": 14, + "line": 4, + }, + "start": Object { + "column": 8, + "line": 4, + }, + }, + "range": Array [ + 51, + 57, + ], + "type": "Line", + "value": " one", + }, + Object { + "loc": Object { + "end": Object { + "column": 14, + "line": 6, + }, + "start": Object { + "column": 8, + "line": 6, + }, + }, + "range": Array [ + 84, + 90, + ], + "type": "Line", + "value": " two", + }, + ], + "loc": Object { + "end": Object { + "column": 0, + "line": 12, + }, + "start": Object { + "column": 0, + "line": 1, + }, + }, + "range": Array [ + 0, + 127, + ], + "sourceType": "script", + "tokens": Array [ + Object { + "loc": Object { + "end": Object { + "column": 5, + "line": 1, + }, + "start": Object { + "column": 0, + "line": 1, + }, + }, + "range": Array [ + 0, + 5, + ], + "type": "Keyword", + "value": "const", + }, + Object { + "loc": Object { + "end": Object { + "column": 10, + "line": 1, + }, + "start": Object { + "column": 6, + "line": 1, + }, + }, + "range": Array [ + 6, + 10, + ], + "type": "Identifier", + "value": "pure", + }, + Object { + "loc": Object { + "end": Object { + "column": 12, + "line": 1, + }, + "start": Object { + "column": 11, + "line": 1, + }, + }, + "range": Array [ + 11, + 12, + ], + "type": "Punctuator", + "value": "=", + }, + Object { + "loc": Object { + "end": Object { + "column": 14, + "line": 1, + }, + "start": Object { + "column": 13, + "line": 1, + }, + }, + "range": Array [ + 13, + 14, + ], + "type": "Punctuator", + "value": "(", + }, + Object { + "loc": Object { + "end": Object { + "column": 15, + "line": 1, + }, + "start": Object { + "column": 14, + "line": 1, + }, + }, + "range": Array [ + 14, + 15, + ], + "type": "Punctuator", + "value": ")", + }, + Object { + "loc": Object { + "end": Object { + "column": 18, + "line": 1, + }, + "start": Object { + "column": 16, + "line": 1, + }, + }, + "range": Array [ + 16, + 18, + ], + "type": "Punctuator", + "value": "=>", + }, + Object { + "loc": Object { + "end": Object { + "column": 20, + "line": 1, + }, + "start": Object { + "column": 19, + "line": 1, + }, + }, + "range": Array [ + 19, + 20, + ], + "type": "Punctuator", + "value": "{", + }, + Object { + "loc": Object { + "end": Object { + "column": 8, + "line": 2, + }, + "start": Object { + "column": 2, + "line": 2, + }, + }, + "range": Array [ + 23, + 29, + ], + "type": "Keyword", + "value": "return", + }, + Object { + "loc": Object { + "end": Object { + "column": 10, + "line": 2, + }, + "start": Object { + "column": 9, + "line": 2, + }, + }, + "range": Array [ + 30, + 31, + ], + "type": "Punctuator", + "value": "(", + }, + Object { + "loc": Object { + "end": Object { + "column": 7, + "line": 3, + }, + "start": Object { + "column": 6, + "line": 3, + }, + }, + "range": Array [ + 38, + 39, + ], + "type": "Punctuator", + "value": "<", + }, + Object { + "loc": Object { + "end": Object { + "column": 10, + "line": 3, + }, + "start": Object { + "column": 7, + "line": 3, + }, + }, + "range": Array [ + 39, + 42, + ], + "type": "JSXIdentifier", + "value": "Foo", + }, + Object { + "loc": Object { + "end": Object { + "column": 11, + "line": 5, + }, + "start": Object { + "column": 8, + "line": 5, + }, + }, + "range": Array [ + 66, + 69, + ], + "type": "JSXIdentifier", + "value": "foo", + }, + Object { + "loc": Object { + "end": Object { + "column": 12, + "line": 5, + }, + "start": Object { + "column": 11, + "line": 5, + }, + }, + "range": Array [ + 69, + 70, + ], + "type": "Punctuator", + "value": "=", + }, + Object { + "loc": Object { + "end": Object { + "column": 13, + "line": 5, + }, + "start": Object { + "column": 12, + "line": 5, + }, + }, + "range": Array [ + 70, + 71, + ], + "type": "Punctuator", + "value": "{", + }, + Object { + "loc": Object { + "end": Object { + "column": 16, + "line": 5, + }, + "start": Object { + "column": 13, + "line": 5, + }, + }, + "range": Array [ + 71, + 74, + ], + "type": "Numeric", + "value": "123", + }, + Object { + "loc": Object { + "end": Object { + "column": 17, + "line": 5, + }, + "start": Object { + "column": 16, + "line": 5, + }, + }, + "range": Array [ + 74, + 75, + ], + "type": "Punctuator", + "value": "}", + }, + Object { + "loc": Object { + "end": Object { + "column": 11, + "line": 7, + }, + "start": Object { + "column": 8, + "line": 7, + }, + }, + "range": Array [ + 99, + 102, + ], + "type": "JSXIdentifier", + "value": "bar", + }, + Object { + "loc": Object { + "end": Object { + "column": 12, + "line": 7, + }, + "start": Object { + "column": 11, + "line": 7, + }, + }, + "range": Array [ + 102, + 103, + ], + "type": "Punctuator", + "value": "=", + }, + Object { + "loc": Object { + "end": Object { + "column": 18, + "line": 7, + }, + "start": Object { + "column": 12, + "line": 7, + }, + }, + "range": Array [ + 103, + 109, + ], + "type": "JSXText", + "value": "\\"woof\\"", + }, + Object { + "loc": Object { + "end": Object { + "column": 7, + "line": 8, + }, + "start": Object { + "column": 6, + "line": 8, + }, + }, + "range": Array [ + 116, + 117, + ], + "type": "Punctuator", + "value": "/", + }, + Object { + "loc": Object { + "end": Object { + "column": 8, + "line": 8, + }, + "start": Object { + "column": 7, + "line": 8, + }, + }, + "range": Array [ + 117, + 118, + ], + "type": "Punctuator", + "value": ">", + }, + Object { + "loc": Object { + "end": Object { + "column": 3, + "line": 9, + }, + "start": Object { + "column": 2, + "line": 9, + }, + }, + "range": Array [ + 121, + 122, + ], + "type": "Punctuator", + "value": ")", + }, + Object { + "loc": Object { + "end": Object { + "column": 4, + "line": 9, + }, + "start": Object { + "column": 3, + "line": 9, + }, + }, + "range": Array [ + 122, + 123, + ], + "type": "Punctuator", + "value": ";", + }, + Object { + "loc": Object { + "end": Object { + "column": 1, + "line": 10, + }, + "start": Object { + "column": 0, + "line": 10, + }, + }, + "range": Array [ + 124, + 125, + ], + "type": "Punctuator", + "value": "}", + }, + ], + "type": "Program", +} +`; + exports[`Comments fixtures/jsx-tag-comments.src 1`] = ` Object { "body": Array [ diff --git a/packages/typescript-estree/tests/lib/__snapshots__/semantic-diagnostics-enabled.ts.snap b/packages/typescript-estree/tests/lib/__snapshots__/semantic-diagnostics-enabled.ts.snap index 7af41c0f302..b4f79cd04d1 100644 --- a/packages/typescript-estree/tests/lib/__snapshots__/semantic-diagnostics-enabled.ts.snap +++ b/packages/typescript-estree/tests/lib/__snapshots__/semantic-diagnostics-enabled.ts.snap @@ -18,6 +18,8 @@ exports[`Parse all fixtures with "errorOnTypeScriptSyntacticAndSemanticIssues" e exports[`Parse all fixtures with "errorOnTypeScriptSyntacticAndSemanticIssues" enabled fixtures/comments/jsx-generic-with-comment-in-tag.src 1`] = `"TEST OUTPUT: No semantic or syntactic issues found"`; +exports[`Parse all fixtures with "errorOnTypeScriptSyntacticAndSemanticIssues" enabled fixtures/comments/jsx-tag-comment-after-prop.src 1`] = `"TEST OUTPUT: No semantic or syntactic issues found"`; + exports[`Parse all fixtures with "errorOnTypeScriptSyntacticAndSemanticIssues" enabled fixtures/comments/jsx-tag-comments.src 1`] = `"TEST OUTPUT: No semantic or syntactic issues found"`; exports[`Parse all fixtures with "errorOnTypeScriptSyntacticAndSemanticIssues" enabled fixtures/comments/jsx-text-with-multiline-non-comment.src 1`] = `"TEST OUTPUT: No semantic or syntactic issues found"`; From 05ba26879dd5a5a0e1159951c8b24dc5e0e5cc4a Mon Sep 17 00:00:00 2001 From: James Henry Date: Tue, 13 Aug 2019 21:31:17 +0000 Subject: [PATCH 10/28] chore: publish v2.0.0 --- CHANGELOG.md | 66 ++++++++++++++++++++++ lerna.json | 2 +- packages/eslint-plugin-tslint/CHANGELOG.md | 22 ++++++++ packages/eslint-plugin-tslint/package.json | 6 +- packages/eslint-plugin/CHANGELOG.md | 62 ++++++++++++++++++++ packages/eslint-plugin/package.json | 4 +- packages/experimental-utils/CHANGELOG.md | 30 ++++++++++ packages/experimental-utils/package.json | 4 +- packages/parser/CHANGELOG.md | 28 +++++++++ packages/parser/package.json | 8 +-- packages/shared-fixtures/CHANGELOG.md | 12 ++++ packages/shared-fixtures/package.json | 2 +- packages/typescript-estree/CHANGELOG.md | 30 ++++++++++ packages/typescript-estree/package.json | 4 +- 14 files changed, 265 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ff90c01198b..306e1582021 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,72 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [2.0.0](https://github.com/typescript-eslint/typescript-eslint/compare/v1.13.0...v2.0.0) (2019-08-13) + + +* feat(eslint-plugin)!: recommended-requiring-type-checking config (#846) ([d3470c9](https://github.com/typescript-eslint/typescript-eslint/commit/d3470c9)), closes [#846](https://github.com/typescript-eslint/typescript-eslint/issues/846) +* feat(eslint-plugin)!: change recommended config (#729) ([428567d](https://github.com/typescript-eslint/typescript-eslint/commit/428567d)), closes [#729](https://github.com/typescript-eslint/typescript-eslint/issues/729) +* feat(typescript-estree)!: throw error on file not in project when `project` set (#760) ([3777b77](https://github.com/typescript-eslint/typescript-eslint/commit/3777b77)), closes [#760](https://github.com/typescript-eslint/typescript-eslint/issues/760) +* feat(eslint-plugin)!: add rule `consistent-type-assertions` (#731) ([92e98de](https://github.com/typescript-eslint/typescript-eslint/commit/92e98de)), closes [#731](https://github.com/typescript-eslint/typescript-eslint/issues/731) +* feat(eslint-plugin)!: [array-type] rework options (#654) ([1389393](https://github.com/typescript-eslint/typescript-eslint/commit/1389393)), closes [#654](https://github.com/typescript-eslint/typescript-eslint/issues/654) [#635](https://github.com/typescript-eslint/typescript-eslint/issues/635) + + +### Bug Fixes + +* **eslint-plugin:** [efrt] flag default export w/allowExpressions ([#831](https://github.com/typescript-eslint/typescript-eslint/issues/831)) ([ebbcc01](https://github.com/typescript-eslint/typescript-eslint/commit/ebbcc01)) +* **eslint-plugin:** [no-explicit-any] Fix ignoreRestArgs for interfaces ([#777](https://github.com/typescript-eslint/typescript-eslint/issues/777)) ([22e9ae5](https://github.com/typescript-eslint/typescript-eslint/commit/22e9ae5)) +* **eslint-plugin:** [no-useless-constructor] handle bodyless constructor ([#685](https://github.com/typescript-eslint/typescript-eslint/issues/685)) ([55e788c](https://github.com/typescript-eslint/typescript-eslint/commit/55e788c)) +* **eslint-plugin:** [prefer-readonly] TypeError when having comp… ([#761](https://github.com/typescript-eslint/typescript-eslint/issues/761)) ([211b1b5](https://github.com/typescript-eslint/typescript-eslint/commit/211b1b5)) +* **eslint-plugin:** [typedef] support "for..in", "for..of" ([#787](https://github.com/typescript-eslint/typescript-eslint/issues/787)) ([39e41b5](https://github.com/typescript-eslint/typescript-eslint/commit/39e41b5)) +* **eslint-plugin:** [typedef] support default value for parameter ([#785](https://github.com/typescript-eslint/typescript-eslint/issues/785)) ([84916e6](https://github.com/typescript-eslint/typescript-eslint/commit/84916e6)) +* **eslint-plugin:** add `Literal` to `RuleListener` types ([#824](https://github.com/typescript-eslint/typescript-eslint/issues/824)) ([3c902a1](https://github.com/typescript-eslint/typescript-eslint/commit/3c902a1)) +* **typescript-estree:** fix `is` token typed as `Keyword ([#750](https://github.com/typescript-eslint/typescript-eslint/issues/750)) ([35dec52](https://github.com/typescript-eslint/typescript-eslint/commit/35dec52)) +* **typescript-estree:** jsx comment parsing ([#703](https://github.com/typescript-eslint/typescript-eslint/issues/703)) ([0cfc48e](https://github.com/typescript-eslint/typescript-eslint/commit/0cfc48e)) +* **utils:** add ES2019 as valid `ecmaVersion` ([#746](https://github.com/typescript-eslint/typescript-eslint/issues/746)) ([d11fbbe](https://github.com/typescript-eslint/typescript-eslint/commit/d11fbbe)) + + +### Features + +* explicitly support eslint v6 ([#645](https://github.com/typescript-eslint/typescript-eslint/issues/645)) ([34a7cf6](https://github.com/typescript-eslint/typescript-eslint/commit/34a7cf6)) +* **eslint-plugin:** [interface-name-prefix, class-name-casing] Add allowUnderscorePrefix option to support private declarations ([#790](https://github.com/typescript-eslint/typescript-eslint/issues/790)) ([0c4f474](https://github.com/typescript-eslint/typescript-eslint/commit/0c4f474)) +* **eslint-plugin:** [no-var-requires] report on foo(require('')) ([#725](https://github.com/typescript-eslint/typescript-eslint/issues/725)) ([b2ca20d](https://github.com/typescript-eslint/typescript-eslint/commit/b2ca20d)), closes [#665](https://github.com/typescript-eslint/typescript-eslint/issues/665) +* **eslint-plugin:** [promise-function-async] make allowAny default true ([#733](https://github.com/typescript-eslint/typescript-eslint/issues/733)) ([590ca50](https://github.com/typescript-eslint/typescript-eslint/commit/590ca50)) +* **eslint-plugin:** [strict-boolean-expressions] add ignoreRhs option ([#691](https://github.com/typescript-eslint/typescript-eslint/issues/691)) ([fd6be42](https://github.com/typescript-eslint/typescript-eslint/commit/fd6be42)) +* **eslint-plugin:** add support for object props in CallExpressions ([#728](https://github.com/typescript-eslint/typescript-eslint/issues/728)) ([8141f01](https://github.com/typescript-eslint/typescript-eslint/commit/8141f01)) +* **eslint-plugin:** added new rule typedef ([#581](https://github.com/typescript-eslint/typescript-eslint/issues/581)) ([35cc99b](https://github.com/typescript-eslint/typescript-eslint/commit/35cc99b)) +* **eslint-plugin:** added new rule use-default-type-parameter ([#562](https://github.com/typescript-eslint/typescript-eslint/issues/562)) ([2b942ba](https://github.com/typescript-eslint/typescript-eslint/commit/2b942ba)) +* **eslint-plugin:** move opinionated rules between configs ([#595](https://github.com/typescript-eslint/typescript-eslint/issues/595)) ([4893aec](https://github.com/typescript-eslint/typescript-eslint/commit/4893aec)) +* **eslint-plugin:** remove deprecated rules ([#739](https://github.com/typescript-eslint/typescript-eslint/issues/739)) ([e32c7ad](https://github.com/typescript-eslint/typescript-eslint/commit/e32c7ad)) + + +### BREAKING CHANGES + +* removed some rules from recommended config +* recommended config changes are considered breaking +* by default we will now throw when a file is not in the `project` provided +* Merges both no-angle-bracket-type-assertion and no-object-literal-type-assertion into one rule +* **eslint-plugin:** both 'eslint-recommended' and 'recommended' have changed. +* **eslint-plugin:** removing rules +* changes config structure + +```ts +type ArrayOption = 'array' | 'generic' | 'array-simple'; +type Options = [ + { + // default case for all arrays + default: ArrayOption, + // optional override for readonly arrays + readonly?: ArrayOption, + }, +]; +``` +* **eslint-plugin:** changing default rule config +* Node 6 is no longer supported + + + + + # [1.13.0](https://github.com/typescript-eslint/typescript-eslint/compare/v1.12.0...v1.13.0) (2019-07-21) diff --git a/lerna.json b/lerna.json index b700c7342eb..e4b8a907880 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "1.13.0", + "version": "2.0.0", "npmClient": "yarn", "useWorkspaces": true, "stream": true diff --git a/packages/eslint-plugin-tslint/CHANGELOG.md b/packages/eslint-plugin-tslint/CHANGELOG.md index fc36ec03175..1339f31787c 100644 --- a/packages/eslint-plugin-tslint/CHANGELOG.md +++ b/packages/eslint-plugin-tslint/CHANGELOG.md @@ -3,6 +3,28 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [2.0.0](https://github.com/typescript-eslint/typescript-eslint/compare/v1.13.0...v2.0.0) (2019-08-13) + + +### Features + +* explicitly support eslint v6 ([#645](https://github.com/typescript-eslint/typescript-eslint/issues/645)) ([34a7cf6](https://github.com/typescript-eslint/typescript-eslint/commit/34a7cf6)) + + +* feat(eslint-plugin)!: change recommended config (#729) ([428567d](https://github.com/typescript-eslint/typescript-eslint/commit/428567d)), closes [#729](https://github.com/typescript-eslint/typescript-eslint/issues/729) +* feat(typescript-estree)!: throw error on file not in project when `project` set (#760) ([3777b77](https://github.com/typescript-eslint/typescript-eslint/commit/3777b77)), closes [#760](https://github.com/typescript-eslint/typescript-eslint/issues/760) + + +### BREAKING CHANGES + +* recommended config changes are considered breaking +* by default we will now throw when a file is not in the `project` provided +* Node 6 is no longer supported + + + + + # [1.13.0](https://github.com/typescript-eslint/typescript-eslint/compare/v1.12.0...v1.13.0) (2019-07-21) diff --git a/packages/eslint-plugin-tslint/package.json b/packages/eslint-plugin-tslint/package.json index bf40809b2db..a35949dc244 100644 --- a/packages/eslint-plugin-tslint/package.json +++ b/packages/eslint-plugin-tslint/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/eslint-plugin-tslint", - "version": "1.13.0", + "version": "2.0.0", "main": "dist/index.js", "typings": "src/index.ts", "description": "TSLint wrapper plugin for ESLint", @@ -31,7 +31,7 @@ "typecheck": "tsc --noEmit" }, "dependencies": { - "@typescript-eslint/experimental-utils": "1.13.0", + "@typescript-eslint/experimental-utils": "2.0.0", "lodash.memoize": "^4.1.2" }, "peerDependencies": { @@ -41,6 +41,6 @@ }, "devDependencies": { "@types/lodash.memoize": "^4.1.4", - "@typescript-eslint/parser": "1.13.0" + "@typescript-eslint/parser": "2.0.0" } } diff --git a/packages/eslint-plugin/CHANGELOG.md b/packages/eslint-plugin/CHANGELOG.md index b7ff8b2d6ac..ac4b1f894c4 100644 --- a/packages/eslint-plugin/CHANGELOG.md +++ b/packages/eslint-plugin/CHANGELOG.md @@ -3,6 +3,68 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [2.0.0](https://github.com/typescript-eslint/typescript-eslint/compare/v1.13.0...v2.0.0) (2019-08-13) + + +### Bug Fixes + +* **eslint-plugin:** [efrt] flag default export w/allowExpressions ([#831](https://github.com/typescript-eslint/typescript-eslint/issues/831)) ([ebbcc01](https://github.com/typescript-eslint/typescript-eslint/commit/ebbcc01)) +* **eslint-plugin:** [no-explicit-any] Fix ignoreRestArgs for interfaces ([#777](https://github.com/typescript-eslint/typescript-eslint/issues/777)) ([22e9ae5](https://github.com/typescript-eslint/typescript-eslint/commit/22e9ae5)) +* **eslint-plugin:** [no-useless-constructor] handle bodyless constructor ([#685](https://github.com/typescript-eslint/typescript-eslint/issues/685)) ([55e788c](https://github.com/typescript-eslint/typescript-eslint/commit/55e788c)) +* **eslint-plugin:** [prefer-readonly] TypeError when having comp… ([#761](https://github.com/typescript-eslint/typescript-eslint/issues/761)) ([211b1b5](https://github.com/typescript-eslint/typescript-eslint/commit/211b1b5)) +* **eslint-plugin:** [typedef] support "for..in", "for..of" ([#787](https://github.com/typescript-eslint/typescript-eslint/issues/787)) ([39e41b5](https://github.com/typescript-eslint/typescript-eslint/commit/39e41b5)) +* **eslint-plugin:** [typedef] support default value for parameter ([#785](https://github.com/typescript-eslint/typescript-eslint/issues/785)) ([84916e6](https://github.com/typescript-eslint/typescript-eslint/commit/84916e6)) + + +* feat(eslint-plugin)!: recommended-requiring-type-checking config (#846) ([d3470c9](https://github.com/typescript-eslint/typescript-eslint/commit/d3470c9)), closes [#846](https://github.com/typescript-eslint/typescript-eslint/issues/846) +* feat(eslint-plugin)!: change recommended config (#729) ([428567d](https://github.com/typescript-eslint/typescript-eslint/commit/428567d)), closes [#729](https://github.com/typescript-eslint/typescript-eslint/issues/729) +* feat(typescript-estree)!: throw error on file not in project when `project` set (#760) ([3777b77](https://github.com/typescript-eslint/typescript-eslint/commit/3777b77)), closes [#760](https://github.com/typescript-eslint/typescript-eslint/issues/760) +* feat(eslint-plugin)!: add rule `consistent-type-assertions` (#731) ([92e98de](https://github.com/typescript-eslint/typescript-eslint/commit/92e98de)), closes [#731](https://github.com/typescript-eslint/typescript-eslint/issues/731) +* feat(eslint-plugin)!: [array-type] rework options (#654) ([1389393](https://github.com/typescript-eslint/typescript-eslint/commit/1389393)), closes [#654](https://github.com/typescript-eslint/typescript-eslint/issues/654) [#635](https://github.com/typescript-eslint/typescript-eslint/issues/635) + + +### Features + +* explicitly support eslint v6 ([#645](https://github.com/typescript-eslint/typescript-eslint/issues/645)) ([34a7cf6](https://github.com/typescript-eslint/typescript-eslint/commit/34a7cf6)) +* **eslint-plugin:** [interface-name-prefix, class-name-casing] Add allowUnderscorePrefix option to support private declarations ([#790](https://github.com/typescript-eslint/typescript-eslint/issues/790)) ([0c4f474](https://github.com/typescript-eslint/typescript-eslint/commit/0c4f474)) +* **eslint-plugin:** [no-var-requires] report on foo(require('')) ([#725](https://github.com/typescript-eslint/typescript-eslint/issues/725)) ([b2ca20d](https://github.com/typescript-eslint/typescript-eslint/commit/b2ca20d)), closes [#665](https://github.com/typescript-eslint/typescript-eslint/issues/665) +* **eslint-plugin:** [promise-function-async] make allowAny default true ([#733](https://github.com/typescript-eslint/typescript-eslint/issues/733)) ([590ca50](https://github.com/typescript-eslint/typescript-eslint/commit/590ca50)) +* **eslint-plugin:** [strict-boolean-expressions] add ignoreRhs option ([#691](https://github.com/typescript-eslint/typescript-eslint/issues/691)) ([fd6be42](https://github.com/typescript-eslint/typescript-eslint/commit/fd6be42)) +* **eslint-plugin:** add support for object props in CallExpressions ([#728](https://github.com/typescript-eslint/typescript-eslint/issues/728)) ([8141f01](https://github.com/typescript-eslint/typescript-eslint/commit/8141f01)) +* **eslint-plugin:** added new rule typedef ([#581](https://github.com/typescript-eslint/typescript-eslint/issues/581)) ([35cc99b](https://github.com/typescript-eslint/typescript-eslint/commit/35cc99b)) +* **eslint-plugin:** added new rule use-default-type-parameter ([#562](https://github.com/typescript-eslint/typescript-eslint/issues/562)) ([2b942ba](https://github.com/typescript-eslint/typescript-eslint/commit/2b942ba)) +* **eslint-plugin:** move opinionated rules between configs ([#595](https://github.com/typescript-eslint/typescript-eslint/issues/595)) ([4893aec](https://github.com/typescript-eslint/typescript-eslint/commit/4893aec)) +* **eslint-plugin:** remove deprecated rules ([#739](https://github.com/typescript-eslint/typescript-eslint/issues/739)) ([e32c7ad](https://github.com/typescript-eslint/typescript-eslint/commit/e32c7ad)) + + +### BREAKING CHANGES + +* removed some rules from recommended config +* recommended config changes are considered breaking +* by default we will now throw when a file is not in the `project` provided +* Merges both no-angle-bracket-type-assertion and no-object-literal-type-assertion into one rule +* **eslint-plugin:** both 'eslint-recommended' and 'recommended' have changed. +* **eslint-plugin:** removing rules +* changes config structure + +```ts +type ArrayOption = 'array' | 'generic' | 'array-simple'; +type Options = [ + { + // default case for all arrays + default: ArrayOption, + // optional override for readonly arrays + readonly?: ArrayOption, + }, +]; +``` +* **eslint-plugin:** changing default rule config +* Node 6 is no longer supported + + + + + # [1.13.0](https://github.com/typescript-eslint/typescript-eslint/compare/v1.12.0...v1.13.0) (2019-07-21) diff --git a/packages/eslint-plugin/package.json b/packages/eslint-plugin/package.json index e0d1c05a9fe..b3d3734d15e 100644 --- a/packages/eslint-plugin/package.json +++ b/packages/eslint-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/eslint-plugin", - "version": "1.13.0", + "version": "2.0.0", "description": "TypeScript plugin for ESLint", "keywords": [ "eslint", @@ -40,7 +40,7 @@ "typecheck": "tsc --noEmit" }, "dependencies": { - "@typescript-eslint/experimental-utils": "1.13.0", + "@typescript-eslint/experimental-utils": "2.0.0", "eslint-utils": "^1.4.0", "functional-red-black-tree": "^1.0.1", "regexpp": "^2.0.1", diff --git a/packages/experimental-utils/CHANGELOG.md b/packages/experimental-utils/CHANGELOG.md index 243445d33cd..8ea9fd4a22c 100644 --- a/packages/experimental-utils/CHANGELOG.md +++ b/packages/experimental-utils/CHANGELOG.md @@ -3,6 +3,36 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [2.0.0](https://github.com/typescript-eslint/typescript-eslint/compare/v1.13.0...v2.0.0) (2019-08-13) + + +### Bug Fixes + +* **eslint-plugin:** add `Literal` to `RuleListener` types ([#824](https://github.com/typescript-eslint/typescript-eslint/issues/824)) ([3c902a1](https://github.com/typescript-eslint/typescript-eslint/commit/3c902a1)) +* **utils:** add ES2019 as valid `ecmaVersion` ([#746](https://github.com/typescript-eslint/typescript-eslint/issues/746)) ([d11fbbe](https://github.com/typescript-eslint/typescript-eslint/commit/d11fbbe)) + + +### Features + +* explicitly support eslint v6 ([#645](https://github.com/typescript-eslint/typescript-eslint/issues/645)) ([34a7cf6](https://github.com/typescript-eslint/typescript-eslint/commit/34a7cf6)) + + +* feat(eslint-plugin)!: recommended-requiring-type-checking config (#846) ([d3470c9](https://github.com/typescript-eslint/typescript-eslint/commit/d3470c9)), closes [#846](https://github.com/typescript-eslint/typescript-eslint/issues/846) +* feat(eslint-plugin)!: change recommended config (#729) ([428567d](https://github.com/typescript-eslint/typescript-eslint/commit/428567d)), closes [#729](https://github.com/typescript-eslint/typescript-eslint/issues/729) +* feat(eslint-plugin)!: add rule `consistent-type-assertions` (#731) ([92e98de](https://github.com/typescript-eslint/typescript-eslint/commit/92e98de)), closes [#731](https://github.com/typescript-eslint/typescript-eslint/issues/731) + + +### BREAKING CHANGES + +* removed some rules from recommended config +* recommended config changes are considered breaking +* Merges both no-angle-bracket-type-assertion and no-object-literal-type-assertion into one rule +* Node 6 is no longer supported + + + + + # [1.13.0](https://github.com/typescript-eslint/typescript-eslint/compare/v1.12.0...v1.13.0) (2019-07-21) diff --git a/packages/experimental-utils/package.json b/packages/experimental-utils/package.json index 4574f3129d1..c4fde675f18 100644 --- a/packages/experimental-utils/package.json +++ b/packages/experimental-utils/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/experimental-utils", - "version": "1.13.0", + "version": "2.0.0", "description": "(Experimental) Utilities for working with TypeScript + ESLint together", "keywords": [ "eslint", @@ -37,7 +37,7 @@ }, "dependencies": { "@types/json-schema": "^7.0.3", - "@typescript-eslint/typescript-estree": "1.13.0", + "@typescript-eslint/typescript-estree": "2.0.0", "eslint-scope": "^4.0.0" }, "peerDependencies": { diff --git a/packages/parser/CHANGELOG.md b/packages/parser/CHANGELOG.md index 0b158483fcf..23eb1182e2a 100644 --- a/packages/parser/CHANGELOG.md +++ b/packages/parser/CHANGELOG.md @@ -3,6 +3,34 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [2.0.0](https://github.com/typescript-eslint/typescript-eslint/compare/v1.13.0...v2.0.0) (2019-08-13) + + +* feat(eslint-plugin)!: change recommended config (#729) ([428567d](https://github.com/typescript-eslint/typescript-eslint/commit/428567d)), closes [#729](https://github.com/typescript-eslint/typescript-eslint/issues/729) +* feat(typescript-estree)!: throw error on file not in project when `project` set (#760) ([3777b77](https://github.com/typescript-eslint/typescript-eslint/commit/3777b77)), closes [#760](https://github.com/typescript-eslint/typescript-eslint/issues/760) + + +### Bug Fixes + +* **typescript-estree:** fix `is` token typed as `Keyword ([#750](https://github.com/typescript-eslint/typescript-eslint/issues/750)) ([35dec52](https://github.com/typescript-eslint/typescript-eslint/commit/35dec52)) +* **typescript-estree:** jsx comment parsing ([#703](https://github.com/typescript-eslint/typescript-eslint/issues/703)) ([0cfc48e](https://github.com/typescript-eslint/typescript-eslint/commit/0cfc48e)) + + +### Features + +* explicitly support eslint v6 ([#645](https://github.com/typescript-eslint/typescript-eslint/issues/645)) ([34a7cf6](https://github.com/typescript-eslint/typescript-eslint/commit/34a7cf6)) + + +### BREAKING CHANGES + +* recommended config changes are considered breaking +* by default we will now throw when a file is not in the `project` provided +* Node 6 is no longer supported + + + + + # [1.13.0](https://github.com/typescript-eslint/typescript-eslint/compare/v1.12.0...v1.13.0) (2019-07-21) **Note:** Version bump only for package @typescript-eslint/parser diff --git a/packages/parser/package.json b/packages/parser/package.json index bc3f3cf29b0..4d67b9a555e 100644 --- a/packages/parser/package.json +++ b/packages/parser/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/parser", - "version": "1.13.0", + "version": "2.0.0", "description": "An ESLint custom parser which leverages TypeScript ESTree", "main": "dist/parser.js", "files": [ @@ -42,13 +42,13 @@ }, "dependencies": { "@types/eslint-visitor-keys": "^1.0.0", - "@typescript-eslint/experimental-utils": "1.13.0", - "@typescript-eslint/typescript-estree": "1.13.0", + "@typescript-eslint/experimental-utils": "2.0.0", + "@typescript-eslint/typescript-estree": "2.0.0", "eslint-visitor-keys": "^1.0.0" }, "devDependencies": { "@types/glob": "^7.1.1", - "@typescript-eslint/shared-fixtures": "1.13.0", + "@typescript-eslint/shared-fixtures": "2.0.0", "glob": "^7.1.4" } } diff --git a/packages/shared-fixtures/CHANGELOG.md b/packages/shared-fixtures/CHANGELOG.md index 2c796c965bb..3aec5aabbcd 100644 --- a/packages/shared-fixtures/CHANGELOG.md +++ b/packages/shared-fixtures/CHANGELOG.md @@ -3,6 +3,18 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [2.0.0](https://github.com/typescript-eslint/typescript-eslint/compare/v1.13.0...v2.0.0) (2019-08-13) + + +### Bug Fixes + +* **typescript-estree:** fix `is` token typed as `Keyword ([#750](https://github.com/typescript-eslint/typescript-eslint/issues/750)) ([35dec52](https://github.com/typescript-eslint/typescript-eslint/commit/35dec52)) +* **typescript-estree:** jsx comment parsing ([#703](https://github.com/typescript-eslint/typescript-eslint/issues/703)) ([0cfc48e](https://github.com/typescript-eslint/typescript-eslint/commit/0cfc48e)) + + + + + # [1.13.0](https://github.com/typescript-eslint/typescript-eslint/compare/v1.12.0...v1.13.0) (2019-07-21) **Note:** Version bump only for package @typescript-eslint/shared-fixtures diff --git a/packages/shared-fixtures/package.json b/packages/shared-fixtures/package.json index d211eed6e38..0fedd2edbe9 100644 --- a/packages/shared-fixtures/package.json +++ b/packages/shared-fixtures/package.json @@ -1,5 +1,5 @@ { "name": "@typescript-eslint/shared-fixtures", - "version": "1.13.0", + "version": "2.0.0", "private": true } diff --git a/packages/typescript-estree/CHANGELOG.md b/packages/typescript-estree/CHANGELOG.md index 2267143f5c7..27a2f9ea6a3 100644 --- a/packages/typescript-estree/CHANGELOG.md +++ b/packages/typescript-estree/CHANGELOG.md @@ -3,6 +3,36 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [2.0.0](https://github.com/typescript-eslint/typescript-eslint/compare/v1.13.0...v2.0.0) (2019-08-13) + + +* feat(eslint-plugin)!: change recommended config (#729) ([428567d](https://github.com/typescript-eslint/typescript-eslint/commit/428567d)), closes [#729](https://github.com/typescript-eslint/typescript-eslint/issues/729) +* feat(typescript-estree)!: throw error on file not in project when `project` set (#760) ([3777b77](https://github.com/typescript-eslint/typescript-eslint/commit/3777b77)), closes [#760](https://github.com/typescript-eslint/typescript-eslint/issues/760) +* feat(eslint-plugin)!: add rule `consistent-type-assertions` (#731) ([92e98de](https://github.com/typescript-eslint/typescript-eslint/commit/92e98de)), closes [#731](https://github.com/typescript-eslint/typescript-eslint/issues/731) + + +### Bug Fixes + +* **typescript-estree:** fix `is` token typed as `Keyword ([#750](https://github.com/typescript-eslint/typescript-eslint/issues/750)) ([35dec52](https://github.com/typescript-eslint/typescript-eslint/commit/35dec52)) +* **typescript-estree:** jsx comment parsing ([#703](https://github.com/typescript-eslint/typescript-eslint/issues/703)) ([0cfc48e](https://github.com/typescript-eslint/typescript-eslint/commit/0cfc48e)) + + +### Features + +* explicitly support eslint v6 ([#645](https://github.com/typescript-eslint/typescript-eslint/issues/645)) ([34a7cf6](https://github.com/typescript-eslint/typescript-eslint/commit/34a7cf6)) + + +### BREAKING CHANGES + +* recommended config changes are considered breaking +* by default we will now throw when a file is not in the `project` provided +* Merges both no-angle-bracket-type-assertion and no-object-literal-type-assertion into one rule +* Node 6 is no longer supported + + + + + # [1.13.0](https://github.com/typescript-eslint/typescript-eslint/compare/v1.12.0...v1.13.0) (2019-07-21) **Note:** Version bump only for package @typescript-eslint/typescript-estree diff --git a/packages/typescript-estree/package.json b/packages/typescript-estree/package.json index e0a0e661882..d7c8bae66ef 100644 --- a/packages/typescript-estree/package.json +++ b/packages/typescript-estree/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/typescript-estree", - "version": "1.13.0", + "version": "2.0.0", "description": "A parser that converts TypeScript source code into an ESTree compatible form", "main": "dist/parser.js", "types": "dist/parser.d.ts", @@ -53,7 +53,7 @@ "@types/lodash.isplainobject": "^4.0.4", "@types/lodash.unescape": "^4.0.4", "@types/semver": "^6.0.1", - "@typescript-eslint/shared-fixtures": "1.13.0", + "@typescript-eslint/shared-fixtures": "2.0.0", "babel-code-frame": "^6.26.0", "glob": "^7.1.4", "lodash.isplainobject": "4.0.6", From 2e2fe90c6645cfc59e59a90b6542415e735dcbb9 Mon Sep 17 00:00:00 2001 From: James Henry Date: Tue, 13 Aug 2019 22:50:38 +0100 Subject: [PATCH 11/28] chore: reenable automated canary releases for master (#849) --- azure-pipelines.yml | 50 ++++++++++++++++++++++----------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index e3bdabad347..960eb86b11d 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -77,28 +77,28 @@ jobs: yarn test displayName: 'Run unit tests' - # - job: publish_canary_version - # displayName: Publish the latest code as a canary version - # dependsOn: - # - primary_code_validation_and_tests - # - unit_tests_on_other_node_versions - # condition: and(succeeded(), eq(variables['Build.SourceBranchName'], 'master'), ne(variables['Build.Reason'], 'PullRequest')) - # pool: - # vmImage: 'Ubuntu-16.04' - # steps: - # - task: NodeTool@0 - # inputs: - # versionSpec: 11 - # displayName: 'Install Node.js 11' - - # - script: | - # # This also runs a build as part of the postinstall - # # bootstrap - # yarn --ignore-engines --frozen-lockfile - - # - script: | - # npm config set //registry.npmjs.org/:_authToken=$(NPM_TOKEN) - - # - script: | - # npx lerna publish --canary --exact --force-publish --yes - # displayName: 'Publish all packages to npm' + - job: publish_canary_version + displayName: Publish the latest code as a canary version + dependsOn: + - primary_code_validation_and_tests + - unit_tests_on_other_node_versions + condition: and(succeeded(), eq(variables['Build.SourceBranchName'], 'master'), ne(variables['Build.Reason'], 'PullRequest')) + pool: + vmImage: 'Ubuntu-16.04' + steps: + - task: NodeTool@0 + inputs: + versionSpec: 11 + displayName: 'Install Node.js 11' + + - script: | + # This also runs a build as part of the postinstall + # bootstrap + yarn --ignore-engines --frozen-lockfile + + - script: | + npm config set //registry.npmjs.org/:_authToken=$(NPM_TOKEN) + + - script: | + npx lerna publish --canary --exact --force-publish --yes + displayName: 'Publish all packages to npm' From ca3b6a57b589213c6761cc311127cf3aa7e42082 Mon Sep 17 00:00:00 2001 From: Alexander T Date: Wed, 14 Aug 2019 19:23:39 +0300 Subject: [PATCH 12/28] docs(parser): fix typo (#855) --- packages/parser/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/parser/README.md b/packages/parser/README.md index 45c620280f1..cbabebd95f8 100644 --- a/packages/parser/README.md +++ b/packages/parser/README.md @@ -55,7 +55,7 @@ The following additional configuration options are available by specifying them ```ts { "extends": "./tsconfig.json", // path to existing tsconfig - "includes": [ + "include": [ "src/**/*.ts", "test/**/*.ts", // etc From a8da33010a87e61f6fc03fb93b7ea20853c90bf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=AA=97=E4=BD=A0=E6=98=AF=E5=B0=8F=E7=8C=AB=E5=92=AA?= Date: Thu, 15 Aug 2019 11:56:59 +0800 Subject: [PATCH 13/28] docs(eslint-plugin): fix link in indent docs (#860) --- packages/eslint-plugin/docs/rules/indent.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/eslint-plugin/docs/rules/indent.md b/packages/eslint-plugin/docs/rules/indent.md index 5678ba291da..4ffd6f894c7 100644 --- a/packages/eslint-plugin/docs/rules/indent.md +++ b/packages/eslint-plugin/docs/rules/indent.md @@ -90,7 +90,7 @@ This rule has an object option: - `"ObjectExpression"` (default: 1) enforces indentation level for properties in objects. It can be set to the string `"first"`, indicating that all properties in the object should be aligned with the first property. This can also be set to `"off"` to disable checking for object properties. - `"ImportDeclaration"` (default: 1) enforces indentation level for import statements. It can be set to the string `"first"`, indicating that all imported members from a module should be aligned with the first member in the list. This can also be set to `"off"` to disable checking for imported module members. - `"flatTernaryExpressions": true` (`false` by default) requires no indentation for ternary expressions which are nested in other ternary expressions. -- `"ignoredNodes"` accepts an array of [selectors](/docs/developer-guide/selectors.md). If an AST node is matched by any of the selectors, the indentation of tokens which are direct children of that node will be ignored. This can be used as an escape hatch to relax the rule if you disagree with the indentation that it enforces for a particular syntactic pattern. +- `"ignoredNodes"` accepts an array of [selectors](https://eslint.org/docs/developer-guide/selectors). If an AST node is matched by any of the selectors, the indentation of tokens which are direct children of that node will be ignored. This can be used as an escape hatch to relax the rule if you disagree with the indentation that it enforces for a particular syntactic pattern. - `"ignoreComments"` (default: false) can be used when comments do not need to be aligned with nodes on the previous or next line. Level of indentation denotes the multiple of the indent specified. Example: From 5eb40dc323720778955c68202117cec38f1f478a Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Bouhier Date: Thu, 15 Aug 2019 14:13:32 -0700 Subject: [PATCH 14/28] fix(eslint-plugin): readme typo (#867) --- packages/eslint-plugin/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/eslint-plugin/README.md b/packages/eslint-plugin/README.md index 1ecc3e86cf9..e0f32686c9d 100644 --- a/packages/eslint-plugin/README.md +++ b/packages/eslint-plugin/README.md @@ -65,7 +65,7 @@ You can also use [eslint:recommended](https://eslint.org/docs/rules/) (the set o As of version 2 of this plugin, _by design_, none of the rules in the main `recommended` config require type-checking in order to run. This means that they are more lightweight and faster to run. -Some highly valuable rules simply require type-checking in order to be implemented correctly, however, so we provide an additional config you can extend from called `recommended-requiring-type-checking`. You wou apply this _in addition_ to the recommended configs previously mentioned, e.g.: +Some highly valuable rules simply require type-checking in order to be implemented correctly, however, so we provide an additional config you can extend from called `recommended-requiring-type-checking`. You would apply this _in addition_ to the recommended configs previously mentioned, e.g.: ```json { From 32d37453121743b329df9b587b78a75c1bdd5efb Mon Sep 17 00:00:00 2001 From: Pete Gonzalez <4673363+octogonz@users.noreply.github.com> Date: Thu, 15 Aug 2019 18:56:49 -0700 Subject: [PATCH 15/28] chore: fix line end character for json/md (#869) --- .gitattributes | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitattributes b/.gitattributes index 8db1f2632df..73968f2a614 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,5 +1,7 @@ * text=auto *.js eol=lf +*.json eol=lf +*.md eol=lf *.ts eol=lf *.tsx eol=lf *.yml eol=lf From 4b0d2d9f9b8ab370150119eb9bee0908b6751203 Mon Sep 17 00:00:00 2001 From: Brad Zacher Date: Mon, 19 Aug 2019 00:19:17 -0700 Subject: [PATCH 16/28] chore(eslint-plugin): fix peer dependency (#859) --- packages/eslint-plugin/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/eslint-plugin/package.json b/packages/eslint-plugin/package.json index b3d3734d15e..6706be22576 100644 --- a/packages/eslint-plugin/package.json +++ b/packages/eslint-plugin/package.json @@ -56,7 +56,7 @@ "typescript": "*" }, "peerDependencies": { - "@typescript-eslint/parser": "^2.0.0-alpha.0", + "@typescript-eslint/parser": "^2.0.0", "eslint": "^5.0.0 || ^6.0.0" } } From 580eceb83183ddc7a4798eb5bc55a36f71dcda89 Mon Sep 17 00:00:00 2001 From: Ben Lichtman Date: Mon, 19 Aug 2019 01:50:35 -0700 Subject: [PATCH 17/28] fix(eslint-plugin): [unified-signatures] type comparison and exported nodes (#839) --- .gitignore | 3 + .../src/rules/unified-signatures.ts | 38 +++++++++--- .../tests/rules/unified-signatures.test.ts | 58 +++++++++++++++++++ .../src/ts-estree/ts-estree.ts | 1 + 4 files changed, 93 insertions(+), 7 deletions(-) diff --git a/.gitignore b/.gitignore index 55599f0c184..3a06baccf6f 100644 --- a/.gitignore +++ b/.gitignore @@ -60,3 +60,6 @@ jspm_packages/ .DS_Store .idea dist + +# Editor-specific metadata folders +.vs diff --git a/packages/eslint-plugin/src/rules/unified-signatures.ts b/packages/eslint-plugin/src/rules/unified-signatures.ts index 1c3f747bc51..c4c7f9bf427 100644 --- a/packages/eslint-plugin/src/rules/unified-signatures.ts +++ b/packages/eslint-plugin/src/rules/unified-signatures.ts @@ -35,6 +35,9 @@ type ScopeNode = | TSESTree.TSTypeLiteral; type OverloadNode = MethodDefinition | SignatureDefinition; +type ContainingNode = + | TSESTree.ExportNamedDeclaration + | TSESTree.ExportDefaultDeclaration; type SignatureDefinition = | TSESTree.FunctionExpression @@ -424,7 +427,8 @@ export default util.createRule({ a === b || (a !== undefined && b !== undefined && - a.typeAnnotation.type === b.typeAnnotation.type) + sourceCode.getText(a.typeAnnotation) === + sourceCode.getText(b.typeAnnotation)) ); } @@ -495,9 +499,16 @@ export default util.createRule({ currentScope = scopes.pop()!; } - function addOverload(signature: OverloadNode, key?: string): void { + function addOverload( + signature: OverloadNode, + key?: string, + containingNode?: ContainingNode, + ): void { key = key || getOverloadKey(signature); - if (currentScope && signature.parent === currentScope.parent && key) { + if ( + currentScope && + (containingNode || signature).parent === currentScope.parent + ) { const overloads = currentScope.overloads.get(key); if (overloads !== undefined) { overloads.push(signature); @@ -521,11 +532,10 @@ export default util.createRule({ createScope(node.body, node.typeParameters); }, TSTypeLiteral: createScope, + // collect overloads TSDeclareFunction(node): void { - if (node.id && !node.body) { - addOverload(node, node.id.name); - } + addOverload(node, node.id.name, getExportingNode(node)); }, TSCallSignatureDeclaration: addOverload, TSConstructSignatureDeclaration: addOverload, @@ -540,6 +550,7 @@ export default util.createRule({ addOverload(node); } }, + // validate scopes 'Program:exit': checkScope, 'TSModuleBlock:exit': checkScope, @@ -550,7 +561,20 @@ export default util.createRule({ }, }); -function getOverloadKey(node: OverloadNode): string | undefined { +function getExportingNode( + node: TSESTree.TSDeclareFunction, +): + | TSESTree.ExportNamedDeclaration + | TSESTree.ExportDefaultDeclaration + | undefined { + return node.parent && + (node.parent.type === AST_NODE_TYPES.ExportNamedDeclaration || + node.parent.type === AST_NODE_TYPES.ExportDefaultDeclaration) + ? node.parent + : undefined; +} + +function getOverloadKey(node: OverloadNode): string { const info = getOverloadInfo(node); return ( diff --git a/packages/eslint-plugin/tests/rules/unified-signatures.test.ts b/packages/eslint-plugin/tests/rules/unified-signatures.test.ts index 5d65e2f1801..c8f9740662d 100644 --- a/packages/eslint-plugin/tests/rules/unified-signatures.test.ts +++ b/packages/eslint-plugin/tests/rules/unified-signatures.test.ts @@ -105,6 +105,30 @@ interface I { function f(x: T[]): void; function f(x: T): void; `, + // Same name, different scopes + ` +declare function foo(n: number): number; + +declare module "hello" { + function foo(n: number, s: string): number; +} +`, + // children of block not checked to match TSLint + ` +{ + function block(): number; + function block(n: number): number; + function block(n?: number): number { + return 3; + } +} +`, + ` +export interface Foo { + bar(baz: string): number[]; + bar(): string[]; +} +`, ], invalid: [ { @@ -591,5 +615,39 @@ class Foo { }, ], }, + { + code: ` +export function foo(line: number): number; +export function foo(line: number, character?: number): number; +`, + errors: [ + { + messageId: 'omittingSingleParameter', + data: { + failureStringStart: + 'These overloads can be combined into one signature', + }, + line: 3, + column: 35, + }, + ], + }, + { + code: ` +declare function foo(line: number): number; +export function foo(line: number, character?: number): number; +`, + errors: [ + { + messageId: 'omittingSingleParameter', + data: { + failureStringStart: + 'These overloads can be combined into one signature', + }, + line: 3, + column: 35, + }, + ], + }, ], }); diff --git a/packages/typescript-estree/src/ts-estree/ts-estree.ts b/packages/typescript-estree/src/ts-estree/ts-estree.ts index f079fd68d47..7276f8d93e3 100644 --- a/packages/typescript-estree/src/ts-estree/ts-estree.ts +++ b/packages/typescript-estree/src/ts-estree/ts-estree.ts @@ -1040,6 +1040,7 @@ export interface TSConstructSignatureDeclaration extends FunctionSignatureBase { } export interface TSDeclareFunction extends FunctionDeclarationBase { + id: Identifier; type: AST_NODE_TYPES.TSDeclareFunction; } From 656d25528f6e9772a80d2569312c302c4bb6329b Mon Sep 17 00:00:00 2001 From: Brad Zacher Date: Mon, 19 Aug 2019 01:51:33 -0700 Subject: [PATCH 18/28] docs: add more issue templates (#811) --- .github/ISSUE_TEMPLATE.md | 37 +++++++++++++++++++ .../eslint-plugin-typescript.md | 2 +- .../ISSUE_TEMPLATE/typescript-eslint-utils.md | 20 ++++++++++ 3 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 .github/ISSUE_TEMPLATE.md create mode 100644 .github/ISSUE_TEMPLATE/typescript-eslint-utils.md diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 00000000000..e219ef75b4f --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,37 @@ +--- +name: catch-all template +about: Provides some general structure to issues that people choose to log outside the normal flow +title: '' +labels: triage +assignees: '' +--- + +**What were you trying to do?** + +```json +{ + "rules": { + "@typescript-eslint/": [""] + } +} +``` + +```ts +// Put your code here +``` + +**What did you expect to happen?** + +**What actually happened?** + +**Versions** + +| package | version | +| --------------------------------------- | ------- | +| `@typescript-eslint/eslint-plugin` | `X.Y.Z` | +| `@typescript-eslint/parser` | `X.Y.Z` | +| `@typescript-eslint/typescript-estree` | `X.Y.Z` | +| `@typescript-eslint/experimental-utils` | `X.Y.Z` | +| `TypeScript` | `X.Y.Z` | +| `node` | `X.Y.Z` | +| `npm` | `X.Y.Z` | diff --git a/.github/ISSUE_TEMPLATE/eslint-plugin-typescript.md b/.github/ISSUE_TEMPLATE/eslint-plugin-typescript.md index 010b5c01cb3..ad7d8fb4944 100644 --- a/.github/ISSUE_TEMPLATE/eslint-plugin-typescript.md +++ b/.github/ISSUE_TEMPLATE/eslint-plugin-typescript.md @@ -22,7 +22,7 @@ Are you opening an issue because the rule you're trying to use is not found? 1) Check the releases log: https://github.com/typescript-eslint/typescript-eslint/releases - If the rule isn't listed there, then chances are it hasn't been released to the main npm tag yet. 2) Try installing the `canary` tag: `npm i @typescript-eslint/eslint-plugin@canary`. - - The canary tag is built for every commit to master, so it contains the bleeding edge build. + - The canary tag is built for every commit to master, so it contains the bleeding edge build. 3) If ESLint still can't find the rule, then consider reporting an issue. --> diff --git a/.github/ISSUE_TEMPLATE/typescript-eslint-utils.md b/.github/ISSUE_TEMPLATE/typescript-eslint-utils.md new file mode 100644 index 00000000000..8403568e4ac --- /dev/null +++ b/.github/ISSUE_TEMPLATE/typescript-eslint-utils.md @@ -0,0 +1,20 @@ +--- +name: '@typescript-eslint/experimental-utils' +about: Report an issue with the `@typescript-eslint/experimental-utils` package +title: '' +labels: 'package: utils, triage' +assignees: '' +--- + +**What did you expect to happen?** + +**What actually happened?** + +**Versions** + +| package | version | +| --------------------------------------- | ------- | +| `@typescript-eslint/experimental-utils` | `X.Y.Z` | +| `TypeScript` | `X.Y.Z` | +| `node` | `X.Y.Z` | +| `npm` | `X.Y.Z` | From 9f8209952c2325763f490b6b283dfa717e3df1b5 Mon Sep 17 00:00:00 2001 From: Alexander T Date: Mon, 19 Aug 2019 12:03:14 +0300 Subject: [PATCH 19/28] feat(eslint-plugin): add quotes [extension] (#762) --- packages/eslint-plugin/README.md | 1 + packages/eslint-plugin/docs/rules/quotes.md | 22 + packages/eslint-plugin/src/configs/all.json | 2 + packages/eslint-plugin/src/rules/index.ts | 2 + packages/eslint-plugin/src/rules/quotes.ts | 62 ++ .../eslint-plugin/tests/rules/quotes.test.ts | 662 ++++++++++++++++++ .../eslint-plugin/tools/generate-configs.ts | 1 + .../eslint-plugin/typings/eslint-rules.d.ts | 20 + 8 files changed, 772 insertions(+) create mode 100644 packages/eslint-plugin/docs/rules/quotes.md create mode 100644 packages/eslint-plugin/src/rules/quotes.ts create mode 100644 packages/eslint-plugin/tests/rules/quotes.test.ts diff --git a/packages/eslint-plugin/README.md b/packages/eslint-plugin/README.md index e0f32686c9d..a3ce816616b 100644 --- a/packages/eslint-plugin/README.md +++ b/packages/eslint-plugin/README.md @@ -191,6 +191,7 @@ Then you should add `airbnb` (or `airbnb-base`) to your `extends` section of `.e | [`@typescript-eslint/prefer-regexp-exec`](./docs/rules/prefer-regexp-exec.md) | Prefer RegExp#exec() over String#match() if no global flag is provided | :heavy_check_mark: | | :thought_balloon: | | [`@typescript-eslint/prefer-string-starts-ends-with`](./docs/rules/prefer-string-starts-ends-with.md) | Enforce the use of `String#startsWith` and `String#endsWith` instead of other equivalent methods of checking substrings | :heavy_check_mark: | :wrench: | :thought_balloon: | | [`@typescript-eslint/promise-function-async`](./docs/rules/promise-function-async.md) | Requires any function or method that returns a Promise to be marked async | | | :thought_balloon: | +| [`@typescript-eslint/quotes`](./docs/rules/quotes.md) | Enforce the consistent use of either backticks, double, or single quotes | | :wrench: | | | [`@typescript-eslint/require-array-sort-compare`](./docs/rules/require-array-sort-compare.md) | Enforce giving `compare` argument to `Array#sort` | | | :thought_balloon: | | [`@typescript-eslint/require-await`](./docs/rules/require-await.md) | Disallow async functions which have no `await` expression | :heavy_check_mark: | | :thought_balloon: | | [`@typescript-eslint/restrict-plus-operands`](./docs/rules/restrict-plus-operands.md) | When adding two variables, operands must both be of type number or of type string | | | :thought_balloon: | diff --git a/packages/eslint-plugin/docs/rules/quotes.md b/packages/eslint-plugin/docs/rules/quotes.md new file mode 100644 index 00000000000..e707a94b3d3 --- /dev/null +++ b/packages/eslint-plugin/docs/rules/quotes.md @@ -0,0 +1,22 @@ +# Enforce the consistent use of either backticks, double, or single quotes + +## Rule Details + +This rule extends the base [eslint/quotes](https://eslint.org/docs/rules/quotes) rule. +It supports all options and features of the base rule. + +## How to use + +```cjson +{ + // note you must disable the base rule as it can report incorrect errors + "quotes": "off", + "@typescript-eslint/quotes": ["error"] +} +``` + +## Options + +See [eslint/quotes options](https://eslint.org/docs/rules/quotes#options). + +Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/quotes.md) diff --git a/packages/eslint-plugin/src/configs/all.json b/packages/eslint-plugin/src/configs/all.json index 30a7cafafb5..cce896718db 100644 --- a/packages/eslint-plugin/src/configs/all.json +++ b/packages/eslint-plugin/src/configs/all.json @@ -62,6 +62,8 @@ "@typescript-eslint/prefer-regexp-exec": "error", "@typescript-eslint/prefer-string-starts-ends-with": "error", "@typescript-eslint/promise-function-async": "error", + "quotes": "off", + "@typescript-eslint/quotes": "error", "@typescript-eslint/require-array-sort-compare": "error", "require-await": "off", "@typescript-eslint/require-await": "error", diff --git a/packages/eslint-plugin/src/rules/index.ts b/packages/eslint-plugin/src/rules/index.ts index db155e691bf..bd39c7867e3 100644 --- a/packages/eslint-plugin/src/rules/index.ts +++ b/packages/eslint-plugin/src/rules/index.ts @@ -48,6 +48,7 @@ import preferReadonly from './prefer-readonly'; import preferRegexpExec from './prefer-regexp-exec'; import preferStringStartsEndsWith from './prefer-string-starts-ends-with'; import promiseFunctionAsync from './promise-function-async'; +import quotes from './quotes'; import requireArraySortCompare from './require-array-sort-compare'; import requireAwait from './require-await'; import restrictPlusOperands from './restrict-plus-operands'; @@ -112,6 +113,7 @@ export default { 'prefer-regexp-exec': preferRegexpExec, 'prefer-string-starts-ends-with': preferStringStartsEndsWith, 'promise-function-async': promiseFunctionAsync, + quotes: quotes, 'require-array-sort-compare': requireArraySortCompare, 'require-await': requireAwait, 'restrict-plus-operands': restrictPlusOperands, diff --git a/packages/eslint-plugin/src/rules/quotes.ts b/packages/eslint-plugin/src/rules/quotes.ts new file mode 100644 index 00000000000..97efc04c821 --- /dev/null +++ b/packages/eslint-plugin/src/rules/quotes.ts @@ -0,0 +1,62 @@ +import { + AST_NODE_TYPES, + TSESTree, +} from '@typescript-eslint/experimental-utils'; +import baseRule from 'eslint/lib/rules/quotes'; +import * as util from '../util'; + +export type Options = util.InferOptionsTypeFromRule; +export type MessageIds = util.InferMessageIdsTypeFromRule; + +export default util.createRule({ + name: 'quotes', + meta: { + type: 'layout', + docs: { + description: + 'Enforce the consistent use of either backticks, double, or single quotes', + category: 'Stylistic Issues', + recommended: false, + }, + fixable: 'code', + messages: baseRule.meta.messages, + schema: baseRule.meta.schema, + }, + defaultOptions: [ + 'double', + { + allowTemplateLiterals: false, + avoidEscape: false, + }, + ], + create(context, [option]) { + const rules = baseRule.create(context); + + const isModuleDeclaration = (node: TSESTree.Literal): boolean => { + return ( + !!node.parent && node.parent.type === AST_NODE_TYPES.TSModuleDeclaration + ); + }; + + const isTypeLiteral = (node: TSESTree.Literal): boolean => { + return !!node.parent && node.parent.type === AST_NODE_TYPES.TSLiteralType; + }; + + return { + Literal(node) { + if ( + option === 'backtick' && + (isModuleDeclaration(node) || isTypeLiteral(node)) + ) { + return; + } + + rules.Literal(node); + }, + + TemplateLiteral(node) { + rules.TemplateLiteral(node); + }, + }; + }, +}); diff --git a/packages/eslint-plugin/tests/rules/quotes.test.ts b/packages/eslint-plugin/tests/rules/quotes.test.ts new file mode 100644 index 00000000000..bec1f4b7e98 --- /dev/null +++ b/packages/eslint-plugin/tests/rules/quotes.test.ts @@ -0,0 +1,662 @@ +import rule from '../../src/rules/quotes'; +import { RuleTester } from '../RuleTester'; + +const ruleTester = new RuleTester({ + parser: '@typescript-eslint/parser', + parserOptions: { + ecmaVersion: 6, + sourceType: 'module', + ecmaFeatures: {}, + }, +}); + +/** + * the base rule `quotes` doesn't use a message id + */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const useDoubleQuote: any = { + message: 'Strings must use doublequote.', +}; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const useSingleQuote: any = { + message: 'Strings must use singlequote.', +}; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const useBacktick: any = { + message: 'Strings must use backtick.', +}; + +ruleTester.run('quotes', rule, { + valid: [ + { + code: `declare module '*.html' {}`, + options: ['backtick'], + parserOptions: { ecmaVersion: 6 }, + }, + { + code: ` + class A { + public prop: IProps['prop']; + } + `, + options: ['backtick'], + parserOptions: { ecmaVersion: 6 }, + }, + + /** ESLint */ + `var foo = "bar";`, + { + code: `var foo = 'bar';`, + options: ['single'], + }, + { + code: `var foo = "bar";`, + options: ['double'], + }, + { + code: `var foo = 1;`, + options: ['single'], + }, + { + code: `var foo = 1;`, + options: ['double'], + }, + { + code: `var foo = "'";`, + options: [ + 'single', + { + avoidEscape: true, + }, + ], + }, + { + code: `var foo = '"';`, + options: [ + 'double', + { + avoidEscape: true, + }, + ], + }, + { + code: `var foo = <>Hello world;`, + options: ['single'], + parserOptions: { + ecmaVersion: 6, + ecmaFeatures: { + jsx: true, + }, + }, + }, + { + code: `var foo = <>Hello world;`, + options: ['double'], + parserOptions: { + ecmaVersion: 6, + ecmaFeatures: { + jsx: true, + }, + }, + }, + { + code: `var foo = <>Hello world;`, + options: [ + 'double', + { + avoidEscape: true, + }, + ], + parserOptions: { + ecmaVersion: 6, + ecmaFeatures: { + jsx: true, + }, + }, + }, + { + code: `var foo = <>Hello world;`, + options: ['backtick'], + parserOptions: { + ecmaVersion: 6, + ecmaFeatures: { + jsx: true, + }, + }, + }, + { + code: `var foo =
Hello world
;`, + options: ['single'], + parserOptions: { + ecmaVersion: 6, + ecmaFeatures: { + jsx: true, + }, + }, + }, + { + code: `var foo =
;`, + options: ['single'], + parserOptions: { + ecmaVersion: 6, + ecmaFeatures: { + jsx: true, + }, + }, + }, + { + code: `var foo =
Hello world
;`, + options: ['double'], + parserOptions: { + ecmaVersion: 6, + ecmaFeatures: { + jsx: true, + }, + }, + }, + { + code: `var foo =
Hello world
;`, + options: [ + 'double', + { + avoidEscape: true, + }, + ], + parserOptions: { + ecmaVersion: 6, + ecmaFeatures: { + jsx: true, + }, + }, + }, + { + code: 'var foo = `bar`;', + options: ['backtick'], + parserOptions: { ecmaVersion: 6 }, + }, + { + code: "var foo = `bar 'baz'`;", + options: ['backtick'], + parserOptions: { ecmaVersion: 6 }, + }, + { + code: 'var foo = `bar "baz"`;', + options: ['backtick'], + parserOptions: { ecmaVersion: 6 }, + }, + { + code: `var foo = 1;`, + options: ['backtick'], + }, + { + code: 'var foo = "a string containing `backtick` quotes";', + options: [ + 'backtick', + { + avoidEscape: true, + }, + ], + }, + { + code: `var foo =
;`, + options: ['backtick'], + parserOptions: { + ecmaVersion: 6, + ecmaFeatures: { + jsx: true, + }, + }, + }, + { + code: `var foo =
Hello world
;`, + options: ['backtick'], + parserOptions: { + ecmaVersion: 6, + ecmaFeatures: { + jsx: true, + }, + }, + }, + + // Backticks are only okay if they have substitutions, contain a line break, or are tagged + { + code: 'var foo = `back\ntick`;', + options: ['single'], + parserOptions: { ecmaVersion: 6 }, + }, + { + code: 'var foo = `back\rtick`;', + options: ['single'], + parserOptions: { ecmaVersion: 6 }, + }, + { + code: 'var foo = `back\u2028tick`;', + options: ['single'], + parserOptions: { ecmaVersion: 6 }, + }, + { + code: 'var foo = `back\u2029tick`;', + options: ['single'], + parserOptions: { ecmaVersion: 6 }, + }, + { + code: 'var foo = `back\\\\\ntick`;', // 2 backslashes followed by a newline + options: ['single'], + parserOptions: { ecmaVersion: 6 }, + }, + { + code: 'var foo = `back\\\\\\\\\ntick`;', + options: ['single'], + parserOptions: { ecmaVersion: 6 }, + }, + { + code: 'var foo = `\n`;', + options: ['single'], + parserOptions: { ecmaVersion: 6 }, + }, + { + code: 'var foo = `back${x}tick`;', + options: ['double'], + parserOptions: { ecmaVersion: 6 }, + }, + { + code: 'var foo = tag`backtick`;', + options: ['double'], + parserOptions: { ecmaVersion: 6 }, + }, + + // Backticks are also okay if allowTemplateLiterals + { + code: "var foo = `bar 'foo' baz` + 'bar';", + options: [ + 'single', + { + allowTemplateLiterals: true, + }, + ], + parserOptions: { ecmaVersion: 6 }, + }, + { + code: 'var foo = `bar \'foo\' baz` + "bar";', + options: [ + 'double', + { + allowTemplateLiterals: true, + }, + ], + parserOptions: { ecmaVersion: 6 }, + }, + { + code: "var foo = `bar 'foo' baz` + `bar`;", + options: [ + 'backtick', + { + allowTemplateLiterals: true, + }, + ], + parserOptions: { ecmaVersion: 6 }, + }, + + // `backtick` should not warn the directive prologues. + { + code: '"use strict"; var foo = `backtick`;', + options: ['backtick'], + parserOptions: { ecmaVersion: 6 }, + }, + { + code: '"use strict"; \'use strong\'; "use asm"; var foo = `backtick`;', + options: ['backtick'], + parserOptions: { ecmaVersion: 6 }, + }, + { + code: + 'function foo() { "use strict"; "use strong"; "use asm"; var foo = `backtick`; }', + options: ['backtick'], + parserOptions: { ecmaVersion: 6 }, + }, + { + code: + "(function() { 'use strict'; 'use strong'; 'use asm'; var foo = `backtick`; })();", + options: ['backtick'], + parserOptions: { ecmaVersion: 6 }, + }, + { + code: + '(() => { "use strict"; "use strong"; "use asm"; var foo = `backtick`; })();', + options: ['backtick'], + parserOptions: { ecmaVersion: 6 }, + }, + + // `backtick` should not warn import/export sources. + { + code: `import "a"; import 'b';`, + options: ['backtick'], + parserOptions: { + ecmaVersion: 6, + sourceType: 'module', + }, + }, + { + code: `import a from "a"; import b from 'b';`, + options: ['backtick'], + parserOptions: { + ecmaVersion: 6, + sourceType: 'module', + }, + }, + { + code: `export * from "a"; export * from 'b';`, + options: ['backtick'], + parserOptions: { + ecmaVersion: 6, + sourceType: 'module', + }, + }, + + // `backtick` should not warn property/method names (not computed). + { + code: `var obj = {"key0": 0, 'key1': 1};`, + options: ['backtick'], + parserOptions: { ecmaVersion: 6 }, + }, + { + code: `class Foo { 'bar'(){} }`, + options: ['backtick'], + parserOptions: { ecmaVersion: 6 }, + }, + { + code: `class Foo { static ''(){} }`, + options: ['backtick'], + parserOptions: { ecmaVersion: 6 }, + }, + ], + + invalid: [ + { + code: `var foo = 'bar';`, + output: `var foo = "bar";`, + errors: [useDoubleQuote], + }, + { + code: `var foo = "bar";`, + output: `var foo = 'bar';`, + options: ['single'], + errors: [useSingleQuote], + }, + { + code: 'var foo = `bar`;', + output: `var foo = 'bar';`, + options: ['single'], + parserOptions: { ecmaVersion: 6 }, + errors: [useSingleQuote], + }, + { + code: `var foo = 'don\\'t';`, + output: `var foo = "don't";`, + errors: [useDoubleQuote], + }, + { + code: `var msg = "Plugin '" + name + "' not found"`, + output: `var msg = 'Plugin \\'' + name + '\\' not found'`, + options: ['single'], + errors: [ + { ...useSingleQuote, column: 11 }, + { ...useSingleQuote, column: 31 }, + ], + }, + { + code: `var foo = 'bar';`, + output: `var foo = "bar";`, + options: ['double'], + errors: [useDoubleQuote], + }, + { + code: 'var foo = `bar`;', + output: `var foo = "bar";`, + options: ['double'], + parserOptions: { ecmaVersion: 6 }, + errors: [useDoubleQuote], + }, + { + code: `var foo = "bar";`, + output: `var foo = 'bar';`, + options: [ + 'single', + { + avoidEscape: true, + }, + ], + errors: [useSingleQuote], + }, + { + code: `var foo = 'bar';`, + output: `var foo = "bar";`, + options: [ + 'double', + { + avoidEscape: true, + }, + ], + errors: [useDoubleQuote], + }, + { + code: `var foo = '\\\\';`, + output: `var foo = "\\\\\";`, // eslint-disable-line no-useless-escape + options: [ + 'double', + { + avoidEscape: true, + }, + ], + errors: [useDoubleQuote], + }, + { + code: `var foo = "bar";`, + output: `var foo = 'bar';`, + options: [ + 'single', + { + allowTemplateLiterals: true, + }, + ], + errors: [useSingleQuote], + }, + { + code: `var foo = 'bar';`, + output: `var foo = "bar";`, + options: [ + 'double', + { + allowTemplateLiterals: true, + }, + ], + errors: [useDoubleQuote], + }, + { + code: `var foo = 'bar';`, + output: 'var foo = `bar`;', + options: ['backtick'], + parserOptions: { ecmaVersion: 2015 }, + errors: [useBacktick], + }, + { + code: "var foo = 'b${x}a$r';", + output: 'var foo = `b\\${x}a$r`;', + options: ['backtick'], + parserOptions: { ecmaVersion: 2015 }, + errors: [useBacktick], + }, + { + code: 'var foo = "bar";', + output: 'var foo = `bar`;', + options: ['backtick'], + parserOptions: { ecmaVersion: 2015 }, + errors: [useBacktick], + }, + { + code: `var foo = "bar";`, + output: 'var foo = `bar`;', + options: [ + 'backtick', + { + avoidEscape: true, + }, + ], + parserOptions: { ecmaVersion: 2015 }, + errors: [useBacktick], + }, + { + code: `var foo = 'bar';`, + output: 'var foo = `bar`;', + options: [ + 'backtick', + { + avoidEscape: true, + }, + ], + parserOptions: { ecmaVersion: 2015 }, + errors: [useBacktick], + }, + + // "use strict" is *not* a directive prologue in these statements so is subject to the rule + { + code: 'var foo = `backtick`; "use strict";', + output: 'var foo = `backtick`; `use strict`;', + options: ['backtick'], + parserOptions: { ecmaVersion: 6 }, + errors: [useBacktick], + }, + { + code: '{ "use strict"; var foo = `backtick`; }', + output: '{ `use strict`; var foo = `backtick`; }', + options: ['backtick'], + parserOptions: { ecmaVersion: 6 }, + errors: [useBacktick], + }, + { + code: 'if (1) { "use strict"; var foo = `backtick`; }', + output: 'if (1) { `use strict`; var foo = `backtick`; }', + options: ['backtick'], + parserOptions: { ecmaVersion: 6 }, + errors: [useBacktick], + }, + + // `backtick` should warn computed property names. + { + code: `var obj = {["key0"]: 0, ['key1']: 1};`, + output: 'var obj = {[`key0`]: 0, [`key1`]: 1};', + options: ['backtick'], + parserOptions: { ecmaVersion: 6 }, + errors: [useBacktick, useBacktick], + }, + { + code: `class Foo { ['a'](){} static ['b'](){} }`, + output: 'class Foo { [`a`](){} static [`b`](){} }', + options: ['backtick'], + parserOptions: { ecmaVersion: 6 }, + errors: [useBacktick, useBacktick], + }, + + // https://github.com/eslint/eslint/issues/7084 + { + code: `
`, + output: `
`, + options: [`single`], + parserOptions: { + ecmaFeatures: { + jsx: true, + }, + }, + errors: [useSingleQuote], + }, + { + code: `
`, + output: `
`, + options: ['double'], + parserOptions: { + ecmaFeatures: { + jsx: true, + }, + }, + errors: [useDoubleQuote], + }, + { + code: `
`, + output: '
', + options: ['backtick'], + parserOptions: { + ecmaFeatures: { + jsx: true, + }, + ecmaVersion: 2015, + }, + errors: [useBacktick], + }, + + // https://github.com/eslint/eslint/issues/7610 + { + code: '`use strict`;', + output: null, + parserOptions: { ecmaVersion: 6 }, + errors: [useDoubleQuote], + }, + { + code: 'function foo() { `use strict`; foo(); }', + output: null, + parserOptions: { ecmaVersion: 6 }, + errors: [useDoubleQuote], + }, + { + code: 'foo = function() { `use strict`; foo(); }', + output: null, + parserOptions: { ecmaVersion: 6 }, + errors: [useDoubleQuote], + }, + { + code: '() => { `use strict`; foo(); }', + output: null, + parserOptions: { ecmaVersion: 6 }, + errors: [useDoubleQuote], + }, + { + code: '() => { foo(); `use strict`; }', + output: `() => { foo(); "use strict"; }`, + parserOptions: { ecmaVersion: 6 }, + errors: [useDoubleQuote], + }, + { + code: 'foo(); `use strict`;', + output: 'foo(); "use strict";', + parserOptions: { ecmaVersion: 6 }, + errors: [useDoubleQuote], + }, + + // https://github.com/eslint/eslint/issues/7646 + { + code: 'var foo = `foo\\nbar`;', + output: 'var foo = "foo\\nbar";', + parserOptions: { ecmaVersion: 6 }, + errors: [useDoubleQuote], + }, + { + code: 'var foo = `foo\\\nbar`;', // 1 backslash followed by a newline + output: 'var foo = "foo\\\nbar";', + parserOptions: { ecmaVersion: 6 }, + errors: [useDoubleQuote], + }, + { + code: 'var foo = `foo\\\\\\\nbar`;', // 3 backslashes followed by a newline + output: 'var foo = "foo\\\\\\\nbar";', + parserOptions: { ecmaVersion: 6 }, + errors: [useDoubleQuote], + }, + { + code: '````', + output: '""``', + parserOptions: { ecmaVersion: 6 }, + errors: [{ ...useDoubleQuote, line: 1, column: 1 }], + }, + ], +}); diff --git a/packages/eslint-plugin/tools/generate-configs.ts b/packages/eslint-plugin/tools/generate-configs.ts index cda1b0771ea..b400ca05722 100644 --- a/packages/eslint-plugin/tools/generate-configs.ts +++ b/packages/eslint-plugin/tools/generate-configs.ts @@ -29,6 +29,7 @@ const BASE_RULES_TO_BE_OVERRIDDEN = new Set([ 'no-empty-function', 'no-extra-parens', 'no-magic-numbers', + 'quotes', 'no-unused-vars', 'no-use-before-define', 'no-useless-constructor', diff --git a/packages/eslint-plugin/typings/eslint-rules.d.ts b/packages/eslint-plugin/typings/eslint-rules.d.ts index 4e5fe5b6449..dcc37035c9d 100644 --- a/packages/eslint-plugin/typings/eslint-rules.d.ts +++ b/packages/eslint-plugin/typings/eslint-rules.d.ts @@ -461,3 +461,23 @@ declare module 'eslint/lib/rules/semi' { >; export = rule; } + +declare module 'eslint/lib/rules/quotes' { + import { TSESLint, TSESTree } from '@typescript-eslint/experimental-utils'; + + const rule: TSESLint.RuleModule< + never, + [ + 'single' | 'double' | 'backtick', + { + allowTemplateLiterals?: boolean; + avoidEscape?: boolean; + }?, + ], + { + Literal(node: TSESTree.Literal): void; + TemplateLiteral(node: TSESTree.TemplateLiteral): void; + } + >; + export = rule; +} From b00666735bc6f7fc0580e9342cf92aba7cf4185c Mon Sep 17 00:00:00 2001 From: Alexander T Date: Mon, 19 Aug 2019 13:50:23 +0300 Subject: [PATCH 20/28] fix(eslint-plugin): [member-naming] should match constructor args (#771) --- .../eslint-plugin/src/rules/member-naming.ts | 69 ++++++++++++++---- .../tests/rules/member-naming.test.ts | 70 +++++++++++++++++++ 2 files changed, 124 insertions(+), 15 deletions(-) diff --git a/packages/eslint-plugin/src/rules/member-naming.ts b/packages/eslint-plugin/src/rules/member-naming.ts index 9995b6cf1e3..d850ef69060 100644 --- a/packages/eslint-plugin/src/rules/member-naming.ts +++ b/packages/eslint-plugin/src/rules/member-naming.ts @@ -1,4 +1,7 @@ -import { TSESTree } from '@typescript-eslint/experimental-utils'; +import { + TSESTree, + AST_NODE_TYPES, +} from '@typescript-eslint/experimental-utils'; import * as util from '../util'; interface Config { @@ -61,37 +64,73 @@ export default util.createRule({ return acc; }, {}); - /** - * Check that the property name matches the convention for its - * accessibility. - * @param {ASTNode} node the named node to evaluate. - * @returns {void} - * @private - */ + function getParameterNode( + node: TSESTree.TSParameterProperty, + ): TSESTree.Identifier | null { + if (node.parameter.type === AST_NODE_TYPES.AssignmentPattern) { + return node.parameter.left as TSESTree.Identifier; + } + + if (node.parameter.type === AST_NODE_TYPES.Identifier) { + return node.parameter; + } + + return null; + } + + function validateParameterName(node: TSESTree.TSParameterProperty): void { + const parameterNode = getParameterNode(node); + if (!parameterNode) { + return; + } + + validate(parameterNode, parameterNode.name, node.accessibility); + } + function validateName( node: TSESTree.MethodDefinition | TSESTree.ClassProperty, ): void { - const name = util.getNameFromClassMember(node, sourceCode); - const accessibility: Modifiers = node.accessibility || 'public'; - const convention = conventions[accessibility]; - - const method = node as TSESTree.MethodDefinition; - if (method.kind === 'constructor') { + if ( + node.type === AST_NODE_TYPES.MethodDefinition && + node.kind === 'constructor' + ) { return; } + validate( + node.key, + util.getNameFromClassMember(node, sourceCode), + node.accessibility, + ); + } + + /** + * Check that the name matches the convention for its accessibility. + * @param {ASTNode} node the named node to evaluate. + * @param {string} name + * @param {Modifiers} accessibility + * @returns {void} + * @private + */ + function validate( + node: TSESTree.Identifier | TSESTree.Expression, + name: string, + accessibility: Modifiers = 'public', + ): void { + const convention = conventions[accessibility]; if (!convention || convention.test(name)) { return; } context.report({ - node: node.key, + node, messageId: 'incorrectName', data: { accessibility, name, convention }, }); } return { + TSParameterProperty: validateParameterName, MethodDefinition: validateName, ClassProperty: validateName, }; diff --git a/packages/eslint-plugin/tests/rules/member-naming.test.ts b/packages/eslint-plugin/tests/rules/member-naming.test.ts index 96ec4b104f3..851d70ea706 100644 --- a/packages/eslint-plugin/tests/rules/member-naming.test.ts +++ b/packages/eslint-plugin/tests/rules/member-naming.test.ts @@ -86,6 +86,30 @@ class Class { }, ], }, + + { + code: ` +class Test { + constructor(public __a: string, protected __b: string, private __c: string = 100) {} +} + `, + options: [ + { + protected: '^__', + private: '^__', + public: '^__', + }, + ], + }, + { + code: + // Semantically invalid test case, TS has to throw an error. + ` +class Foo { + constructor(private ...name: string[], private [test]: [string]) {} +} + `, + }, ], invalid: [ { @@ -329,5 +353,51 @@ class Class { }, ], }, + { + code: ` +class Test { + constructor(public a: string, protected b: string, private c: string = 100) {} +} + `, + options: [ + { + public: '^__', + protected: '^__', + private: '^__', + }, + ], + errors: [ + { + messageId: 'incorrectName', + data: { + accessibility: 'public', + convention: '/^__/', + name: 'a', + }, + line: 3, + column: 24, + }, + { + messageId: 'incorrectName', + data: { + accessibility: 'protected', + convention: '/^__/', + name: 'b', + }, + line: 3, + column: 45, + }, + { + messageId: 'incorrectName', + data: { + accessibility: 'private', + convention: '/^__/', + name: 'c', + }, + line: 3, + column: 64, + }, + ], + }, ], }); From 14c6f807ec3e3184b358952cdc4d01977d112ff1 Mon Sep 17 00:00:00 2001 From: Zen <843968788@qq.com> Date: Mon, 19 Aug 2019 19:14:57 +0800 Subject: [PATCH 21/28] feat: [no-unnecessary-type-assertion] allow `as const` arrow functions (#876) --- .../rules/explicit-function-return-type.ts | 38 +++++++++++++ .../explicit-function-return-type.test.ts | 56 +++++++++++++++++++ 2 files changed, 94 insertions(+) diff --git a/packages/eslint-plugin/src/rules/explicit-function-return-type.ts b/packages/eslint-plugin/src/rules/explicit-function-return-type.ts index 67692c20fc0..da3e7334b6a 100644 --- a/packages/eslint-plugin/src/rules/explicit-function-return-type.ts +++ b/packages/eslint-plugin/src/rules/explicit-function-return-type.ts @@ -9,6 +9,7 @@ type Options = [ allowExpressions?: boolean; allowTypedFunctionExpressions?: boolean; allowHigherOrderFunctions?: boolean; + allowDirectConstAssertionInArrowFunctions?: boolean; }, ]; type MessageIds = 'missingReturnType'; @@ -39,6 +40,9 @@ export default util.createRule({ allowHigherOrderFunctions: { type: 'boolean', }, + allowDirectConstAssertionInArrowFunctions: { + type: 'boolean', + }, }, additionalProperties: false, }, @@ -49,6 +53,7 @@ export default util.createRule({ allowExpressions: false, allowTypedFunctionExpressions: true, allowHigherOrderFunctions: true, + allowDirectConstAssertionInArrowFunctions: true, }, ], create(context, [options]) { @@ -203,6 +208,30 @@ export default util.createRule({ ); } + /** + * Checks if a function belongs to: + * `() => ({ action: 'xxx' }) as const` + */ + function returnsConstAssertionDirectly( + node: TSESTree.ArrowFunctionExpression, + ): boolean { + const { body } = node; + if (body.type === AST_NODE_TYPES.TSAsExpression) { + const { typeAnnotation } = body; + if (typeAnnotation.type === AST_NODE_TYPES.TSTypeReference) { + const { typeName } = typeAnnotation; + if ( + typeName.type === AST_NODE_TYPES.Identifier && + typeName.name === 'const' + ) { + return true; + } + } + } + + return false; + } + /** * Checks if a function declaration/expression has a return type. */ @@ -263,6 +292,15 @@ export default util.createRule({ } } + // https://github.com/typescript-eslint/typescript-eslint/issues/653 + if ( + node.type === AST_NODE_TYPES.ArrowFunctionExpression && + options.allowDirectConstAssertionInArrowFunctions && + returnsConstAssertionDirectly(node) + ) { + return; + } + checkFunctionReturnType(node); } diff --git a/packages/eslint-plugin/tests/rules/explicit-function-return-type.test.ts b/packages/eslint-plugin/tests/rules/explicit-function-return-type.test.ts index 32f23792591..942ed5ea2e8 100644 --- a/packages/eslint-plugin/tests/rules/explicit-function-return-type.test.ts +++ b/packages/eslint-plugin/tests/rules/explicit-function-return-type.test.ts @@ -308,6 +308,20 @@ foo({ }, ], }, + { + filename: 'test.ts', + code: ` +const func = (value: number) => (({ type: "X", value }) as const); +const func = (value: number) => ({ type: "X", value } as const); +const func = (value: number) => (x as const); +const func = (value: number) => x as const; + `, + options: [ + { + allowDirectConstAssertionInArrowFunctions: true, + }, + ], + }, ], invalid: [ { @@ -749,5 +763,47 @@ foo({ }, ], }, + { + filename: 'test.ts', + code: ` +const func = (value: number) => ({ type: "X", value } as any); +const func = (value: number) => ({ type: "X", value } as Action); + `, + options: [ + { + allowDirectConstAssertionInArrowFunctions: true, + }, + ], + errors: [ + { + messageId: 'missingReturnType', + line: 2, + column: 14, + }, + { + messageId: 'missingReturnType', + line: 3, + column: 14, + }, + ], + }, + { + filename: 'test.ts', + code: ` +const func = (value: number) => ({ type: "X", value } as const); + `, + options: [ + { + allowDirectConstAssertionInArrowFunctions: false, + }, + ], + errors: [ + { + messageId: 'missingReturnType', + line: 2, + column: 14, + }, + ], + }, ], }); From c68e033f423d1143330e8e21104d5de10185e9a8 Mon Sep 17 00:00:00 2001 From: Ankeet Maini Date: Mon, 19 Aug 2019 18:57:34 +0530 Subject: [PATCH 22/28] feat(eslint-plugin): [no-type-alias] support tuples (#775) --- .../eslint-plugin/docs/rules/no-type-alias.md | 76 +++++ .../eslint-plugin/src/rules/no-type-alias.ts | 145 +++++----- .../tests/rules/no-type-alias.test.ts | 261 ++++++++++++++++++ .../src/ts-estree/ts-estree.ts | 2 +- 4 files changed, 408 insertions(+), 76 deletions(-) diff --git a/packages/eslint-plugin/docs/rules/no-type-alias.md b/packages/eslint-plugin/docs/rules/no-type-alias.md index 63656dfada8..46230f3d329 100644 --- a/packages/eslint-plugin/docs/rules/no-type-alias.md +++ b/packages/eslint-plugin/docs/rules/no-type-alias.md @@ -86,6 +86,7 @@ or more of the following you may pass an object with the options set as follows: - `allowCallbacks` set to `"always"` will allow you to use type aliases with callbacks (Defaults to `"never"`) - `allowLiterals` set to `"always"` will allow you to use type aliases with literal objects (Defaults to `"never"`) - `allowMappedTypes` set to `"always"` will allow you to use type aliases as mapping tools (Defaults to `"never"`) +- `allowTupleTypes` set to `"always"` will allow you to use type aliases with tuples (Defaults to `"never"`) ### allowAliases @@ -453,6 +454,81 @@ type Foo = { readonly [P in keyof T]: T[P] } & type Foo = { [P in keyof T]?: T[P] } & { [P in keyof U]?: U[P] }; ``` +### allowTupleTypes + +This applies to tuple types (`type Foo = [number]`). + +The setting accepts the following options: + +- `"always"` or `"never"` to active or deactivate the feature. +- `"in-unions"`, allows tuples in union statements, e.g. `type Foo = [string] | [string, string];` +- `"in-intersections"`, allows tuples in intersection statements, e.g. `type Foo = [string] & [string, string];` +- `"in-unions-and-intersections"`, allows tuples in union and/or intersection statements. + +Examples of **correct** code for the `{ "allowTupleTypes": "always" }` options: + +```ts +type Foo = [number]; + +type Foo = [number] | [number, number]; + +type Foo = [number] & [number, number]; + +type Foo = [number] | [number, number] & [string, string]; +``` + +Examples of **incorrect** code for the `{ "allowTupleTypes": "in-unions" }` option: + +```ts +type Foo = [number]; + +type Foo = [number] & [number, number]; + +type Foo = [string] & [number]; +``` + +Examples of **correct** code for the `{ "allowTupleTypes": "in-unions" }` option: + +```ts +type Foo = [number] | [number, number]; + +type Foo = [string] | [number]; +``` + +Examples of **incorrect** code for the `{ "allowTupleTypes": "in-intersections" }` option: + +```ts +type Foo = [number]; + +type Foo = [number] | [number, number]; + +type Foo = [string] | [number]; +``` + +Examples of **correct** code for the `{ "allowTupleTypes": "in-intersections" }` option: + +```ts +type Foo = [number] & [number, number]; + +type Foo = [string] & [number]; +``` + +Examples of **incorrect** code for the `{ "allowTupleTypes": "in-unions-and-intersections" }` option: + +```ts +type Foo = [number]; + +type Foo = [string]; +``` + +Examples of **correct** code for the `{ "allowLiterals": "in-unions-and-intersections" }` option: + +```ts +type Foo = [number] & [number, number]; + +type Foo = [string] | [number]; +``` + ## When Not To Use It When you can't express some shape with an interface or you need to use a union, tuple type, diff --git a/packages/eslint-plugin/src/rules/no-type-alias.ts b/packages/eslint-plugin/src/rules/no-type-alias.ts index 1648b89f0df..68d537cc09b 100644 --- a/packages/eslint-plugin/src/rules/no-type-alias.ts +++ b/packages/eslint-plugin/src/rules/no-type-alias.ts @@ -4,27 +4,27 @@ import { } from '@typescript-eslint/experimental-utils'; import * as util from '../util'; +type Values = + | 'always' + | 'never' + | 'in-unions' + | 'in-intersections' + | 'in-unions-and-intersections'; +const enumValues: Values[] = [ + 'always', + 'never', + 'in-unions', + 'in-intersections', + 'in-unions-and-intersections', +]; + type Options = [ { - allowAliases?: - | 'always' - | 'never' - | 'in-unions' - | 'in-intersections' - | 'in-unions-and-intersections'; + allowAliases?: Values; allowCallbacks?: 'always' | 'never'; - allowLiterals?: - | 'always' - | 'never' - | 'in-unions' - | 'in-intersections' - | 'in-unions-and-intersections'; - allowMappedTypes?: - | 'always' - | 'never' - | 'in-unions' - | 'in-intersections' - | 'in-unions-and-intersections'; + allowLiterals?: Values; + allowMappedTypes?: Values; + allowTupleTypes?: Values; }, ]; type MessageIds = 'noTypeAlias' | 'noCompositionAlias'; @@ -57,34 +57,19 @@ export default util.createRule({ type: 'object', properties: { allowAliases: { - enum: [ - 'always', - 'never', - 'in-unions', - 'in-intersections', - 'in-unions-and-intersections', - ], + enum: enumValues, }, allowCallbacks: { enum: ['always', 'never'], }, allowLiterals: { - enum: [ - 'always', - 'never', - 'in-unions', - 'in-intersections', - 'in-unions-and-intersections', - ], + enum: enumValues, }, allowMappedTypes: { - enum: [ - 'always', - 'never', - 'in-unions', - 'in-intersections', - 'in-unions-and-intersections', - ], + enum: enumValues, + }, + allowTupleTypes: { + enum: enumValues, }, }, additionalProperties: false, @@ -97,11 +82,20 @@ export default util.createRule({ allowCallbacks: 'never', allowLiterals: 'never', allowMappedTypes: 'never', + allowTupleTypes: 'never', }, ], create( context, - [{ allowAliases, allowCallbacks, allowLiterals, allowMappedTypes }], + [ + { + allowAliases, + allowCallbacks, + allowLiterals, + allowMappedTypes, + allowTupleTypes, + }, + ], ) { const unions = ['always', 'in-unions', 'in-unions-and-intersections']; const intersections = [ @@ -180,6 +174,36 @@ export default util.createRule({ }); } + const isValidTupleType = (type: TypeWithLabel) => { + if (type.node.type === AST_NODE_TYPES.TSTupleType) { + return true; + } + if (type.node.type === AST_NODE_TYPES.TSTypeOperator) { + if ( + ['keyof', 'readonly'].includes(type.node.operator) && + type.node.typeAnnotation && + type.node.typeAnnotation.type === AST_NODE_TYPES.TSTupleType + ) { + return true; + } + } + return false; + }; + + const checkAndReport = ( + optionValue: Values, + isTopLevel: boolean, + type: TypeWithLabel, + label: string, + ) => { + if ( + optionValue === 'never' || + !isSupportedComposition(isTopLevel, type.compositionType, optionValue) + ) { + reportError(type.node, type.compositionType, isTopLevel, label); + } + }; + /** * Validates the node looking for aliases, callbacks and literals. * @param node the node to be validated. @@ -198,48 +222,19 @@ export default util.createRule({ } } else if (type.node.type === AST_NODE_TYPES.TSTypeLiteral) { // literal object type - if ( - allowLiterals === 'never' || - !isSupportedComposition( - isTopLevel, - type.compositionType, - allowLiterals!, - ) - ) { - reportError(type.node, type.compositionType, isTopLevel, 'Literals'); - } + checkAndReport(allowLiterals!, isTopLevel, type, 'Literals'); } else if (type.node.type === AST_NODE_TYPES.TSMappedType) { // mapped type - if ( - allowMappedTypes === 'never' || - !isSupportedComposition( - isTopLevel, - type.compositionType, - allowMappedTypes!, - ) - ) { - reportError( - type.node, - type.compositionType, - isTopLevel, - 'Mapped types', - ); - } + checkAndReport(allowMappedTypes!, isTopLevel, type, 'Mapped types'); + } else if (isValidTupleType(type)) { + // tuple types + checkAndReport(allowTupleTypes!, isTopLevel, type, 'Tuple Types'); } else if ( type.node.type.endsWith('Keyword') || aliasTypes.has(type.node.type) ) { // alias / keyword - if ( - allowAliases === 'never' || - !isSupportedComposition( - isTopLevel, - type.compositionType, - allowAliases!, - ) - ) { - reportError(type.node, type.compositionType, isTopLevel, 'Aliases'); - } + checkAndReport(allowAliases!, isTopLevel, type, 'Aliases'); } else { // unhandled type - shouldn't happen reportError(type.node, type.compositionType, isTopLevel, 'Unhandled'); diff --git a/packages/eslint-plugin/tests/rules/no-type-alias.test.ts b/packages/eslint-plugin/tests/rules/no-type-alias.test.ts index 3c3d83520e8..d17348af746 100644 --- a/packages/eslint-plugin/tests/rules/no-type-alias.test.ts +++ b/packages/eslint-plugin/tests/rules/no-type-alias.test.ts @@ -376,6 +376,69 @@ type Foo = { code: 'type Foo = typeof bar | typeof baz;', options: [{ allowAliases: 'in-unions' }], }, + { + code: 'type Foo = keyof [string]', + options: [{ allowTupleTypes: 'always' }], + }, + { + code: 'type Foo = [string] | [number, number];', + options: [{ allowTupleTypes: 'always' }], + }, + { + code: 'type Foo = [string] | [number, number];', + options: [{ allowTupleTypes: 'in-unions' }], + }, + { + code: 'type Foo = [string] & [number, number];', + options: [{ allowTupleTypes: 'in-intersections' }], + }, + { + code: + 'type Foo = [string] & [number, number] | [number, number, number];', + options: [{ allowTupleTypes: 'in-unions-and-intersections' }], + }, + { + code: 'type Foo = readonly [string] | [number, number];', + options: [{ allowTupleTypes: 'always' }], + }, + { + code: 'type Foo = readonly [string] | readonly [number, number];', + options: [{ allowTupleTypes: 'always' }], + }, + { + code: 'type Foo = readonly [string] | [number, number];', + options: [{ allowTupleTypes: 'in-unions' }], + }, + { + code: 'type Foo = [string] & readonly [number, number];', + options: [{ allowTupleTypes: 'in-intersections' }], + }, + { + code: + 'type Foo = [string] & [number, number] | readonly [number, number, number];', + options: [{ allowTupleTypes: 'in-unions-and-intersections' }], + }, + { + code: 'type Foo = keyof [string] | [number, number];', + options: [{ allowTupleTypes: 'always' }], + }, + { + code: 'type Foo = keyof [string] | keyof [number, number];', + options: [{ allowTupleTypes: 'always' }], + }, + { + code: 'type Foo = keyof [string] | [number, number];', + options: [{ allowTupleTypes: 'in-unions' }], + }, + { + code: 'type Foo = [string] & keyof [number, number];', + options: [{ allowTupleTypes: 'in-intersections' }], + }, + { + code: + 'type Foo = [string] & [number, number] | keyof [number, number, number];', + options: [{ allowTupleTypes: 'in-unions-and-intersections' }], + }, ], invalid: [ { @@ -2915,5 +2978,203 @@ type Foo = { }, ], }, + { + code: 'type Foo = [number] | [number, number]', + options: [{ allowTupleTypes: 'never' }], + errors: [ + { + messageId: 'noCompositionAlias', + data: { + compositionType: 'union', + typeName: 'Tuple Types', + }, + line: 1, + column: 12, + }, + { + messageId: 'noCompositionAlias', + data: { + compositionType: 'union', + typeName: 'Tuple Types', + }, + line: 1, + column: 23, + }, + ], + }, + { + code: 'type Foo = [number] & [number, number]', + options: [{ allowTupleTypes: 'in-unions' }], + errors: [ + { + messageId: 'noCompositionAlias', + data: { + compositionType: 'intersection', + typeName: 'Tuple Types', + }, + line: 1, + column: 12, + }, + { + messageId: 'noCompositionAlias', + data: { + compositionType: 'intersection', + typeName: 'Tuple Types', + }, + line: 1, + column: 23, + }, + ], + }, + { + code: 'type Foo = [number] | [number, number]', + options: [{ allowTupleTypes: 'in-intersections' }], + errors: [ + { + messageId: 'noCompositionAlias', + data: { + compositionType: 'union', + typeName: 'Tuple Types', + }, + line: 1, + column: 12, + }, + { + messageId: 'noCompositionAlias', + data: { + compositionType: 'union', + typeName: 'Tuple Types', + }, + line: 1, + column: 23, + }, + ], + }, + { + code: 'type Foo = [number];', + options: [{ allowTupleTypes: 'in-intersections' }], + errors: [ + { + messageId: 'noTypeAlias', + }, + ], + }, + { + code: 'type Foo = [number];', + options: [{ allowTupleTypes: 'in-unions' }], + errors: [ + { + messageId: 'noTypeAlias', + }, + ], + }, + { + code: 'type Foo = [number];', + options: [{ allowTupleTypes: 'in-unions-and-intersections' }], + errors: [ + { + messageId: 'noTypeAlias', + }, + ], + }, + { + code: 'type Foo = readonly [number] | keyof [number, number]', + options: [{ allowTupleTypes: 'never' }], + errors: [ + { + messageId: 'noCompositionAlias', + data: { + compositionType: 'union', + typeName: 'Tuple Types', + }, + line: 1, + column: 12, + }, + { + messageId: 'noCompositionAlias', + data: { + compositionType: 'union', + typeName: 'Tuple Types', + }, + line: 1, + column: 32, + }, + ], + }, + { + code: 'type Foo = keyof [number] & [number, number]', + options: [{ allowTupleTypes: 'in-unions' }], + errors: [ + { + messageId: 'noCompositionAlias', + data: { + compositionType: 'intersection', + typeName: 'Tuple Types', + }, + line: 1, + column: 12, + }, + { + messageId: 'noCompositionAlias', + data: { + compositionType: 'intersection', + typeName: 'Tuple Types', + }, + line: 1, + column: 29, + }, + ], + }, + { + code: 'type Foo = [number] | readonly [number, number]', + options: [{ allowTupleTypes: 'in-intersections' }], + errors: [ + { + messageId: 'noCompositionAlias', + data: { + compositionType: 'union', + typeName: 'Tuple Types', + }, + line: 1, + column: 12, + }, + { + messageId: 'noCompositionAlias', + data: { + compositionType: 'union', + typeName: 'Tuple Types', + }, + line: 1, + column: 23, + }, + ], + }, + { + code: 'type Foo = readonly [number];', + options: [{ allowTupleTypes: 'in-intersections' }], + errors: [ + { + messageId: 'noTypeAlias', + }, + ], + }, + { + code: 'type Foo = keyof [number];', + options: [{ allowTupleTypes: 'in-unions' }], + errors: [ + { + messageId: 'noTypeAlias', + }, + ], + }, + { + code: 'type Foo = readonly [number];', + options: [{ allowTupleTypes: 'in-unions-and-intersections' }], + errors: [ + { + messageId: 'noTypeAlias', + }, + ], + }, ], }); diff --git a/packages/typescript-estree/src/ts-estree/ts-estree.ts b/packages/typescript-estree/src/ts-estree/ts-estree.ts index 7276f8d93e3..51529a470cb 100644 --- a/packages/typescript-estree/src/ts-estree/ts-estree.ts +++ b/packages/typescript-estree/src/ts-estree/ts-estree.ts @@ -1324,7 +1324,7 @@ export interface TSTypeLiteral extends BaseNode { export interface TSTypeOperator extends BaseNode { type: AST_NODE_TYPES.TSTypeOperator; operator: 'keyof' | 'unique' | 'readonly'; - typeAnnotation?: TSTypeAnnotation; + typeAnnotation?: TypeNode; } export interface TSTypeParameter extends BaseNode { From 8f3b0a8e48abaffe5707d401e37ae5d2b616d1b9 Mon Sep 17 00:00:00 2001 From: Brad Zacher Date: Mon, 19 Aug 2019 08:56:01 -0700 Subject: [PATCH 23/28] fix(typescript-estree): improve missing project file error msg (#866) Fixes #853 --- .../no-unnecessary-type-assertion.test.ts | 6 -- .../rules/restrict-plus-operands.test.ts | 16 ++--- packages/parser/README.md | 29 +++++---- packages/typescript-estree/jest.config.js | 8 +++ packages/typescript-estree/src/convert.ts | 2 +- packages/typescript-estree/src/parser.ts | 39 +++++++++++- .../typescript-estree/src/tsconfig-parser.ts | 5 +- .../fixtures/invalidFileErrors/js/included.js | 1 + .../invalidFileErrors/js/included.jsx | 1 + .../invalidFileErrors/js/notIncluded.js | 1 + .../invalidFileErrors/js/notIncluded.jsx | 1 + .../invalidFileErrors/other/included.vue | 1 + .../invalidFileErrors/other/notIncluded.vue | 1 + .../other/unknownFileType.unknown | 1 + .../fixtures/invalidFileErrors/ts/included.ts | 1 + .../invalidFileErrors/ts/included.tsx | 1 + .../invalidFileErrors/ts/notIncluded.ts | 1 + .../invalidFileErrors/ts/notIncluded.tsx | 1 + .../fixtures/invalidFileErrors/tsconfig.json | 9 +++ .../tests/lib/__snapshots__/parse.ts.snap | 36 +++++++++++ packages/typescript-estree/tests/lib/parse.ts | 60 ++++++++++++++++++- .../tests/lib/semanticInfo.ts | 4 +- packages/typescript-estree/tsconfig.json | 3 +- 23 files changed, 190 insertions(+), 38 deletions(-) create mode 100644 packages/typescript-estree/tests/fixtures/invalidFileErrors/js/included.js create mode 100644 packages/typescript-estree/tests/fixtures/invalidFileErrors/js/included.jsx create mode 100644 packages/typescript-estree/tests/fixtures/invalidFileErrors/js/notIncluded.js create mode 100644 packages/typescript-estree/tests/fixtures/invalidFileErrors/js/notIncluded.jsx create mode 100644 packages/typescript-estree/tests/fixtures/invalidFileErrors/other/included.vue create mode 100644 packages/typescript-estree/tests/fixtures/invalidFileErrors/other/notIncluded.vue create mode 100644 packages/typescript-estree/tests/fixtures/invalidFileErrors/other/unknownFileType.unknown create mode 100644 packages/typescript-estree/tests/fixtures/invalidFileErrors/ts/included.ts create mode 100644 packages/typescript-estree/tests/fixtures/invalidFileErrors/ts/included.tsx create mode 100644 packages/typescript-estree/tests/fixtures/invalidFileErrors/ts/notIncluded.ts create mode 100644 packages/typescript-estree/tests/fixtures/invalidFileErrors/ts/notIncluded.tsx create mode 100644 packages/typescript-estree/tests/fixtures/invalidFileErrors/tsconfig.json diff --git a/packages/eslint-plugin/tests/rules/no-unnecessary-type-assertion.test.ts b/packages/eslint-plugin/tests/rules/no-unnecessary-type-assertion.test.ts index 4c06e95a042..4b4e496253f 100644 --- a/packages/eslint-plugin/tests/rules/no-unnecessary-type-assertion.test.ts +++ b/packages/eslint-plugin/tests/rules/no-unnecessary-type-assertion.test.ts @@ -99,12 +99,6 @@ foo(str!); ` declare function a(a: string): any; declare const b: string | null; -class Mx { - @a(b!) - private prop = 1; -} - `, - ` class Mx { @a(b!) private prop = 1; diff --git a/packages/eslint-plugin/tests/rules/restrict-plus-operands.test.ts b/packages/eslint-plugin/tests/rules/restrict-plus-operands.test.ts index fdda8fe0ef4..44583a202a1 100644 --- a/packages/eslint-plugin/tests/rules/restrict-plus-operands.test.ts +++ b/packages/eslint-plugin/tests/rules/restrict-plus-operands.test.ts @@ -26,37 +26,37 @@ ruleTester.run('restrict-plus-operands', rule, { `var foo = BigInt(1) + 1n`, `var foo = 1n; foo + 2n`, ` -function test () : number { return 2; } +function test(s: string, n: number) : number { return 2; } var foo = test("5.5", 10) + 10; - `, + `, ` var x = 5; var z = 8.2; var foo = x + z; - `, + `, ` var w = "6.5"; var y = "10"; var foo = y + w; - `, + `, 'var foo = 1 + 1;', "var foo = '1' + '1';", ` var pair: { first: number, second: string } = { first: 5, second: "10" }; var foo = pair.first + 10; - `, + `, ` var pair: { first: number, second: string } = { first: 5, second: "10" }; var foo = pair.first + (10 as number); - `, + `, ` var pair: { first: number, second: string } = { first: 5, second: "10" }; var foo = "5.5" + pair.second; - `, + `, ` var pair: { first: number, second: string } = { first: 5, second: "10" }; var foo = ("5.5" as string) + pair.second; - `, + `, `const foo = 'hello' + (someBoolean ? 'a' : 'b') + (() => someBoolean ? 'c' : 'd')() + 'e';`, `const balls = true;`, `balls === true;`, diff --git a/packages/parser/README.md b/packages/parser/README.md index cbabebd95f8..718e2c82ba5 100644 --- a/packages/parser/README.md +++ b/packages/parser/README.md @@ -52,25 +52,30 @@ The following additional configuration options are available by specifying them - Note that if this setting is specified and `createDefaultProgram` is not, you must only lint files that are included in the projects as defined by the provided `tsconfig.json` files. If your existing configuration does not include all of the files you would like to lint, you can create a separate `tsconfig.eslint.json` as follows: - ```ts - { - "extends": "./tsconfig.json", // path to existing tsconfig - "include": [ - "src/**/*.ts", - "test/**/*.ts", - // etc - ] - } - ``` + ```ts + { + // extend your base config so you don't have to redefine your compilerOptions + "extends": "./tsconfig.json", + "include": [ + "src/**/*.ts", + "test/**/*.ts", + "typings/**/*.ts", + // etc + + // if you have a mixed JS/TS codebase, don't forget to include your JS files + "src/**/*.js" + ] + } + ``` - **`tsconfigRootDir`** - default `undefined`. This option allows you to provide the root directory for relative tsconfig paths specified in the `project` option above. -- **`createDefaultProgram`** - default `false`. This option allows you to request that when the `project` setting is specified, files will be allowed when not included in the projects defined by the provided `tsconfig.json` files. However, this may incur significant performance costs, so this option is primarily included for backwards-compatibility. See the **`project`** section for more information. - - **`extraFileExtensions`** - default `undefined`. This option allows you to provide one or more additional file extensions which should be considered in the TypeScript Program compilation. E.g. a `.vue` file - **`warnOnUnsupportedTypeScriptVersion`** - default `true`. This option allows you to toggle the warning that the parser will give you if you use a version of TypeScript which is not explicitly supported +- **`createDefaultProgram`** - default `false`. This option allows you to request that when the `project` setting is specified, files will be allowed when not included in the projects defined by the provided `tsconfig.json` files. **Using this option will incur significant performance costs. This option is primarily included for backwards-compatibility.** See the **`project`** section above for more information. + ### .eslintrc.json ```json diff --git a/packages/typescript-estree/jest.config.js b/packages/typescript-estree/jest.config.js index 4005947d277..e01f6ed0775 100644 --- a/packages/typescript-estree/jest.config.js +++ b/packages/typescript-estree/jest.config.js @@ -10,4 +10,12 @@ module.exports = { collectCoverageFrom: ['src/**/*.{js,jsx,ts,tsx}'], moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'], coverageReporters: ['text-summary', 'lcov'], + globals: { + 'ts-jest': { + diagnostics: { + // ignore the diagnostic error for the invalidFileErrors fixtures + ignoreCodes: [5056], + }, + }, + }, }; diff --git a/packages/typescript-estree/src/convert.ts b/packages/typescript-estree/src/convert.ts index 3a7d2de99dc..43ab7889c6b 100644 --- a/packages/typescript-estree/src/convert.ts +++ b/packages/typescript-estree/src/convert.ts @@ -67,7 +67,7 @@ export class Converter { */ constructor(ast: ts.SourceFile, options: ConverterOptions) { this.ast = ast; - this.options = options; + this.options = { ...options }; } getASTMaps(): ASTMaps { diff --git a/packages/typescript-estree/src/parser.ts b/packages/typescript-estree/src/parser.ts index f215fa44492..1b89136928a 100644 --- a/packages/typescript-estree/src/parser.ts +++ b/packages/typescript-estree/src/parser.ts @@ -1,3 +1,4 @@ +import path from 'path'; import semver from 'semver'; import * as ts from 'typescript'; // leave this as * as ts so people using util package don't need syntheticDefaultImports import { astConverter } from './ast-converter'; @@ -9,6 +10,7 @@ import { TSESTree } from './ts-estree'; import { calculateProjectParserOptions, createProgram, + defaultCompilerOptions, } from './tsconfig-parser'; /** @@ -87,9 +89,39 @@ function getASTFromProject( ); if (!astAndProgram && !createDefaultProgram) { - throw new Error( - `If "parserOptions.project" has been set for @typescript-eslint/parser, ${filePath} must be included in at least one of the projects provided.`, - ); + // the file was either not matched within the tsconfig, or the extension wasn't expected + const errorLines = [ + '"parserOptions.project" has been set for @typescript-eslint/parser.', + `The file does not match your project config: ${filePath}.`, + ]; + let hasMatchedAnError = false; + + const fileExtension = path.extname(filePath); + if (!['.ts', '.tsx', '.js', '.jsx'].includes(fileExtension)) { + const nonStandardExt = `The extension for the file (${fileExtension}) is non-standard`; + if (extra.extraFileExtensions && extra.extraFileExtensions.length > 0) { + if (!extra.extraFileExtensions.includes(fileExtension)) { + errorLines.push( + `${nonStandardExt}. It should be added to your existing "parserOptions.extraFileExtensions".`, + ); + hasMatchedAnError = true; + } + } else { + errorLines.push( + `${nonStandardExt}. You should add "parserOptions.extraFileExtensions" to your config.`, + ); + hasMatchedAnError = true; + } + } + + if (!hasMatchedAnError) { + errorLines.push( + 'The file must be included in at least one of the projects provided.', + ); + hasMatchedAnError = true; + } + + throw new Error(errorLines.join('\n')); } return astAndProgram; @@ -158,6 +190,7 @@ function createNewProgram(code: string): ASTAndProgram { noResolve: true, target: ts.ScriptTarget.Latest, jsx: extra.jsx ? ts.JsxEmit.Preserve : undefined, + ...defaultCompilerOptions, }, compilerHost, ); diff --git a/packages/typescript-estree/src/tsconfig-parser.ts b/packages/typescript-estree/src/tsconfig-parser.ts index 88c63171545..affbe090627 100644 --- a/packages/typescript-estree/src/tsconfig-parser.ts +++ b/packages/typescript-estree/src/tsconfig-parser.ts @@ -9,9 +9,10 @@ import { Extra } from './parser-options'; /** * Default compiler options for program generation from single root file */ -const defaultCompilerOptions: ts.CompilerOptions = { +export const defaultCompilerOptions: ts.CompilerOptions = { allowNonTsExtensions: true, allowJs: true, + checkJs: true, }; /** @@ -109,7 +110,7 @@ export function calculateProjectParserOptions( // create compiler host const watchCompilerHost = ts.createWatchCompilerHost( tsconfigPath, - /*optionsToExtend*/ { allowNonTsExtensions: true } as ts.CompilerOptions, + defaultCompilerOptions, ts.sys, ts.createSemanticDiagnosticsBuilderProgram, diagnosticReporter, diff --git a/packages/typescript-estree/tests/fixtures/invalidFileErrors/js/included.js b/packages/typescript-estree/tests/fixtures/invalidFileErrors/js/included.js new file mode 100644 index 00000000000..25005f98f8f --- /dev/null +++ b/packages/typescript-estree/tests/fixtures/invalidFileErrors/js/included.js @@ -0,0 +1 @@ +export var a = true; diff --git a/packages/typescript-estree/tests/fixtures/invalidFileErrors/js/included.jsx b/packages/typescript-estree/tests/fixtures/invalidFileErrors/js/included.jsx new file mode 100644 index 00000000000..25005f98f8f --- /dev/null +++ b/packages/typescript-estree/tests/fixtures/invalidFileErrors/js/included.jsx @@ -0,0 +1 @@ +export var a = true; diff --git a/packages/typescript-estree/tests/fixtures/invalidFileErrors/js/notIncluded.js b/packages/typescript-estree/tests/fixtures/invalidFileErrors/js/notIncluded.js new file mode 100644 index 00000000000..25005f98f8f --- /dev/null +++ b/packages/typescript-estree/tests/fixtures/invalidFileErrors/js/notIncluded.js @@ -0,0 +1 @@ +export var a = true; diff --git a/packages/typescript-estree/tests/fixtures/invalidFileErrors/js/notIncluded.jsx b/packages/typescript-estree/tests/fixtures/invalidFileErrors/js/notIncluded.jsx new file mode 100644 index 00000000000..25005f98f8f --- /dev/null +++ b/packages/typescript-estree/tests/fixtures/invalidFileErrors/js/notIncluded.jsx @@ -0,0 +1 @@ +export var a = true; diff --git a/packages/typescript-estree/tests/fixtures/invalidFileErrors/other/included.vue b/packages/typescript-estree/tests/fixtures/invalidFileErrors/other/included.vue new file mode 100644 index 00000000000..25005f98f8f --- /dev/null +++ b/packages/typescript-estree/tests/fixtures/invalidFileErrors/other/included.vue @@ -0,0 +1 @@ +export var a = true; diff --git a/packages/typescript-estree/tests/fixtures/invalidFileErrors/other/notIncluded.vue b/packages/typescript-estree/tests/fixtures/invalidFileErrors/other/notIncluded.vue new file mode 100644 index 00000000000..25005f98f8f --- /dev/null +++ b/packages/typescript-estree/tests/fixtures/invalidFileErrors/other/notIncluded.vue @@ -0,0 +1 @@ +export var a = true; diff --git a/packages/typescript-estree/tests/fixtures/invalidFileErrors/other/unknownFileType.unknown b/packages/typescript-estree/tests/fixtures/invalidFileErrors/other/unknownFileType.unknown new file mode 100644 index 00000000000..25005f98f8f --- /dev/null +++ b/packages/typescript-estree/tests/fixtures/invalidFileErrors/other/unknownFileType.unknown @@ -0,0 +1 @@ +export var a = true; diff --git a/packages/typescript-estree/tests/fixtures/invalidFileErrors/ts/included.ts b/packages/typescript-estree/tests/fixtures/invalidFileErrors/ts/included.ts new file mode 100644 index 00000000000..25005f98f8f --- /dev/null +++ b/packages/typescript-estree/tests/fixtures/invalidFileErrors/ts/included.ts @@ -0,0 +1 @@ +export var a = true; diff --git a/packages/typescript-estree/tests/fixtures/invalidFileErrors/ts/included.tsx b/packages/typescript-estree/tests/fixtures/invalidFileErrors/ts/included.tsx new file mode 100644 index 00000000000..25005f98f8f --- /dev/null +++ b/packages/typescript-estree/tests/fixtures/invalidFileErrors/ts/included.tsx @@ -0,0 +1 @@ +export var a = true; diff --git a/packages/typescript-estree/tests/fixtures/invalidFileErrors/ts/notIncluded.ts b/packages/typescript-estree/tests/fixtures/invalidFileErrors/ts/notIncluded.ts new file mode 100644 index 00000000000..25005f98f8f --- /dev/null +++ b/packages/typescript-estree/tests/fixtures/invalidFileErrors/ts/notIncluded.ts @@ -0,0 +1 @@ +export var a = true; diff --git a/packages/typescript-estree/tests/fixtures/invalidFileErrors/ts/notIncluded.tsx b/packages/typescript-estree/tests/fixtures/invalidFileErrors/ts/notIncluded.tsx new file mode 100644 index 00000000000..25005f98f8f --- /dev/null +++ b/packages/typescript-estree/tests/fixtures/invalidFileErrors/ts/notIncluded.tsx @@ -0,0 +1 @@ +export var a = true; diff --git a/packages/typescript-estree/tests/fixtures/invalidFileErrors/tsconfig.json b/packages/typescript-estree/tests/fixtures/invalidFileErrors/tsconfig.json new file mode 100644 index 00000000000..de5d69d736c --- /dev/null +++ b/packages/typescript-estree/tests/fixtures/invalidFileErrors/tsconfig.json @@ -0,0 +1,9 @@ +{ + "include": [ + "ts/included.ts", + "ts/included.tsx", + "js/included.js", + "js/included.jsx", + "other/included.vue" + ] +} diff --git a/packages/typescript-estree/tests/lib/__snapshots__/parse.ts.snap b/packages/typescript-estree/tests/lib/__snapshots__/parse.ts.snap index 38e378d9f7e..bca89ec8d8c 100644 --- a/packages/typescript-estree/tests/lib/__snapshots__/parse.ts.snap +++ b/packages/typescript-estree/tests/lib/__snapshots__/parse.ts.snap @@ -189,6 +189,42 @@ Object { } `; +exports[`parse() invalid file error messages "parserOptions.extraFileExtensions" is non-empty the extension does not match 1`] = ` +"\\"parserOptions.project\\" has been set for @typescript-eslint/parser. +The file does not match your project config: tests/fixtures/invalidFileErrors/other/unknownFileType.unknown. +The extension for the file (.unknown) is non-standard. It should be added to your existing \\"parserOptions.extraFileExtensions\\"." +`; + +exports[`parse() invalid file error messages "parserOptions.extraFileExtensions" is non-empty the extension matches the file isn't included 1`] = ` +"\\"parserOptions.project\\" has been set for @typescript-eslint/parser. +The file does not match your project config: tests/fixtures/invalidFileErrors/other/notIncluded.vue. +The file must be included in at least one of the projects provided." +`; + +exports[`parse() invalid file error messages project includes errors for not included files 1`] = ` +"\\"parserOptions.project\\" has been set for @typescript-eslint/parser. +The file does not match your project config: tests/fixtures/invalidFileErrors/ts/notIncluded.ts. +The file must be included in at least one of the projects provided." +`; + +exports[`parse() invalid file error messages project includes errors for not included files 2`] = ` +"\\"parserOptions.project\\" has been set for @typescript-eslint/parser. +The file does not match your project config: tests/fixtures/invalidFileErrors/ts/notIncluded.tsx. +The file must be included in at least one of the projects provided." +`; + +exports[`parse() invalid file error messages project includes errors for not included files 3`] = ` +"\\"parserOptions.project\\" has been set for @typescript-eslint/parser. +The file does not match your project config: tests/fixtures/invalidFileErrors/js/notIncluded.js. +The file must be included in at least one of the projects provided." +`; + +exports[`parse() invalid file error messages project includes errors for not included files 4`] = ` +"\\"parserOptions.project\\" has been set for @typescript-eslint/parser. +The file does not match your project config: tests/fixtures/invalidFileErrors/js/notIncluded.jsx. +The file must be included in at least one of the projects provided." +`; + exports[`parse() non string code should correctly convert code to a string for parse() 1`] = ` Object { "body": Array [ diff --git a/packages/typescript-estree/tests/lib/parse.ts b/packages/typescript-estree/tests/lib/parse.ts index 57a4bc05742..6ed8db76cbc 100644 --- a/packages/typescript-estree/tests/lib/parse.ts +++ b/packages/typescript-estree/tests/lib/parse.ts @@ -1,8 +1,8 @@ +import { join, resolve, relative } from 'path'; import * as parser from '../../src/parser'; import * as astConverter from '../../src/ast-converter'; import { TSESTreeOptions } from '../../src/parser-options'; import { createSnapshotTestBlock } from '../../tools/test-utils'; -import { join } from 'path'; const FIXTURES_DIR = './tests/fixtures/simpleProject'; @@ -145,7 +145,7 @@ describe('parse()', () => { }; const projectConfig: TSESTreeOptions = { ...baseConfig, - tsconfigRootDir: join(process.cwd(), FIXTURES_DIR), + tsconfigRootDir: FIXTURES_DIR, project: './tsconfig.json', }; @@ -241,4 +241,60 @@ describe('parse()', () => { ).toBeUndefined(); }); }); + + describe('invalid file error messages', () => { + const PROJECT_DIR = resolve(FIXTURES_DIR, '../invalidFileErrors'); + const code = 'var a = true'; + const config: TSESTreeOptions = { + comment: true, + tokens: true, + range: true, + loc: true, + tsconfigRootDir: PROJECT_DIR, + project: './tsconfig.json', + extraFileExtensions: ['.vue'], + }; + const testParse = (filePath: string) => (): void => { + parser.parseAndGenerateServices(code, { + ...config, + filePath: relative(process.cwd(), join(PROJECT_DIR, filePath)), + }); + }; + + describe('project includes', () => { + it("doesn't error for matched files", () => { + expect(testParse('ts/included.ts')).not.toThrow(); + expect(testParse('ts/included.tsx')).not.toThrow(); + expect(testParse('js/included.js')).not.toThrow(); + expect(testParse('js/included.jsx')).not.toThrow(); + }); + + it('errors for not included files', () => { + expect(testParse('ts/notIncluded.ts')).toThrowErrorMatchingSnapshot(); + expect(testParse('ts/notIncluded.tsx')).toThrowErrorMatchingSnapshot(); + expect(testParse('js/notIncluded.js')).toThrowErrorMatchingSnapshot(); + expect(testParse('js/notIncluded.jsx')).toThrowErrorMatchingSnapshot(); + }); + }); + + describe('"parserOptions.extraFileExtensions" is non-empty', () => { + describe('the extension matches', () => { + it('the file is included', () => { + expect(testParse('other/included.vue')).not.toThrow(); + }); + + it("the file isn't included", () => { + expect( + testParse('other/notIncluded.vue'), + ).toThrowErrorMatchingSnapshot(); + }); + }); + + it('the extension does not match', () => { + expect( + testParse('other/unknownFileType.unknown'), + ).toThrowErrorMatchingSnapshot(); + }); + }); + }); }); diff --git a/packages/typescript-estree/tests/lib/semanticInfo.ts b/packages/typescript-estree/tests/lib/semanticInfo.ts index 1afab0c5e44..157edec7412 100644 --- a/packages/typescript-estree/tests/lib/semanticInfo.ts +++ b/packages/typescript-estree/tests/lib/semanticInfo.ts @@ -236,9 +236,7 @@ describe('semanticInfo', () => { `function M() { return Base }`, createOptions(''), ), - ).toThrow( - `If "parserOptions.project" has been set for @typescript-eslint/parser, must be included in at least one of the projects provided.`, - ); + ).toThrow(/The file does not match your project config: /); }); it('non-existent project file', () => { diff --git a/packages/typescript-estree/tsconfig.json b/packages/typescript-estree/tsconfig.json index e389d7edef3..2ea9199d263 100644 --- a/packages/typescript-estree/tsconfig.json +++ b/packages/typescript-estree/tsconfig.json @@ -3,5 +3,6 @@ "compilerOptions": { "outDir": "./dist" }, - "include": ["src", "tests", "tools"] + "include": ["src", "tests", "tools"], + "exclude": ["tests/fixtures/**/*"] } From 9e5f21e65afee3b6f4384f6cf1e45ff4d8e720a8 Mon Sep 17 00:00:00 2001 From: Teodor Tanasoaia <28601907+Teoxoy@users.noreply.github.com> Date: Mon, 19 Aug 2019 18:11:18 +0200 Subject: [PATCH 24/28] feat(typescript-estree): Accept a glob pattern for `options.project` (#806) --- packages/parser/README.md | 18 +++++++++++++++++- packages/typescript-estree/package.json | 3 +++ packages/typescript-estree/src/parser.ts | 11 +++++++++++ .../typescript-estree/src/tsconfig-parser.ts | 4 ++-- yarn.lock | 7 ++++++- 5 files changed, 39 insertions(+), 4 deletions(-) diff --git a/packages/parser/README.md b/packages/parser/README.md index 718e2c82ba5..c0e0187b314 100644 --- a/packages/parser/README.md +++ b/packages/parser/README.md @@ -50,9 +50,25 @@ The following additional configuration options are available by specifying them - **`project`** - default `undefined`. This option allows you to provide a path to your project's `tsconfig.json`. **This setting is required if you want to use rules which require type information**. You may want to use this setting in tandem with the `tsconfigRootDir` option below. + - Accepted values: + + ```js + // path + project: './tsconfig.json'; + + // glob pattern + project: './packages/**/tsconfig.json'; + + // array of paths and/or glob patterns + project: [ + './packages/**/tsconfig.json', + './separate-package/tsconfig.json', + ]; + ``` + - Note that if this setting is specified and `createDefaultProgram` is not, you must only lint files that are included in the projects as defined by the provided `tsconfig.json` files. If your existing configuration does not include all of the files you would like to lint, you can create a separate `tsconfig.eslint.json` as follows: - ```ts + ```js { // extend your base config so you don't have to redefine your compilerOptions "extends": "./tsconfig.json", diff --git a/packages/typescript-estree/package.json b/packages/typescript-estree/package.json index d7c8bae66ef..9400fb5def3 100644 --- a/packages/typescript-estree/package.json +++ b/packages/typescript-estree/package.json @@ -41,6 +41,8 @@ "unit-tests": "jest \"./tests/lib/.*\"" }, "dependencies": { + "glob": "^7.1.4", + "is-glob": "^4.0.1", "lodash.unescape": "4.0.1", "semver": "^6.2.0" }, @@ -50,6 +52,7 @@ "@babel/types": "^7.3.2", "@types/babel-code-frame": "^6.20.1", "@types/glob": "^7.1.1", + "@types/is-glob": "^4.0.1", "@types/lodash.isplainobject": "^4.0.4", "@types/lodash.unescape": "^4.0.4", "@types/semver": "^6.0.1", diff --git a/packages/typescript-estree/src/parser.ts b/packages/typescript-estree/src/parser.ts index 1b89136928a..df8df03f0c2 100644 --- a/packages/typescript-estree/src/parser.ts +++ b/packages/typescript-estree/src/parser.ts @@ -1,6 +1,8 @@ import path from 'path'; import semver from 'semver'; import * as ts from 'typescript'; // leave this as * as ts so people using util package don't need syntheticDefaultImports +import { sync as globSync } from 'glob'; +import isGlob from 'is-glob'; import { astConverter } from './ast-converter'; import { convertError } from './convert'; import { firstDefined } from './node-utils'; @@ -285,6 +287,15 @@ function applyParserOptionsToExtra(options: TSESTreeOptions): void { extra.projects = options.project; } + // Transform glob patterns into paths + if (extra.projects) { + extra.projects = extra.projects.reduce( + (projects, project) => + projects.concat(isGlob(project) ? globSync(project) : project), + [], + ); + } + if (typeof options.tsconfigRootDir === 'string') { extra.tsconfigRootDir = options.tsconfigRootDir; } diff --git a/packages/typescript-estree/src/tsconfig-parser.ts b/packages/typescript-estree/src/tsconfig-parser.ts index affbe090627..364dd0543c8 100644 --- a/packages/typescript-estree/src/tsconfig-parser.ts +++ b/packages/typescript-estree/src/tsconfig-parser.ts @@ -72,7 +72,7 @@ function getTsconfigPath(tsconfigPath: string, extra: Extra): string { * @param code The code being linted * @param filePath The path of the file being parsed * @param extra.tsconfigRootDir The root directory for relative tsconfig paths - * @param extra.project Provided tsconfig paths + * @param extra.projects Provided tsconfig paths * @returns The programs corresponding to the supplied tsconfig paths */ export function calculateProjectParserOptions( @@ -207,7 +207,7 @@ export function calculateProjectParserOptions( * @param code The code being linted * @param filePath The file being linted * @param extra.tsconfigRootDir The root directory for relative tsconfig paths - * @param extra.project Provided tsconfig paths + * @param extra.projects Provided tsconfig paths * @returns The program containing just the file being linted and associated library files */ export function createProgram( diff --git a/yarn.lock b/yarn.lock index c86527946ff..559279edf28 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1345,6 +1345,11 @@ "@types/minimatch" "*" "@types/node" "*" +"@types/is-glob@^4.0.1": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@types/is-glob/-/is-glob-4.0.1.tgz#a93eec1714172c8eb3225a1cc5eb88c2477b7d00" + integrity sha512-k3RS5HyBPu4h+5hTmIEfPB2rl5P3LnGdQEZrV2b9OWTJVtsUQ2VBcedqYKGqxvZqle5UALUXdSfVA8nf3HfyWQ== + "@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0": version "2.0.1" resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.1.tgz#42995b446db9a48a11a07ec083499a860e9138ff" @@ -4706,7 +4711,7 @@ is-glob@^3.1.0: dependencies: is-extglob "^2.1.0" -is-glob@^4.0.0: +is-glob@^4.0.0, is-glob@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== From 4a29098c5b2484c144f48594d6a62ec342a5cf30 Mon Sep 17 00:00:00 2001 From: Brad Zacher Date: Mon, 19 Aug 2019 17:21:15 -0700 Subject: [PATCH 25/28] docs(parser): clarify project references (#884) Closes #856 --- packages/parser/README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/parser/README.md b/packages/parser/README.md index c0e0187b314..042fb4f5f54 100644 --- a/packages/parser/README.md +++ b/packages/parser/README.md @@ -66,9 +66,11 @@ The following additional configuration options are available by specifying them ]; ``` + - Note that if you use project references, TypeScript will not automatically use project references to resolve files. This means that you will have to add each referenced tsconfig to the `project` field either separately, or via a glob. + - Note that if this setting is specified and `createDefaultProgram` is not, you must only lint files that are included in the projects as defined by the provided `tsconfig.json` files. If your existing configuration does not include all of the files you would like to lint, you can create a separate `tsconfig.eslint.json` as follows: - ```js + ```jsonc { // extend your base config so you don't have to redefine your compilerOptions "extends": "./tsconfig.json", From c1c94601a1ab578b2521e3c4fba12650d02a700b Mon Sep 17 00:00:00 2001 From: Masafumi Koba <473530+ybiquitous@users.noreply.github.com> Date: Wed, 21 Aug 2019 01:15:24 +0900 Subject: [PATCH 26/28] docs(eslint-plugin): [explicit-member-accessibility] example (#887) --- .../docs/rules/explicit-member-accessibility.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/eslint-plugin/docs/rules/explicit-member-accessibility.md b/packages/eslint-plugin/docs/rules/explicit-member-accessibility.md index 3ed9bb7e5d0..9f8c313d3df 100644 --- a/packages/eslint-plugin/docs/rules/explicit-member-accessibility.md +++ b/packages/eslint-plugin/docs/rules/explicit-member-accessibility.md @@ -79,7 +79,7 @@ The following patterns are considered correct with the default options `{ access ```ts class Animal { - public constructor(public breed, animalName) { + public constructor(public breed, name) { // Parameter property and constructor this.animalName = name; } @@ -102,7 +102,7 @@ The following patterns are considered incorrect with the accessibility set to ** ```ts class Animal { - public constructor(public breed, animalName) { + public constructor(public breed, name) { // Parameter property and constructor this.animalName = name; } @@ -125,7 +125,7 @@ The following patterns are considered correct with the accessibility set to **no ```ts class Animal { - constructor(protected breed, animalName) { + constructor(protected breed, name) { // Parameter property and constructor this.name = name; } From 2a710233e7b00c811c91f866f5d4a1c258bac250 Mon Sep 17 00:00:00 2001 From: Niles Date: Wed, 21 Aug 2019 10:42:58 -0500 Subject: [PATCH 27/28] docs(eslint-plugin): correct typo (#891) --- packages/eslint-plugin/src/configs/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/eslint-plugin/src/configs/README.md b/packages/eslint-plugin/src/configs/README.md index 3ba76f661e6..95accb69490 100644 --- a/packages/eslint-plugin/src/configs/README.md +++ b/packages/eslint-plugin/src/configs/README.md @@ -52,4 +52,4 @@ If you disagree with a rule (or it disagrees with your codebase), consider using ### Suggesting changes to the recommended set -If you feel _very_, **very**, **_very_** strongly that a specific rule should (or should not) be in the recommended ruleset, please feel free to file an issue along with a **detailed** argument explaning your reasoning. We expect to see you citing concrete evidence supporting why (or why not) a rule is considered best practice. **Please note that if your reasoning is along the lines of "it's what my project/company does", or "I don't like the rule", then we will likely close the request without discussion.** +If you feel _very_, **very**, **_very_** strongly that a specific rule should (or should not) be in the recommended ruleset, please feel free to file an issue along with a **detailed** argument explaining your reasoning. We expect to see you citing concrete evidence supporting why (or why not) a rule is considered best practice. **Please note that if your reasoning is along the lines of "it's what my project/company does", or "I don't like the rule", then we will likely close the request without discussion.** From 6a30de2e00828563d2ddedf5d931f30d07f1a682 Mon Sep 17 00:00:00 2001 From: Brad Zacher Date: Wed, 21 Aug 2019 09:11:41 -0700 Subject: [PATCH 28/28] docs: update contributors list --- .all-contributorsrc | 106 +++++++++++++++++++++++++++------ .prettierignore | 2 + CONTRIBUTORS.md | 56 ++++++++++++++++- package.json | 2 +- tools/generate-contributors.ts | 2 +- yarn.lock | 8 +-- 6 files changed, 149 insertions(+), 27 deletions(-) diff --git a/.all-contributorsrc b/.all-contributorsrc index b6b3e43aa31..f44ccc64c55 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -23,13 +23,6 @@ "profile": "https://github.com/armano2", "contributions": [] }, - { - "login": "soda0289", - "name": "Reyad Attiyat", - "avatar_url": "https://avatars1.githubusercontent.com/u/2373964?v=4", - "profile": "https://github.com/soda0289", - "contributions": [] - }, { "login": "bradzacher", "name": "Brad Zacher", @@ -37,6 +30,13 @@ "profile": "https://github.com/bradzacher", "contributions": [] }, + { + "login": "soda0289", + "name": "Reyad Attiyat", + "avatar_url": "https://avatars1.githubusercontent.com/u/2373964?v=4", + "profile": "https://github.com/soda0289", + "contributions": [] + }, { "login": "weirdpattern", "name": "Patricio Trevino", @@ -60,7 +60,7 @@ }, { "login": "uniqueiniquity", - "name": "Benjamin Lichtman", + "name": "Ben Lichtman", "avatar_url": "https://avatars1.githubusercontent.com/u/9092011?v=4", "profile": "https://github.com/uniqueiniquity", "contributions": [] @@ -79,6 +79,20 @@ "profile": "https://github.com/Pajn", "contributions": [] }, + { + "login": "mysticatea", + "name": "Toru Nagashima", + "avatar_url": "https://avatars2.githubusercontent.com/u/1937871?v=4", + "profile": "https://github.com/mysticatea", + "contributions": [] + }, + { + "login": "JoshuaKGoldberg", + "name": "Josh Goldberg", + "avatar_url": "https://avatars1.githubusercontent.com/u/3335181?v=4", + "profile": "https://github.com/JoshuaKGoldberg", + "contributions": [] + }, { "login": "azz", "name": "Lucas Azzola", @@ -101,10 +115,10 @@ "contributions": [] }, { - "login": "mysticatea", - "name": "Toru Nagashima", - "avatar_url": "https://avatars2.githubusercontent.com/u/1937871?v=4", - "profile": "https://github.com/mysticatea", + "login": "scottohara", + "name": "Scott O'Hara", + "avatar_url": "https://avatars3.githubusercontent.com/u/289327?v=4", + "profile": "https://github.com/scottohara", "contributions": [] }, { @@ -121,6 +135,27 @@ "profile": "https://github.com/lukyth", "contributions": [] }, + { + "login": "ldrick", + "name": "Ricky Lippmann", + "avatar_url": "https://avatars3.githubusercontent.com/u/3674067?v=4", + "profile": "https://github.com/ldrick", + "contributions": [] + }, + { + "login": "SimenB", + "name": "Simen Bekkhus", + "avatar_url": "https://avatars1.githubusercontent.com/u/1404810?v=4", + "profile": "https://github.com/SimenB", + "contributions": [] + }, + { + "login": "gavinbarron", + "name": "Gavin Barron", + "avatar_url": "https://avatars2.githubusercontent.com/u/7122716?v=4", + "profile": "https://github.com/gavinbarron", + "contributions": [] + }, { "login": "platinumazure", "name": "Kevin Partington", @@ -128,18 +163,39 @@ "profile": "https://github.com/platinumazure", "contributions": [] }, + { + "login": "duailibe", + "name": "Lucas Duailibe", + "avatar_url": "https://avatars3.githubusercontent.com/u/1574588?v=4", + "profile": "https://github.com/duailibe", + "contributions": [] + }, + { + "login": "octogonz", + "name": "Pete Gonzalez", + "avatar_url": "https://avatars0.githubusercontent.com/u/4673363?v=4", + "profile": "https://github.com/octogonz", + "contributions": [] + }, { "login": "mightyiam", - "name": "Shahar Or", + "name": "Shahar Dawn Or", "avatar_url": "https://avatars2.githubusercontent.com/u/635591?v=4", "profile": "https://github.com/mightyiam", "contributions": [] }, { - "login": "invalid-email-address", - "name": "Check your git settings!", - "avatar_url": "https://avatars0.githubusercontent.com/u/148100?v=4", - "profile": "https://github.com/invalid-email-address", + "login": "a-tarasyuk", + "name": "Alexander T.", + "avatar_url": "https://avatars0.githubusercontent.com/u/509265?v=4", + "profile": "https://github.com/a-tarasyuk", + "contributions": [] + }, + { + "login": "webschik", + "name": "Denys Kniazevych", + "avatar_url": "https://avatars2.githubusercontent.com/u/1665314?v=4", + "profile": "https://github.com/webschik", "contributions": [] }, { @@ -155,7 +211,21 @@ "avatar_url": "https://avatars1.githubusercontent.com/u/17216317?v=4", "profile": "https://github.com/g-plane", "contributions": [] + }, + { + "login": "ThomasdenH", + "name": "Thomas den Hollander", + "avatar_url": "https://avatars0.githubusercontent.com/u/3889750?v=4", + "profile": "https://github.com/ThomasdenH", + "contributions": [] + }, + { + "login": "madbence", + "name": "Bence Dányi", + "avatar_url": "https://avatars2.githubusercontent.com/u/296735?v=4", + "profile": "https://github.com/madbence", + "contributions": [] } ], - "contributorsPerLine": 7 + "contributorsPerLine": 5 } \ No newline at end of file diff --git a/.prettierignore b/.prettierignore index a86a2f04fc9..aed86e816d4 100644 --- a/.prettierignore +++ b/.prettierignore @@ -9,6 +9,8 @@ packages/eslint-plugin-tslint/tests/test-tslint-rules-directory/alwaysFailRule.js .github packages/eslint-plugin/src/configs/*.json +.all-contributorsrc +CONTRIBUTORS.md # Ignore CHANGELOG.md files to avoid issues with automated release job CHANGELOG.md diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index d0f6e0b84d6..d458acc3c54 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -3,8 +3,58 @@ Thanks goes to these wonderful people ([emoji key](https://github.com/all-contributors/all-contributors#emoji-key)): - -
James Henry
James Henry

Armano
Armano

Reyad Attiyat
Reyad Attiyat

Brad Zacher
Brad Zacher

Patricio Trevino
Patricio Trevino

Nicholas C. Zakas
Nicholas C. Zakas

Jed Fox
Jed Fox

Benjamin Lichtman
Benjamin Lichtman

Kai Cataldo
Kai Cataldo

Rasmus Eneman
Rasmus Eneman

Lucas Azzola
Lucas Azzola

Danny Fritz
Danny Fritz

Ika
Ika

Toru Nagashima
Toru Nagashima

mackie
mackie

Kanitkorn Sujautra
Kanitkorn Sujautra

Kevin Partington
Kevin Partington

Shahar Or
Shahar Or

Check your git settings!
Check your git settings!

Philipp A.
Philipp A.

Pig Fang
Pig Fang

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
James Henry
James Henry

Armano
Armano

Brad Zacher
Brad Zacher

Reyad Attiyat
Reyad Attiyat

Patricio Trevino
Patricio Trevino

Nicholas C. Zakas
Nicholas C. Zakas

Jed Fox
Jed Fox

Ben Lichtman
Ben Lichtman

Kai Cataldo
Kai Cataldo

Rasmus Eneman
Rasmus Eneman

Toru Nagashima
Toru Nagashima

Josh Goldberg
Josh Goldberg

Lucas Azzola
Lucas Azzola

Danny Fritz
Danny Fritz

Ika
Ika

Scott O'Hara
Scott O'Hara

mackie
mackie

Kanitkorn Sujautra
Kanitkorn Sujautra

Ricky Lippmann
Ricky Lippmann

Simen Bekkhus
Simen Bekkhus

Gavin Barron
Gavin Barron

Kevin Partington
Kevin Partington

Lucas Duailibe
Lucas Duailibe

Pete Gonzalez
Pete Gonzalez

Shahar Dawn Or
Shahar Dawn Or

Alexander T.
Alexander T.

Denys Kniazevych
Denys Kniazevych

Philipp A.
Philipp A.

Pig Fang
Pig Fang

Thomas den Hollander
Thomas den Hollander

Bence Dányi
Bence Dányi

+ + + -This list is auto-generated using `yarn generate-contributors`. +This list is auto-generated using `yarn generate-contributors`. It shows the top 100 contributors with > 3 contributions. diff --git a/package.json b/package.json index d0c730ff1b4..da363951d70 100644 --- a/package.json +++ b/package.json @@ -55,7 +55,7 @@ "@commitlint/travis-cli": "^8.1.0", "@types/jest": "^24.0.15", "@types/node": "^12.6.8", - "all-contributors-cli": "^6.8.0", + "all-contributors-cli": "^6.8.1", "cz-conventional-changelog": "2.1.0", "eslint": "^6.0.0", "eslint-plugin-eslint-comments": "^3.1.2", diff --git a/tools/generate-contributors.ts b/tools/generate-contributors.ts index 11b23765615..86de0772395 100644 --- a/tools/generate-contributors.ts +++ b/tools/generate-contributors.ts @@ -99,7 +99,7 @@ async function main(): Promise { imageSize: 100, commit: false, contributors, - contributorsPerLine: 7, + contributorsPerLine: 5, }; const rcPath = path.resolve(__dirname, '../.all-contributorsrc'); fs.writeFileSync(rcPath, JSON.stringify(allContributorsConfig, null, 2)); diff --git a/yarn.lock b/yarn.lock index 559279edf28..7fef7edc509 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1706,10 +1706,10 @@ ajv@^6.10.0, ajv@^6.5.5: json-schema-traverse "^0.4.1" uri-js "^4.2.2" -all-contributors-cli@^6.8.0: - version "6.8.0" - resolved "https://registry.yarnpkg.com/all-contributors-cli/-/all-contributors-cli-6.8.0.tgz#1b98e9ee60ca3724ef50fb7469b8e85de1ebdec9" - integrity sha512-7xYAmljxgGL4w0XTRBuGTJMqf/xTGhvPyRbIp2InKfn0INo08faCT6gP18iyYMpVPotgAUcaGTeLrewh2IP54Q== +all-contributors-cli@^6.8.1: + version "6.8.1" + resolved "https://registry.yarnpkg.com/all-contributors-cli/-/all-contributors-cli-6.8.1.tgz#70c8c560ad05d054c09798d4a155887c82c5d553" + integrity sha512-06nnLE9Gl0gGqUIzmELNT/k8IWF31Xgq97GkuMJjEOS+3DFXuJ/0U+AJwa9UxP3Ivlqn484xXx4o3XDqPhytjA== dependencies: "@babel/runtime" "^7.2.0" async "^3.0.1"