From 4ba63cda4310b75786b39a8e8bfeb830a43c6fd4 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Fri, 5 Jul 2019 09:30:04 -0400 Subject: [PATCH 1/2] Respect ESLint extensions in deduplicating rules and config writing The original ESLint configuration is now included in the `writeConversionResults` written file with a single `...`. Additionally skipped outputting rules that match an existing ESLint plugin. If an extended ESLint plugin defines a rule with the same values as the resultant config, the config will skip printing it. This necessitated expanding "failures" to allow a new `ConfigurationError` type of error for unfound ESLint configurations. Renames "packages" in the configuration reading/converting logic areas to "plugins", as that's what ESLint calls them. --- .eslintrc.js | 3 +- README.md | 2 +- src/adapters/importer.ts | 9 + src/cli/main.ts | 26 ++- src/conversion/conversionResults.stubs.ts | 4 +- src/conversion/convertConfig.test.ts | 3 +- src/conversion/convertConfig.ts | 14 +- .../removeExtendsDuplicatedRules.test.ts | 165 ++++++++++++++++++ .../removeExtendsDuplicatedRules.ts | 87 +++++++++ .../resolveExtensionNames.test.ts | 68 ++++++++ .../simplification/resolveExtensionNames.ts | 13 ++ .../retrieveExtendsValues.test.ts | 88 ++++++++++ .../simplification/retrieveExtendsValues.ts | 61 +++++++ .../simplifyPackageRules.test.ts | 103 +++++++++++ .../simplification/simplifyPackageRules.ts | 36 ++++ src/creation/writeConversionResults.test.ts | 64 ++++++- src/creation/writeConversionResults.ts | 2 + src/errors/configurationError.ts | 3 + src/{rules => errors}/conversionError.ts | 2 +- src/input/findESLintConfiguration.test.ts | 1 + src/input/findESLintConfiguration.ts | 18 +- src/input/findOriginalConfigurations.test.ts | 2 + src/reporting/reportConversionResults.test.ts | 40 +++-- src/reporting/reportConversionResults.ts | 32 ++-- src/rules/convertRule.test.ts | 2 +- src/rules/convertRule.ts | 4 +- src/rules/convertRuleSeverity.test.ts | 54 +++++- src/rules/convertRuleSeverity.ts | 22 ++- src/rules/convertRules.test.ts | 10 +- src/rules/convertRules.ts | 21 +-- src/rules/converter.ts | 6 +- 31 files changed, 895 insertions(+), 70 deletions(-) create mode 100644 src/adapters/importer.ts create mode 100644 src/creation/simplification/removeExtendsDuplicatedRules.test.ts create mode 100644 src/creation/simplification/removeExtendsDuplicatedRules.ts create mode 100644 src/creation/simplification/resolveExtensionNames.test.ts create mode 100644 src/creation/simplification/resolveExtensionNames.ts create mode 100644 src/creation/simplification/retrieveExtendsValues.test.ts create mode 100644 src/creation/simplification/retrieveExtendsValues.ts create mode 100644 src/creation/simplification/simplifyPackageRules.test.ts create mode 100644 src/creation/simplification/simplifyPackageRules.ts create mode 100644 src/errors/configurationError.ts rename src/{rules => errors}/conversionError.ts (75%) diff --git a/.eslintrc.js b/.eslintrc.js index 03f9d2312..d51ea9b33 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -17,18 +17,19 @@ module.exports = { "@typescript-eslint/no-use-before-define": 0, "@typescript-eslint/prefer-interface": 0, "default-case": 0, + "guard-for-in": 0, "import/no-extraneous-dependencies": [ "error", { devDependencies: ["**/*.stubs.ts", "**/*.test.*"] }, ], "import/first": 0, - "import/newline-after-import": 0, "import/no-unresolved": 0, "import/prefer-default-export": 0, "no-console": 0, "no-continue": 0, "no-empty-function": 0, "no-restricted-syntax": 0, + "no-param-reassign": 0, "no-shadow": 0, "no-undef": 0, "no-useless-constructor": 0, diff --git a/README.md b/README.md index 18abc4c1d..8c8085551 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ npx tslint-to-eslint-config --eslint ./path/to/seslintrc.json _Default: `.eslintrc.js`_ Path to an ESLint configuration file to read settings from. -This isn't yet used for anything, but will eventually inform settings for the generated ESLint configuration file. +The generated ESLint configuration file will include any settings `import`ed from this file. #### `package` diff --git a/src/adapters/importer.ts b/src/adapters/importer.ts new file mode 100644 index 000000000..7aa40a2f6 --- /dev/null +++ b/src/adapters/importer.ts @@ -0,0 +1,9 @@ +export type Importer = (moduleName: string) => unknown | Error; + +export const nativeImporter = async (moduleName: string) => { + try { + return await import(moduleName); + } catch (error) { + return error; + } +}; diff --git a/src/cli/main.ts b/src/cli/main.ts index 15ae45bd7..cbfd1f197 100644 --- a/src/cli/main.ts +++ b/src/cli/main.ts @@ -1,11 +1,20 @@ import { EOL } from "os"; -import { bind } from "../binding"; -import { runCli, RunCliDependencies } from "./runCli"; +import { nativeImporter } from "../adapters/importer"; import { processLogger } from "../adapters/processLogger"; import { childProcessExec } from "../adapters/childProcessExec"; import { fsFileSystem } from "../adapters/fsFileSystem"; +import { bind } from "../binding"; import { ConvertConfigDependencies, convertConfig } from "../conversion/convertConfig"; +import { removeExtendsDuplicatedRules } from "../creation/simplification/removeExtendsDuplicatedRules"; +import { + RetrieveExtendsValuesDependencies, + retrieveExtendsValues, +} from "../creation/simplification/retrieveExtendsValues"; +import { + simplifyPackageRules, + SimplifyPackageRulesDependencies, +} from "../creation/simplification/simplifyPackageRules"; import { writeConversionResults, WriteConversionResultsDependencies, @@ -14,6 +23,7 @@ import { findOriginalConfigurations, FindOriginalConfigurationsDependencies, } from "../input/findOriginalConfigurations"; +import { findPackagesConfiguration } from "../input/findPackagesConfiguration"; import { findESLintConfiguration } from "../input/findESLintConfiguration"; import { findTSLintConfiguration } from "../input/findTSLintConfiguration"; import { findTypeScriptConfiguration } from "../input/findTypeScriptConfiguration"; @@ -24,7 +34,7 @@ import { import { converters } from "../rules/converters"; import { convertRules } from "../rules/convertRules"; import { mergers } from "../rules/mergers"; -import { findPackagesConfiguration } from "../input/findPackagesConfiguration"; +import { runCli, RunCliDependencies } from "./runCli"; const convertRulesDependencies = { converters, @@ -46,6 +56,15 @@ const reportConversionResultsDependencies: ReportConversionResultsDependencies = logger: processLogger, }; +const retrieveExtendsValuesDependencies: RetrieveExtendsValuesDependencies = { + importer: nativeImporter, +}; + +const simplifyPackageRulesDependencies: SimplifyPackageRulesDependencies = { + removeExtendsDuplicatedRules, + retrieveExtendsValues: bind(retrieveExtendsValues, retrieveExtendsValuesDependencies), +}; + const writeConversionResultsDependencies: WriteConversionResultsDependencies = { fileSystem: fsFileSystem, }; @@ -57,6 +76,7 @@ const convertConfigDependencies: ConvertConfigDependencies = { findOriginalConfigurationsDependencies, ), reportConversionResults: bind(reportConversionResults, reportConversionResultsDependencies), + simplifyPackageRules: bind(simplifyPackageRules, simplifyPackageRulesDependencies), writeConversionResults: bind(writeConversionResults, writeConversionResultsDependencies), }; diff --git a/src/conversion/conversionResults.stubs.ts b/src/conversion/conversionResults.stubs.ts index 0acb2d7f6..57249adb2 100644 --- a/src/conversion/conversionResults.stubs.ts +++ b/src/conversion/conversionResults.stubs.ts @@ -1,11 +1,11 @@ import { RuleConversionResults } from "../rules/convertRules"; export const createEmptyConversionResults = ( - overrides: Partial, + overrides: Partial = {}, ): RuleConversionResults => ({ converted: new Map(), failed: [], missing: [], - packages: new Set(), + plugins: new Set(), ...overrides, }); diff --git a/src/conversion/convertConfig.test.ts b/src/conversion/convertConfig.test.ts index 8a8718f1a..f3b89996e 100644 --- a/src/conversion/convertConfig.test.ts +++ b/src/conversion/convertConfig.test.ts @@ -4,9 +4,10 @@ import { OriginalConfigurations } from "../input/findOriginalConfigurations"; const createStubDependencies = ( overrides: Pick, -) => ({ +): ConvertConfigDependencies => ({ convertRules: jest.fn(), reportConversionResults: jest.fn(), + simplifyPackageRules: async (_configurations, data) => data, writeConversionResults: jest.fn().mockReturnValue(Promise.resolve()), ...overrides, }); diff --git a/src/conversion/convertConfig.ts b/src/conversion/convertConfig.ts index fc4046a34..8aa3c55de 100644 --- a/src/conversion/convertConfig.ts +++ b/src/conversion/convertConfig.ts @@ -1,4 +1,5 @@ import { SansDependencies } from "../binding"; +import { simplifyPackageRules } from "../creation/simplification/simplifyPackageRules"; import { writeConversionResults } from "../creation/writeConversionResults"; import { findOriginalConfigurations } from "../input/findOriginalConfigurations"; import { reportConversionResults } from "../reporting/reportConversionResults"; @@ -9,6 +10,7 @@ export type ConvertConfigDependencies = { convertRules: SansDependencies; findOriginalConfigurations: SansDependencies; reportConversionResults: SansDependencies; + simplifyPackageRules: SansDependencies; writeConversionResults: SansDependencies; }; @@ -25,8 +27,16 @@ export const convertConfig = async ( originalConfigurations.data.tslint.rules, ); - await dependencies.writeConversionResults(ruleConversionResults, originalConfigurations.data); - dependencies.reportConversionResults(ruleConversionResults); + const mergedConfiguration = { + ...ruleConversionResults, + ...(await dependencies.simplifyPackageRules( + originalConfigurations.data.eslint, + ruleConversionResults, + )), + }; + + await dependencies.writeConversionResults(mergedConfiguration, originalConfigurations.data); + dependencies.reportConversionResults(mergedConfiguration); return { status: ResultStatus.Succeeded, diff --git a/src/creation/simplification/removeExtendsDuplicatedRules.test.ts b/src/creation/simplification/removeExtendsDuplicatedRules.test.ts new file mode 100644 index 000000000..5c64be1d4 --- /dev/null +++ b/src/creation/simplification/removeExtendsDuplicatedRules.test.ts @@ -0,0 +1,165 @@ +import { + ESLintConfiguration, + ESLintConfigurationRuleValue, +} from "../../input/findESLintConfiguration"; +import { ESLintRuleOptions } from "../../rules/types"; +import { removeExtendsDuplicatedRules } from "./removeExtendsDuplicatedRules"; + +const prepareTestRule = ( + ruleOptions: Partial, + extensionConfiguration: ESLintConfigurationRuleValue = 2, +) => { + const ruleName = "rule-a"; + const allRules = new Map([ + [ + ruleName, + { + ruleArguments: [], + ruleName, + ruleSeverity: "off", + ...ruleOptions, + }, + ], + ]); + const extensions: Partial[] = [ + { + rules: { + [ruleName]: extensionConfiguration, + }, + }, + ]; + + return { ruleName, allRules, extensions }; +}; + +describe("removeExtendsDuplicatedRules", () => { + it("keeps a rule when there are no rules in the extension", () => { + // Arrange + const { allRules } = prepareTestRule( + { + ruleName: "mismatched", + }, + 2, + ); + + // Act + const differentRules = removeExtendsDuplicatedRules(allRules, [{}]); + + // Assert + expect(differentRules.size).toBe(1); + }); + + it("keeps a rule when it doesn't match any existing rule", () => { + // Arrange + const { allRules, extensions } = prepareTestRule( + { + ruleName: "mismatched", + }, + 2, + ); + + // Act + const differentRules = removeExtendsDuplicatedRules(allRules, extensions); + + // Assert + expect(differentRules.size).toBe(1); + }); + + it("removes a rule when it matches an existing rule as numbers", () => { + // Arrange + const { allRules, extensions } = prepareTestRule( + { + ruleSeverity: "warn", + }, + 1, + ); + + // Act + const differentRules = removeExtendsDuplicatedRules(allRules, extensions); + + // Assert + expect(differentRules.size).toBe(0); + }); + + it("keeps a rule when it conflicts with an existing rule as numbers", () => { + // Arrange + const { allRules, extensions } = prepareTestRule( + { + ruleSeverity: "warn", + }, + 2, + ); + + // Act + const differentRules = removeExtendsDuplicatedRules(allRules, extensions); + + // Assert + expect(differentRules.size).toBe(1); + }); + + it("removes a rule when it matches an existing rule as strings", () => { + // Arrange + const { allRules, extensions } = prepareTestRule( + { + ruleSeverity: "warn", + }, + "warn", + ); + + // Act + const differentRules = removeExtendsDuplicatedRules(allRules, extensions); + + // Assert + expect(differentRules.size).toBe(0); + }); + + it("keeps a rule when it conflicts with an existing rule as strings", () => { + // Arrange + const { allRules, extensions } = prepareTestRule( + { + ruleSeverity: "warn", + }, + "error", + ); + + // Act + const differentRules = removeExtendsDuplicatedRules(allRules, extensions); + + // Assert + expect(differentRules.size).toBe(1); + }); + + it("removes a rule when it matches an existing rule as objects", () => { + // Arrange + const { allRules, extensions } = prepareTestRule( + { + ruleArguments: ["some-argument"], + ruleSeverity: "warn", + }, + ["warn", "some-argument"], + ); + + // Act + const differentRules = removeExtendsDuplicatedRules(allRules, extensions); + + // Assert + expect(differentRules.size).toBe(0); + }); + + it("keeps a rule when it conflicts with an existing rule as objects", () => { + // Arrange + const { allRules, extensions } = prepareTestRule( + { + ruleArguments: ["some-argument-one"], + ruleSeverity: "warn", + }, + ["warn", "some-argument-modified"], + ); + + // Act + const differentRules = removeExtendsDuplicatedRules(allRules, extensions); + + // Assert + expect(differentRules.size).toBe(1); + }); +}); diff --git a/src/creation/simplification/removeExtendsDuplicatedRules.ts b/src/creation/simplification/removeExtendsDuplicatedRules.ts new file mode 100644 index 000000000..2e1744699 --- /dev/null +++ b/src/creation/simplification/removeExtendsDuplicatedRules.ts @@ -0,0 +1,87 @@ +import { isDeepStrictEqual } from "util"; + +import { + ESLintConfiguration, + ESLintConfigurationRuleValue, +} from "../../input/findESLintConfiguration"; +import { convertRawESLintRuleSeverity } from "../../rules/convertRuleSeverity"; +import { ESLintRuleOptions } from "../../rules/types"; + +export const removeExtendsDuplicatedRules = ( + allRules: Map, + extensions: Partial[], +): Map => { + const differentRules = new Map(); + const mergedExtensionRules = mergeExtensions(extensions); + + for (const [ruleName, value] of allRules) { + if (!ruleValuesAreTheSame(value, mergedExtensionRules.get(ruleName))) { + differentRules.set(ruleName, value); + } + } + + return differentRules; +}; + +const mergeExtensions = (extensions: Partial[]) => { + const mergedRules = new Map(); + + for (const extension of extensions) { + if (extension.rules === undefined) { + continue; + } + + for (const ruleName in extension.rules) { + mergedRules.set(ruleName, formatRuleArguments(ruleName, extension.rules[ruleName])); + } + } + + return mergedRules; +}; + +const formatRuleArguments = ( + ruleName: string, + originalValue: ESLintConfigurationRuleValue, +): ESLintRuleOptions => { + if (typeof originalValue === "number") { + return { + ruleArguments: [], + ruleName, + ruleSeverity: convertRawESLintRuleSeverity(originalValue), + }; + } + + if (typeof originalValue === "string") { + return { + ruleArguments: [], + ruleName, + ruleSeverity: originalValue, + }; + } + + return { + ruleArguments: originalValue.slice(1), + ruleName, + ruleSeverity: convertRawESLintRuleSeverity(originalValue[0]), + }; +}; + +const ruleValuesAreTheSame = ( + configurationValue: ESLintRuleOptions, + extensionValue: ESLintRuleOptions | undefined, +) => { + return ( + extensionValue !== undefined && + configurationValue.ruleSeverity === extensionValue.ruleSeverity && + isDeepStrictEqual( + { + ruleArguments: [], + ...configurationValue.ruleArguments, + }, + { + ruleArguments: [], + ...extensionValue.ruleArguments, + }, + ) + ); +}; diff --git a/src/creation/simplification/resolveExtensionNames.test.ts b/src/creation/simplification/resolveExtensionNames.test.ts new file mode 100644 index 000000000..5fa8322ca --- /dev/null +++ b/src/creation/simplification/resolveExtensionNames.test.ts @@ -0,0 +1,68 @@ +import { resolveExtensionNames } from "./resolveExtensionNames"; + +describe("resolveExtensionNames", () => { + it("returns a single plugin name as an array when given a single string", () => { + // Arrange + const rawExtensionName = "eslint-plugin-linting"; + + // Act + const extensionNames = resolveExtensionNames(rawExtensionName); + + // Assert + expect(extensionNames).toEqual([rawExtensionName]); + }); + + it("returns multiple plugin names as an array when given an array", () => { + // Arrange + const rawExtensionNames = ["eslint-plugin-one", "eslint-plugin-two"]; + + // Act + const extensionNames = resolveExtensionNames(rawExtensionNames); + + // Assert + expect(extensionNames).toEqual(rawExtensionNames); + }); + + it("prepends eslint-plugin- when a plugin doesn't start with it", () => { + // Arrange + const rawExtensionName = "custom"; + + // Act + const extensionNames = resolveExtensionNames(rawExtensionName); + + // Assert + expect(extensionNames).toEqual(["eslint-plugin-custom"]); + }); + + it("doesn't prepend eslint-plugin- when a plugin starts with .", () => { + // Arrange + const rawExtensionName = "../my-config.js"; + + // Act + const extensionNames = resolveExtensionNames(rawExtensionName); + + // Assert + expect(extensionNames).toEqual(["../my-config.js"]); + }); + + it("doesn't prepend eslint-plugin- when a plugin starts with t-plugin-", () => { + // Arrange + const rawExtensionName = "eslint-plugin-value"; + + // Act + const extensionNames = resolveExtensionNames(rawExtensionName); + + // Assert + expect(extensionNames).toEqual(["eslint-plugin-value"]); + }); + it("doesn't prepend eslint-plugin- when a plugin starts with eslint:", () => { + // Arrange + const rawExtensionName = "eslint:recommended"; + + // Act + const extensionNames = resolveExtensionNames(rawExtensionName); + + // Assert + expect(extensionNames).toEqual(["eslint:recommended"]); + }); +}); diff --git a/src/creation/simplification/resolveExtensionNames.ts b/src/creation/simplification/resolveExtensionNames.ts new file mode 100644 index 000000000..7b1b1b645 --- /dev/null +++ b/src/creation/simplification/resolveExtensionNames.ts @@ -0,0 +1,13 @@ +export const resolveExtensionNames = (rawExtensionNames: string | string[]) => { + if (typeof rawExtensionNames === "string") { + rawExtensionNames = [rawExtensionNames]; + } + + return rawExtensionNames.map(rawExtensionName => + rawExtensionName.startsWith(".") || + rawExtensionName.startsWith("eslint-plugin-") || + rawExtensionName.startsWith("eslint:") + ? rawExtensionName + : `eslint-plugin-${rawExtensionName}`, + ); +}; diff --git a/src/creation/simplification/retrieveExtendsValues.test.ts b/src/creation/simplification/retrieveExtendsValues.test.ts new file mode 100644 index 000000000..3cf54c632 --- /dev/null +++ b/src/creation/simplification/retrieveExtendsValues.test.ts @@ -0,0 +1,88 @@ +import { ConfigurationError } from "../../errors/configurationError"; +import { retrieveExtendsValues } from "./retrieveExtendsValues"; + +describe("retrieveExtendsValues", () => { + it("retrieves eslint-all when an extension is named eslint:all", async () => { + // Arrange + const eslintAll = { rules: {} }; + const importer = async (extensionName: string) => + extensionName === "eslint/conf/eslint-all" + ? eslintAll + : new Error(`Unknown extension name: '${extensionName}`); + + // Act + const { importedExtensions } = await retrieveExtendsValues({ importer }, "eslint:all"); + + // Assert + expect(importedExtensions).toEqual([eslintAll]); + }); + + it("retrieves eslint-recommended when an extension is named eslint:recommended", async () => { + // Arrange + const eslintRecommended = { rules: {} }; + const importer = async (extensionName: string) => + extensionName === "eslint/conf/eslint-recommended" + ? eslintRecommended + : new Error(`Unknown extension name: '${extensionName}`); + + // Act + const { importedExtensions } = await retrieveExtendsValues( + { importer }, + "eslint:recommended", + ); + + // Assert + expect(importedExtensions).toEqual([eslintRecommended]); + }); + + it("reports a failure when an extension fails to import", async () => { + // Arrange + const error = new Error("Oh no"); + const importer = async () => error; + + // Act + const { configurationErrors } = await retrieveExtendsValues({ importer }, "extension-name"); + + // Assert + expect(configurationErrors).toEqual([expect.objectContaining({ error })]); + }); + + it("retrieves an extension when an import succeeds", async () => { + // Arrange + const extension = { rules: {} }; + const importer = async () => extension; + + // Act + const { importedExtensions } = await retrieveExtendsValues({ importer }, "extension-name"); + + // Assert + expect(importedExtensions).toEqual([extension]); + }); + + it("retrieves multiple extensions when multiple are provided", async () => { + // Arrange + const extensions = { + "eslint-plugin-one": { + rules: { + "rule-one": {}, + }, + }, + "eslint-plugin-two": { + rules: { + "rule-two": {}, + }, + }, + } as const; + const importer = async (extensionName: string) => + extensions[extensionName as keyof typeof extensions]; + + // Act + const { importedExtensions } = await retrieveExtendsValues( + { importer }, + Object.keys(extensions), + ); + + // Assert + expect(importedExtensions).toEqual(Object.values(extensions)); + }); +}); diff --git a/src/creation/simplification/retrieveExtendsValues.ts b/src/creation/simplification/retrieveExtendsValues.ts new file mode 100644 index 000000000..aedb5334e --- /dev/null +++ b/src/creation/simplification/retrieveExtendsValues.ts @@ -0,0 +1,61 @@ +import { Importer } from "../../adapters/importer"; +import { ConfigurationError } from "../../errors/configurationError"; +import { ESLintConfiguration } from "../../input/findESLintConfiguration"; +import { resolveExtensionNames } from "./resolveExtensionNames"; + +export type RetrieveExtendsValuesDependencies = { + importer: Importer; +}; + +export type RetrievedExtensionValues = { + configurationErrors: ConfigurationError[]; + importedExtensions: Partial[]; +}; + +const builtInExtensionGetters = new Map< + string, + (importer: Importer) => Promise +>([ + [ + "eslint:all", + async importer => (await importer("eslint/conf/eslint-all")) as ESLintConfiguration, + ], + [ + "eslint:recommended", + async importer => (await importer("eslint/conf/eslint-recommended")) as ESLintConfiguration, + ], +]); + +export const retrieveExtendsValues = async ( + dependencies: RetrieveExtendsValuesDependencies, + rawExtensionNames: string | string[], +): Promise => { + const importedExtensions: Partial[] = []; + const configurationErrors: ConfigurationError[] = []; + const extensionNames = resolveExtensionNames(rawExtensionNames); + + await Promise.all( + extensionNames.map(async extensionName => { + const getBuiltInExtension = builtInExtensionGetters.get(extensionName); + if (getBuiltInExtension !== undefined) { + importedExtensions.push(await getBuiltInExtension(dependencies.importer)); + return; + } + + const imported = await dependencies.importer(extensionName); + + if (imported instanceof Error) { + configurationErrors.push( + new ConfigurationError( + imported, + `Could not resolve ESLint extension '${extensionName}'.`, + ), + ); + } else { + importedExtensions.push(imported as Partial); + } + }), + ); + + return { configurationErrors, importedExtensions }; +}; diff --git a/src/creation/simplification/simplifyPackageRules.test.ts b/src/creation/simplification/simplifyPackageRules.test.ts new file mode 100644 index 000000000..b95b73075 --- /dev/null +++ b/src/creation/simplification/simplifyPackageRules.test.ts @@ -0,0 +1,103 @@ +import { ConfigurationError } from "../../errors/configurationError"; +import { ESLintRuleOptions } from "../../rules/types"; +import { createEmptyConversionResults } from "../../conversion/conversionResults.stubs"; +import { simplifyPackageRules } from "./simplifyPackageRules"; + +const createStubDependencies = () => ({ + removeExtendsDuplicatedRules: jest.fn(), + retrieveExtendsValues: jest.fn(), +}); + +describe("simplifyPackageRules", () => { + it("returns the conversion results directly when there is no loaded eslint configuration", async () => { + // Arrange + const dependencies = createStubDependencies(); + const eslint = undefined; + const ruleConversionResults = createEmptyConversionResults(); + + // Act + const simplifiedResults = await simplifyPackageRules( + dependencies, + eslint, + ruleConversionResults, + ); + + // Assert + expect(simplifiedResults).toBe(ruleConversionResults); + }); + + it("returns the conversion results directly when the eslint configuration doesn't extend", async () => { + // Arrange + const dependencies = createStubDependencies(); + const eslint = {}; + const ruleConversionResults = createEmptyConversionResults(); + + // Act + const simplifiedResults = await simplifyPackageRules( + dependencies, + eslint, + ruleConversionResults, + ); + + // Assert + expect(simplifiedResults).toBe(ruleConversionResults); + }); + + it("returns the conversion results directly when the eslint configuration has an empty extends", async () => { + // Arrange + const dependencies = createStubDependencies(); + const eslint = { + extends: [], + }; + const ruleConversionResults = createEmptyConversionResults(); + + // Act + const simplifiedResults = await simplifyPackageRules( + dependencies, + eslint, + ruleConversionResults, + ); + + // Assert + expect(simplifiedResults).toBe(ruleConversionResults); + }); + + it("includes deduplicated rules and extension failures when the eslint configuration extends", async () => { + // Arrange + const configurationErrors = [new ConfigurationError(new Error("oh no"), "darn")]; + const deduplicatedRules = new Map([ + [ + "rule-name", + { + ruleArguments: [], + ruleName: "rule-name", + ruleSeverity: "warn", + }, + ], + ]); + const dependencies = { + removeExtendsDuplicatedRules: () => deduplicatedRules, + retrieveExtendsValues: async () => ({ + configurationErrors, + importedExtensions: [], + }), + }; + const eslint = { + extends: ["extension-name"], + }; + const ruleConversionResults = createEmptyConversionResults(); + + // Act + const simplifiedResults = await simplifyPackageRules( + dependencies, + eslint, + ruleConversionResults, + ); + + // Assert + expect(simplifiedResults).toEqual({ + converted: deduplicatedRules, + failed: configurationErrors, + }); + }); +}); diff --git a/src/creation/simplification/simplifyPackageRules.ts b/src/creation/simplification/simplifyPackageRules.ts new file mode 100644 index 000000000..550fff9f6 --- /dev/null +++ b/src/creation/simplification/simplifyPackageRules.ts @@ -0,0 +1,36 @@ +import { SansDependencies } from "../../binding"; +import { RuleConversionResults } from "../../rules/convertRules"; +import { removeExtendsDuplicatedRules } from "./removeExtendsDuplicatedRules"; +import { retrieveExtendsValues } from "./retrieveExtendsValues"; +import { ESLintConfiguration } from "../../input/findESLintConfiguration"; + +export type SimplifyPackageRulesDependencies = { + removeExtendsDuplicatedRules: typeof removeExtendsDuplicatedRules; + retrieveExtendsValues: SansDependencies; +}; + +export type SimplifiedRuleConversionResults = Pick; + +export const simplifyPackageRules = async ( + dependencies: SimplifyPackageRulesDependencies, + eslint: Partial | undefined, + ruleConversionResults: SimplifiedRuleConversionResults, +): Promise => { + if (eslint === undefined || eslint.extends === undefined || eslint.extends.length === 0) { + return ruleConversionResults; + } + + const { configurationErrors, importedExtensions } = await dependencies.retrieveExtendsValues( + eslint.extends, + ); + + const converted = dependencies.removeExtendsDuplicatedRules( + ruleConversionResults.converted, + importedExtensions, + ); + + return { + converted, + failed: [...ruleConversionResults.failed, ...configurationErrors], + }; +}; diff --git a/src/creation/writeConversionResults.test.ts b/src/creation/writeConversionResults.test.ts index 65258c19d..c6601772c 100644 --- a/src/creation/writeConversionResults.test.ts +++ b/src/creation/writeConversionResults.test.ts @@ -1,12 +1,14 @@ import { createEmptyConversionResults } from "../conversion/conversionResults.stubs"; import { writeConversionResults } from "./writeConversionResults"; +import { OriginalConfigurations } from "../input/findOriginalConfigurations"; -const originalConfigurations = { +const createStubOriginalConfigurations = (overrides: Partial = {}) => ({ tslint: { rulesDirectory: [], rules: {}, }, -}; + ...overrides, +}); describe("writeConversionResults", () => { it("excludes the tslint plugin when there are no missing rules", async () => { @@ -17,7 +19,11 @@ describe("writeConversionResults", () => { const fileSystem = { writeFile: jest.fn().mockReturnValue(Promise.resolve()) }; // Act - await writeConversionResults({ fileSystem }, conversionResults, originalConfigurations); + await writeConversionResults( + { fileSystem }, + conversionResults, + createStubOriginalConfigurations(), + ); // Assert expect(fileSystem.writeFile).toHaveBeenLastCalledWith( @@ -58,7 +64,11 @@ describe("writeConversionResults", () => { const fileSystem = { writeFile: jest.fn().mockReturnValue(Promise.resolve()) }; // Act - await writeConversionResults({ fileSystem }, conversionResults, originalConfigurations); + await writeConversionResults( + { fileSystem }, + conversionResults, + createStubOriginalConfigurations(), + ); // Assert expect(fileSystem.writeFile).toHaveBeenLastCalledWith( @@ -92,4 +102,50 @@ describe("writeConversionResults", () => { ), ); }); + + it("includes the original eslint configuration when it exists", async () => { + // Arrange + const conversionResults = createEmptyConversionResults({ + converted: new Map(), + }); + const eslint = { + env: {}, + extends: [], + globals: { + Promise: true, + }, + rules: {}, + }; + const originalConfigurations = createStubOriginalConfigurations({ + eslint, + }); + const fileSystem = { writeFile: jest.fn().mockReturnValue(Promise.resolve()) }; + + // Act + await writeConversionResults({ fileSystem }, conversionResults, originalConfigurations); + + // Assert + expect(fileSystem.writeFile).toHaveBeenLastCalledWith( + ".eslintrc.json", + JSON.stringify( + { + ...eslint, + env: { + browser: true, + es6: true, + node: true, + }, + parser: "@typescript-eslint/parser", + parserOptions: { + project: "tsconfig.json", + sourceType: "module", + }, + plugins: ["@typescript-eslint"], + rules: {}, + }, + undefined, + 4, + ), + ); + }); }); diff --git a/src/creation/writeConversionResults.ts b/src/creation/writeConversionResults.ts index f09fe8655..dca67ecd4 100644 --- a/src/creation/writeConversionResults.ts +++ b/src/creation/writeConversionResults.ts @@ -18,7 +18,9 @@ export const writeConversionResults = async ( if (ruleConversionResults.missing.length !== 0) { plugins.push("@typescript-eslint/tslint"); } + const output = { + ...(originalConfigurations.eslint && originalConfigurations.eslint), env: createEnv(originalConfigurations), parser: "@typescript-eslint/parser", parserOptions: { diff --git a/src/errors/configurationError.ts b/src/errors/configurationError.ts new file mode 100644 index 000000000..5b7662725 --- /dev/null +++ b/src/errors/configurationError.ts @@ -0,0 +1,3 @@ +export class ConfigurationError { + public constructor(public readonly error: Error, public readonly complaint: string) {} +} diff --git a/src/rules/conversionError.ts b/src/errors/conversionError.ts similarity index 75% rename from src/rules/conversionError.ts rename to src/errors/conversionError.ts index 367db4a00..9a06ee374 100644 --- a/src/rules/conversionError.ts +++ b/src/errors/conversionError.ts @@ -1,4 +1,4 @@ -import { TSLintRuleOptions } from "./types"; +import { TSLintRuleOptions } from "../rules/types"; export class ConversionError { public constructor( diff --git a/src/input/findESLintConfiguration.test.ts b/src/input/findESLintConfiguration.test.ts index 0615c990d..1eb08b261 100644 --- a/src/input/findESLintConfiguration.test.ts +++ b/src/input/findESLintConfiguration.test.ts @@ -54,6 +54,7 @@ describe("findESLintConfiguration", () => { // Assert expect(result).toEqual({ env: {}, + extends: [], rules: {}, }); }); diff --git a/src/input/findESLintConfiguration.ts b/src/input/findESLintConfiguration.ts index 179c82ccb..f5956115b 100644 --- a/src/input/findESLintConfiguration.ts +++ b/src/input/findESLintConfiguration.ts @@ -1,16 +1,28 @@ +import { ESLintRuleSeverity } from "../rules/types"; import { findConfiguration, FindConfigurationDependencies } from "./findConfiguration"; export type ESLintConfiguration = { env: { [i: string]: boolean; }; - rules: { - [i: string]: number | [string, any]; - }; + extends: string | string[]; + rules: ESLintConfigurationRules; +}; + +export type ESLintConfigurationRules = { + [i: string]: ESLintConfigurationRuleValue; }; +export type ESLintConfigurationRuleValue = + | 0 + | 1 + | 2 + | ESLintRuleSeverity + | [ESLintRuleSeverity, any]; + const defaultESLintConfiguration = { env: {}, + extends: [], rules: {}, }; diff --git a/src/input/findOriginalConfigurations.test.ts b/src/input/findOriginalConfigurations.test.ts index c2a36a815..6998a2794 100644 --- a/src/input/findOriginalConfigurations.test.ts +++ b/src/input/findOriginalConfigurations.test.ts @@ -13,6 +13,7 @@ const createRawSettings = () => ({ const createDependencies = (overrides: Partial = {}) => ({ findESLintConfiguration: async () => ({ env: {}, + extends: [], rules: {}, }), findPackagesConfiguration: async () => ({ @@ -84,6 +85,7 @@ describe("findOriginalConfigurations", () => { data: { eslint: { env: {}, + extends: [], rules: {}, }, packages: { diff --git a/src/reporting/reportConversionResults.test.ts b/src/reporting/reportConversionResults.test.ts index ddc3e6477..bbd335d2d 100644 --- a/src/reporting/reportConversionResults.test.ts +++ b/src/reporting/reportConversionResults.test.ts @@ -1,8 +1,9 @@ -import { ConversionError } from "../rules/conversionError"; import { ESLintRuleOptions } from "../rules/types"; import { reportConversionResults } from "./reportConversionResults"; import { createStubLogger, expectEqualWrites } from "../adapters/logger.stubs"; import { createEmptyConversionResults } from "../conversion/conversionResults.stubs"; +import { ConversionError } from "../errors/conversionError"; +import { ConfigurationError } from "../errors/configurationError"; describe("reportConversionResults", () => { it("logs a successful conversion when there is one converted rule", () => { @@ -64,6 +65,25 @@ describe("reportConversionResults", () => { ); }); + it("logs a failed configuration when there is one failed configuration error", () => { + // Arrange + const conversionResults = createEmptyConversionResults({ + failed: [new ConfigurationError(new Error("and a one"), "some complaint")], + }); + + const logger = createStubLogger(); + + // Act + reportConversionResults({ logger }, conversionResults); + + // Assert + expectEqualWrites( + logger.stderr.write, + "💀 1 error thrown. 💀", + `Check ${logger.debugFileName} for details.`, + ); + }); + it("logs a failed conversion when there is one failed conversion", () => { // Arrange const conversionResults = createEmptyConversionResults({ @@ -84,7 +104,7 @@ describe("reportConversionResults", () => { // Assert expectEqualWrites( logger.stderr.write, - "💀 1 rule threw an error; using eslint-plugin-tslint instead. 💀", + "💀 1 error thrown. 💀", `Check ${logger.debugFileName} for details.`, ); }); @@ -114,7 +134,7 @@ describe("reportConversionResults", () => { // Assert expectEqualWrites( logger.stderr.write, - "💀 2 rules threw errors; using eslint-plugin-tslint instead. 💀", + "💀 2 errors thrown. 💀", `Check ${logger.debugFileName} for details.`, ); }); @@ -181,10 +201,10 @@ describe("reportConversionResults", () => { ); }); - it("logs a missing package when there is a missing package", () => { + it("logs a missing plugin when there is a missing plugin", () => { // Arrange const conversionResults = createEmptyConversionResults({ - packages: new Set(["package-one"]), + plugins: new Set(["plugin-one"]), }); const logger = createStubLogger(); @@ -196,14 +216,14 @@ describe("reportConversionResults", () => { expectEqualWrites( logger.stdout.write, "⚡ 1 package is required for new ESLint rules. ⚡", - "\tpackage-one", + "\tplugin-one", ); }); - it("logs missing packages when there are missing packages", () => { + it("logs missing plugins when there are missing plugins", () => { // Arrange const conversionResults = createEmptyConversionResults({ - packages: new Set(["package-one", "package-two"]), + plugins: new Set(["plugin-one", "plugin-two"]), }); const logger = createStubLogger(); @@ -215,8 +235,8 @@ describe("reportConversionResults", () => { expectEqualWrites( logger.stdout.write, "⚡ 2 packages are required for new ESLint rules. ⚡", - "\tpackage-one", - "\tpackage-two", + "\tplugin-one", + "\tplugin-two", ); }); }); diff --git a/src/reporting/reportConversionResults.ts b/src/reporting/reportConversionResults.ts index dd32b4217..4ff8dce5d 100644 --- a/src/reporting/reportConversionResults.ts +++ b/src/reporting/reportConversionResults.ts @@ -2,7 +2,8 @@ import chalk from "chalk"; import { EOL } from "os"; import { Logger } from "../adapters/logger"; -import { ConversionError } from "../rules/conversionError"; +import { ConfigurationError } from "../errors/configurationError"; +import { ConversionError } from "../errors/conversionError"; import { RuleConversionResults } from "../rules/convertRules"; import { TSLintRuleOptions, ESLintRuleOptions } from "../rules/types"; @@ -26,8 +27,8 @@ export const reportConversionResults = ( logMissingRules(ruleConversionResults.missing, dependencies.logger); } - if (ruleConversionResults.packages.size !== 0) { - logMissingPackages(ruleConversionResults.packages, dependencies.logger); + if (ruleConversionResults.plugins.size !== 0) { + logMissingPlugins(ruleConversionResults.plugins, dependencies.logger); } }; @@ -41,19 +42,18 @@ const logSuccessfulConversions = (converted: Map, log logger.stdout.write(chalk.greenBright(` ✨${EOL}`)); }; -const logFailedConversions = (failed: ConversionError[], logger: Logger) => { +const logFailedConversions = (failed: (ConfigurationError | ConversionError)[], logger: Logger) => { logger.stderr.write(`${chalk.redBright(`💀 ${failed.length}`)}`); - logger.stderr.write( - chalk.red(` rule${failed.length === 1 ? " threw an error" : "s threw errors"}`), - ); - logger.stderr.write(chalk.red("; using eslint-plugin-tslint instead.")); + logger.stderr.write(chalk.red(` error${failed.length === 1 ? "" : "s"}`)); + logger.stderr.write(chalk.red(" thrown.")); logger.stderr.write(chalk.redBright(` 💀${EOL}`)); logger.info.write( failed - .map( - failed => - `${failed.tslintRule.ruleName} threw an error during conversion: ${failed.error.stack}.${EOL}`, + .map(failed => + failed instanceof ConfigurationError + ? `${failed.complaint}: ${failed.error.stack}${EOL}` + : `${failed.tslintRule.ruleName} threw an error during conversion: ${failed.error.stack}${EOL}`, ) .join(""), ); @@ -80,16 +80,16 @@ const logMissingRules = (missing: TSLintRuleOptions[], logger: Logger) => { ); }; -const logMissingPackages = (packages: Set, logger: Logger) => { - logger.stdout.write(chalk.cyanBright(`⚡ ${packages.size}`)); +const logMissingPlugins = (plugins: Set, logger: Logger) => { + logger.stdout.write(chalk.cyanBright(`⚡ ${plugins.size}`)); logger.stdout.write(chalk.cyan(" package")); - logger.stdout.write(chalk.cyan(packages.size === 1 ? " is" : "s are")); + logger.stdout.write(chalk.cyan(plugins.size === 1 ? " is" : "s are")); logger.stdout.write(chalk.cyan(` required for new ESLint rules.`)); logger.stdout.write(chalk.cyanBright(` ⚡${EOL}`)); logger.stdout.write( - Array.from(packages) - .map(packageName => `\t${chalk.cyanBright(packageName)}${EOL}`) + Array.from(plugins) + .map(pluginName => `\t${chalk.cyanBright(pluginName)}${EOL}`) .join(""), ); }; diff --git a/src/rules/convertRule.test.ts b/src/rules/convertRule.test.ts index 3448d6efe..74776ae84 100644 --- a/src/rules/convertRule.test.ts +++ b/src/rules/convertRule.test.ts @@ -1,6 +1,6 @@ +import { ConversionError } from "../errors/conversionError"; import { convertRule } from "./convertRule"; import { RuleConverter } from "./converter"; -import { ConversionError } from "./conversionError"; import { TSLintRuleOptions } from "./types"; describe("convertRule", () => { diff --git a/src/rules/convertRule.ts b/src/rules/convertRule.ts index a9a4622c5..80a6ded7c 100644 --- a/src/rules/convertRule.ts +++ b/src/rules/convertRule.ts @@ -1,6 +1,6 @@ -import { TSLintRuleOptions } from "./types"; -import { ConversionError } from "./conversionError"; +import { ConversionError } from "../errors/conversionError"; import { RuleConverter } from "./converter"; +import { TSLintRuleOptions } from "./types"; export const convertRule = ( tslintRule: TSLintRuleOptions, diff --git a/src/rules/convertRuleSeverity.test.ts b/src/rules/convertRuleSeverity.test.ts index a200969dc..c72b200d2 100644 --- a/src/rules/convertRuleSeverity.test.ts +++ b/src/rules/convertRuleSeverity.test.ts @@ -1,4 +1,4 @@ -import { convertRuleSeverity } from "./convertRuleSeverity"; +import { convertTSLintRuleSeverity, convertRawESLintRuleSeverity } from "./convertRuleSeverity"; describe("convertRuleSeverity", () => { it("returns error when the severity is error", () => { @@ -6,7 +6,7 @@ describe("convertRuleSeverity", () => { const tslintSeverity = "error"; // Act - const eslintSeverity = convertRuleSeverity(tslintSeverity); + const eslintSeverity = convertTSLintRuleSeverity(tslintSeverity); // Assert expect(eslintSeverity).toEqual("error"); @@ -17,7 +17,7 @@ describe("convertRuleSeverity", () => { const tslintSeverity = "off"; // Act - const eslintSeverity = convertRuleSeverity(tslintSeverity); + const eslintSeverity = convertTSLintRuleSeverity(tslintSeverity); // Assert expect(eslintSeverity).toEqual("off"); @@ -28,9 +28,55 @@ describe("convertRuleSeverity", () => { const tslintSeverity = "warning"; // Act - const eslintSeverity = convertRuleSeverity(tslintSeverity); + const eslintSeverity = convertTSLintRuleSeverity(tslintSeverity); // Assert expect(eslintSeverity).toEqual("warn"); }); }); + +describe("convertRawESLintRuleSeverity", () => { + it("returns off when the severity is 0", () => { + // Arrange + const rawSeverity = 0; + + // Act + const converted = convertRawESLintRuleSeverity(rawSeverity); + + // Assert + expect(converted).toEqual("off"); + }); + + it("returns off when the severity is 2", () => { + // Arrange + const rawSeverity = 1; + + // Act + const converted = convertRawESLintRuleSeverity(rawSeverity); + + // Assert + expect(converted).toEqual("warn"); + }); + + it("returns off when the severity is 3", () => { + // Arrange + const rawSeverity = 2; + + // Act + const converted = convertRawESLintRuleSeverity(rawSeverity); + + // Assert + expect(converted).toEqual("error"); + }); + + it("returns the original severity when it's a string", () => { + // Arrange + const rawSeverity = "warn"; + + // Act + const converted = convertRawESLintRuleSeverity(rawSeverity); + + // Assert + expect(converted).toEqual("warn"); + }); +}); diff --git a/src/rules/convertRuleSeverity.ts b/src/rules/convertRuleSeverity.ts index a33b957f5..861a1eab4 100644 --- a/src/rules/convertRuleSeverity.ts +++ b/src/rules/convertRuleSeverity.ts @@ -3,6 +3,26 @@ import { ESLintRuleSeverity, TSLintRuleSeverity } from "./types"; /** * Converts a TSLint rule severity string to the ESLint equivalent. */ -export const convertRuleSeverity = (tslintSeverity: TSLintRuleSeverity): ESLintRuleSeverity => { +export const convertTSLintRuleSeverity = ( + tslintSeverity: TSLintRuleSeverity, +): ESLintRuleSeverity => { return tslintSeverity === "warning" ? "warn" : tslintSeverity; }; + +export const convertRawESLintRuleSeverity = ( + rawSeverity: 0 | 1 | 2 | ESLintRuleSeverity, +): ESLintRuleSeverity => { + switch (rawSeverity) { + case 0: + return "off"; + + case 1: + return "warn"; + + case 2: + return "error"; + + default: + return rawSeverity; + } +}; diff --git a/src/rules/convertRules.test.ts b/src/rules/convertRules.test.ts index 5b3c3518d..bb1108904 100644 --- a/src/rules/convertRules.test.ts +++ b/src/rules/convertRules.test.ts @@ -1,6 +1,6 @@ +import { ConversionError } from "../errors/conversionError"; import { convertRules } from "./convertRules"; import { TSLintRuleOptions } from "./types"; -import { ConversionError } from "./conversionError"; describe("convertRules", () => { it("doesn't marks a disabled rule as missing when its converter returns undefined", () => { @@ -182,7 +182,7 @@ describe("convertRules", () => { ); }); - it("marks a new package when a conversion has a new package", () => { + it("marks a new plugin when a conversion has a new plugin", () => { // Arrange const tslintRule: TSLintRuleOptions = { ruleArguments: [], @@ -190,19 +190,19 @@ describe("convertRules", () => { ruleSeverity: "error", }; const conversionResult = { - packages: ["extra-package"], + plugins: ["extra-plugin"], rules: [], }; const converters = new Map([[tslintRule.ruleName, () => conversionResult]]); const mergers = new Map(); // Act - const { packages } = convertRules( + const { plugins } = convertRules( { converters, mergers }, { [tslintRule.ruleName]: tslintRule }, ); // Assert - expect(packages).toEqual(new Set(["extra-package"])); + expect(plugins).toEqual(new Set(["extra-plugin"])); }); }); diff --git a/src/rules/convertRules.ts b/src/rules/convertRules.ts index d5bf1763a..31fef3f25 100644 --- a/src/rules/convertRules.ts +++ b/src/rules/convertRules.ts @@ -1,8 +1,9 @@ +import { ConfigurationError } from "../errors/configurationError"; +import { ConversionError } from "../errors/conversionError"; import { TSLintConfigurationRules } from "../input/findTSLintConfiguration"; -import { ConversionError } from "./conversionError"; import { RuleConverter } from "./converter"; import { convertRule } from "./convertRule"; -import { convertRuleSeverity } from "./convertRuleSeverity"; +import { convertTSLintRuleSeverity } from "./convertRuleSeverity"; import { formatRawTslintRule } from "./formatRawTslintRule"; import { RuleMerger } from "./merger"; import { TSLintRuleOptions, ESLintRuleOptions } from "./types"; @@ -14,9 +15,9 @@ export type ConvertRulesDependencies = { export type RuleConversionResults = { converted: Map; - failed: ConversionError[]; + failed: (ConfigurationError | ConversionError)[]; missing: TSLintRuleOptions[]; - packages: Set; + plugins: Set; }; export const convertRules = ( @@ -26,7 +27,7 @@ export const convertRules = ( const converted = new Map(); const failed: ConversionError[] = []; const missing: TSLintRuleOptions[] = []; - const packages = new Set(); + const plugins = new Set(); for (const [ruleName, value] of Object.entries(rawTslintRules)) { const tslintRule = formatRawTslintRule(ruleName, value); @@ -49,7 +50,7 @@ export const convertRules = ( const existingConversion = converted.get(changes.ruleName); const newConversion = { ...changes, - ruleSeverity: convertRuleSeverity(tslintRule.ruleSeverity), + ruleSeverity: convertTSLintRuleSeverity(tslintRule.ruleSeverity), }; if (existingConversion === undefined) { @@ -78,12 +79,12 @@ export const convertRules = ( } } - if (conversion.packages !== undefined) { - for (const newPackage of conversion.packages) { - packages.add(newPackage); + if (conversion.plugins !== undefined) { + for (const newPlugin of conversion.plugins) { + plugins.add(newPlugin); } } } - return { converted, failed, missing, packages }; + return { converted, failed, missing, plugins }; }; diff --git a/src/rules/converter.ts b/src/rules/converter.ts index 1a275b7b7..77d63fb2a 100644 --- a/src/rules/converter.ts +++ b/src/rules/converter.ts @@ -1,4 +1,4 @@ -import { ConversionError } from "./conversionError"; +import { ConversionError } from "../errors/conversionError"; import { TSLintRuleOptions } from "./types"; /** @@ -23,9 +23,9 @@ export type ConversionResult = { notices?: string[]; /** - * Any packages that should now be installed if not already. + * Any plugins that should now be installed if not already. */ - packages?: string[]; + plugins?: string[]; /** * At least one equivalent ESLint rule and options. From 228e7fcae8c3d44b824cbb47df6853951fd94b93 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Fri, 5 Jul 2019 13:28:32 -0500 Subject: [PATCH 2/2] Used formatJsonOutput in merged test --- src/creation/writeConversionResults.test.ts | 32 +++++++++------------ 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/src/creation/writeConversionResults.test.ts b/src/creation/writeConversionResults.test.ts index 52c7a29e0..5af5c928c 100644 --- a/src/creation/writeConversionResults.test.ts +++ b/src/creation/writeConversionResults.test.ts @@ -127,25 +127,21 @@ describe("writeConversionResults", () => { // Assert expect(fileSystem.writeFile).toHaveBeenLastCalledWith( ".eslintrc.json", - JSON.stringify( - { - ...eslint, - env: { - browser: true, - es6: true, - node: true, - }, - parser: "@typescript-eslint/parser", - parserOptions: { - project: "tsconfig.json", - sourceType: "module", - }, - plugins: ["@typescript-eslint"], - rules: {}, + formatJsonOutput({ + ...eslint, + env: { + browser: true, + es6: true, + node: true, + }, + parser: "@typescript-eslint/parser", + parserOptions: { + project: "tsconfig.json", + sourceType: "module", }, - undefined, - 4, - ), + plugins: ["@typescript-eslint"], + rules: {}, + }), ); }); });