From da45a94f0eb3faef11ea25592663724ee8cee947 Mon Sep 17 00:00:00 2001 From: bob1983 Date: Sat, 25 Jul 2020 22:17:35 +0900 Subject: [PATCH 01/11] feat(eslint-plugin): add prefer-enum-initializers rule --- packages/eslint-plugin/README.md | 1 + packages/eslint-plugin/src/rules/index.ts | 2 + .../src/rules/prefer-enum-initializers.ts | 42 ++++++ .../rules/prefer-enum-initializers.test.ts | 133 ++++++++++++++++++ 4 files changed, 178 insertions(+) create mode 100644 packages/eslint-plugin/src/rules/prefer-enum-initializers.ts create mode 100644 packages/eslint-plugin/tests/rules/prefer-enum-initializers.test.ts diff --git a/packages/eslint-plugin/README.md b/packages/eslint-plugin/README.md index b3360692178..cb8a5546142 100644 --- a/packages/eslint-plugin/README.md +++ b/packages/eslint-plugin/README.md @@ -147,6 +147,7 @@ Pro Tip: For larger codebases you may want to consider splitting our linting int | [`@typescript-eslint/no-unused-vars-experimental`](./docs/rules/no-unused-vars-experimental.md) | Disallow unused variables and arguments | | | | | [`@typescript-eslint/no-var-requires`](./docs/rules/no-var-requires.md) | Disallows the use of require statements except in import statements | :heavy_check_mark: | | | | [`@typescript-eslint/prefer-as-const`](./docs/rules/prefer-as-const.md) | Prefer usage of `as const` over literal type | :heavy_check_mark: | :wrench: | | +| [`@typescript-eslint/prefer-enum-initializers`](./docs/rules/prefer-enum-initializers.md) | Prefer initializing each enums member value | | | | | [`@typescript-eslint/prefer-for-of`](./docs/rules/prefer-for-of.md) | Prefer a ‘for-of’ loop over a standard ‘for’ loop if the index is only used to access the array being iterated | | | | | [`@typescript-eslint/prefer-function-type`](./docs/rules/prefer-function-type.md) | Use function types instead of interfaces with call signatures | | :wrench: | | | [`@typescript-eslint/prefer-includes`](./docs/rules/prefer-includes.md) | Enforce `includes` method over `indexOf` method | | :wrench: | :thought_balloon: | diff --git a/packages/eslint-plugin/src/rules/index.ts b/packages/eslint-plugin/src/rules/index.ts index 73083bedfe4..85d0642fc79 100644 --- a/packages/eslint-plugin/src/rules/index.ts +++ b/packages/eslint-plugin/src/rules/index.ts @@ -70,6 +70,7 @@ import noUseBeforeDefine from './no-use-before-define'; import noUselessConstructor from './no-useless-constructor'; import noVarRequires from './no-var-requires'; import preferAsConst from './prefer-as-const'; +import preferEnumInitializers from './prefer-enum-initializers'; import preferForOf from './prefer-for-of'; import preferFunctionType from './prefer-function-type'; import preferIncludes from './prefer-includes'; @@ -169,6 +170,7 @@ export default { 'no-useless-constructor': noUselessConstructor, 'no-var-requires': noVarRequires, 'prefer-as-const': preferAsConst, + 'prefer-enum-initializers': preferEnumInitializers, 'prefer-for-of': preferForOf, 'prefer-function-type': preferFunctionType, 'prefer-includes': preferIncludes, diff --git a/packages/eslint-plugin/src/rules/prefer-enum-initializers.ts b/packages/eslint-plugin/src/rules/prefer-enum-initializers.ts new file mode 100644 index 00000000000..7c4bf9efad6 --- /dev/null +++ b/packages/eslint-plugin/src/rules/prefer-enum-initializers.ts @@ -0,0 +1,42 @@ +import { TSESTree } from '@typescript-eslint/experimental-utils'; +import * as util from '../util'; + +export default util.createRule({ + name: 'prefer-enum-initializers', + meta: { + type: 'suggestion', + docs: { + description: 'Prefer initializing each enums member value', + category: 'Best Practices', + recommended: false, + }, + messages: { + defineInitializer: + "The value of the member '{{ name }}' should be explicitly defined", + }, + schema: [], + }, + defaultOptions: [], + create(context) { + const sourceCode = context.getSourceCode(); + + return { + TSEnumDeclaration(node: TSESTree.TSEnumDeclaration): void { + const { members } = node; + const violatingMembers = members.filter(member => { + return member.initializer == null; + }); + + for (const member of violatingMembers) { + context.report({ + node: member, + messageId: 'defineInitializer', + data: { + name: sourceCode.getText(member), + }, + }); + } + }, + }; + }, +}); diff --git a/packages/eslint-plugin/tests/rules/prefer-enum-initializers.test.ts b/packages/eslint-plugin/tests/rules/prefer-enum-initializers.test.ts new file mode 100644 index 00000000000..b78d25000bf --- /dev/null +++ b/packages/eslint-plugin/tests/rules/prefer-enum-initializers.test.ts @@ -0,0 +1,133 @@ +import rule from '../../src/rules/prefer-enum-initializers'; +import { RuleTester } from '../RuleTester'; + +const ruleTester = new RuleTester({ + parser: '@typescript-eslint/parser', +}); + +ruleTester.run('prefer-enum-initializers', rule, { + valid: [ + ` +enum Direction {} + `, + ` +enum Direction { + Up = 1, +} + `, + ` +enum Direction { + Up = 1, + Down = 2, +} + `, + ` +enum Direction { + Up = 'Up', + Down = 'Down', +} + `, + ` +enum Direction { + Up = 1, + Down = 'Up', +} + `, + ], + invalid: [ + { + code: ` +enum Direction { + Up, +} + `, + errors: [ + { + messageId: 'defineInitializer', + data: { name: 'Up' }, + line: 3, + }, + ], + }, + { + code: ` +enum Direction { + Up, + Down, +} + `, + errors: [ + { + messageId: 'defineInitializer', + data: { name: 'Up' }, + line: 3, + }, + { + messageId: 'defineInitializer', + data: { name: 'Down' }, + line: 4, + }, + ], + }, + { + code: ` +enum Direction { + Up = 1, + Down, +} + `, + errors: [ + { + messageId: 'defineInitializer', + data: { name: 'Down' }, + line: 4, + }, + ], + }, + { + code: ` +enum Direction { + Up, + Down = 2, +} + `, + errors: [ + { + messageId: 'defineInitializer', + data: { name: 'Up' }, + line: 3, + }, + ], + }, + { + code: ` +enum Direction { + Up = 'Up', + Down, +} + `, + errors: [ + { + messageId: 'defineInitializer', + data: { name: 'Down' }, + line: 4, + }, + ], + }, + { + code: ` +enum Direction { + Up, + Down = 'Down', +} + `, + errors: [ + { + messageId: 'defineInitializer', + data: { name: 'Up' }, + line: 3, + }, + ], + }, + ], +}); From 5e50f09267eac6e61b3c4cf11ac90c6ab778582c Mon Sep 17 00:00:00 2001 From: bob1983 Date: Sat, 25 Jul 2020 23:25:21 +0900 Subject: [PATCH 02/11] feat(eslint-plugin): add prefer-enum-initializers doc --- .../docs/rules/prefer-enum-initializers.md | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 packages/eslint-plugin/docs/rules/prefer-enum-initializers.md diff --git a/packages/eslint-plugin/docs/rules/prefer-enum-initializers.md b/packages/eslint-plugin/docs/rules/prefer-enum-initializers.md new file mode 100644 index 00000000000..1e8f568d721 --- /dev/null +++ b/packages/eslint-plugin/docs/rules/prefer-enum-initializers.md @@ -0,0 +1,70 @@ +# Prefer initializing each enums member value (`prefer-enum-initializers`) + +This rule recommends having each `enum`s member value explicitly initialized. + +`enum`s are a practical way to organize semantically related constant values. However, by implicitly defining values, `enum`s can lead to unexpected bugs if it's modified without paying attention to the order of its items. + +## Rule Details + +`enum`s infers sequential numbers automatically when initializers are omitted: + +```ts +enum Status { + Open, // infer 0 + Closed, // infer 1 +} +``` + +If a new member is added to the top of `Status`, both `Open` and `Closed` would have its values altered: + +```ts +enum Status { + Pending, // infer 0 + Open, // infer 1 + Closed, // infer 2 +} +``` + +Examples of **incorrect** code for this rule: + +```ts +enum Status { + Open = 1, + Close, +} + +enum Direction { + Up, + Down, +} + +enum Color { + Red, + Green = 'Green' + Blue = 'Blue', +} +``` + +Examples of **correct** code for this rule: + +```ts +enum Status { + Open = 'Open', + Close = 'Close', +} + +enum Direction { + Up = 1, + Down = 2, +} + +enum Color { + Red = 'Red', + Green = 'Green', + Blue = 'Blue', +} +``` + +## When Not To Use It + +If you don't care about `enum`s having implicit values you can safely disable this rule. From f7acc75cf9934b8bff58d4f718cb2254ffc747a7 Mon Sep 17 00:00:00 2001 From: bob1983 Date: Sat, 25 Jul 2020 23:47:36 +0900 Subject: [PATCH 03/11] feat(eslint-plugin): run yarn run generate:configs --- packages/eslint-plugin/src/configs/all.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/eslint-plugin/src/configs/all.ts b/packages/eslint-plugin/src/configs/all.ts index e78feb7d44a..8361bde29b2 100644 --- a/packages/eslint-plugin/src/configs/all.ts +++ b/packages/eslint-plugin/src/configs/all.ts @@ -97,6 +97,7 @@ export = { '@typescript-eslint/no-useless-constructor': 'error', '@typescript-eslint/no-var-requires': 'error', '@typescript-eslint/prefer-as-const': 'error', + '@typescript-eslint/prefer-enum-initializers': 'error', '@typescript-eslint/prefer-for-of': 'error', '@typescript-eslint/prefer-function-type': 'error', '@typescript-eslint/prefer-includes': 'error', From a964e523879ff37a801ae54abd30886eadf6ac46 Mon Sep 17 00:00:00 2001 From: bob1983 Date: Wed, 29 Jul 2020 00:21:22 +0900 Subject: [PATCH 04/11] feat(eslint-plugin): provide suggestion --- .../src/rules/prefer-enum-initializers.ts | 59 ++++++++++++++----- 1 file changed, 45 insertions(+), 14 deletions(-) diff --git a/packages/eslint-plugin/src/rules/prefer-enum-initializers.ts b/packages/eslint-plugin/src/rules/prefer-enum-initializers.ts index 7c4bf9efad6..27f5fbaa9c8 100644 --- a/packages/eslint-plugin/src/rules/prefer-enum-initializers.ts +++ b/packages/eslint-plugin/src/rules/prefer-enum-initializers.ts @@ -1,7 +1,11 @@ import { TSESTree } from '@typescript-eslint/experimental-utils'; import * as util from '../util'; +import { RuleFix } from '@typescript-eslint/experimental-utils/dist/ts-eslint'; -export default util.createRule({ +type Option = '0-based' | '1-based' | 'key-name'; +type MessageIds = 'defineInitializer' | 'defineInitializerSuggestion'; + +export default util.createRule({ name: 'prefer-enum-initializers', meta: { type: 'suggestion', @@ -9,33 +13,60 @@ export default util.createRule({ description: 'Prefer initializing each enums member value', category: 'Best Practices', recommended: false, + suggestion: true, }, messages: { defineInitializer: "The value of the member '{{ name }}' should be explicitly defined", + defineInitializerSuggestion: + 'can be suggestion fixed to {{ name }} = {{ suggestedValue }}', }, - schema: [], + schema: [ + { + enum: ['0-based', '1-based', 'key-name'], + }, + ], }, defaultOptions: [], create(context) { const sourceCode = context.getSourceCode(); + const config = context.options[0]; return { TSEnumDeclaration(node: TSESTree.TSEnumDeclaration): void { const { members } = node; - const violatingMembers = members.filter(member => { - return member.initializer == null; + members.forEach((member, index) => { + if (member.initializer == null) { + const name = sourceCode.getText(member); + context.report({ + node: member, + messageId: 'defineInitializer', + data: { + name, + }, + suggest: [ + { + messageId: 'defineInitializerSuggestion', + data: { name }, + fix: (fixer): RuleFix | null => { + if (config === '0-based') { + return fixer.replaceText(member, `${name} = ${index}`); + } else if (config === '1-based') { + return fixer.replaceText( + member, + `${name} = ${index + 1}`, + ); + } else if (config === 'key-name') { + return fixer.replaceText(member, `${name} = '${name}'`); + } else { + return null; + } + }, + }, + ], + }); + } }); - - for (const member of violatingMembers) { - context.report({ - node: member, - messageId: 'defineInitializer', - data: { - name: sourceCode.getText(member), - }, - }); - } }, }; }, From f060e775c0fb4cc489f07e30d7ef1fb53da7f40f Mon Sep 17 00:00:00 2001 From: bob1983 Date: Wed, 29 Jul 2020 00:24:31 +0900 Subject: [PATCH 05/11] feat(eslint-plugin): fix tests --- .../rules/prefer-enum-initializers.test.ts | 117 +++++++++++++++--- 1 file changed, 98 insertions(+), 19 deletions(-) diff --git a/packages/eslint-plugin/tests/rules/prefer-enum-initializers.test.ts b/packages/eslint-plugin/tests/rules/prefer-enum-initializers.test.ts index b78d25000bf..1744ade4032 100644 --- a/packages/eslint-plugin/tests/rules/prefer-enum-initializers.test.ts +++ b/packages/eslint-plugin/tests/rules/prefer-enum-initializers.test.ts @@ -25,12 +25,6 @@ enum Direction { enum Direction { Up = 'Up', Down = 'Down', -} - `, - ` -enum Direction { - Up = 1, - Down = 'Up', } `, ], @@ -41,11 +35,22 @@ enum Direction { Up, } `, + options: ['key-name'], errors: [ { messageId: 'defineInitializer', data: { name: 'Up' }, line: 3, + suggestions: [ + { + messageId: 'defineInitializerSuggestion', + output: ` +enum Direction { + Up = 'Up', +} + `, + }, + ], }, ], }, @@ -56,31 +61,64 @@ enum Direction { Down, } `, + options: ['key-name'], errors: [ { messageId: 'defineInitializer', data: { name: 'Up' }, line: 3, + suggestions: [ + { + messageId: 'defineInitializerSuggestion', + output: ` +enum Direction { + Up = 'Up', + Down, +} + `, + }, + ], }, { messageId: 'defineInitializer', data: { name: 'Down' }, line: 4, + suggestions: [ + { + messageId: 'defineInitializerSuggestion', + output: ` +enum Direction { + Up, + Down = 'Down', +} + `, + }, + ], }, ], }, { code: ` enum Direction { - Up = 1, - Down, + Up, } `, + options: ['0-based'], errors: [ { messageId: 'defineInitializer', - data: { name: 'Down' }, - line: 4, + data: { name: 'Up' }, + line: 3, + suggestions: [ + { + messageId: 'defineInitializerSuggestion', + output: ` +enum Direction { + Up = 0, +} + `, + }, + ], }, ], }, @@ -88,29 +126,42 @@ enum Direction { code: ` enum Direction { Up, - Down = 2, + Down, } `, + options: ['0-based'], errors: [ { messageId: 'defineInitializer', data: { name: 'Up' }, line: 3, - }, - ], - }, - { - code: ` + suggestions: [ + { + messageId: 'defineInitializerSuggestion', + output: ` enum Direction { - Up = 'Up', + Up = 0, Down, } `, - errors: [ + }, + ], + }, { messageId: 'defineInitializer', data: { name: 'Down' }, line: 4, + suggestions: [ + { + messageId: 'defineInitializerSuggestion', + output: ` +enum Direction { + Up, + Down = 1, +} + `, + }, + ], }, ], }, @@ -118,14 +169,42 @@ enum Direction { code: ` enum Direction { Up, - Down = 'Down', + Down, } `, + options: ['1-based'], errors: [ { messageId: 'defineInitializer', data: { name: 'Up' }, line: 3, + suggestions: [ + { + messageId: 'defineInitializerSuggestion', + output: ` +enum Direction { + Up = 1, + Down, +} + `, + }, + ], + }, + { + messageId: 'defineInitializer', + data: { name: 'Down' }, + line: 4, + suggestions: [ + { + messageId: 'defineInitializerSuggestion', + output: ` +enum Direction { + Up, + Down = 2, +} + `, + }, + ], }, ], }, From 9edb2b2a5e775ba92acbb5826123fdd7a2b3dcda Mon Sep 17 00:00:00 2001 From: bob1983 Date: Wed, 29 Jul 2020 00:25:03 +0900 Subject: [PATCH 06/11] feat(eslint-plugin): suppress eslint violations --- .../tests/rules/prefer-enum-initializers.test.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/packages/eslint-plugin/tests/rules/prefer-enum-initializers.test.ts b/packages/eslint-plugin/tests/rules/prefer-enum-initializers.test.ts index 1744ade4032..d3639ad5591 100644 --- a/packages/eslint-plugin/tests/rules/prefer-enum-initializers.test.ts +++ b/packages/eslint-plugin/tests/rules/prefer-enum-initializers.test.ts @@ -44,11 +44,13 @@ enum Direction { suggestions: [ { messageId: 'defineInitializerSuggestion', + /* eslint-disable @typescript-eslint/internal/plugin-test-formatting */ output: ` enum Direction { Up = 'Up', } `, + /* eslint-enable @typescript-eslint/internal/plugin-test-formatting */ }, ], }, @@ -70,12 +72,14 @@ enum Direction { suggestions: [ { messageId: 'defineInitializerSuggestion', + /* eslint-disable @typescript-eslint/internal/plugin-test-formatting */ output: ` enum Direction { Up = 'Up', Down, } `, + /* eslint-enable @typescript-eslint/internal/plugin-test-formatting */ }, ], }, @@ -86,12 +90,14 @@ enum Direction { suggestions: [ { messageId: 'defineInitializerSuggestion', + /* eslint-disable @typescript-eslint/internal/plugin-test-formatting */ output: ` enum Direction { Up, Down = 'Down', } `, + /* eslint-enable @typescript-eslint/internal/plugin-test-formatting */ }, ], }, @@ -112,11 +118,13 @@ enum Direction { suggestions: [ { messageId: 'defineInitializerSuggestion', + /* eslint-disable @typescript-eslint/internal/plugin-test-formatting */ output: ` enum Direction { Up = 0, } `, + /* eslint-enable @typescript-eslint/internal/plugin-test-formatting */ }, ], }, @@ -138,12 +146,14 @@ enum Direction { suggestions: [ { messageId: 'defineInitializerSuggestion', + /* eslint-disable @typescript-eslint/internal/plugin-test-formatting */ output: ` enum Direction { Up = 0, Down, } `, + /* eslint-enable @typescript-eslint/internal/plugin-test-formatting */ }, ], }, @@ -154,12 +164,14 @@ enum Direction { suggestions: [ { messageId: 'defineInitializerSuggestion', + /* eslint-disable @typescript-eslint/internal/plugin-test-formatting */ output: ` enum Direction { Up, Down = 1, } `, + /* eslint-enable @typescript-eslint/internal/plugin-test-formatting */ }, ], }, @@ -181,12 +193,14 @@ enum Direction { suggestions: [ { messageId: 'defineInitializerSuggestion', + /* eslint-disable @typescript-eslint/internal/plugin-test-formatting */ output: ` enum Direction { Up = 1, Down, } `, + /* eslint-enable @typescript-eslint/internal/plugin-test-formatting */ }, ], }, @@ -197,12 +211,14 @@ enum Direction { suggestions: [ { messageId: 'defineInitializerSuggestion', + /* eslint-disable @typescript-eslint/internal/plugin-test-formatting */ output: ` enum Direction { Up, Down = 2, } `, + /* eslint-enable @typescript-eslint/internal/plugin-test-formatting */ }, ], }, From 52f08ec3e304062b60c17f06d3dadef366cd155f Mon Sep 17 00:00:00 2001 From: bob1983 Date: Wed, 29 Jul 2020 01:01:45 +0900 Subject: [PATCH 07/11] feat(eslint-plugin): use exhaustive switch --- .../src/rules/prefer-enum-initializers.ts | 69 +++++++++++-------- 1 file changed, 39 insertions(+), 30 deletions(-) diff --git a/packages/eslint-plugin/src/rules/prefer-enum-initializers.ts b/packages/eslint-plugin/src/rules/prefer-enum-initializers.ts index 27f5fbaa9c8..27a9e376ebe 100644 --- a/packages/eslint-plugin/src/rules/prefer-enum-initializers.ts +++ b/packages/eslint-plugin/src/rules/prefer-enum-initializers.ts @@ -1,6 +1,10 @@ import { TSESTree } from '@typescript-eslint/experimental-utils'; import * as util from '../util'; -import { RuleFix } from '@typescript-eslint/experimental-utils/dist/ts-eslint'; +import { + RuleFix, + ReportFixFunction, +} from '@typescript-eslint/experimental-utils/dist/ts-eslint'; +import { TSEnumMember } from '../../../parser/node_modules/@typescript-eslint/types/dist/ts-estree'; type Option = '0-based' | '1-based' | 'key-name'; type MessageIds = 'defineInitializer' | 'defineInitializerSuggestion'; @@ -32,42 +36,47 @@ export default util.createRule({ const sourceCode = context.getSourceCode(); const config = context.options[0]; - return { - TSEnumDeclaration(node: TSESTree.TSEnumDeclaration): void { - const { members } = node; - members.forEach((member, index) => { - if (member.initializer == null) { - const name = sourceCode.getText(member); - context.report({ - node: member, - messageId: 'defineInitializer', - data: { - name, - }, - suggest: [ - { - messageId: 'defineInitializerSuggestion', - data: { name }, - fix: (fixer): RuleFix | null => { - if (config === '0-based') { + function TSEnumDeclaration(node: TSESTree.TSEnumDeclaration): void { + const { members } = node; + + members.forEach((member, index) => { + if (member.initializer == null) { + const name = sourceCode.getText(member); + context.report({ + node: member, + messageId: 'defineInitializer', + data: { + name, + }, + suggest: [ + { + messageId: 'defineInitializerSuggestion', + data: { name }, + fix: fixer => { + switch (config) { + case '0-based': return fixer.replaceText(member, `${name} = ${index}`); - } else if (config === '1-based') { + case '1-based': return fixer.replaceText( member, `${name} = ${index + 1}`, ); - } else if (config === 'key-name') { + case 'key-name': return fixer.replaceText(member, `${name} = '${name}'`); - } else { - return null; - } - }, + default: + const _exhaustiveCheck: never = config; + return _exhaustiveCheck; + } }, - ], - }); - } - }); - }, + }, + ], + }); + } + }); + } + + return { + TSEnumDeclaration, }; }, }); From 262e2998ffa2193ea8098c7fe22c0e82fc19e48a Mon Sep 17 00:00:00 2001 From: bob1983 Date: Wed, 29 Jul 2020 01:20:36 +0900 Subject: [PATCH 08/11] feat(eslint-plugin): fix lint --- .../src/rules/prefer-enum-initializers.ts | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/packages/eslint-plugin/src/rules/prefer-enum-initializers.ts b/packages/eslint-plugin/src/rules/prefer-enum-initializers.ts index 27a9e376ebe..900718a17f6 100644 --- a/packages/eslint-plugin/src/rules/prefer-enum-initializers.ts +++ b/packages/eslint-plugin/src/rules/prefer-enum-initializers.ts @@ -1,10 +1,6 @@ import { TSESTree } from '@typescript-eslint/experimental-utils'; import * as util from '../util'; -import { - RuleFix, - ReportFixFunction, -} from '@typescript-eslint/experimental-utils/dist/ts-eslint'; -import { TSEnumMember } from '../../../parser/node_modules/@typescript-eslint/types/dist/ts-estree'; +import { RuleFix } from '@typescript-eslint/experimental-utils/dist/ts-eslint'; type Option = '0-based' | '1-based' | 'key-name'; type MessageIds = 'defineInitializer' | 'defineInitializerSuggestion'; @@ -52,7 +48,7 @@ export default util.createRule({ { messageId: 'defineInitializerSuggestion', data: { name }, - fix: fixer => { + fix: (fixer): RuleFix => { switch (config) { case '0-based': return fixer.replaceText(member, `${name} = ${index}`); @@ -63,9 +59,10 @@ export default util.createRule({ ); case 'key-name': return fixer.replaceText(member, `${name} = '${name}'`); - default: + default: { const _exhaustiveCheck: never = config; return _exhaustiveCheck; + } } }, }, From a24c335193ad6c6c067a2129d19a4aa2fac7f45f Mon Sep 17 00:00:00 2001 From: bob1983 Date: Wed, 29 Jul 2020 02:20:00 +0900 Subject: [PATCH 09/11] feat(eslint-plugin): suggest 3 variations at once --- .../src/rules/prefer-enum-initializers.ts | 48 ++++++++----------- 1 file changed, 21 insertions(+), 27 deletions(-) diff --git a/packages/eslint-plugin/src/rules/prefer-enum-initializers.ts b/packages/eslint-plugin/src/rules/prefer-enum-initializers.ts index 900718a17f6..1a5c1248c89 100644 --- a/packages/eslint-plugin/src/rules/prefer-enum-initializers.ts +++ b/packages/eslint-plugin/src/rules/prefer-enum-initializers.ts @@ -1,11 +1,10 @@ import { TSESTree } from '@typescript-eslint/experimental-utils'; import * as util from '../util'; -import { RuleFix } from '@typescript-eslint/experimental-utils/dist/ts-eslint'; +import { TSESLint } from '@typescript-eslint/experimental-utils'; -type Option = '0-based' | '1-based' | 'key-name'; type MessageIds = 'defineInitializer' | 'defineInitializerSuggestion'; -export default util.createRule({ +export default util.createRule<[], MessageIds>({ name: 'prefer-enum-initializers', meta: { type: 'suggestion', @@ -19,18 +18,13 @@ export default util.createRule({ defineInitializer: "The value of the member '{{ name }}' should be explicitly defined", defineInitializerSuggestion: - 'can be suggestion fixed to {{ name }} = {{ suggestedValue }}', + 'Can be fixed to {{ name }} = {{ suggested }}', }, - schema: [ - { - enum: ['0-based', '1-based', 'key-name'], - }, - ], + schema: [], }, defaultOptions: [], create(context) { const sourceCode = context.getSourceCode(); - const config = context.options[0]; function TSEnumDeclaration(node: TSESTree.TSEnumDeclaration): void { const { members } = node; @@ -47,23 +41,23 @@ export default util.createRule({ suggest: [ { messageId: 'defineInitializerSuggestion', - data: { name }, - fix: (fixer): RuleFix => { - switch (config) { - case '0-based': - return fixer.replaceText(member, `${name} = ${index}`); - case '1-based': - return fixer.replaceText( - member, - `${name} = ${index + 1}`, - ); - case 'key-name': - return fixer.replaceText(member, `${name} = '${name}'`); - default: { - const _exhaustiveCheck: never = config; - return _exhaustiveCheck; - } - } + data: { name, suggested: index }, + fix: (fixer): TSESLint.RuleFix => { + return fixer.replaceText(member, `${name} = ${index}`); + }, + }, + { + messageId: 'defineInitializerSuggestion', + data: { name, suggested: index + 1 }, + fix: (fixer): TSESLint.RuleFix => { + return fixer.replaceText(member, `${name} = ${index + 1}`); + }, + }, + { + messageId: 'defineInitializerSuggestion', + data: { name, suggested: `'${name}'` }, + fix: (fixer): TSESLint.RuleFix => { + return fixer.replaceText(member, `${name} = '${name}'`); }, }, ], From 4d58e38fe77af39ba00d6ea1548a1bb305248690 Mon Sep 17 00:00:00 2001 From: bob1983 Date: Wed, 29 Jul 2020 02:21:25 +0900 Subject: [PATCH 10/11] feat(eslint-plugin): fix tests --- .../rules/prefer-enum-initializers.test.ts | 129 ++++++++++++------ 1 file changed, 85 insertions(+), 44 deletions(-) diff --git a/packages/eslint-plugin/tests/rules/prefer-enum-initializers.test.ts b/packages/eslint-plugin/tests/rules/prefer-enum-initializers.test.ts index d3639ad5591..05604ff4985 100644 --- a/packages/eslint-plugin/tests/rules/prefer-enum-initializers.test.ts +++ b/packages/eslint-plugin/tests/rules/prefer-enum-initializers.test.ts @@ -35,7 +35,6 @@ enum Direction { Up, } `, - options: ['key-name'], errors: [ { messageId: 'defineInitializer', @@ -46,6 +45,26 @@ enum Direction { messageId: 'defineInitializerSuggestion', /* eslint-disable @typescript-eslint/internal/plugin-test-formatting */ output: ` +enum Direction { + Up = 0, +} + `, + /* eslint-enable @typescript-eslint/internal/plugin-test-formatting */ + }, + { + messageId: 'defineInitializerSuggestion', + /* eslint-disable @typescript-eslint/internal/plugin-test-formatting */ + output: ` +enum Direction { + Up = 1, +} + `, + /* eslint-enable @typescript-eslint/internal/plugin-test-formatting */ + }, + { + messageId: 'defineInitializerSuggestion', + /* eslint-disable @typescript-eslint/internal/plugin-test-formatting */ + output: ` enum Direction { Up = 'Up', } @@ -63,7 +82,6 @@ enum Direction { Down, } `, - options: ['key-name'], errors: [ { messageId: 'defineInitializer', @@ -74,6 +92,28 @@ enum Direction { messageId: 'defineInitializerSuggestion', /* eslint-disable @typescript-eslint/internal/plugin-test-formatting */ output: ` +enum Direction { + Up = 0, + Down, +} + `, + /* eslint-enable @typescript-eslint/internal/plugin-test-formatting */ + }, + { + messageId: 'defineInitializerSuggestion', + /* eslint-disable @typescript-eslint/internal/plugin-test-formatting */ + output: ` +enum Direction { + Up = 1, + Down, +} + `, + /* eslint-enable @typescript-eslint/internal/plugin-test-formatting */ + }, + { + messageId: 'defineInitializerSuggestion', + /* eslint-disable @typescript-eslint/internal/plugin-test-formatting */ + output: ` enum Direction { Up = 'Up', Down, @@ -94,34 +134,29 @@ enum Direction { output: ` enum Direction { Up, - Down = 'Down', + Down = 1, } `, /* eslint-enable @typescript-eslint/internal/plugin-test-formatting */ }, - ], - }, - ], - }, - { - code: ` + { + messageId: 'defineInitializerSuggestion', + /* eslint-disable @typescript-eslint/internal/plugin-test-formatting */ + output: ` enum Direction { Up, + Down = 2, } `, - options: ['0-based'], - errors: [ - { - messageId: 'defineInitializer', - data: { name: 'Up' }, - line: 3, - suggestions: [ + /* eslint-enable @typescript-eslint/internal/plugin-test-formatting */ + }, { messageId: 'defineInitializerSuggestion', /* eslint-disable @typescript-eslint/internal/plugin-test-formatting */ output: ` enum Direction { - Up = 0, + Up, + Down = 'Down', } `, /* eslint-enable @typescript-eslint/internal/plugin-test-formatting */ @@ -133,42 +168,45 @@ enum Direction { { code: ` enum Direction { - Up, + Up = 'Up', Down, } `, - options: ['0-based'], errors: [ { messageId: 'defineInitializer', - data: { name: 'Up' }, - line: 3, + data: { name: 'Down' }, + line: 4, suggestions: [ { messageId: 'defineInitializerSuggestion', /* eslint-disable @typescript-eslint/internal/plugin-test-formatting */ output: ` enum Direction { - Up = 0, - Down, + Up = 'Up', + Down = 1, } `, /* eslint-enable @typescript-eslint/internal/plugin-test-formatting */ }, - ], - }, - { - messageId: 'defineInitializer', - data: { name: 'Down' }, - line: 4, - suggestions: [ { messageId: 'defineInitializerSuggestion', /* eslint-disable @typescript-eslint/internal/plugin-test-formatting */ output: ` enum Direction { - Up, - Down = 1, + Up = 'Up', + Down = 2, +} + `, + /* eslint-enable @typescript-eslint/internal/plugin-test-formatting */ + }, + { + messageId: 'defineInitializerSuggestion', + /* eslint-disable @typescript-eslint/internal/plugin-test-formatting */ + output: ` +enum Direction { + Up = 'Up', + Down = 'Down', } `, /* eslint-enable @typescript-eslint/internal/plugin-test-formatting */ @@ -181,10 +219,9 @@ enum Direction { code: ` enum Direction { Up, - Down, + Down = 'Down', } `, - options: ['1-based'], errors: [ { messageId: 'defineInitializer', @@ -195,27 +232,31 @@ enum Direction { messageId: 'defineInitializerSuggestion', /* eslint-disable @typescript-eslint/internal/plugin-test-formatting */ output: ` +enum Direction { + Up = 0, + Down = 'Down', +} + `, + /* eslint-enable @typescript-eslint/internal/plugin-test-formatting */ + }, + { + messageId: 'defineInitializerSuggestion', + /* eslint-disable @typescript-eslint/internal/plugin-test-formatting */ + output: ` enum Direction { Up = 1, - Down, + Down = 'Down', } `, /* eslint-enable @typescript-eslint/internal/plugin-test-formatting */ }, - ], - }, - { - messageId: 'defineInitializer', - data: { name: 'Down' }, - line: 4, - suggestions: [ { messageId: 'defineInitializerSuggestion', /* eslint-disable @typescript-eslint/internal/plugin-test-formatting */ output: ` enum Direction { - Up, - Down = 2, + Up = 'Up', + Down = 'Down', } `, /* eslint-enable @typescript-eslint/internal/plugin-test-formatting */ From 22c90c9e3fec53758d9f79d9f43b21f04e0052a9 Mon Sep 17 00:00:00 2001 From: bob1983 Date: Wed, 29 Jul 2020 03:00:30 +0900 Subject: [PATCH 11/11] feat(eslint-plugin): use trimRight() instead of disabling eslint rule --- .../rules/prefer-enum-initializers.test.ts | 70 ++++++------------- 1 file changed, 21 insertions(+), 49 deletions(-) diff --git a/packages/eslint-plugin/tests/rules/prefer-enum-initializers.test.ts b/packages/eslint-plugin/tests/rules/prefer-enum-initializers.test.ts index 05604ff4985..0a49c52d945 100644 --- a/packages/eslint-plugin/tests/rules/prefer-enum-initializers.test.ts +++ b/packages/eslint-plugin/tests/rules/prefer-enum-initializers.test.ts @@ -28,13 +28,15 @@ enum Direction { } `, ], + // We need to keep indentation for avoiding @typescript-eslint/internal/plugin-test-formatting. + // Use trimRight() to make tests pass for now. https://github.com/typescript-eslint/typescript-eslint/pull/2326#discussion_r461760044 invalid: [ { code: ` enum Direction { Up, } - `, + `.trimRight(), errors: [ { messageId: 'defineInitializer', @@ -43,33 +45,27 @@ enum Direction { suggestions: [ { messageId: 'defineInitializerSuggestion', - /* eslint-disable @typescript-eslint/internal/plugin-test-formatting */ output: ` enum Direction { Up = 0, } - `, - /* eslint-enable @typescript-eslint/internal/plugin-test-formatting */ + `.trimRight(), }, { messageId: 'defineInitializerSuggestion', - /* eslint-disable @typescript-eslint/internal/plugin-test-formatting */ output: ` enum Direction { Up = 1, } - `, - /* eslint-enable @typescript-eslint/internal/plugin-test-formatting */ + `.trimRight(), }, { messageId: 'defineInitializerSuggestion', - /* eslint-disable @typescript-eslint/internal/plugin-test-formatting */ output: ` enum Direction { Up = 'Up', } - `, - /* eslint-enable @typescript-eslint/internal/plugin-test-formatting */ + `.trimRight(), }, ], }, @@ -81,7 +77,7 @@ enum Direction { Up, Down, } - `, + `.trimRight(), errors: [ { messageId: 'defineInitializer', @@ -90,36 +86,30 @@ enum Direction { suggestions: [ { messageId: 'defineInitializerSuggestion', - /* eslint-disable @typescript-eslint/internal/plugin-test-formatting */ output: ` enum Direction { Up = 0, Down, } - `, - /* eslint-enable @typescript-eslint/internal/plugin-test-formatting */ + `.trimRight(), }, { messageId: 'defineInitializerSuggestion', - /* eslint-disable @typescript-eslint/internal/plugin-test-formatting */ output: ` enum Direction { Up = 1, Down, } - `, - /* eslint-enable @typescript-eslint/internal/plugin-test-formatting */ + `.trimRight(), }, { messageId: 'defineInitializerSuggestion', - /* eslint-disable @typescript-eslint/internal/plugin-test-formatting */ output: ` enum Direction { Up = 'Up', Down, } - `, - /* eslint-enable @typescript-eslint/internal/plugin-test-formatting */ + `.trimRight(), }, ], }, @@ -130,36 +120,30 @@ enum Direction { suggestions: [ { messageId: 'defineInitializerSuggestion', - /* eslint-disable @typescript-eslint/internal/plugin-test-formatting */ output: ` enum Direction { Up, Down = 1, } - `, - /* eslint-enable @typescript-eslint/internal/plugin-test-formatting */ + `.trimRight(), }, { messageId: 'defineInitializerSuggestion', - /* eslint-disable @typescript-eslint/internal/plugin-test-formatting */ output: ` enum Direction { Up, Down = 2, } - `, - /* eslint-enable @typescript-eslint/internal/plugin-test-formatting */ + `.trimRight(), }, { messageId: 'defineInitializerSuggestion', - /* eslint-disable @typescript-eslint/internal/plugin-test-formatting */ output: ` enum Direction { Up, Down = 'Down', } - `, - /* eslint-enable @typescript-eslint/internal/plugin-test-formatting */ + `.trimRight(), }, ], }, @@ -171,7 +155,7 @@ enum Direction { Up = 'Up', Down, } - `, + `.trimRight(), errors: [ { messageId: 'defineInitializer', @@ -180,36 +164,30 @@ enum Direction { suggestions: [ { messageId: 'defineInitializerSuggestion', - /* eslint-disable @typescript-eslint/internal/plugin-test-formatting */ output: ` enum Direction { Up = 'Up', Down = 1, } - `, - /* eslint-enable @typescript-eslint/internal/plugin-test-formatting */ + `.trimRight(), }, { messageId: 'defineInitializerSuggestion', - /* eslint-disable @typescript-eslint/internal/plugin-test-formatting */ output: ` enum Direction { Up = 'Up', Down = 2, } - `, - /* eslint-enable @typescript-eslint/internal/plugin-test-formatting */ + `.trimRight(), }, { messageId: 'defineInitializerSuggestion', - /* eslint-disable @typescript-eslint/internal/plugin-test-formatting */ output: ` enum Direction { Up = 'Up', Down = 'Down', } - `, - /* eslint-enable @typescript-eslint/internal/plugin-test-formatting */ + `.trimRight(), }, ], }, @@ -221,7 +199,7 @@ enum Direction { Up, Down = 'Down', } - `, + `.trimRight(), errors: [ { messageId: 'defineInitializer', @@ -230,36 +208,30 @@ enum Direction { suggestions: [ { messageId: 'defineInitializerSuggestion', - /* eslint-disable @typescript-eslint/internal/plugin-test-formatting */ output: ` enum Direction { Up = 0, Down = 'Down', } - `, - /* eslint-enable @typescript-eslint/internal/plugin-test-formatting */ + `.trimRight(), }, { messageId: 'defineInitializerSuggestion', - /* eslint-disable @typescript-eslint/internal/plugin-test-formatting */ output: ` enum Direction { Up = 1, Down = 'Down', } - `, - /* eslint-enable @typescript-eslint/internal/plugin-test-formatting */ + `.trimRight(), }, { messageId: 'defineInitializerSuggestion', - /* eslint-disable @typescript-eslint/internal/plugin-test-formatting */ output: ` enum Direction { Up = 'Up', Down = 'Down', } - `, - /* eslint-enable @typescript-eslint/internal/plugin-test-formatting */ + `.trimRight(), }, ], },