From 3292b41c7aebe42b07a0814cd8ac75ddc680dc96 Mon Sep 17 00:00:00 2001 From: Dmytro Borysovskyi Date: Thu, 12 Dec 2019 12:53:06 +0200 Subject: [PATCH 1/3] feat: add [import-blacklist] converter #277 --- src/rules/converters/import-blacklist.ts | 52 ++++++++++++++ .../converters/tests/import-blacklist.test.ts | 67 +++++++++++++++++++ src/rules/rulesConverters.ts | 2 + src/utils.ts | 5 ++ 4 files changed, 126 insertions(+) create mode 100644 src/rules/converters/import-blacklist.ts create mode 100644 src/rules/converters/tests/import-blacklist.test.ts diff --git a/src/rules/converters/import-blacklist.ts b/src/rules/converters/import-blacklist.ts new file mode 100644 index 000000000..8651c4eb5 --- /dev/null +++ b/src/rules/converters/import-blacklist.ts @@ -0,0 +1,52 @@ +import { RuleConverter } from "../converter"; +import { RequireAtLeastOne } from "../../utils"; + +type ESLintOptionPath = { + name: string; + importNames: string[]; + message?: string; +}; +type ESLintSimpleOption = string[]; +type ESLintComplexOption = RequireAtLeastOne<{ + paths: (string | ESLintOptionPath)[]; + patterns: string[]; +}>; +type ESLintOptions = ESLintSimpleOption | ESLintComplexOption; + +export const convertImportBlacklist: RuleConverter = tslintRule => { + const tslintRules = tslintRule.ruleArguments; + let ruleArguments: ESLintOptions = []; + + if (tslintRules.every(isString)) { + ruleArguments = tslintRules; + } else { + ruleArguments = [ + tslintRules.reduce((rules, rule) => { + if (!Array.isArray(rule)) { + const eslintRule = isString(rule) + ? rule + : { + name: Object.keys(rule)[0], + importNames: Object.values(rule)[0] as string[], + }; + return { ...rules, paths: [...(rules.paths || []), eslintRule] }; + } else if (Array.isArray(rule)) { + return { ...rules, patterns: [...(rules.patterns || []), ...rule] }; + } + return rules; + }, {} as ESLintComplexOption), + ]; + } + return { + rules: [ + { + ruleArguments, + ruleName: "no-restricted-imports", + }, + ], + }; +}; + +function isString(value: string): boolean { + return typeof value === "string"; +} diff --git a/src/rules/converters/tests/import-blacklist.test.ts b/src/rules/converters/tests/import-blacklist.test.ts new file mode 100644 index 000000000..2370e6bfa --- /dev/null +++ b/src/rules/converters/tests/import-blacklist.test.ts @@ -0,0 +1,67 @@ +import { convertImportBlacklist } from "../import-blacklist"; + +describe(convertImportBlacklist, () => { + test.each([ + [[], []], + [["rxjs"], ["rxjs"]], + [ + ["rxjs", "lodash"], + ["rxjs", "lodash"], + ], + [ + [".*\\.temp$", ".*\\.tmp$"], + [".*\\.temp$", ".*\\.tmp$"], + ], + [ + ["moment", "date-fns", ["eslint/*"]], + [{ patterns: ["eslint/*"], paths: ["moment", "date-fns"] }], + ], + [ + [{ lodash: ["pullAll", "pull"] }], + [ + { + paths: [ + { + name: "lodash", + importNames: ["pullAll", "pull"], + }, + ], + }, + ], + ], + [ + [ + "rxjs", + [".*\\.temp$", ".*\\.tmp$"], + { lodash: ["pullAll", "pull"] }, + { dummy: ["default"] }, + ], + [ + { + paths: [ + "rxjs", + { + name: "lodash", + importNames: ["pullAll", "pull"], + }, + { + name: "dummy", + importNames: ["default"], + }, + ], + patterns: [".*\\.temp$", ".*\\.tmp$"], + }, + ], + ], + ] as any[][])("convert %j", (ruleArguments: any[], expected: any[]) => { + const result = convertImportBlacklist({ ruleArguments }); + expect(result).toEqual({ + rules: [ + { + ruleArguments: expected, + ruleName: "no-restricted-imports", + }, + ], + }); + }); +}); diff --git a/src/rules/rulesConverters.ts b/src/rules/rulesConverters.ts index 084e91c22..062698fc7 100644 --- a/src/rules/rulesConverters.ts +++ b/src/rules/rulesConverters.ts @@ -18,6 +18,7 @@ import { convertEofline } from "./converters/eofline"; import { convertFileNameCasing } from "./converters/file-name-casing"; import { convertForin } from "./converters/forin"; import { convertFunctionConstructor } from "./converters/function-constructor"; +import { convertImportBlacklist } from "./converters/import-blacklist"; import { convertIncrementDecrement } from "./converters/increment-decrement"; import { convertIndent } from "./converters/indent"; import { convertInterfaceName } from "./converters/interface-name"; @@ -155,6 +156,7 @@ export const rulesConverters = new Map([ ["file-name-casing", convertFileNameCasing], ["forin", convertForin], ["function-constructor", convertFunctionConstructor], + ["import-blacklist", convertImportBlacklist], ["increment-decrement", convertIncrementDecrement], ["indent", convertIndent], ["interface-name", convertInterfaceName], diff --git a/src/utils.ts b/src/utils.ts index 65139d347..c69d6f9df 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -8,6 +8,11 @@ export type RemoveErrors = { export type PromiseValue = T extends Promise ? R : never; +export type RequireAtLeastOne = Pick> & + { + [K in Keys]-?: Required> & Partial>>; + }[Keys]; + export const uniqueFromSources = (...sources: (T | T[] | undefined)[]) => { const items: T[] = []; From e904faccb57f53005c08daf155fe15c43a22a06c Mon Sep 17 00:00:00 2001 From: Dmytro Borysovskyi Date: Thu, 12 Dec 2019 13:02:23 +0200 Subject: [PATCH 2/3] chore: remove comment as being resolving --- src/rules/rulesConverters.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/rules/rulesConverters.ts b/src/rules/rulesConverters.ts index 062698fc7..4ac20ea89 100644 --- a/src/rules/rulesConverters.ts +++ b/src/rules/rulesConverters.ts @@ -277,7 +277,6 @@ export const rulesConverters = new Map([ // TSLint core rules: // ["ban", convertBan], // no-restricted-properties - // ["import-blacklist", convertImportBlacklist], // no-restricted-imports // tslint-microsoft-contrib rules: // ["max-func-body-length", convertMaxFuncBodyLength], From 3b4595ae5f76868c1edcfeb5d5d225391f323ab0 Mon Sep 17 00:00:00 2001 From: Dmytro Borysovskyi Date: Mon, 16 Dec 2019 09:54:43 +0200 Subject: [PATCH 3/3] refactor: add notice, pass test coverage --- src/rules/converters/import-blacklist.ts | 46 +++++++++++-------- .../converters/tests/import-blacklist.test.ts | 7 +++ 2 files changed, 34 insertions(+), 19 deletions(-) diff --git a/src/rules/converters/import-blacklist.ts b/src/rules/converters/import-blacklist.ts index 8651c4eb5..0f0a7f283 100644 --- a/src/rules/converters/import-blacklist.ts +++ b/src/rules/converters/import-blacklist.ts @@ -13,34 +13,42 @@ type ESLintComplexOption = RequireAtLeastOne<{ }>; type ESLintOptions = ESLintSimpleOption | ESLintComplexOption; +const NOTICE_MATCH_PATTERNS = + "ESLint and TSLint use different strategies to match patterns. TSLint uses standard regular expressions, but ESLint .gitignore spec."; + export const convertImportBlacklist: RuleConverter = tslintRule => { - const tslintRules = tslintRule.ruleArguments; let ruleArguments: ESLintOptions = []; + const notices = []; - if (tslintRules.every(isString)) { - ruleArguments = tslintRules; + if (tslintRule.ruleArguments.every(isString)) { + ruleArguments = tslintRule.ruleArguments; } else { - ruleArguments = [ - tslintRules.reduce((rules, rule) => { - if (!Array.isArray(rule)) { - const eslintRule = isString(rule) - ? rule - : { - name: Object.keys(rule)[0], - importNames: Object.values(rule)[0] as string[], - }; - return { ...rules, paths: [...(rules.paths || []), eslintRule] }; - } else if (Array.isArray(rule)) { - return { ...rules, patterns: [...(rules.patterns || []), ...rule] }; - } - return rules; - }, {} as ESLintComplexOption), - ]; + const objectOption = tslintRule.ruleArguments.reduce((rules, rule) => { + if (!Array.isArray(rule)) { + const eslintRule = isString(rule) + ? rule + : { + name: Object.keys(rule)[0], + importNames: Object.values(rule)[0] as string[], + }; + return { ...rules, paths: [...(rules.paths || []), eslintRule] }; + } + + return { ...rules, patterns: [...(rules.patterns || []), ...rule] }; + }, {} as ESLintComplexOption); + + if ("patterns" in objectOption && objectOption.patterns.length > 0) { + notices.push(NOTICE_MATCH_PATTERNS); + } + + ruleArguments = [objectOption]; } + return { rules: [ { ruleArguments, + ...(notices.length > 0 && { notices }), ruleName: "no-restricted-imports", }, ], diff --git a/src/rules/converters/tests/import-blacklist.test.ts b/src/rules/converters/tests/import-blacklist.test.ts index 2370e6bfa..7edf8c85e 100644 --- a/src/rules/converters/tests/import-blacklist.test.ts +++ b/src/rules/converters/tests/import-blacklist.test.ts @@ -55,10 +55,17 @@ describe(convertImportBlacklist, () => { ], ] as any[][])("convert %j", (ruleArguments: any[], expected: any[]) => { const result = convertImportBlacklist({ ruleArguments }); + const hasPatterns = typeof expected[0] === "object" && "patterns" in expected[0]; + expect(result).toEqual({ rules: [ { ruleArguments: expected, + ...(hasPatterns && { + notices: [ + "ESLint and TSLint use different strategies to match patterns. TSLint uses standard regular expressions, but ESLint .gitignore spec.", + ], + }), ruleName: "no-restricted-imports", }, ],