From d7dc108580cdcb9890ac0539e7223aedbff4a0ed Mon Sep 17 00:00:00 2001 From: Remco Haszing Date: Mon, 28 Sep 2020 20:22:29 +0200 Subject: [PATCH 1/4] feat(eslint-plugin): add `consistent-indexed-object-style` rule (#2401) --- packages/eslint-plugin/README.md | 1 + .../rules/consistent-indexed-object-style.md | 67 ++++++ packages/eslint-plugin/src/configs/all.ts | 1 + .../rules/consistent-indexed-object-style.ts | 122 +++++++++++ packages/eslint-plugin/src/rules/index.ts | 2 + .../consistent-indexed-object-style.test.ts | 200 ++++++++++++++++++ 6 files changed, 393 insertions(+) create mode 100644 packages/eslint-plugin/docs/rules/consistent-indexed-object-style.md create mode 100644 packages/eslint-plugin/src/rules/consistent-indexed-object-style.ts create mode 100644 packages/eslint-plugin/tests/rules/consistent-indexed-object-style.test.ts diff --git a/packages/eslint-plugin/README.md b/packages/eslint-plugin/README.md index 37a27ca9511f..e043549326aa 100644 --- a/packages/eslint-plugin/README.md +++ b/packages/eslint-plugin/README.md @@ -104,6 +104,7 @@ Pro Tip: For larger codebases you may want to consider splitting our linting int | [`@typescript-eslint/ban-tslint-comment`](./docs/rules/ban-tslint-comment.md) | Bans `// tslint:` comments from being used | | :wrench: | | | [`@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-indexed-object-style`](./docs/rules/consistent-indexed-object-style.md) | Enforce or disallow the use of the record type | | :wrench: | | | [`@typescript-eslint/consistent-type-assertions`](./docs/rules/consistent-type-assertions.md) | Enforces consistent usage of type assertions | | | | | [`@typescript-eslint/consistent-type-definitions`](./docs/rules/consistent-type-definitions.md) | Consistent with type definition either `interface` or `type` | | :wrench: | | | [`@typescript-eslint/consistent-type-imports`](./docs/rules/consistent-type-imports.md) | Enforces consistent usage of type imports | | :wrench: | | diff --git a/packages/eslint-plugin/docs/rules/consistent-indexed-object-style.md b/packages/eslint-plugin/docs/rules/consistent-indexed-object-style.md new file mode 100644 index 000000000000..9ed81aed7c22 --- /dev/null +++ b/packages/eslint-plugin/docs/rules/consistent-indexed-object-style.md @@ -0,0 +1,67 @@ +# Enforce or disallow the use of the record type (`consistent-indexed-object-style`) + +TypeScript supports defining object show keys can be flexible using an index signature. TypeScript also has a builtin type named `Record` to create an empty object defining only an index signature. For example, the following types are equal: + +```ts +interface Foo { + [key: string]: unknown; +} + +type Foo = { + [key: string]: unknown; +}; + +type Foo = Record; +``` + +## Options + +- `"record"`: Set to `"record"` to only allow the `Record` type. Set to `"index-signature"` to only allow index signatures. (Defaults to `"record"`) + +For example: + +```CJSON +{ + "@typescript-eslint/consistent-type-definitions": ["error", "index-signature"] +} +``` + +## Rule details + +This rule enforces a consistent way to define records. + +Examples of **incorrect** code with `record` option. + +```ts +interface Foo { + [key: string]: unknown; +} + +type Foo = { + [key: string]: unknown; +}; +``` + +Examples of **correct** code with `record` option. + +```ts +type Foo = Record; +``` + +Examples of **incorrect** code with `index-signature` option. + +```ts +type Foo = Record; +``` + +Examples of **correct** code with `index-signature` option. + +```ts +interface Foo { + [key: string]: unknown; +} + +type Foo = { + [key: string]: unknown; +}; +``` diff --git a/packages/eslint-plugin/src/configs/all.ts b/packages/eslint-plugin/src/configs/all.ts index 84b4e93fc90c..1de48a81f6e9 100644 --- a/packages/eslint-plugin/src/configs/all.ts +++ b/packages/eslint-plugin/src/configs/all.ts @@ -16,6 +16,7 @@ export = { '@typescript-eslint/class-literal-property-style': 'error', 'comma-spacing': 'off', '@typescript-eslint/comma-spacing': 'error', + '@typescript-eslint/consistent-indexed-object-style': 'error', '@typescript-eslint/consistent-type-assertions': 'error', '@typescript-eslint/consistent-type-definitions': 'error', '@typescript-eslint/consistent-type-imports': 'error', diff --git a/packages/eslint-plugin/src/rules/consistent-indexed-object-style.ts b/packages/eslint-plugin/src/rules/consistent-indexed-object-style.ts new file mode 100644 index 000000000000..021f5038770d --- /dev/null +++ b/packages/eslint-plugin/src/rules/consistent-indexed-object-style.ts @@ -0,0 +1,122 @@ +import { createRule } from '../util'; +import { + AST_NODE_TYPES, + TSESTree, +} from '@typescript-eslint/experimental-utils'; + +type MessageIds = 'preferRecord' | 'preferIndexSignature'; +type Options = ['record' | 'index-signature']; + +export default createRule({ + name: 'consistent-indexed-object-style', + meta: { + type: 'suggestion', + docs: { + description: 'Enforce or disallow the use of the record type', + category: 'Stylistic Issues', + // too opinionated to be recommended + recommended: false, + }, + messages: { + preferRecord: 'A record is preferred over an index signature', + preferIndexSignature: 'An index signature is preferred over a record.', + }, + fixable: 'code', + schema: [ + { + enum: ['record', 'index-signature'], + }, + ], + }, + defaultOptions: ['record'], + create(context) { + const sourceCode = context.getSourceCode(); + + if (context.options[0] === 'index-signature') { + return { + TSTypeReference(node): void { + const typeName = node.typeName; + if (typeName.type !== AST_NODE_TYPES.Identifier) { + return; + } + if (typeName.name !== 'Record') { + return; + } + + const params = node.typeParameters?.params; + if (params?.length !== 2) { + return; + } + + context.report({ + node, + messageId: 'preferIndexSignature', + fix(fixer) { + const key = sourceCode.getText(params[0]); + const type = sourceCode.getText(params[1]); + return fixer.replaceText(node, `{ [key: ${key}]: ${type} }`); + }, + }); + }, + }; + } + + function checkMembers( + members: TSESTree.TypeElement[], + node: TSESTree.Node, + prefix: string, + postfix: string, + ): void { + if (members.length !== 1) { + return; + } + const [member] = members; + + if (member.type !== AST_NODE_TYPES.TSIndexSignature) { + return; + } + + const [parameter] = member.parameters; + + if (!parameter) { + return; + } + + if (parameter.type !== AST_NODE_TYPES.Identifier) { + return; + } + const keyType = parameter.typeAnnotation; + if (!keyType) { + return; + } + + const valueType = member.typeAnnotation; + if (!valueType) { + return; + } + + context.report({ + node, + messageId: 'preferRecord', + fix(fixer) { + const key = sourceCode.getText(keyType.typeAnnotation); + const value = sourceCode.getText(valueType.typeAnnotation); + return fixer.replaceText( + node, + `${prefix}Record<${key}, ${value}>${postfix}`, + ); + }, + }); + } + + return { + TSTypeLiteral(node): void { + checkMembers(node.members, node, '', ''); + }, + + TSInterfaceDeclaration(node): void { + checkMembers(node.body.body, node, `type ${node.id.name} = `, ';'); + }, + }; + }, +}); diff --git a/packages/eslint-plugin/src/rules/index.ts b/packages/eslint-plugin/src/rules/index.ts index 8b5049e25022..10d849873a05 100644 --- a/packages/eslint-plugin/src/rules/index.ts +++ b/packages/eslint-plugin/src/rules/index.ts @@ -9,6 +9,7 @@ import classLiteralPropertyStyle from './class-literal-property-style'; import commaDangle from './comma-dangle'; import commaSpacing from './comma-spacing'; import confusingNonNullAssertionLikeNotEqual from './no-confusing-non-null-assertion'; +import consistentIndexedObjectStyle from './consistent-indexed-object-style'; import consistentTypeAssertions from './consistent-type-assertions'; import consistentTypeDefinitions from './consistent-type-definitions'; import consistentTypeImports from './consistent-type-imports'; @@ -117,6 +118,7 @@ export default { 'class-literal-property-style': classLiteralPropertyStyle, 'comma-dangle': commaDangle, 'comma-spacing': commaSpacing, + 'consistent-indexed-object-style': consistentIndexedObjectStyle, 'consistent-type-assertions': consistentTypeAssertions, 'consistent-type-definitions': consistentTypeDefinitions, 'consistent-type-imports': consistentTypeImports, diff --git a/packages/eslint-plugin/tests/rules/consistent-indexed-object-style.test.ts b/packages/eslint-plugin/tests/rules/consistent-indexed-object-style.test.ts new file mode 100644 index 000000000000..328f0cbf5dd5 --- /dev/null +++ b/packages/eslint-plugin/tests/rules/consistent-indexed-object-style.test.ts @@ -0,0 +1,200 @@ +import rule from '../../src/rules/consistent-indexed-object-style'; +import { RuleTester } from '../RuleTester'; + +const ruleTester = new RuleTester({ + parser: '@typescript-eslint/parser', +}); + +ruleTester.run('consistent-indexed-object-style', rule, { + valid: [ + // 'record' (default) + // Record + 'type Foo = Record;', + + // Interface + 'interface Foo {}', + ` +interface Foo { + bar: string; +} + `, + ` +interface Foo { + bar: string; + [key: string]: any; +} + `, + ` +interface Foo { + [key: string]: any; + bar: string; +} + `, + + // Type literal + 'type Foo = {};', + ` +type Foo = { + bar: string; + [key: string]: any; +}; + `, + ` +type Foo = { + bar: string; +}; + `, + ` +type Foo = { + [key: string]: any; + bar: string; +}; + `, + + // Generic + ` +type Foo = Generic<{ + [key: string]: any; + bar: string; +}>; + `, + + // Function types + 'function foo(arg: { [key: string]: any; bar: string }) {}', + 'function foo(): { [key: string]: any; bar: string } {}', + + // Invalid syntax allowed by the parser + 'type Foo = { [key: string] };', + 'type Foo = { [] };', + ` +interface Foo { + [key: string]; +} + `, + ` +interface Foo { + []; +} + `, + + // 'index-signature' + // Unhandled type + { + code: 'type Foo = Misc;', + options: ['index-signature'], + }, + + // Invalid record + { + code: 'type Foo = Record;', + options: ['index-signature'], + }, + { + code: 'type Foo = Record;', + options: ['index-signature'], + }, + { + code: 'type Foo = Record;', + options: ['index-signature'], + }, + + // Type literal + { + code: 'type Foo = { [key: string]: any };', + options: ['index-signature'], + }, + + // Generic + { + code: 'type Foo = Generic<{ [key: string]: any }>;', + options: ['index-signature'], + }, + + // Function types + { + code: 'function foo(arg: { [key: string]: any }) {}', + options: ['index-signature'], + }, + { + code: 'function foo(): { [key: string]: any } {}', + options: ['index-signature'], + }, + + // Namespace + { + code: 'type T = A.B;', + options: ['index-signature'], + }, + ], + invalid: [ + // Interface + { + code: ` +interface Foo { + [key: string]: any; +} + `, + output: ` +type Foo = Record; + `, + errors: [{ messageId: 'preferRecord', line: 2, column: 1 }], + }, + + // Type literal + { + code: 'type Foo = { [key: string]: any };', + output: 'type Foo = Record;', + errors: [{ messageId: 'preferRecord', line: 1, column: 12 }], + }, + + // Generic + { + code: 'type Foo = Generic<{ [key: string]: any }>;', + output: 'type Foo = Generic>;', + errors: [{ messageId: 'preferRecord', line: 1, column: 20 }], + }, + + // Function types + { + code: 'function foo(arg: { [key: string]: any }) {}', + output: 'function foo(arg: Record) {}', + errors: [{ messageId: 'preferRecord', line: 1, column: 19 }], + }, + { + code: 'function foo(): { [key: string]: any } {}', + output: 'function foo(): Record {}', + errors: [{ messageId: 'preferRecord', line: 1, column: 17 }], + }, + + // Never + // Type literal + { + code: 'type Foo = Record;', + options: ['index-signature'], + output: 'type Foo = { [key: string]: any };', + errors: [{ messageId: 'preferIndexSignature', line: 1, column: 12 }], + }, + + // Generic + { + code: 'type Foo = Generic>;', + options: ['index-signature'], + output: 'type Foo = Generic<{ [key: string]: any }>;', + errors: [{ messageId: 'preferIndexSignature', line: 1, column: 20 }], + }, + + // Function types + { + code: 'function foo(arg: Record) {}', + options: ['index-signature'], + output: 'function foo(arg: { [key: string]: any }) {}', + errors: [{ messageId: 'preferIndexSignature', line: 1, column: 19 }], + }, + { + code: 'function foo(): Record {}', + options: ['index-signature'], + output: 'function foo(): { [key: string]: any } {}', + errors: [{ messageId: 'preferIndexSignature', line: 1, column: 17 }], + }, + ], +}); From d72951aca52c0b8bfff6eed0629161a04759cdc9 Mon Sep 17 00:00:00 2001 From: Scott Hutcheson Date: Fri, 2 Oct 2020 11:44:44 +0100 Subject: [PATCH 2/4] docs(eslint-plugin): fix plugin name in config example (#2625) --- .../eslint-plugin/docs/rules/consistent-indexed-object-style.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/eslint-plugin/docs/rules/consistent-indexed-object-style.md b/packages/eslint-plugin/docs/rules/consistent-indexed-object-style.md index 9ed81aed7c22..c98997c5e0a6 100644 --- a/packages/eslint-plugin/docs/rules/consistent-indexed-object-style.md +++ b/packages/eslint-plugin/docs/rules/consistent-indexed-object-style.md @@ -22,7 +22,7 @@ For example: ```CJSON { - "@typescript-eslint/consistent-type-definitions": ["error", "index-signature"] + "@typescript-eslint/consistent-indexed-object-style": ["error", "index-signature"] } ``` From 498f397ff3898dde631f37311615b555f38a414e Mon Sep 17 00:00:00 2001 From: YeonJuan Date: Sun, 4 Oct 2020 18:01:16 +0900 Subject: [PATCH 3/4] feat(eslint-plugin): add extension rule `no-duplicate-imports` (#2609) --- packages/eslint-plugin/README.md | 1 + .../docs/rules/no-duplicate-imports.md | 22 +++ packages/eslint-plugin/src/configs/all.ts | 6 +- packages/eslint-plugin/src/rules/index.ts | 2 + .../src/rules/no-duplicate-imports.ts | 118 ++++++++++++++ .../tests/rules/no-duplicate-imports.test.ts | 153 ++++++++++++++++++ .../eslint-plugin/typings/eslint-rules.d.ts | 26 +++ 7 files changed, 326 insertions(+), 2 deletions(-) create mode 100644 packages/eslint-plugin/docs/rules/no-duplicate-imports.md create mode 100644 packages/eslint-plugin/src/rules/no-duplicate-imports.ts create mode 100644 packages/eslint-plugin/tests/rules/no-duplicate-imports.test.ts diff --git a/packages/eslint-plugin/README.md b/packages/eslint-plugin/README.md index e043549326aa..45d6ec7652cc 100644 --- a/packages/eslint-plugin/README.md +++ b/packages/eslint-plugin/README.md @@ -200,6 +200,7 @@ In these cases, we create what we call an extension rule; a rule within our plug | [`@typescript-eslint/lines-between-class-members`](./docs/rules/lines-between-class-members.md) | Require or disallow an empty line between class members | | :wrench: | | | [`@typescript-eslint/no-array-constructor`](./docs/rules/no-array-constructor.md) | Disallow generic `Array` constructors | :heavy_check_mark: | :wrench: | | | [`@typescript-eslint/no-dupe-class-members`](./docs/rules/no-dupe-class-members.md) | Disallow duplicate class members | | | | +| [`@typescript-eslint/no-duplicate-imports`](./docs/rules/no-duplicate-imports.md) | Disallow duplicate imports | | | | | [`@typescript-eslint/no-empty-function`](./docs/rules/no-empty-function.md) | Disallow empty functions | :heavy_check_mark: | | | | [`@typescript-eslint/no-extra-parens`](./docs/rules/no-extra-parens.md) | Disallow unnecessary parentheses | | :wrench: | | | [`@typescript-eslint/no-extra-semi`](./docs/rules/no-extra-semi.md) | Disallow unnecessary semicolons | :heavy_check_mark: | :wrench: | | diff --git a/packages/eslint-plugin/docs/rules/no-duplicate-imports.md b/packages/eslint-plugin/docs/rules/no-duplicate-imports.md new file mode 100644 index 000000000000..140a8a1ece14 --- /dev/null +++ b/packages/eslint-plugin/docs/rules/no-duplicate-imports.md @@ -0,0 +1,22 @@ +# Disallow duplicate imports (`no-duplicate-imports`) + +## Rule Details + +This rule extends the base [`eslint/no-duplicate-imports`](https://eslint.org/docs/rules/no-duplicate-imports) rule. +This version adds support for type-only import and export. + +## How to use + +```jsonc +{ + // note you must disable the base rule as it can report incorrect errors + "no-duplicate-imports": "off", + "@typescript-eslint/no-duplicate-imports": ["error"] +} +``` + +## Options + +See [`eslint/no-duplicate-imports` options](https://eslint.org/docs/rules/no-duplicate-imports#options). + +Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/no-duplicate-imports.md) diff --git a/packages/eslint-plugin/src/configs/all.ts b/packages/eslint-plugin/src/configs/all.ts index 1de48a81f6e9..ec2984064982 100644 --- a/packages/eslint-plugin/src/configs/all.ts +++ b/packages/eslint-plugin/src/configs/all.ts @@ -14,6 +14,8 @@ export = { 'brace-style': 'off', '@typescript-eslint/brace-style': 'error', '@typescript-eslint/class-literal-property-style': 'error', + 'comma-dangle': 'off', + '@typescript-eslint/comma-dangle': 'error', 'comma-spacing': 'off', '@typescript-eslint/comma-spacing': 'error', '@typescript-eslint/consistent-indexed-object-style': 'error', @@ -47,6 +49,8 @@ export = { '@typescript-eslint/no-confusing-non-null-assertion': 'error', 'no-dupe-class-members': 'off', '@typescript-eslint/no-dupe-class-members': 'error', + 'no-duplicate-imports': 'off', + '@typescript-eslint/no-duplicate-imports': 'error', '@typescript-eslint/no-dynamic-delete': 'error', 'no-empty-function': 'off', '@typescript-eslint/no-empty-function': 'error', @@ -140,7 +144,5 @@ export = { '@typescript-eslint/typedef': 'error', '@typescript-eslint/unbound-method': 'error', '@typescript-eslint/unified-signatures': 'error', - 'comma-dangle': 'off', - '@typescript-eslint/comma-dangle': 'error', }, }; diff --git a/packages/eslint-plugin/src/rules/index.ts b/packages/eslint-plugin/src/rules/index.ts index 10d849873a05..587b79d30178 100644 --- a/packages/eslint-plugin/src/rules/index.ts +++ b/packages/eslint-plugin/src/rules/index.ts @@ -106,6 +106,7 @@ import typeAnnotationSpacing from './type-annotation-spacing'; import typedef from './typedef'; import unboundMethod from './unbound-method'; import unifiedSignatures from './unified-signatures'; +import noDuplicateImports from './no-duplicate-imports'; export default { 'adjacent-overload-signatures': adjacentOverloadSignatures, @@ -212,6 +213,7 @@ export default { 'type-annotation-spacing': typeAnnotationSpacing, 'unbound-method': unboundMethod, 'unified-signatures': unifiedSignatures, + 'no-duplicate-imports': noDuplicateImports, indent: indent, quotes: quotes, semi: semi, diff --git a/packages/eslint-plugin/src/rules/no-duplicate-imports.ts b/packages/eslint-plugin/src/rules/no-duplicate-imports.ts new file mode 100644 index 000000000000..d539845746f7 --- /dev/null +++ b/packages/eslint-plugin/src/rules/no-duplicate-imports.ts @@ -0,0 +1,118 @@ +import { + AST_NODE_TYPES, + TSESTree, +} from '@typescript-eslint/experimental-utils'; +import baseRule from 'eslint/lib/rules/no-duplicate-imports'; +import * as util from '../util'; + +type Options = util.InferOptionsTypeFromRule; +type MessageIds = util.InferMessageIdsTypeFromRule; + +export default util.createRule({ + name: 'no-duplicate-imports', + meta: { + type: 'problem', + docs: { + description: 'Disallow duplicate imports', + category: 'Best Practices', + recommended: false, + extendsBaseRule: true, + }, + schema: baseRule.meta.schema, + messages: { + ...baseRule.meta.messages, + importType: '{{module}} type import is duplicated', + importTypeAs: '{{module}} type import is duplicated as type export', + exportType: '{{module}} type export is duplicated', + exportTypeAs: '{{module}} type export is duplicated as type import', + }, + }, + defaultOptions: [ + { + includeExports: false, + }, + ], + create(context, [option]) { + const rules = baseRule.create(context); + const includeExports = option.includeExports; + const typeImports = new Set(); + const typeExports = new Set(); + + function report( + messageId: MessageIds, + node: TSESTree.Node, + module: string, + ): void { + context.report({ + messageId, + node, + data: { + module, + }, + }); + } + + function isStringLiteral( + node: TSESTree.Node | null, + ): node is TSESTree.StringLiteral { + return ( + !!node && + node.type === AST_NODE_TYPES.Literal && + typeof node.value === 'string' + ); + } + + function checkTypeImport(node: TSESTree.ImportDeclaration): void { + if (isStringLiteral(node.source)) { + const value = node.source.value; + if (typeImports.has(value)) { + report('importType', node, value); + } + if (includeExports && typeExports.has(value)) { + report('importTypeAs', node, value); + } + typeImports.add(value); + } + } + + function checkTypeExport( + node: TSESTree.ExportNamedDeclaration | TSESTree.ExportAllDeclaration, + ): void { + if (isStringLiteral(node.source)) { + const value = node.source.value; + if (typeExports.has(value)) { + report('exportType', node, value); + } + if (typeImports.has(value)) { + report('exportTypeAs', node, value); + } + typeExports.add(value); + } + } + + return { + ...rules, + ImportDeclaration(node): void { + if (node.importKind === 'type') { + checkTypeImport(node); + return; + } + rules.ImportDeclaration(node); + }, + ExportNamedDeclaration(node): void { + if (includeExports && node.exportKind === 'type') { + checkTypeExport(node); + return; + } + rules.ExportNamedDeclaration?.(node); + }, + ExportAllDeclaration(node): void { + if (includeExports && node.exportKind === 'type') { + checkTypeExport(node); + return; + } + rules.ExportAllDeclaration?.(node); + }, + }; + }, +}); diff --git a/packages/eslint-plugin/tests/rules/no-duplicate-imports.test.ts b/packages/eslint-plugin/tests/rules/no-duplicate-imports.test.ts new file mode 100644 index 000000000000..d133a7aadd9e --- /dev/null +++ b/packages/eslint-plugin/tests/rules/no-duplicate-imports.test.ts @@ -0,0 +1,153 @@ +import rule from '../../src/rules/no-duplicate-imports'; +import { RuleTester } from '../RuleTester'; + +const ruleTester = new RuleTester({ + parser: '@typescript-eslint/parser', +}); + +ruleTester.run('no-dupe-class-members', rule, { + valid: [ + { + code: "import type foo from 'foo';", + }, + { + code: "import type { foo } from 'foo';", + }, + { + code: ` + import foo from 'foo'; + import type bar from 'foo'; + `, + }, + { + code: ` + import { foo } from 'foo'; + import type { bar } from 'foo'; + `, + }, + { + code: ` + import type { foo } from 'foo'; + export type foo = foo; + `, + }, + { + code: ` + import type { foo } from 'foo'; + export type { foo }; + `, + }, + { + code: ` + export { foo } from 'foo'; + export type { foo } from 'foo'; + `, + }, + { + code: ` + export type * as foo from 'foo'; + export type * as bar from 'foo'; + `, + }, + { + code: ` + import type { bar } from 'foo'; + export type { foo } from 'foo'; + `, + }, + { + code: ` + import type { foo } from 'foo'; + export type { bar } from 'bar'; + `, + options: [{ includeExports: true }], + }, + { + code: ` + import type { foo } from 'foo'; + export type { bar }; + `, + options: [{ includeExports: true }], + }, + ], + invalid: [ + { + code: ` + import type foo from 'foo'; + import type bar from 'foo'; + `, + errors: [ + { + messageId: 'importType', + data: { + module: 'foo', + }, + }, + ], + }, + { + code: ` + import type { foo } from 'foo'; + import type { bar } from 'foo'; + `, + errors: [{ messageId: 'importType' }], + }, + { + code: ` + export type { foo } from 'foo'; + import type { bar } from 'foo'; + `, + options: [{ includeExports: true }], + errors: [{ messageId: 'importTypeAs' }], + }, + { + code: ` + import type foo from 'foo'; + export type * from 'foo'; + `, + options: [{ includeExports: true }], + errors: [{ messageId: 'exportTypeAs' }], + }, + { + code: ` + import type { foo } from 'foo'; + export type { foo } from 'foo'; + `, + options: [{ includeExports: true }], + errors: [{ messageId: 'exportTypeAs' }], + }, + { + code: ` + export type * as foo from 'foo'; + export type * as bar from 'foo'; + `, + options: [{ includeExports: true }], + errors: [{ messageId: 'exportType' }], + }, + + // check base rule + { + code: ` + import foo from 'foo'; + import bar from 'foo'; + `, + errors: [{ messageId: 'import' }], + }, + { + code: ` + import foo from 'foo'; + export * from 'foo'; + `, + options: [{ includeExports: true }], + errors: [{ messageId: 'exportAs' }], + }, + { + code: ` + import foo from 'foo'; + export { foo } from 'foo'; + `, + options: [{ includeExports: true }], + errors: [{ messageId: 'exportAs' }], + }, + ], +}); diff --git a/packages/eslint-plugin/typings/eslint-rules.d.ts b/packages/eslint-plugin/typings/eslint-rules.d.ts index 39d49db7328e..d11c5589b384 100644 --- a/packages/eslint-plugin/typings/eslint-rules.d.ts +++ b/packages/eslint-plugin/typings/eslint-rules.d.ts @@ -762,3 +762,29 @@ declare module 'eslint/lib/rules/comma-dangle' { >; export = rule; } + +declare module 'eslint/lib/rules/no-duplicate-imports' { + import { TSESLint, TSESTree } from '@typescript-eslint/experimental-utils'; + + const rule: TSESLint.RuleModule< + | 'import' + | 'importAs' + | 'export' + | 'exportAs' + | 'importType' + | 'importTypeAs' + | 'exportType' + | 'exportTypeAs', + [ + { + includeExports?: boolean; + }, + ], + { + ImportDeclaration(node: TSESTree.ImportDeclaration): void; + ExportNamedDeclaration?(node: TSESTree.ExportNamedDeclaration): void; + ExportAllDeclaration?(node: TSESTree.ExportAllDeclaration): void; + } + >; + export = rule; +} From ef7dfb6836f5dd95a7a716068993ba3d880e8fdc Mon Sep 17 00:00:00 2001 From: James Henry Date: Mon, 5 Oct 2020 17:02:35 +0000 Subject: [PATCH 4/4] chore: publish v4.4.0 --- CHANGELOG.md | 12 ++++++++++++ lerna.json | 2 +- packages/eslint-plugin-internal/CHANGELOG.md | 8 ++++++++ packages/eslint-plugin-internal/package.json | 4 ++-- packages/eslint-plugin-tslint/CHANGELOG.md | 8 ++++++++ packages/eslint-plugin-tslint/package.json | 6 +++--- packages/eslint-plugin/CHANGELOG.md | 12 ++++++++++++ packages/eslint-plugin/package.json | 6 +++--- packages/experimental-utils/CHANGELOG.md | 8 ++++++++ packages/experimental-utils/package.json | 8 ++++---- packages/parser/CHANGELOG.md | 8 ++++++++ packages/parser/package.json | 12 ++++++------ packages/scope-manager/CHANGELOG.md | 8 ++++++++ packages/scope-manager/package.json | 8 ++++---- packages/shared-fixtures/CHANGELOG.md | 8 ++++++++ packages/shared-fixtures/package.json | 2 +- packages/types/CHANGELOG.md | 8 ++++++++ packages/types/package.json | 2 +- packages/typescript-estree/CHANGELOG.md | 8 ++++++++ packages/typescript-estree/package.json | 8 ++++---- packages/visitor-keys/CHANGELOG.md | 8 ++++++++ packages/visitor-keys/package.json | 4 ++-- 22 files changed, 127 insertions(+), 31 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 504afb47d025..ec588f1fdaa9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,18 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [4.4.0](https://github.com/typescript-eslint/typescript-eslint/compare/v4.3.0...v4.4.0) (2020-10-05) + + +### Features + +* **eslint-plugin:** add `consistent-indexed-object-style` rule ([#2401](https://github.com/typescript-eslint/typescript-eslint/issues/2401)) ([d7dc108](https://github.com/typescript-eslint/typescript-eslint/commit/d7dc108580cdcb9890ac0539e7223aedbff4a0ed)) +* **eslint-plugin:** add extension rule `no-duplicate-imports` ([#2609](https://github.com/typescript-eslint/typescript-eslint/issues/2609)) ([498f397](https://github.com/typescript-eslint/typescript-eslint/commit/498f397ff3898dde631f37311615b555f38a414e)) + + + + + # [4.3.0](https://github.com/typescript-eslint/typescript-eslint/compare/v4.2.0...v4.3.0) (2020-09-28) diff --git a/lerna.json b/lerna.json index cdce5135a1dd..687365c98036 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "4.3.0", + "version": "4.4.0", "npmClient": "yarn", "useWorkspaces": true, "stream": true diff --git a/packages/eslint-plugin-internal/CHANGELOG.md b/packages/eslint-plugin-internal/CHANGELOG.md index b547950da658..e28fc1b2b222 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. +# [4.4.0](https://github.com/typescript-eslint/typescript-eslint/compare/v4.3.0...v4.4.0) (2020-10-05) + +**Note:** Version bump only for package @typescript-eslint/eslint-plugin-internal + + + + + # [4.3.0](https://github.com/typescript-eslint/typescript-eslint/compare/v4.2.0...v4.3.0) (2020-09-28) **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 665d964e8e71..9058481419ce 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": "4.3.0", + "version": "4.4.0", "private": true, "main": "dist/index.js", "scripts": { @@ -14,7 +14,7 @@ }, "dependencies": { "@types/prettier": "*", - "@typescript-eslint/experimental-utils": "4.3.0", + "@typescript-eslint/experimental-utils": "4.4.0", "prettier": "*" } } diff --git a/packages/eslint-plugin-tslint/CHANGELOG.md b/packages/eslint-plugin-tslint/CHANGELOG.md index bb34688171a6..c14f19f69019 100644 --- a/packages/eslint-plugin-tslint/CHANGELOG.md +++ b/packages/eslint-plugin-tslint/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. +# [4.4.0](https://github.com/typescript-eslint/typescript-eslint/compare/v4.3.0...v4.4.0) (2020-10-05) + +**Note:** Version bump only for package @typescript-eslint/eslint-plugin-tslint + + + + + # [4.3.0](https://github.com/typescript-eslint/typescript-eslint/compare/v4.2.0...v4.3.0) (2020-09-28) **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 9ea12ed75d99..d15ec2ace7ed 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": "4.3.0", + "version": "4.4.0", "main": "dist/index.js", "typings": "src/index.ts", "description": "TSLint wrapper plugin for ESLint", @@ -38,7 +38,7 @@ "typecheck": "tsc -p tsconfig.json --noEmit" }, "dependencies": { - "@typescript-eslint/experimental-utils": "4.3.0", + "@typescript-eslint/experimental-utils": "4.4.0", "lodash": "^4.17.15" }, "peerDependencies": { @@ -48,6 +48,6 @@ }, "devDependencies": { "@types/lodash": "*", - "@typescript-eslint/parser": "4.3.0" + "@typescript-eslint/parser": "4.4.0" } } diff --git a/packages/eslint-plugin/CHANGELOG.md b/packages/eslint-plugin/CHANGELOG.md index eb0a0c0db51d..122bd696318c 100644 --- a/packages/eslint-plugin/CHANGELOG.md +++ b/packages/eslint-plugin/CHANGELOG.md @@ -3,6 +3,18 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [4.4.0](https://github.com/typescript-eslint/typescript-eslint/compare/v4.3.0...v4.4.0) (2020-10-05) + + +### Features + +* **eslint-plugin:** add `consistent-indexed-object-style` rule ([#2401](https://github.com/typescript-eslint/typescript-eslint/issues/2401)) ([d7dc108](https://github.com/typescript-eslint/typescript-eslint/commit/d7dc108580cdcb9890ac0539e7223aedbff4a0ed)) +* **eslint-plugin:** add extension rule `no-duplicate-imports` ([#2609](https://github.com/typescript-eslint/typescript-eslint/issues/2609)) ([498f397](https://github.com/typescript-eslint/typescript-eslint/commit/498f397ff3898dde631f37311615b555f38a414e)) + + + + + # [4.3.0](https://github.com/typescript-eslint/typescript-eslint/compare/v4.2.0...v4.3.0) (2020-09-28) diff --git a/packages/eslint-plugin/package.json b/packages/eslint-plugin/package.json index e9e1d0cd38be..53de6f9fefaa 100644 --- a/packages/eslint-plugin/package.json +++ b/packages/eslint-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/eslint-plugin", - "version": "4.3.0", + "version": "4.4.0", "description": "TypeScript plugin for ESLint", "keywords": [ "eslint", @@ -42,8 +42,8 @@ "typecheck": "tsc -p tsconfig.json --noEmit" }, "dependencies": { - "@typescript-eslint/experimental-utils": "4.3.0", - "@typescript-eslint/scope-manager": "4.3.0", + "@typescript-eslint/experimental-utils": "4.4.0", + "@typescript-eslint/scope-manager": "4.4.0", "debug": "^4.1.1", "functional-red-black-tree": "^1.0.1", "regexpp": "^3.0.0", diff --git a/packages/experimental-utils/CHANGELOG.md b/packages/experimental-utils/CHANGELOG.md index 88a8ced7b0ce..91cd1ad40a0c 100644 --- a/packages/experimental-utils/CHANGELOG.md +++ b/packages/experimental-utils/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. +# [4.4.0](https://github.com/typescript-eslint/typescript-eslint/compare/v4.3.0...v4.4.0) (2020-10-05) + +**Note:** Version bump only for package @typescript-eslint/experimental-utils + + + + + # [4.3.0](https://github.com/typescript-eslint/typescript-eslint/compare/v4.2.0...v4.3.0) (2020-09-28) diff --git a/packages/experimental-utils/package.json b/packages/experimental-utils/package.json index 4ac8648c8aa8..e91a2baf3f23 100644 --- a/packages/experimental-utils/package.json +++ b/packages/experimental-utils/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/experimental-utils", - "version": "4.3.0", + "version": "4.4.0", "description": "(Experimental) Utilities for working with TypeScript + ESLint together", "keywords": [ "eslint", @@ -40,9 +40,9 @@ }, "dependencies": { "@types/json-schema": "^7.0.3", - "@typescript-eslint/scope-manager": "4.3.0", - "@typescript-eslint/types": "4.3.0", - "@typescript-eslint/typescript-estree": "4.3.0", + "@typescript-eslint/scope-manager": "4.4.0", + "@typescript-eslint/types": "4.4.0", + "@typescript-eslint/typescript-estree": "4.4.0", "eslint-scope": "^5.0.0", "eslint-utils": "^2.0.0" }, diff --git a/packages/parser/CHANGELOG.md b/packages/parser/CHANGELOG.md index 4b413595271f..1592b404d7f9 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. +# [4.4.0](https://github.com/typescript-eslint/typescript-eslint/compare/v4.3.0...v4.4.0) (2020-10-05) + +**Note:** Version bump only for package @typescript-eslint/parser + + + + + # [4.3.0](https://github.com/typescript-eslint/typescript-eslint/compare/v4.2.0...v4.3.0) (2020-09-28) **Note:** Version bump only for package @typescript-eslint/parser diff --git a/packages/parser/package.json b/packages/parser/package.json index 683cdd04db55..7b383e493fdf 100644 --- a/packages/parser/package.json +++ b/packages/parser/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/parser", - "version": "4.3.0", + "version": "4.4.0", "description": "An ESLint custom parser which leverages TypeScript ESTree", "main": "dist/index.js", "types": "dist/index.d.ts", @@ -44,15 +44,15 @@ "eslint": "^5.0.0 || ^6.0.0 || ^7.0.0" }, "dependencies": { - "@typescript-eslint/scope-manager": "4.3.0", - "@typescript-eslint/types": "4.3.0", - "@typescript-eslint/typescript-estree": "4.3.0", + "@typescript-eslint/scope-manager": "4.4.0", + "@typescript-eslint/types": "4.4.0", + "@typescript-eslint/typescript-estree": "4.4.0", "debug": "^4.1.1" }, "devDependencies": { "@types/glob": "*", - "@typescript-eslint/experimental-utils": "4.3.0", - "@typescript-eslint/shared-fixtures": "4.3.0", + "@typescript-eslint/experimental-utils": "4.4.0", + "@typescript-eslint/shared-fixtures": "4.4.0", "glob": "*", "typescript": "*" }, diff --git a/packages/scope-manager/CHANGELOG.md b/packages/scope-manager/CHANGELOG.md index d68103fe5761..755f9b9a0920 100644 --- a/packages/scope-manager/CHANGELOG.md +++ b/packages/scope-manager/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. +# [4.4.0](https://github.com/typescript-eslint/typescript-eslint/compare/v4.3.0...v4.4.0) (2020-10-05) + +**Note:** Version bump only for package @typescript-eslint/scope-manager + + + + + # [4.3.0](https://github.com/typescript-eslint/typescript-eslint/compare/v4.2.0...v4.3.0) (2020-09-28) **Note:** Version bump only for package @typescript-eslint/scope-manager diff --git a/packages/scope-manager/package.json b/packages/scope-manager/package.json index 84cb81967c91..2296fc74278b 100644 --- a/packages/scope-manager/package.json +++ b/packages/scope-manager/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/scope-manager", - "version": "4.3.0", + "version": "4.4.0", "description": "TypeScript scope analyser for ESLint", "keywords": [ "eslint", @@ -39,12 +39,12 @@ "typecheck": "tsc -p tsconfig.json --noEmit" }, "dependencies": { - "@typescript-eslint/types": "4.3.0", - "@typescript-eslint/visitor-keys": "4.3.0" + "@typescript-eslint/types": "4.4.0", + "@typescript-eslint/visitor-keys": "4.4.0" }, "devDependencies": { "@types/glob": "*", - "@typescript-eslint/typescript-estree": "4.3.0", + "@typescript-eslint/typescript-estree": "4.4.0", "glob": "*", "jest-specific-snapshot": "*", "make-dir": "*", diff --git a/packages/shared-fixtures/CHANGELOG.md b/packages/shared-fixtures/CHANGELOG.md index c11ce5fdef69..c008c2d632d5 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. +# [4.4.0](https://github.com/typescript-eslint/typescript-eslint/compare/v4.3.0...v4.4.0) (2020-10-05) + +**Note:** Version bump only for package @typescript-eslint/shared-fixtures + + + + + # [4.3.0](https://github.com/typescript-eslint/typescript-eslint/compare/v4.2.0...v4.3.0) (2020-09-28) **Note:** Version bump only for package @typescript-eslint/shared-fixtures diff --git a/packages/shared-fixtures/package.json b/packages/shared-fixtures/package.json index de66b604889b..492a942cbada 100644 --- a/packages/shared-fixtures/package.json +++ b/packages/shared-fixtures/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/shared-fixtures", - "version": "4.3.0", + "version": "4.4.0", "private": true, "scripts": { "build": "tsc -b tsconfig.build.json", diff --git a/packages/types/CHANGELOG.md b/packages/types/CHANGELOG.md index a9206bf533de..aa694c4b7f12 100644 --- a/packages/types/CHANGELOG.md +++ b/packages/types/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. +# [4.4.0](https://github.com/typescript-eslint/typescript-eslint/compare/v4.3.0...v4.4.0) (2020-10-05) + +**Note:** Version bump only for package @typescript-eslint/types + + + + + # [4.3.0](https://github.com/typescript-eslint/typescript-eslint/compare/v4.2.0...v4.3.0) (2020-09-28) **Note:** Version bump only for package @typescript-eslint/types diff --git a/packages/types/package.json b/packages/types/package.json index 7aaca2739b79..41d6b014b96a 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/types", - "version": "4.3.0", + "version": "4.4.0", "description": "Types for the TypeScript-ESTree AST spec", "keywords": [ "eslint", diff --git a/packages/typescript-estree/CHANGELOG.md b/packages/typescript-estree/CHANGELOG.md index 0a5a33b1c563..6d2608b31a16 100644 --- a/packages/typescript-estree/CHANGELOG.md +++ b/packages/typescript-estree/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. +# [4.4.0](https://github.com/typescript-eslint/typescript-eslint/compare/v4.3.0...v4.4.0) (2020-10-05) + +**Note:** Version bump only for package @typescript-eslint/typescript-estree + + + + + # [4.3.0](https://github.com/typescript-eslint/typescript-eslint/compare/v4.2.0...v4.3.0) (2020-09-28) **Note:** Version bump only for package @typescript-eslint/typescript-estree diff --git a/packages/typescript-estree/package.json b/packages/typescript-estree/package.json index 69f10c2f5e9c..869183a5d000 100644 --- a/packages/typescript-estree/package.json +++ b/packages/typescript-estree/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/typescript-estree", - "version": "4.3.0", + "version": "4.4.0", "description": "A parser that converts TypeScript source code into an ESTree compatible form", "main": "dist/index.js", "types": "dist/index.d.ts", @@ -41,8 +41,8 @@ "typecheck": "tsc -p tsconfig.json --noEmit" }, "dependencies": { - "@typescript-eslint/types": "4.3.0", - "@typescript-eslint/visitor-keys": "4.3.0", + "@typescript-eslint/types": "4.4.0", + "@typescript-eslint/visitor-keys": "4.4.0", "debug": "^4.1.1", "globby": "^11.0.1", "is-glob": "^4.0.1", @@ -61,7 +61,7 @@ "@types/lodash": "*", "@types/semver": "^7.1.0", "@types/tmp": "^0.2.0", - "@typescript-eslint/shared-fixtures": "4.3.0", + "@typescript-eslint/shared-fixtures": "4.4.0", "glob": "*", "jest-specific-snapshot": "*", "make-dir": "*", diff --git a/packages/visitor-keys/CHANGELOG.md b/packages/visitor-keys/CHANGELOG.md index 4ef4cf67f1be..fa0eb27682ce 100644 --- a/packages/visitor-keys/CHANGELOG.md +++ b/packages/visitor-keys/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. +# [4.4.0](https://github.com/typescript-eslint/typescript-eslint/compare/v4.3.0...v4.4.0) (2020-10-05) + +**Note:** Version bump only for package @typescript-eslint/visitor-keys + + + + + # [4.3.0](https://github.com/typescript-eslint/typescript-eslint/compare/v4.2.0...v4.3.0) (2020-09-28) **Note:** Version bump only for package @typescript-eslint/visitor-keys diff --git a/packages/visitor-keys/package.json b/packages/visitor-keys/package.json index 792e9cc10fef..1efdff1f83bd 100644 --- a/packages/visitor-keys/package.json +++ b/packages/visitor-keys/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/visitor-keys", - "version": "4.3.0", + "version": "4.4.0", "description": "Visitor keys used to help traverse the TypeScript-ESTree AST", "keywords": [ "eslint", @@ -38,7 +38,7 @@ "typecheck": "tsc -p tsconfig.json --noEmit" }, "dependencies": { - "@typescript-eslint/types": "4.3.0", + "@typescript-eslint/types": "4.4.0", "eslint-visitor-keys": "^2.0.0" }, "devDependencies": {