From b2dbd890a5bef81aa6978d68c166457838ee04a1 Mon Sep 17 00:00:00 2001 From: Gareth Jones Date: Mon, 23 Mar 2020 18:10:17 +1300 Subject: [PATCH 01/36] feat(eslint-plugin): add `class-literal-property-style` rule (#1582) --- packages/eslint-plugin/README.md | 1 + .../rules/class-literal-property-style.md | 96 +++++ packages/eslint-plugin/src/configs/all.json | 1 + .../src/rules/class-literal-property-style.ts | 136 +++++++ packages/eslint-plugin/src/rules/index.ts | 2 + .../class-literal-property-style.test.ts | 378 ++++++++++++++++++ 6 files changed, 614 insertions(+) create mode 100644 packages/eslint-plugin/docs/rules/class-literal-property-style.md create mode 100644 packages/eslint-plugin/src/rules/class-literal-property-style.ts create mode 100644 packages/eslint-plugin/tests/rules/class-literal-property-style.test.ts diff --git a/packages/eslint-plugin/README.md b/packages/eslint-plugin/README.md index b7927757924..fa049b2cfb2 100644 --- a/packages/eslint-plugin/README.md +++ b/packages/eslint-plugin/README.md @@ -99,6 +99,7 @@ Pro Tip: For larger codebases you may want to consider splitting our linting int | [`@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-comment`](./docs/rules/ban-ts-comment.md) | Bans `// @ts-` 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/class-literal-property-style`](./docs/rules/class-literal-property-style.md) | Ensures that literals on classes are exposed in a consistent style | | :wrench: | | | [`@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: | | | diff --git a/packages/eslint-plugin/docs/rules/class-literal-property-style.md b/packages/eslint-plugin/docs/rules/class-literal-property-style.md new file mode 100644 index 00000000000..903a695dd76 --- /dev/null +++ b/packages/eslint-plugin/docs/rules/class-literal-property-style.md @@ -0,0 +1,96 @@ +# Ensures that literals on classes are exposed in a consistent style (`class-literal-property-style`) + +When writing TypeScript applications, it's typically safe to store literal values on classes using fields with the `readonly` modifier to prevent them from being reassigned. +When writing TypeScript libraries that could be used by Javascript users however, it's typically safer to expose these literals using `getter`s, since the `readonly` modifier is enforced at compile type. + +## Rule Details + +This rule aims to ensure that literals exposed by classes are done so consistently, in one of the two style described above. +By default this rule prefers the `fields` style as it means JS doesn't have to setup & teardown a function closure. + +Note that this rule only checks for constant _literal_ values (string, template string, number, bigint, boolean, regexp, null). It does not check objects or arrays, because a readonly field behaves differently to a getter in those cases. It also does not check functions, as it is a common pattern to use readonly fields with arrow function values as auto-bound methods. +This is because these types can be mutated and carry with them more complex implications about their usage. + +#### The `fields` style + +This style checks for any getter methods that return literal values, and requires them to be defined using fields with the `readonly` modifier instead. + +Examples of **correct** code with the `fields` style: + +```ts +/* eslint @typescript-eslint/class-literal-property-style: ["error", "fields"] */ + +class Mx { + public readonly myField1 = 1; + + // not a literal + public readonly myField2 = [1, 2, 3]; + + private readonly ['myField3'] = 'hello world'; + + public get myField4() { + return `hello from ${window.location.href}`; + } +} +``` + +Examples of **incorrect** code with the `fields` style: + +```ts +/* eslint @typescript-eslint/class-literal-property-style: ["error", "fields"] */ + +class Mx { + public static get myField1() { + return 1; + } + + private get ['myField2']() { + return 'hello world'; + } +} +``` + +#### The `getters` style + +This style checks for any `readonly` fields that are assigned literal values, and requires them to be defined as getters instead. +This style pairs well with the [`@typescript-eslint/prefer-readonly`](prefer-readonly.md) rule, +as it will identify fields that can be `readonly`, and thus should be made into getters. + +Examples of **correct** code with the `getters` style: + +```ts +/* eslint @typescript-eslint/class-literal-property-style: ["error", "getters"] */ + +class Mx { + // no readonly modifier + public myField1 = 'hello'; + + // not a literal + public readonly myField2 = [1, 2, 3]; + + public static get myField3() { + return 1; + } + + private get ['myField4']() { + return 'hello world'; + } +} +``` + +Examples of **incorrect** code with the `getters` style: + +```ts +/* eslint @typescript-eslint/class-literal-property-style: ["error", "getters"] */ + +class Mx { + readonly myField1 = 1; + readonly myField2 = `hello world`; + private readonly myField3 = 'hello world'; +} +``` + +## When Not To Use It + +When you have no strong preference, or do not wish to enforce a particular style +for how literal values are exposed by your classes. diff --git a/packages/eslint-plugin/src/configs/all.json b/packages/eslint-plugin/src/configs/all.json index 2626767b84c..c5575d4970d 100644 --- a/packages/eslint-plugin/src/configs/all.json +++ b/packages/eslint-plugin/src/configs/all.json @@ -8,6 +8,7 @@ "@typescript-eslint/ban-types": "error", "brace-style": "off", "@typescript-eslint/brace-style": "error", + "@typescript-eslint/class-literal-property-style": "error", "comma-spacing": "off", "@typescript-eslint/comma-spacing": "error", "@typescript-eslint/consistent-type-assertions": "error", diff --git a/packages/eslint-plugin/src/rules/class-literal-property-style.ts b/packages/eslint-plugin/src/rules/class-literal-property-style.ts new file mode 100644 index 00000000000..a4500cdfdbf --- /dev/null +++ b/packages/eslint-plugin/src/rules/class-literal-property-style.ts @@ -0,0 +1,136 @@ +import { + AST_NODE_TYPES, + TSESTree, +} from '@typescript-eslint/experimental-utils'; +import * as util from '../util'; + +type Options = ['fields' | 'getters']; +type MessageIds = 'preferFieldStyle' | 'preferGetterStyle'; + +interface NodeWithModifiers { + accessibility?: TSESTree.Accessibility; + static: boolean; +} + +const printNodeModifiers = ( + node: NodeWithModifiers, + final: 'readonly' | 'get', +): string => + `${node.accessibility ?? ''}${ + node.static ? ' static' : '' + } ${final} `.trimLeft(); + +const isSupportedLiteral = ( + node: TSESTree.Node, +): node is TSESTree.LiteralExpression => { + if ( + node.type === AST_NODE_TYPES.Literal || + node.type === AST_NODE_TYPES.BigIntLiteral + ) { + return true; + } + + if ( + node.type === AST_NODE_TYPES.TaggedTemplateExpression || + node.type === AST_NODE_TYPES.TemplateLiteral + ) { + return ('quasi' in node ? node.quasi.quasis : node.quasis).length === 1; + } + + return false; +}; + +export default util.createRule({ + name: 'class-literal-property-style', + meta: { + type: 'problem', + docs: { + description: + 'Ensures that literals on classes are exposed in a consistent style', + category: 'Best Practices', + recommended: false, + }, + fixable: 'code', + messages: { + preferFieldStyle: 'Literals should be exposed using readonly fields.', + preferGetterStyle: 'Literals should be exposed using getters.', + }, + schema: [{ enum: ['fields', 'getters'] }], + }, + defaultOptions: ['fields'], + create(context, [style]) { + if (style === 'fields') { + return { + MethodDefinition(node: TSESTree.MethodDefinition): void { + if ( + node.kind !== 'get' || + !node.value.body || + !node.value.body.body.length + ) { + return; + } + + const [statement] = node.value.body.body; + + if (statement.type !== AST_NODE_TYPES.ReturnStatement) { + return; + } + + const { argument } = statement; + + if (!argument || !isSupportedLiteral(argument)) { + return; + } + + context.report({ + node: node.key, + messageId: 'preferFieldStyle', + fix(fixer) { + const sourceCode = context.getSourceCode(); + const name = sourceCode.getText(node.key); + + let text = ''; + + text += printNodeModifiers(node, 'readonly'); + text += node.computed ? `[${name}]` : name; + text += ` = ${sourceCode.getText(argument)};`; + + return fixer.replaceText(node, text); + }, + }); + }, + }; + } + + return { + ClassProperty(node: TSESTree.ClassProperty): void { + if (!node.readonly || node.declare) { + return; + } + + const { value } = node; + + if (!value || !isSupportedLiteral(value)) { + return; + } + + context.report({ + node: node.key, + messageId: 'preferGetterStyle', + fix(fixer) { + const sourceCode = context.getSourceCode(); + const name = sourceCode.getText(node.key); + + let text = ''; + + text += printNodeModifiers(node, 'get'); + text += node.computed ? `[${name}]` : name; + text += `() { return ${sourceCode.getText(value)}; }`; + + return fixer.replaceText(node, text); + }, + }); + }, + }; + }, +}); diff --git a/packages/eslint-plugin/src/rules/index.ts b/packages/eslint-plugin/src/rules/index.ts index 486b8a97945..29db58b681a 100644 --- a/packages/eslint-plugin/src/rules/index.ts +++ b/packages/eslint-plugin/src/rules/index.ts @@ -7,6 +7,7 @@ import banTypes from './ban-types'; import braceStyle from './brace-style'; import camelcase from './camelcase'; import classNameCasing from './class-name-casing'; +import classLiteralPropertyStyle from './class-literal-property-style'; import commaSpacing from './comma-spacing'; import consistentTypeAssertions from './consistent-type-assertions'; import consistentTypeDefinitions from './consistent-type-definitions'; @@ -102,6 +103,7 @@ export default { 'brace-style': braceStyle, camelcase: camelcase, 'class-name-casing': classNameCasing, + 'class-literal-property-style': classLiteralPropertyStyle, 'comma-spacing': commaSpacing, 'consistent-type-assertions': consistentTypeAssertions, 'consistent-type-definitions': consistentTypeDefinitions, diff --git a/packages/eslint-plugin/tests/rules/class-literal-property-style.test.ts b/packages/eslint-plugin/tests/rules/class-literal-property-style.test.ts new file mode 100644 index 00000000000..f0c5f2afc05 --- /dev/null +++ b/packages/eslint-plugin/tests/rules/class-literal-property-style.test.ts @@ -0,0 +1,378 @@ +import rule from '../../src/rules/class-literal-property-style'; +import { RuleTester } from '../RuleTester'; + +const ruleTester = new RuleTester({ + parser: '@typescript-eslint/parser', +}); + +ruleTester.run('class-literal-property-style', rule, { + valid: [ + 'class Mx { declare readonly p1 = 1; }', + 'class Mx { readonly p1 = "hello world"; }', + 'class Mx { p1 = "hello world"; }', + 'class Mx { static p1 = "hello world"; }', + 'class Mx { p1: string; }', + 'class Mx { get p1(); }', + 'class Mx { get p1() {} }', + 'abstract class Mx { abstract get p1(): string }', + ` + class Mx { + get mySetting() { + if(this._aValue) { + return 'on'; + } + + return 'off'; + } + } + `, + ` + class Mx { + get mySetting() { + return \`build-\${process.env.build}\` + } + } + `, + ` + class Mx { + getMySetting() { + if(this._aValue) { + return 'on'; + } + + return 'off'; + } + } + `, + ` + class Mx { + public readonly myButton = styled.button\` + color: \${props => (props.primary ? 'hotpink' : 'turquoise')}; + \`; + } + `, + { + code: ` + class Mx { + public get myButton() { + return styled.button\` + color: \${props => (props.primary ? 'hotpink' : 'turquoise')}; + \`; + } + } + `, + options: ['fields'], + }, + { + code: 'class Mx { declare public readonly foo = 1; }', + options: ['getters'], + }, + { + code: 'class Mx { get p1() { return "hello world"; } }', + options: ['getters'], + }, + { + code: 'class Mx { p1 = "hello world"; }', + options: ['getters'], + }, + { + code: 'class Mx { p1: string; }', + options: ['getters'], + }, + { + code: 'class Mx { readonly p1 = [1, 2, 3]; }', + options: ['getters'], + }, + { + code: 'class Mx { static p1: string; }', + options: ['getters'], + }, + { + code: 'class Mx { static get p1() { return "hello world"; } }', + options: ['getters'], + }, + { + code: ` + class Mx { + public readonly myButton = styled.button\` + color: \${props => (props.primary ? 'hotpink' : 'turquoise')}; + \`; + } + `, + options: ['getters'], + }, + { + code: ` + class Mx { + public get myButton() { + return styled.button\` + color: \${props => (props.primary ? 'hotpink' : 'turquoise')}; + \`; + } + } + `, + options: ['getters'], + }, + ], + invalid: [ + { + code: 'class Mx { get p1() { return "hello world"; } }', + output: 'class Mx { readonly p1 = "hello world"; }', + errors: [ + { + messageId: 'preferFieldStyle', + column: 16, + line: 1, + }, + ], + }, + { + code: 'class Mx { get p1() { return `hello world`; } }', + output: 'class Mx { readonly p1 = `hello world`; }', + errors: [ + { + messageId: 'preferFieldStyle', + column: 16, + line: 1, + }, + ], + }, + { + code: 'class Mx { static get p1() { return "hello world"; } }', + output: 'class Mx { static readonly p1 = "hello world"; }', + errors: [ + { + messageId: 'preferFieldStyle', + column: 23, + line: 1, + }, + ], + }, + { + code: + 'class Mx { public static readonly static private public protected get foo() { return 1; } }', + output: 'class Mx { public static readonly foo = 1; }', + errors: [ + { + messageId: 'preferFieldStyle', + column: 71, + line: 1, + }, + ], + }, + { + code: ` + class Mx { + public get [myValue]() { + return 'a literal value'; + } + } + `, + output: ` + class Mx { + public readonly [myValue] = 'a literal value'; + } + `, + errors: [ + { + messageId: 'preferFieldStyle', + column: 23, + line: 3, + }, + ], + }, + { + code: ` + class Mx { + public get [myValue]() { + return 12345n; + } + } + `, + output: ` + class Mx { + public readonly [myValue] = 12345n; + } + `, + errors: [ + { + messageId: 'preferFieldStyle', + column: 23, + line: 3, + }, + ], + }, + { + code: ` + class Mx { + public readonly [myValue] = 'a literal value'; + } + `, + output: ` + class Mx { + public get [myValue]() { return 'a literal value'; } + } + `, + errors: [ + { + messageId: 'preferGetterStyle', + column: 28, + line: 3, + }, + ], + options: ['getters'], + }, + { + code: 'class Mx { readonly p1 = "hello world"; }', + output: 'class Mx { get p1() { return "hello world"; } }', + errors: [ + { + messageId: 'preferGetterStyle', + column: 21, + line: 1, + }, + ], + options: ['getters'], + }, + { + code: 'class Mx { readonly p1 = `hello world`; }', + output: 'class Mx { get p1() { return `hello world`; } }', + errors: [ + { + messageId: 'preferGetterStyle', + column: 21, + line: 1, + }, + ], + options: ['getters'], + }, + { + code: 'class Mx { static readonly p1 = "hello world"; }', + output: 'class Mx { static get p1() { return "hello world"; } }', + errors: [ + { + messageId: 'preferGetterStyle', + column: 28, + line: 1, + }, + ], + options: ['getters'], + }, + { + code: 'class Mx { protected get p1() { return "hello world"; } }', + output: 'class Mx { protected readonly p1 = "hello world"; }', + errors: [ + { + messageId: 'preferFieldStyle', + column: 26, + line: 1, + }, + ], + options: ['fields'], + }, + { + code: 'class Mx { protected readonly p1 = "hello world"; }', + output: 'class Mx { protected get p1() { return "hello world"; } }', + errors: [ + { + messageId: 'preferGetterStyle', + column: 31, + line: 1, + }, + ], + options: ['getters'], + }, + { + code: 'class Mx { public static get p1() { return "hello world"; } }', + output: 'class Mx { public static readonly p1 = "hello world"; }', + errors: [ + { + messageId: 'preferFieldStyle', + column: 30, + line: 1, + }, + ], + }, + { + code: 'class Mx { public static readonly p1 = "hello world"; }', + output: 'class Mx { public static get p1() { return "hello world"; } }', + errors: [ + { + messageId: 'preferGetterStyle', + column: 35, + line: 1, + }, + ], + options: ['getters'], + }, + { + code: ` + class Mx { + public get myValue() { + return gql\` + { + user(id: 5) { + firstName + lastName + } + } + \`; + } + } + `, + output: ` + class Mx { + public readonly myValue = gql\` + { + user(id: 5) { + firstName + lastName + } + } + \`; + } + `, + errors: [ + { + messageId: 'preferFieldStyle', + column: 22, + line: 3, + }, + ], + }, + { + code: ` + class Mx { + public readonly myValue = gql\` + { + user(id: 5) { + firstName + lastName + } + } + \`; + } + `, + output: ` + class Mx { + public get myValue() { return gql\` + { + user(id: 5) { + firstName + lastName + } + } + \`; } + } + `, + errors: [ + { + messageId: 'preferGetterStyle', + column: 27, + line: 3, + }, + ], + options: ['getters'], + }, + ], +}); From 9cd3e4fe53c0224c75767a3f127f19b86060e277 Mon Sep 17 00:00:00 2001 From: James Henry Date: Mon, 23 Mar 2020 17:02:04 +0000 Subject: [PATCH 02/36] chore: publish v2.25.0 --- CHANGELOG.md | 23 ++++++++++++++++++++ lerna.json | 2 +- packages/eslint-plugin-internal/CHANGELOG.md | 8 +++++++ packages/eslint-plugin-internal/package.json | 4 ++-- packages/eslint-plugin-tslint/CHANGELOG.md | 11 ++++++++++ packages/eslint-plugin-tslint/package.json | 6 ++--- packages/eslint-plugin/CHANGELOG.md | 20 +++++++++++++++++ packages/eslint-plugin/package.json | 4 ++-- packages/experimental-utils/CHANGELOG.md | 11 ++++++++++ packages/experimental-utils/package.json | 4 ++-- packages/parser/CHANGELOG.md | 8 +++++++ packages/parser/package.json | 8 +++---- packages/shared-fixtures/CHANGELOG.md | 8 +++++++ packages/shared-fixtures/package.json | 2 +- packages/typescript-estree/CHANGELOG.md | 11 ++++++++++ packages/typescript-estree/package.json | 4 ++-- 16 files changed, 117 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d1da108f54..df5d4854494 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,29 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [2.25.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.24.0...v2.25.0) (2020-03-23) + + +### Bug Fixes + +* only run publish_canary_version on master ([3814d4e](https://github.com/typescript-eslint/typescript-eslint/commit/3814d4e3b3c1552c7601b5d722b2a37c5a570841)) +* **eslint-plugin:** [quotes] false positive with backtick in import equals statement ([#1769](https://github.com/typescript-eslint/typescript-eslint/issues/1769)) ([199863d](https://github.com/typescript-eslint/typescript-eslint/commit/199863d35cb36bdb7178b8116d146258506644c7)) +* **eslint-plugin:** fix message of no-base-to-string ([#1755](https://github.com/typescript-eslint/typescript-eslint/issues/1755)) ([6646959](https://github.com/typescript-eslint/typescript-eslint/commit/6646959b255b08afe5175bba6621bad11b9e1d5e)) +* **eslint-plugin-tslint:** fix tslintConfig memoization key ([#1719](https://github.com/typescript-eslint/typescript-eslint/issues/1719)) ([abf1a2f](https://github.com/typescript-eslint/typescript-eslint/commit/abf1a2fa5574e41af8070be3d79a886ea2f989cc)), closes [typescript-eslint#1692](https://github.com/typescript-eslint/issues/1692) +* **typescript-estree:** export * regression from 133f622f ([#1751](https://github.com/typescript-eslint/typescript-eslint/issues/1751)) ([09d8afc](https://github.com/typescript-eslint/typescript-eslint/commit/09d8afca684635b5ac604bc1794240484a70ce91)) + + +### Features + +* **eslint-plugin:** [no-unnec-type-assertion] allow const assertions ([#1741](https://github.com/typescript-eslint/typescript-eslint/issues/1741)) ([f76a1b3](https://github.com/typescript-eslint/typescript-eslint/commit/f76a1b3e63afda9f239e46f4ad5b36c1d7a6e8da)) +* **eslint-plugin:** [no-unnecessary-condition] ignore basic array indexing false positives ([#1534](https://github.com/typescript-eslint/typescript-eslint/issues/1534)) ([2b9603d](https://github.com/typescript-eslint/typescript-eslint/commit/2b9603d868c57556d8cd6087685e798d74cb6f26)) +* **eslint-plugin:** add `class-literal-property-style` rule ([#1582](https://github.com/typescript-eslint/typescript-eslint/issues/1582)) ([b2dbd89](https://github.com/typescript-eslint/typescript-eslint/commit/b2dbd890a5bef81aa6978d68c166457838ee04a1)) +* **experimental-utils:** expose ast utility functions ([#1670](https://github.com/typescript-eslint/typescript-eslint/issues/1670)) ([3eb5d45](https://github.com/typescript-eslint/typescript-eslint/commit/3eb5d4525e95c8ab990f55588b8d830a02ce5a9c)) + + + + + # [2.24.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.23.0...v2.24.0) (2020-03-16) diff --git a/lerna.json b/lerna.json index b6ba0964df8..f7a5aa4c731 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "2.24.0", + "version": "2.25.0", "npmClient": "yarn", "useWorkspaces": true, "stream": true diff --git a/packages/eslint-plugin-internal/CHANGELOG.md b/packages/eslint-plugin-internal/CHANGELOG.md index e8e1b2cb841..42a8af8be87 100644 --- a/packages/eslint-plugin-internal/CHANGELOG.md +++ b/packages/eslint-plugin-internal/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [2.25.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.24.0...v2.25.0) (2020-03-23) + +**Note:** Version bump only for package @typescript-eslint/eslint-plugin-internal + + + + + # [2.24.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.23.0...v2.24.0) (2020-03-16) **Note:** Version bump only for package @typescript-eslint/eslint-plugin-internal diff --git a/packages/eslint-plugin-internal/package.json b/packages/eslint-plugin-internal/package.json index a318b9d4571..afbc489b9ba 100644 --- a/packages/eslint-plugin-internal/package.json +++ b/packages/eslint-plugin-internal/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/eslint-plugin-internal", - "version": "2.24.0", + "version": "2.25.0", "private": true, "main": "dist/index.js", "scripts": { @@ -12,6 +12,6 @@ "typecheck": "tsc -p tsconfig.json --noEmit" }, "dependencies": { - "@typescript-eslint/experimental-utils": "2.24.0" + "@typescript-eslint/experimental-utils": "2.25.0" } } diff --git a/packages/eslint-plugin-tslint/CHANGELOG.md b/packages/eslint-plugin-tslint/CHANGELOG.md index a2fd2d00412..38770408378 100644 --- a/packages/eslint-plugin-tslint/CHANGELOG.md +++ b/packages/eslint-plugin-tslint/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [2.25.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.24.0...v2.25.0) (2020-03-23) + + +### Bug Fixes + +* **eslint-plugin-tslint:** fix tslintConfig memoization key ([#1719](https://github.com/typescript-eslint/typescript-eslint/issues/1719)) ([abf1a2f](https://github.com/typescript-eslint/typescript-eslint/commit/abf1a2fa5574e41af8070be3d79a886ea2f989cc)), closes [typescript-eslint#1692](https://github.com/typescript-eslint/issues/1692) + + + + + # [2.24.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.23.0...v2.24.0) (2020-03-16) **Note:** Version bump only for package @typescript-eslint/eslint-plugin-tslint diff --git a/packages/eslint-plugin-tslint/package.json b/packages/eslint-plugin-tslint/package.json index 10ea4787726..a10074080b2 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": "2.24.0", + "version": "2.25.0", "main": "dist/index.js", "typings": "src/index.ts", "description": "TSLint wrapper plugin for ESLint", @@ -31,7 +31,7 @@ "typecheck": "tsc -p tsconfig.json --noEmit" }, "dependencies": { - "@typescript-eslint/experimental-utils": "2.24.0", + "@typescript-eslint/experimental-utils": "2.25.0", "lodash": "^4.17.15" }, "peerDependencies": { @@ -41,6 +41,6 @@ }, "devDependencies": { "@types/lodash": "^4.14.149", - "@typescript-eslint/parser": "2.24.0" + "@typescript-eslint/parser": "2.25.0" } } diff --git a/packages/eslint-plugin/CHANGELOG.md b/packages/eslint-plugin/CHANGELOG.md index 56a3265aba7..074bd66c7ca 100644 --- a/packages/eslint-plugin/CHANGELOG.md +++ b/packages/eslint-plugin/CHANGELOG.md @@ -3,6 +3,26 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [2.25.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.24.0...v2.25.0) (2020-03-23) + + +### Bug Fixes + +* **eslint-plugin:** [quotes] false positive with backtick in import equals statement ([#1769](https://github.com/typescript-eslint/typescript-eslint/issues/1769)) ([199863d](https://github.com/typescript-eslint/typescript-eslint/commit/199863d35cb36bdb7178b8116d146258506644c7)) +* **eslint-plugin:** fix message of no-base-to-string ([#1755](https://github.com/typescript-eslint/typescript-eslint/issues/1755)) ([6646959](https://github.com/typescript-eslint/typescript-eslint/commit/6646959b255b08afe5175bba6621bad11b9e1d5e)) + + +### Features + +* **eslint-plugin:** [no-unnec-type-assertion] allow const assertions ([#1741](https://github.com/typescript-eslint/typescript-eslint/issues/1741)) ([f76a1b3](https://github.com/typescript-eslint/typescript-eslint/commit/f76a1b3e63afda9f239e46f4ad5b36c1d7a6e8da)) +* **eslint-plugin:** [no-unnecessary-condition] ignore basic array indexing false positives ([#1534](https://github.com/typescript-eslint/typescript-eslint/issues/1534)) ([2b9603d](https://github.com/typescript-eslint/typescript-eslint/commit/2b9603d868c57556d8cd6087685e798d74cb6f26)) +* **eslint-plugin:** add `class-literal-property-style` rule ([#1582](https://github.com/typescript-eslint/typescript-eslint/issues/1582)) ([b2dbd89](https://github.com/typescript-eslint/typescript-eslint/commit/b2dbd890a5bef81aa6978d68c166457838ee04a1)) +* **experimental-utils:** expose ast utility functions ([#1670](https://github.com/typescript-eslint/typescript-eslint/issues/1670)) ([3eb5d45](https://github.com/typescript-eslint/typescript-eslint/commit/3eb5d4525e95c8ab990f55588b8d830a02ce5a9c)) + + + + + # [2.24.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.23.0...v2.24.0) (2020-03-16) **Note:** Version bump only for package @typescript-eslint/eslint-plugin diff --git a/packages/eslint-plugin/package.json b/packages/eslint-plugin/package.json index 4855beab6a2..cb725f5be6d 100644 --- a/packages/eslint-plugin/package.json +++ b/packages/eslint-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/eslint-plugin", - "version": "2.24.0", + "version": "2.25.0", "description": "TypeScript plugin for ESLint", "keywords": [ "eslint", @@ -41,7 +41,7 @@ "typecheck": "tsc -p tsconfig.json --noEmit" }, "dependencies": { - "@typescript-eslint/experimental-utils": "2.24.0", + "@typescript-eslint/experimental-utils": "2.25.0", "functional-red-black-tree": "^1.0.1", "regexpp": "^3.0.0", "tsutils": "^3.17.1" diff --git a/packages/experimental-utils/CHANGELOG.md b/packages/experimental-utils/CHANGELOG.md index 33f3bce56c0..6a731bc439b 100644 --- a/packages/experimental-utils/CHANGELOG.md +++ b/packages/experimental-utils/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [2.25.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.24.0...v2.25.0) (2020-03-23) + + +### Features + +* **experimental-utils:** expose ast utility functions ([#1670](https://github.com/typescript-eslint/typescript-eslint/issues/1670)) ([3eb5d45](https://github.com/typescript-eslint/typescript-eslint/commit/3eb5d4525e95c8ab990f55588b8d830a02ce5a9c)) + + + + + # [2.24.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.23.0...v2.24.0) (2020-03-16) **Note:** Version bump only for package @typescript-eslint/experimental-utils diff --git a/packages/experimental-utils/package.json b/packages/experimental-utils/package.json index 287b52001c6..52b42129a61 100644 --- a/packages/experimental-utils/package.json +++ b/packages/experimental-utils/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/experimental-utils", - "version": "2.24.0", + "version": "2.25.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": "2.24.0", + "@typescript-eslint/typescript-estree": "2.25.0", "eslint-scope": "^5.0.0", "eslint-utils": "^2.0.0" }, diff --git a/packages/parser/CHANGELOG.md b/packages/parser/CHANGELOG.md index 6f5b20e66e8..ce9056408ac 100644 --- a/packages/parser/CHANGELOG.md +++ b/packages/parser/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [2.25.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.24.0...v2.25.0) (2020-03-23) + +**Note:** Version bump only for package @typescript-eslint/parser + + + + + # [2.24.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.23.0...v2.24.0) (2020-03-16) diff --git a/packages/parser/package.json b/packages/parser/package.json index da6fbd2b5e8..33047db7557 100644 --- a/packages/parser/package.json +++ b/packages/parser/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/parser", - "version": "2.24.0", + "version": "2.25.0", "description": "An ESLint custom parser which leverages TypeScript ESTree", "main": "dist/parser.js", "types": "dist/parser.d.ts", @@ -43,13 +43,13 @@ }, "dependencies": { "@types/eslint-visitor-keys": "^1.0.0", - "@typescript-eslint/experimental-utils": "2.24.0", - "@typescript-eslint/typescript-estree": "2.24.0", + "@typescript-eslint/experimental-utils": "2.25.0", + "@typescript-eslint/typescript-estree": "2.25.0", "eslint-visitor-keys": "^1.1.0" }, "devDependencies": { "@types/glob": "^7.1.1", - "@typescript-eslint/shared-fixtures": "2.24.0", + "@typescript-eslint/shared-fixtures": "2.25.0", "glob": "*" }, "peerDependenciesMeta": { diff --git a/packages/shared-fixtures/CHANGELOG.md b/packages/shared-fixtures/CHANGELOG.md index 70196052327..535e49bbb58 100644 --- a/packages/shared-fixtures/CHANGELOG.md +++ b/packages/shared-fixtures/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [2.25.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.24.0...v2.25.0) (2020-03-23) + +**Note:** Version bump only for package @typescript-eslint/shared-fixtures + + + + + # [2.24.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.23.0...v2.24.0) (2020-03-16) diff --git a/packages/shared-fixtures/package.json b/packages/shared-fixtures/package.json index 360b466615b..4775ceb0f14 100644 --- a/packages/shared-fixtures/package.json +++ b/packages/shared-fixtures/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/shared-fixtures", - "version": "2.24.0", + "version": "2.25.0", "private": true, "scripts": { "build": "tsc -b tsconfig.build.json", diff --git a/packages/typescript-estree/CHANGELOG.md b/packages/typescript-estree/CHANGELOG.md index b52cddede33..91b7426e23d 100644 --- a/packages/typescript-estree/CHANGELOG.md +++ b/packages/typescript-estree/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [2.25.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.24.0...v2.25.0) (2020-03-23) + + +### Bug Fixes + +* **typescript-estree:** export * regression from 133f622f ([#1751](https://github.com/typescript-eslint/typescript-eslint/issues/1751)) ([09d8afc](https://github.com/typescript-eslint/typescript-eslint/commit/09d8afca684635b5ac604bc1794240484a70ce91)) + + + + + # [2.24.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.23.0...v2.24.0) (2020-03-16) diff --git a/packages/typescript-estree/package.json b/packages/typescript-estree/package.json index 9375c156787..20ce7e15b5a 100644 --- a/packages/typescript-estree/package.json +++ b/packages/typescript-estree/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/typescript-estree", - "version": "2.24.0", + "version": "2.25.0", "description": "A parser that converts TypeScript source code into an ESTree compatible form", "main": "dist/parser.js", "types": "dist/parser.d.ts", @@ -58,7 +58,7 @@ "@types/lodash": "^4.14.149", "@types/semver": "^6.2.0", "@types/tmp": "^0.1.0", - "@typescript-eslint/shared-fixtures": "2.24.0", + "@typescript-eslint/shared-fixtures": "2.25.0", "tmp": "^0.1.0", "typescript": "*" }, From 258fdd059526175431dbfe60dc88e0cc9d99c33e Mon Sep 17 00:00:00 2001 From: tieTYT Date: Mon, 23 Mar 2020 13:26:19 -0700 Subject: [PATCH 03/36] docs: title wording in root readme (#1787) --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 95cfe76c5eb..c298f806950 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ ## Table of Contents -- [Getting Started](#getting-started) +- [Getting Started / Installation](#getting-started--installation) - [What are ESLint and TypeScript, and how do they compare?](#what-are-eslint-and-typescript-and-how-do-they-compare) - [Why does this project exist?](#why-does-this-project-exist) - [What about TSLint?](#what-about-tslint) @@ -32,7 +32,7 @@ - [Contributors](#contributors) - [Contributing Guide](#contributing-guide) -## Getting Started +## Getting Started / Installation - **[You can find our Getting Started docs here](./docs/getting-started/README.md)** - **[You can find our Linting FAQ / Troubleshooting docs here](./docs/getting-started/linting/FAQ.md)** From b1b82847aa1db5040b18daf7b601f9c63c96a99c Mon Sep 17 00:00:00 2001 From: Brad Zacher Date: Mon, 23 Mar 2020 16:06:15 -0700 Subject: [PATCH 04/36] docs(eslint-plugin): [prefer-readonly-parameter-types] fix invalid example (#1791) --- .../docs/rules/prefer-readonly-parameter-types.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/eslint-plugin/docs/rules/prefer-readonly-parameter-types.md b/packages/eslint-plugin/docs/rules/prefer-readonly-parameter-types.md index c69b3b21c34..74697f2d3e6 100644 --- a/packages/eslint-plugin/docs/rules/prefer-readonly-parameter-types.md +++ b/packages/eslint-plugin/docs/rules/prefer-readonly-parameter-types.md @@ -74,7 +74,8 @@ function object3(arg: { readonly prop: { readonly prop2: string } }) {} interface CustomArrayType extends ReadonlyArray { readonly prop: string; } -function custom1(arg: CustomArrayType) {} +function custom1(arg: Readonly) {} +// interfaces that extend the array types are not considered arrays, and thus must be made readonly. interface CustomFunction { (): void; From 4fa710754ecc412b65ac3864fe0c7857c254ac1b Mon Sep 17 00:00:00 2001 From: Brad Zacher Date: Wed, 25 Mar 2020 20:09:04 -0700 Subject: [PATCH 05/36] fix(eslint-plugin): [no-unsafe-call] allow import expressions (#1800) --- packages/eslint-plugin/src/rules/no-unsafe-call.ts | 6 +++--- packages/eslint-plugin/tests/rules/no-unsafe-call.test.ts | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/eslint-plugin/src/rules/no-unsafe-call.ts b/packages/eslint-plugin/src/rules/no-unsafe-call.ts index 42630181936..13cabc87d92 100644 --- a/packages/eslint-plugin/src/rules/no-unsafe-call.ts +++ b/packages/eslint-plugin/src/rules/no-unsafe-call.ts @@ -41,10 +41,10 @@ export default util.createRule<[], MessageIds>({ } return { - 'CallExpression, OptionalCallExpression'( - node: TSESTree.CallExpression | TSESTree.OptionalCallExpression, + ':matches(CallExpression, OptionalCallExpression) > :not(Import)'( + node: Exclude, ): void { - checkCall(node.callee, node.callee, 'unsafeCall'); + checkCall(node, node, 'unsafeCall'); }, NewExpression(node): void { checkCall(node.callee, node, 'unsafeNew'); diff --git a/packages/eslint-plugin/tests/rules/no-unsafe-call.test.ts b/packages/eslint-plugin/tests/rules/no-unsafe-call.test.ts index 1930db27326..a51273c9b9c 100644 --- a/packages/eslint-plugin/tests/rules/no-unsafe-call.test.ts +++ b/packages/eslint-plugin/tests/rules/no-unsafe-call.test.ts @@ -20,6 +20,7 @@ ruleTester.run('no-unsafe-call', rule, { 'function foo(x: { a?: () => void }) { x.a?.() }', 'new Map()', 'String.raw`foo`', + 'const x = import("./foo");', ], invalid: [ ...batchedSingleLineTests({ From 638d84ddd77d07117b3ec7c5431f3b0e44b1995d Mon Sep 17 00:00:00 2001 From: doniyor2109 Date: Thu, 26 Mar 2020 13:04:48 +0500 Subject: [PATCH 06/36] fix(eslint-plugin): [no-explicit-any] error with ignoreRestArgs (#1796) --- packages/eslint-plugin/src/rules/no-explicit-any.ts | 9 ++------- .../eslint-plugin/tests/rules/no-explicit-any.test.ts | 11 +++++++++++ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/packages/eslint-plugin/src/rules/no-explicit-any.ts b/packages/eslint-plugin/src/rules/no-explicit-any.ts index 29fb16651a2..91765c40e26 100644 --- a/packages/eslint-plugin/src/rules/no-explicit-any.ts +++ b/packages/eslint-plugin/src/rules/no-explicit-any.ts @@ -131,9 +131,7 @@ export default util.createRule({ */ function isGreatGrandparentRestElement(node: TSESTree.Node): boolean { return ( - typeof node.parent !== 'undefined' && - typeof node.parent.parent !== 'undefined' && - typeof node.parent.parent.parent !== 'undefined' && + node?.parent?.parent?.parent != null && isNodeRestElementInFunction(node.parent.parent.parent) ); } @@ -146,11 +144,8 @@ export default util.createRule({ */ function isGreatGreatGrandparentRestElement(node: TSESTree.Node): boolean { return ( - typeof node.parent !== 'undefined' && - typeof node.parent.parent !== 'undefined' && + node.parent?.parent?.parent?.parent != null && isNodeValidTSType(node.parent.parent) && - typeof node.parent.parent.parent !== 'undefined' && - typeof node.parent.parent.parent.parent !== 'undefined' && isNodeRestElementInFunction(node.parent.parent.parent.parent) ); } diff --git a/packages/eslint-plugin/tests/rules/no-explicit-any.test.ts b/packages/eslint-plugin/tests/rules/no-explicit-any.test.ts index d065c410987..fa68b09919e 100644 --- a/packages/eslint-plugin/tests/rules/no-explicit-any.test.ts +++ b/packages/eslint-plugin/tests/rules/no-explicit-any.test.ts @@ -925,6 +925,17 @@ const test = >() => {}; }, ], }, + { + code: `type Any = any;`, + options: [{ ignoreRestArgs: true }], + errors: [ + { + messageId: 'unexpectedAny', + line: 1, + column: 12, + }, + ], + }, { code: `function foo5(...args: any) {}`, options: [{ ignoreRestArgs: true }], From 7d963fd846935acd91b7b0cd31c56a70a2b994d1 Mon Sep 17 00:00:00 2001 From: crfrolik <60711200+crfrolik@users.noreply.github.com> Date: Thu, 26 Mar 2020 14:53:43 -0400 Subject: [PATCH 07/36] feat(eslint-plugin-tslint): support tslint 6 (#1809) --- package.json | 2 +- packages/eslint-plugin-tslint/package.json | 2 +- yarn.lock | 17 +++++++++++------ 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index db9c7201528..5aa87589e19 100644 --- a/package.json +++ b/package.json @@ -73,7 +73,7 @@ "prettier": "^1.19.1", "ts-jest": "^25.0.0", "ts-node": "^8.5.0", - "tslint": "^5.20.1", + "tslint": "^6.1.0", "typescript": ">=3.2.1 <3.9.0" }, "resolutions": { diff --git a/packages/eslint-plugin-tslint/package.json b/packages/eslint-plugin-tslint/package.json index a10074080b2..1c422e8fa11 100644 --- a/packages/eslint-plugin-tslint/package.json +++ b/packages/eslint-plugin-tslint/package.json @@ -36,7 +36,7 @@ }, "peerDependencies": { "eslint": "^5.0.0 || ^6.0.0", - "tslint": "^5.0.0", + "tslint": "^5.0.0 || ^6.0.0", "typescript": "*" }, "devDependencies": { diff --git a/yarn.lock b/yarn.lock index 4e2a78cbd7e..8fb6fd0a838 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8065,15 +8065,20 @@ ts-node@^8.5.0: source-map-support "^0.5.6" yn "^3.0.0" -tslib@^1.8.0, tslib@^1.8.1, tslib@^1.9.0: +tslib@^1.10.0: + version "1.11.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.11.1.tgz#eb15d128827fbee2841549e171f45ed338ac7e35" + integrity sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA== + +tslib@^1.8.1, tslib@^1.9.0: version "1.10.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a" integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ== -tslint@^5.20.1: - version "5.20.1" - resolved "https://registry.yarnpkg.com/tslint/-/tslint-5.20.1.tgz#e401e8aeda0152bc44dd07e614034f3f80c67b7d" - integrity sha512-EcMxhzCFt8k+/UP5r8waCf/lzmeSyVlqxqMEDQE7rWYiQky8KpIBz1JAoYXfROHrPZ1XXd43q8yQnULOLiBRQg== +tslint@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/tslint/-/tslint-6.1.0.tgz#c6c611b8ba0eed1549bf5a59ba05a7732133d851" + integrity sha512-fXjYd/61vU6da04E505OZQGb2VCN2Mq3doeWcOIryuG+eqdmFUXTYVwdhnbEu2k46LNLgUYt9bI5icQze/j0bQ== dependencies: "@babel/code-frame" "^7.0.0" builtin-modules "^1.1.1" @@ -8086,7 +8091,7 @@ tslint@^5.20.1: mkdirp "^0.5.1" resolve "^1.3.2" semver "^5.3.0" - tslib "^1.8.0" + tslib "^1.10.0" tsutils "^2.29.0" tsutils@^2.29.0: From 1e29e69b289d61107a7de67592beae331ba50222 Mon Sep 17 00:00:00 2001 From: Brad Zacher Date: Fri, 27 Mar 2020 09:31:51 -0700 Subject: [PATCH 08/36] feat(typescript-estree): add option to ignore certain folders from glob resolution (#1802) --- .../src/ts-eslint/ParserOptions.ts | 1 + packages/parser/README.md | 19 +++- packages/typescript-estree/README.md | 11 ++ .../typescript-estree/src/parser-options.ts | 91 +++++++++------- packages/typescript-estree/src/parser.ts | 100 ++++++++++++++---- .../projectFolderIgnoreList/ignoreme/file.ts | 1 + .../ignoreme/tsconfig.json | 3 + .../projectFolderIgnoreList/includeme/file.ts | 1 + .../includeme/tsconfig.json | 3 + packages/typescript-estree/tests/lib/parse.ts | 45 ++++++++ tsconfig.eslint.json | 2 +- 11 files changed, 213 insertions(+), 64 deletions(-) create mode 100644 packages/typescript-estree/tests/fixtures/projectFolderIgnoreList/ignoreme/file.ts create mode 100644 packages/typescript-estree/tests/fixtures/projectFolderIgnoreList/ignoreme/tsconfig.json create mode 100644 packages/typescript-estree/tests/fixtures/projectFolderIgnoreList/includeme/file.ts create mode 100644 packages/typescript-estree/tests/fixtures/projectFolderIgnoreList/includeme/tsconfig.json diff --git a/packages/experimental-utils/src/ts-eslint/ParserOptions.ts b/packages/experimental-utils/src/ts-eslint/ParserOptions.ts index d6bf57bb585..0ff55c81c56 100644 --- a/packages/experimental-utils/src/ts-eslint/ParserOptions.ts +++ b/packages/experimental-utils/src/ts-eslint/ParserOptions.ts @@ -16,6 +16,7 @@ interface ParserOptions { loc?: boolean; noWatch?: boolean; project?: string | string[]; + projectFolderIgnoreList?: (string | RegExp)[]; range?: boolean; sourceType?: 'script' | 'module'; tokens?: boolean; diff --git a/packages/parser/README.md b/packages/parser/README.md index cf69891fb7a..70cb1cb2ca2 100644 --- a/packages/parser/README.md +++ b/packages/parser/README.md @@ -54,6 +54,7 @@ interface ParserOptions { jsx?: boolean; }; project?: string | string[]; + projectFolderIgnoreList?: (string | RegExp)[]; tsconfigRootDir?: string; extraFileExtensions?: string[]; warnOnUnsupportedTypeScriptVersion?: boolean; @@ -118,26 +119,36 @@ This option allows you to provide a path to your project's `tsconfig.json`. **Th } ``` -### `tsconfigRootDir` +### `parserOptions.tsconfigRootDir` Default `undefined`. This option allows you to provide the root directory for relative tsconfig paths specified in the `project` option above. -### `extraFileExtensions` +### `parserOptions.projectFolderIgnoreList` + +Default `["/node_modules/"]`. + +This option allows you to ignore folders from being included in your provided list of `project`s. +Any resolved project path that matches one or more of the provided regular expressions will be removed from the list. +This is useful if you have configured glob patterns, but want to make sure you ignore certain folders. + +For example, by default it will ensure that a glob like `./**/tsconfig.json` will not match any `tsconfig`s within your `node_modules` folder (some npm packages do not exclude their source files from their published packages). + +### `parserOptions.extraFileExtensions` Default `undefined`. This option allows you to provide one or more additional file extensions which should be considered in the TypeScript Program compilation. The default extensions are `.ts`, `.tsx`, `.js`, and `.jsx`. Add extensions starting with `.`, followed by the file extension. E.g. for a `.vue` file use `"extraFileExtensions: [".vue"]`. -### `warnOnUnsupportedTypeScriptVersion` +### `parserOptions.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` +### `parserOptions.createDefaultProgram` Default `false`. diff --git a/packages/typescript-estree/README.md b/packages/typescript-estree/README.md index 54706cf47f9..df2492c56b3 100644 --- a/packages/typescript-estree/README.md +++ b/packages/typescript-estree/README.md @@ -182,6 +182,16 @@ interface ParseAndGenerateServicesOptions extends ParseOptions { */ project?: string | string[]; + /** + * If you provide a glob (or globs) to the project option, you can use this option to blacklist + * certain folders from being matched by the globs. + * Any project path that matches one or more of the provided regular expressions will be removed from the list. + * + * Accepts an array of strings that are passed to new RegExp(), or an array of regular expressions. + * By default, this is set to ["/node_modules/"] + */ + projectFolderIgnoreList?: (string | RegExp)[]; + /** * The absolute path to the root directory for all provided `project`s. */ @@ -205,6 +215,7 @@ const PARSE_AND_GENERATE_SERVICES_DEFAULT_OPTIONS: ParseOptions = { extraFileExtensions: [], preserveNodeMaps: false, // or true, if you do not set this, but pass `project` project: undefined, + projectFolderIgnoreList: ['/node_modules/'], tsconfigRootDir: process.cwd(), }; diff --git a/packages/typescript-estree/src/parser-options.ts b/packages/typescript-estree/src/parser-options.ts index 73ea6ba3c04..c55ace5adb0 100644 --- a/packages/typescript-estree/src/parser-options.ts +++ b/packages/typescript-estree/src/parser-options.ts @@ -29,28 +29,23 @@ export interface Extra { // MAKE SURE THIS IS KEPT IN SYNC WITH THE README // //////////////////////////////////////////////////// -export interface TSESTreeOptions { +interface ParseOptions { /** * create a top-level comments array containing all comments */ comment?: boolean; /** - * For convenience: - * - true === ['typescript-eslint'] - * - false === [] - * * An array of modules to turn explicit debugging on for. * - 'typescript-eslint' is the same as setting the env var `DEBUG=typescript-eslint:*` * - 'eslint' is the same as setting the env var `DEBUG=eslint:*` * - 'typescript' is the same as setting `extendedDiagnostics: true` in your tsconfig compilerOptions + * + * For convenience, also supports a boolean: + * - true === ['typescript-eslint'] + * - false === [] */ - debugLevel?: boolean | DebugModule[]; - - /** - * Causes the parser to error if the TypeScript compiler returns any unexpected syntax/semantic errors. - */ - errorOnTypeScriptSyntacticAndSemanticIssues?: boolean; + debugLevel?: boolean | ('typescript-eslint' | 'eslint' | 'typescript')[]; /** * Cause the parser to error if it encounters an unknown AST node type (useful for testing). @@ -59,14 +54,7 @@ export interface TSESTreeOptions { errorOnUnknownASTType?: boolean; /** - * When `project` is provided, this controls the non-standard file extensions which will be parsed. - * It accepts an array of file extensions, each preceded by a `.`. - */ - extraFileExtensions?: string[]; - - /** - * Absolute (or relative to `tsconfigRootDir`) path to the file being parsed. - * When `project` is provided, this is required, as it is used to fetch the file from the TypeScript compiler's cache. + * Absolute (or relative to `cwd`) path to the file being parsed. */ filePath?: string; @@ -95,6 +83,45 @@ export interface TSESTreeOptions { */ loggerFn?: Function | false; + /** + * Controls whether the `range` property is included on AST nodes. + * The `range` property is a [number, number] which indicates the start/end index of the node in the file contents. + * This is similar to the `loc` property, except this is the absolute index. + */ + range?: boolean; + + /** + * Set to true to create a top-level array containing all tokens from the file. + */ + tokens?: boolean; + + /* + * The JSX AST changed the node type for string literals + * inside a JSX Element from `Literal` to `JSXText`. + * When value is `true`, these nodes will be parsed as type `JSXText`. + * When value is `false`, these nodes will be parsed as type `Literal`. + */ + useJSXTextNode?: boolean; +} + +interface ParseAndGenerateServicesOptions extends ParseOptions { + /** + * Causes the parser to error if the TypeScript compiler returns any unexpected syntax/semantic errors. + */ + errorOnTypeScriptSyntacticAndSemanticIssues?: boolean; + + /** + * When `project` is provided, this controls the non-standard file extensions which will be parsed. + * It accepts an array of file extensions, each preceded by a `.`. + */ + extraFileExtensions?: string[]; + + /** + * Absolute (or relative to `tsconfigRootDir`) path to the file being parsed. + * When `project` is provided, this is required, as it is used to fetch the file from the TypeScript compiler's cache. + */ + filePath?: string; + /** * Allows the user to control whether or not two-way AST node maps are preserved * during the AST conversion process. @@ -114,30 +141,20 @@ export interface TSESTreeOptions { project?: string | string[]; /** - * Controls whether the `range` property is included on AST nodes. - * The `range` property is a [number, number] which indicates the start/end index of the node in the file contents. - * This is similar to the `loc` property, except this is the absolute index. - */ - range?: boolean; - - /** - * Set to true to create a top-level array containing all tokens from the file. + * If you provide a glob (or globs) to the project option, you can use this option to blacklist + * certain folders from being matched by the globs. + * Any project path that matches one or more of the provided regular expressions will be removed from the list. + * + * Accepts an array of strings that are passed to new RegExp(), or an array of regular expressions. + * By default, this is set to ["/node_modules/"] */ - tokens?: boolean; + projectFolderIgnoreList?: (string | RegExp)[]; /** * The absolute path to the root directory for all provided `project`s. */ tsconfigRootDir?: string; - /* - * The JSX AST changed the node type for string literals - * inside a JSX Element from `Literal` to `JSXText`. - * When value is `true`, these nodes will be parsed as type `JSXText`. - * When value is `false`, these nodes will be parsed as type `Literal`. - */ - useJSXTextNode?: boolean; - /** *************************************************************************************** * IT IS RECOMMENDED THAT YOU DO NOT USE THIS OPTION, AS IT CAUSES PERFORMANCE ISSUES. * @@ -150,6 +167,8 @@ export interface TSESTreeOptions { createDefaultProgram?: boolean; } +export type TSESTreeOptions = ParseAndGenerateServicesOptions; + // This lets us use generics to type the return value, and removes the need to // handle the undefined type in the get method export interface ParserWeakMap { diff --git a/packages/typescript-estree/src/parser.ts b/packages/typescript-estree/src/parser.ts index b5e75d15559..5e3e9b90261 100644 --- a/packages/typescript-estree/src/parser.ts +++ b/packages/typescript-estree/src/parser.ts @@ -14,6 +14,8 @@ import { getFirstSemanticOrSyntacticError } from './semantic-or-syntactic-errors import { TSESTree } from './ts-estree'; import { ensureAbsolutePath } from './create-program/shared'; +const log = debug('typescript-eslint:typescript-estree:parser'); + /** * This needs to be kept in sync with the top-level README.md in the * typescript-eslint monorepo @@ -111,6 +113,74 @@ function resetExtra(): void { }; } +/** + * Normalizes, sanitizes, resolves and filters the provided + */ +function prepareAndTransformProjects( + projectsInput: string | string[] | undefined, + ignoreListInput: (string | RegExp)[] | undefined, +): string[] { + let projects: string[] = []; + + // Normalize and sanitize the project paths + if (typeof projectsInput === 'string') { + projects.push(projectsInput); + } else if (Array.isArray(projectsInput)) { + for (const project of projectsInput) { + if (typeof project === 'string') { + projects.push(project); + } + } + } + + if (projects.length === 0) { + return projects; + } + + // Transform glob patterns into paths + projects = projects.reduce( + (projects, project) => + projects.concat( + isGlob(project) + ? globSync(project, { + cwd: extra.tsconfigRootDir, + }) + : project, + ), + [], + ); + + // Normalize and sanitize the ignore regex list + const ignoreRegexes: RegExp[] = []; + if (Array.isArray(ignoreListInput)) { + for (const ignore of ignoreListInput) { + if (ignore instanceof RegExp) { + ignoreRegexes.push(ignore); + } else if (typeof ignore === 'string') { + ignoreRegexes.push(new RegExp(ignore)); + } + } + } else { + ignoreRegexes.push(/\/node_modules\//); + } + + // Remove any paths that match the ignore list + const filtered = projects.filter(project => { + for (const ignore of ignoreRegexes) { + if (ignore.test(project)) { + return false; + } + } + + return true; + }); + + log('parserOptions.project matched projects: %s', projects); + log('ignore list applied to parserOptions.project: %s', filtered); + + return filtered; +} + function applyParserOptionsToExtra(options: TSESTreeOptions): void { /** * Configure Debug logging @@ -205,34 +275,18 @@ function applyParserOptionsToExtra(options: TSESTreeOptions): void { extra.log = Function.prototype; } - if (typeof options.project === 'string') { - extra.projects = [options.project]; - } else if ( - Array.isArray(options.project) && - options.project.every(projectPath => typeof projectPath === 'string') - ) { - extra.projects = options.project; - } - if (typeof options.tsconfigRootDir === 'string') { extra.tsconfigRootDir = options.tsconfigRootDir; } + + // NOTE - ensureAbsolutePath relies upon having the correct tsconfigRootDir in extra extra.filePath = ensureAbsolutePath(extra.filePath, extra); - // Transform glob patterns into paths - if (extra.projects) { - extra.projects = extra.projects.reduce( - (projects, project) => - projects.concat( - isGlob(project) - ? globSync(project, { - cwd: extra.tsconfigRootDir || process.cwd(), - }) - : project, - ), - [], - ); - } + // NOTE - prepareAndTransformProjects relies upon having the correct tsconfigRootDir in extra + extra.projects = prepareAndTransformProjects( + options.project, + options.projectFolderIgnoreList, + ); if ( Array.isArray(options.extraFileExtensions) && diff --git a/packages/typescript-estree/tests/fixtures/projectFolderIgnoreList/ignoreme/file.ts b/packages/typescript-estree/tests/fixtures/projectFolderIgnoreList/ignoreme/file.ts new file mode 100644 index 00000000000..ad1d380d6cc --- /dev/null +++ b/packages/typescript-estree/tests/fixtures/projectFolderIgnoreList/ignoreme/file.ts @@ -0,0 +1 @@ +export const x = 1; diff --git a/packages/typescript-estree/tests/fixtures/projectFolderIgnoreList/ignoreme/tsconfig.json b/packages/typescript-estree/tests/fixtures/projectFolderIgnoreList/ignoreme/tsconfig.json new file mode 100644 index 00000000000..a01e8a941e8 --- /dev/null +++ b/packages/typescript-estree/tests/fixtures/projectFolderIgnoreList/ignoreme/tsconfig.json @@ -0,0 +1,3 @@ +{ + "include": ["./file.ts"] +} diff --git a/packages/typescript-estree/tests/fixtures/projectFolderIgnoreList/includeme/file.ts b/packages/typescript-estree/tests/fixtures/projectFolderIgnoreList/includeme/file.ts new file mode 100644 index 00000000000..25dfdc46133 --- /dev/null +++ b/packages/typescript-estree/tests/fixtures/projectFolderIgnoreList/includeme/file.ts @@ -0,0 +1 @@ +export const x = 2; diff --git a/packages/typescript-estree/tests/fixtures/projectFolderIgnoreList/includeme/tsconfig.json b/packages/typescript-estree/tests/fixtures/projectFolderIgnoreList/includeme/tsconfig.json new file mode 100644 index 00000000000..a01e8a941e8 --- /dev/null +++ b/packages/typescript-estree/tests/fixtures/projectFolderIgnoreList/includeme/tsconfig.json @@ -0,0 +1,3 @@ +{ + "include": ["./file.ts"] +} diff --git a/packages/typescript-estree/tests/lib/parse.ts b/packages/typescript-estree/tests/lib/parse.ts index 1e6a1e97236..c6435508eac 100644 --- a/packages/typescript-estree/tests/lib/parse.ts +++ b/packages/typescript-estree/tests/lib/parse.ts @@ -557,4 +557,49 @@ describe('parse()', () => { ); }); }); + + describe('projectFolderIgnoreList', () => { + beforeEach(() => { + parser.clearCaches(); + }); + + const PROJECT_DIR = resolve(FIXTURES_DIR, '../projectFolderIgnoreList'); + const code = 'var a = true'; + const config: TSESTreeOptions = { + comment: true, + tokens: true, + range: true, + loc: true, + tsconfigRootDir: PROJECT_DIR, + project: './**/tsconfig.json', + }; + + const testParse = ( + filePath: 'ignoreme' | 'includeme', + projectFolderIgnoreList: TSESTreeOptions['projectFolderIgnoreList'] = [], + ) => (): void => { + parser.parseAndGenerateServices(code, { + ...config, + projectFolderIgnoreList, + filePath: join(PROJECT_DIR, filePath, './file.ts'), + }); + }; + + it('ignores nothing when given nothing', () => { + expect(testParse('ignoreme')).not.toThrow(); + expect(testParse('includeme')).not.toThrow(); + }); + + it('ignores a folder when given a string regexp', () => { + const ignore = ['/ignoreme/']; + expect(testParse('ignoreme', ignore)).toThrow(); + expect(testParse('includeme', ignore)).not.toThrow(); + }); + + it('ignores a folder when given a RegExp', () => { + const ignore = [/\/ignoreme\//]; + expect(testParse('ignoreme', ignore)).toThrow(); + expect(testParse('includeme', ignore)).not.toThrow(); + }); + }); }); diff --git a/tsconfig.eslint.json b/tsconfig.eslint.json index 2d9938163ea..40defd4b3b8 100644 --- a/tsconfig.eslint.json +++ b/tsconfig.eslint.json @@ -1,4 +1,4 @@ { "extends": "./tsconfig.base.json", - "include": ["tests/**/*.ts", "tools/**/*.ts"] + "include": ["tests/**/*.ts", "tools/**/*.ts", ".eslintrc.js"] } From f3160b471f8247e157555b6cf5b40a1f6ccdc233 Mon Sep 17 00:00:00 2001 From: Brad Zacher Date: Fri, 27 Mar 2020 18:31:43 -0700 Subject: [PATCH 09/36] fix(eslint-plugin): [no-unsafe-return] error with Date: Mon, 30 Mar 2020 00:44:52 -0700 Subject: [PATCH 10/36] chore: separate linting checks to their own step (#1801) If linting/formatting/spelling fails, it should not block running the tests. If they do block, they can increase the iteration time for contributors, because they will have to fix them before they can see the status of their tests. By not blocking, a contributor will be able to submit a PR, and come back ~5min later knowing they will be able to see if their code passes linting _and_ tests. This should also knock ~40-60s off of the perceived runtime, as it parallelises some of the work. --- .cspell.json | 3 ++- .github/workflows/ci.yml | 50 +++++++++++++++++++++++++++++++--------- 2 files changed, 41 insertions(+), 12 deletions(-) diff --git a/.cspell.json b/.cspell.json index c65ff294c24..7ee50214e7f 100644 --- a/.cspell.json +++ b/.cspell.json @@ -11,7 +11,8 @@ "**/**/ROADMAP.md", "**/*.{json,snap}", ".cspell.json", - "yarn.lock" + "yarn.lock", + ".github/workflows/**" ], "dictionaries": [ "typescript", diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 933b4895837..8f6904d879d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,7 +13,7 @@ env: jobs: primary_code_validation_and_tests: - name: Primary code validation and tests + name: Typecheck and tests runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 @@ -49,15 +49,6 @@ jobs: - name: Typecheck all packages run: yarn typecheck - - name: Check code formatting - run: yarn format-check - - - name: Run linting - run: yarn lint - - - name: Validate spelling - run: yarn check:spelling - - name: Run unit tests run: yarn test env: @@ -71,6 +62,43 @@ jobs: flags: unittest name: codecov + linting_and_style: + name: Code style and lint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Use Node.js ${{ env.PRIMARY_NODE_VERSION }} + uses: actions/setup-node@v1 + with: + node-version: ${{ env.PRIMARY_NODE_VERSION }} + + - name: Get yarn cache directory path + id: yarn-cache-dir-path + run: echo "::set-output name=dir::$(yarn cache dir)" + + - uses: actions/cache@v1 + id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`) + with: + path: ${{ steps.yarn-cache-dir-path.outputs.dir }} + key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} + restore-keys: | + ${{ runner.os }}-yarn- + + # This also runs a build as part of the postinstall bootstrap + - name: Install dependencies and build + run: | + yarn --ignore-engines --frozen-lockfile + yarn check-clean-workspace-after-install + + - name: Check code formatting + run: yarn format-check + + - name: Run linting + run: yarn lint + + - name: Validate spelling + run: yarn check:spelling + integration_tests: name: Run integration tests on primary Node.js version runs-on: ubuntu-latest @@ -143,7 +171,7 @@ jobs: publish_canary_version: name: Publish the latest code as a canary version runs-on: ubuntu-latest - needs: [primary_code_validation_and_tests, unit_tests_on_other_node_versions, integration_tests] + needs: [primary_code_validation_and_tests, unit_tests_on_other_node_versions, linting_and_style, integration_tests] if: github.ref == 'refs/heads/master' steps: - uses: actions/checkout@v2 From 543bc795733948f4111388a84c807530b68380dd Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Mon, 30 Mar 2020 04:56:39 -0400 Subject: [PATCH 11/36] docs(eslint-plugin): add missing invalid-void link to ROADMAP.md (#1816) --- packages/eslint-plugin/ROADMAP.md | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/eslint-plugin/ROADMAP.md b/packages/eslint-plugin/ROADMAP.md index 32eb363609c..7ca8048769b 100644 --- a/packages/eslint-plugin/ROADMAP.md +++ b/packages/eslint-plugin/ROADMAP.md @@ -338,6 +338,7 @@ Relevant plugins: [`chai-expect-keywords`](https://github.com/gavinaiken/eslint- [`adjacent-overload-signatures`]: https://palantir.github.io/tslint/rules/adjacent-overload-signatures [`ban-ts-ignore`]: https://palantir.github.io/tslint/rules/ban-ts-ignore/ [`ban-types`]: https://palantir.github.io/tslint/rules/ban-types +[`invalid-void`]: https://palantir.github.io/tslint/rules/invalid-void [`member-access`]: https://palantir.github.io/tslint/rules/member-access [`member-ordering`]: https://palantir.github.io/tslint/rules/member-ordering [`no-any`]: https://palantir.github.io/tslint/rules/no-any From 151f89b007d3474a4d9b572128388ae4cb3595f7 Mon Sep 17 00:00:00 2001 From: James Henry Date: Mon, 30 Mar 2020 17:01:55 +0000 Subject: [PATCH 12/36] chore: publish v2.26.0 --- CHANGELOG.md | 19 +++++++++++++++++++ lerna.json | 2 +- packages/eslint-plugin-internal/CHANGELOG.md | 8 ++++++++ packages/eslint-plugin-internal/package.json | 4 ++-- packages/eslint-plugin-tslint/CHANGELOG.md | 11 +++++++++++ packages/eslint-plugin-tslint/package.json | 6 +++--- packages/eslint-plugin/CHANGELOG.md | 13 +++++++++++++ packages/eslint-plugin/package.json | 4 ++-- packages/experimental-utils/CHANGELOG.md | 11 +++++++++++ packages/experimental-utils/package.json | 4 ++-- packages/parser/CHANGELOG.md | 11 +++++++++++ packages/parser/package.json | 8 ++++---- packages/shared-fixtures/CHANGELOG.md | 8 ++++++++ packages/shared-fixtures/package.json | 2 +- packages/typescript-estree/CHANGELOG.md | 11 +++++++++++ packages/typescript-estree/package.json | 4 ++-- 16 files changed, 109 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index df5d4854494..c4f8ac29ce3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,25 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [2.26.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.25.0...v2.26.0) (2020-03-30) + + +### Bug Fixes + +* **eslint-plugin:** [no-explicit-any] error with ignoreRestArgs ([#1796](https://github.com/typescript-eslint/typescript-eslint/issues/1796)) ([638d84d](https://github.com/typescript-eslint/typescript-eslint/commit/638d84ddd77d07117b3ec7c5431f3b0e44b1995d)) +* **eslint-plugin:** [no-unsafe-call] allow import expressions ([#1800](https://github.com/typescript-eslint/typescript-eslint/issues/1800)) ([4fa7107](https://github.com/typescript-eslint/typescript-eslint/commit/4fa710754ecc412b65ac3864fe0c7857c254ac1b)) +* **eslint-plugin:** [no-unsafe-return] error with Date: Mon, 30 Mar 2020 11:42:21 -0700 Subject: [PATCH 13/36] feat(eslint-plugin-internal): add plugin-test-formatting rule (#1821) The strings that are used for eslint plugins will not be checked for formatting. This can lead to diff noise as one contributor adjusts formatting, uses different quotes, etc. This rule just enforces the following: - all code samples are formatted with prettier - all single line tests do not use backticks - all multiline tests have: - no code on the first line - no code on the last line - the closing backtick indentation === property indentation - one of the following indentations: - no indentation at all - indentation of 1 + object indent examples of enforced style: ```ts ruleTester.run('foo', rule, { valid: [ 'const a = 1;', ` const a = 1; `, ` const a = 1; `, { code: 'const a = 1;', }, { code: ` const a = 1; `, }, { code: ` const a = 1; `, }, ], invalid: [ { code: 'const a = 1;', }, { code: ` const a = 1; `, }, { code: ` const a = 1; `, }, ], }); ``` --- .prettierrc.json | 1 + packages/eslint-plugin-internal/package.json | 3 +- .../eslint-plugin-internal/src/rules/index.ts | 2 + .../src/rules/plugin-test-formatting.ts | 508 +++++++++++++++++ .../rules/plugin-test-formatting.test.ts | 518 ++++++++++++++++++ packages/eslint-plugin/tests/RuleTester.ts | 14 +- .../eslint-plugin/tools/generate-configs.ts | 2 +- 7 files changed, 1045 insertions(+), 3 deletions(-) create mode 100644 packages/eslint-plugin-internal/src/rules/plugin-test-formatting.ts create mode 100644 packages/eslint-plugin-internal/tests/rules/plugin-test-formatting.test.ts diff --git a/.prettierrc.json b/.prettierrc.json index bf357fbbc08..a20502b7f06 100644 --- a/.prettierrc.json +++ b/.prettierrc.json @@ -1,3 +1,4 @@ { + "singleQuote": true, "trailingComma": "all" } diff --git a/packages/eslint-plugin-internal/package.json b/packages/eslint-plugin-internal/package.json index 2a66b048492..8e250384428 100644 --- a/packages/eslint-plugin-internal/package.json +++ b/packages/eslint-plugin-internal/package.json @@ -12,6 +12,7 @@ "typecheck": "tsc -p tsconfig.json --noEmit" }, "dependencies": { - "@typescript-eslint/experimental-utils": "2.26.0" + "@typescript-eslint/experimental-utils": "2.26.0", + "prettier": "*" } } diff --git a/packages/eslint-plugin-internal/src/rules/index.ts b/packages/eslint-plugin-internal/src/rules/index.ts index f781e01e6df..eb8d8efe852 100644 --- a/packages/eslint-plugin-internal/src/rules/index.ts +++ b/packages/eslint-plugin-internal/src/rules/index.ts @@ -1,9 +1,11 @@ import noTypescriptDefaultImport from './no-typescript-default-import'; import noTypescriptEstreeImport from './no-typescript-estree-import'; +import pluginTestFormatting from './plugin-test-formatting'; import preferASTTypesEnum from './prefer-ast-types-enum'; export default { 'no-typescript-default-import': noTypescriptDefaultImport, 'no-typescript-estree-import': noTypescriptEstreeImport, + 'plugin-test-formatting': pluginTestFormatting, 'prefer-ast-types-enum': preferASTTypesEnum, }; diff --git a/packages/eslint-plugin-internal/src/rules/plugin-test-formatting.ts b/packages/eslint-plugin-internal/src/rules/plugin-test-formatting.ts new file mode 100644 index 00000000000..bc26d9d9d9a --- /dev/null +++ b/packages/eslint-plugin-internal/src/rules/plugin-test-formatting.ts @@ -0,0 +1,508 @@ +import { + AST_NODE_TYPES, + TSESTree, +} from '@typescript-eslint/experimental-utils'; +import { format, resolveConfig } from 'prettier'; +import { createRule } from '../util'; + +/* +The strings that are used for eslint plugins will not be checked for formatting. +This can lead to diff noise as one contributor adjusts formatting, uses different quotes, etc. + +This rule just enforces the following: +- all code samples are formatted with prettier +- all single line tests do not use backticks +- all multiline tests have: + - no code on the first line + - no code on the last line + - the closing backtick indentation === property indentation + - one of the following indentations: + - no indentation at all + - indentation of 1 + object indent + +eg: +[ + 'const a = 1;', + ` +const a = 1; + `, + ` + const a = 1; + `, + { + code: 'const a = 1', + } + { + code: ` +const a = 1; + `, + } + { + code: ` + const a = 1; + `, + } +] +*/ + +const prettierConfig = resolveConfig.sync(__dirname) ?? {}; +const START_OF_LINE_WHITESPACE_MATCHER = /^([ ]*)/; +const BACKTICK_REGEX = /`/g; +const TEMPLATE_EXPR_OPENER = /\$\{/g; + +function getExpectedIndentForNode( + node: TSESTree.Node, + sourceCodeLines: string[], +): number { + const lineIdx = node.loc.start.line - 1; + const indent = START_OF_LINE_WHITESPACE_MATCHER.exec( + sourceCodeLines[lineIdx], + )![1]; + return indent.length; +} +function doIndent(line: string, indent: number): string { + for (let i = 0; i < indent; i += 1) { + line = ' ' + line; + } + return line; +} + +function getQuote(code: string): "'" | '"' | null { + const hasSingleQuote = code.includes("'"); + const hasDoubleQuote = code.includes('"'); + if (hasSingleQuote && hasDoubleQuote) { + // be lazy and make them fix and escape the quotes manually + return null; + } + + return hasSingleQuote ? '"' : "'"; +} + +function escapeTemplateString(code: string): string { + let fixed = code; + fixed = fixed.replace(BACKTICK_REGEX, '\\`'); + fixed = fixed.replace(TEMPLATE_EXPR_OPENER, '\\${'); + return fixed; +} + +type Options = [ + { + // This option exists so that rules like type-annotation-spacing can exist without every test needing a prettier-ignore + formatWithPrettier?: boolean; + }, +]; + +type MessageIds = + | 'invalidFormatting' + | 'invalidFormattingErrorTest' + | 'singleLineQuotes' + | 'templateLiteralEmptyEnds' + | 'templateLiteralLastLineIndent' + | 'templateStringRequiresIndent' + | 'templateStringMinimumIndent' + | 'prettierException'; + +export default createRule({ + name: 'plugin-test-formatting', + meta: { + type: 'problem', + docs: { + description: `Enforces that eslint-plugin test snippets are correctly formatted`, + category: 'Stylistic Issues', + recommended: 'error', + }, + fixable: 'code', + schema: [ + { + type: 'object', + additionalProperties: false, + properties: { + formatWithPrettier: { + type: 'boolean', + }, + }, + }, + ], + messages: { + invalidFormatting: + 'This snippet should be formatted correctly. Use the fixer to format the code.', + invalidFormattingErrorTest: + 'This snippet should be formatted correctly. Use the fixer to format the code. Note that the automated fixer may break your test locations.', + singleLineQuotes: 'Use quotes (\' or ") for single line tests.', + templateLiteralEmptyEnds: + 'Template literals must start and end with an empty line.', + templateLiteralLastLineIndent: + 'The closing line of the template literal must be indented to align with its parent.', + templateStringRequiresIndent: + 'Test code should either have no indent, or be indented {{indent}} spaces.', + templateStringMinimumIndent: + 'Test code should be indented at least {{indent}} spaces.', + prettierException: + 'Prettier was unable to format this snippet: {{message}}', + }, + }, + defaultOptions: [ + { + formatWithPrettier: true, + }, + ], + create(context, [{ formatWithPrettier }]) { + const sourceCode = context.getSourceCode(); + + function prettierFormat( + code: string, + location: TSESTree.Node, + ): string | null { + if (formatWithPrettier === false) { + return null; + } + + try { + return format(code, { + ...prettierConfig, + parser: 'typescript', + }).trimRight(); // prettier will insert a new line at the end of the code + } catch (ex) { + // adapted from https://github.com/prettier/eslint-plugin-prettier/blob/185b1064d3dd674538456fb2fad97fbfcde49e0d/eslint-plugin-prettier.js#L242-L257 + if (!(ex instanceof SyntaxError)) { + throw ex; + } + const err = ex as Error & { + codeFrame: string; + loc?: unknown; + }; + + let message = err.message; + + if (err.codeFrame) { + message = message.replace(`\n${err.codeFrame}`, ''); + } + if (err.loc) { + message = message.replace(/ \(\d+:\d+\)$/, ''); + } + + context.report({ + node: location, + messageId: 'prettierException', + data: { + message, + }, + }); + return null; + } + } + + function checkExpression(node: TSESTree.Node, isErrorTest: boolean): void { + switch (node.type) { + case AST_NODE_TYPES.Literal: + checkLiteral(node, isErrorTest); + break; + + case AST_NODE_TYPES.TemplateLiteral: + checkTemplateLiteral(node, isErrorTest); + break; + + case AST_NODE_TYPES.TaggedTemplateExpression: + checkTaggedTemplateExpression(node, isErrorTest); + break; + + case AST_NODE_TYPES.CallExpression: + checkCallExpression(node, isErrorTest); + break; + } + } + + function checkLiteral( + literal: TSESTree.Literal, + isErrorTest: boolean, + quoteIn?: string, + ): void { + if (typeof literal.value === 'string') { + const output = prettierFormat(literal.value, literal); + if (output && output !== literal.value) { + context.report({ + node: literal, + messageId: isErrorTest + ? 'invalidFormattingErrorTest' + : 'invalidFormatting', + fix(fixer) { + if (output.includes('\n')) { + // formatted string is multiline, then have to use backticks + return fixer.replaceText( + literal, + `\`${escapeTemplateString(output)}\``, + ); + } + + const quote = quoteIn ?? getQuote(output); + if (quote == null) { + return null; + } + + return fixer.replaceText(literal, `${quote}${output}${quote}`); + }, + }); + } + } + } + + function checkTemplateLiteral( + literal: TSESTree.TemplateLiteral, + isErrorTest: boolean, + isNoFormatTagged = false, + ): void { + if (literal.quasis.length > 1) { + // ignore template literals with ${expressions} for simplicity + return; + } + + const text = literal.quasis[0].value.cooked; + + if (literal.loc.end.line === literal.loc.start.line) { + // don't use template strings for single line tests + return context.report({ + node: literal, + messageId: 'singleLineQuotes', + fix(fixer) { + const quote = getQuote(text); + if (quote == null) { + return null; + } + + return [ + fixer.replaceTextRange( + [literal.range[0], literal.range[0] + 1], + quote, + ), + fixer.replaceTextRange( + [literal.range[1] - 1, literal.range[1]], + quote, + ), + ]; + }, + }); + } + + const lines = text.split('\n'); + const lastLine = lines[lines.length - 1]; + // prettier will trim out the end of line on save, but eslint will check before then + const isStartEmpty = lines[0].trimRight() === ''; + // last line can be indented + const isEndEmpty = lastLine.trimLeft() === ''; + if (!isStartEmpty || !isEndEmpty) { + // multiline template strings must have an empty first/last line + return context.report({ + node: literal, + messageId: 'templateLiteralEmptyEnds', + *fix(fixer) { + if (!isStartEmpty) { + yield fixer.replaceTextRange( + [literal.range[0], literal.range[0] + 1], + '`\n', + ); + } + + if (!isEndEmpty) { + yield fixer.replaceTextRange( + [literal.range[1] - 1, literal.range[1]], + '\n`', + ); + } + }, + }); + } + + const parentIndent = getExpectedIndentForNode(literal, sourceCode.lines); + if (lastLine.length !== parentIndent) { + return context.report({ + node: literal, + messageId: 'templateLiteralLastLineIndent', + fix(fixer) { + return fixer.replaceTextRange( + [literal.range[1] - lastLine.length - 1, literal.range[1]], + doIndent('`', parentIndent), + ); + }, + }); + } + + // remove the empty lines + lines.pop(); + lines.shift(); + + // +2 because we expect the string contents are indented one level + const expectedIndent = parentIndent + 2; + + const firstLineIndent = START_OF_LINE_WHITESPACE_MATCHER.exec( + lines[0], + )![1]; + const requiresIndent = firstLineIndent.length > 0; + if (requiresIndent) { + if (firstLineIndent.length !== expectedIndent) { + return context.report({ + node: literal, + messageId: 'templateStringRequiresIndent', + data: { + indent: expectedIndent, + }, + }); + } + + // quick-and-dirty validation that lines are roughly indented correctly + for (const line of lines) { + if (line.length === 0) { + // empty lines are valid + continue; + } + + const matches = START_OF_LINE_WHITESPACE_MATCHER.exec(line)!; + + const indent = matches[1]; + if (indent.length < expectedIndent) { + return context.report({ + node: literal, + messageId: 'templateStringMinimumIndent', + data: { + indent: expectedIndent, + }, + }); + } + } + + // trim the lines to remove expectedIndent characters from the start + // this makes it easier to check formatting + for (let i = 0; i < lines.length; i += 1) { + lines[i] = lines[i].substring(expectedIndent); + } + } + + if (isNoFormatTagged) { + return; + } + + const code = lines.join('\n'); + const formatted = prettierFormat(code, literal); + if (formatted && formatted !== code) { + const formattedIndented = requiresIndent + ? formatted + .split('\n') + .map(l => doIndent(l, expectedIndent)) + .join('\n') + : formatted; + + return context.report({ + node: literal, + messageId: isErrorTest + ? 'invalidFormattingErrorTest' + : 'invalidFormatting', + fix(fixer) { + return fixer.replaceText( + literal, + `\`\n${escapeTemplateString(formattedIndented)}\n${doIndent( + '', + parentIndent, + )}\``, + ); + }, + }); + } + } + + function isNoFormatTemplateTag(tag: TSESTree.Expression): boolean { + return tag.type === AST_NODE_TYPES.Identifier && tag.name === 'noFormat'; + } + + function checkTaggedTemplateExpression( + expr: TSESTree.TaggedTemplateExpression, + isErrorTest: boolean, + ): void { + if (!isNoFormatTemplateTag(expr.tag)) { + return; + } + + if (expr.loc.start.line === expr.loc.end.line) { + // all we do on single line test cases is check format, but there's no formatting to do + return; + } + + checkTemplateLiteral( + expr.quasi, + isErrorTest, + isNoFormatTemplateTag(expr.tag), + ); + } + + function checkCallExpression( + callExpr: TSESTree.CallExpression, + isErrorTest: boolean, + ): void { + if (callExpr.callee.type !== AST_NODE_TYPES.MemberExpression) { + return; + } + const memberExpr = callExpr.callee; + // handle cases like 'aa'.trimRight and `aa`.trimRight() + checkExpression(memberExpr.object, isErrorTest); + } + + function checkInvalidTest( + test: TSESTree.ObjectExpression, + isErrorTest = true, + ): void { + for (const prop of test.properties) { + if ( + prop.type !== AST_NODE_TYPES.Property || + prop.computed || + prop.key.type !== AST_NODE_TYPES.Identifier + ) { + continue; + } + + if (prop.key.name === 'code' || prop.key.name === 'output') { + checkExpression(prop.value, isErrorTest); + } + } + } + + function checkValidTest(tests: TSESTree.ArrayExpression): void { + for (const test of tests.elements) { + switch (test.type) { + case AST_NODE_TYPES.ObjectExpression: + // delegate object-style tests to the invalid checker + checkInvalidTest(test, false); + break; + + default: + checkExpression(test, false); + break; + } + } + } + + const invalidTestsSelectorPath = [ + AST_NODE_TYPES.CallExpression, + AST_NODE_TYPES.ObjectExpression, + 'Property[key.name = "invalid"]', + AST_NODE_TYPES.ArrayExpression, + AST_NODE_TYPES.ObjectExpression, + ]; + + return { + // valid + 'CallExpression > ObjectExpression > Property[key.name = "valid"] > ArrayExpression': checkValidTest, + // invalid - errors + [invalidTestsSelectorPath.join(' > ')]: checkInvalidTest, + // invalid - suggestions + [[ + ...invalidTestsSelectorPath, + 'Property[key.name = "errors"]', + AST_NODE_TYPES.ArrayExpression, + AST_NODE_TYPES.ObjectExpression, + 'Property[key.name = "suggestions"]', + AST_NODE_TYPES.ArrayExpression, + AST_NODE_TYPES.ObjectExpression, + ].join(' > ')]: checkInvalidTest, + // special case for our batchedSingleLineTests utility + 'CallExpression[callee.name = "batchedSingleLineTests"] > ObjectExpression': checkInvalidTest, + }; + }, +}); diff --git a/packages/eslint-plugin-internal/tests/rules/plugin-test-formatting.test.ts b/packages/eslint-plugin-internal/tests/rules/plugin-test-formatting.test.ts new file mode 100644 index 00000000000..8d7e0b2c1d8 --- /dev/null +++ b/packages/eslint-plugin-internal/tests/rules/plugin-test-formatting.test.ts @@ -0,0 +1,518 @@ +import rule from '../../src/rules/plugin-test-formatting'; +import { RuleTester } from '../RuleTester'; + +const ruleTester = new RuleTester({ + parser: '@typescript-eslint/parser', + parserOptions: { + sourceType: 'module', + }, +}); + +const CODE_INDENT = ' '; +const PARENT_INDENT = ' '; +function wrap(strings: TemplateStringsArray, ...keys: string[]): string { + const lastIndex = strings.length - 1; + const code = + strings.slice(0, lastIndex).reduce((p, s, i) => p + s + keys[i], '') + + strings[lastIndex]; + return ` +ruleTester.run({ + valid: [ + { + code: ${code}, + }, + ], +}); + `; +} +function wrapWithOutput( + strings: TemplateStringsArray, + ...keys: string[] +): string { + const lastIndex = strings.length - 1; + const code = + strings.slice(0, lastIndex).reduce((p, s, i) => p + s + keys[i], '') + + strings[lastIndex]; + return ` +ruleTester.run({ + invalid: [ + { + code: ${code}, + output: ${code}, + }, + ], +}); + `; +} + +ruleTester.run('plugin-test-formatting', rule, { + valid: [ + // sanity check for valid tests non-object style + ` +ruleTester.run({ + valid: [ + 'const a = 1;', + \` + const a = 1; + \`, + \` +const a = 1; + \`, + noFormat\`const x=1;\`, + ], +}); + `, + wrap`'const a = 1;'`, + wrap`\` +${CODE_INDENT}const a = 1; +${PARENT_INDENT}\``, + wrap`\` +const a = 1; +${PARENT_INDENT}\``, + wrap`noFormat\`const a = 1;\``, + // sanity check suggestion validation + ` + ruleTester.run({ + invalid: [ + { + code: 'const a = 1;', + output: 'const a = 1;', + errors: [ + { + messageId: 'foo', + suggestions: [ + { + messageId: 'bar', + output: 'const a = 1;', + }, + ], + } + ] + }, + { + code: \` + const a = 1; + \`, + output: \` + const a = 1; + \`, + errors: [ + { + messageId: 'foo', + suggestions: [ + { + messageId: 'bar', + output: \` + const a = 1; + \`, + }, + ], + } + ] + }, + { + code: \` +const a = 1; + \`, + output: \` +const a = 1; + \`, + errors: [ + { + messageId: 'foo', + suggestions: [ + { + messageId: 'bar', + output: \` +const a = 1; + \`, + }, + ], + } + ] + }, + ], + }); + `, + + // test the only option + { + code: wrap`'const x=1;'`, + options: [ + { + formatWithPrettier: false, + }, + ], + }, + + // empty linems are valid when everything else is indented + wrap`\` +${CODE_INDENT}const a = 1; + +${CODE_INDENT}const b = 1; +${PARENT_INDENT}\``, + ], + invalid: [ + // Literal + { + code: wrap`'const a=1;'`, + output: wrap`'const a = 1;'`, + errors: [ + { + messageId: 'invalidFormatting', + }, + ], + }, + { + code: wrap`'const a="1";'`, + output: wrap`"const a = '1';"`, + errors: [ + { + messageId: 'invalidFormatting', + }, + ], + }, + { + code: wrap`"const a='1';"`, + output: wrap`"const a = '1';"`, + errors: [ + { + messageId: 'invalidFormatting', + }, + ], + }, + { + code: wrap`'for (const x of y) {}'`, + output: wrap`\`for (const x of y) { +}\``, + errors: [ + { + messageId: 'invalidFormatting', + }, + ], + }, + { + code: wrap`'for (const x of \`asdf\`) {}'`, + // make sure it escapes the backticks + output: wrap`\`for (const x of \\\`asdf\\\`) { +}\``, + errors: [ + { + messageId: 'invalidFormatting', + }, + ], + }, + // TemplateLiteral + // singleLineQuotes + { + code: wrap`\`const a = 1;\``, + output: wrap`'const a = 1;'`, + errors: [ + { + messageId: 'singleLineQuotes', + }, + ], + }, + { + code: wrap`\`const a = '1'\``, + output: wrap`"const a = '1'"`, + errors: [ + { + messageId: 'singleLineQuotes', + }, + ], + }, + { + code: wrap`\`const a = "1";\``, + output: wrap`'const a = "1";'`, + errors: [ + { + messageId: 'singleLineQuotes', + }, + ], + }, + // templateLiteralEmptyEnds + { + code: wrap`\`const a = "1"; +${PARENT_INDENT}\``, + output: wrap`\` +const a = "1"; +${PARENT_INDENT}\``, + errors: [ + { + messageId: 'templateLiteralEmptyEnds', + }, + ], + }, + { + code: wrap`\` +${CODE_INDENT}const a = "1";\``, + output: wrap`\` +${CODE_INDENT}const a = "1"; +\``, + errors: [ + { + messageId: 'templateLiteralEmptyEnds', + }, + ], + }, + { + code: wrap`\`const a = "1"; +${CODE_INDENT}const b = "2";\``, + output: wrap`\` +const a = "1"; +${CODE_INDENT}const b = "2"; +\``, + errors: [ + { + messageId: 'templateLiteralEmptyEnds', + }, + ], + }, + // templateLiteralLastLineIndent + { + code: wrap`\` +${CODE_INDENT}const a = "1"; +\``, + output: wrap`\` +${CODE_INDENT}const a = "1"; +${PARENT_INDENT}\``, + errors: [ + { + messageId: 'templateLiteralLastLineIndent', + }, + ], + }, + { + code: wrap`\` +${CODE_INDENT}const a = "1"; + \``, + output: wrap`\` +${CODE_INDENT}const a = "1"; +${PARENT_INDENT}\``, + errors: [ + { + messageId: 'templateLiteralLastLineIndent', + }, + ], + }, + // templateStringRequiresIndent + { + code: wrap`\` + const a = "1"; +${PARENT_INDENT}\``, + errors: [ + { + messageId: 'templateStringRequiresIndent', + data: { + indent: CODE_INDENT.length, + }, + }, + ], + }, + { + code: ` +ruleTester.run({ + valid: [ + \` + const a = "1"; + \`, + ], +}); + `, + errors: [ + { + messageId: 'templateStringRequiresIndent', + data: { + indent: 6, + }, + }, + ], + }, + // templateStringMinimumIndent + { + code: wrap`\` +${CODE_INDENT}const a = "1"; + const b = "2"; +${PARENT_INDENT}\``, + errors: [ + { + messageId: 'templateStringMinimumIndent', + data: { + indent: CODE_INDENT.length, + }, + }, + ], + }, + // invalidFormatting + { + code: wrap`\` +${CODE_INDENT}const a="1"; +${CODE_INDENT} const b = "2"; +${PARENT_INDENT}\``, + output: wrap`\` +${CODE_INDENT}const a = '1'; +${CODE_INDENT}const b = '2'; +${PARENT_INDENT}\``, + errors: [ + { + messageId: 'invalidFormatting', + }, + ], + }, + { + code: wrap`\` +${CODE_INDENT}const a=\\\`\\\${a}\\\`; +${PARENT_INDENT}\``, + // make sure it escapes backticks + output: wrap`\` +${CODE_INDENT}const a = \\\`\\\${a}\\\`; +${PARENT_INDENT}\``, + errors: [ + { + messageId: 'invalidFormatting', + }, + ], + }, + + // sanity check that it runs on both output and code properties + { + code: wrapWithOutput`\` +${CODE_INDENT}const a="1"; +${CODE_INDENT} const b = "2"; +${PARENT_INDENT}\``, + output: wrapWithOutput`\` +${CODE_INDENT}const a = '1'; +${CODE_INDENT}const b = '2'; +${PARENT_INDENT}\``, + errors: [ + { + messageId: 'invalidFormattingErrorTest', + }, + { + messageId: 'invalidFormattingErrorTest', + }, + ], + }, + + // sanity check that it handles suggestion output + { + code: ` +ruleTester.run({ + valid: [], + invalid: [ + { + code: 'const x=1;', + errors: [ + { + messageId: 'foo', + suggestions: [ + { + messageId: 'bar', + output: 'const x=1;', + }, + ], + }, + ], + }, + ], +}); + `, + errors: [ + { + messageId: 'invalidFormattingErrorTest', + }, + { + messageId: 'invalidFormattingErrorTest', + }, + ], + }, + + // sanity check that it runs on all tests + { + code: ` +ruleTester.run({ + valid: [ + { + code: \`foo\`, + }, + { + code: \`foo +\`, + }, + { + code: \` + foo\`, + }, + ], + invalid: [ + { + code: \`foo\`, + }, + { + code: \`foo +\`, + }, + { + code: \` + foo\`, + }, + ], +}); + `, + errors: [ + { + messageId: 'singleLineQuotes', + }, + { + messageId: 'templateLiteralEmptyEnds', + }, + { + messageId: 'templateLiteralEmptyEnds', + }, + { + messageId: 'singleLineQuotes', + }, + { + messageId: 'templateLiteralEmptyEnds', + }, + { + messageId: 'templateLiteralEmptyEnds', + }, + ], + }, + + // handles prettier errors + { + code: wrap`'const x = ";'`, + errors: [ + { + messageId: 'prettierException', + data: { + message: 'Unterminated string literal.', + }, + }, + ], + }, + + // checks tests with .trimRight calls + { + code: wrap`'const a=1;'.trimRight()`, + output: wrap`'const a = 1;'.trimRight()`, + errors: [ + { + messageId: 'invalidFormatting', + }, + ], + }, + { + code: wrap`\`const a = "1"; +${CODE_INDENT}\`.trimRight()`, + errors: [ + { + messageId: 'templateLiteralEmptyEnds', + }, + ], + }, + ], +}); diff --git a/packages/eslint-plugin/tests/RuleTester.ts b/packages/eslint-plugin/tests/RuleTester.ts index c650c83449c..1774200db32 100644 --- a/packages/eslint-plugin/tests/RuleTester.ts +++ b/packages/eslint-plugin/tests/RuleTester.ts @@ -89,4 +89,16 @@ afterAll(() => { clearCaches(); }); -export { RuleTester, getFixturesRootDir, batchedSingleLineTests }; +/** + * Simple no-op tag to mark code samples as "should not format with prettier" + * for the internal/plugin-test-formatting lint rule + */ +function noFormat(strings: TemplateStringsArray, ...keys: string[]): string { + const lastIndex = strings.length - 1; + return ( + strings.slice(0, lastIndex).reduce((p, s, i) => p + s + keys[i], '') + + strings[lastIndex] + ); +} + +export { batchedSingleLineTests, getFixturesRootDir, noFormat, RuleTester }; diff --git a/packages/eslint-plugin/tools/generate-configs.ts b/packages/eslint-plugin/tools/generate-configs.ts index d35fd7362bc..e9d2a8be762 100644 --- a/packages/eslint-plugin/tools/generate-configs.ts +++ b/packages/eslint-plugin/tools/generate-configs.ts @@ -5,7 +5,7 @@ import path from 'path'; import { format, resolveConfig } from 'prettier'; import rules from '../src/rules'; -const prettierConfig = resolveConfig(__dirname); +const prettierConfig = resolveConfig.sync(__dirname); interface LinterConfigRules { [name: string]: From 8ec53a3579fcb59cdffea0c60fbb755d056f4c8a Mon Sep 17 00:00:00 2001 From: Brad Zacher Date: Mon, 30 Mar 2020 17:04:10 -0700 Subject: [PATCH 14/36] fix(eslint-plugin): [no-unsafe-call] fix incorrect selector (#1826) --- packages/eslint-plugin/src/rules/no-unsafe-call.ts | 2 +- packages/eslint-plugin/tests/rules/no-unsafe-call.test.ts | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/eslint-plugin/src/rules/no-unsafe-call.ts b/packages/eslint-plugin/src/rules/no-unsafe-call.ts index 13cabc87d92..66f66448c28 100644 --- a/packages/eslint-plugin/src/rules/no-unsafe-call.ts +++ b/packages/eslint-plugin/src/rules/no-unsafe-call.ts @@ -41,7 +41,7 @@ export default util.createRule<[], MessageIds>({ } return { - ':matches(CallExpression, OptionalCallExpression) > :not(Import)'( + ':matches(CallExpression, OptionalCallExpression) > :not(Import).callee'( node: Exclude, ): void { checkCall(node, node, 'unsafeCall'); diff --git a/packages/eslint-plugin/tests/rules/no-unsafe-call.test.ts b/packages/eslint-plugin/tests/rules/no-unsafe-call.test.ts index a51273c9b9c..bc7b6c7d086 100644 --- a/packages/eslint-plugin/tests/rules/no-unsafe-call.test.ts +++ b/packages/eslint-plugin/tests/rules/no-unsafe-call.test.ts @@ -21,6 +21,11 @@ ruleTester.run('no-unsafe-call', rule, { 'new Map()', 'String.raw`foo`', 'const x = import("./foo");', + // https://github.com/typescript-eslint/typescript-eslint/issues/1825 + ` + let foo: any = 23; + String(foo); // ERROR: Unsafe call of an any typed value + `, ], invalid: [ ...batchedSingleLineTests({ From 9642d9dce693befac89a4e9d8bf8dd18f4361e2a Mon Sep 17 00:00:00 2001 From: Anix Date: Tue, 31 Mar 2020 09:53:23 +0530 Subject: [PATCH 15/36] fix(eslint-plugin): [require-await] handle async generators (#1782) Co-authored-by: Brad Zacher --- .../eslint-plugin/src/rules/require-await.ts | 38 ++- .../tests/rules/require-await.test.ts | 244 +++++++++++++----- 2 files changed, 217 insertions(+), 65 deletions(-) diff --git a/packages/eslint-plugin/src/rules/require-await.ts b/packages/eslint-plugin/src/rules/require-await.ts index e7b6a5f5959..99c455cd0f6 100644 --- a/packages/eslint-plugin/src/rules/require-await.ts +++ b/packages/eslint-plugin/src/rules/require-await.ts @@ -11,6 +11,8 @@ interface ScopeInfo { upper: ScopeInfo | null; hasAwait: boolean; hasAsync: boolean; + isGen: boolean; + isAsyncYield: boolean; } type FunctionNode = | TSESTree.FunctionDeclaration @@ -49,6 +51,8 @@ export default util.createRule({ upper: scopeInfo, hasAwait: false, hasAsync: node.async, + isGen: node.generator || false, + isAsyncYield: false, }; } @@ -62,7 +66,12 @@ export default util.createRule({ return; } - if (node.async && !scopeInfo.hasAwait && !isEmptyFunction(node)) { + if ( + node.async && + !scopeInfo.hasAwait && + !isEmptyFunction(node) && + !(scopeInfo.isGen && scopeInfo.isAsyncYield) + ) { context.report({ node, loc: getFunctionHeadLoc(node, sourceCode), @@ -92,10 +101,34 @@ export default util.createRule({ if (!scopeInfo) { return; } - scopeInfo.hasAwait = true; } + /** + * mark `scopeInfo.isAsyncYield` to `true` if its a generator + * function and the delegate is `true` + */ + function markAsHasDelegateGen(node: TSESTree.YieldExpression): void { + if (!scopeInfo || !scopeInfo.isGen || !node.argument) { + return; + } + + if (node?.argument?.type === AST_NODE_TYPES.Literal) { + // making this `false` as for literals we don't need to check the definition + // eg : async function* run() { yield* 1 } + scopeInfo.isAsyncYield = false; + } + + const tsNode = parserServices.esTreeNodeToTSNodeMap.get(node?.argument); + const type = checker.getTypeAtLocation(tsNode); + const symbol = type.getSymbol(); + + // async function* test1() {yield* asyncGenerator() } + if (symbol?.getName() === 'AsyncGenerator') { + scopeInfo.isAsyncYield = true; + } + } + return { FunctionDeclaration: enterFunction, FunctionExpression: enterFunction, @@ -106,6 +139,7 @@ export default util.createRule({ AwaitExpression: markAsHasAwait, 'ForOfStatement[await = true]': markAsHasAwait, + 'YieldExpression[delegate = true]': markAsHasDelegateGen, // check body-less async arrow function. // ignore `async () => await foo` because it's obviously correct diff --git a/packages/eslint-plugin/tests/rules/require-await.test.ts b/packages/eslint-plugin/tests/rules/require-await.test.ts index 66db5497980..caea93fbfb9 100644 --- a/packages/eslint-plugin/tests/rules/require-await.test.ts +++ b/packages/eslint-plugin/tests/rules/require-await.test.ts @@ -16,102 +16,130 @@ ruleTester.run('require-await', rule, { valid: [ // Non-async function declaration `function numberOne(): number { - return 1; - }`, + return 1; + }`, // Non-async function expression `const numberOne = function(): number { - return 1; - }`, + return 1; + }`, // Non-async arrow function expression (concise-body) `const numberOne = (): number => 1;`, // Non-async arrow function expression (block-body) `const numberOne = (): number => { - return 1; - };`, + return 1; + };`, // Non-async function that returns a promise // https://github.com/typescript-eslint/typescript-eslint/issues/1226 ` -function delay() { - return Promise.resolve(); -} - `, + function delay() { + return Promise.resolve(); + } + `, ` -const delay = () => { - return Promise.resolve(); -} - `, + const delay = () => { + return Promise.resolve(); + } + `, `const delay = () => Promise.resolve();`, // Async function declaration with await `async function numberOne(): Promise { - return await 1; - }`, + return await 1; + }`, // Async function expression with await `const numberOne = async function(): Promise { - return await 1; - }`, + return await 1; + }`, // Async arrow function expression with await (concise-body) `const numberOne = async (): Promise => await 1;`, // Async arrow function expression with await (block-body) `const numberOne = async (): Promise => { - return await 1; - };`, + return await 1; + };`, // Async function declaration with promise return `async function numberOne(): Promise { - return Promise.resolve(1); - }`, + return Promise.resolve(1); + }`, // Async function expression with promise return `const numberOne = async function(): Promise { - return Promise.resolve(1); - }`, + return Promise.resolve(1); + }`, // Async arrow function with promise return (concise-body) `const numberOne = async (): Promise => Promise.resolve(1);`, // Async arrow function with promise return (block-body) `const numberOne = async (): Promise => { - return Promise.resolve(1); - };`, + return Promise.resolve(1); + };`, // Async function declaration with async function return `async function numberOne(): Promise { - return getAsyncNumber(1); - } - async function getAsyncNumber(x: number): Promise { - return Promise.resolve(x); - }`, + return getAsyncNumber(1); + } + async function getAsyncNumber(x: number): Promise { + return Promise.resolve(x); + }`, // Async function expression with async function return `const numberOne = async function(): Promise { - return getAsyncNumber(1); - } - const getAsyncNumber = async function(x: number): Promise { - return Promise.resolve(x); - }`, + return getAsyncNumber(1); + } + const getAsyncNumber = async function(x: number): Promise { + return Promise.resolve(x); + }`, // Async arrow function with async function return (concise-body) `const numberOne = async (): Promise => getAsyncNumber(1); - const getAsyncNumber = async function(x: number): Promise { - return Promise.resolve(x); - }`, + const getAsyncNumber = async function(x: number): Promise { + return Promise.resolve(x); + }`, // Async arrow function with async function return (block-body) `const numberOne = async (): Promise => { - return getAsyncNumber(1); - }; - const getAsyncNumber = async function(x: number): Promise { - return Promise.resolve(x); - }`, + return getAsyncNumber(1); + }; + const getAsyncNumber = async function(x: number): Promise { + return Promise.resolve(x); + }`, // https://github.com/typescript-eslint/typescript-eslint/issues/1188 ` -async function testFunction(): Promise { - await Promise.all([1, 2, 3].map( - // this should not trigger an error on the parent function - async value => Promise.resolve(value) - )) -} + async function testFunction(): Promise { + await Promise.all([1, 2, 3].map( + // this should not trigger an error on the parent function + async value => Promise.resolve(value) + )) + } + `, + ` + async function* run() { + await new Promise(resolve => setTimeout(resolve, 100)); + yield 'Hello'; + console.log('World'); + } + `, + 'async function* run() { }', + 'async function* asyncGenerator() { await Promise.resolve(); yield 1 }', + 'function* test6() { yield* syncGenerator() }', + 'function* test8() { yield syncGenerator() }', + 'function* syncGenerator() { yield 1 }', + ` + async function* asyncGenerator() { + await Promise.resolve() + yield 1 + } + async function* test1() {yield* asyncGenerator() } `, + `async function* foo() { + await Promise.resolve() + yield 1 + } + async function* bar() { + yield* foo() + }`, + 'const foo : () => void = async function *(){}', + 'async function* foo() : Promise { return new Promise((res) => res(`hello`)) }', ], invalid: [ { // Async function declaration with no await code: `async function numberOne(): Promise { - return 1; - }`, + return 1; + }`, errors: [ { messageId: 'missingAwait', @@ -124,8 +152,8 @@ async function testFunction(): Promise { { // Async function expression with no await code: `const numberOne = async function(): Promise { - return 1; - }`, + return 1; + }`, errors: [ { messageId: 'missingAwait', @@ -160,9 +188,85 @@ async function testFunction(): Promise { }, ], }, + { + code: 'async function* foo() : void { doSomething() }', + errors: [ + { + messageId: 'missingAwait', + data: { + name: "Async generator function 'foo'", + }, + }, + ], + }, + { + code: 'async function* foo() { yield 1 }', + errors: [ + { + messageId: 'missingAwait', + data: { + name: "Async generator function 'foo'", + }, + }, + ], + }, + { + code: 'async function* run() { console.log("bar") }', + errors: [ + { + messageId: 'missingAwait', + data: { + name: "Async generator function 'run'", + }, + }, + ], + }, + { + code: 'const foo = async function *(){ console.log("bar") }', + errors: [ + { + messageId: 'missingAwait', + data: { + name: 'Async generator function', + }, + }, + ], + }, + { + code: 'async function* syncGenerator() { yield 1 }', + errors: [ + { + messageId: 'missingAwait', + data: { + name: "Async generator function 'syncGenerator'", + }, + }, + ], + }, + { + code: 'async function* test3() { yield asyncGenerator() }', + errors: [ + { + messageId: 'missingAwait', + data: { + name: "Async generator function 'test3'", + }, + }, + ], + }, + { + code: 'async function* test7() { yield syncGenerator() }', + errors: [ + { + messageId: 'missingAwait', + data: { + name: "Async generator function 'test7'", + }, + }, + ], + }, ], }); - // base eslint tests // https://github.com/eslint/eslint/blob/03a69dbe86d5b5768a310105416ae726822e3c1c/tests/lib/rules/require-await.js#L25-L132 ruleTester.run('require-await', rule, { @@ -175,27 +279,23 @@ ruleTester.run('require-await', rule, { 'class A { async foo() { await doSomething() } }', '(class { async foo() { await doSomething() } })', 'async function foo() { await (async () => { await doSomething() }) }', - // empty functions are ok. 'async function foo() {}', 'async () => {}', - // normal functions are ok. 'function foo() { doSomething() }', - // for-await-of 'async function foo() { for await (x of xs); }', - // global await { code: 'await foo()', }, { code: ` - for await (let num of asyncIterable) { - console.log(num); - } - `, + for await (let num of asyncIterable) { + console.log(num); + } + `, }, ], invalid: [ @@ -289,5 +389,23 @@ ruleTester.run('require-await', rule, { }, ], }, + { + code: 'async function* run() { yield * anotherAsyncGenerator() }', + errors: [ + { + messageId: 'missingAwait', + data: { name: "Async generator function 'run'" }, + }, + ], + }, + { + code: 'async function* run() { yield* 1 }', + errors: [ + { + messageId: 'missingAwait', + data: { name: "Async generator function 'run'" }, + }, + ], + }, ], }); From 188b68979d114ba0957a076fe9c27d154223512c Mon Sep 17 00:00:00 2001 From: Brad Zacher Date: Mon, 30 Mar 2020 23:31:50 -0700 Subject: [PATCH 16/36] chore: turn on plugin-test-formatting rule (#1822) --- .eslintrc.js | 13 +- .../tests/rules/no-typescript-estree.test.ts | 18 +- .../rules/plugin-test-formatting.test.ts | 1 + .../tests/rules/prefer-ast-types-enum.test.ts | 28 +- .../adjacent-overload-signatures.test.ts | 640 ++++++------ .../tests/rules/array-type.test.ts | 430 +++++---- .../tests/rules/await-thenable.test.ts | 67 +- .../tests/rules/ban-ts-comment.test.ts | 50 +- .../tests/rules/ban-ts-ignore.test.ts | 16 +- .../tests/rules/ban-types.test.ts | 52 +- .../tests/rules/brace-style.test.ts | 352 +++---- .../tests/rules/camelcase.test.ts | 200 +++- .../class-literal-property-style.test.ts | 431 ++++++--- .../tests/rules/class-name-casing.test.ts | 21 +- .../tests/rules/comma-spacing.test.ts | 347 +++---- .../rules/consistent-type-definitions.test.ts | 86 +- .../tests/rules/default-param-last.test.ts | 96 +- .../explicit-function-return-type.test.ts | 303 +++--- .../explicit-member-accessibility.test.ts | 368 +++---- .../explicit-module-boundary-types.test.ts | 306 +++--- .../tests/rules/func-call-spacing.test.ts | 9 +- .../tests/rules/generic-type-naming.test.ts | 95 +- .../tests/rules/indent/indent-eslint.test.ts | 5 + .../tests/rules/indent/indent.test.ts | 108 ++- .../tests/rules/interface-name-prefix.test.ts | 56 +- .../rules/member-delimiter-style.test.ts | 407 ++++---- .../tests/rules/member-naming.test.ts | 186 ++-- .../tests/rules/member-ordering.test.ts | 3 + .../tests/rules/naming-convention.test.ts | 23 +- .../tests/rules/no-array-constructor.test.ts | 94 +- .../tests/rules/no-base-to-string.test.ts | 114 ++- .../tests/rules/no-dupe-class-members.test.ts | 153 ++- .../tests/rules/no-dynamic-delete.test.ts | 200 ++-- .../tests/rules/no-empty-function.test.ts | 65 +- .../tests/rules/no-empty-interface.test.ts | 42 +- .../tests/rules/no-explicit-any.test.ts | 185 ++-- .../rules/no-extra-non-null-assertion.test.ts | 21 +- .../tests/rules/no-extra-parens.test.ts | 9 +- .../tests/rules/no-extra-semi.test.ts | 5 + .../tests/rules/no-extraneous-class.test.ts | 94 +- .../tests/rules/no-floating-promises.test.ts | 284 ++++-- .../tests/rules/no-for-in-array.test.ts | 20 +- .../tests/rules/no-implied-eval.test.ts | 105 +- .../tests/rules/no-inferrable-types.test.ts | 68 +- .../tests/rules/no-magic-numbers.test.ts | 16 +- .../tests/rules/no-misused-new.test.ts | 117 ++- .../tests/rules/no-misused-promises.test.ts | 178 ++-- .../tests/rules/no-namespace.test.ts | 22 +- ...o-non-null-asserted-optional-chain.test.ts | 62 +- .../tests/rules/no-non-null-assertion.test.ts | 68 +- .../rules/no-parameter-properties.test.ts | 264 ++--- .../tests/rules/no-require-imports.test.ts | 46 +- .../tests/rules/no-this-alias.test.ts | 42 +- .../tests/rules/no-throw-literal.test.ts | 66 +- .../tests/rules/no-type-alias.test.ts | 384 ++++---- ...nnecessary-boolean-literal-compare.test.ts | 26 +- .../rules/no-unnecessary-condition.test.ts | 314 +++--- .../rules/no-unnecessary-qualifier.test.ts | 86 +- .../no-unnecessary-type-arguments.test.ts | 314 +++--- .../no-unnecessary-type-assertion.test.ts | 151 +-- .../tests/rules/no-unsafe-call.test.ts | 33 +- .../rules/no-unsafe-member-access.test.ts | 67 +- .../tests/rules/no-unsafe-return.test.ts | 85 +- .../rules/no-untyped-public-signature.test.ts | 203 ++-- .../tests/rules/no-unused-expressions.test.ts | 49 +- .../tests/rules/no-unused-vars.test.ts | 476 ++++----- .../tests/rules/no-use-before-define.test.ts | 324 ++++--- .../rules/no-useless-constructor.test.ts | 292 +++++- .../tests/rules/no-var-requires.test.ts | 24 +- .../tests/rules/prefer-as-const.test.ts | 38 +- .../tests/rules/prefer-for-of.test.ts | 156 ++- .../tests/rules/prefer-function-type.test.ts | 61 +- .../rules/prefer-namespace-keyword.test.ts | 28 +- .../tests/rules/prefer-optional-chain.test.ts | 74 +- .../prefer-readonly-parameter-types.test.ts | 129 +-- .../tests/rules/prefer-readonly.test.ts | 734 ++++++++------ .../tests/rules/prefer-regexp-exec.test.ts | 48 +- .../rules/promise-function-async.test.ts | 132 ++- .../eslint-plugin/tests/rules/quotes.test.ts | 123 +-- .../rules/require-array-sort-compare.test.ts | 55 +- .../tests/rules/require-await.test.ts | 469 +++++---- .../rules/restrict-plus-operands.test.ts | 171 ++-- .../restrict-template-expressions.test.ts | 16 +- .../tests/rules/return-await.test.ts | 599 +++++++----- .../eslint-plugin/tests/rules/semi.test.ts | 464 ++++----- .../rules/space-before-function-paren.test.ts | 5 + .../rules/strict-boolean-expressions.test.ts | 184 ++-- .../rules/switch-exhaustiveness-check.test.ts | 314 +++--- .../rules/triple-slash-reference.test.ts | 80 +- .../rules/type-annotation-spacing.test.ts | 909 +++++++++--------- .../eslint-plugin/tests/rules/typedef.test.ts | 412 +++++--- .../tests/rules/unbound-method.test.ts | 36 +- .../tests/rules/unified-signatures.test.ts | 307 +++--- 93 files changed, 8819 insertions(+), 6610 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 39ca61de516..cee2ec901cc 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -174,7 +174,7 @@ module.exports = { '@typescript-eslint/internal/no-typescript-estree-import': 'error', }, }, - // rule source files + // plugin rule source files { files: [ 'packages/eslint-plugin-internal/src/rules/**/*.ts', @@ -187,6 +187,17 @@ module.exports = { 'import/no-default-export': 'off', }, }, + // plugin rule tests + { + files: [ + 'packages/eslint-plugin-internal/tests/rules/**/*.test.ts', + 'packages/eslint-plugin-tslint/tests/rules/**/*.test.ts', + 'packages/eslint-plugin/tests/rules/**/*.test.ts', + ], + rules: { + '@typescript-eslint/internal/plugin-test-formatting': 'error', + }, + }, // tools and tests { files: ['**/tools/**/*.ts', '**/tests/**/*.ts'], diff --git a/packages/eslint-plugin-internal/tests/rules/no-typescript-estree.test.ts b/packages/eslint-plugin-internal/tests/rules/no-typescript-estree.test.ts index a76e03b7c1d..74b9478b58a 100644 --- a/packages/eslint-plugin-internal/tests/rules/no-typescript-estree.test.ts +++ b/packages/eslint-plugin-internal/tests/rules/no-typescript-estree.test.ts @@ -10,20 +10,20 @@ const ruleTester = new RuleTester({ ruleTester.run('no-typescript-estree-import', rule, { valid: [ - 'import { foo } from "@typescript-eslint/experimental-utils";', - 'import foo from "@typescript-eslint/experimental-utils";', - 'import * as foo from "@typescript-eslint/experimental-utils";', + "import { foo } from '@typescript-eslint/experimental-utils';", + "import foo from '@typescript-eslint/experimental-utils';", + "import * as foo from '@typescript-eslint/experimental-utils';", ], invalid: batchedSingleLineTests({ code: ` -import { foo } from "@typescript-eslint/typescript-estree"; -import foo from "@typescript-eslint/typescript-estree"; -import * as foo from "@typescript-eslint/typescript-estree"; +import { foo } from '@typescript-eslint/typescript-estree'; +import foo from '@typescript-eslint/typescript-estree'; +import * as foo from '@typescript-eslint/typescript-estree'; `, output: ` -import { foo } from "@typescript-eslint/experimental-utils"; -import foo from "@typescript-eslint/experimental-utils"; -import * as foo from "@typescript-eslint/experimental-utils"; +import { foo } from '@typescript-eslint/experimental-utils'; +import foo from '@typescript-eslint/experimental-utils'; +import * as foo from '@typescript-eslint/experimental-utils'; `, errors: [ { diff --git a/packages/eslint-plugin-internal/tests/rules/plugin-test-formatting.test.ts b/packages/eslint-plugin-internal/tests/rules/plugin-test-formatting.test.ts index 8d7e0b2c1d8..c2af09bec90 100644 --- a/packages/eslint-plugin-internal/tests/rules/plugin-test-formatting.test.ts +++ b/packages/eslint-plugin-internal/tests/rules/plugin-test-formatting.test.ts @@ -71,6 +71,7 @@ const a = 1; ${PARENT_INDENT}\``, wrap`noFormat\`const a = 1;\``, // sanity check suggestion validation + // eslint-disable-next-line @typescript-eslint/internal/plugin-test-formatting ` ruleTester.run({ invalid: [ diff --git a/packages/eslint-plugin-internal/tests/rules/prefer-ast-types-enum.test.ts b/packages/eslint-plugin-internal/tests/rules/prefer-ast-types-enum.test.ts index 2688e035b59..c2ffd8e489e 100644 --- a/packages/eslint-plugin-internal/tests/rules/prefer-ast-types-enum.test.ts +++ b/packages/eslint-plugin-internal/tests/rules/prefer-ast-types-enum.test.ts @@ -14,29 +14,29 @@ const ruleTester = new RuleTester({ ruleTester.run('prefer-ast-types-enum', rule, { valid: [ - 'node.type === "constructor"', - 'node.type === AST_NODE_TYPES.Literal', - 'node.type === AST_TOKEN_TYPES.Keyword', - 'node.type === 1', + "node.type === 'constructor';", + 'node.type === AST_NODE_TYPES.Literal;', + 'node.type === AST_TOKEN_TYPES.Keyword;', + 'node.type === 1;', ` - enum MY_ENUM { - Literal = 1 - } + enum MY_ENUM { + Literal = 1, + } `, ` - enum AST_NODE_TYPES { - Literal = 'Literal' - } + enum AST_NODE_TYPES { + Literal = 'Literal', + } `, ], invalid: batchedSingleLineTests({ code: ` -node.type === 'Literal' -node.type === 'Keyword' +node.type === 'Literal'; +node.type === 'Keyword'; `, output: ` -node.type === AST_NODE_TYPES.Literal -node.type === AST_TOKEN_TYPES.Keyword +node.type === AST_NODE_TYPES.Literal; +node.type === AST_TOKEN_TYPES.Keyword; `, errors: [ { diff --git a/packages/eslint-plugin/tests/rules/adjacent-overload-signatures.test.ts b/packages/eslint-plugin/tests/rules/adjacent-overload-signatures.test.ts index c040362a128..8e844bb96be 100644 --- a/packages/eslint-plugin/tests/rules/adjacent-overload-signatures.test.ts +++ b/packages/eslint-plugin/tests/rules/adjacent-overload-signatures.test.ts @@ -11,217 +11,224 @@ ruleTester.run('adjacent-overload-signatures', rule, { code: ` function error(a: string); function error(b: number); -function error(ab: string|number){ } +function error(ab: string | number) {} export { error }; - `, + `, parserOptions: { sourceType: 'module' }, }, { code: ` import { connect } from 'react-redux'; -export interface ErrorMessageModel { message: string; } -function mapStateToProps() { } -function mapDispatchToProps() { } +export interface ErrorMessageModel { + message: string; +} +function mapStateToProps() {} +function mapDispatchToProps() {} export default connect(mapStateToProps, mapDispatchToProps)(ErrorMessage); - `, + `, parserOptions: { sourceType: 'module' }, }, ` -export const foo = "a", bar = "b"; +export const foo = 'a', + bar = 'b'; export interface Foo {} export class Foo {} - `, + `, ` export interface Foo {} -export const foo = "a", bar = "b"; +export const foo = 'a', + bar = 'b'; export class Foo {} - `, + `, ` -const foo = "a", bar = "b"; +const foo = 'a', + bar = 'b'; interface Foo {} class Foo {} - `, + `, ` interface Foo {} -const foo = "a", bar = "b"; +const foo = 'a', + bar = 'b'; class Foo {} - `, + `, ` export class Foo {} export class Bar {} export type FooBar = Foo | Bar; - `, + `, ` export interface Foo {} export class Foo {} export class Bar {} export type FooBar = Foo | Bar; - `, + `, ` export function foo(s: string); export function foo(n: number); export function foo(sn: string | number) {} export function bar(): void {} export function baz(): void {} - `, + `, ` function foo(s: string); function foo(n: number); function foo(sn: string | number) {} function bar(): void {} function baz(): void {} - `, + `, ` declare function foo(s: string); declare function foo(n: number); declare function foo(sn: string | number); declare function bar(): void; declare function baz(): void; - `, + `, ` -declare module "Foo" { - export function foo(s: string): void; - export function foo(n: number): void; - export function foo(sn: string | number): void; - export function bar(): void; - export function baz(): void; -} - `, +declare module 'Foo' { + export function foo(s: string): void; + export function foo(n: number): void; + export function foo(sn: string | number): void; + export function bar(): void; + export function baz(): void; +} + `, ` declare namespace Foo { - export function foo(s: string): void; - export function foo(n: number): void; - export function foo(sn: string | number): void; - export function bar(): void; - export function baz(): void; + export function foo(s: string): void; + export function foo(n: number): void; + export function foo(sn: string | number): void; + export function bar(): void; + export function baz(): void; } - `, + `, ` type Foo = { - foo(s: string): void; - foo(n: number): void; - foo(sn: string | number): void; - bar(): void; - baz(): void; -} - `, + foo(s: string): void; + foo(n: number): void; + foo(sn: string | number): void; + bar(): void; + baz(): void; +}; + `, ` type Foo = { - foo(s: string): void; - ["foo"](n: number): void; - foo(sn: string | number): void; - bar(): void; - baz(): void; -} - `, + foo(s: string): void; + ['foo'](n: number): void; + foo(sn: string | number): void; + bar(): void; + baz(): void; +}; + `, ` interface Foo { - (s: string): void; - (n: number): void; - (sn: string | number): void; - foo(n: number): void; - bar(): void; - baz(): void; -} - `, + (s: string): void; + (n: number): void; + (sn: string | number): void; + foo(n: number): void; + bar(): void; + baz(): void; +} + `, ` interface Foo { - foo(s: string): void; - foo(n: number): void; - foo(sn: string | number): void; - bar(): void; - baz(): void; + foo(s: string): void; + foo(n: number): void; + foo(sn: string | number): void; + bar(): void; + baz(): void; } - `, + `, ` interface Foo { - foo(s: string): void; - ["foo"](n: number): void; - foo(sn: string | number): void; - bar(): void; - baz(): void; + foo(s: string): void; + ['foo'](n: number): void; + foo(sn: string | number): void; + bar(): void; + baz(): void; } - `, + `, ` interface Foo { - foo(): void; - bar: { - baz(s: string): void; - baz(n: number): void; - baz(sn: string | number): void; - } -} - `, + foo(): void; + bar: { + baz(s: string): void; + baz(n: number): void; + baz(sn: string | number): void; + }; +} + `, ` interface Foo { - new(s: string); - new(n: number); - new(sn: string | number); - foo(): void; + new (s: string); + new (n: number); + new (sn: string | number); + foo(): void; } - `, + `, ` class Foo { - constructor(s: string); - constructor(n: number); - constructor(sn: string | number) {} - bar(): void {} - baz(): void {} + constructor(s: string); + constructor(n: number); + constructor(sn: string | number) {} + bar(): void {} + baz(): void {} } - `, + `, ` class Foo { - foo(s: string): void; - foo(n: number): void; - foo(sn: string | number): void {} - bar(): void {} - baz(): void {} + foo(s: string): void; + foo(n: number): void; + foo(sn: string | number): void {} + bar(): void {} + baz(): void {} } - `, + `, ` class Foo { - foo(s: string): void; - ["foo"](n: number): void; - foo(sn: string | number): void {} - bar(): void {} - baz(): void {} + foo(s: string): void; + ['foo'](n: number): void; + foo(sn: string | number): void {} + bar(): void {} + baz(): void {} } - `, + `, ` class Foo { - name: string; - foo(s: string): void; - foo(n: number): void; - foo(sn: string | number): void {} - bar(): void {} - baz(): void {} -} - `, + name: string; + foo(s: string): void; + foo(n: number): void; + foo(sn: string | number): void {} + bar(): void {} + baz(): void {} +} + `, ` class Foo { - name: string; - static foo(s: string): void; - static foo(n: number): void; - static foo(sn: string | number): void {} - bar(): void {} - baz(): void {} -} - `, + name: string; + static foo(s: string): void; + static foo(n: number): void; + static foo(sn: string | number): void {} + bar(): void {} + baz(): void {} +} + `, ` class Test { static test() {} untest() {} test() {} } - `, + `, // examples from https://github.com/nzakas/eslint-plugin-typescript/issues/138 - 'export default function(foo : T) {}', - 'export default function named(foo : T) {}', + 'export default function(foo: T) {}', + 'export default function named(foo: T) {}', ` interface Foo { [Symbol.toStringTag](): void; [Symbol.iterator](): void; -}`, +} + `, ], invalid: [ { @@ -231,7 +238,7 @@ export function foo(n: number); export function bar(): void {} export function baz(): void {} export function foo(sn: string | number) {} - `, + `, errors: [ { messageId: 'adjacentSignature', @@ -248,7 +255,7 @@ export function foo(n: number); export type bar = number; export type baz = number | string; export function foo(sn: string | number) {} - `, + `, errors: [ { messageId: 'adjacentSignature', @@ -265,7 +272,7 @@ function foo(n: number); function bar(): void {} function baz(): void {} function foo(sn: string | number) {} - `, + `, errors: [ { messageId: 'adjacentSignature', @@ -282,7 +289,7 @@ function foo(n: number); type bar = number; type baz = number | string; function foo(sn: string | number) {} - `, + `, errors: [ { messageId: 'adjacentSignature', @@ -296,10 +303,10 @@ function foo(sn: string | number) {} code: ` function foo(s: string) {} function foo(n: number) {} -const a = ""; -const b = ""; +const a = ''; +const b = ''; function foo(sn: string | number) {} - `, + `, errors: [ { messageId: 'adjacentSignature', @@ -315,7 +322,7 @@ function foo(s: string) {} function foo(n: number) {} class Bar {} function foo(sn: string | number) {} - `, + `, errors: [ { messageId: 'adjacentSignature', @@ -331,18 +338,18 @@ function foo(s: string) {} function foo(n: number) {} function foo(sn: string | number) {} class Bar { - foo(s: string); - foo(n: number); - name: string; - foo(sn: string | number) { } + foo(s: string); + foo(n: number); + name: string; + foo(sn: string | number) {} } - `, + `, errors: [ { messageId: 'adjacentSignature', data: { name: 'foo' }, line: 9, - column: 5, + column: 3, }, ], }, @@ -353,7 +360,7 @@ declare function foo(n: number); declare function bar(): void; declare function baz(): void; declare function foo(sn: string | number); - `, + `, errors: [ { messageId: 'adjacentSignature', @@ -367,10 +374,10 @@ declare function foo(sn: string | number); code: ` declare function foo(s: string); declare function foo(n: number); -const a = ""; -const b = ""; +const a = ''; +const b = ''; declare function foo(sn: string | number); - `, + `, errors: [ { messageId: 'adjacentSignature', @@ -382,437 +389,438 @@ declare function foo(sn: string | number); }, { code: ` -declare module "Foo" { - export function foo(s: string): void; - export function foo(n: number): void; - export function bar(): void; - export function baz(): void; - export function foo(sn: string | number): void; +declare module 'Foo' { + export function foo(s: string): void; + export function foo(n: number): void; + export function bar(): void; + export function baz(): void; + export function foo(sn: string | number): void; } - `, + `, errors: [ { messageId: 'adjacentSignature', data: { name: 'foo' }, line: 7, - column: 5, + column: 3, }, ], }, { code: ` -declare module "Foo" { - export function foo(s: string): void; - export function foo(n: number): void; - export function foo(sn: string | number): void; - function baz(s: string): void; - export function bar(): void; - function baz(n: number): void; - function baz(sn: string | number): void; +declare module 'Foo' { + export function foo(s: string): void; + export function foo(n: number): void; + export function foo(sn: string | number): void; + function baz(s: string): void; + export function bar(): void; + function baz(n: number): void; + function baz(sn: string | number): void; } - `, + `, errors: [ { messageId: 'adjacentSignature', data: { name: 'baz' }, line: 8, - column: 5, + column: 3, }, ], }, { code: ` declare namespace Foo { - export function foo(s: string): void; - export function foo(n: number): void; - export function bar(): void; - export function baz(): void; - export function foo(sn: string | number): void; + export function foo(s: string): void; + export function foo(n: number): void; + export function bar(): void; + export function baz(): void; + export function foo(sn: string | number): void; } - `, + `, errors: [ { messageId: 'adjacentSignature', data: { name: 'foo' }, line: 7, - column: 5, + column: 3, }, ], }, { code: ` declare namespace Foo { - export function foo(s: string): void; - export function foo(n: number): void; - export function foo(sn: string | number): void; - function baz(s: string): void; - export function bar(): void; - function baz(n: number): void; - function baz(sn: string | number): void; -} - `, + export function foo(s: string): void; + export function foo(n: number): void; + export function foo(sn: string | number): void; + function baz(s: string): void; + export function bar(): void; + function baz(n: number): void; + function baz(sn: string | number): void; +} + `, errors: [ { messageId: 'adjacentSignature', data: { name: 'baz' }, line: 8, - column: 5, + column: 3, }, ], }, { code: ` type Foo = { - foo(s: string): void; - foo(n: number): void; - bar(): void; - baz(): void; - foo(sn: string | number): void; -} - `, + foo(s: string): void; + foo(n: number): void; + bar(): void; + baz(): void; + foo(sn: string | number): void; +}; + `, errors: [ { messageId: 'adjacentSignature', data: { name: 'foo' }, line: 7, - column: 5, + column: 3, }, ], }, { code: ` type Foo = { - foo(s: string): void; - ["foo"](n: number): void; - bar(): void; - baz(): void; - foo(sn: string | number): void; -} - `, + foo(s: string): void; + ['foo'](n: number): void; + bar(): void; + baz(): void; + foo(sn: string | number): void; +}; + `, errors: [ { messageId: 'adjacentSignature', data: { name: 'foo' }, line: 7, - column: 5, + column: 3, }, ], }, { code: ` type Foo = { - foo(s: string): void; - name: string; - foo(n: number): void; - foo(sn: string | number): void; - bar(): void; - baz(): void; -} - `, + foo(s: string): void; + name: string; + foo(n: number): void; + foo(sn: string | number): void; + bar(): void; + baz(): void; +}; + `, errors: [ { messageId: 'adjacentSignature', data: { name: 'foo' }, line: 5, - column: 5, + column: 3, }, ], }, { code: ` interface Foo { - (s: string): void; - foo(n: number): void; - (n: number): void; - (sn: string | number): void; - bar(): void; - baz(): void; -} - `, + (s: string): void; + foo(n: number): void; + (n: number): void; + (sn: string | number): void; + bar(): void; + baz(): void; +} + `, errors: [ { messageId: 'adjacentSignature', data: { name: 'call' }, line: 5, - column: 5, + column: 3, }, ], }, { code: ` interface Foo { - foo(s: string): void; - foo(n: number): void; - bar(): void; - baz(): void; - foo(sn: string | number): void; + foo(s: string): void; + foo(n: number): void; + bar(): void; + baz(): void; + foo(sn: string | number): void; } - `, + `, errors: [ { messageId: 'adjacentSignature', data: { name: 'foo' }, line: 7, - column: 5, + column: 3, }, ], }, { code: ` interface Foo { - foo(s: string): void; - ["foo"](n: number): void; - bar(): void; - baz(): void; - foo(sn: string | number): void; + foo(s: string): void; + ['foo'](n: number): void; + bar(): void; + baz(): void; + foo(sn: string | number): void; } - `, + `, errors: [ { messageId: 'adjacentSignature', data: { name: 'foo' }, line: 7, - column: 5, + column: 3, }, ], }, { code: ` interface Foo { - foo(s: string): void; - "foo"(n: number): void; - bar(): void; - baz(): void; - foo(sn: string | number): void; + foo(s: string): void; + 'foo'(n: number): void; + bar(): void; + baz(): void; + foo(sn: string | number): void; } - `, + `, errors: [ { messageId: 'adjacentSignature', data: { name: 'foo' }, line: 7, - column: 5, + column: 3, }, ], }, { code: ` interface Foo { - foo(s: string): void; - name: string; - foo(n: number): void; - foo(sn: string | number): void; - bar(): void; - baz(): void; -} - `, + foo(s: string): void; + name: string; + foo(n: number): void; + foo(sn: string | number): void; + bar(): void; + baz(): void; +} + `, errors: [ { messageId: 'adjacentSignature', data: { name: 'foo' }, line: 5, - column: 5, + column: 3, }, ], }, { code: ` interface Foo { + foo(): void; + bar: { + baz(s: string): void; + baz(n: number): void; foo(): void; - bar: { - baz(s: string): void; - baz(n: number): void; - foo(): void; - baz(sn: string | number): void; - } -} - `, + baz(sn: string | number): void; + }; +} + `, errors: [ { messageId: 'adjacentSignature', data: { name: 'baz' }, line: 8, - column: 9, + column: 5, }, ], }, { code: ` interface Foo { - new(s: string); - new(n: number); - foo(): void; - bar(): void; - new(sn: string | number); + new (s: string); + new (n: number); + foo(): void; + bar(): void; + new (sn: string | number); } - `, + `, errors: [ { messageId: 'adjacentSignature', data: { name: 'new' }, line: 7, - column: 5, + column: 3, }, ], }, { code: ` interface Foo { - new(s: string); - foo(): void; - new(n: number); - bar(): void; - new(sn: string | number); + new (s: string); + foo(): void; + new (n: number); + bar(): void; + new (sn: string | number); } - `, + `, errors: [ { messageId: 'adjacentSignature', data: { name: 'new' }, line: 5, - column: 5, + column: 3, }, { messageId: 'adjacentSignature', data: { name: 'new' }, line: 7, - column: 5, + column: 3, }, ], }, { code: ` class Foo { - constructor(s: string); - constructor(n: number); - bar(): void {} - baz(): void {} - constructor(sn: string | number) {} + constructor(s: string); + constructor(n: number); + bar(): void {} + baz(): void {} + constructor(sn: string | number) {} } - `, + `, errors: [ { messageId: 'adjacentSignature', data: { name: 'constructor' }, line: 7, - column: 5, + column: 3, }, ], }, { code: ` class Foo { - foo(s: string): void; - foo(n: number): void; - bar(): void {} - baz(): void {} - foo(sn: string | number): void {} + foo(s: string): void; + foo(n: number): void; + bar(): void {} + baz(): void {} + foo(sn: string | number): void {} } - `, + `, errors: [ { messageId: 'adjacentSignature', data: { name: 'foo' }, line: 7, - column: 5, + column: 3, }, ], }, { code: ` class Foo { - foo(s: string): void; - ["foo"](n: number): void; - bar(): void {} - baz(): void {} - foo(sn: string | number): void {} + foo(s: string): void; + ['foo'](n: number): void; + bar(): void {} + baz(): void {} + foo(sn: string | number): void {} } - `, + `, errors: [ { messageId: 'adjacentSignature', data: { name: 'foo' }, line: 7, - column: 5, + column: 3, }, ], }, { code: ` class Foo { - foo(s: string): void; - "foo"(n: number): void; - bar(): void {} - baz(): void {} - foo(sn: string | number): void {} -} - `, + // prettier-ignore + "foo"(s: string): void; + foo(n: number): void; + bar(): void {} + baz(): void {} + foo(sn: string | number): void {} +} + `, errors: [ { messageId: 'adjacentSignature', data: { name: 'foo' }, - line: 7, - column: 5, + line: 8, + column: 3, }, ], }, { code: ` class Foo { - constructor(s: string); - name: string; - constructor(n: number); - constructor(sn: string | number) {} - bar(): void {} - baz(): void {} -} - `, + constructor(s: string); + name: string; + constructor(n: number); + constructor(sn: string | number) {} + bar(): void {} + baz(): void {} +} + `, errors: [ { messageId: 'adjacentSignature', data: { name: 'constructor' }, line: 5, - column: 5, + column: 3, }, ], }, { code: ` class Foo { - foo(s: string): void; - name: string; - foo(n: number): void; - foo(sn: string | number): void {} - bar(): void {} - baz(): void {} -} - `, + foo(s: string): void; + name: string; + foo(n: number): void; + foo(sn: string | number): void {} + bar(): void {} + baz(): void {} +} + `, errors: [ { messageId: 'adjacentSignature', data: { name: 'foo' }, line: 5, - column: 5, + column: 3, }, ], }, { code: ` class Foo { - static foo(s: string): void; - name: string; - static foo(n: number): void; - static foo(sn: string | number): void {} - bar(): void {} - baz(): void {} -} - `, + static foo(s: string): void; + name: string; + static foo(n: number): void; + static foo(sn: string | number): void {} + bar(): void {} + baz(): void {} +} + `, errors: [ { messageId: 'adjacentSignature', data: { name: 'static foo' }, line: 5, - column: 5, + column: 3, }, ], }, diff --git a/packages/eslint-plugin/tests/rules/array-type.test.ts b/packages/eslint-plugin/tests/rules/array-type.test.ts index b74c06c09fd..5f15ba5e822 100644 --- a/packages/eslint-plugin/tests/rules/array-type.test.ts +++ b/packages/eslint-plugin/tests/rules/array-type.test.ts @@ -10,35 +10,35 @@ const ruleTester = new RuleTester({ ruleTester.run('array-type', rule, { valid: [ { - code: 'let a: readonly any[] = []', + code: 'let a: readonly any[] = [];', options: [{ default: 'array' }], }, { - code: 'let a = new Array()', + code: 'let a = new Array();', options: [{ default: 'array' }], }, { - code: 'let a: string[] = []', + code: 'let a: string[] = [];', options: [{ default: 'array' }], }, { - code: 'let a: (string | number)[] = []', + code: 'let a: (string | number)[] = [];', options: [{ default: 'array' }], }, { - code: 'let a: ({ foo: Bar[] })[] = []', + code: 'let a: { foo: Bar[] }[] = [];', options: [{ default: 'array' }], }, { - code: 'let a: Array = []', + code: 'let a: Array = [];', options: [{ default: 'generic' }], }, { - code: 'let a: Array = []', + code: 'let a: Array = [];', options: [{ default: 'generic' }], }, { - code: 'let a: Array<{ foo: Array }> = []', + code: 'let a: Array<{ foo: Array }> = [];', options: [{ default: 'generic' }], }, { @@ -46,7 +46,7 @@ ruleTester.run('array-type', rule, { options: [{ default: 'generic' }], }, { - code: 'function foo (a: Array): Array {}', + code: 'function foo(a: Array): Array {}', options: [{ default: 'generic' }], }, { @@ -54,15 +54,19 @@ ruleTester.run('array-type', rule, { options: [{ default: 'array-simple' }], }, { - code: `function fooFunction(foo: Array>) { - return foo.map(e => e.foo); -}`, + code: ` +function fooFunction(foo: Array>) { + return foo.map(e => e.foo); +} + `, options: [{ default: 'array-simple' }], }, { - code: `function bazFunction(baz: Arr>) { - return baz.map(e => e.baz); -}`, + code: ` +function bazFunction(baz: Arr>) { + return baz.map(e => e.baz); +} + `, options: [{ default: 'array-simple' }], }, { @@ -78,16 +82,20 @@ ruleTester.run('array-type', rule, { options: [{ default: 'array-simple' }], }, { - code: `namespace fooName { - type BarType = { bar: string }; - type BazType = Arr; -}`, + code: ` +namespace fooName { + type BarType = { bar: string }; + type BazType = Arr; +} + `, options: [{ default: 'array-simple' }], }, { - code: `interface FooInterface { - '.bar': {baz: string[];}; -}`, + code: ` +interface FooInterface { + '.bar': { baz: string[] }; +} + `, options: [{ default: 'array-simple' }], }, { @@ -95,19 +103,23 @@ ruleTester.run('array-type', rule, { options: [{ default: 'array' }], }, { - code: 'let ya = [[1, "2"]] as[number, string][];', + code: "let ya = [[1, '2']] as [number, string][];", options: [{ default: 'array' }], }, { - code: `function barFunction(bar: ArrayClass[]) { - return bar.map(e => e.bar); -}`, + code: ` +function barFunction(bar: ArrayClass[]) { + return bar.map(e => e.bar); +} + `, options: [{ default: 'array' }], }, { - code: `function bazFunction(baz: Arr>) { - return baz.map(e => e.baz); -}`, + code: ` +function bazFunction(baz: Arr>) { + return baz.map(e => e.baz); +} + `, options: [{ default: 'array' }], }, { @@ -115,7 +127,7 @@ ruleTester.run('array-type', rule, { options: [{ default: 'array' }], }, { - code: 'type barUnion = (string|number|boolean)[];', + code: 'type barUnion = (string | number | boolean)[];', options: [{ default: 'array' }], }, { @@ -123,18 +135,20 @@ ruleTester.run('array-type', rule, { options: [{ default: 'array' }], }, { - code: `interface FooInterface { - '.bar': {baz: string[];}; -}`, + code: ` +interface FooInterface { + '.bar': { baz: string[] }; +} + `, options: [{ default: 'array' }], }, { // https://github.com/typescript-eslint/typescript-eslint/issues/172 - code: 'type Unwrap = T extends (infer E)[] ? E : T', + code: 'type Unwrap = T extends (infer E)[] ? E : T;', options: [{ default: 'array' }], }, { - code: 'let z: Array = [3, "4"];', + code: "let z: Array = [3, '4'];", options: [{ default: 'generic' }], }, { @@ -146,15 +160,19 @@ ruleTester.run('array-type', rule, { options: [{ default: 'generic' }], }, { - code: `function fooFunction(foo: Array>) { - return foo.map(e => e.foo); -}`, + code: ` +function fooFunction(foo: Array>) { + return foo.map(e => e.foo); +} + `, options: [{ default: 'generic' }], }, { - code: `function bazFunction(baz: Arr>) { - return baz.map(e => e.baz); -}`, + code: ` +function bazFunction(baz: Arr>) { + return baz.map(e => e.baz); +} + `, options: [{ default: 'generic' }], }, { @@ -162,7 +180,7 @@ ruleTester.run('array-type', rule, { options: [{ default: 'generic' }], }, { - code: 'type fooUnion = Array;', + code: 'type fooUnion = Array;', options: [{ default: 'generic' }], }, { @@ -171,40 +189,40 @@ ruleTester.run('array-type', rule, { }, { // https://github.com/typescript-eslint/typescript-eslint/issues/172 - code: 'type Unwrap = T extends Array ? E : T', + code: 'type Unwrap = T extends Array ? E : T;', options: [{ default: 'generic' }], }, // readonly { - code: 'let a: string[] = []', + code: 'let a: string[] = [];', options: [{ default: 'array', readonly: 'generic' }], }, { - code: 'let a: ReadonlyArray = []', + code: 'let a: ReadonlyArray = [];', options: [{ default: 'array', readonly: 'generic' }], }, { - code: 'let a: ReadonlyArray = [[]]', + code: 'let a: ReadonlyArray = [[]];', options: [{ default: 'array', readonly: 'generic' }], }, { - code: 'let a: Array = []', + code: 'let a: Array = [];', options: [{ default: 'generic', readonly: 'array' }], }, { - code: 'let a: readonly number[] = []', + code: 'let a: readonly number[] = [];', options: [{ default: 'generic', readonly: 'array' }], }, { - code: 'let a: readonly Array[] = [[]]', + code: 'let a: readonly Array[] = [[]];', options: [{ default: 'generic', readonly: 'array' }], }, ], invalid: [ { - code: 'let a: Array = []', - output: 'let a: string[] = []', + code: 'let a: Array = [];', + output: 'let a: string[] = [];', options: [{ default: 'array' }], errors: [ { @@ -216,8 +234,8 @@ ruleTester.run('array-type', rule, { ], }, { - code: 'let a: Array = []', - output: 'let a: (string | number)[] = []', + code: 'let a: Array = [];', + output: 'let a: (string | number)[] = [];', options: [{ default: 'array' }], errors: [ { @@ -229,21 +247,21 @@ ruleTester.run('array-type', rule, { ], }, { - code: 'let a: ({ foo: Array })[] = []', - output: 'let a: ({ foo: Bar[] })[] = []', + code: 'let a: { foo: Array }[] = [];', + output: 'let a: { foo: Bar[] }[] = [];', options: [{ default: 'array' }], errors: [ { messageId: 'errorStringArray', data: { type: 'Bar' }, line: 1, - column: 16, + column: 15, }, ], }, { - code: 'let a: string[] = []', - output: 'let a: Array = []', + code: 'let a: string[] = [];', + output: 'let a: Array = [];', options: [{ default: 'generic' }], errors: [ { @@ -255,8 +273,8 @@ ruleTester.run('array-type', rule, { ], }, { - code: 'let a: (string | number)[] = []', - output: 'let a: Array = []', + code: 'let a: (string | number)[] = [];', + output: 'let a: Array = [];', options: [{ default: 'generic' }], errors: [ { @@ -268,8 +286,8 @@ ruleTester.run('array-type', rule, { ], }, { - code: 'let a: Array<{ foo: Bar[] }> = []', - output: 'let a: Array<{ foo: Array }> = []', + code: 'let a: Array<{ foo: Bar[] }> = [];', + output: 'let a: Array<{ foo: Array }> = [];', options: [{ default: 'generic' }], errors: [ { @@ -281,8 +299,8 @@ ruleTester.run('array-type', rule, { ], }, { - code: 'let a: Array<{ foo: Foo | Bar[] }> = []', - output: 'let a: Array<{ foo: Foo | Array }> = []', + code: 'let a: Array<{ foo: Foo | Bar[] }> = [];', + output: 'let a: Array<{ foo: Foo | Array }> = [];', options: [{ default: 'generic' }], errors: [ { @@ -294,27 +312,27 @@ ruleTester.run('array-type', rule, { ], }, { - code: 'function foo (a: Array): Array {}', - output: 'function foo (a: Bar[]): Bar[] {}', + code: 'function foo(a: Array): Array {}', + output: 'function foo(a: Bar[]): Bar[] {}', options: [{ default: 'array' }], errors: [ { messageId: 'errorStringArray', data: { type: 'Bar' }, line: 1, - column: 18, + column: 17, }, { messageId: 'errorStringArray', data: { type: 'Bar' }, line: 1, - column: 31, + column: 30, }, ], }, { - code: 'let a: Array<>[] = []', - output: 'let a: any[][] = []', + code: 'let a: Array<>[] = [];', + output: 'let a: any[][] = [];', options: [{ default: 'array-simple' }], errors: [ { @@ -358,8 +376,8 @@ ruleTester.run('array-type', rule, { ], }, { - code: 'let y: string[] = >["2"];', - output: 'let y: string[] = ["2"];', + code: "let y: string[] = >['2'];", + output: "let y: string[] = ['2'];", options: [{ default: 'array-simple' }], errors: [ { @@ -371,8 +389,8 @@ ruleTester.run('array-type', rule, { ], }, { - code: 'let z: Array = [3, "4"];', - output: 'let z: any[] = [3, "4"];', + code: "let z: Array = [3, '4'];", + output: "let z: any[] = [3, '4'];", options: [{ default: 'array-simple' }], errors: [ { @@ -384,15 +402,15 @@ ruleTester.run('array-type', rule, { ], }, { - code: 'let ya = [[1, "2"]] as[number, string][];', - output: 'let ya = [[1, "2"]] as Array<[number, string]>;', + code: "let ya = [[1, '2']] as [number, string][];", + output: "let ya = [[1, '2']] as Array<[number, string]>;", options: [{ default: 'array-simple' }], errors: [ { messageId: 'errorStringGenericSimple', data: { type: 'T' }, line: 1, - column: 23, + column: 24, }, ], }, @@ -410,56 +428,68 @@ ruleTester.run('array-type', rule, { ], }, { - code: `// Ignore user defined aliases -let yyyy: Arr>[]> = [[[["2"]]]];`, - output: `// Ignore user defined aliases -let yyyy: Arr>>> = [[[["2"]]]];`, + code: ` +// Ignore user defined aliases +let yyyy: Arr>[]> = [[[['2']]]]; + `, + output: ` +// Ignore user defined aliases +let yyyy: Arr>>> = [[[['2']]]]; + `, options: [{ default: 'array-simple' }], errors: [ { messageId: 'errorStringGenericSimple', data: { type: 'T' }, - line: 2, + line: 3, column: 15, }, ], }, { - code: `interface ArrayClass { - foo: Array; - bar: T[]; - baz: Arr; - xyz: this[]; -}`, - output: `interface ArrayClass { - foo: T[]; - bar: T[]; - baz: Arr; - xyz: this[]; -}`, + code: ` +interface ArrayClass { + foo: Array; + bar: T[]; + baz: Arr; + xyz: this[]; +} + `, + output: ` +interface ArrayClass { + foo: T[]; + bar: T[]; + baz: Arr; + xyz: this[]; +} + `, options: [{ default: 'array-simple' }], errors: [ { messageId: 'errorStringArraySimple', data: { type: 'T' }, - line: 2, - column: 10, + line: 3, + column: 8, }, ], }, { - code: `function barFunction(bar: ArrayClass[]) { - return bar.map(e => e.bar); -}`, - output: `function barFunction(bar: Array>) { - return bar.map(e => e.bar); -}`, + code: ` +function barFunction(bar: ArrayClass[]) { + return bar.map(e => e.bar); +} + `, + output: ` +function barFunction(bar: Array>) { + return bar.map(e => e.bar); +} + `, options: [{ default: 'array-simple' }], errors: [ { messageId: 'errorStringGenericSimple', data: { type: 'T' }, - line: 1, + line: 2, column: 27, }, ], @@ -478,8 +508,8 @@ let yyyy: Arr>>> = [[[["2"]]]];`, ], }, { - code: 'type barUnion = (string|number|boolean)[];', - output: 'type barUnion = Array;', + code: 'type barUnion = (string | number | boolean)[];', + output: 'type barUnion = Array;', options: [{ default: 'array-simple' }], errors: [ { @@ -504,8 +534,8 @@ let yyyy: Arr>>> = [[[["2"]]]];`, ], }, { - code: 'let v: Array = [{ bar: "bar" }];', - output: 'let v: fooName.BarType[] = [{ bar: "bar" }];', + code: "let v: Array = [{ bar: 'bar' }];", + output: "let v: fooName.BarType[] = [{ bar: 'bar' }];", options: [{ default: 'array-simple' }], errors: [ { @@ -517,8 +547,8 @@ let yyyy: Arr>>> = [[[["2"]]]];`, ], }, { - code: 'let w: fooName.BazType[] = [["baz"]];', - output: 'let w: Array> = [["baz"]];', + code: "let w: fooName.BazType[] = [['baz']];", + output: "let w: Array> = [['baz']];", options: [{ default: 'array-simple' }], errors: [ { @@ -543,8 +573,8 @@ let yyyy: Arr>>> = [[[["2"]]]];`, ], }, { - code: 'let y: string[] = >["2"];', - output: 'let y: string[] = ["2"];', + code: "let y: string[] = >['2'];", + output: "let y: string[] = ['2'];", options: [{ default: 'array' }], errors: [ { @@ -556,8 +586,8 @@ let yyyy: Arr>>> = [[[["2"]]]];`, ], }, { - code: 'let z: Array = [3, "4"];', - output: 'let z: any[] = [3, "4"];', + code: "let z: Array = [3, '4'];", + output: "let z: any[] = [3, '4'];", options: [{ default: 'array' }], errors: [ { @@ -582,54 +612,66 @@ let yyyy: Arr>>> = [[[["2"]]]];`, ], }, { - code: `// Ignore user defined aliases -let yyyy: Arr>[]> = [[[["2"]]]];`, - output: `// Ignore user defined aliases -let yyyy: Arr[][]> = [[[["2"]]]];`, + code: ` +// Ignore user defined aliases +let yyyy: Arr>[]> = [[[['2']]]]; + `, + output: ` +// Ignore user defined aliases +let yyyy: Arr[][]> = [[[['2']]]]; + `, options: [{ default: 'array' }], errors: [ { messageId: 'errorStringArray', data: { type: 'T' }, - line: 2, + line: 3, column: 15, }, ], }, { - code: `interface ArrayClass { - foo: Array; - bar: T[]; - baz: Arr; -}`, - output: `interface ArrayClass { - foo: T[]; - bar: T[]; - baz: Arr; -}`, + code: ` +interface ArrayClass { + foo: Array; + bar: T[]; + baz: Arr; +} + `, + output: ` +interface ArrayClass { + foo: T[]; + bar: T[]; + baz: Arr; +} + `, options: [{ default: 'array' }], errors: [ { messageId: 'errorStringArray', data: { type: 'T' }, - line: 2, - column: 10, + line: 3, + column: 8, }, ], }, { - code: `function fooFunction(foo: Array>) { - return foo.map(e => e.foo); -}`, - output: `function fooFunction(foo: ArrayClass[]) { - return foo.map(e => e.foo); -}`, + code: ` +function fooFunction(foo: Array>) { + return foo.map(e => e.foo); +} + `, + output: ` +function fooFunction(foo: ArrayClass[]) { + return foo.map(e => e.foo); +} + `, options: [{ default: 'array' }], errors: [ { messageId: 'errorStringArray', data: { type: 'T' }, - line: 1, + line: 2, column: 27, }, ], @@ -648,8 +690,8 @@ let yyyy: Arr[][]> = [[[["2"]]]];`, ], }, { - code: 'type fooUnion = Array;', - output: 'type fooUnion = (string|number|boolean)[];', + code: 'type fooUnion = Array;', + output: 'type fooUnion = (string | number | boolean)[];', options: [{ default: 'array' }], errors: [ { @@ -711,8 +753,8 @@ let yyyy: Arr[][]> = [[[["2"]]]];`, ], }, { - code: 'let y: string[] = >["2"];', - output: 'let y: Array = >["2"];', + code: "let y: string[] = >['2'];", + output: "let y: Array = >['2'];", options: [{ default: 'generic' }], errors: [ { @@ -724,67 +766,79 @@ let yyyy: Arr[][]> = [[[["2"]]]];`, ], }, { - code: 'let ya = [[1, "2"]] as[number, string][];', - output: 'let ya = [[1, "2"]] as Array<[number, string]>;', + code: "let ya = [[1, '2']] as [number, string][];", + output: "let ya = [[1, '2']] as Array<[number, string]>;", options: [{ default: 'generic' }], errors: [ { messageId: 'errorStringGeneric', data: { type: 'T' }, line: 1, - column: 23, + column: 24, }, ], }, { - code: `// Ignore user defined aliases -let yyyy: Arr>[]> = [[[["2"]]]];`, - output: `// Ignore user defined aliases -let yyyy: Arr>>> = [[[["2"]]]];`, + code: ` +// Ignore user defined aliases +let yyyy: Arr>[]> = [[[['2']]]]; + `, + output: ` +// Ignore user defined aliases +let yyyy: Arr>>> = [[[['2']]]]; + `, options: [{ default: 'generic' }], errors: [ { messageId: 'errorStringGeneric', data: { type: 'T' }, - line: 2, + line: 3, column: 15, }, ], }, { - code: `interface ArrayClass { - foo: Array; - bar: T[]; - baz: Arr; -}`, - output: `interface ArrayClass { - foo: Array; - bar: Array; - baz: Arr; -}`, + code: ` +interface ArrayClass { + foo: Array; + bar: T[]; + baz: Arr; +} + `, + output: ` +interface ArrayClass { + foo: Array; + bar: Array; + baz: Arr; +} + `, options: [{ default: 'generic' }], errors: [ { messageId: 'errorStringGeneric', data: { type: 'T' }, - line: 3, - column: 10, + line: 4, + column: 8, }, ], }, { - code: `function barFunction(bar: ArrayClass[]) { - return bar.map(e => e.bar); -}`, - output: `function barFunction(bar: Array>) { - return bar.map(e => e.bar); -}`, + code: ` +function barFunction(bar: ArrayClass[]) { + return bar.map(e => e.bar); +} + `, + output: ` +function barFunction(bar: Array>) { + return bar.map(e => e.bar); +} + `, options: [{ default: 'generic' }], errors: [ { messageId: 'errorStringGeneric', data: { type: 'T' }, - line: 1, + line: 2, column: 27, }, ], @@ -803,8 +857,8 @@ let yyyy: Arr>>> = [[[["2"]]]];`, ], }, { - code: 'type barUnion = (string|number|boolean)[];', - output: 'type barUnion = Array;', + code: 'type barUnion = (string | number | boolean)[];', + output: 'type barUnion = Array;', options: [{ default: 'generic' }], errors: [ { @@ -829,26 +883,30 @@ let yyyy: Arr>>> = [[[["2"]]]];`, ], }, { - code: `interface FooInterface { - '.bar': {baz: string[];}; -}`, - output: `interface FooInterface { - '.bar': {baz: Array;}; -}`, + code: ` +interface FooInterface { + '.bar': { baz: string[] }; +} + `, + output: ` +interface FooInterface { + '.bar': { baz: Array }; +} + `, options: [{ default: 'generic' }], errors: [ { messageId: 'errorStringGeneric', data: { type: 'string' }, - line: 2, - column: 19, + line: 3, + column: 18, }, ], }, { // https://github.com/typescript-eslint/typescript-eslint/issues/172 - code: 'type Unwrap = T extends Array ? E : T', - output: 'type Unwrap = T extends (infer E)[] ? E : T', + code: 'type Unwrap = T extends Array ? E : T;', + output: 'type Unwrap = T extends (infer E)[] ? E : T;', options: [{ default: 'array' }], errors: [ { @@ -861,8 +919,8 @@ let yyyy: Arr>>> = [[[["2"]]]];`, }, { // https://github.com/typescript-eslint/typescript-eslint/issues/172 - code: 'type Unwrap = T extends (infer E)[] ? E : T', - output: 'type Unwrap = T extends Array ? E : T', + code: 'type Unwrap = T extends (infer E)[] ? E : T;', + output: 'type Unwrap = T extends Array ? E : T;', options: [{ default: 'generic' }], errors: [ { @@ -928,8 +986,8 @@ let yyyy: Arr>>> = [[[["2"]]]];`, ], }, { - code: 'const x: readonly number[] = []', - output: 'const x: ReadonlyArray = []', + code: 'const x: readonly number[] = [];', + output: 'const x: ReadonlyArray = [];', options: [{ default: 'array', readonly: 'generic' }], errors: [ { @@ -941,8 +999,8 @@ let yyyy: Arr>>> = [[[["2"]]]];`, ], }, { - code: 'const x: readonly number[][] = []', - output: 'const x: readonly Array[] = []', + code: 'const x: readonly number[][] = [];', + output: 'const x: readonly Array[] = [];', options: [{ default: 'generic', readonly: 'array' }], errors: [ { diff --git a/packages/eslint-plugin/tests/rules/await-thenable.test.ts b/packages/eslint-plugin/tests/rules/await-thenable.test.ts index 5ee0f7d0341..52fc1ca2306 100644 --- a/packages/eslint-plugin/tests/rules/await-thenable.test.ts +++ b/packages/eslint-plugin/tests/rules/await-thenable.test.ts @@ -13,51 +13,51 @@ const ruleTester = new RuleTester({ parser: '@typescript-eslint/parser', }); -ruleTester.run('await-promise', rule, { +ruleTester.run('await-thenable', rule, { valid: [ ` async function test() { - await Promise.resolve("value"); - await Promise.reject(new Error("message")); + await Promise.resolve('value'); + await Promise.reject(new Error('message')); } -`, + `, ` async function test() { await (async () => true)(); } -`, + `, ` async function test() { function returnsPromise() { - return Promise.resolve("value"); + return Promise.resolve('value'); } await returnsPromise(); } -`, + `, ` async function test() { async function returnsPromiseAsync() {} await returnsPromiseAsync(); } -`, + `, ` async function test() { let anyValue: any; await anyValue; } -`, + `, ` async function test() { let unknownValue: unknown; await unknownValue; } -`, + `, ` async function test() { const numberPromise: Promise; await numberPromise; } -`, + `, ` async function test() { class Foo extends Promise {} @@ -68,7 +68,7 @@ async function test() { const bar: Bar = Bar.resolve(2); await bar; } -`, + `, ` async function test() { await (Math.random() > 0.5 ? numberPromise : 0); @@ -78,15 +78,17 @@ async function test() { const intersectionPromise: Promise & number; await intersectionPromise; } -`, + `, ` async function test() { - class Thenable { then(callback: () => {}) { } }; + class Thenable { + then(callback: () => {}) {} + } const thenable = new Thenable(); await thenable; } -`, + `, ` // https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/promise-polyfill/index.d.ts // Type definitions for promise-polyfill 6.0 @@ -96,7 +98,7 @@ async function test() { // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped interface PromisePolyfillConstructor extends PromiseConstructor { - _immediateFn?: (handler: (() => void) | string) => void; + _immediateFn?: (handler: (() => void) | string) => void; } declare const PromisePolyfill: PromisePolyfillConstructor; @@ -106,7 +108,7 @@ async function test() { await promise; } -`, + `, ` // https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/bluebird/index.d.ts // Type definitions for bluebird 3.5 @@ -151,14 +153,21 @@ type CatchFilter = ((error: E) => boolean) | (object & E); type IterableItem = R extends Iterable ? U : never; type IterableOrNever = Extract>; type Resolvable = R | PromiseLike; -type IterateFunction = (item: T, index: number, arrayLength: number) => Resolvable; +type IterateFunction = ( + item: T, + index: number, + arrayLength: number, +) => Resolvable; declare class Bluebird implements PromiseLike { - then(onFulfill?: (value: R) => Resolvable, onReject?: (error: any) => Resolvable): Bluebird; // For simpler signature help. + then( + onFulfill?: (value: R) => Resolvable, + onReject?: (error: any) => Resolvable, + ): Bluebird; // For simpler signature help. then( - onfulfilled?: ((value: R) => Resolvable) | null, - onrejected?: ((reason: any) => Resolvable) | null - ): Bluebird; + onfulfilled?: ((value: R) => Resolvable) | null, + onrejected?: ((reason: any) => Resolvable) | null, + ): Bluebird; } declare const bluebird: Bluebird; @@ -166,7 +175,7 @@ declare const bluebird: Bluebird; async function test() { await bluebird; } -`, + `, ], invalid: [ @@ -174,14 +183,14 @@ async function test() { code: ` async function test() { await 0; - await "value"; + await 'value'; - await (Math.random() > 0.5 ? "" : 0); + await (Math.random() > 0.5 ? '' : 0); class NonPromise extends Array {} await new NonPromise(); } -`, + `, errors: [ { line: 3, @@ -204,7 +213,9 @@ async function test() { { code: ` async function test() { - class IncorrectThenable { then() { } }; + class IncorrectThenable { + then() {} + } const thenable = new IncorrectThenable(); await thenable; @@ -212,7 +223,7 @@ async function test() { `, errors: [ { - line: 6, + line: 8, messageId, }, ], diff --git a/packages/eslint-plugin/tests/rules/ban-ts-comment.test.ts b/packages/eslint-plugin/tests/rules/ban-ts-comment.test.ts index c2226f7041a..fcbb788a308 100644 --- a/packages/eslint-plugin/tests/rules/ban-ts-comment.test.ts +++ b/packages/eslint-plugin/tests/rules/ban-ts-comment.test.ts @@ -5,14 +5,16 @@ const ruleTester = new RuleTester({ parser: '@typescript-eslint/parser', }); -ruleTester.run('ts-ignore', rule, { +ruleTester.run('ban-ts-comment', rule, { valid: [ - `// just a comment containing @ts-ignore somewhere`, - `/* @ts-ignore */`, - `/** @ts-ignore */`, - `/* + '// just a comment containing @ts-ignore somewhere', + '/* @ts-ignore */', + '/** @ts-ignore */', + ` +/* // @ts-ignore in a block -*/`, +*/ + `, { code: '// @ts-ignore', options: [{ 'ts-ignore': false }], @@ -68,9 +70,9 @@ ruleTester.run('ts-ignore', rule, { code: ` if (false) { // @ts-ignore: Unreachable code error - console.log("hello"); + console.log('hello'); } - `, + `, errors: [ { data: { directive: 'ignore' }, @@ -85,12 +87,14 @@ if (false) { ruleTester.run('ts-nocheck', rule, { valid: [ - `// just a comment containing @ts-nocheck somewhere`, - `/* @ts-nocheck */`, - `/** @ts-nocheck */`, - `/* + '// just a comment containing @ts-nocheck somewhere', + '/* @ts-nocheck */', + '/** @ts-nocheck */', + ` +/* // @ts-nocheck in a block -*/`, +*/ + `, { code: '// @ts-nocheck', options: [{ 'ts-nocheck': false }], @@ -146,9 +150,9 @@ ruleTester.run('ts-nocheck', rule, { code: ` if (false) { // @ts-nocheck: Unreachable code error - console.log("hello"); + console.log('hello'); } - `, + `, errors: [ { data: { directive: 'nocheck' }, @@ -163,12 +167,14 @@ if (false) { ruleTester.run('ts-check', rule, { valid: [ - `// just a comment containing @ts-check somewhere`, - `/* @ts-check */`, - `/** @ts-check */`, - `/* + '// just a comment containing @ts-check somewhere', + '/* @ts-check */', + '/** @ts-check */', + ` +/* // @ts-check in a block -*/`, +*/ + `, { code: '// @ts-check', options: [{ 'ts-check': false }], @@ -216,9 +222,9 @@ ruleTester.run('ts-check', rule, { code: ` if (false) { // @ts-check: Unreachable code error - console.log("hello"); + console.log('hello'); } - `, + `, options: [{ 'ts-check': true }], errors: [ { diff --git a/packages/eslint-plugin/tests/rules/ban-ts-ignore.test.ts b/packages/eslint-plugin/tests/rules/ban-ts-ignore.test.ts index d9132df2b0d..e1471950f46 100644 --- a/packages/eslint-plugin/tests/rules/ban-ts-ignore.test.ts +++ b/packages/eslint-plugin/tests/rules/ban-ts-ignore.test.ts @@ -7,12 +7,14 @@ const ruleTester = new RuleTester({ ruleTester.run('ban-ts-ignore', rule, { valid: [ - `// just a comment containing @ts-ignore somewhere`, - `/* @ts-ignore */`, - `/** @ts-ignore */`, - `/* + '// just a comment containing @ts-ignore somewhere', + '/* @ts-ignore */', + '/** @ts-ignore */', + ` +/* // @ts-ignore in a block -*/`, +*/ + `, ], invalid: [ { @@ -49,9 +51,9 @@ ruleTester.run('ban-ts-ignore', rule, { code: ` if (false) { // @ts-ignore: Unreachable code error - console.log("hello"); + console.log('hello'); } - `, + `, errors: [ { messageId: 'tsIgnoreComment', diff --git a/packages/eslint-plugin/tests/rules/ban-types.test.ts b/packages/eslint-plugin/tests/rules/ban-types.test.ts index 0edeb74f8d1..772fa7716f8 100644 --- a/packages/eslint-plugin/tests/rules/ban-types.test.ts +++ b/packages/eslint-plugin/tests/rules/ban-types.test.ts @@ -1,5 +1,5 @@ import rule from '../../src/rules/ban-types'; -import { RuleTester } from '../RuleTester'; +import { RuleTester, noFormat } from '../RuleTester'; import { InferOptionsTypeFromRule } from '../../src/util'; const ruleTester = new RuleTester({ @@ -47,7 +47,7 @@ ruleTester.run('ban-types', rule, { valid: [ 'let f = Object();', // Should not fail if there is no options set 'let f: {} = {};', - 'let f: { x: number, y: number } = { x: 1, y: 1 };', + 'let f: { x: number; y: number } = { x: 1, y: 1 };', { code: 'let f = Object();', options, @@ -65,11 +65,11 @@ ruleTester.run('ban-types', rule, { options, }, { - code: 'let a: _.NS.Bad', + code: 'let a: _.NS.Bad;', options, }, { - code: 'let a: NS.Bad._', + code: 'let a: NS.Bad._;', options, }, // Replace default options instead of merging with extendDefaults: false @@ -88,11 +88,11 @@ ruleTester.run('ban-types', rule, { ], }, { - code: 'let a: undefined', + code: 'let a: undefined;', options: options2, }, { - code: 'let a: null', + code: 'let a: null;', options: options3, }, ], @@ -166,8 +166,8 @@ ruleTester.run('ban-types', rule, { ], }, { - code: 'let b: {c: String};', - output: 'let b: {c: string};', + code: 'let b: { c: String };', + output: 'let b: { c: string };', errors: [ { messageId: 'bannedTypeMessage', @@ -177,7 +177,7 @@ ruleTester.run('ban-types', rule, { customMessage: ' Use string instead.', }, line: 1, - column: 12, + column: 13, }, ], options, @@ -231,22 +231,22 @@ ruleTester.run('ban-types', rule, { { code: ` class Foo extends Bar implements Baz { - constructor (foo: String | Object) {} + constructor(foo: String | Object) {} - exit() : Array { - const foo: String = 1 as String + exit(): Array { + const foo: String = 1 as String; } } - `, + `, output: ` class Foo extends Bar implements Baz { - constructor (foo: string | Object) {} + constructor(foo: string | Object) {} - exit() : Array { - const foo: string = 1 as string + exit(): Array { + const foo: string = 1 as string; } } - `, + `, errors: [ { messageId: 'bannedTypeMessage', @@ -285,7 +285,7 @@ class Foo extends Bar implements Baz { customMessage: ' Use string instead.', }, line: 3, - column: 21, + column: 20, }, { messageId: 'bannedTypeMessage', @@ -294,13 +294,13 @@ class Foo extends Bar implements Baz { customMessage: " Use '{}' instead.", }, line: 3, - column: 30, + column: 29, }, { messageId: 'bannedTypeMessage', data: { name: 'Array', customMessage: '' }, line: 5, - column: 12, + column: 11, }, { messageId: 'bannedTypeMessage', @@ -310,7 +310,7 @@ class Foo extends Bar implements Baz { customMessage: ' Use string instead.', }, line: 5, - column: 18, + column: 17, }, { messageId: 'bannedTypeMessage', @@ -383,8 +383,8 @@ let b: Foo; options, }, { - code: `let foo: {} = {};`, - output: `let foo: object = {};`, + code: 'let foo: {} = {};', + output: 'let foo: object = {};', options: [ { types: { @@ -408,7 +408,7 @@ let b: Foo; ], }, { - code: ` + code: noFormat` let foo: {} = {}; let bar: { } = {}; `, @@ -473,8 +473,8 @@ let bar: object = {}; ], }, { - code: 'let a: Foo< F >;', - output: 'let a: Foo< T >;', + code: noFormat`let a: Foo< F >;`, + output: noFormat`let a: Foo< T >;`, errors: [ { messageId: 'bannedTypeMessage', diff --git a/packages/eslint-plugin/tests/rules/brace-style.test.ts b/packages/eslint-plugin/tests/rules/brace-style.test.ts index 21d10998eac..28b9413795d 100644 --- a/packages/eslint-plugin/tests/rules/brace-style.test.ts +++ b/packages/eslint-plugin/tests/rules/brace-style.test.ts @@ -1,3 +1,8 @@ +/* eslint-disable eslint-comments/no-use */ +// this rule tests the position of braces, which prettier will want to fix and break the tests +/* eslint "@typescript-eslint/internal/plugin-test-formatting": ["error", { formatWithPrettier: false }] */ +/* eslint-enable eslint-comments/no-use */ + import rule from '../../src/rules/brace-style'; import { RuleTester } from '../RuleTester'; @@ -211,67 +216,67 @@ catch (e) options: ['allman'], }, { - code: `function foo () { return; }`, + code: 'function foo () { return; }', options: ['1tbs', { allowSingleLine: true }], }, { - code: `function foo () { a(); b(); return; }`, + code: 'function foo () { a(); b(); return; }', options: ['1tbs', { allowSingleLine: true }], }, { - code: `function a(b,c,d) { }`, + code: 'function a(b,c,d) { }', options: ['1tbs', { allowSingleLine: true }], }, { - code: `!function foo () { return; }`, + code: '!function foo () { return; }', options: ['1tbs', { allowSingleLine: true }], }, { - code: `!function a(b,c,d) { }`, + code: '!function a(b,c,d) { }', options: ['1tbs', { allowSingleLine: true }], }, { - code: `if (foo) { bar(); }`, + code: 'if (foo) { bar(); }', options: ['1tbs', { allowSingleLine: true }], }, { - code: `if (a) { b(); } else { c(); }`, + code: 'if (a) { b(); } else { c(); }', options: ['1tbs', { allowSingleLine: true }], }, { - code: `while (foo) { bar(); }`, + code: 'while (foo) { bar(); }', options: ['1tbs', { allowSingleLine: true }], }, { - code: `for (;;) { bar(); }`, + code: 'for (;;) { bar(); }', options: ['1tbs', { allowSingleLine: true }], }, { - code: `with (foo) { bar(); }`, + code: 'with (foo) { bar(); }', options: ['1tbs', { allowSingleLine: true }], }, { - code: `switch (foo) { case 'bar': break; }`, + code: "switch (foo) { case 'bar': break; }", options: ['1tbs', { allowSingleLine: true }], }, { - code: `try { bar(); } catch (e) { baz(); }`, + code: 'try { bar(); } catch (e) { baz(); }', options: ['1tbs', { allowSingleLine: true }], }, { - code: `do { bar(); } while (true)`, + code: 'do { bar(); } while (true)', options: ['1tbs', { allowSingleLine: true }], }, { - code: `for (foo in bar) { baz(); }`, + code: 'for (foo in bar) { baz(); }', options: ['1tbs', { allowSingleLine: true }], }, { - code: `if (a && b && c) { }`, + code: 'if (a && b && c) { }', options: ['1tbs', { allowSingleLine: true }], }, { - code: `switch(0) {}`, + code: 'switch(0) {}', options: ['1tbs', { allowSingleLine: true }], }, { @@ -289,7 +294,7 @@ catch (e) { baz(); } options: ['stroustrup', { allowSingleLine: true }], }, { - code: `var foo = () => { return; }`, + code: 'var foo = () => { return; }', options: ['stroustrup', { allowSingleLine: true }], parserOptions: { ecmaVersion: 6 }, }, @@ -308,7 +313,7 @@ catch (e) { baz(); } options: ['allman', { allowSingleLine: true }], }, { - code: `var foo = () => { return; }`, + code: 'var foo = () => { return; }', options: ['allman', { allowSingleLine: true }], parserOptions: { ecmaVersion: 6 }, }, @@ -345,7 +350,7 @@ switch(x) options: ['allman'], }, { - code: `switch(x) {}`, + code: 'switch(x) {}', options: ['allman', { allowSingleLine: true }], }, { @@ -388,25 +393,25 @@ Foo options: ['allman'], }, { - code: `class Foo {}`, + code: 'class Foo {}', options: ['1tbs', { allowSingleLine: true }], }, { - code: `class Foo {}`, + code: 'class Foo {}', options: ['allman', { allowSingleLine: true }], }, { - code: `(class {})`, + code: '(class {})', options: ['1tbs', { allowSingleLine: true }], }, { - code: `(class {})`, + code: '(class {})', options: ['allman', { allowSingleLine: true }], }, // https://github.com/eslint/eslint/issues/7908 { - code: `{}`, + code: '{}', }, { code: ` @@ -568,7 +573,7 @@ enum Foo { options: ['stroustrup'], }, { - code: `enum Foo { A, B }`, + code: 'enum Foo { A, B }', options: ['1tbs', { allowSingleLine: true }], }, ], @@ -591,8 +596,8 @@ if (f) { errors: [{ messageId: 'nextLineClose' }], }, { - code: `var foo = () => { return; }`, - output: `var foo = () => {\n return; \n}`, + code: 'var foo = () => { return; }', + output: 'var foo = () => {\n return; \n}', parserOptions: { ecmaVersion: 6 }, errors: [ { messageId: 'blockSameLine' }, @@ -600,31 +605,31 @@ if (f) { ], }, { - code: `function foo() { return; }`, - output: `function foo() {\n return; \n}`, + code: 'function foo() { return; }', + output: 'function foo() {\n return; \n}', errors: [ { messageId: 'blockSameLine' }, { messageId: 'singleLineClose' }, ], }, { - code: `function foo() \n { \n return; }`, - output: `function foo() { \n return; \n}`, + code: 'function foo() \n { \n return; }', + output: 'function foo() { \n return; \n}', errors: [{ messageId: 'nextLineOpen' }, { messageId: 'singleLineClose' }], }, { - code: `!function foo() \n { \n return; }`, - output: `!function foo() { \n return; \n}`, + code: '!function foo() \n { \n return; }', + output: '!function foo() { \n return; \n}', errors: [{ messageId: 'nextLineOpen' }, { messageId: 'singleLineClose' }], }, { - code: `if (foo) \n { \n bar(); }`, - output: `if (foo) { \n bar(); \n}`, + code: 'if (foo) \n { \n bar(); }', + output: 'if (foo) { \n bar(); \n}', errors: [{ messageId: 'nextLineOpen' }, { messageId: 'singleLineClose' }], }, { - code: `if (a) { \nb();\n } else \n { c(); }`, - output: `if (a) { \nb();\n } else {\n c(); \n}`, + code: 'if (a) { \nb();\n } else \n { c(); }', + output: 'if (a) { \nb();\n } else {\n c(); \n}', errors: [ { messageId: 'nextLineOpen' }, { messageId: 'blockSameLine' }, @@ -632,104 +637,108 @@ if (f) { ], }, { - code: `while (foo) \n { \n bar(); }`, - output: `while (foo) { \n bar(); \n}`, + code: 'while (foo) \n { \n bar(); }', + output: 'while (foo) { \n bar(); \n}', errors: [{ messageId: 'nextLineOpen' }, { messageId: 'singleLineClose' }], }, { - code: `for (;;) \n { \n bar(); }`, - output: `for (;;) { \n bar(); \n}`, + code: 'for (;;) \n { \n bar(); }', + output: 'for (;;) { \n bar(); \n}', errors: [{ messageId: 'nextLineOpen' }, { messageId: 'singleLineClose' }], }, { - code: `with (foo) \n { \n bar(); }`, - output: `with (foo) { \n bar(); \n}`, + code: 'with (foo) \n { \n bar(); }', + output: 'with (foo) { \n bar(); \n}', errors: [{ messageId: 'nextLineOpen' }, { messageId: 'singleLineClose' }], }, { - code: `switch (foo) \n { \n case 'bar': break; }`, - output: `switch (foo) { \n case 'bar': break; \n}`, + code: "switch (foo) \n { \n case 'bar': break; }", + output: "switch (foo) { \n case 'bar': break; \n}", errors: [{ messageId: 'nextLineOpen' }, { messageId: 'singleLineClose' }], }, { - code: `switch (foo) \n { }`, - output: `switch (foo) { }`, + code: 'switch (foo) \n { }', + output: 'switch (foo) { }', errors: [{ messageId: 'nextLineOpen' }], }, { - code: `try \n { \n bar(); \n } catch (e) {}`, - output: `try { \n bar(); \n } catch (e) {}`, + code: 'try \n { \n bar(); \n } catch (e) {}', + output: 'try { \n bar(); \n } catch (e) {}', errors: [{ messageId: 'nextLineOpen' }], }, { - code: `try { \n bar(); \n } catch (e) \n {}`, - output: `try { \n bar(); \n } catch (e) {}`, + code: 'try { \n bar(); \n } catch (e) \n {}', + output: 'try { \n bar(); \n } catch (e) {}', errors: [{ messageId: 'nextLineOpen' }], }, { - code: `do \n { \n bar(); \n} while (true)`, - output: `do { \n bar(); \n} while (true)`, + code: 'do \n { \n bar(); \n} while (true)', + output: 'do { \n bar(); \n} while (true)', errors: [{ messageId: 'nextLineOpen' }], }, { - code: `for (foo in bar) \n { \n baz(); \n }`, - output: `for (foo in bar) { \n baz(); \n }`, + code: 'for (foo in bar) \n { \n baz(); \n }', + output: 'for (foo in bar) { \n baz(); \n }', errors: [{ messageId: 'nextLineOpen' }], }, { - code: `for (foo of bar) \n { \n baz(); \n }`, - output: `for (foo of bar) { \n baz(); \n }`, + code: 'for (foo of bar) \n { \n baz(); \n }', + output: 'for (foo of bar) { \n baz(); \n }', parserOptions: { ecmaVersion: 6 }, errors: [{ messageId: 'nextLineOpen' }], }, { - code: `try { \n bar(); \n }\ncatch (e) {\n}`, - output: `try { \n bar(); \n } catch (e) {\n}`, + code: 'try { \n bar(); \n }\ncatch (e) {\n}', + output: 'try { \n bar(); \n } catch (e) {\n}', errors: [{ messageId: 'nextLineClose' }], }, { - code: `try { \n bar(); \n } catch (e) {\n}\n finally {\n}`, - output: `try { \n bar(); \n } catch (e) {\n} finally {\n}`, + code: 'try { \n bar(); \n } catch (e) {\n}\n finally {\n}', + output: 'try { \n bar(); \n } catch (e) {\n} finally {\n}', errors: [{ messageId: 'nextLineClose' }], }, { - code: `if (a) { \nb();\n } \n else { \nc();\n }`, - output: `if (a) { \nb();\n } else { \nc();\n }`, + code: 'if (a) { \nb();\n } \n else { \nc();\n }', + output: 'if (a) { \nb();\n } else { \nc();\n }', errors: [{ messageId: 'nextLineClose' }], }, { - code: `try { \n bar(); \n }\ncatch (e) {\n} finally {\n}`, - output: `try { \n bar(); \n }\ncatch (e) {\n}\n finally {\n}`, + code: 'try { \n bar(); \n }\ncatch (e) {\n} finally {\n}', + output: 'try { \n bar(); \n }\ncatch (e) {\n}\n finally {\n}', options: ['stroustrup'], errors: [{ messageId: 'sameLineClose' }], }, { - code: `try { \n bar(); \n } catch (e) {\n}\n finally {\n}`, - output: `try { \n bar(); \n }\n catch (e) {\n}\n finally {\n}`, + code: 'try { \n bar(); \n } catch (e) {\n}\n finally {\n}', + output: 'try { \n bar(); \n }\n catch (e) {\n}\n finally {\n}', options: ['stroustrup'], errors: [{ messageId: 'sameLineClose' }], }, { - code: `if (a) { \nb();\n } else { \nc();\n }`, - output: `if (a) { \nb();\n }\n else { \nc();\n }`, + code: 'if (a) { \nb();\n } else { \nc();\n }', + output: 'if (a) { \nb();\n }\n else { \nc();\n }', options: ['stroustrup'], errors: [{ messageId: 'sameLineClose' }], }, { - code: `if (foo) {\nbaz();\n} else if (bar) {\nbaz();\n}\nelse {\nqux();\n}`, - output: `if (foo) {\nbaz();\n}\n else if (bar) {\nbaz();\n}\nelse {\nqux();\n}`, + code: + 'if (foo) {\nbaz();\n} else if (bar) {\nbaz();\n}\nelse {\nqux();\n}', + output: + 'if (foo) {\nbaz();\n}\n else if (bar) {\nbaz();\n}\nelse {\nqux();\n}', options: ['stroustrup'], errors: [{ messageId: 'sameLineClose' }], }, { - code: `if (foo) {\npoop();\n} \nelse if (bar) {\nbaz();\n} else if (thing) {\nboom();\n}\nelse {\nqux();\n}`, - output: `if (foo) {\npoop();\n} \nelse if (bar) {\nbaz();\n}\n else if (thing) {\nboom();\n}\nelse {\nqux();\n}`, + code: + 'if (foo) {\npoop();\n} \nelse if (bar) {\nbaz();\n} else if (thing) {\nboom();\n}\nelse {\nqux();\n}', + output: + 'if (foo) {\npoop();\n} \nelse if (bar) {\nbaz();\n}\n else if (thing) {\nboom();\n}\nelse {\nqux();\n}', options: ['stroustrup'], errors: [{ messageId: 'sameLineClose' }], }, { - code: `try { \n bar(); \n }\n catch (e) {\n}\n finally {\n}`, - output: `try \n{ \n bar(); \n }\n catch (e) \n{\n}\n finally \n{\n}`, + code: 'try { \n bar(); \n }\n catch (e) {\n}\n finally {\n}', + output: 'try \n{ \n bar(); \n }\n catch (e) \n{\n}\n finally \n{\n}', options: ['allman'], errors: [ { messageId: 'sameLineOpen', line: 1 }, @@ -738,8 +747,8 @@ if (f) { ], }, { - code: `switch(x) { case 1: \nbar(); }\n `, - output: `switch(x) \n{\n case 1: \nbar(); \n}\n `, + code: 'switch(x) { case 1: \nbar(); }\n ', + output: 'switch(x) \n{\n case 1: \nbar(); \n}\n ', options: ['allman'], errors: [ { messageId: 'sameLineOpen', line: 1 }, @@ -748,8 +757,8 @@ if (f) { ], }, { - code: `if (a) { \nb();\n } else { \nc();\n }`, - output: `if (a) \n{ \nb();\n }\n else \n{ \nc();\n }`, + code: 'if (a) { \nb();\n } else { \nc();\n }', + output: 'if (a) \n{ \nb();\n }\n else \n{ \nc();\n }', options: ['allman'], errors: [ { messageId: 'sameLineOpen' }, @@ -758,8 +767,10 @@ if (f) { ], }, { - code: `if (foo) {\nbaz();\n} else if (bar) {\nbaz();\n}\nelse {\nqux();\n}`, - output: `if (foo) \n{\nbaz();\n}\n else if (bar) \n{\nbaz();\n}\nelse \n{\nqux();\n}`, + code: + 'if (foo) {\nbaz();\n} else if (bar) {\nbaz();\n}\nelse {\nqux();\n}', + output: + 'if (foo) \n{\nbaz();\n}\n else if (bar) \n{\nbaz();\n}\nelse \n{\nqux();\n}', options: ['allman'], errors: [ { messageId: 'sameLineOpen' }, @@ -769,8 +780,10 @@ if (f) { ], }, { - code: `if (foo)\n{ poop();\n} \nelse if (bar) {\nbaz();\n} else if (thing) {\nboom();\n}\nelse {\nqux();\n}`, - output: `if (foo)\n{\n poop();\n} \nelse if (bar) \n{\nbaz();\n}\n else if (thing) \n{\nboom();\n}\nelse \n{\nqux();\n}`, + code: + 'if (foo)\n{ poop();\n} \nelse if (bar) {\nbaz();\n} else if (thing) {\nboom();\n}\nelse {\nqux();\n}', + output: + 'if (foo)\n{\n poop();\n} \nelse if (bar) \n{\nbaz();\n}\n else if (thing) \n{\nboom();\n}\nelse \n{\nqux();\n}', options: ['allman'], errors: [ { messageId: 'blockSameLine' }, @@ -781,159 +794,162 @@ if (f) { ], }, { - code: `if (foo)\n{\n bar(); }`, - output: `if (foo)\n{\n bar(); \n}`, + code: 'if (foo)\n{\n bar(); }', + output: 'if (foo)\n{\n bar(); \n}', options: ['allman'], errors: [{ messageId: 'singleLineClose' }], }, { - code: `try\n{\n somethingRisky();\n} catch (e)\n{\n handleError()\n}`, - output: `try\n{\n somethingRisky();\n}\n catch (e)\n{\n handleError()\n}`, + code: 'try\n{\n somethingRisky();\n} catch (e)\n{\n handleError()\n}', + output: + 'try\n{\n somethingRisky();\n}\n catch (e)\n{\n handleError()\n}', options: ['allman'], errors: [{ messageId: 'sameLineClose' }], }, // allowSingleLine: true { - code: `function foo() { return; \n}`, - output: `function foo() {\n return; \n}`, + code: 'function foo() { return; \n}', + output: 'function foo() {\n return; \n}', options: ['1tbs', { allowSingleLine: true }], errors: [{ messageId: 'blockSameLine' }], }, { - code: `function foo() { a(); b(); return; \n}`, - output: `function foo() {\n a(); b(); return; \n}`, + code: 'function foo() { a(); b(); return; \n}', + output: 'function foo() {\n a(); b(); return; \n}', options: ['1tbs', { allowSingleLine: true }], errors: [{ messageId: 'blockSameLine' }], }, { - code: `function foo() { \n return; }`, - output: `function foo() { \n return; \n}`, + code: 'function foo() { \n return; }', + output: 'function foo() { \n return; \n}', options: ['1tbs', { allowSingleLine: true }], errors: [{ messageId: 'singleLineClose' }], }, { - code: `function foo() {\na();\nb();\nreturn; }`, - output: `function foo() {\na();\nb();\nreturn; \n}`, + code: 'function foo() {\na();\nb();\nreturn; }', + output: 'function foo() {\na();\nb();\nreturn; \n}', options: ['1tbs', { allowSingleLine: true }], errors: [{ messageId: 'singleLineClose' }], }, { - code: `!function foo() { \n return; }`, - output: `!function foo() { \n return; \n}`, + code: '!function foo() { \n return; }', + output: '!function foo() { \n return; \n}', options: ['1tbs', { allowSingleLine: true }], errors: [{ messageId: 'singleLineClose' }], }, { - code: `if (a) { b();\n } else { c(); }`, - output: `if (a) {\n b();\n } else { c(); }`, + code: 'if (a) { b();\n } else { c(); }', + output: 'if (a) {\n b();\n } else { c(); }', options: ['1tbs', { allowSingleLine: true }], errors: [{ messageId: 'blockSameLine' }], }, { - code: `if (a) { b(); }\nelse { c(); }`, - output: `if (a) { b(); } else { c(); }`, + code: 'if (a) { b(); }\nelse { c(); }', + output: 'if (a) { b(); } else { c(); }', options: ['1tbs', { allowSingleLine: true }], errors: [{ messageId: 'nextLineClose' }], }, { - code: `while (foo) { \n bar(); }`, - output: `while (foo) { \n bar(); \n}`, + code: 'while (foo) { \n bar(); }', + output: 'while (foo) { \n bar(); \n}', options: ['1tbs', { allowSingleLine: true }], errors: [{ messageId: 'singleLineClose' }], }, { - code: `for (;;) { bar(); \n }`, - output: `for (;;) {\n bar(); \n }`, + code: 'for (;;) { bar(); \n }', + output: 'for (;;) {\n bar(); \n }', options: ['1tbs', { allowSingleLine: true }], errors: [{ messageId: 'blockSameLine' }], }, { - code: `with (foo) { bar(); \n }`, - output: `with (foo) {\n bar(); \n }`, + code: 'with (foo) { bar(); \n }', + output: 'with (foo) {\n bar(); \n }', options: ['1tbs', { allowSingleLine: true }], errors: [{ messageId: 'blockSameLine' }], }, { - code: `switch (foo) \n { \n case \`bar\`: break; }`, - output: `switch (foo) { \n case \`bar\`: break; \n}`, + code: 'switch (foo) \n { \n case `bar`: break; }', + output: 'switch (foo) { \n case `bar`: break; \n}', options: ['1tbs', { allowSingleLine: true }], errors: [{ messageId: 'nextLineOpen' }, { messageId: 'singleLineClose' }], }, { - code: `switch (foo) \n { }`, - output: `switch (foo) { }`, + code: 'switch (foo) \n { }', + output: 'switch (foo) { }', options: ['1tbs', { allowSingleLine: true }], errors: [{ messageId: 'nextLineOpen' }], }, { - code: `try { bar(); }\ncatch (e) { baz(); }`, - output: `try { bar(); } catch (e) { baz(); }`, + code: 'try { bar(); }\ncatch (e) { baz(); }', + output: 'try { bar(); } catch (e) { baz(); }', options: ['1tbs', { allowSingleLine: true }], errors: [{ messageId: 'nextLineClose' }], }, { - code: `try \n { \n bar(); \n } catch (e) {}`, - output: `try { \n bar(); \n } catch (e) {}`, + code: 'try \n { \n bar(); \n } catch (e) {}', + output: 'try { \n bar(); \n } catch (e) {}', options: ['1tbs', { allowSingleLine: true }], errors: [{ messageId: 'nextLineOpen' }], }, { - code: `try { \n bar(); \n } catch (e) \n {}`, - output: `try { \n bar(); \n } catch (e) {}`, + code: 'try { \n bar(); \n } catch (e) \n {}', + output: 'try { \n bar(); \n } catch (e) {}', options: ['1tbs', { allowSingleLine: true }], errors: [{ messageId: 'nextLineOpen' }], }, { - code: `do \n { \n bar(); \n} while (true)`, - output: `do { \n bar(); \n} while (true)`, + code: 'do \n { \n bar(); \n} while (true)', + output: 'do { \n bar(); \n} while (true)', options: ['1tbs', { allowSingleLine: true }], errors: [{ messageId: 'nextLineOpen' }], }, { - code: `for (foo in bar) \n { \n baz(); \n }`, - output: `for (foo in bar) { \n baz(); \n }`, + code: 'for (foo in bar) \n { \n baz(); \n }', + output: 'for (foo in bar) { \n baz(); \n }', options: ['1tbs', { allowSingleLine: true }], errors: [{ messageId: 'nextLineOpen' }], }, { - code: `try { \n bar(); \n }\ncatch (e) {\n}`, - output: `try { \n bar(); \n } catch (e) {\n}`, + code: 'try { \n bar(); \n }\ncatch (e) {\n}', + output: 'try { \n bar(); \n } catch (e) {\n}', options: ['1tbs', { allowSingleLine: true }], errors: [{ messageId: 'nextLineClose' }], }, { - code: `try { \n bar(); \n } catch (e) {\n}\n finally {\n}`, - output: `try { \n bar(); \n } catch (e) {\n} finally {\n}`, + code: 'try { \n bar(); \n } catch (e) {\n}\n finally {\n}', + output: 'try { \n bar(); \n } catch (e) {\n} finally {\n}', options: ['1tbs', { allowSingleLine: true }], errors: [{ messageId: 'nextLineClose' }], }, { - code: `if (a) { \nb();\n } \n else { \nc();\n }`, - output: `if (a) { \nb();\n } else { \nc();\n }`, + code: 'if (a) { \nb();\n } \n else { \nc();\n }', + output: 'if (a) { \nb();\n } else { \nc();\n }', options: ['1tbs', { allowSingleLine: true }], errors: [{ messageId: 'nextLineClose' }], }, { - code: `try { \n bar(); \n }\ncatch (e) {\n} finally {\n}`, - output: `try { \n bar(); \n }\ncatch (e) {\n}\n finally {\n}`, + code: 'try { \n bar(); \n }\ncatch (e) {\n} finally {\n}', + output: 'try { \n bar(); \n }\ncatch (e) {\n}\n finally {\n}', options: ['stroustrup', { allowSingleLine: true }], errors: [{ messageId: 'sameLineClose' }], }, { - code: `try { \n bar(); \n } catch (e) {\n}\n finally {\n}`, - output: `try { \n bar(); \n }\n catch (e) {\n}\n finally {\n}`, + code: 'try { \n bar(); \n } catch (e) {\n}\n finally {\n}', + output: 'try { \n bar(); \n }\n catch (e) {\n}\n finally {\n}', options: ['stroustrup', { allowSingleLine: true }], errors: [{ messageId: 'sameLineClose' }], }, { - code: `if (a) { \nb();\n } else { \nc();\n }`, - output: `if (a) { \nb();\n }\n else { \nc();\n }`, + code: 'if (a) { \nb();\n } else { \nc();\n }', + output: 'if (a) { \nb();\n }\n else { \nc();\n }', options: ['stroustrup', { allowSingleLine: true }], errors: [{ messageId: 'sameLineClose' }], }, { - code: `if (foo)\n{ poop();\n} \nelse if (bar) {\nbaz();\n} else if (thing) {\nboom();\n}\nelse {\nqux();\n}`, - output: `if (foo)\n{\n poop();\n} \nelse if (bar) \n{\nbaz();\n}\n else if (thing) \n{\nboom();\n}\nelse \n{\nqux();\n}`, + code: + 'if (foo)\n{ poop();\n} \nelse if (bar) {\nbaz();\n} else if (thing) {\nboom();\n}\nelse {\nqux();\n}', + output: + 'if (foo)\n{\n poop();\n} \nelse if (bar) \n{\nbaz();\n}\n else if (thing) \n{\nboom();\n}\nelse \n{\nqux();\n}', options: ['allman', { allowSingleLine: true }], errors: [ { messageId: 'blockSameLine' }, @@ -945,25 +961,25 @@ if (f) { }, // Comment interferes with fix { - code: `if (foo) // comment \n{\nbar();\n}`, + code: 'if (foo) // comment \n{\nbar();\n}', output: null, errors: [{ messageId: 'nextLineOpen' }], }, // https://github.com/eslint/eslint/issues/7493 { - code: `if (foo) {\n bar\n.baz }`, - output: `if (foo) {\n bar\n.baz \n}`, + code: 'if (foo) {\n bar\n.baz }', + output: 'if (foo) {\n bar\n.baz \n}', errors: [{ messageId: 'singleLineClose' }], }, { - code: `if (foo)\n{\n bar\n.baz }`, - output: `if (foo)\n{\n bar\n.baz \n}`, + code: 'if (foo)\n{\n bar\n.baz }', + output: 'if (foo)\n{\n bar\n.baz \n}', options: ['allman'], errors: [{ messageId: 'singleLineClose' }], }, { - code: `if (foo) { bar\n.baz }`, - output: `if (foo) {\n bar\n.baz \n}`, + code: 'if (foo) { bar\n.baz }', + output: 'if (foo) {\n bar\n.baz \n}', options: ['1tbs', { allowSingleLine: true }], errors: [ { messageId: 'blockSameLine' }, @@ -971,8 +987,8 @@ if (f) { ], }, { - code: `if (foo) { bar\n.baz }`, - output: `if (foo) \n{\n bar\n.baz \n}`, + code: 'if (foo) { bar\n.baz }', + output: 'if (foo) \n{\n bar\n.baz \n}', options: ['allman', { allowSingleLine: true }], errors: [ { messageId: 'sameLineOpen' }, @@ -981,46 +997,46 @@ if (f) { ], }, { - code: `switch (x) {\n case 1: foo() }`, - output: `switch (x) {\n case 1: foo() \n}`, + code: 'switch (x) {\n case 1: foo() }', + output: 'switch (x) {\n case 1: foo() \n}', options: ['1tbs', { allowSingleLine: true }], errors: [{ messageId: 'singleLineClose' }], }, { - code: `class Foo\n{\n}`, - output: `class Foo {\n}`, + code: 'class Foo\n{\n}', + output: 'class Foo {\n}', errors: [{ messageId: 'nextLineOpen' }], }, { - code: `(class\n{\n})`, - output: `(class {\n})`, + code: '(class\n{\n})', + output: '(class {\n})', errors: [{ messageId: 'nextLineOpen' }], }, { - code: `class Foo{\n}`, - output: `class Foo\n{\n}`, + code: 'class Foo{\n}', + output: 'class Foo\n{\n}', options: ['allman'], errors: [{ messageId: 'sameLineOpen' }], }, { - code: `(class {\n})`, - output: `(class \n{\n})`, + code: '(class {\n})', + output: '(class \n{\n})', options: ['allman'], errors: [{ messageId: 'sameLineOpen' }], }, { - code: `class Foo {\nbar() {\n}}`, - output: `class Foo {\nbar() {\n}\n}`, + code: 'class Foo {\nbar() {\n}}', + output: 'class Foo {\nbar() {\n}\n}', errors: [{ messageId: 'singleLineClose' }], }, { - code: `(class Foo {\nbar() {\n}})`, - output: `(class Foo {\nbar() {\n}\n})`, + code: '(class Foo {\nbar() {\n}})', + output: '(class Foo {\nbar() {\n}\n})', errors: [{ messageId: 'singleLineClose' }], }, { - code: `class\nFoo{}`, - output: `class\nFoo\n{}`, + code: 'class\nFoo{}', + output: 'class\nFoo\n{}', options: ['allman'], errors: [{ messageId: 'sameLineOpen' }], }, @@ -1070,8 +1086,8 @@ interface Foo { errors: [{ messageId: 'nextLineOpen' }], }, { - code: `interface Foo { \n }`, - output: `interface Foo \n{ \n }`, + code: 'interface Foo { \n }', + output: 'interface Foo \n{ \n }', options: ['allman'], errors: [{ messageId: 'sameLineOpen' }], }, @@ -1101,8 +1117,8 @@ module "Foo" { errors: [{ messageId: 'nextLineOpen' }], }, { - code: `module "Foo" { \n }`, - output: `module "Foo" \n{ \n }`, + code: 'module "Foo" { \n }', + output: 'module "Foo" \n{ \n }', options: ['allman'], errors: [{ messageId: 'sameLineOpen' }], }, @@ -1132,8 +1148,8 @@ namespace Foo { errors: [{ messageId: 'nextLineOpen' }], }, { - code: `namespace Foo { \n }`, - output: `namespace Foo \n{ \n }`, + code: 'namespace Foo { \n }', + output: 'namespace Foo \n{ \n }', options: ['allman'], errors: [{ messageId: 'sameLineOpen' }], }, @@ -1163,8 +1179,8 @@ enum Foo { errors: [{ messageId: 'nextLineOpen' }], }, { - code: `enum Foo { A }`, - output: `enum Foo \n{\n A \n}`, + code: 'enum Foo { A }', + output: 'enum Foo \n{\n A \n}', options: ['allman'], errors: [ { messageId: 'sameLineOpen' }, diff --git a/packages/eslint-plugin/tests/rules/camelcase.test.ts b/packages/eslint-plugin/tests/rules/camelcase.test.ts index 617cdf3e40f..1a35ee87cc8 100644 --- a/packages/eslint-plugin/tests/rules/camelcase.test.ts +++ b/packages/eslint-plugin/tests/rules/camelcase.test.ts @@ -1,5 +1,5 @@ import rule from '../../src/rules/camelcase'; -import { RuleTester } from '../RuleTester'; +import { RuleTester, noFormat } from '../RuleTester'; const ruleTester = new RuleTester({ parser: '@typescript-eslint/parser', @@ -8,75 +8,147 @@ const ruleTester = new RuleTester({ ruleTester.run('camelcase', rule, { valid: [ { - code: 'interface Foo { b_ar: number }', + code: ` +interface Foo { + b_ar: number; +} + `, options: [{ properties: 'never' }], }, { - code: 'interface Foo { bar: number }', + code: ` +interface Foo { + bar: number; +} + `, options: [{ properties: 'always' }], }, { - code: 'class Foo { b_ar: number; }', + code: ` +class Foo { + b_ar: number; +} + `, options: [{ properties: 'never' }], }, { - code: 'class Foo { bar: number; }', + code: ` +class Foo { + bar: number; +} + `, options: [{ properties: 'always' }], }, { - code: 'class Foo { b_ar: number = 0; }', + code: ` +class Foo { + b_ar: number = 0; +} + `, options: [{ properties: 'never' }], }, { - code: 'class Foo { bar: number = 0; }', + code: ` +class Foo { + bar: number = 0; +} + `, options: [{ properties: 'always' }], }, { - code: 'class Foo { constructor(private b_ar: number) {} }', + code: ` +class Foo { + constructor(private b_ar: number) {} +} + `, options: [{ properties: 'never' }], }, { - code: 'class Foo { constructor(private bar: number) {} }', + code: ` +class Foo { + constructor(private bar: number) {} +} + `, options: [{ properties: 'always' }], }, { - code: 'class Foo { constructor(private b_ar: number = 0) {} }', + code: ` +class Foo { + constructor(private b_ar: number = 0) {} +} + `, options: [{ properties: 'never' }], }, { - code: 'class Foo { constructor(private bar: number = 0) {} }', + code: ` +class Foo { + constructor(private bar: number = 0) {} +} + `, options: [{ properties: 'always' }], }, { - code: 'abstract class Foo { b_ar: number; }', + code: ` +abstract class Foo { + b_ar: number; +} + `, options: [{ properties: 'never' }], }, { - code: 'abstract class Foo { bar: number; }', + code: ` +abstract class Foo { + bar: number; +} + `, options: [{ properties: 'always' }], }, { - code: 'abstract class Foo { b_ar: number = 0; }', + code: ` +abstract class Foo { + b_ar: number = 0; +} + `, options: [{ properties: 'never' }], }, { - code: 'abstract class Foo { bar: number = 0; }', + code: ` +abstract class Foo { + bar: number = 0; +} + `, options: [{ properties: 'always' }], }, { - code: 'abstract class Foo { abstract b_ar: number; }', + code: ` +abstract class Foo { + abstract b_ar: number; +} + `, options: [{ properties: 'never' }], }, { - code: 'abstract class Foo { abstract bar: number; }', + code: ` +abstract class Foo { + abstract bar: number; +} + `, options: [{ properties: 'always' }], }, { - code: 'abstract class Foo { abstract b_ar: number = 0; }', + code: ` +abstract class Foo { + abstract b_ar: number = 0; +} + `, options: [{ properties: 'never' }], }, { - code: 'abstract class Foo { abstract bar: number = 0; }', + code: ` +abstract class Foo { + abstract bar: number = 0; +} + `, options: [{ properties: 'always' }], }, { @@ -141,19 +213,19 @@ class Foo { }, { code: ` -type Foo = {} +type Foo = {}; `, options: [{ genericType: 'always' }], }, { code: ` -type Foo = {} +type Foo = {}; `, options: [{ genericType: 'always' }], }, { code: ` -type Foo = {} +type Foo = {}; `, options: [{ genericType: 'never' }], }, @@ -183,7 +255,7 @@ class Foo { code: 'const foo = foo.bar?.foo_bar_baz;', }, { - code: 'const foo = (foo?.bar?.baz)?.foo_bar_baz;', + code: noFormat`const foo = (foo?.bar?.baz)?.foo_bar_baz;`, }, { code: 'const foo = foo_bar?.foo;', @@ -193,7 +265,11 @@ class Foo { invalid: [ { - code: 'interface Foo { b_ar: number }', + code: ` +interface Foo { + b_ar: number; +} + `, options: [{ properties: 'always' }], errors: [ { @@ -201,13 +277,17 @@ class Foo { data: { name: 'b_ar', }, - line: 1, - column: 17, + line: 3, + column: 3, }, ], }, { - code: 'class Foo { b_ar: number; }', + code: ` +class Foo { + b_ar: number; +} + `, options: [{ properties: 'always' }], errors: [ { @@ -215,13 +295,17 @@ class Foo { data: { name: 'b_ar', }, - line: 1, - column: 13, + line: 3, + column: 3, }, ], }, { - code: 'class Foo { constructor(private b_ar: number) {} }', + code: ` +class Foo { + constructor(private b_ar: number) {} +} + `, options: [{ properties: 'always' }], errors: [ { @@ -229,13 +313,17 @@ class Foo { data: { name: 'b_ar', }, - line: 1, - column: 33, + line: 3, + column: 23, }, ], }, { - code: 'class Foo { constructor(private b_ar: number = 0) {} }', + code: ` +class Foo { + constructor(private b_ar: number = 0) {} +} + `, options: [{ properties: 'always' }], errors: [ { @@ -243,13 +331,17 @@ class Foo { data: { name: 'b_ar', }, - line: 1, - column: 33, + line: 3, + column: 23, }, ], }, { - code: 'abstract class Foo { b_ar: number; }', + code: ` +abstract class Foo { + b_ar: number; +} + `, options: [{ properties: 'always' }], errors: [ { @@ -257,13 +349,17 @@ class Foo { data: { name: 'b_ar', }, - line: 1, - column: 22, + line: 3, + column: 3, }, ], }, { - code: 'abstract class Foo { b_ar: number = 0; }', + code: ` +abstract class Foo { + b_ar: number = 0; +} + `, options: [{ properties: 'always' }], errors: [ { @@ -271,13 +367,17 @@ class Foo { data: { name: 'b_ar', }, - line: 1, - column: 22, + line: 3, + column: 3, }, ], }, { - code: 'abstract class Foo { abstract b_ar: number; }', + code: ` +abstract class Foo { + abstract b_ar: number; +} + `, options: [{ properties: 'always' }], errors: [ { @@ -285,13 +385,17 @@ class Foo { data: { name: 'b_ar', }, - line: 1, - column: 31, + line: 3, + column: 12, }, ], }, { - code: 'abstract class Foo { abstract b_ar: number = 0; }', + code: ` +abstract class Foo { + abstract b_ar: number = 0; +} + `, options: [{ properties: 'always' }], errors: [ { @@ -299,8 +403,8 @@ class Foo { data: { name: 'b_ar', }, - line: 1, - column: 31, + line: 3, + column: 12, }, ], }, @@ -319,7 +423,7 @@ class Foo { ], }, { - code: 'const foo = (foo_test?.bar)?.baz;', + code: noFormat`const foo = (foo_test?.bar)?.baz;`, options: [{ properties: 'always' }], errors: [ { diff --git a/packages/eslint-plugin/tests/rules/class-literal-property-style.test.ts b/packages/eslint-plugin/tests/rules/class-literal-property-style.test.ts index f0c5f2afc05..19d5fb183b5 100644 --- a/packages/eslint-plugin/tests/rules/class-literal-property-style.test.ts +++ b/packages/eslint-plugin/tests/rules/class-literal-property-style.test.ts @@ -1,5 +1,5 @@ import rule from '../../src/rules/class-literal-property-style'; -import { RuleTester } from '../RuleTester'; +import { RuleTester, noFormat } from '../RuleTester'; const ruleTester = new RuleTester({ parser: '@typescript-eslint/parser', @@ -7,18 +7,50 @@ const ruleTester = new RuleTester({ ruleTester.run('class-literal-property-style', rule, { valid: [ - 'class Mx { declare readonly p1 = 1; }', - 'class Mx { readonly p1 = "hello world"; }', - 'class Mx { p1 = "hello world"; }', - 'class Mx { static p1 = "hello world"; }', - 'class Mx { p1: string; }', - 'class Mx { get p1(); }', - 'class Mx { get p1() {} }', - 'abstract class Mx { abstract get p1(): string }', + ` +class Mx { + declare readonly p1 = 1; +} + `, + ` +class Mx { + readonly p1 = 'hello world'; +} + `, + ` +class Mx { + p1 = 'hello world'; +} + `, + ` +class Mx { + static p1 = 'hello world'; +} + `, + ` +class Mx { + p1: string; +} + `, + ` +class Mx { + get p1(); +} + `, + ` +class Mx { + get p1() {} +} + `, + ` +abstract class Mx { + abstract get p1(): string; +} + `, ` class Mx { get mySetting() { - if(this._aValue) { + if (this._aValue) { return 'on'; } @@ -29,14 +61,14 @@ ruleTester.run('class-literal-property-style', rule, { ` class Mx { get mySetting() { - return \`build-\${process.env.build}\` + return \`build-\${process.env.build}\`; } } `, ` class Mx { getMySetting() { - if(this._aValue) { + if (this._aValue) { return 'on'; } @@ -64,31 +96,63 @@ ruleTester.run('class-literal-property-style', rule, { options: ['fields'], }, { - code: 'class Mx { declare public readonly foo = 1; }', + code: ` +class Mx { + public declare readonly foo = 1; +} + `, options: ['getters'], }, { - code: 'class Mx { get p1() { return "hello world"; } }', + code: ` +class Mx { + get p1() { + return 'hello world'; + } +} + `, options: ['getters'], }, { - code: 'class Mx { p1 = "hello world"; }', + code: ` +class Mx { + p1 = 'hello world'; +} + `, options: ['getters'], }, { - code: 'class Mx { p1: string; }', + code: ` +class Mx { + p1: string; +} + `, options: ['getters'], }, { - code: 'class Mx { readonly p1 = [1, 2, 3]; }', + code: ` +class Mx { + readonly p1 = [1, 2, 3]; +} + `, options: ['getters'], }, { - code: 'class Mx { static p1: string; }', + code: ` +class Mx { + static p1: string; +} + `, options: ['getters'], }, { - code: 'class Mx { static get p1() { return "hello world"; } }', + code: ` +class Mx { + static get p1() { + return 'hello world'; + } +} + `, options: ['getters'], }, { @@ -116,259 +180,358 @@ ruleTester.run('class-literal-property-style', rule, { ], invalid: [ { - code: 'class Mx { get p1() { return "hello world"; } }', - output: 'class Mx { readonly p1 = "hello world"; }', + code: ` +class Mx { + get p1() { + return 'hello world'; + } +} + `, + output: ` +class Mx { + readonly p1 = 'hello world'; +} + `, errors: [ { messageId: 'preferFieldStyle', - column: 16, - line: 1, + column: 7, + line: 3, }, ], }, { - code: 'class Mx { get p1() { return `hello world`; } }', - output: 'class Mx { readonly p1 = `hello world`; }', + code: ` +class Mx { + get p1() { + return \`hello world\`; + } +} + `, + output: ` +class Mx { + readonly p1 = \`hello world\`; +} + `, errors: [ { messageId: 'preferFieldStyle', - column: 16, - line: 1, + column: 7, + line: 3, }, ], }, { - code: 'class Mx { static get p1() { return "hello world"; } }', - output: 'class Mx { static readonly p1 = "hello world"; }', + code: ` +class Mx { + static get p1() { + return 'hello world'; + } +} + `, + output: ` +class Mx { + static readonly p1 = 'hello world'; +} + `, errors: [ { messageId: 'preferFieldStyle', - column: 23, - line: 1, + column: 14, + line: 3, }, ], }, { - code: - 'class Mx { public static readonly static private public protected get foo() { return 1; } }', - output: 'class Mx { public static readonly foo = 1; }', + code: ` +class Mx { + public static get foo() { + return 1; + } +} + `, + output: ` +class Mx { + public static readonly foo = 1; +} + `, errors: [ { messageId: 'preferFieldStyle', - column: 71, - line: 1, + column: 21, + line: 3, }, ], }, { code: ` - class Mx { - public get [myValue]() { - return 'a literal value'; - } - } +class Mx { + public get [myValue]() { + return 'a literal value'; + } +} `, output: ` - class Mx { - public readonly [myValue] = 'a literal value'; - } +class Mx { + public readonly [myValue] = 'a literal value'; +} `, errors: [ { messageId: 'preferFieldStyle', - column: 23, + column: 15, line: 3, }, ], }, { code: ` - class Mx { - public get [myValue]() { - return 12345n; - } - } +class Mx { + public get [myValue]() { + return 12345n; + } +} `, output: ` - class Mx { - public readonly [myValue] = 12345n; - } +class Mx { + public readonly [myValue] = 12345n; +} `, errors: [ { messageId: 'preferFieldStyle', - column: 23, + column: 15, line: 3, }, ], }, { code: ` - class Mx { - public readonly [myValue] = 'a literal value'; - } +class Mx { + public readonly [myValue] = 'a literal value'; +} `, - output: ` - class Mx { - public get [myValue]() { return 'a literal value'; } - } + output: noFormat` +class Mx { + public get [myValue]() { return 'a literal value'; } +} `, errors: [ { messageId: 'preferGetterStyle', - column: 28, + column: 20, line: 3, }, ], options: ['getters'], }, { - code: 'class Mx { readonly p1 = "hello world"; }', - output: 'class Mx { get p1() { return "hello world"; } }', + code: ` +class Mx { + readonly p1 = 'hello world'; +} + `, + output: noFormat` +class Mx { + get p1() { return 'hello world'; } +} + `, errors: [ { messageId: 'preferGetterStyle', - column: 21, - line: 1, + column: 12, + line: 3, }, ], options: ['getters'], }, { - code: 'class Mx { readonly p1 = `hello world`; }', - output: 'class Mx { get p1() { return `hello world`; } }', + code: ` +class Mx { + readonly p1 = \`hello world\`; +} + `, + output: noFormat` +class Mx { + get p1() { return \`hello world\`; } +} + `, errors: [ { messageId: 'preferGetterStyle', - column: 21, - line: 1, + column: 12, + line: 3, }, ], options: ['getters'], }, { - code: 'class Mx { static readonly p1 = "hello world"; }', - output: 'class Mx { static get p1() { return "hello world"; } }', + code: ` +class Mx { + static readonly p1 = 'hello world'; +} + `, + output: noFormat` +class Mx { + static get p1() { return 'hello world'; } +} + `, errors: [ { messageId: 'preferGetterStyle', - column: 28, - line: 1, + column: 19, + line: 3, }, ], options: ['getters'], }, { - code: 'class Mx { protected get p1() { return "hello world"; } }', - output: 'class Mx { protected readonly p1 = "hello world"; }', + code: ` +class Mx { + protected get p1() { + return 'hello world'; + } +} + `, + output: ` +class Mx { + protected readonly p1 = 'hello world'; +} + `, errors: [ { messageId: 'preferFieldStyle', - column: 26, - line: 1, + column: 17, + line: 3, }, ], options: ['fields'], }, { - code: 'class Mx { protected readonly p1 = "hello world"; }', - output: 'class Mx { protected get p1() { return "hello world"; } }', + code: ` +class Mx { + protected readonly p1 = 'hello world'; +} + `, + output: noFormat` +class Mx { + protected get p1() { return 'hello world'; } +} + `, errors: [ { messageId: 'preferGetterStyle', - column: 31, - line: 1, + column: 22, + line: 3, }, ], options: ['getters'], }, { - code: 'class Mx { public static get p1() { return "hello world"; } }', - output: 'class Mx { public static readonly p1 = "hello world"; }', + code: ` +class Mx { + public static get p1() { + return 'hello world'; + } +} + `, + output: ` +class Mx { + public static readonly p1 = 'hello world'; +} + `, errors: [ { messageId: 'preferFieldStyle', - column: 30, - line: 1, + column: 21, + line: 3, }, ], }, { - code: 'class Mx { public static readonly p1 = "hello world"; }', - output: 'class Mx { public static get p1() { return "hello world"; } }', + code: ` +class Mx { + public static readonly p1 = 'hello world'; +} + `, + output: noFormat` +class Mx { + public static get p1() { return 'hello world'; } +} + `, errors: [ { messageId: 'preferGetterStyle', - column: 35, - line: 1, + column: 26, + line: 3, }, ], options: ['getters'], }, { code: ` - class Mx { - public get myValue() { - return gql\` - { - user(id: 5) { - firstName - lastName - } - } - \`; - } +class Mx { + public get myValue() { + return gql\` + { + user(id: 5) { + firstName + lastName } + } + \`; + } +} `, - output: ` - class Mx { - public readonly myValue = gql\` - { - user(id: 5) { - firstName - lastName - } - } - \`; + output: noFormat` +class Mx { + public readonly myValue = gql\` + { + user(id: 5) { + firstName + lastName } + } + \`; +} `, errors: [ { messageId: 'preferFieldStyle', - column: 22, + column: 14, line: 3, }, ], }, { code: ` - class Mx { - public readonly myValue = gql\` - { - user(id: 5) { - firstName - lastName - } - } - \`; - } +class Mx { + public readonly myValue = gql\` + { + user(id: 5) { + firstName + lastName + } + } + \`; +} `, - output: ` - class Mx { - public get myValue() { return gql\` - { - user(id: 5) { - firstName - lastName - } - } - \`; } - } + output: noFormat` +class Mx { + public get myValue() { return gql\` + { + user(id: 5) { + firstName + lastName + } + } + \`; } +} `, errors: [ { messageId: 'preferGetterStyle', - column: 27, + column: 19, line: 3, }, ], 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 11f6dc8b888..1977a36415d 100644 --- a/packages/eslint-plugin/tests/rules/class-name-casing.test.ts +++ b/packages/eslint-plugin/tests/rules/class-name-casing.test.ts @@ -35,12 +35,19 @@ ruleTester.run('class-name-casing', rule, { 'class ÈClassNameWithUnicode {}', 'class ClassNameWithæUnicode {}', // Following test cases are valid, but no one is going to write code like this - 'var { bar } = class { static bar() { return 2 } }', - `var [ bar ] = class { - static [Symbol.iterator]() { - return { next: () => ({ value: 1, done: false}) } - } - } + ` +var { bar } = class { + static bar() { + return 2; + } +}; + `, + ` +var [bar] = class { + static [Symbol.iterator]() { + return { next: () => ({ value: 1, done: false }) }; + } +}; `, ], @@ -116,7 +123,7 @@ ruleTester.run('class-name-casing', rule, { ], }, { - code: 'var bar = class invalidName {}', + code: 'var bar = class invalidName {};', errors: [ { messageId: 'notPascalCased', diff --git a/packages/eslint-plugin/tests/rules/comma-spacing.test.ts b/packages/eslint-plugin/tests/rules/comma-spacing.test.ts index 12a6e0219f8..7147815b759 100644 --- a/packages/eslint-plugin/tests/rules/comma-spacing.test.ts +++ b/packages/eslint-plugin/tests/rules/comma-spacing.test.ts @@ -1,3 +1,8 @@ +/* eslint-disable eslint-comments/no-use */ +// this rule tests the spacing, which prettier will want to fix and break the tests +/* eslint "@typescript-eslint/internal/plugin-test-formatting": ["error", { formatWithPrettier: false }] */ +/* eslint-enable eslint-comments/no-use */ + import rule from '../../src/rules/comma-spacing'; import { RuleTester } from '../RuleTester'; @@ -7,280 +12,280 @@ const ruleTester = new RuleTester({ ruleTester.run('comma-spacing', rule, { valid: [ - `foo(1, true/* comment */, 'text');`, - `foo(1, true /* comment */, 'text');`, - `foo(1, true/* comment *//* comment */, 'text');`, - `foo(1, true/* comment */ /* comment */, 'text');`, - `foo(1, true, /* comment */ 'text');`, - `foo(1, // comment\n true, /* comment */ 'text');`, - { - code: `foo(1, // comment\n true,/* comment */ 'text');`, + "foo(1, true/* comment */, 'text');", + "foo(1, true /* comment */, 'text');", + "foo(1, true/* comment *//* comment */, 'text');", + "foo(1, true/* comment */ /* comment */, 'text');", + "foo(1, true, /* comment */ 'text');", + "foo(1, // comment\n true, /* comment */ 'text');", + { + code: "foo(1, // comment\n true,/* comment */ 'text');", options: [{ before: false, after: false }], }, - `const a = 1, b = 2;`, - `const foo = [, ];`, - `const foo = [1, ];`, - `const foo = [, 2];`, - `const foo = [1, 2];`, - `const foo = [, , ];`, - `const foo = [1, , ];`, - `const foo = [, 2, ];`, - `const foo = [, , 3];`, - `const foo = [1, 2, ];`, - `const foo = [, 2, 3];`, - `const foo = [1, , 3];`, - `const foo = [1, 2, 3];`, - `const foo = {'foo':'foo', 'baz':'baz'};`, - `const foo = {'foo':'foo', 'baz':\n'baz'};`, - `const foo = {'foo':\n'foo', 'baz':\n'baz'};`, - `function foo(a, b){}`, - `function foo(a, b = 1){}`, - `function foo(a = 1, b, c){}`, - `const foo = (a, b) => {}`, - `const foo = (a=1, b) => {}`, - `const foo = a => a + 2`, - `a, b`, - `const a = (1 + 2, 2)`, - `a(b, c)`, - `new A(b, c)`, - `foo((a), b)`, - `const b = ((1 + 2), 2)`, - `parseInt((a + b), 10)`, - `go.boom((a + b), 10)`, - `go.boom((a + b), 10, (4))`, - `const x = [ (a + c), (b + b) ]`, - `[' , ']`, + 'const a = 1, b = 2;', + 'const foo = [, ];', + 'const foo = [1, ];', + 'const foo = [, 2];', + 'const foo = [1, 2];', + 'const foo = [, , ];', + 'const foo = [1, , ];', + 'const foo = [, 2, ];', + 'const foo = [, , 3];', + 'const foo = [1, 2, ];', + 'const foo = [, 2, 3];', + 'const foo = [1, , 3];', + 'const foo = [1, 2, 3];', + "const foo = {'foo':'foo', 'baz':'baz'};", + "const foo = {'foo':'foo', 'baz':\n'baz'};", + "const foo = {'foo':\n'foo', 'baz':\n'baz'};", + 'function foo(a, b){}', + 'function foo(a, b = 1){}', + 'function foo(a = 1, b, c){}', + 'const foo = (a, b) => {}', + 'const foo = (a=1, b) => {}', + 'const foo = a => a + 2', + 'a, b', + 'const a = (1 + 2, 2)', + 'a(b, c)', + 'new A(b, c)', + 'foo((a), b)', + 'const b = ((1 + 2), 2)', + 'parseInt((a + b), 10)', + 'go.boom((a + b), 10)', + 'go.boom((a + b), 10, (4))', + 'const x = [ (a + c), (b + b) ]', + "[' , ']", '[` , `]', '`${[1, 2]}`', - `fn(a, b,)`, - `const fn = (a, b,) => {}`, - `const fn = function (a, b,) {}`, - `foo(/,/, 'a')`, - `const x = ',,,,,';`, - `const code = 'var foo = 1, bar = 3;'`, - `['apples', \n 'oranges'];`, - `{x: 'var x,y,z'}`, - { - code: `const foo = {'foo':\n'bar' ,'baz':\n'qur'};`, + 'fn(a, b,)', + 'const fn = (a, b,) => {}', + 'const fn = function (a, b,) {}', + "foo(/,/, 'a')", + "const x = ',,,,,';", + "const code = 'var foo = 1, bar = 3;'", + "['apples', \n 'oranges'];", + "{x: 'var x,y,z'}", + { + code: "const foo = {'foo':\n'bar' ,'baz':\n'qur'};", options: [{ before: true, after: false }], }, { - code: `const a = 1 ,b = 2;`, + code: 'const a = 1 ,b = 2;', options: [{ before: true, after: false }], }, { - code: `function foo(a ,b){}`, + code: 'function foo(a ,b){}', options: [{ before: true, after: false }], }, { - code: `const arr = [,];`, + code: 'const arr = [,];', options: [{ before: true, after: false }], }, { - code: `const arr = [1 ,];`, + code: 'const arr = [1 ,];', options: [{ before: true, after: false }], }, { - code: `const arr = [ ,2];`, + code: 'const arr = [ ,2];', options: [{ before: true, after: false }], }, { - code: `const arr = [1 ,2];`, + code: 'const arr = [1 ,2];', options: [{ before: true, after: false }], }, { - code: `const arr = [,,];`, + code: 'const arr = [,,];', options: [{ before: true, after: false }], }, { - code: `const arr = [1 , ,];`, + code: 'const arr = [1 , ,];', options: [{ before: true, after: false }], }, { - code: `const arr = [ ,2 ,];`, + code: 'const arr = [ ,2 ,];', options: [{ before: true, after: false }], }, { - code: `const arr = [ , ,3];`, + code: 'const arr = [ , ,3];', options: [{ before: true, after: false }], }, { - code: `const arr = [1 ,2 ,];`, + code: 'const arr = [1 ,2 ,];', options: [{ before: true, after: false }], }, { - code: `const arr = [ ,2 ,3];`, + code: 'const arr = [ ,2 ,3];', options: [{ before: true, after: false }], }, { - code: `const arr = [1 , ,3];`, + code: 'const arr = [1 , ,3];', options: [{ before: true, after: false }], }, { - code: `const arr = [1 ,2 ,3];`, + code: 'const arr = [1 ,2 ,3];', options: [{ before: true, after: false }], }, { - code: `const obj = {'foo':'bar' , 'baz':'qur'};`, + code: "const obj = {'foo':'bar' , 'baz':'qur'};", options: [{ before: true, after: true }], }, { - code: `const a = 1 , b = 2;`, + code: 'const a = 1 , b = 2;', options: [{ before: true, after: true }], }, { - code: `const arr = [, ];`, + code: 'const arr = [, ];', options: [{ before: true, after: true }], }, { - code: `const arr = [1 , ];`, + code: 'const arr = [1 , ];', options: [{ before: true, after: true }], }, { - code: `const arr = [ , 2];`, + code: 'const arr = [ , 2];', options: [{ before: true, after: true }], }, { - code: `const arr = [1 , 2];`, + code: 'const arr = [1 , 2];', options: [{ before: true, after: true }], }, { - code: `const arr = [, , ];`, + code: 'const arr = [, , ];', options: [{ before: true, after: true }], }, { - code: `const arr = [1 , , ];`, + code: 'const arr = [1 , , ];', options: [{ before: true, after: true }], }, { - code: `const arr = [ , 2 , ];`, + code: 'const arr = [ , 2 , ];', options: [{ before: true, after: true }], }, { - code: `const arr = [ , , 3];`, + code: 'const arr = [ , , 3];', options: [{ before: true, after: true }], }, { - code: `const arr = [1 , 2 , ];`, + code: 'const arr = [1 , 2 , ];', options: [{ before: true, after: true }], }, { - code: `const arr = [, 2 , 3];`, + code: 'const arr = [, 2 , 3];', options: [{ before: true, after: true }], }, { - code: `const arr = [1 , , 3];`, + code: 'const arr = [1 , , 3];', options: [{ before: true, after: true }], }, { - code: `const arr = [1 , 2 , 3];`, + code: 'const arr = [1 , 2 , 3];', options: [{ before: true, after: true }], }, { - code: `a , b`, + code: 'a , b', options: [{ before: true, after: true }], }, { - code: `const arr = [,];`, + code: 'const arr = [,];', options: [{ before: false, after: false }], }, { - code: `const arr = [ ,];`, + code: 'const arr = [ ,];', options: [{ before: false, after: false }], }, { - code: `const arr = [1,];`, + code: 'const arr = [1,];', options: [{ before: false, after: false }], }, { - code: `const arr = [,2];`, + code: 'const arr = [,2];', options: [{ before: false, after: false }], }, { - code: `const arr = [ ,2];`, + code: 'const arr = [ ,2];', options: [{ before: false, after: false }], }, { - code: `const arr = [1,2];`, + code: 'const arr = [1,2];', options: [{ before: false, after: false }], }, { - code: `const arr = [,,];`, + code: 'const arr = [,,];', options: [{ before: false, after: false }], }, { - code: `const arr = [ ,,];`, + code: 'const arr = [ ,,];', options: [{ before: false, after: false }], }, { - code: `const arr = [1,,];`, + code: 'const arr = [1,,];', options: [{ before: false, after: false }], }, { - code: `const arr = [,2,];`, + code: 'const arr = [,2,];', options: [{ before: false, after: false }], }, { - code: `const arr = [ ,2,];`, + code: 'const arr = [ ,2,];', options: [{ before: false, after: false }], }, { - code: `const arr = [,,3];`, + code: 'const arr = [,,3];', options: [{ before: false, after: false }], }, { - code: `const arr = [1,2,];`, + code: 'const arr = [1,2,];', options: [{ before: false, after: false }], }, { - code: `const arr = [,2,3];`, + code: 'const arr = [,2,3];', options: [{ before: false, after: false }], }, { - code: `const arr = [1,,3];`, + code: 'const arr = [1,,3];', options: [{ before: false, after: false }], }, { - code: `const arr = [1,2,3];`, + code: 'const arr = [1,2,3];', options: [{ before: false, after: false }], }, { - code: `const a = (1 + 2,2)`, + code: 'const a = (1 + 2,2)', options: [{ before: false, after: false }], }, 'const a; console.log(`${a}`, "a");', - `const [a, b] = [1, 2];`, - `const [a, b, ] = [1, 2];`, - `const [a, , b] = [1, 2, 3];`, - `const [ , b] = a;`, - `const [, b] = a;`, + 'const [a, b] = [1, 2];', + 'const [a, b, ] = [1, 2];', + 'const [a, , b] = [1, 2, 3];', + 'const [ , b] = a;', + 'const [, b] = a;', { - code: `,`, + code: ',', parserOptions: { ecmaFeatures: { jsx: true }, }, }, { - code: ` , `, + code: ' , ', parserOptions: { ecmaFeatures: { jsx: true }, }, }, { - code: `Hello, world`, + code: 'Hello, world', options: [{ before: true, after: false }], parserOptions: { ecmaFeatures: { jsx: true } }, }, - `const Foo = (foo: T) => {}`, - `function foo() {}`, - `class Foo {}`, - `interface Foo{}`, + 'const Foo = (foo: T) => {}', + 'function foo() {}', + 'class Foo {}', + 'interface Foo{}', ], invalid: [ { - code: `a(b,c)`, - output: `a(b , c)`, + code: 'a(b,c)', + output: 'a(b , c)', options: [{ before: true, after: true }], errors: [ { @@ -298,8 +303,8 @@ ruleTester.run('comma-spacing', rule, { ], }, { - code: `new A(b,c)`, - output: `new A(b , c)`, + code: 'new A(b,c)', + output: 'new A(b , c)', options: [{ before: true, after: true }], errors: [ { @@ -317,8 +322,8 @@ ruleTester.run('comma-spacing', rule, { ], }, { - code: `const a = 1 ,b = 2;`, - output: `const a = 1, b = 2;`, + code: 'const a = 1 ,b = 2;', + output: 'const a = 1, b = 2;', errors: [ { messageId: 'unexpected', @@ -335,8 +340,8 @@ ruleTester.run('comma-spacing', rule, { ], }, { - code: `const arr = [1 , 2];`, - output: `const arr = [1, 2];`, + code: 'const arr = [1 , 2];', + output: 'const arr = [1, 2];', errors: [ { messageId: 'unexpected', @@ -359,8 +364,8 @@ ruleTester.run('comma-spacing', rule, { ], }, { - code: `const arr = [1 , ];`, - output: `const arr = [1 ,];`, + code: 'const arr = [1 , ];', + output: 'const arr = [1 ,];', options: [{ before: true, after: false }], errors: [ { @@ -372,8 +377,8 @@ ruleTester.run('comma-spacing', rule, { ], }, { - code: `const arr = [1 ,2];`, - output: `const arr = [1, 2];`, + code: 'const arr = [1 ,2];', + output: 'const arr = [1, 2];', errors: [ { messageId: 'unexpected', @@ -390,8 +395,8 @@ ruleTester.run('comma-spacing', rule, { ], }, { - code: `const arr = [(1) , 2];`, - output: `const arr = [(1), 2];`, + code: 'const arr = [(1) , 2];', + output: 'const arr = [(1), 2];', errors: [ { messageId: 'unexpected', @@ -402,8 +407,8 @@ ruleTester.run('comma-spacing', rule, { ], }, { - code: `const arr = [1, 2];`, - output: `const arr = [1 ,2];`, + code: 'const arr = [1, 2];', + output: 'const arr = [1 ,2];', options: [{ before: true, after: false }], errors: [ { @@ -421,8 +426,8 @@ ruleTester.run('comma-spacing', rule, { ], }, { - code: `const arr = [1\n , 2];`, - output: `const arr = [1\n ,2];`, + code: 'const arr = [1\n , 2];', + output: 'const arr = [1\n ,2];', options: [{ before: false, after: false }], errors: [ { @@ -434,8 +439,8 @@ ruleTester.run('comma-spacing', rule, { ], }, { - code: `const arr = [1,\n 2];`, - output: `const arr = [1 ,\n 2];`, + code: 'const arr = [1,\n 2];', + output: 'const arr = [1 ,\n 2];', options: [{ before: true, after: false }], errors: [ { @@ -447,8 +452,8 @@ ruleTester.run('comma-spacing', rule, { ], }, { - code: `const obj = {'foo':\n'bar', 'baz':\n'qur'};`, - output: `const obj = {'foo':\n'bar' ,'baz':\n'qur'};`, + code: "const obj = {'foo':\n'bar', 'baz':\n'qur'};", + output: "const obj = {'foo':\n'bar' ,'baz':\n'qur'};", options: [{ before: true, after: false }], errors: [ { @@ -466,8 +471,8 @@ ruleTester.run('comma-spacing', rule, { ], }, { - code: `const obj = {a: 1\n ,b: 2};`, - output: `const obj = {a: 1\n , b: 2};`, + code: 'const obj = {a: 1\n ,b: 2};', + output: 'const obj = {a: 1\n , b: 2};', options: [{ before: false, after: true }], errors: [ { @@ -479,8 +484,8 @@ ruleTester.run('comma-spacing', rule, { ], }, { - code: `const obj = {a: 1 ,\n b: 2};`, - output: `const obj = {a: 1,\n b: 2};`, + code: 'const obj = {a: 1 ,\n b: 2};', + output: 'const obj = {a: 1,\n b: 2};', options: [{ before: false, after: false }], errors: [ { @@ -492,8 +497,8 @@ ruleTester.run('comma-spacing', rule, { ], }, { - code: `const arr = [1 ,2];`, - output: `const arr = [1 , 2];`, + code: 'const arr = [1 ,2];', + output: 'const arr = [1 , 2];', options: [{ before: true, after: true }], errors: [ { @@ -505,8 +510,8 @@ ruleTester.run('comma-spacing', rule, { ], }, { - code: `const arr = [1,2];`, - output: `const arr = [1 , 2];`, + code: 'const arr = [1,2];', + output: 'const arr = [1 , 2];', options: [{ before: true, after: true }], errors: [ { @@ -524,8 +529,8 @@ ruleTester.run('comma-spacing', rule, { ], }, { - code: `const obj = {'foo':\n'bar','baz':\n'qur'};`, - output: `const obj = {'foo':\n'bar' , 'baz':\n'qur'};`, + code: "const obj = {'foo':\n'bar','baz':\n'qur'};", + output: "const obj = {'foo':\n'bar' , 'baz':\n'qur'};", options: [{ before: true, after: true }], errors: [ { @@ -543,8 +548,8 @@ ruleTester.run('comma-spacing', rule, { ], }, { - code: `const arr = [1 , 2];`, - output: `const arr = [1,2];`, + code: 'const arr = [1 , 2];', + output: 'const arr = [1,2];', options: [{ before: false, after: false }], errors: [ { @@ -562,8 +567,8 @@ ruleTester.run('comma-spacing', rule, { ], }, { - code: `a ,b`, - output: `a, b`, + code: 'a ,b', + output: 'a, b', options: [{ before: false, after: true }], errors: [ { @@ -581,8 +586,8 @@ ruleTester.run('comma-spacing', rule, { ], }, { - code: `function foo(a,b){}`, - output: `function foo(a , b){}`, + code: 'function foo(a,b){}', + output: 'function foo(a , b){}', options: [{ before: true, after: true }], errors: [ { @@ -600,8 +605,8 @@ ruleTester.run('comma-spacing', rule, { ], }, { - code: `const foo = (a,b) => {}`, - output: `const foo = (a , b) => {}`, + code: 'const foo = (a,b) => {}', + output: 'const foo = (a , b) => {}', options: [{ before: true, after: true }], errors: [ { @@ -619,8 +624,8 @@ ruleTester.run('comma-spacing', rule, { ], }, { - code: `const foo = (a = 1,b) => {}`, - output: `const foo = (a = 1 , b) => {}`, + code: 'const foo = (a = 1,b) => {}', + output: 'const foo = (a = 1 , b) => {}', options: [{ before: true, after: true }], errors: [ { @@ -638,8 +643,8 @@ ruleTester.run('comma-spacing', rule, { ], }, { - code: `function foo(a = 1 ,b = 2) {}`, - output: `function foo(a = 1, b = 2) {}`, + code: 'function foo(a = 1 ,b = 2) {}', + output: 'function foo(a = 1, b = 2) {}', options: [{ before: false, after: true }], errors: [ { @@ -657,8 +662,8 @@ ruleTester.run('comma-spacing', rule, { ], }, { - code: `{foo(1 ,2)}`, - output: `{foo(1, 2)}`, + code: '{foo(1 ,2)}', + output: '{foo(1, 2)}', parserOptions: { ecmaFeatures: { jsx: true }, }, @@ -678,8 +683,8 @@ ruleTester.run('comma-spacing', rule, { ], }, { - code: `foo(1, true/* comment */ , 'foo');`, - output: `foo(1, true/* comment */, 'foo');`, + code: "foo(1, true/* comment */ , 'foo');", + output: "foo(1, true/* comment */, 'foo');", errors: [ { messageId: 'unexpected', @@ -690,8 +695,8 @@ ruleTester.run('comma-spacing', rule, { ], }, { - code: `foo(1, true,/* comment */ 'foo');`, - output: `foo(1, true, /* comment */ 'foo');`, + code: "foo(1, true,/* comment */ 'foo');", + output: "foo(1, true, /* comment */ 'foo');", errors: [ { messageId: 'missing', @@ -702,8 +707,8 @@ ruleTester.run('comma-spacing', rule, { ], }, { - code: `foo(404,// comment\n true, 'hello');`, - output: `foo(404, // comment\n true, 'hello');`, + code: "foo(404,// comment\n true, 'hello');", + output: "foo(404, // comment\n true, 'hello');", errors: [ { messageId: 'missing', @@ -714,8 +719,8 @@ ruleTester.run('comma-spacing', rule, { ], }, { - code: `function Foo() {}`, - output: `function Foo() {}`, + code: 'function Foo() {}', + output: 'function Foo() {}', errors: [ { messageId: 'missing', @@ -726,8 +731,8 @@ ruleTester.run('comma-spacing', rule, { ], }, { - code: `function Foo() {}`, - output: `function Foo() {}`, + code: 'function Foo() {}', + output: 'function Foo() {}', errors: [ { messageId: 'unexpected', @@ -738,8 +743,8 @@ ruleTester.run('comma-spacing', rule, { ], }, { - code: `function Foo() {}`, - output: `function Foo() {}`, + code: 'function Foo() {}', + output: 'function Foo() {}', errors: [ { messageId: 'unexpected', @@ -756,8 +761,8 @@ ruleTester.run('comma-spacing', rule, { ], }, { - code: `function Foo() {}`, - output: `function Foo() {}`, + code: 'function Foo() {}', + output: 'function Foo() {}', options: [{ before: false, after: false }], errors: [ { @@ -769,8 +774,8 @@ ruleTester.run('comma-spacing', rule, { ], }, { - code: `function Foo() {}`, - output: `function Foo() {}`, + code: 'function Foo() {}', + output: 'function Foo() {}', options: [{ before: true, after: false }], errors: [ { diff --git a/packages/eslint-plugin/tests/rules/consistent-type-definitions.test.ts b/packages/eslint-plugin/tests/rules/consistent-type-definitions.test.ts index b777a1fc68f..78e5d3ceb01 100644 --- a/packages/eslint-plugin/tests/rules/consistent-type-definitions.test.ts +++ b/packages/eslint-plugin/tests/rules/consistent-type-definitions.test.ts @@ -1,5 +1,5 @@ import rule from '../../src/rules/consistent-type-definitions'; -import { RuleTester } from '../RuleTester'; +import { RuleTester, noFormat } from '../RuleTester'; const ruleTester = new RuleTester({ parser: '@typescript-eslint/parser', @@ -8,58 +8,62 @@ const ruleTester = new RuleTester({ ruleTester.run('consistent-type-definitions', rule, { valid: [ { - code: `var foo = { };`, + code: 'var foo = {};', options: ['interface'], }, { - code: `interface A {}`, + code: 'interface A {}', options: ['interface'], }, { - code: `interface A extends B { x: number; }`, + code: ` +interface A extends B { + x: number; +} + `, options: ['interface'], }, { - code: `type U = string;`, + code: 'type U = string;', options: ['interface'], }, { - code: `type V = { x: number; } | { y: string; };`, + code: 'type V = { x: number } | { y: string };', options: ['interface'], }, { code: ` type Record = { - [K in T]: U; -} -`, + [K in T]: U; +}; + `, options: ['interface'], }, { - code: `type T = { x: number; }`, + code: 'type T = { x: number };', options: ['type'], }, { - code: `type A = { x: number; } & B & C;`, + code: 'type A = { x: number } & B & C;', options: ['type'], }, { - code: `type A = { x: number; } & B & C;`, + code: 'type A = { x: number } & B & C;', options: ['type'], }, { code: ` export type W = { - x: T, + x: T; }; -`, + `, options: ['type'], }, ], invalid: [ { - code: `type T = { x: number; };`, - output: `interface T { x: number; }`, + code: noFormat`type T = { x: number; };`, + output: noFormat`interface T { x: number; }`, options: ['interface'], errors: [ { @@ -70,8 +74,8 @@ export type W = { ], }, { - code: `type T={ x: number; };`, - output: `interface T { x: number; }`, + code: noFormat`type T={ x: number; };`, + output: noFormat`interface T { x: number; }`, options: ['interface'], errors: [ { @@ -82,8 +86,8 @@ export type W = { ], }, { - code: `type T= { x: number; };`, - output: `interface T { x: number; }`, + code: noFormat`type T= { x: number; };`, + output: noFormat`interface T { x: number; }`, options: ['interface'], errors: [ { @@ -96,14 +100,14 @@ export type W = { { code: ` export type W = { - x: T, + x: T; }; -`, + `, output: ` export interface W { - x: T, + x: T; } -`, + `, options: ['interface'], errors: [ { @@ -114,8 +118,8 @@ export interface W { ], }, { - code: `interface T { x: number; }`, - output: `type T = { x: number; }`, + code: noFormat`interface T { x: number; }`, + output: noFormat`type T = { x: number; }`, options: ['type'], errors: [ { @@ -126,8 +130,8 @@ export interface W { ], }, { - code: `interface T{ x: number; }`, - output: `type T = { x: number; }`, + code: noFormat`interface T{ x: number; }`, + output: noFormat`type T = { x: number; }`, options: ['type'], errors: [ { @@ -138,8 +142,8 @@ export interface W { ], }, { - code: `interface T { x: number; }`, - output: `type T = { x: number; }`, + code: noFormat`interface T { x: number; }`, + output: noFormat`type T = { x: number; }`, options: ['type'], errors: [ { @@ -150,8 +154,8 @@ export interface W { ], }, { - code: `interface A extends B, C { x: number; };`, - output: `type A = { x: number; } & B & C;`, + code: noFormat`interface A extends B, C { x: number; };`, + output: noFormat`type A = { x: number; } & B & C;`, options: ['type'], errors: [ { @@ -162,8 +166,8 @@ export interface W { ], }, { - code: `interface A extends B, C { x: number; };`, - output: `type A = { x: number; } & B & C;`, + code: noFormat`interface A extends B, C { x: number; };`, + output: noFormat`type A = { x: number; } & B & C;`, options: ['type'], errors: [ { @@ -176,14 +180,14 @@ export interface W { { code: ` export interface W { - x: T, -}; -`, - output: ` + x: T; +} + `, + output: noFormat` export type W = { - x: T, -}; -`, + x: T; +} + `, options: ['type'], errors: [ { diff --git a/packages/eslint-plugin/tests/rules/default-param-last.test.ts b/packages/eslint-plugin/tests/rules/default-param-last.test.ts index d965df3c77f..76e23bbe8a7 100644 --- a/packages/eslint-plugin/tests/rules/default-param-last.test.ts +++ b/packages/eslint-plugin/tests/rules/default-param-last.test.ts @@ -19,29 +19,29 @@ ruleTester.run('default-param-last', rule, { 'function foo(a: number, b?: number, c = 1) {}', 'function foo(a: number, b = 1, ...c) {}', - 'const foo = function () {}', - 'const foo = function (a: number) {}', - 'const foo = function (a = 1) {}', - 'const foo = function (a?: number) {}', - 'const foo = function (a: number, b: number) {}', - 'const foo = function (a: number, b: number, c?: number) {}', - 'const foo = function (a: number, b = 1) {}', - 'const foo = function (a: number, b = 1, c = 1) {}', - 'const foo = function (a: number, b = 1, c?: number) {}', - 'const foo = function (a: number, b?: number, c = 1) {}', - 'const foo = function (a: number, b = 1, ...c) {}', + 'const foo = function() {};', + 'const foo = function(a: number) {};', + 'const foo = function(a = 1) {};', + 'const foo = function(a?: number) {};', + 'const foo = function(a: number, b: number) {};', + 'const foo = function(a: number, b: number, c?: number) {};', + 'const foo = function(a: number, b = 1) {};', + 'const foo = function(a: number, b = 1, c = 1) {};', + 'const foo = function(a: number, b = 1, c?: number) {};', + 'const foo = function(a: number, b?: number, c = 1) {};', + 'const foo = function(a: number, b = 1, ...c) {};', - 'const foo = () => {}', - 'const foo = (a: number) => {}', - 'const foo = (a = 1) => {}', - 'const foo = (a?: number) => {}', - 'const foo = (a: number, b: number) => {}', - 'const foo = (a: number, b: number, c?: number) => {}', - 'const foo = (a: number, b = 1) => {}', - 'const foo = (a: number, b = 1, c = 1) => {}', - 'const foo = (a: number, b = 1, c?: number) => {}', - 'const foo = (a: number, b?: number, c = 1) => {}', - 'const foo = (a: number, b = 1, ...c) => {}', + 'const foo = () => {};', + 'const foo = (a: number) => {};', + 'const foo = (a = 1) => {};', + 'const foo = (a?: number) => {};', + 'const foo = (a: number, b: number) => {};', + 'const foo = (a: number, b: number, c?: number) => {};', + 'const foo = (a: number, b = 1) => {};', + 'const foo = (a: number, b = 1, c = 1) => {};', + 'const foo = (a: number, b = 1, c?: number) => {};', + 'const foo = (a: number, b?: number, c = 1) => {};', + 'const foo = (a: number, b = 1, ...c) => {};', ` class Foo { constructor(a: number, b: number, c: number) {} @@ -251,7 +251,7 @@ class Foo { ], }, { - code: 'const foo = function(a = 1, b: number) {}', + code: 'const foo = function(a = 1, b: number) {};', errors: [ { messageId: 'shouldBeLast', @@ -262,7 +262,7 @@ class Foo { ], }, { - code: 'const foo = function(a = 1, b = 2, c: number) {}', + code: 'const foo = function(a = 1, b = 2, c: number) {};', errors: [ { messageId: 'shouldBeLast', @@ -279,7 +279,7 @@ class Foo { ], }, { - code: 'const foo = function(a = 1, b: number, c = 2, d: number) {}', + code: 'const foo = function(a = 1, b: number, c = 2, d: number) {};', errors: [ { messageId: 'shouldBeLast', @@ -296,7 +296,7 @@ class Foo { ], }, { - code: 'const foo = function(a = 1, b: number, c = 2) {}', + code: 'const foo = function(a = 1, b: number, c = 2) {};', errors: [ { messageId: 'shouldBeLast', @@ -307,7 +307,7 @@ class Foo { ], }, { - code: 'const foo = function(a = 1, b: number, ...c) {}', + code: 'const foo = function(a = 1, b: number, ...c) {};', errors: [ { messageId: 'shouldBeLast', @@ -318,7 +318,7 @@ class Foo { ], }, { - code: 'const foo = function(a?: number, b: number) {}', + code: 'const foo = function(a?: number, b: number) {};', errors: [ { messageId: 'shouldBeLast', @@ -329,7 +329,7 @@ class Foo { ], }, { - code: 'const foo = function(a: number, b?: number, c: number) {}', + code: 'const foo = function(a: number, b?: number, c: number) {};', errors: [ { messageId: 'shouldBeLast', @@ -340,7 +340,7 @@ class Foo { ], }, { - code: 'const foo = function(a = 1, b?: number, c: number) {}', + code: 'const foo = function(a = 1, b?: number, c: number) {};', errors: [ { messageId: 'shouldBeLast', @@ -357,7 +357,7 @@ class Foo { ], }, { - code: 'const foo = function(a = 1, { b }) {}', + code: 'const foo = function(a = 1, { b }) {};', errors: [ { messageId: 'shouldBeLast', @@ -368,7 +368,7 @@ class Foo { ], }, { - code: 'const foo = function({ a } = {}, b) {}', + code: 'const foo = function({ a } = {}, b) {};', errors: [ { messageId: 'shouldBeLast', @@ -379,7 +379,7 @@ class Foo { ], }, { - code: 'const foo = function({ a, b } = { a: 1, b: 2 }, c) {}', + code: 'const foo = function({ a, b } = { a: 1, b: 2 }, c) {};', errors: [ { messageId: 'shouldBeLast', @@ -390,7 +390,7 @@ class Foo { ], }, { - code: 'const foo = function([a] = [], b) {}', + code: 'const foo = function([a] = [], b) {};', errors: [ { messageId: 'shouldBeLast', @@ -401,7 +401,7 @@ class Foo { ], }, { - code: 'const foo = function([a, b] = [1, 2], c) {}', + code: 'const foo = function([a, b] = [1, 2], c) {};', errors: [ { messageId: 'shouldBeLast', @@ -412,7 +412,7 @@ class Foo { ], }, { - code: 'const foo = (a = 1, b: number) => {}', + code: 'const foo = (a = 1, b: number) => {};', errors: [ { messageId: 'shouldBeLast', @@ -423,7 +423,7 @@ class Foo { ], }, { - code: 'const foo = (a = 1, b = 2, c: number) => {}', + code: 'const foo = (a = 1, b = 2, c: number) => {};', errors: [ { messageId: 'shouldBeLast', @@ -440,7 +440,7 @@ class Foo { ], }, { - code: 'const foo = (a = 1, b: number, c = 2, d: number) => {}', + code: 'const foo = (a = 1, b: number, c = 2, d: number) => {};', errors: [ { messageId: 'shouldBeLast', @@ -457,7 +457,7 @@ class Foo { ], }, { - code: 'const foo = (a = 1, b: number, c = 2) => {}', + code: 'const foo = (a = 1, b: number, c = 2) => {};', errors: [ { messageId: 'shouldBeLast', @@ -468,7 +468,7 @@ class Foo { ], }, { - code: 'const foo = (a = 1, b: number, ...c) => {}', + code: 'const foo = (a = 1, b: number, ...c) => {};', errors: [ { messageId: 'shouldBeLast', @@ -479,7 +479,7 @@ class Foo { ], }, { - code: 'const foo = (a?: number, b: number) => {}', + code: 'const foo = (a?: number, b: number) => {};', errors: [ { messageId: 'shouldBeLast', @@ -490,7 +490,7 @@ class Foo { ], }, { - code: 'const foo = (a: number, b?: number, c: number) => {}', + code: 'const foo = (a: number, b?: number, c: number) => {};', errors: [ { messageId: 'shouldBeLast', @@ -501,7 +501,7 @@ class Foo { ], }, { - code: 'const foo = (a = 1, b?: number, c: number) => {}', + code: 'const foo = (a = 1, b?: number, c: number) => {};', errors: [ { messageId: 'shouldBeLast', @@ -518,7 +518,7 @@ class Foo { ], }, { - code: 'const foo = (a = 1, { b }) => {}', + code: 'const foo = (a = 1, { b }) => {};', errors: [ { messageId: 'shouldBeLast', @@ -529,7 +529,7 @@ class Foo { ], }, { - code: 'const foo = ({ a } = {}, b) => {}', + code: 'const foo = ({ a } = {}, b) => {};', errors: [ { messageId: 'shouldBeLast', @@ -540,7 +540,7 @@ class Foo { ], }, { - code: 'const foo = ({ a, b } = { a: 1, b: 2 }, c) => {}', + code: 'const foo = ({ a, b } = { a: 1, b: 2 }, c) => {};', errors: [ { messageId: 'shouldBeLast', @@ -551,7 +551,7 @@ class Foo { ], }, { - code: 'const foo = ([a] = [], b) => {}', + code: 'const foo = ([a] = [], b) => {};', errors: [ { messageId: 'shouldBeLast', @@ -562,7 +562,7 @@ class Foo { ], }, { - code: 'const foo = ([a, b] = [1, 2], c) => {}', + code: 'const foo = ([a, b] = [1, 2], c) => {};', errors: [ { messageId: 'shouldBeLast', 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 fb353eec1a3..46ab4944185 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 @@ -11,23 +11,23 @@ ruleTester.run('explicit-function-return-type', rule, { filename: 'test.ts', code: ` function test(): void { - return; + return; } - `, + `, }, { filename: 'test.ts', code: ` var fn = function(): number { - return 1; + return 1; }; - `, + `, }, { filename: 'test.ts', code: ` var arrowFn = (): string => 'test'; - `, + `, }, { filename: 'test.ts', @@ -43,11 +43,11 @@ class Test { } arrow = (): string => 'arrow'; } - `, + `, }, { filename: 'test.ts', - code: `fn(() => {});`, + code: 'fn(() => {});', options: [ { allowExpressions: true, @@ -56,7 +56,7 @@ class Test { }, { filename: 'test.ts', - code: `fn(function() {});`, + code: 'fn(function() {});', options: [ { allowExpressions: true, @@ -65,7 +65,7 @@ class Test { }, { filename: 'test.ts', - code: `[function() {}, () => {}]`, + code: '[function() {}, () => {}];', options: [ { allowExpressions: true, @@ -74,7 +74,7 @@ class Test { }, { filename: 'test.ts', - code: `(function() {});`, + code: '(function() {});', options: [ { allowExpressions: true, @@ -83,7 +83,7 @@ class Test { }, { filename: 'test.ts', - code: `(() => {})();`, + code: '(() => {})();', options: [ { allowExpressions: true, @@ -92,7 +92,7 @@ class Test { }, { filename: 'test.ts', - code: `export default (): void => {}`, + code: 'export default (): void => {};', options: [ { allowExpressions: true, @@ -103,7 +103,7 @@ class Test { filename: 'test.ts', code: ` var arrowFn: Foo = () => 'test'; - `, + `, options: [ { allowTypedFunctionExpressions: true, @@ -113,8 +113,10 @@ var arrowFn: Foo = () => 'test'; { filename: 'test.ts', code: ` -var funcExpr: Foo = function() { return 'test'; }; - `, +var funcExpr: Foo = function() { + return 'test'; +}; + `, options: [ { allowTypedFunctionExpressions: true, @@ -123,12 +125,12 @@ var funcExpr: Foo = function() { return 'test'; }; }, { filename: 'test.ts', - code: `const x = (() => {}) as Foo`, + code: 'const x = (() => {}) as Foo;', options: [{ allowTypedFunctionExpressions: true }], }, { filename: 'test.ts', - code: `const x = (() => {})`, + code: 'const x = (() => {});', options: [{ allowTypedFunctionExpressions: true }], }, { @@ -136,7 +138,7 @@ var funcExpr: Foo = function() { return 'test'; }; code: ` const x = { foo: () => {}, -} as Foo +} as Foo; `, options: [{ allowTypedFunctionExpressions: true }], }, @@ -145,7 +147,7 @@ const x = { code: ` const x = { foo: () => {}, -} +}; `, options: [{ allowTypedFunctionExpressions: true }], }, @@ -154,7 +156,7 @@ const x = { code: ` const x: Foo = { foo: () => {}, -} +}; `, options: [{ allowTypedFunctionExpressions: true }], }, @@ -165,7 +167,7 @@ const x: Foo = { type MethodType = () => void; class App { - private method: MethodType = () => {} + private method: MethodType = () => {}; } `, options: [{ allowTypedFunctionExpressions: true }], @@ -185,42 +187,50 @@ const myObj = { filename: 'test.ts', code: ` () => (): void => {}; - `, + `, options: [{ allowHigherOrderFunctions: true }], }, { filename: 'test.ts', code: ` -() => function (): void {}; - `, +() => function(): void {}; + `, options: [{ allowHigherOrderFunctions: true }], }, { filename: 'test.ts', code: ` -() => { return (): void => {} }; - `, +() => { + return (): void => {}; +}; + `, options: [{ allowHigherOrderFunctions: true }], }, { filename: 'test.ts', code: ` -() => { return function (): void {} }; - `, +() => { + return function(): void {}; +}; + `, options: [{ allowHigherOrderFunctions: true }], }, { filename: 'test.ts', code: ` -function fn() { return (): void => {} }; - `, +function fn() { + return (): void => {}; +} + `, options: [{ allowHigherOrderFunctions: true }], }, { filename: 'test.ts', code: ` -function fn() { return function (): void {} }; - `, +function fn() { + return function(): void {}; +} + `, options: [{ allowHigherOrderFunctions: true }], }, { @@ -229,33 +239,39 @@ function fn() { return function (): void {} }; function FunctionDeclaration() { return function FunctionExpression_Within_FunctionDeclaration() { return function FunctionExpression_Within_FunctionExpression() { - return () => { // ArrowFunctionExpression_Within_FunctionExpression - return () => // ArrowFunctionExpression_Within_ArrowFunctionExpression - (): number => 1 // ArrowFunctionExpression_Within_ArrowFunctionExpression_WithNoBody - } - } - } + return () => { + // ArrowFunctionExpression_Within_FunctionExpression + return () => + // ArrowFunctionExpression_Within_ArrowFunctionExpression + (): number => 1; // ArrowFunctionExpression_Within_ArrowFunctionExpression_WithNoBody + }; + }; + }; } - `, + `, options: [{ allowHigherOrderFunctions: true }], }, { filename: 'test.ts', code: ` -() => () => { return (): void => { return; } }; - `, +() => () => { + return (): void => { + return; + }; +}; + `, options: [{ allowHigherOrderFunctions: true }], }, // https://github.com/typescript-eslint/typescript-eslint/issues/679 { filename: 'test.ts', code: ` -declare function foo(arg: () => void): void -foo(() => 1) -foo(() => {}) -foo(() => null) -foo(() => true) -foo(() => '') +declare function foo(arg: () => void): void; +foo(() => 1); +foo(() => {}); +foo(() => null); +foo(() => true); +foo(() => ''); `, options: [ { @@ -266,12 +282,12 @@ foo(() => '') { filename: 'test.ts', code: ` -declare function foo(arg: () => void): void -foo?.(() => 1) -foo?.bar(() => {}) -foo?.bar?.(() => null) -foo.bar?.(() => true) -foo?.(() => '') +declare function foo(arg: () => void): void; +foo?.(() => 1); +foo?.bar(() => {}); +foo?.bar?.(() => null); +foo.bar?.(() => true); +foo?.(() => ''); `, options: [ { @@ -301,22 +317,22 @@ new Accumulator().accumulate(() => 1); { filename: 'test.ts', code: ` -declare function foo(arg: { meth: () => number }): void +declare function foo(arg: { meth: () => number }): void; foo({ meth() { return 1; }, -}) +}); foo({ - meth: function () { + meth: function() { return 1; }, -}) +}); foo({ meth: () => { return 1; }, -}) +}); `, options: [ { @@ -327,9 +343,9 @@ 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) => ({ 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: [ @@ -355,10 +371,7 @@ new Foo(1, () => {}); { filename: 'test.ts', code: ` -function test( - a: number, - b: number, -) { +function test(a: number, b: number) { return; } `, @@ -366,9 +379,9 @@ function test( { messageId: 'missingReturnType', line: 2, - endLine: 5, + endLine: 2, column: 1, - endColumn: 2, + endColumn: 36, }, ], }, @@ -427,7 +440,7 @@ var arrowFn = () => 'test'; class Test { constructor() {} get prop() { - return 1; + return 1; } set prop() {} method() { @@ -532,7 +545,7 @@ function test() { }, { filename: 'test.ts', - code: 'export default function() {};', + code: 'export default function() {}', options: [{ allowExpressions: true }], errors: [ { @@ -549,11 +562,11 @@ function test() { code: ` class Foo { public a = () => {}; - public b = function () {}; + public b = function() {}; public c = function test() {}; static d = () => {}; - static e = function () {}; + static e = function() {}; } `, options: [{ allowExpressions: true }], @@ -570,7 +583,7 @@ class Foo { line: 4, endLine: 4, column: 14, - endColumn: 25, + endColumn: 24, }, { messageId: 'missingReturnType', @@ -591,7 +604,7 @@ class Foo { line: 8, endLine: 8, column: 14, - endColumn: 25, + endColumn: 24, }, ], }, @@ -611,13 +624,17 @@ class Foo { }, { filename: 'test.ts', - code: "var funcExpr = function() { return 'test'; };", + code: ` +var funcExpr = function() { + return 'test'; +}; + `, options: [{ allowTypedFunctionExpressions: true }], errors: [ { messageId: 'missingReturnType', - line: 1, - endLine: 1, + line: 2, + endLine: 2, column: 16, endColumn: 26, }, @@ -626,7 +643,7 @@ class Foo { { filename: 'test.ts', - code: 'const x = (() => {}) as Foo', + code: 'const x = (() => {}) as Foo;', options: [{ allowTypedFunctionExpressions: false }], errors: [ { @@ -644,7 +661,7 @@ class Foo { interface Foo {} const x = { foo: () => {}, -} as Foo +} as Foo; `, options: [{ allowTypedFunctionExpressions: false }], errors: [ @@ -663,7 +680,7 @@ const x = { interface Foo {} const x: Foo = { foo: () => {}, -} +}; `, options: [{ allowTypedFunctionExpressions: false }], errors: [ @@ -692,7 +709,7 @@ const x: Foo = { }, { filename: 'test.ts', - code: '() => function () {};', + code: '() => function() {};', options: [{ allowHigherOrderFunctions: true }], errors: [ { @@ -700,63 +717,79 @@ const x: Foo = { line: 1, endLine: 1, column: 7, - endColumn: 18, + endColumn: 17, }, ], }, { filename: 'test.ts', - code: '() => { return () => {} };', + code: ` +() => { + return () => {}; +}; + `, options: [{ allowHigherOrderFunctions: true }], errors: [ { messageId: 'missingReturnType', - line: 1, - endLine: 1, - column: 16, - endColumn: 21, + line: 3, + endLine: 3, + column: 10, + endColumn: 15, }, ], }, { filename: 'test.ts', - code: '() => { return function () {} };', + code: ` +() => { + return function() {}; +}; + `, options: [{ allowHigherOrderFunctions: true }], errors: [ { messageId: 'missingReturnType', - line: 1, - endLine: 1, - column: 16, - endColumn: 27, + line: 3, + endLine: 3, + column: 10, + endColumn: 20, }, ], }, { filename: 'test.ts', - code: 'function fn() { return () => {} };', + code: ` +function fn() { + return () => {}; +} + `, options: [{ allowHigherOrderFunctions: true }], errors: [ { messageId: 'missingReturnType', - line: 1, - endLine: 1, - column: 24, - endColumn: 29, + line: 3, + endLine: 3, + column: 10, + endColumn: 15, }, ], }, { filename: 'test.ts', - code: 'function fn() { return function () {} };', + code: ` +function fn() { + return function() {}; +} + `, options: [{ allowHigherOrderFunctions: true }], errors: [ { messageId: 'missingReturnType', - line: 1, - endLine: 1, - column: 24, - endColumn: 35, + line: 3, + endLine: 3, + column: 10, + endColumn: 20, }, ], }, @@ -766,20 +799,22 @@ const x: Foo = { function FunctionDeclaration() { return function FunctionExpression_Within_FunctionDeclaration() { return function FunctionExpression_Within_FunctionExpression() { - return () => { // ArrowFunctionExpression_Within_FunctionExpression - return () => // ArrowFunctionExpression_Within_ArrowFunctionExpression - () => 1 // ArrowFunctionExpression_Within_ArrowFunctionExpression_WithNoBody - } - } - } + return () => { + // ArrowFunctionExpression_Within_FunctionExpression + return () => + // ArrowFunctionExpression_Within_ArrowFunctionExpression + () => 1; // ArrowFunctionExpression_Within_ArrowFunctionExpression_WithNoBody + }; + }; + }; } - `, + `, options: [{ allowHigherOrderFunctions: true }], errors: [ { messageId: 'missingReturnType', - line: 7, - endLine: 7, + line: 9, + endLine: 9, column: 11, endColumn: 16, }, @@ -787,15 +822,21 @@ function FunctionDeclaration() { }, { filename: 'test.ts', - code: '() => () => { return () => { return; } };', + code: ` +() => () => { + return () => { + return; + }; +}; + `, options: [{ allowHigherOrderFunctions: true }], errors: [ { messageId: 'missingReturnType', - line: 1, - endLine: 1, - column: 22, - endColumn: 27, + line: 3, + endLine: 3, + column: 10, + endColumn: 15, }, ], }, @@ -803,12 +844,12 @@ function FunctionDeclaration() { { filename: 'test.ts', code: ` -declare function foo(arg: () => void): void -foo(() => 1) -foo(() => {}) -foo(() => null) -foo(() => true) -foo(() => '') +declare function foo(arg: () => void): void; +foo(() => 1); +foo(() => {}); +foo(() => null); +foo(() => true); +foo(() => ''); `, options: [ { @@ -883,7 +924,7 @@ new Accumulator().accumulate(() => 1); }, { filename: 'test.ts', - code: '(() => true)()', + code: '(() => true)();', options: [ { allowTypedFunctionExpressions: false, @@ -902,22 +943,22 @@ new Accumulator().accumulate(() => 1); { filename: 'test.ts', code: ` -declare function foo(arg: { meth: () => number }): void +declare function foo(arg: { meth: () => number }): void; foo({ meth() { return 1; }, -}) +}); foo({ - meth: function () { + meth: function() { return 1; }, -}) +}); foo({ meth: () => { return 1; }, -}) +}); `, options: [ { @@ -937,7 +978,7 @@ foo({ line: 9, endLine: 9, column: 9, - endColumn: 20, + endColumn: 19, }, { messageId: 'missingReturnType', @@ -951,8 +992,8 @@ foo({ { filename: 'test.ts', code: ` -const func = (value: number) => ({ type: "X", value } as any); -const func = (value: number) => ({ type: "X", value } as Action); +const func = (value: number) => ({ type: 'X', value } as any); +const func = (value: number) => ({ type: 'X', value } as Action); `, options: [ { @@ -979,7 +1020,7 @@ const func = (value: number) => ({ type: "X", value } as Action); { filename: 'test.ts', code: ` -const func = (value: number) => ({ type: "X", value } as const); +const func = (value: number) => ({ type: 'X', value } as const); `, options: [ { diff --git a/packages/eslint-plugin/tests/rules/explicit-member-accessibility.test.ts b/packages/eslint-plugin/tests/rules/explicit-member-accessibility.test.ts index fe0a09dd4c1..906f1b6cc60 100644 --- a/packages/eslint-plugin/tests/rules/explicit-member-accessibility.test.ts +++ b/packages/eslint-plugin/tests/rules/explicit-member-accessibility.test.ts @@ -1,5 +1,5 @@ import rule from '../../src/rules/explicit-member-accessibility'; -import { RuleTester } from '../RuleTester'; +import { RuleTester, noFormat } from '../RuleTester'; const ruleTester = new RuleTester({ parser: '@typescript-eslint/parser', @@ -109,72 +109,72 @@ class Test { filename: 'test.ts', code: ` class Test { - protected name: string - private x: number - public getX () { - return this.x + protected name: string; + private x: number; + public getX() { + return this.x; } } - `, + `, }, { filename: 'test.ts', code: ` class Test { - protected name: string - protected foo?: string - public "foo-bar"?: string + protected name: string; + protected foo?: string; + public 'foo-bar'?: string; } - `, + `, }, { filename: 'test.ts', code: ` class Test { - public constructor({x, y}: {x: number; y: number;}) {} + public constructor({ x, y }: { x: number; y: number }) {} } - `, + `, }, { filename: 'test.ts', code: ` class Test { - protected name: string - protected foo?: string - public getX () { - return this.x + protected name: string; + protected foo?: string; + public getX() { + return this.x; } } - `, + `, options: [{ accessibility: 'explicit' }], }, { filename: 'test.ts', code: ` class Test { - protected name: string - protected foo?: string - getX () { - return this.x + protected name: string; + protected foo?: string; + getX() { + return this.x; } } - `, + `, options: [{ accessibility: 'no-public' }], }, { filename: 'test.ts', code: ` class Test { - name: string - foo?: string - getX () { - return this.x + name: string; + foo?: string; + getX() { + return this.x; } get fooName(): string { - return this.foo + ' ' + this.name + return this.foo + ' ' + this.name; } } - `, + `, options: [{ accessibility: 'no-public' }], }, { @@ -182,7 +182,7 @@ class Test { code: ` class Test { private x: number; - constructor (x: number) { + constructor(x: number) { this.x = x; } get internalValue() { @@ -191,7 +191,7 @@ class Test { private set internalValue(value: number) { this.x = value; } - public square (): number { + public square(): number { return this.x * this.x; } } @@ -203,7 +203,7 @@ class Test { code: ` class Test { private x: number; - public constructor (x: number) { + public constructor(x: number) { this.x = x; } public get internalValue() { @@ -212,10 +212,10 @@ class Test { public set internalValue(value: number) { this.x = value; } - public square (): number { + public square(): number { return this.x * this.x; } - half (): number { + half(): number { return this.x / 2; } } @@ -226,7 +226,7 @@ class Test { filename: 'test.ts', code: ` class Test { - constructor(private x: number){} + constructor(private x: number) {} } `, options: [{ accessibility: 'no-public' }], @@ -235,7 +235,7 @@ class Test { filename: 'test.ts', code: ` class Test { - constructor(public x: number){} + constructor(public x: number) {} } `, options: [ @@ -249,7 +249,7 @@ class Test { filename: 'test.ts', code: ` class Test { - constructor(public foo: number){} + constructor(public foo: number) {} } `, options: [{ accessibility: 'no-public' }], @@ -258,8 +258,8 @@ class Test { filename: 'test.ts', code: ` class Test { - public getX () { - return this.x + public getX() { + return this.x; } } `, @@ -269,8 +269,8 @@ class Test { filename: 'test.ts', code: ` class Test { - public static getX () { - return this.x + public static getX() { + return this.x; } } `, @@ -280,8 +280,8 @@ class Test { filename: 'test.ts', code: ` class Test { - get getX () { - return this.x + get getX() { + return this.x; } } `, @@ -291,8 +291,8 @@ class Test { filename: 'test.ts', code: ` class Test { - getX () { - return this.x + getX() { + return this.x; } } `, @@ -300,24 +300,38 @@ class Test { }, { filename: 'test.ts', - code: 'class Test { x = 2 }', + code: ` +class Test { + x = 2; +} + `, options: [{ overrides: { properties: 'off' } }], }, { filename: 'test.ts', - code: 'class Test { private x = 2 }', + code: ` +class Test { + private x = 2; +} + `, options: [{ overrides: { properties: 'explicit' } }], }, { filename: 'test.ts', - code: `class Test { - x = 2 - private x = 2 - }`, + code: ` +class Test { + x = 2; + private x = 2; +} + `, options: [{ overrides: { properties: 'no-public' } }], }, { - code: 'class Test { constructor(private { x }: any[]) { }}', + code: ` +class Test { + constructor(private { x }: any[]) {} +} + `, options: [{ accessibility: 'no-public' }], }, ], @@ -412,12 +426,12 @@ class Test { filename: 'test.ts', code: ` class Test { - x: number - public getX () { - return this.x + x: number; + public getX() { + return this.x; } } - `, + `, errors: [ { messageId: 'missingAccessibility', @@ -431,23 +445,23 @@ class Test { ], output: ` class Test { - x: number - public getX () { - return this.x + x: number; + public getX() { + return this.x; } } - `, + `, }, { filename: 'test.ts', code: ` class Test { - private x: number - getX () { - return this.x + private x: number; + getX() { + return this.x; } } - `, + `, errors: [ { messageId: 'missingAccessibility', @@ -461,23 +475,23 @@ class Test { ], output: ` class Test { - private x: number - getX () { - return this.x + private x: number; + getX() { + return this.x; } } - `, + `, }, { filename: 'test.ts', code: ` class Test { - x?: number - getX? () { - return this.x + x?: number; + getX?() { + return this.x; } } - `, + `, errors: [ { messageId: 'missingAccessibility', @@ -500,24 +514,24 @@ class Test { ], output: ` class Test { - x?: number - getX? () { - return this.x + x?: number; + getX?() { + return this.x; } } - `, + `, }, { filename: 'test.ts', code: ` class Test { - protected name: string - protected foo?: string - public getX () { - return this.x + protected name: string; + protected foo?: string; + public getX() { + return this.x; } } - `, + `, options: [{ accessibility: 'no-public' }], errors: [ { @@ -532,25 +546,25 @@ class Test { ], output: ` class Test { - protected name: string - protected foo?: string - getX () { - return this.x + protected name: string; + protected foo?: string; + getX() { + return this.x; } } - `, + `, }, { filename: 'test.ts', code: ` class Test { - protected name: string - public foo?: string - getX () { - return this.x + protected name: string; + public foo?: string; + getX() { + return this.x; } } - `, + `, options: [{ accessibility: 'no-public' }], errors: [ { @@ -565,24 +579,24 @@ class Test { ], output: ` class Test { - protected name: string - foo?: string - getX () { - return this.x + protected name: string; + foo?: string; + getX() { + return this.x; } } - `, + `, }, { filename: 'test.ts', code: ` class Test { - public x: number - public getX () { - return this.x + public x: number; + public getX() { + return this.x; } } - `, + `, errors: [ { messageId: 'unwantedPublicAccessibility', @@ -598,19 +612,19 @@ class Test { options: [{ accessibility: 'no-public' }], output: ` class Test { - x: number - getX () { - return this.x + x: number; + getX() { + return this.x; } } - `, + `, }, { filename: 'test.ts', code: ` class Test { private x: number; - constructor (x: number) { + constructor(x: number) { this.x = x; } get internalValue() { @@ -637,7 +651,7 @@ class Test { output: ` class Test { private x: number; - constructor (x: number) { + constructor(x: number) { this.x = x; } get internalValue() { @@ -654,7 +668,7 @@ class Test { code: ` class Test { private x: number; - constructor (x: number) { + constructor(x: number) { this.x = x; } get internalValue() { @@ -685,7 +699,7 @@ class Test { output: ` class Test { private x: number; - constructor (x: number) { + constructor(x: number) { this.x = x; } get internalValue() { @@ -701,7 +715,7 @@ class Test { filename: 'test.ts', code: ` class Test { - constructor(public x: number){} + constructor(public x: number) {} public foo(): string { return 'foo'; } @@ -721,7 +735,7 @@ class Test { ], output: ` class Test { - constructor(public x: number){} + constructor(public x: number) {} public foo(): string { return 'foo'; } @@ -732,7 +746,7 @@ class Test { filename: 'test.ts', code: ` class Test { - constructor(public x: number){} + constructor(public x: number) {} } `, errors: [ @@ -744,7 +758,7 @@ class Test { ], output: ` class Test { - constructor(public x: number){} + constructor(public x: number) {} } `, }, @@ -752,7 +766,7 @@ class Test { filename: 'test.ts', code: ` class Test { - constructor(public readonly x: number){} + constructor(public readonly x: number) {} } `, options: [ @@ -770,13 +784,17 @@ class Test { ], output: ` class Test { - constructor(readonly x: number){} + constructor(readonly x: number) {} } `, }, { filename: 'test.ts', - code: 'class Test { x = 2 }', + code: ` +class Test { + x = 2; +} + `, options: [ { accessibility: 'off', @@ -786,18 +804,24 @@ class Test { errors: [ { messageId: 'missingAccessibility', - line: 1, - column: 14, + line: 3, + column: 3, }, ], - output: 'class Test { x = 2 }', + output: ` +class Test { + x = 2; +} + `, }, { filename: 'test.ts', - code: `class Test { - public x = 2 - private x = 2 - }`, + code: ` +class Test { + public x = 2; + private x = 2; +} + `, options: [ { accessibility: 'off', @@ -807,30 +831,40 @@ class Test { errors: [ { messageId: 'unwantedPublicAccessibility', - line: 2, - column: 9, + line: 3, + column: 3, }, ], - output: `class Test { - x = 2 - private x = 2 - }`, + output: ` +class Test { + x = 2; + private x = 2; +} + `, }, { - code: 'class Test { constructor(public ...x: any[]) { }}', + code: ` +class Test { + constructor(public ...x: any[]) {} +} + `, options: [{ accessibility: 'explicit' }], errors: [ { messageId: 'missingAccessibility', - line: 1, - column: 14, + line: 3, + column: 3, }, ], - output: 'class Test { constructor(public ...x: any[]) { }}', + output: ` +class Test { + constructor(public ...x: any[]) {} +} + `, }, { filename: 'test.ts', - code: ` + code: noFormat` class Test { @public public /*public*/constructor(private foo: string) {} @@ -848,7 +882,7 @@ class Test { column: 3, }, ], - output: ` + output: noFormat` class Test { @public /*public*/constructor(private foo: string) {} @@ -914,7 +948,7 @@ class Test { filename: 'test.ts', code: ` class Test { - public foo = ""; + public foo = ''; } `, options: [ @@ -931,16 +965,16 @@ class Test { ], output: ` class Test { - foo = ""; + foo = ''; } `, }, { filename: 'test.ts', - code: ` + code: noFormat` class Test { - contructor(public/* Hi there */ readonly foo) + contructor(public/* Hi there */ readonly foo); } `, options: [ @@ -958,7 +992,7 @@ class Test { ], output: ` class Test { - contructor(/* Hi there */ readonly foo) + contructor(/* Hi there */ readonly foo); } `, }, @@ -966,7 +1000,7 @@ class Test { filename: 'test.ts', code: ` class Test { - contructor(public readonly foo: string) + contructor(public readonly foo: string); } `, options: [ @@ -983,33 +1017,17 @@ class Test { ], output: ` class Test { - contructor(readonly foo: string) + contructor(readonly foo: string); } `, }, { filename: 'test.ts', - code: - 'class EnsureWhiteSPaceSpan { public constructor() {}}', - options: [ - { - accessibility: 'no-public', - overrides: { parameterProperties: 'no-public' }, - }, - ], - errors: [ - { - messageId: 'unwantedPublicAccessibility', - line: 1, - column: 30, - }, - ], - output: 'class EnsureWhiteSPaceSpan { constructor() {}}', - }, - { - filename: 'test.ts', - code: - 'class EnsureWhiteSPaceSpan { public /* */ constructor() {}}', + code: ` +class EnsureWhiteSPaceSpan { + public constructor() {} +} + `, options: [ { accessibility: 'no-public', @@ -1019,17 +1037,23 @@ class Test { errors: [ { messageId: 'unwantedPublicAccessibility', - line: 1, - column: 30, + line: 3, + column: 3, }, ], - output: - 'class EnsureWhiteSPaceSpan { /* */ constructor() {}}', + output: ` +class EnsureWhiteSPaceSpan { + constructor() {} +} + `, }, { filename: 'test.ts', - code: - 'class EnsureWhiteSPaceSpan { public /* */ constructor() {}}', + code: ` +class EnsureWhiteSPaceSpan { + public /* */ constructor() {} +} + `, options: [ { accessibility: 'no-public', @@ -1039,11 +1063,15 @@ class Test { errors: [ { messageId: 'unwantedPublicAccessibility', - line: 1, - column: 30, + line: 3, + column: 3, }, ], - output: 'class EnsureWhiteSPaceSpan { /* */ constructor() {}}', + output: ` +class EnsureWhiteSPaceSpan { + /* */ constructor() {} +} + `, }, ], }); diff --git a/packages/eslint-plugin/tests/rules/explicit-module-boundary-types.test.ts b/packages/eslint-plugin/tests/rules/explicit-module-boundary-types.test.ts index 388adb99431..cdff53533b7 100644 --- a/packages/eslint-plugin/tests/rules/explicit-module-boundary-types.test.ts +++ b/packages/eslint-plugin/tests/rules/explicit-module-boundary-types.test.ts @@ -10,28 +10,28 @@ ruleTester.run('explicit-module-boundary-types', rule, { { code: ` function test(): void { - return; + return; } - `, + `, }, { code: ` export function test(): void { - return; + return; } - `, + `, }, { code: ` export var fn = function(): number { - return 1; + return 1; }; - `, + `, }, { code: ` export var arrowFn = (): string => 'test'; - `, + `, }, { code: ` @@ -46,7 +46,7 @@ class Test { } arrow = (): string => 'arrow'; } - `, + `, }, { code: ` @@ -61,42 +61,42 @@ export class Test { } arrow = (): string => 'arrow'; } - `, + `, }, { code: ` export function test(): void { - nested(); - return; + nested(); + return; - function nested() {} + function nested() {} } - `, + `, }, { code: ` export function test(): string { - const nested = () => 'value'; - return nested(); + const nested = () => 'value'; + return nested(); } - `, + `, }, { code: ` export function test(): string { - class Nested { - public method() { - return 'value'; - } + class Nested { + public method() { + return 'value'; } - return new Nested().method(); + } + return new Nested().method(); } - `, + `, }, { code: ` export var arrowFn: Foo = () => 'test'; - `, + `, options: [ { allowTypedFunctionExpressions: true, @@ -105,8 +105,10 @@ export var arrowFn: Foo = () => 'test'; }, { code: ` -export var funcExpr: Foo = function() { return 'test'; }; - `, +export var funcExpr: Foo = function() { + return 'test'; +}; + `, options: [ { allowTypedFunctionExpressions: true, @@ -114,18 +116,18 @@ export var funcExpr: Foo = function() { return 'test'; }; ], }, { - code: `const x = (() => {}) as Foo`, + code: 'const x = (() => {}) as Foo;', options: [{ allowTypedFunctionExpressions: true }], }, { - code: `const x = (() => {})`, + code: 'const x = (() => {});', options: [{ allowTypedFunctionExpressions: true }], }, { code: ` export const x = { foo: () => {}, -} as Foo +} as Foo; `, options: [{ allowTypedFunctionExpressions: true }], }, @@ -133,7 +135,7 @@ export const x = { code: ` export const x = { foo: () => {}, -} +}; `, options: [{ allowTypedFunctionExpressions: true }], }, @@ -141,7 +143,7 @@ export const x = { code: ` export const x: Foo = { foo: () => {}, -} +}; `, options: [{ allowTypedFunctionExpressions: true }], }, @@ -151,7 +153,7 @@ export const x: Foo = { type MethodType = () => void; export class App { - public method: MethodType = () => {} + public method: MethodType = () => {}; } `, options: [{ allowTypedFunctionExpressions: true }], @@ -169,37 +171,45 @@ export const myObj = { { code: ` export default () => (): void => {}; - `, + `, options: [{ allowHigherOrderFunctions: true }], }, { code: ` -export default () => function (): void {}; - `, +export default () => function(): void {}; + `, options: [{ allowHigherOrderFunctions: true }], }, { code: ` -export default () => { return (): void => {} }; - `, +export default () => { + return (): void => {}; +}; + `, options: [{ allowHigherOrderFunctions: true }], }, { code: ` -export default () => { return function (): void {} }; - `, +export default () => { + return function(): void {}; +}; + `, options: [{ allowHigherOrderFunctions: true }], }, { code: ` -export function fn() { return (): void => {} }; - `, +export function fn() { + return (): void => {}; +} + `, options: [{ allowHigherOrderFunctions: true }], }, { code: ` -export function fn() { return function (): void {} }; - `, +export function fn() { + return function(): void {}; +} + `, options: [{ allowHigherOrderFunctions: true }], }, { @@ -207,20 +217,26 @@ export function fn() { return function (): void {} }; export function FunctionDeclaration() { return function FunctionExpression_Within_FunctionDeclaration() { return function FunctionExpression_Within_FunctionExpression() { - return () => { // ArrowFunctionExpression_Within_FunctionExpression - return () => // ArrowFunctionExpression_Within_ArrowFunctionExpression - (): number => 1 // ArrowFunctionExpression_Within_ArrowFunctionExpression_WithNoBody - } - } - } + return () => { + // ArrowFunctionExpression_Within_FunctionExpression + return () => + // ArrowFunctionExpression_Within_ArrowFunctionExpression + (): number => 1; // ArrowFunctionExpression_Within_ArrowFunctionExpression_WithNoBody + }; + }; + }; } - `, + `, options: [{ allowHigherOrderFunctions: true }], }, { code: ` -export default () => () => { return (): void => { return; } }; - `, +export default () => () => { + return (): void => { + return; + }; +}; + `, options: [{ allowHigherOrderFunctions: true }], }, { @@ -243,9 +259,9 @@ new Accumulator().accumulate(() => 1); }, { code: ` -export const func1 = (value: number) => (({ type: "X", value }) as const); -export const func2 = (value: number) => ({ type: "X", value } as const); -export const func3 = (value: number) => (x as const); +export const func1 = (value: number) => ({ type: 'X', value } as const); +export const func2 = (value: number) => ({ type: 'X', value } as const); +export const func3 = (value: number) => x as const; export const func4 = (value: number) => x as const; `, options: [ @@ -257,7 +273,7 @@ export const func4 = (value: number) => x as const; { code: ` export const func1 = (value: string) => value; -export const func2 = (value: number) => ({ type: "X", value }); +export const func2 = (value: number) => ({ type: 'X', value }); `, options: [ { @@ -315,13 +331,8 @@ export class Test { { code: ` export const Foo: FC = () => ( -
{}} - b={function (e) {}} - c={function foo(e) {}} - > -
-) +
{}} b={function(e) {}} c={function foo(e) {}}>
+); `, parserOptions: { ecmaFeatures: { jsx: true }, @@ -329,13 +340,9 @@ export const Foo: FC = () => ( }, { code: ` -export const Foo: JSX.Element = -
{}} - b={function (e) {}} - c={function foo(e) {}} - > -
+export const Foo: JSX.Element = ( +
{}} b={function(e) {}} c={function foo(e) {}}>
+); `, parserOptions: { ecmaFeatures: { jsx: true }, @@ -345,10 +352,7 @@ export const Foo: JSX.Element = invalid: [ { code: ` -export function test( - a: number, - b: number, -) { +export function test(a: number, b: number) { return; } `, @@ -356,9 +360,9 @@ export function test( { messageId: 'missingReturnType', line: 2, - endLine: 5, + endLine: 2, column: 8, - endColumn: 2, + endColumn: 43, }, ], }, @@ -413,13 +417,13 @@ export var arrowFn = () => 'test'; export class Test { constructor() {} get prop() { - return 1; + return 1; } set prop(value) {} method() { return; } - arrow = (arg) => 'arrow'; + arrow = arg => 'arrow'; private method() { return; } @@ -452,14 +456,14 @@ export class Test { line: 11, endLine: 11, column: 11, - endColumn: 19, + endColumn: 17, }, { messageId: 'missingArgType', line: 11, endLine: 11, column: 11, - endColumn: 27, + endColumn: 25, }, ], }, @@ -467,11 +471,11 @@ export class Test { code: ` export class Foo { public a = () => {}; - public b = function () {}; + public b = function() {}; public c = function test() {}; static d = () => {}; - static e = function () {}; + static e = function() {}; } `, errors: [ @@ -487,7 +491,7 @@ export class Foo { line: 4, endLine: 4, column: 14, - endColumn: 25, + endColumn: 24, }, { messageId: 'missingReturnType', @@ -508,12 +512,12 @@ export class Foo { line: 8, endLine: 8, column: 14, - endColumn: 25, + endColumn: 24, }, ], }, { - code: 'export default () => true ? (() => {}) : ((): void => {});', + code: 'export default () => (true ? () => {} : (): void => {});', errors: [ { messageId: 'missingReturnType', @@ -545,20 +549,24 @@ export class Foo { ], }, { - code: "export var funcExpr = function() { return 'test'; };", + code: ` +export var funcExpr = function() { + return 'test'; +}; + `, options: [{ allowTypedFunctionExpressions: true }], errors: [ { messageId: 'missingReturnType', - line: 1, - endLine: 1, + line: 2, + endLine: 2, column: 23, endColumn: 33, }, ], }, { - code: 'export const x = (() => {}) as Foo', + code: 'export const x = (() => {}) as Foo;', options: [{ allowTypedFunctionExpressions: false }], errors: [ { @@ -575,7 +583,7 @@ export class Foo { interface Foo {} export const x = { foo: () => {}, -} as Foo +} as Foo; `, options: [{ allowTypedFunctionExpressions: false }], errors: [ @@ -593,7 +601,7 @@ export const x = { interface Foo {} export const x: Foo = { foo: () => {}, -} +}; `, options: [{ allowTypedFunctionExpressions: false }], errors: [ @@ -620,7 +628,7 @@ export const x: Foo = { ], }, { - code: 'export default () => function () {};', + code: 'export default () => function() {};', options: [{ allowHigherOrderFunctions: true }], errors: [ { @@ -628,59 +636,75 @@ export const x: Foo = { line: 1, endLine: 1, column: 22, - endColumn: 33, + endColumn: 32, }, ], }, { - code: 'export default () => { return () => {} };', + code: ` +export default () => { + return () => {}; +}; + `, options: [{ allowHigherOrderFunctions: true }], errors: [ { messageId: 'missingReturnType', - line: 1, - endLine: 1, - column: 31, - endColumn: 36, + line: 3, + endLine: 3, + column: 10, + endColumn: 15, }, ], }, { - code: 'export default () => { return function () {} };', + code: ` +export default () => { + return function() {}; +}; + `, options: [{ allowHigherOrderFunctions: true }], errors: [ { messageId: 'missingReturnType', - line: 1, - endLine: 1, - column: 31, - endColumn: 42, + line: 3, + endLine: 3, + column: 10, + endColumn: 20, }, ], }, { - code: 'export function fn() { return () => {} };', + code: ` +export function fn() { + return () => {}; +} + `, options: [{ allowHigherOrderFunctions: true }], errors: [ { messageId: 'missingReturnType', - line: 1, - endLine: 1, - column: 31, - endColumn: 36, + line: 3, + endLine: 3, + column: 10, + endColumn: 15, }, ], }, { - code: 'export function fn() { return function () {} };', + code: ` +export function fn() { + return function() {}; +} + `, options: [{ allowHigherOrderFunctions: true }], errors: [ { messageId: 'missingReturnType', - line: 1, - endLine: 1, - column: 31, - endColumn: 42, + line: 3, + endLine: 3, + column: 10, + endColumn: 20, }, ], }, @@ -689,40 +713,48 @@ export const x: Foo = { export function FunctionDeclaration() { return function FunctionExpression_Within_FunctionDeclaration() { return function FunctionExpression_Within_FunctionExpression() { - return () => { // ArrowFunctionExpression_Within_FunctionExpression - return () => // ArrowFunctionExpression_Within_ArrowFunctionExpression - () => 1 // ArrowFunctionExpression_Within_ArrowFunctionExpression_WithNoBody - } - } - } + return () => { + // ArrowFunctionExpression_Within_FunctionExpression + return () => + // ArrowFunctionExpression_Within_ArrowFunctionExpression + () => 1; // ArrowFunctionExpression_Within_ArrowFunctionExpression_WithNoBody + }; + }; + }; } - `, + `, options: [{ allowHigherOrderFunctions: true }], errors: [ { messageId: 'missingReturnType', - line: 7, - endLine: 7, + line: 9, + endLine: 9, column: 11, endColumn: 16, }, ], }, { - code: 'export default () => () => { return () => { return; } };', + code: ` +export default () => () => { + return () => { + return; + }; +}; + `, options: [{ allowHigherOrderFunctions: true }], errors: [ { messageId: 'missingReturnType', - line: 1, - endLine: 1, - column: 37, - endColumn: 42, + line: 3, + endLine: 3, + column: 10, + endColumn: 15, }, ], }, { - code: 'export default (() => true)()', + code: 'export default (() => true)();', options: [ { allowTypedFunctionExpressions: false, @@ -740,8 +772,8 @@ export function FunctionDeclaration() { }, { code: ` -export const func1 = (value: number) => ({ type: "X", value } as any); -export const func2 = (value: number) => ({ type: "X", value } as Action); +export const func1 = (value: number) => ({ type: 'X', value } as any); +export const func2 = (value: number) => ({ type: 'X', value } as Action); `, options: [ { @@ -767,7 +799,7 @@ export const func2 = (value: number) => ({ type: "X", value } as Action); }, { code: ` -export const func = (value: number) => ({ type: "X", value } as const); +export const func = (value: number) => ({ type: 'X', value } as const); `, options: [ { @@ -834,19 +866,23 @@ export const func2 = (value: number) => value; ], }, { - code: 'export function fn(test): string { return "123" };', + code: ` +export function fn(test): string { + return '123'; +} + `, errors: [ { messageId: 'missingArgType', - line: 1, - endLine: 1, + line: 2, + endLine: 4, column: 8, - endColumn: 50, + endColumn: 2, }, ], }, { - code: 'export const fn = (one: number, two): string => "123";', + code: "export const fn = (one: number, two): string => '123';", errors: [ { messageId: 'missingArgType', diff --git a/packages/eslint-plugin/tests/rules/func-call-spacing.test.ts b/packages/eslint-plugin/tests/rules/func-call-spacing.test.ts index e2250716a07..db884fd3cf1 100644 --- a/packages/eslint-plugin/tests/rules/func-call-spacing.test.ts +++ b/packages/eslint-plugin/tests/rules/func-call-spacing.test.ts @@ -1,3 +1,8 @@ +/* eslint-disable eslint-comments/no-use */ +// this rule tests the spacing, which prettier will want to fix and break the tests +/* eslint "@typescript-eslint/internal/plugin-test-formatting": ["error", { formatWithPrettier: false }] */ +/* eslint-enable eslint-comments/no-use */ + import { TSESLint } from '@typescript-eslint/experimental-utils'; import rule, { MessageIds, Options } from '../../src/rules/func-call-spacing'; import { RuleTester } from '../RuleTester'; @@ -180,7 +185,7 @@ ruleTester.run('func-call-spacing', rule, { this.cancelled.add(request) this.decrement(request) (request.reject(new api.Cancel())) - `, + `, output: null, // no change errors: [ { @@ -208,7 +213,7 @@ var a = foo code: ` var a = foo (baz()) - `, + `, output: null, // no change errors: [ { diff --git a/packages/eslint-plugin/tests/rules/generic-type-naming.test.ts b/packages/eslint-plugin/tests/rules/generic-type-naming.test.ts index 11f7d1618bb..da26e9675b6 100644 --- a/packages/eslint-plugin/tests/rules/generic-type-naming.test.ts +++ b/packages/eslint-plugin/tests/rules/generic-type-naming.test.ts @@ -7,35 +7,60 @@ const ruleTester = new RuleTester({ ruleTester.run('generic-type-naming', rule, { valid: [ - { code: 'class { }', options: [] }, - { code: 'type ReadOnly = {}', options: [] }, - { code: 'interface SimpleMap { }', options: [] }, - { code: 'function get() {}', options: [] }, - { code: 'interface GenericIdentityFn { (arg: T): T }', options: [] }, - { code: 'class { }', options: ['^x+$'] }, - { code: 'class { }', options: ['^[A-Z]$'] }, - { - code: 'class extends B implements Foo { }', + { + code: 'class {}', + options: [], + }, + { + code: 'type ReadOnly = {};', + options: [], + }, + { + code: 'interface SimpleMap {}', + options: [], + }, + { + code: 'function get() {}', + options: [], + }, + { + code: ` +interface GenericIdentityFn { + (arg: T): T; +} + `, + options: [], + }, + { + code: 'class {}', + options: ['^x+$'], + }, + { + code: 'class {}', + options: ['^[A-Z]$'], + }, + { + code: 'class extends B implements Foo {}', options: ['^[A-Z]$'], }, { code: ` class extends B implements Foo { - test () { - type Foo = Bar - } + test() { + type Foo = Bar; + } } - `, + `, options: ['^[A-Z]$'], }, { - code: 'class CounterContainer extends Container { }', + code: 'class CounterContainer extends Container {}', options: ['^T$'], }, ], invalid: [ { - code: 'class { }', + code: 'class {}', options: [], errors: [ { @@ -49,7 +74,7 @@ class extends B implements Foo { ], }, { - code: 'class { }', + code: 'class {}', options: ['^[A-Z]+$'], errors: [ { @@ -61,7 +86,7 @@ class extends B implements Foo { ], }, { - code: 'interface SimpleMap { }', + code: 'interface SimpleMap {}', options: ['^[A-Z]+$'], errors: [ { @@ -73,7 +98,7 @@ class extends B implements Foo { ], }, { - code: 'type R = {}', + code: 'type R = {};', options: ['^[A-Z]+$'], errors: [ { @@ -97,25 +122,29 @@ class extends B implements Foo { ], }, { - code: 'interface GenericIdentityFn { (arg: x): x }', + code: ` +interface GenericIdentityFn { + (arg: x): x; +} + `, options: ['^[A-Z]+$'], errors: [ { messageId: 'paramNotMatchRule', data: { name: 'x', rule: '^[A-Z]+$' }, - line: 1, - column: 32, + line: 3, + column: 4, }, ], }, { code: ` class extends B implements Foo { - test () { - type Foo = Bar - } + test() { + type Foo = Bar; + } } - `, + `, options: ['^[A-Z][0-9]$'], errors: [ { @@ -128,24 +157,24 @@ class extends B implements Foo { messageId: 'paramNotMatchRule', data: { name: 'Z', rule: '^[A-Z][0-9]$' }, line: 3, - column: 10, + column: 8, }, { messageId: 'paramNotMatchRule', data: { name: 'T', rule: '^[A-Z][0-9]$' }, line: 4, - column: 18, + column: 14, }, ], }, { code: ` abstract class extends B implements Foo { - test () { - type Foo = Bar - } + test() { + type Foo = Bar; + } } - `, + `, options: ['^[A-Z][0-9]$'], errors: [ { @@ -164,13 +193,13 @@ abstract class extends B implements Foo { messageId: 'paramNotMatchRule', data: { name: 'Z', rule: '^[A-Z][0-9]$' }, line: 3, - column: 10, + column: 8, }, { messageId: 'paramNotMatchRule', data: { name: 'T', rule: '^[A-Z][0-9]$' }, line: 4, - column: 18, + column: 14, }, ], }, diff --git a/packages/eslint-plugin/tests/rules/indent/indent-eslint.test.ts b/packages/eslint-plugin/tests/rules/indent/indent-eslint.test.ts index d726a5e2c95..a9dfda6b127 100644 --- a/packages/eslint-plugin/tests/rules/indent/indent-eslint.test.ts +++ b/packages/eslint-plugin/tests/rules/indent/indent-eslint.test.ts @@ -4,6 +4,11 @@ // NOTE - this test suite is intentionally kept in a separate file to our // custom tests. This is to keep a clear boundary between the two. +/* eslint-disable eslint-comments/no-use */ +// this rule tests the spacing, which prettier will want to fix and break the tests +/* eslint "@typescript-eslint/internal/plugin-test-formatting": ["error", { formatWithPrettier: false }] */ +/* eslint-enable eslint-comments/no-use */ + import { AST_TOKEN_TYPES, AST_NODE_TYPES, diff --git a/packages/eslint-plugin/tests/rules/indent/indent.test.ts b/packages/eslint-plugin/tests/rules/indent/indent.test.ts index fe88d3de17a..6cec699c053 100644 --- a/packages/eslint-plugin/tests/rules/indent/indent.test.ts +++ b/packages/eslint-plugin/tests/rules/indent/indent.test.ts @@ -1,3 +1,8 @@ +/* eslint-disable eslint-comments/no-use */ +// this rule tests the spacing, which prettier will want to fix and break the tests +/* eslint "@typescript-eslint/internal/plugin-test-formatting": ["error", { formatWithPrettier: false }] */ +/* eslint-enable eslint-comments/no-use */ + import { AST_NODE_TYPES, TSESLint, @@ -704,7 +709,7 @@ export default class App extends Vue return this.$store.state.errorHandler.error } } - `, + `, // https://github.com/eslint/typescript-eslint-parser/issues/474 ` /** @@ -713,7 +718,7 @@ export default class App extends Vue * @returns {string} */ function foo(name: string, age: number): string {} - `, + `, ` const firebaseApp = firebase.apps.length ? firebase.app() @@ -725,7 +730,7 @@ const firebaseApp = firebase.apps.length storageBucket: __FIREBASE_STORAGE_BUCKET__, messagingSenderId: __FIREBASE_MESSAGING_SENDER_ID__, }) - `, + `, // https://github.com/bradzacher/eslint-plugin-typescript/issues/271 { code: ` @@ -734,7 +739,7 @@ const foo = { b: 2 }, bar = 1; - `, + `, options: [4, { VariableDeclarator: { const: 3 } }], }, { @@ -744,7 +749,7 @@ const foo : Foo = { b: 2 }, bar = 1; - `, + `, options: [4, { VariableDeclarator: { const: 3 } }], }, { @@ -756,7 +761,7 @@ const name: string = ' Typescript ' greeting: string = (" Hello " + name) .toUpperCase() .trim(); - `, + `, options: [2, { VariableDeclarator: { const: 3 } }], }, { @@ -768,15 +773,15 @@ const div: JQuery = $('
') button: JQuery = $('