From e325b728098f73b98ada8ec05ea0175403c80d1f Mon Sep 17 00:00:00 2001 From: golopot Date: Fri, 28 Jun 2019 23:10:06 +0800 Subject: [PATCH] feat(eslint-plugin): [ban-types] Support namespaced type (#616) --- packages/eslint-plugin/src/rules/ban-types.ts | 77 +++++++++--------- .../tests/rules/ban-types.test.ts | 78 +++++++++++++++++++ 2 files changed, 119 insertions(+), 36 deletions(-) diff --git a/packages/eslint-plugin/src/rules/ban-types.ts b/packages/eslint-plugin/src/rules/ban-types.ts index 059f7302024..d0a57daf233 100644 --- a/packages/eslint-plugin/src/rules/ban-types.ts +++ b/packages/eslint-plugin/src/rules/ban-types.ts @@ -1,8 +1,4 @@ -import { - TSESLint, - TSESTree, - AST_NODE_TYPES, -} from '@typescript-eslint/experimental-utils'; +import { TSESLint, TSESTree } from '@typescript-eslint/experimental-utils'; import * as util from '../util'; type Options = [ @@ -20,6 +16,31 @@ type Options = [ ]; type MessageIds = 'bannedTypeMessage'; +function stringifyTypeName( + node: TSESTree.EntityName, + sourceCode: TSESLint.SourceCode, +): string { + return sourceCode.getText(node).replace(/ /g, ''); +} + +function getCustomMessage( + bannedType: null | string | { message?: string; fixWith?: string }, +) { + if (bannedType === null) { + return ''; + } + + if (typeof bannedType === 'string') { + return ` ${bannedType}`; + } + + if (bannedType.message) { + return ` ${bannedType.message}`; + } + + return ''; +} + export default util.createRule({ name: 'ban-types', meta: { @@ -87,39 +108,23 @@ export default util.createRule({ ], create(context, [{ types: bannedTypes }]) { return { - 'TSTypeReference Identifier'(node: TSESTree.Identifier) { - if ( - node.parent && - node.parent.type !== AST_NODE_TYPES.TSQualifiedName - ) { - if (node.name in bannedTypes) { - let customMessage = ''; - const bannedCfgValue = bannedTypes[node.name]; + TSTypeReference({ typeName }) { + const name = stringifyTypeName(typeName, context.getSourceCode()); - let fix: TSESLint.ReportFixFunction | null = null; + if (name in bannedTypes) { + const bannedType = bannedTypes[name]; + const customMessage = getCustomMessage(bannedType); + const fixWith = bannedType && (bannedType as any).fixWith; - if (typeof bannedCfgValue === 'string') { - customMessage += ` ${bannedCfgValue}`; - } else if (bannedCfgValue !== null) { - if (bannedCfgValue.message) { - customMessage += ` ${bannedCfgValue.message}`; - } - if (bannedCfgValue.fixWith) { - const fixWith = bannedCfgValue.fixWith; - fix = fixer => fixer.replaceText(node, fixWith); - } - } - - context.report({ - node, - messageId: 'bannedTypeMessage', - data: { - name: node.name, - customMessage, - }, - fix, - }); - } + context.report({ + node: typeName, + messageId: 'bannedTypeMessage', + data: { + name: name, + customMessage, + }, + fix: fixWith ? fixer => fixer.replaceText(typeName, fixWith) : null, + }); } }, }; diff --git a/packages/eslint-plugin/tests/rules/ban-types.test.ts b/packages/eslint-plugin/tests/rules/ban-types.test.ts index 12766b0438a..73427fcc806 100644 --- a/packages/eslint-plugin/tests/rules/ban-types.test.ts +++ b/packages/eslint-plugin/tests/rules/ban-types.test.ts @@ -16,6 +16,10 @@ const options: InferOptionsTypeFromRule = [ Object: "Use '{}' instead.", Array: null, F: null, + 'NS.Bad': { + message: 'Use NS.Good instead.', + fixWith: 'NS.Good', + }, }, }, ]; @@ -39,6 +43,14 @@ ruleTester.run('ban-types', rule, { code: 'let e: foo.String;', options, }, + { + code: 'let a: _.NS.Bad', + options, + }, + { + code: 'let a: NS.Bad._', + options, + }, ], invalid: [ { @@ -56,6 +68,25 @@ ruleTester.run('ban-types', rule, { ], options, }, + { + code: 'let aa: Foo;', + errors: [ + { + messageId: 'bannedTypeMessage', + data: { + name: 'Foo', + customMessage: '', + }, + }, + ], + options: [ + { + types: { + Foo: { message: '' }, + }, + }, + ], + }, { code: 'let b: {c: String};', output: 'let b: {c: string};', @@ -217,5 +248,52 @@ class Foo extends Bar implements Baz { ], options, }, + { + code: 'let a: NS.Bad;', + output: 'let a: NS.Good;', + errors: [ + { + messageId: 'bannedTypeMessage', + data: { + name: 'NS.Bad', + customMessage: ' Use NS.Good instead.', + }, + line: 1, + column: 8, + }, + ], + options, + }, + { + code: ` +let a: NS.Bad; +let b: Foo; + `, + output: ` +let a: NS.Good; +let b: Foo; + `, + errors: [ + { + messageId: 'bannedTypeMessage', + data: { + name: 'NS.Bad', + customMessage: ' Use NS.Good instead.', + }, + line: 2, + column: 8, + }, + { + messageId: 'bannedTypeMessage', + data: { + name: 'NS.Bad', + customMessage: ' Use NS.Good instead.', + }, + line: 3, + column: 12, + }, + ], + options, + }, ], });