Repository: typescript-eslint/tslint-to-eslint-config
Pull request #423 (2020-04-27 02:25:59)

Update dependency tslint to v6.1.2
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [tslint](https://palantir.github.io/tslint) ([source](https://togithub.com/palantir/tslint)) | dependencies | patch | [`6.1.1` -> `6.1.2`](https://renovatebot.com/diffs/npm/tslint/6.1.1/6.1.2) |

---

### Release Notes

<details>
<summary>palantir/tslint</summary>

### [`v6.1.2`](https://togithub.com/palantir/tslint/blob/master/CHANGELOG.md#v612)

[Compare Source](https://togithub.com/palantir/tslint/compare/6.1.1...6.1.2)

-   [bugfix] restore typings compatibility with TS &lt; 3.8 ([#&#8203;4933](https://togithub.com/palantir/tslint/issues/4933))

</details>

---

### Renovate configuration

:date: **Schedule**: At any time (no schedule defined).

:vertical_traffic_light: **Automerge**: Enabled.

:recycle: **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

:no_bell: **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [WhiteSource Renovate](https://renovate.whitesourcesoftware.com). View repository job log [here](https://app.renovatebot.com/dashboard#typescript-eslint/tslint-to-eslint-config).


Patch:
diff --git a/.eslintrc.js b/.eslintrc.js
index c98de62..981a771 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/package-lock.json b/package-lock.json
index ee04bb1..9bb6303 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -8076,8 +8076,7 @@
         "minimist": {
             "version": "1.2.5",
             "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
-            "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
-            "dev": true
+            "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
         },
         "mixin-deep": {
             "version": "1.3.2",
@@ -8104,7 +8103,6 @@
             "version": "0.5.5",
             "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
             "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
-            "dev": true,
             "requires": {
                 "minimist": "^1.2.5"
             }
@@ -9875,9 +9873,9 @@
             "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ=="
         },
         "tslint": {
-            "version": "6.1.1",
-            "resolved": "https://registry.npmjs.org/tslint/-/tslint-6.1.1.tgz",
-            "integrity": "sha512-kd6AQ/IgPRpLn6g5TozqzPdGNZ0q0jtXW4//hRcj10qLYBaa3mTUU2y2MCG+RXZm8Zx+KZi0eA+YCrMyNlF4UA==",
+            "version": "6.1.2",
+            "resolved": "https://registry.npmjs.org/tslint/-/tslint-6.1.2.tgz",
+            "integrity": "sha512-UyNrLdK3E0fQG/xWNqAFAC5ugtFyPO4JJR1KyyfQAyzR8W0fTRrC91A8Wej4BntFzcvETdCSDa/4PnNYJQLYiA==",
             "requires": {
                 "@babel/code-frame": "^7.0.0",
                 "builtin-modules": "^1.1.1",
@@ -9909,19 +9907,6 @@
                     "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
                     "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
                 },
-                "minimist": {
-                    "version": "1.2.5",
-                    "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
-                    "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
-                },
-                "mkdirp": {
-                    "version": "0.5.4",
-                    "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.4.tgz",
-                    "integrity": "sha512-iG9AK/dJLtJ0XNgTuDbSyNS3zECqDlAhnQW4CsNxBG3LQJBbHmRX1egw39DmtOdCAqY+dKXV+sgPgilNWUKMVw==",
-                    "requires": {
-                        "minimist": "^1.2.5"
-                    }
-                },
                 "supports-color": {
                     "version": "5.5.0",
                     "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
diff --git a/package.json b/package.json
index ae7f4ec..582f0ee 100644
--- a/package.json
+++ b/package.json
@@ -14,7 +14,7 @@
         "commander": "5.1.0",
         "eslint-config-prettier": "6.11.0",
         "strip-json-comments": "3.1.0",
-        "tslint": "6.1.1",
+        "tslint": "6.1.2",
         "typescript": "3.8.3"
     },
     "devDependencies": {
diff --git a/src/cli/main.ts b/src/cli/main.ts
index bc28831..a55117c 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.ts b/src/conversion/convertConfig.ts
index d7bbae2..4d96531 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<typeof convertRules>;
     findOriginalConfigurations: SansDependencies<typeof findOriginalConfigurations>;
+    logMissingPackages: SansDependencies<typeof logMissingPackages>;
     reportConversionResults: SansDependencies<typeof reportConversionResults>;
     simplifyPackageRules: SansDependencies<typeof simplifyPackageRules>;
     writeConversionResults: SansDependencies<typeof writeConversionResults>;
@@ -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.ts b/src/creation/simplification/simplifyPackageRules.ts
index e16f18b..3de99f5 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 213f657..42c79b1 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<string, string>;
+    devDependencies: Record<string, string>;
 };
 
 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.ts b/src/reporting/packages/logMissingPackages.ts
new file mode 100644
index 0000000..fc7a9e5
--- /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<typeof choosePackageManager>;
+    logger: Logger;
+};
+
+export const logMissingPackages = async (
+    dependencies: LogMissingPackagesDependencies,
+    ruleConversionResults: Pick<SimplifiedResultsConfiguration, "extends" | "missing" | "plugins">,
+    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.ts b/src/reporting/reportConversionResults.ts
index 30f1c3a..7f63938 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<typeof choosePackageManager>;
 };
 
 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.ts b/src/reporting/reportOutputs.ts
index 138b232..113add2 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<EditorSetting, "editorSettingName">;
 
@@ -69,29 +66,3 @@ export const logMissingConversionTarget = <T>(
     );
     logger.info.write(EOL);
 };
-
-export const logMissingPackages = (
-    ruleConversionResults: Pick<SimplifiedResultsConfiguration, "extends" | "missing" | "plugins">,
-    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 c69d6f9..29c8d5e 100644
--- a/src/utils.ts
+++ b/src/utils.ts
@@ -1,7 +1,9 @@
-export const isDefined = <Item>(item: Item | undefined): item is Item => !!item;
+export const isDefined = <Item>(item: Item | undefined): item is Item => item !== undefined;
 
 export const isError = <Item>(item: Item | Error): item is Error => item instanceof Error;
 
+export const isTruthy = <Item>(item: Item | false | undefined | null | 0): item is Item => !!item;
+
 export type RemoveErrors<Items> = {
     [P in keyof Items]: Exclude<Items[P], Error>;
 };


Test patch:
diff --git a/src/conversion/convertConfig.test.ts b/src/conversion/convertConfig.test.ts
index c3f4c8b..3fa1e53 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/creation/simplification/simplifyPackageRules.test.ts b/src/creation/simplification/simplifyPackageRules.test.ts
index ea119d4..07266de 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/reporting/packages/logMissingPackages.test.ts b/src/reporting/packages/logMissingPackages.test.ts
new file mode 100644
index 0000000..ac1a6fb
--- /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/reportConversionResults.test.ts b/src/reporting/reportConversionResults.test.ts
index 0b6a6e1..4da981b 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<string, ESLintRuleOptions>([
                 [
@@ -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<string, ESLintRuleOptions>([
                 [
@@ -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<string, ESLintRuleOptions>([
                 [
@@ -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/reportOutputs.test.ts b/src/reporting/reportOutputs.test.ts
deleted file mode 100644
index 4e7f01c..0000000
--- 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<SimplifiedResultsConfiguration>,
-) => ({
-    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`,
-        );
-    });
-});