diff --git a/.eslintrc.js b/.eslintrc.js index c98de62b4..981a771fe 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -38,6 +38,7 @@ module.exports = { "@typescript-eslint/no-unsafe-return": "off", "@typescript-eslint/no-untyped-public-signature": "off", "@typescript-eslint/no-unused-vars": "off", + "@typescript-eslint/no-unused-vars-experimental": "off", "@typescript-eslint/no-use-before-define": "off", "@typescript-eslint/prefer-readonly-parameter-types": "off", "@typescript-eslint/prefer-reduce-type-parameter": "off", diff --git a/src/cli/main.ts b/src/cli/main.ts index bc2883176..a55117cfc 100644 --- a/src/cli/main.ts +++ b/src/cli/main.ts @@ -44,7 +44,14 @@ import { findTSLintConfiguration } from "../input/findTSLintConfiguration"; import { findTypeScriptConfiguration } from "../input/findTypeScriptConfiguration"; import { importer, ImporterDependencies } from "../input/importer"; import { mergeLintConfigurations } from "../input/mergeLintConfigurations"; -import { choosePackageManager } from "../reporting/packages/choosePackageManager"; +import { + ChoosePackageManagerDependencies, + choosePackageManager, +} from "../reporting/packages/choosePackageManager"; +import { + logMissingPackages, + LogMissingPackagesDependencies, +} from "../reporting/packages/logMissingPackages"; import { reportConversionResults, ReportConversionResultsDependencies, @@ -90,15 +97,19 @@ const findOriginalConfigurationsDependencies: FindOriginalConfigurationsDependen mergeLintConfigurations, }; -const choosePackageManagerDependencies = { +const choosePackageManagerDependencies: ChoosePackageManagerDependencies = { fileSystem: fsFileSystem, }; -const reportConversionResultsDependencies: ReportConversionResultsDependencies = { +const logMissingPackagesDependencies: LogMissingPackagesDependencies = { choosePackageManager: bind(choosePackageManager, choosePackageManagerDependencies), logger: processLogger, }; +const reportConversionResultsDependencies: ReportConversionResultsDependencies = { + logger: processLogger, +}; + const retrieveExtendsValuesDependencies: RetrieveExtendsValuesDependencies = { importer: boundImporter, }; @@ -132,6 +143,7 @@ const convertConfigDependencies: ConvertConfigDependencies = { findOriginalConfigurations, findOriginalConfigurationsDependencies, ), + logMissingPackages: bind(logMissingPackages, logMissingPackagesDependencies), reportConversionResults: bind(reportConversionResults, reportConversionResultsDependencies), simplifyPackageRules: bind(simplifyPackageRules, simplifyPackageRulesDependencies), writeConversionResults: bind(writeConversionResults, writeConversionResultsDependencies), diff --git a/src/conversion/convertConfig.test.ts b/src/conversion/convertConfig.test.ts index c3f4c8b4f..3fa1e53ea 100644 --- a/src/conversion/convertConfig.test.ts +++ b/src/conversion/convertConfig.test.ts @@ -22,6 +22,7 @@ const createStubDependencies = ( missing: [], plugins: new Set(), }), + logMissingPackages: jest.fn().mockReturnValue(Promise.resolve()), writeConversionResults: jest.fn().mockReturnValue(Promise.resolve()), ...overrides, }); diff --git a/src/conversion/convertConfig.ts b/src/conversion/convertConfig.ts index d7bbae2f3..4d965315e 100644 --- a/src/conversion/convertConfig.ts +++ b/src/conversion/convertConfig.ts @@ -2,6 +2,7 @@ import { SansDependencies } from "../binding"; import { simplifyPackageRules } from "../creation/simplification/simplifyPackageRules"; import { writeConversionResults } from "../creation/writeConversionResults"; import { findOriginalConfigurations } from "../input/findOriginalConfigurations"; +import { logMissingPackages } from "../reporting/packages/logMissingPackages"; import { reportConversionResults } from "../reporting/reportConversionResults"; import { convertRules } from "../rules/convertRules"; import { ResultStatus, ResultWithStatus, TSLintToESLintSettings } from "../types"; @@ -9,6 +10,7 @@ import { ResultStatus, ResultWithStatus, TSLintToESLintSettings } from "../types export type ConvertConfigDependencies = { convertRules: SansDependencies; findOriginalConfigurations: SansDependencies; + logMissingPackages: SansDependencies; reportConversionResults: SansDependencies; simplifyPackageRules: SansDependencies; writeConversionResults: SansDependencies; @@ -56,6 +58,10 @@ export const convertConfig = async ( // 5. A summary of the results is printed to the user's console await dependencies.reportConversionResults(settings.config, simplifiedConfiguration); + await dependencies.logMissingPackages( + simplifiedConfiguration, + originalConfigurations.data.packages, + ); return { status: ResultStatus.Succeeded, diff --git a/src/creation/simplification/simplifyPackageRules.test.ts b/src/creation/simplification/simplifyPackageRules.test.ts index ea119d452..07266dea9 100644 --- a/src/creation/simplification/simplifyPackageRules.test.ts +++ b/src/creation/simplification/simplifyPackageRules.test.ts @@ -68,7 +68,10 @@ describe("simplifyPackageRules", () => { expect(simplifiedResults).toEqual({ ...ruleConversionResults, converted: undefined, - extends: ["eslint-config-prettier", "eslint-config-prettier/@typescript-eslint"], + extends: [ + "plugin:eslint-config-prettier", + "plugin:eslint-config-prettier/@typescript-eslint", + ], }); }); diff --git a/src/creation/simplification/simplifyPackageRules.ts b/src/creation/simplification/simplifyPackageRules.ts index e16f18b71..3de99f5b0 100644 --- a/src/creation/simplification/simplifyPackageRules.ts +++ b/src/creation/simplification/simplifyPackageRules.ts @@ -36,7 +36,10 @@ export const simplifyPackageRules = async ( // 3a. If no output rules conflict with `eslint-config-prettier`, it's added in if (await dependencies.addPrettierExtensions(ruleConversionResults, prettierRequested)) { - allExtensions.push("eslint-config-prettier", "eslint-config-prettier/@typescript-eslint"); + allExtensions.push( + "plugin:eslint-config-prettier", + "plugin:eslint-config-prettier/@typescript-eslint", + ); } if (allExtensions.length === 0) { diff --git a/src/input/findPackagesConfiguration.ts b/src/input/findPackagesConfiguration.ts index 213f657c5..42c79b160 100644 --- a/src/input/findPackagesConfiguration.ts +++ b/src/input/findPackagesConfiguration.ts @@ -4,17 +4,8 @@ import { } from "./findReportedConfiguration"; export type PackagesConfiguration = { - dependencies: { - [i: string]: string; - }; - devDependencies: { - [i: string]: string; - }; -}; - -const defaultPackagesConfiguration = { - dependencies: {}, - devDependencies: {}, + dependencies: Record; + devDependencies: Record; }; export const findPackagesConfiguration = async ( @@ -32,11 +23,9 @@ export const findPackagesConfiguration = async ( : { dependencies: { ...rawConfiguration.dependencies, - ...defaultPackagesConfiguration.dependencies, }, devDependencies: { ...rawConfiguration.devDependencies, - ...defaultPackagesConfiguration.devDependencies, }, }; }; diff --git a/src/reporting/packages/logMissingPackages.test.ts b/src/reporting/packages/logMissingPackages.test.ts new file mode 100644 index 000000000..ac1a6fb72 --- /dev/null +++ b/src/reporting/packages/logMissingPackages.test.ts @@ -0,0 +1,175 @@ +import { createStubLogger, expectEqualWrites } from "../../adapters/logger.stubs"; +import { createEmptyConversionResults } from "../../conversion/conversionResults.stubs"; +import { logMissingPackages } from "./logMissingPackages"; +import { PackageManager } from "./packageManagers"; + +const createStubDependencies = (packageManager = PackageManager.npm) => ({ + choosePackageManager: async () => packageManager, + logger: createStubLogger(), +}); + +describe("logMissingPackages", () => { + it("reports a singular message when one package is missing", async () => { + // Arrange + const { choosePackageManager, logger } = createStubDependencies(PackageManager.npm); + const ruleConversionResults = createEmptyConversionResults(); + + // Act + await logMissingPackages({ choosePackageManager, logger }, ruleConversionResults, { + dependencies: { + "@typescript-eslint/eslint-plugin": "*", + "@typescript-eslint/parser": "*", + }, + devDependencies: {}, + }); + + // Assert + expectEqualWrites( + logger.stdout.write, + `⚡ 1 new package is required for this ESLint configuration. ⚡`, + ` npm install eslint --save-dev`, + ); + }); + + it("does not include eslint-config-prettier when there are no extensions", async () => { + // Arrange + const { choosePackageManager, logger } = createStubDependencies(); + const ruleConversionResults = createEmptyConversionResults({ + extends: undefined, + }); + + // Act + await logMissingPackages({ choosePackageManager, logger }, ruleConversionResults); + + // Assert + expectEqualWrites( + logger.stdout.write, + `⚡ 3 new packages are required for this ESLint configuration. ⚡`, + ` npm install @typescript-eslint/eslint-plugin @typescript-eslint/parser eslint --save-dev`, + ); + }); + + it("does not include eslint-config-prettier when extensions don't include eslint-config-prettier", async () => { + // Arrange + const { choosePackageManager, logger } = createStubDependencies(); + const ruleConversionResults = createEmptyConversionResults({ + extends: [], + }); + + // Act + await logMissingPackages({ choosePackageManager, logger }, ruleConversionResults); + + // Assert + expectEqualWrites( + logger.stdout.write, + `⚡ 3 new packages are required for this ESLint configuration. ⚡`, + ` npm install @typescript-eslint/eslint-plugin @typescript-eslint/parser eslint --save-dev`, + ); + }); + + it("includes eslint-config-prettier when extensions include eslint-config-prettier", async () => { + // Arrange + const { choosePackageManager, logger } = createStubDependencies(); + const ruleConversionResults = createEmptyConversionResults({ + extends: ["plugin:eslint-config-prettier"], + }); + + // Act + await logMissingPackages({ choosePackageManager, logger }, ruleConversionResults); + + // Assert + expectEqualWrites( + logger.stdout.write, + `⚡ 4 new packages are required for this ESLint configuration. ⚡`, + ` npm install @typescript-eslint/eslint-plugin @typescript-eslint/parser eslint eslint-config-prettier --save-dev`, + ); + }); + + it("includes @typescript-eslint/eslint-plugin-tslint when there are missing conversions", async () => { + // Arrange + const { choosePackageManager, logger } = createStubDependencies(); + const ruleConversionResults = createEmptyConversionResults({ + missing: [ + { + ruleArguments: [], + ruleName: "missing-rule", + ruleSeverity: "error", + }, + ], + }); + + // Act + await logMissingPackages({ choosePackageManager, logger }, ruleConversionResults); + + // Assert + expectEqualWrites( + logger.stdout.write, + `⚡ 4 new packages are required for this ESLint configuration. ⚡`, + ` npm install @typescript-eslint/eslint-plugin @typescript-eslint/eslint-plugin-tslint @typescript-eslint/parser eslint --save-dev`, + ); + }); + + it("reports an npm command when the package manager is npm", async () => { + // Arrange + const { choosePackageManager, logger } = createStubDependencies(PackageManager.npm); + const ruleConversionResults = createEmptyConversionResults(); + + // Act + await logMissingPackages({ choosePackageManager, logger }, ruleConversionResults); + + // Assert + expectEqualWrites( + logger.stdout.write, + `⚡ 3 new packages are required for this ESLint configuration. ⚡`, + ` npm install @typescript-eslint/eslint-plugin @typescript-eslint/parser eslint --save-dev`, + ); + }); + + it("reports a pnpm command when the package manager is pnpm", async () => { + // Arrange + const { choosePackageManager, logger } = createStubDependencies(PackageManager.pnpm); + const ruleConversionResults = createEmptyConversionResults(); + + // Act + await logMissingPackages({ choosePackageManager, logger }, ruleConversionResults); + + // Assert + expectEqualWrites( + logger.stdout.write, + `⚡ 3 new packages are required for this ESLint configuration. ⚡`, + ` pnpm add @typescript-eslint/eslint-plugin @typescript-eslint/parser eslint --save-dev`, + ); + }); + + it("reports a Yarn command when the package manager is Yarn", async () => { + // Arrange + const { choosePackageManager, logger } = createStubDependencies(PackageManager.Yarn); + const ruleConversionResults = createEmptyConversionResults(); + + // Act + await logMissingPackages({ choosePackageManager, logger }, ruleConversionResults); + + // Assert + expectEqualWrites( + logger.stdout.write, + `⚡ 3 new packages are required for this ESLint configuration. ⚡`, + ` yarn add @typescript-eslint/eslint-plugin @typescript-eslint/parser eslint --dev`, + ); + }); + + it("reports a Yarn command when the package manager is Yarn", async () => { + // Arrange + const { choosePackageManager, logger } = createStubDependencies(PackageManager.Yarn); + const ruleConversionResults = createEmptyConversionResults(); + + // Act + await logMissingPackages({ choosePackageManager, logger }, ruleConversionResults); + + // Assert + expectEqualWrites( + logger.stdout.write, + `⚡ 3 new packages are required for this ESLint configuration. ⚡`, + ` yarn add @typescript-eslint/eslint-plugin @typescript-eslint/parser eslint --dev`, + ); + }); +}); diff --git a/src/reporting/packages/logMissingPackages.ts b/src/reporting/packages/logMissingPackages.ts new file mode 100644 index 000000000..fc7a9e5e5 --- /dev/null +++ b/src/reporting/packages/logMissingPackages.ts @@ -0,0 +1,57 @@ +import chalk from "chalk"; +import { EOL } from "os"; + +import { Logger } from "../../adapters/logger"; +import { SansDependencies } from "../../binding"; +import { SimplifiedResultsConfiguration } from "../../creation/simplification/simplifyPackageRules"; +import { PackagesConfiguration } from "../../input/findPackagesConfiguration"; +import { isTruthy } from "../../utils"; +import { installationMessages } from "../packages/packageManagers"; +import { choosePackageManager } from "./choosePackageManager"; + +export type LogMissingPackagesDependencies = { + choosePackageManager: SansDependencies; + logger: Logger; +}; + +export const logMissingPackages = async ( + dependencies: LogMissingPackagesDependencies, + ruleConversionResults: Pick, + packageConfiguration?: PackagesConfiguration, +) => { + const packageManager = await dependencies.choosePackageManager(); + + const existingPackageNames = new Set([ + ...Object.keys(packageConfiguration?.dependencies ?? {}), + ...Object.keys(packageConfiguration?.devDependencies ?? {}), + ]); + + const requiredPackageNames = [ + "@typescript-eslint/eslint-plugin", + "@typescript-eslint/parser", + ruleConversionResults.extends?.join("").includes("eslint-config-prettier") && + "eslint-config-prettier", + ruleConversionResults.missing.length !== 0 && "@typescript-eslint/eslint-plugin-tslint", + "eslint", + ...Array.from(ruleConversionResults.plugins), + ].filter(isTruthy); + + const missingPackageNames = requiredPackageNames + .filter((packageName) => !existingPackageNames.has(packageName)) + .sort(); + + dependencies.logger.stdout.write(chalk.cyanBright(`${EOL}⚡ ${missingPackageNames.length}`)); + dependencies.logger.stdout.write( + chalk.cyan( + ` new package${ + missingPackageNames.length === 1 ? " is" : "s are" + } required for this ESLint configuration.`, + ), + ); + dependencies.logger.stdout.write(chalk.cyanBright(" ⚡")); + dependencies.logger.stdout.write(`${EOL} `); + dependencies.logger.stdout.write( + chalk.cyan(installationMessages[packageManager](missingPackageNames.join(" "))), + ); + dependencies.logger.stdout.write(EOL); +}; diff --git a/src/reporting/reportConversionResults.test.ts b/src/reporting/reportConversionResults.test.ts index 0b6a6e164..4da981b76 100644 --- a/src/reporting/reportConversionResults.test.ts +++ b/src/reporting/reportConversionResults.test.ts @@ -3,21 +3,17 @@ import { EOL } from "os"; import { createStubLogger, expectEqualWrites } from "../adapters/logger.stubs"; import { createEmptyConversionResults } from "../conversion/conversionResults.stubs"; import { ESLintRuleOptions } from "../rules/types"; -import { PackageManager } from "./packages/packageManagers"; import { reportConversionResults } from "./reportConversionResults"; -const createStubDependencies = (packageManager = PackageManager.Yarn) => { - const choosePackageManager = jest.fn().mockResolvedValueOnce(packageManager); - const logger = createStubLogger(); - - return { choosePackageManager, logger }; -}; - -const basicExtends = ["eslint-config-prettier", "eslint-config-prettier/@typescript-eslint"]; +const basicExtends = [ + "plugin:eslint-config-prettier", + "plugin:eslint-config-prettier/@typescript-eslint", +]; describe("reportConversionResults", () => { it("logs a successful conversion without notices when there is one converted rule without notices", async () => { // Arrange + const logger = createStubLogger(); const conversionResults = createEmptyConversionResults({ converted: new Map([ [ @@ -32,27 +28,16 @@ describe("reportConversionResults", () => { extends: basicExtends, }); - const { choosePackageManager, logger } = createStubDependencies(); - // Act - await reportConversionResults( - { choosePackageManager, logger }, - ".eslintrc.js", - conversionResults, - ); + await reportConversionResults({ logger }, ".eslintrc.js", conversionResults); // Assert - expectEqualWrites( - logger.stdout.write, - `✨ 1 rule replaced with its ESLint equivalent. ✨`, - ``, - `⚡ 4 packages are required for this ESLint configuration. ⚡`, - ` yarn add @typescript-eslint/eslint-plugin @typescript-eslint/parser eslint eslint-config-prettier --dev`, - ); + expectEqualWrites(logger.stdout.write, `✨ 1 rule replaced with its ESLint equivalent. ✨`); }); it("logs a successful conversion with notices when there is one converted rule with notices", async () => { // Arrange + const logger = createStubLogger(); const conversionResults = createEmptyConversionResults({ converted: new Map([ [ @@ -68,14 +53,8 @@ describe("reportConversionResults", () => { extends: basicExtends, }); - const { choosePackageManager, logger } = createStubDependencies(); - // Act - await reportConversionResults( - { choosePackageManager, logger }, - ".eslintrc.js", - conversionResults, - ); + await reportConversionResults({ logger }, ".eslintrc.js", conversionResults); // Assert expectEqualWrites( @@ -83,9 +62,6 @@ describe("reportConversionResults", () => { `✨ 1 rule replaced with its ESLint equivalent. ✨${EOL}`, `❗ 1 ESLint rule behaves differently from its TSLint counterpart ❗`, ` Check ${logger.debugFileName} for details.`, - ``, - `⚡ 4 packages are required for this ESLint configuration. ⚡`, - ` yarn add @typescript-eslint/eslint-plugin @typescript-eslint/parser eslint eslint-config-prettier --dev`, ); expectEqualWrites( logger.info.write, @@ -98,6 +74,7 @@ describe("reportConversionResults", () => { it("logs successful conversions when there are multiple converted rules", async () => { // Arrange + const logger = createStubLogger(); const conversionResults = createEmptyConversionResults({ converted: new Map([ [ @@ -122,14 +99,8 @@ describe("reportConversionResults", () => { extends: basicExtends, }); - const { choosePackageManager, logger } = createStubDependencies(); - // Act - await reportConversionResults( - { choosePackageManager, logger }, - ".eslintrc.js", - conversionResults, - ); + await reportConversionResults({ logger }, ".eslintrc.js", conversionResults); // Assert expectEqualWrites( @@ -138,9 +109,6 @@ describe("reportConversionResults", () => { ``, `❗ 2 ESLint rules behave differently from their TSLint counterparts ❗`, ` Check ${logger.debugFileName} for details.`, - ``, - `⚡ 4 packages are required for this ESLint configuration. ⚡`, - ` yarn add @typescript-eslint/eslint-plugin @typescript-eslint/parser eslint eslint-config-prettier --dev`, ); expectEqualWrites( logger.info.write, @@ -156,19 +124,14 @@ describe("reportConversionResults", () => { it("logs a failed conversion when there is one failed conversion", async () => { // Arrange + const logger = createStubLogger(); const conversionResults = createEmptyConversionResults({ failed: [{ getSummary: () => "It broke." }], extends: basicExtends, }); - const { choosePackageManager, logger } = createStubDependencies(); - // Act - await reportConversionResults( - { choosePackageManager, logger }, - ".eslintrc.js", - conversionResults, - ); + await reportConversionResults({ logger }, ".eslintrc.js", conversionResults); // Assert expectEqualWrites( @@ -180,19 +143,14 @@ describe("reportConversionResults", () => { it("logs failed conversions when there are multiple failed conversions", async () => { // Arrange + const logger = createStubLogger(); const conversionResults = createEmptyConversionResults({ extends: basicExtends, failed: [{ getSummary: () => "It broke." }, { getSummary: () => "It really broke." }], }); - const { choosePackageManager, logger } = createStubDependencies(); - // Act - await reportConversionResults( - { choosePackageManager, logger }, - ".eslintrc.js", - conversionResults, - ); + await reportConversionResults({ logger }, ".eslintrc.js", conversionResults); // Assert expectEqualWrites( @@ -204,6 +162,7 @@ describe("reportConversionResults", () => { it("logs a missing rule when there is a missing rule", async () => { // Arrange + const logger = createStubLogger(); const conversionResults = createEmptyConversionResults({ extends: basicExtends, missing: [ @@ -215,14 +174,8 @@ describe("reportConversionResults", () => { ], }); - const { choosePackageManager, logger } = createStubDependencies(); - // Act - await reportConversionResults( - { choosePackageManager, logger }, - ".eslintrc.js", - conversionResults, - ); + await reportConversionResults({ logger }, ".eslintrc.js", conversionResults); // Assert expectEqualWrites( @@ -230,9 +183,6 @@ describe("reportConversionResults", () => { `❓ 1 rule is not known by tslint-to-eslint-config to have an ESLint equivalent. ❓`, ` The "@typescript-eslint/tslint/config" section of .eslintrc.js configures eslint-plugin-tslint to run it in TSLint within ESLint.`, ` Check ${logger.debugFileName} for details.`, - ``, - `⚡ 5 packages are required for this ESLint configuration. ⚡`, - ` yarn add @typescript-eslint/eslint-plugin @typescript-eslint/eslint-plugin-tslint @typescript-eslint/parser eslint eslint-config-prettier --dev`, ); expectEqualWrites( logger.info.write, @@ -243,6 +193,7 @@ describe("reportConversionResults", () => { it("logs missing rules when there are missing rules", async () => { // Arrange + const logger = createStubLogger(); const conversionResults = createEmptyConversionResults({ extends: basicExtends, missing: [ @@ -259,14 +210,8 @@ describe("reportConversionResults", () => { ], }); - const { choosePackageManager, logger } = createStubDependencies(); - // Act - await reportConversionResults( - { choosePackageManager, logger }, - ".eslintrc.js", - conversionResults, - ); + await reportConversionResults({ logger }, ".eslintrc.js", conversionResults); // Assert expectEqualWrites( @@ -274,9 +219,6 @@ describe("reportConversionResults", () => { `❓ 2 rules are not known by tslint-to-eslint-config to have ESLint equivalents. ❓`, ` The "@typescript-eslint/tslint/config" section of .eslintrc.js configures eslint-plugin-tslint to run them in TSLint within ESLint.`, ` Check ${logger.debugFileName} for details.`, - ``, - `⚡ 5 packages are required for this ESLint configuration. ⚡`, - ` yarn add @typescript-eslint/eslint-plugin @typescript-eslint/eslint-plugin-tslint @typescript-eslint/parser eslint eslint-config-prettier --dev`, ); expectEqualWrites( logger.info.write, @@ -286,51 +228,38 @@ describe("reportConversionResults", () => { ); }); - it("logs missing plugins when there are missing plugins", async () => { + it("logs a Prettier recommendation when there are no extensions", async () => { // Arrange + const logger = createStubLogger(); const conversionResults = createEmptyConversionResults({ - extends: basicExtends, - plugins: new Set(["plugin-one", "plugin-two"]), + extends: undefined, }); - const { choosePackageManager, logger } = createStubDependencies(); - // Act - await reportConversionResults( - { choosePackageManager, logger }, - ".eslintrc.js", - conversionResults, - ); + await reportConversionResults({ logger }, ".eslintrc.js", conversionResults); // Assert expectEqualWrites( logger.stdout.write, - `⚡ 6 packages are required for this ESLint configuration. ⚡`, - ` yarn add @typescript-eslint/eslint-plugin @typescript-eslint/parser eslint eslint-config-prettier plugin-one plugin-two --dev`, + `☠ Prettier plugins are missing from your configuration. ☠`, + ` We highly recommend running tslint-to-eslint-config --prettier to disable formatting ESLint rules.`, + ` See https://github/typescript-eslint/tslint-to-eslint-config/docs/FAQs.md#should-i-use-prettier.`, ); }); - it("logs a Prettier recommendation when eslint-config-prettier isn't extended", async () => { + it("logs a Prettier recommendation when extends don't include eslint-config-prettier", async () => { // Arrange + const logger = createStubLogger(); const conversionResults = createEmptyConversionResults({ extends: [], }); - const { choosePackageManager, logger } = createStubDependencies(); - // Act - await reportConversionResults( - { choosePackageManager, logger }, - ".eslintrc.js", - conversionResults, - ); + await reportConversionResults({ logger }, ".eslintrc.js", conversionResults); // Assert expectEqualWrites( logger.stdout.write, - `⚡ 3 packages are required for this ESLint configuration. ⚡`, - ` yarn add @typescript-eslint/eslint-plugin @typescript-eslint/parser eslint --dev`, - ``, `☠ Prettier plugins are missing from your configuration. ☠`, ` We highly recommend running tslint-to-eslint-config --prettier to disable formatting ESLint rules.`, ` See https://github/typescript-eslint/tslint-to-eslint-config/docs/FAQs.md#should-i-use-prettier.`, diff --git a/src/reporting/reportConversionResults.ts b/src/reporting/reportConversionResults.ts index 30f1c3aa7..7f63938e7 100644 --- a/src/reporting/reportConversionResults.ts +++ b/src/reporting/reportConversionResults.ts @@ -2,20 +2,16 @@ import chalk from "chalk"; import { EOL } from "os"; import { Logger } from "../adapters/logger"; -import { SansDependencies } from "../binding"; import { SimplifiedResultsConfiguration } from "../creation/simplification/simplifyPackageRules"; import { ESLintRuleOptions, TSLintRuleOptions } from "../rules/types"; -import { choosePackageManager } from "./packages/choosePackageManager"; import { logFailedConversions, logMissingConversionTarget, - logMissingPackages, logSuccessfulConversions, } from "./reportOutputs"; export type ReportConversionResultsDependencies = { logger: Logger; - choosePackageManager: SansDependencies; }; export const reportConversionResults = async ( @@ -23,8 +19,6 @@ export const reportConversionResults = async ( outputPath: string, ruleConversionResults: SimplifiedResultsConfiguration, ) => { - const packageManager = await dependencies.choosePackageManager(); - if (ruleConversionResults.converted.size !== 0) { logSuccessfulConversions("rule", ruleConversionResults.converted, dependencies.logger); logNotices(ruleConversionResults.converted, dependencies.logger); @@ -48,9 +42,7 @@ export const reportConversionResults = async ( ); } - logMissingPackages(ruleConversionResults, packageManager, dependencies.logger); - - if (!ruleConversionResults.extends?.includes("eslint-config-prettier")) { + if (!ruleConversionResults.extends?.join("").includes("eslint-config-prettier")) { logPrettierExtension(dependencies.logger); } }; diff --git a/src/reporting/reportOutputs.test.ts b/src/reporting/reportOutputs.test.ts deleted file mode 100644 index 4e7f01ce5..000000000 --- a/src/reporting/reportOutputs.test.ts +++ /dev/null @@ -1,88 +0,0 @@ -import { createStubLogger, expectEqualWrites } from "../adapters/logger.stubs"; -import { createEmptyConversionResults } from "../conversion/conversionResults.stubs"; -import { PackageManager } from "./packages/packageManagers"; -import { logMissingPackages } from "./reportOutputs"; -import { SimplifiedResultsConfiguration } from "../creation/simplification/simplifyPackageRules"; - -const createStubDependencies = ( - packageManager: PackageManager, - results?: Partial, -) => ({ - logger: createStubLogger(), - packageManager, - ruleConversionResults: createEmptyConversionResults(results), -}); - -describe("reportOutputs", () => { - it("reports an npm command when the package manager is npm", () => { - // Arrange - createEmptyConversionResults(); - const { logger, packageManager, ruleConversionResults } = createStubDependencies( - PackageManager.npm, - ); - - // Act - logMissingPackages(ruleConversionResults, packageManager, logger); - - // Assert - expectEqualWrites( - logger.stdout.write, - `⚡ 3 packages are required for this ESLint configuration. ⚡`, - ` npm install @typescript-eslint/eslint-plugin @typescript-eslint/parser eslint --save-dev`, - ); - }); - - it("reports a pnpm command when the package manager is pnpm", () => { - // Arrange - const { logger, packageManager, ruleConversionResults } = createStubDependencies( - PackageManager.pnpm, - ); - - // Act - logMissingPackages(ruleConversionResults, packageManager, logger); - - // Assert - expectEqualWrites( - logger.stdout.write, - `⚡ 3 packages are required for this ESLint configuration. ⚡`, - ` pnpm add @typescript-eslint/eslint-plugin @typescript-eslint/parser eslint --save-dev`, - ); - }); - - it("reports a Yarn command when the package manager is Yarn", () => { - // Arrange - const { logger, packageManager, ruleConversionResults } = createStubDependencies( - PackageManager.Yarn, - ); - - // Act - logMissingPackages(ruleConversionResults, packageManager, logger); - - // Assert - expectEqualWrites( - logger.stdout.write, - `⚡ 3 packages are required for this ESLint configuration. ⚡`, - ` yarn add @typescript-eslint/eslint-plugin @typescript-eslint/parser eslint --dev`, - ); - }); - - it("adds extensions to the missing packages list when they exist", () => { - // Arrange - const { logger, packageManager, ruleConversionResults } = createStubDependencies( - PackageManager.Yarn, - { - extends: ["eslint-config-prettier", "eslint-config-prettier/@typescript-eslint"], - }, - ); - - // Act - logMissingPackages(ruleConversionResults, packageManager, logger); - - // Assert - expectEqualWrites( - logger.stdout.write, - `⚡ 4 packages are required for this ESLint configuration. ⚡`, - ` yarn add @typescript-eslint/eslint-plugin @typescript-eslint/parser eslint eslint-config-prettier --dev`, - ); - }); -}); diff --git a/src/reporting/reportOutputs.ts b/src/reporting/reportOutputs.ts index 138b23273..113add239 100644 --- a/src/reporting/reportOutputs.ts +++ b/src/reporting/reportOutputs.ts @@ -2,12 +2,9 @@ import chalk from "chalk"; import { EOL } from "os"; import { Logger } from "../adapters/logger"; -import { SimplifiedResultsConfiguration } from "../creation/simplification/simplifyPackageRules"; import { EditorSetting } from "../editorSettings/types"; import { ErrorSummary } from "../errors/errorSummary"; import { ESLintRuleOptions } from "../rules/types"; -import { uniqueFromSources } from "../utils"; -import { PackageManager, installationMessages } from "./packages/packageManagers"; export type EditorSettingEntry = Pick; @@ -69,29 +66,3 @@ export const logMissingConversionTarget = ( ); logger.info.write(EOL); }; - -export const logMissingPackages = ( - ruleConversionResults: Pick, - packageManager: PackageManager, - logger: Logger, -) => { - const packageNames = uniqueFromSources([ - "@typescript-eslint/eslint-plugin", - "@typescript-eslint/parser", - ruleConversionResults.missing.length !== 0 && "@typescript-eslint/eslint-plugin-tslint", - "eslint", - ...Array.from( - ruleConversionResults.extends?.map((extension) => extension.split("/")[0]) ?? [], - ), - ...Array.from(ruleConversionResults.plugins), - ]) - .filter(Boolean) - .sort(); - - logger.stdout.write(chalk.cyanBright(`${EOL}⚡ ${packageNames.length}`)); - logger.stdout.write(chalk.cyan(" packages are required for this ESLint configuration.")); - logger.stdout.write(chalk.cyanBright(" ⚡")); - logger.stdout.write(`${EOL} `); - logger.stdout.write(chalk.cyan(installationMessages[packageManager](packageNames.join(" ")))); - logger.stdout.write(EOL); -}; diff --git a/src/utils.ts b/src/utils.ts index c69d6f9df..29c8d5e70 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,7 +1,9 @@ -export const isDefined = (item: Item | undefined): item is Item => !!item; +export const isDefined = (item: Item | undefined): item is Item => item !== undefined; export const isError = (item: Item | Error): item is Error => item instanceof Error; +export const isTruthy = (item: Item | false | undefined | null | 0): item is Item => !!item; + export type RemoveErrors = { [P in keyof Items]: Exclude; };