Repository: typescript-eslint/tslint-to-eslint-config
Pull request #240 (2019-10-16 02:35:30)

Add comment-format converter
<!--
👋 Hi, thanks for sending a PR to tslint-to-eslint-config! 💖
Please fill out all fields below to ensure your PR is reviewed quickly.
-->

## PR Checklist

-   [ ] Addresses an existing issue
-   [ ] That issue was marked as [`status: accepting prs`](https://github.com/typescript-eslint/tslint-to-eslint-config/labels/status%3A%20accepting%20prs)

## Overview

<!-- Brief description of what is changed and how the code change does that. -->

Add missing `comment-format` converter as already being listed at [ROADMAP](https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/ROADMAP.md)


Patch:
diff --git a/package-lock.json b/package-lock.json
index 4632e3d..f5695cf 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -6781,8 +6781,7 @@
         "strip-json-comments": {
             "version": "3.0.1",
             "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.0.1.tgz",
-            "integrity": "sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw==",
-            "dev": true
+            "integrity": "sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw=="
         },
         "supports-color": {
             "version": "5.5.0",
diff --git a/package.json b/package.json
index 6588256..419df3b 100644
--- a/package.json
+++ b/package.json
@@ -9,6 +9,7 @@
     "dependencies": {
         "chalk": "2.4.2",
         "commander": "3.0.2",
+        "strip-json-comments": "^3.0.1",
         "tslint": "5.20.0",
         "typescript": "3.6.3"
     },
diff --git a/src/adapters/importer.ts b/src/adapters/nativeImporter.ts
similarity index 70%
rename from src/adapters/importer.ts
rename to src/adapters/nativeImporter.ts
index 7aa40a2..c0975b7 100644
--- a/src/adapters/importer.ts
+++ b/src/adapters/nativeImporter.ts
@@ -1,4 +1,4 @@
-export type Importer = (moduleName: string) => unknown | Error;
+export type NativeImporter = (moduleName: string) => unknown | Error;
 
 export const nativeImporter = async (moduleName: string) => {
     try {
diff --git a/src/cli/main.ts b/src/cli/main.ts
index c6a3535..efd20eb 100644
--- a/src/cli/main.ts
+++ b/src/cli/main.ts
@@ -1,9 +1,9 @@
 import { EOL } from "os";
 
-import { nativeImporter } from "../adapters/importer";
-import { processLogger } from "../adapters/processLogger";
 import { childProcessExec } from "../adapters/childProcessExec";
 import { fsFileSystem } from "../adapters/fsFileSystem";
+import { nativeImporter } from "../adapters/nativeImporter";
+import { processLogger } from "../adapters/processLogger";
 import { bind } from "../binding";
 import { ConvertConfigDependencies, convertConfig } from "../conversion/convertConfig";
 import { removeExtendsDuplicatedRules } from "../creation/simplification/removeExtendsDuplicatedRules";
@@ -27,6 +27,7 @@ import { findPackagesConfiguration } from "../input/findPackagesConfiguration";
 import { findESLintConfiguration } from "../input/findESLintConfiguration";
 import { findTSLintConfiguration } from "../input/findTSLintConfiguration";
 import { findTypeScriptConfiguration } from "../input/findTypeScriptConfiguration";
+import { importer, ImporterDependencies } from "../input/importer";
 import { mergeLintConfigurations } from "../input/mergeLintConfigurations";
 import {
     reportConversionResults,
@@ -42,8 +43,17 @@ const convertRulesDependencies = {
     mergers,
 };
 
+const nativeImporterDependencies: ImporterDependencies = {
+    fileSystem: fsFileSystem,
+    getCwd: () => process.cwd(),
+    nativeImporter: nativeImporter,
+};
+
+const boundImporter = bind(importer, nativeImporterDependencies);
+
 const findConfigurationDependencies = {
     exec: childProcessExec,
+    importer: boundImporter,
 };
 
 const findOriginalConfigurationsDependencies: FindOriginalConfigurationsDependencies = {
@@ -59,7 +69,7 @@ const reportConversionResultsDependencies: ReportConversionResultsDependencies =
 };
 
 const retrieveExtendsValuesDependencies: RetrieveExtendsValuesDependencies = {
-    importer: nativeImporter,
+    importer: boundImporter,
 };
 
 const simplifyPackageRulesDependencies: SimplifyPackageRulesDependencies = {
diff --git a/src/conversion/convertConfig.ts b/src/conversion/convertConfig.ts
index a6308d0..17195d7 100644
--- a/src/conversion/convertConfig.ts
+++ b/src/conversion/convertConfig.ts
@@ -30,7 +30,7 @@ export const convertConfig = async (
 
     // 2. TSLint rules are converted into their ESLint configurations
     const ruleConversionResults = dependencies.convertRules(
-        originalConfigurations.data.tslint.rules,
+        originalConfigurations.data.tslint.full.rules,
     );
 
     // 3. ESLint configurations are simplified based on extended ESLint presets
diff --git a/src/creation/eslint/createEnv.ts b/src/creation/eslint/createEnv.ts
index 4022ad4..b878a4e 100644
--- a/src/creation/eslint/createEnv.ts
+++ b/src/creation/eslint/createEnv.ts
@@ -1,9 +1,9 @@
-import { OriginalConfigurations } from "../../input/findOriginalConfigurations";
+import { AllOriginalConfigurations } from "../../input/findOriginalConfigurations";
 
 export const createEnv = ({
     packages,
     typescript,
-}: Pick<OriginalConfigurations, "packages" | "typescript">) => {
+}: Pick<AllOriginalConfigurations, "packages" | "typescript">) => {
     const browser =
         typescript === undefined ||
         typescript.compilerOptions.lib === undefined ||
diff --git a/src/creation/formatting/formatters/formatJsOutput.ts b/src/creation/formatting/formatters/formatJsOutput.ts
index d5e0208..23bc78f 100644
--- a/src/creation/formatting/formatters/formatJsOutput.ts
+++ b/src/creation/formatting/formatters/formatJsOutput.ts
@@ -1,4 +1,7 @@
 import { EOL } from "os";
 
-export const formatJsOutput = (configuration: unknown) =>
-    `module.exports = ${JSON.stringify(configuration, undefined, 4)};${EOL}`;
+import { withKeysSorted } from "./withKeysSorted";
+
+export const formatJsOutput = (configuration: any) => {
+    return `module.exports = ${JSON.stringify(withKeysSorted(configuration), undefined, 4)};${EOL}`;
+};
diff --git a/src/creation/formatting/formatters/formatJsonOutput.ts b/src/creation/formatting/formatters/formatJsonOutput.ts
index 6aceb00..16298f1 100644
--- a/src/creation/formatting/formatters/formatJsonOutput.ts
+++ b/src/creation/formatting/formatters/formatJsonOutput.ts
@@ -1,4 +1,7 @@
 import { EOL } from "os";
 
-export const formatJsonOutput = (configuration: unknown) =>
-    `${JSON.stringify(configuration, undefined, 4)}${EOL}`;
+import { withKeysSorted } from "./withKeysSorted";
+
+export const formatJsonOutput = (configuration: any) => {
+    return `${JSON.stringify(withKeysSorted(configuration), undefined, 4)}${EOL}`;
+};
diff --git a/src/creation/formatting/formatters/withKeysSorted.ts b/src/creation/formatting/formatters/withKeysSorted.ts
new file mode 100644
index 0000000..b0fad6b
--- /dev/null
+++ b/src/creation/formatting/formatters/withKeysSorted.ts
@@ -0,0 +1,7 @@
+export const withKeysSorted = (input: any) => {
+    return Object.fromEntries(
+        Object.keys(input)
+            .sort((a, b) => a.localeCompare(b))
+            .map(key => [key, input[key]]),
+    );
+};
diff --git a/src/creation/simplification/retrieveExtendsValues.ts b/src/creation/simplification/retrieveExtendsValues.ts
index aedb533..6138d23 100644
--- a/src/creation/simplification/retrieveExtendsValues.ts
+++ b/src/creation/simplification/retrieveExtendsValues.ts
@@ -1,10 +1,11 @@
-import { Importer } from "../../adapters/importer";
+import { SansDependencies } from "../../binding";
 import { ConfigurationError } from "../../errors/configurationError";
 import { ESLintConfiguration } from "../../input/findESLintConfiguration";
+import { importer } from "../../input/importer";
 import { resolveExtensionNames } from "./resolveExtensionNames";
 
 export type RetrieveExtendsValuesDependencies = {
-    importer: Importer;
+    importer: SansDependencies<typeof importer>;
 };
 
 export type RetrievedExtensionValues = {
@@ -12,20 +13,6 @@ export type RetrievedExtensionValues = {
     importedExtensions: Partial<ESLintConfiguration>[];
 };
 
-const builtInExtensionGetters = new Map<
-    string,
-    (importer: Importer) => Promise<ESLintConfiguration>
->([
-    [
-        "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[],
@@ -34,11 +21,26 @@ export const retrieveExtendsValues = async (
     const configurationErrors: ConfigurationError[] = [];
     const extensionNames = resolveExtensionNames(rawExtensionNames);
 
+    const builtInExtensionGetters = new Map<string, () => Promise<ESLintConfiguration>>([
+        [
+            "eslint:all",
+            async () =>
+                (await dependencies.importer("eslint/conf/eslint-all")) as ESLintConfiguration,
+        ],
+        [
+            "eslint:recommended",
+            async () =>
+                (await dependencies.importer(
+                    "eslint/conf/eslint-recommended",
+                )) as ESLintConfiguration,
+        ],
+    ]);
+
     await Promise.all(
         extensionNames.map(async extensionName => {
             const getBuiltInExtension = builtInExtensionGetters.get(extensionName);
             if (getBuiltInExtension !== undefined) {
-                importedExtensions.push(await getBuiltInExtension(dependencies.importer));
+                importedExtensions.push(await getBuiltInExtension());
                 return;
             }
 
diff --git a/src/creation/simplification/simplifyPackageRules.ts b/src/creation/simplification/simplifyPackageRules.ts
index 550fff9..b813ddd 100644
--- a/src/creation/simplification/simplifyPackageRules.ts
+++ b/src/creation/simplification/simplifyPackageRules.ts
@@ -3,6 +3,7 @@ import { RuleConversionResults } from "../../rules/convertRules";
 import { removeExtendsDuplicatedRules } from "./removeExtendsDuplicatedRules";
 import { retrieveExtendsValues } from "./retrieveExtendsValues";
 import { ESLintConfiguration } from "../../input/findESLintConfiguration";
+import { OriginalConfigurations } from "../../input/findOriginalConfigurations";
 
 export type SimplifyPackageRulesDependencies = {
     removeExtendsDuplicatedRules: typeof removeExtendsDuplicatedRules;
@@ -13,15 +14,15 @@ export type SimplifiedRuleConversionResults = Pick<RuleConversionResults, "conve
 
 export const simplifyPackageRules = async (
     dependencies: SimplifyPackageRulesDependencies,
-    eslint: Partial<ESLintConfiguration> | undefined,
+    eslint: Pick<OriginalConfigurations<ESLintConfiguration>, "full"> | undefined,
     ruleConversionResults: SimplifiedRuleConversionResults,
 ): Promise<SimplifiedRuleConversionResults> => {
-    if (eslint === undefined || eslint.extends === undefined || eslint.extends.length === 0) {
+    if (eslint === undefined || eslint.full.extends.length === 0) {
         return ruleConversionResults;
     }
 
     const { configurationErrors, importedExtensions } = await dependencies.retrieveExtendsValues(
-        eslint.extends,
+        eslint.full.extends,
     );
 
     const converted = dependencies.removeExtendsDuplicatedRules(
diff --git a/src/creation/writeConversionResults.ts b/src/creation/writeConversionResults.ts
index 49be3eb..6fc0ebd 100644
--- a/src/creation/writeConversionResults.ts
+++ b/src/creation/writeConversionResults.ts
@@ -1,7 +1,7 @@
 import { FileSystem } from "../adapters/fileSystem";
 import { RuleConversionResults } from "../rules/convertRules";
 import { formatConvertedRules } from "./formatConvertedRules";
-import { OriginalConfigurations } from "../input/findOriginalConfigurations";
+import { AllOriginalConfigurations } from "../input/findOriginalConfigurations";
 import { createEnv } from "./eslint/createEnv";
 import { formatOutput } from "./formatting/formatOutput";
 
@@ -13,24 +13,29 @@ export const writeConversionResults = async (
     dependencies: WriteConversionResultsDependencies,
     outputPath: string,
     ruleConversionResults: RuleConversionResults,
-    originalConfigurations: OriginalConfigurations,
+    originalConfigurations: AllOriginalConfigurations,
 ) => {
     const plugins = ["@typescript-eslint"];
+    const { eslint, tslint } = originalConfigurations;
 
     if (ruleConversionResults.missing.length !== 0) {
         plugins.push("@typescript-eslint/tslint");
     }
 
     const output = {
-        ...(originalConfigurations.eslint && originalConfigurations.eslint),
+        ...(eslint && eslint.full),
         env: createEnv(originalConfigurations),
+        ...(eslint && { globals: eslint.raw.globals }),
         parser: "@typescript-eslint/parser",
         parserOptions: {
             project: "tsconfig.json",
             sourceType: "module",
         },
         plugins,
-        rules: formatConvertedRules(ruleConversionResults, originalConfigurations.tslint),
+        rules: {
+            ...(eslint && eslint.full.rules),
+            ...formatConvertedRules(ruleConversionResults, tslint.full),
+        },
     };
 
     return await dependencies.fileSystem.writeFile(outputPath, formatOutput(outputPath, output));
diff --git a/src/input/findESLintConfiguration.ts b/src/input/findESLintConfiguration.ts
index 7a4817c..3704bb6 100644
--- a/src/input/findESLintConfiguration.ts
+++ b/src/input/findESLintConfiguration.ts
@@ -1,12 +1,16 @@
+import { Exec } from "../adapters/exec";
+import { SansDependencies } from "../binding";
 import { ESLintRuleSeverity } from "../rules/types";
 import { TSLintToESLintSettings } from "../types";
-import { findConfiguration, FindConfigurationDependencies } from "./findConfiguration";
+import { findRawConfiguration } from "./findRawConfiguration";
+import { findReportedConfiguration } from "./findReportedConfiguration";
+import { OriginalConfigurations } from "./findOriginalConfigurations";
+import { importer } from "./importer";
 
 export type ESLintConfiguration = {
-    env: {
-        [i: string]: boolean;
-    };
+    env: Record<string, boolean>;
     extends: string | string[];
+    globals?: Record<string, boolean>;
     rules: ESLintConfigurationRules;
 };
 
@@ -27,20 +31,45 @@ const defaultESLintConfiguration = {
     rules: {},
 };
 
+export type FindESLintConfigurationDependencies = {
+    exec: Exec;
+    importer: SansDependencies<typeof importer>;
+};
+
 export const findESLintConfiguration = async (
-    dependencies: FindConfigurationDependencies,
+    dependencies: FindESLintConfigurationDependencies,
     rawSettings: Pick<TSLintToESLintSettings, "config" | "eslint">,
-): Promise<ESLintConfiguration | Error> => {
-    const rawConfiguration = await findConfiguration<ESLintConfiguration>(
-        dependencies.exec,
-        "eslint --print-config",
-        rawSettings.eslint || rawSettings.config,
+): Promise<OriginalConfigurations<ESLintConfiguration> | Error> => {
+    const filePath = rawSettings.eslint || rawSettings.config;
+    const [rawConfiguration, reportedConfiguration] = await Promise.all([
+        findRawConfiguration<ESLintConfiguration>(dependencies.importer, filePath, {
+            extends: [],
+        }),
+        findReportedConfiguration<Partial<ESLintConfiguration>>(
+            dependencies.exec,
+            "eslint --print-config",
+            filePath,
+        ),
+    ]);
+
+    if (rawConfiguration instanceof Error) {
+        return rawConfiguration;
+    }
+
+    if (reportedConfiguration instanceof Error) {
+        return reportedConfiguration;
+    }
+
+    const extensions = [rawConfiguration.extends, [reportedConfiguration.extends || []]].flat(
+        Infinity,
     );
 
-    return rawConfiguration instanceof Error
-        ? rawConfiguration
-        : {
-              ...defaultESLintConfiguration,
-              ...rawConfiguration,
-          };
+    return {
+        full: {
+            ...defaultESLintConfiguration,
+            ...reportedConfiguration,
+            extends: Array.from(new Set(extensions)),
+        },
+        raw: rawConfiguration,
+    };
 };
diff --git a/src/input/findOriginalConfigurations.ts b/src/input/findOriginalConfigurations.ts
index 754b6bd..fcfe101 100644
--- a/src/input/findOriginalConfigurations.ts
+++ b/src/input/findOriginalConfigurations.ts
@@ -17,17 +17,32 @@ export type FindOriginalConfigurationsDependencies = {
     mergeLintConfigurations: typeof mergeLintConfigurations;
 };
 
-export type OriginalConfigurations = {
-    eslint?: ESLintConfiguration;
+/**
+ * Both found configurations for a particular linter.
+ */
+export type OriginalConfigurations<Configuration> = {
+    /**
+     * Settings reported by the linter's native --print-config equivalent.
+     */
+    full: Configuration;
+
+    /**
+     * Raw import results from `import`ing the configuration file.
+     */
+    raw: Partial<Configuration>;
+};
+
+export type AllOriginalConfigurations = {
+    eslint?: OriginalConfigurations<ESLintConfiguration>;
     packages?: PackagesConfiguration;
-    tslint: TSLintConfiguration;
+    tslint: OriginalConfigurations<TSLintConfiguration>;
     typescript?: TypeScriptConfiguration;
 };
 
 export const findOriginalConfigurations = async (
     dependencies: FindOriginalConfigurationsDependencies,
     rawSettings: TSLintToESLintSettings,
-): Promise<ResultWithDataStatus<OriginalConfigurations>> => {
+): Promise<ResultWithDataStatus<AllOriginalConfigurations>> => {
     // Simultaneously search for all required configuration types
     const [eslint, packages, tslint, typescript] = await Promise.all([
         dependencies.findESLintConfiguration(rawSettings),
diff --git a/src/input/findPackagesConfiguration.ts b/src/input/findPackagesConfiguration.ts
index 54ab887..e27fc4d 100644
--- a/src/input/findPackagesConfiguration.ts
+++ b/src/input/findPackagesConfiguration.ts
@@ -1,4 +1,7 @@
-import { findConfiguration, FindConfigurationDependencies } from "./findConfiguration";
+import {
+    findReportedConfiguration,
+    FindReportedConfigurationDependencies,
+} from "./findReportedConfiguration";
 
 export type PackagesConfiguration = {
     dependencies: {
@@ -15,10 +18,10 @@ const defaultPackagesConfiguration = {
 };
 
 export const findPackagesConfiguration = async (
-    dependencies: FindConfigurationDependencies,
+    dependencies: FindReportedConfigurationDependencies,
     config: string | undefined,
 ): Promise<PackagesConfiguration | Error> => {
-    const rawConfiguration = await findConfiguration<PackagesConfiguration>(
+    const rawConfiguration = await findReportedConfiguration<PackagesConfiguration>(
         dependencies.exec,
         "cat",
         config || "./package.json",
diff --git a/src/input/findRawConfiguration.ts b/src/input/findRawConfiguration.ts
new file mode 100644
index 0000000..4faf5f4
--- /dev/null
+++ b/src/input/findRawConfiguration.ts
@@ -0,0 +1,21 @@
+import { SansDependencies } from "../binding";
+import { importer } from "./importer";
+
+export const findRawConfiguration = async <Configuration>(
+    fileImporter: SansDependencies<typeof importer>,
+    filePath: string,
+    defaults: Partial<Configuration>,
+): Promise<Configuration | Error> => {
+    let results: Configuration;
+
+    try {
+        results = (await fileImporter(filePath)) as Configuration;
+    } catch (error) {
+        return error;
+    }
+
+    return {
+        ...defaults,
+        ...results,
+    };
+};
diff --git a/src/input/findConfiguration.ts b/src/input/findReportedConfiguration.ts
similarity index 88%
rename from src/input/findConfiguration.ts
rename to src/input/findReportedConfiguration.ts
index d91acb9..977d143 100644
--- a/src/input/findConfiguration.ts
+++ b/src/input/findReportedConfiguration.ts
@@ -4,11 +4,11 @@ export type DeepPartial<T> = {
     [P in keyof T]: T[P] extends {} ? DeepPartial<T[P]> : T[P];
 };
 
-export type FindConfigurationDependencies = {
+export type FindReportedConfigurationDependencies = {
     exec: Exec;
 };
 
-export const findConfiguration = async <Configuration>(
+export const findReportedConfiguration = async <Configuration>(
     exec: Exec,
     command: string,
     config: string,
diff --git a/src/input/findTSLintConfiguration.ts b/src/input/findTSLintConfiguration.ts
index 97962ff..d14bfdc 100644
--- a/src/input/findTSLintConfiguration.ts
+++ b/src/input/findTSLintConfiguration.ts
@@ -1,44 +1,67 @@
+import { findRawConfiguration } from "./findRawConfiguration";
+import { findReportedConfiguration } from "./findReportedConfiguration";
 import { Exec } from "../adapters/exec";
-import { findConfiguration } from "./findConfiguration";
+import { OriginalConfigurations } from "./findOriginalConfigurations";
+import { SansDependencies } from "../binding";
+import { importer } from "./importer";
 
 export type TSLintConfiguration = {
+    extends?: string[];
     rulesDirectory: string[];
     rules: TSLintConfigurationRules;
 };
 
-export type TSLintConfigurationRules = {
-    [i: string]: any;
-};
+export type TSLintConfigurationRules = Record<string, any>;
 
 const defaultTSLintConfiguration = {
+    extends: [],
     rulesDirectory: [],
     rules: {},
 };
 
 export type FindTSLintConfigurationDependencies = {
     exec: Exec;
+    importer: SansDependencies<typeof importer>;
 };
 
 export const findTSLintConfiguration = async (
     dependencies: FindTSLintConfigurationDependencies,
     config: string | undefined,
-): Promise<TSLintConfiguration | Error> => {
-    const rawConfiguration = await findConfiguration<TSLintConfiguration>(
-        dependencies.exec,
-        "tslint --print-config",
-        config || "./tslint.json",
-    );
+): Promise<OriginalConfigurations<TSLintConfiguration> | Error> => {
+    const filePath = config || "./tslint.json";
+    const [rawConfiguration, reportedConfiguration] = await Promise.all([
+        findRawConfiguration<Partial<TSLintConfiguration>>(dependencies.importer, filePath, {
+            extends: [],
+        }),
+        findReportedConfiguration<TSLintConfiguration>(
+            dependencies.exec,
+            "tslint --print-config",
+            config || "./tslint.json",
+        ),
+    ]);
 
-    if (rawConfiguration instanceof Error) {
-        if (rawConfiguration.message.includes("unknown option `--print-config")) {
+    if (reportedConfiguration instanceof Error) {
+        if (reportedConfiguration.message.includes("unknown option `--print-config")) {
             return new Error("TSLint v5.18 required. Please update your version.");
         }
 
+        return reportedConfiguration;
+    }
+
+    if (rawConfiguration instanceof Error) {
         return rawConfiguration;
     }
 
     return {
-        ...defaultTSLintConfiguration,
-        ...rawConfiguration,
+        full: {
+            ...defaultTSLintConfiguration,
+            ...rawConfiguration,
+            extends: Array.from(
+                new Set(
+                    [[rawConfiguration.extends], [reportedConfiguration.extends]].flat(Infinity),
+                ),
+            ),
+        },
+        raw: rawConfiguration,
     };
 };
diff --git a/src/input/findTypeScriptConfiguration.ts b/src/input/findTypeScriptConfiguration.ts
index 27badf3..2284e1b 100644
--- a/src/input/findTypeScriptConfiguration.ts
+++ b/src/input/findTypeScriptConfiguration.ts
@@ -1,4 +1,7 @@
-import { findConfiguration, FindConfigurationDependencies } from "./findConfiguration";
+import {
+    findReportedConfiguration,
+    FindReportedConfigurationDependencies,
+} from "./findReportedConfiguration";
 
 export type TypeScriptConfiguration = {
     compilerOptions: {
@@ -14,10 +17,10 @@ const defaultTypeScriptConfiguration = {
 };
 
 export const findTypeScriptConfiguration = async (
-    dependencies: FindConfigurationDependencies,
+    dependencies: FindReportedConfigurationDependencies,
     config: string | undefined,
 ): Promise<TypeScriptConfiguration | Error> => {
-    const rawConfiguration = await findConfiguration<TypeScriptConfiguration>(
+    const rawConfiguration = await findReportedConfiguration<TypeScriptConfiguration>(
         dependencies.exec,
         "tsc --showConfig -p",
         config || "./tsconfig.json",
diff --git a/src/input/importer.ts b/src/input/importer.ts
new file mode 100644
index 0000000..03955ff
--- /dev/null
+++ b/src/input/importer.ts
@@ -0,0 +1,51 @@
+import * as path from "path";
+import stripJsonComments from "strip-json-comments";
+
+import { FileSystem } from "../adapters/fileSystem";
+import { NativeImporter } from "../adapters/nativeImporter";
+
+export type ImporterDependencies = {
+    fileSystem: Pick<FileSystem, "fileExists" | "readFile">;
+    getCwd: () => string;
+    nativeImporter: NativeImporter;
+};
+
+export const importer = async (dependencies: ImporterDependencies, moduleName: string) => {
+    const pathAttempts = [path.join(dependencies.getCwd(), moduleName), moduleName];
+
+    const importFile = async (filePath: string) => {
+        if (!filePath.endsWith(".json")) {
+            return await dependencies.nativeImporter(filePath);
+        }
+
+        if (!(await dependencies.fileSystem.fileExists(filePath))) {
+            return undefined;
+        }
+
+        const rawJsonContents = await dependencies.fileSystem.readFile(filePath);
+        if (rawJsonContents instanceof Error) {
+            return rawJsonContents;
+        }
+
+        try {
+            return JSON.parse(stripJsonComments(rawJsonContents));
+        } catch (error) {
+            return error;
+        }
+    };
+
+    for (const pathAttempt of pathAttempts) {
+        try {
+            const result = await importFile(pathAttempt);
+            if (result) {
+                return result;
+            }
+        } catch {}
+    }
+
+    return new Error(
+        `Could not find '${moduleName}' after trying: ${pathAttempts
+            .map(attempt => `'${attempt}'`)
+            .join(", ")}`,
+    );
+};
diff --git a/src/input/mergeLintConfigurations.ts b/src/input/mergeLintConfigurations.ts
index 662be35..de2bb80 100644
--- a/src/input/mergeLintConfigurations.ts
+++ b/src/input/mergeLintConfigurations.ts
@@ -1,24 +1,28 @@
-import { TSLintConfiguration } from "./findTSLintConfiguration";
 import { ESLintConfiguration } from "./findESLintConfiguration";
+import { OriginalConfigurations } from "./findOriginalConfigurations";
+import { TSLintConfiguration } from "./findTSLintConfiguration";
 
 export const mergeLintConfigurations = (
-    eslint: ESLintConfiguration | Error,
-    tslint: TSLintConfiguration,
-) => {
+    eslint: OriginalConfigurations<ESLintConfiguration> | Error,
+    tslint: OriginalConfigurations<TSLintConfiguration>,
+): OriginalConfigurations<TSLintConfiguration> => {
     if (eslint instanceof Error) {
         return tslint;
     }
 
-    const mappedConfig = eslint.rules["@typescript-eslint/tslint/config"];
+    const mappedConfig = eslint.full.rules["@typescript-eslint/tslint/config"];
     if (!(mappedConfig instanceof Array) || mappedConfig[0] === "off") {
         return tslint;
     }
 
     return {
         ...tslint,
-        rules: {
-            ...tslint.rules,
-            ...mappedConfig[1].rules,
+        full: {
+            ...tslint.full,
+            rules: {
+                ...tslint.full.rules,
+                ...mappedConfig[1].rules,
+            },
         },
     };
 };
diff --git a/src/reporting/reportConversionResults.ts b/src/reporting/reportConversionResults.ts
index 2eb2a1a..7cd25c8 100644
--- a/src/reporting/reportConversionResults.ts
+++ b/src/reporting/reportConversionResults.ts
@@ -62,7 +62,11 @@ const logMissingRules = (missing: TSLintRuleOptions[], logger: Logger) => {
                 : " rules do not yet have ESLint equivalents",
         ),
     );
-    logger.stdout.write(chalk.yellow(" (see generated log file); defaulting to eslint-plugin-tslint for these rules."));
+    logger.stdout.write(
+        chalk.yellow(
+            " (see generated log file); defaulting to eslint-plugin-tslint for these rules.",
+        ),
+    );
     logger.stdout.write(chalk.yellowBright(` 👀${EOL}`));
 
     logger.info.write(
diff --git a/src/rules/converters.ts b/src/rules/converters.ts
index d21e68b..fd44418 100644
--- a/src/rules/converters.ts
+++ b/src/rules/converters.ts
@@ -9,6 +9,7 @@ import { convertBanTypes } from "./converters/ban-types";
 import { convertBinaryExpressionOperandOrder } from "./converters/binary-expression-operand-order";
 import { convertCallableTypes } from "./converters/callable-types";
 import { convertClassName } from "./converters/class-name";
+import { convertCommentFormat } from "./converters/comment-format";
 import { convertCurly } from "./converters/curly";
 import { convertCyclomaticComplexity } from "./converters/cyclomatic-complexity";
 import { convertEofline } from "./converters/eofline";
@@ -112,6 +113,7 @@ import { convertRestrictPlusOperands } from "./converters/restrict-plus-operands
 import { convertSemicolon } from "./converters/semicolon";
 import { convertSpaceBeforeFunctionParen } from "./converters/space-before-function-paren";
 import { convertSpaceWithinParens } from "./converters/space-within-parens";
+import { convertStrictBooleanExpressions } from "./converters/strict-boolean-expressions";
 import { convertSwitchDefault } from "./converters/switch-default";
 import { convertTripleEquals } from "./converters/triple-equals";
 import { convertTypedefWhitespace } from "./converters/typedef-whitespace";
@@ -139,6 +141,7 @@ export const converters = new Map([
     ["binary-expression-operand-order", convertBinaryExpressionOperandOrder],
     ["callable-types", convertCallableTypes],
     ["class-name", convertClassName],
+    ["comment-format", convertCommentFormat],
     ["curly", convertCurly],
     ["cyclomatic-complexity", convertCyclomaticComplexity],
     ["eofline", convertEofline],
@@ -242,6 +245,7 @@ export const converters = new Map([
     ["semicolon", convertSemicolon],
     ["space-before-function-paren", convertSpaceBeforeFunctionParen],
     ["space-within-parens", convertSpaceWithinParens],
+    ["strict-boolean-expressions", convertStrictBooleanExpressions],
     ["switch-default", convertSwitchDefault],
     ["triple-equals", convertTripleEquals],
     ["type-literal-delimiter", convertTypeLiteralDelimiter],
@@ -262,10 +266,6 @@ export const converters = new Map([
     // TSLint core rules:
     // ["ban", convertBan], // no-restricted-properties
     // ["import-blacklist", convertImportBlacklist], // no-restricted-imports
-    // ["no-duplicate-variable", convertNoDuplicateVariable], // no-redeclare
-    // ["no-void-expression", convertNoVoidExpression], // (no exact equivalent)
-    // ["quotemark", convertQuotemark], // quotes
-    // ["triple-equals", convertTripleEquals], // eqeqeq
 
     // tslint-microsoft-contrib rules:
     // ["max-func-body-length", convertMaxFuncBodyLength],
diff --git a/src/rules/converters/comment-format.ts b/src/rules/converters/comment-format.ts
new file mode 100644
index 0000000..faf8b14
--- /dev/null
+++ b/src/rules/converters/comment-format.ts
@@ -0,0 +1,53 @@
+import { RuleConverter } from "../converter";
+
+type CommentFormatOptions = {
+    "ignore-words": string[];
+    "ignore-pattern": string;
+};
+
+export const CapitalizedIgnoreMessage = "Only accepts a single string pattern to be ignored.";
+
+export const convertCommentFormat: RuleConverter = tslintRule => {
+    const capitalizedRuleArguments: string[] = [];
+    const spaceCommentRuleArguments: string[] = [];
+    const capitalizedNotices: string[] = [];
+
+    const hasCheckSpace = tslintRule.ruleArguments.includes("check-space");
+    const hasCheckLowercase = tslintRule.ruleArguments.includes("check-lowercase");
+    const hasCheckUppercase = tslintRule.ruleArguments.includes("check-uppercase");
+
+    if (!hasCheckSpace) {
+        spaceCommentRuleArguments.push("never");
+    }
+
+    if (hasCheckUppercase) {
+        capitalizedRuleArguments.push("always");
+    } else if (hasCheckLowercase) {
+        capitalizedRuleArguments.push("never");
+    }
+
+    if (typeof tslintRule.ruleArguments[tslintRule.ruleArguments.length - 1] === "object") {
+        const objectArgument: CommentFormatOptions =
+            tslintRule.ruleArguments[tslintRule.ruleArguments.length - 1];
+        if (
+            (objectArgument["ignore-words"] && objectArgument["ignore-words"].length) ||
+            objectArgument["ignore-pattern"]
+        ) {
+            capitalizedNotices.push(CapitalizedIgnoreMessage);
+        }
+    }
+
+    return {
+        rules: [
+            {
+                ruleName: "capitalized-comments",
+                ruleArguments: capitalizedRuleArguments,
+                notices: capitalizedNotices,
+            },
+            {
+                ruleName: "spaced-comment",
+                ruleArguments: spaceCommentRuleArguments,
+            },
+        ],
+    };
+};
diff --git a/src/rules/converters/no-implicit-dependencies.ts b/src/rules/converters/no-implicit-dependencies.ts
index a485524..74ae82e 100644
--- a/src/rules/converters/no-implicit-dependencies.ts
+++ b/src/rules/converters/no-implicit-dependencies.ts
@@ -2,7 +2,7 @@ import { RuleConverter } from "../converter";
 
 export const convertNoImplicitDependencies: RuleConverter = tslintRule => {
     const ruleArgs: {
-        [eslintOption in "devDependencies" | "optionalDependencies"]?: boolean
+        [eslintOption in "devDependencies" | "optionalDependencies"]?: boolean;
     }[] = [];
     const notices: string[] = [];
 
diff --git a/src/rules/converters/no-reference.ts b/src/rules/converters/no-reference.ts
index daa1e30..a8e2ce6 100644
--- a/src/rules/converters/no-reference.ts
+++ b/src/rules/converters/no-reference.ts
@@ -4,7 +4,7 @@ export const convertNoReference: RuleConverter = () => {
     return {
         rules: [
             {
-                ruleName: "@typescript-eslint/no-triple-slash-reference",
+                ruleName: "@typescript-eslint/triple-slash-reference",
             },
         ],
     };
diff --git a/src/rules/converters/strict-boolean-expressions.ts b/src/rules/converters/strict-boolean-expressions.ts
new file mode 100644
index 0000000..e3eda4d
--- /dev/null
+++ b/src/rules/converters/strict-boolean-expressions.ts
@@ -0,0 +1,37 @@
+import { RuleConverter } from "../converter";
+
+export const ForbiddenOtherNonBooleanTypes =
+    "String, number, enum, and mixed union types are now forbidden.";
+
+export const convertStrictBooleanExpressions: RuleConverter = tslintRule => {
+    const notices: string[] = [];
+    const ruleArguments: { [key: string]: boolean } = {};
+
+    if (tslintRule.ruleArguments.length >= 2 || tslintRule.ruleArguments[0] === true) {
+        notices.push(ForbiddenOtherNonBooleanTypes);
+        tslintRule.ruleArguments.forEach(ruleArgument => {
+            switch (ruleArgument) {
+                case "allow-undefined-union":
+                case "allow-boolean-or-undefined":
+                case "allow-null-union":
+                    ruleArguments["allowNullable"] = true;
+                    break;
+                case "ignore-rhs":
+                    ruleArguments["ignoreRhs"] = true;
+                    break;
+                default:
+                    break;
+            }
+        });
+    }
+
+    return {
+        rules: [
+            {
+                ruleName: "@typescript-eslint/strict-boolean-expressions",
+                ruleArguments: Object.keys(ruleArguments).length !== 0 ? [ruleArguments] : [],
+                notices,
+            },
+        ],
+    };
+};
diff --git a/tsconfig.json b/tsconfig.json
index ad0c7da..d836d02 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -2,6 +2,7 @@
     "compilerOptions": {
         "alwaysStrict": true,
         "declaration": true,
+        "esModuleInterop": true,
         "incremental": true,
         "lib": ["esnext"],
         "module": "commonjs",


Test patch:
diff --git a/src/conversion/convertConfig.test.ts b/src/conversion/convertConfig.test.ts
index aadc257..8a456bb 100644
--- a/src/conversion/convertConfig.test.ts
+++ b/src/conversion/convertConfig.test.ts
@@ -21,8 +21,11 @@ const createStubDependencies = (
 
 const createStubOriginalConfigurationsData = () => ({
     tslint: {
-        rules: [],
-        rulesDirectory: [],
+        full: {
+            rules: [],
+            rulesDirectory: [],
+        },
+        raw: {},
     },
 });
 
diff --git a/src/creation/simplification/simplifyPackageRules.test.ts b/src/creation/simplification/simplifyPackageRules.test.ts
index b95b730..e603dbc 100644
--- a/src/creation/simplification/simplifyPackageRules.test.ts
+++ b/src/creation/simplification/simplifyPackageRules.test.ts
@@ -8,6 +8,14 @@ const createStubDependencies = () => ({
     retrieveExtendsValues: jest.fn(),
 });
 
+const createStubESLintConfiguration = (fullExtends: string | string[]) => ({
+    full: {
+        env: {},
+        extends: fullExtends,
+        rules: {},
+    },
+});
+
 describe("simplifyPackageRules", () => {
     it("returns the conversion results directly when there is no loaded eslint configuration", async () => {
         // Arrange
@@ -26,29 +34,10 @@ describe("simplifyPackageRules", () => {
         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 eslint = createStubESLintConfiguration([]);
         const ruleConversionResults = createEmptyConversionResults();
 
         // Act
@@ -82,9 +71,7 @@ describe("simplifyPackageRules", () => {
                 importedExtensions: [],
             }),
         };
-        const eslint = {
-            extends: ["extension-name"],
-        };
+        const eslint = createStubESLintConfiguration(["extension-name"]);
         const ruleConversionResults = createEmptyConversionResults();
 
         // Act
diff --git a/src/creation/writeConversionResults.test.ts b/src/creation/writeConversionResults.test.ts
index 5af5c92..b64e1c6 100644
--- a/src/creation/writeConversionResults.test.ts
+++ b/src/creation/writeConversionResults.test.ts
@@ -1,12 +1,15 @@
 import { createEmptyConversionResults } from "../conversion/conversionResults.stubs";
 import { writeConversionResults } from "./writeConversionResults";
-import { OriginalConfigurations } from "../input/findOriginalConfigurations";
+import { AllOriginalConfigurations } from "../input/findOriginalConfigurations";
 import { formatJsonOutput } from "./formatting/formatters/formatJsonOutput";
 
-const createStubOriginalConfigurations = (overrides: Partial<OriginalConfigurations> = {}) => ({
+const createStubOriginalConfigurations = (overrides: Partial<AllOriginalConfigurations> = {}) => ({
     tslint: {
-        rulesDirectory: [],
-        rules: {},
+        full: {
+            rulesDirectory: [],
+            rules: {},
+        },
+        raw: {},
     },
     ...overrides,
 });
@@ -14,9 +17,7 @@ const createStubOriginalConfigurations = (overrides: Partial<OriginalConfigurati
 describe("writeConversionResults", () => {
     it("excludes the tslint plugin when there are no missing rules", async () => {
         // Arrange
-        const conversionResults = createEmptyConversionResults({
-            converted: new Map(),
-        });
+        const conversionResults = createEmptyConversionResults();
         const fileSystem = { writeFile: jest.fn().mockReturnValue(Promise.resolve()) };
 
         // Act
@@ -50,7 +51,6 @@ describe("writeConversionResults", () => {
     it("includes typescript-eslint plugin settings when there are missing rules", async () => {
         // Arrange
         const conversionResults = createEmptyConversionResults({
-            converted: new Map(),
             missing: [
                 {
                     ruleArguments: [],
@@ -100,16 +100,17 @@ describe("writeConversionResults", () => {
 
     it("includes the original eslint configuration when it exists", async () => {
         // Arrange
-        const conversionResults = createEmptyConversionResults({
-            converted: new Map(),
-        });
+        const conversionResults = createEmptyConversionResults();
         const eslint = {
-            env: {},
-            extends: [],
-            globals: {
-                Promise: true,
+            full: {
+                env: {},
+                extends: [],
+                globals: {
+                    Promise: true,
+                },
+                rules: {},
             },
-            rules: {},
+            raw: {},
         };
         const originalConfigurations = createStubOriginalConfigurations({
             eslint,
@@ -128,19 +129,71 @@ describe("writeConversionResults", () => {
         expect(fileSystem.writeFile).toHaveBeenLastCalledWith(
             ".eslintrc.json",
             formatJsonOutput({
-                ...eslint,
                 env: {
                     browser: true,
                     es6: true,
                     node: true,
                 },
+                extends: [],
+                rules: {},
                 parser: "@typescript-eslint/parser",
                 parserOptions: {
                     project: "tsconfig.json",
                     sourceType: "module",
                 },
                 plugins: ["@typescript-eslint"],
+            }),
+        );
+    });
+
+    it("includes raw globals when they exist", async () => {
+        // Arrange
+        const conversionResults = createEmptyConversionResults();
+        const eslint = {
+            full: {
+                env: {},
+                extends: [],
                 rules: {},
+            },
+            raw: {
+                globals: {
+                    Promise: true,
+                },
+            },
+        };
+        const originalConfigurations = createStubOriginalConfigurations({
+            eslint,
+        });
+        const fileSystem = { writeFile: jest.fn().mockReturnValue(Promise.resolve()) };
+
+        // Act
+        await writeConversionResults(
+            { fileSystem },
+            ".eslintrc.json",
+            conversionResults,
+            originalConfigurations,
+        );
+
+        // Assert
+        expect(fileSystem.writeFile).toHaveBeenLastCalledWith(
+            ".eslintrc.json",
+            formatJsonOutput({
+                env: {
+                    browser: true,
+                    es6: true,
+                    node: true,
+                },
+                extends: [],
+                rules: {},
+                globals: {
+                    Promise: true,
+                },
+                parser: "@typescript-eslint/parser",
+                parserOptions: {
+                    project: "tsconfig.json",
+                    sourceType: "module",
+                },
+                plugins: ["@typescript-eslint"],
             }),
         );
     });
diff --git a/src/input/findESLintConfiguration.test.ts b/src/input/findESLintConfiguration.test.ts
index d19dba9..01529dc 100644
--- a/src/input/findESLintConfiguration.test.ts
+++ b/src/input/findESLintConfiguration.test.ts
@@ -1,7 +1,16 @@
-import { findESLintConfiguration } from "./findESLintConfiguration";
+import {
+    findESLintConfiguration,
+    FindESLintConfigurationDependencies,
+} from "./findESLintConfiguration";
 import { createStubExec, createStubThrowingExec } from "../adapters/exec.stubs";
 import { TSLintToESLintSettings } from "../types";
 
+const createStubDependencies = (overrides: Partial<FindESLintConfigurationDependencies> = {}) => ({
+    exec: createStubExec({ stdout: "{}" }),
+    importer: async () => ({}),
+    ...overrides,
+});
+
 const createStubRawSettings = (overrides: Partial<TSLintToESLintSettings> = {}) => ({
     config: "./eslintrc.js",
     eslint: undefined,
@@ -9,10 +18,32 @@ const createStubRawSettings = (overrides: Partial<TSLintToESLintSettings> = {})
 });
 
 describe("findESLintConfiguration", () => {
-    it("returns an error when one occurs", async () => {
+    it("returns an error when exec returns one", async () => {
         // Arrange
         const message = "error";
-        const dependencies = { exec: createStubThrowingExec({ stderr: message }) };
+        const dependencies = createStubDependencies({
+            exec: createStubThrowingExec({ stderr: message }),
+        });
+
+        // Act
+        const result = await findESLintConfiguration(dependencies, createStubRawSettings());
+
+        // Assert
+        expect(result).toEqual(
+            expect.objectContaining({
+                message,
+            }),
+        );
+    });
+
+    it("returns an error when importer returns one", async () => {
+        // Arrange
+        const message = "error";
+        const dependencies = createStubDependencies({
+            importer: async () => {
+                throw new Error(message);
+            },
+        });
 
         // Act
         const result = await findESLintConfiguration(dependencies, createStubRawSettings());
@@ -27,7 +58,7 @@ describe("findESLintConfiguration", () => {
 
     it("defaults the configuration file when one isn't provided", async () => {
         // Arrange
-        const dependencies = { exec: createStubExec() };
+        const dependencies = createStubDependencies({ exec: createStubExec() });
 
         // Act
         await findESLintConfiguration(dependencies, createStubRawSettings());
@@ -38,7 +69,7 @@ describe("findESLintConfiguration", () => {
 
     it("includes a configuration file in the ESLint command when one is provided", async () => {
         // Arrange
-        const dependencies = { exec: createStubExec() };
+        const dependencies = createStubDependencies({ exec: createStubExec() });
         const config = createStubRawSettings({
             eslint: "./custom/eslintrc.js",
         });
@@ -54,7 +85,72 @@ describe("findESLintConfiguration", () => {
 
     it("applies ESLint defaults when none are provided", async () => {
         // Arrange
-        const dependencies = { exec: createStubExec({ stdout: "{}" }) };
+        const dependencies = createStubDependencies({ exec: createStubExec({ stdout: "{}" }) });
+        const config = createStubRawSettings({
+            eslint: "./custom/eslintrc.js",
+        });
+
+        // Act
+        const result = await findESLintConfiguration(dependencies, config);
+
+        // Assert
+        expect(result).toEqual({
+            full: {
+                env: {},
+                extends: [],
+                rules: {},
+            },
+            raw: {
+                extends: [],
+            },
+        });
+    });
+
+    it("doesn't apply raw extends on top of reported when they don't exist", async () => {
+        // Arrange
+        const reportedExtends = ["reported"];
+        const dependencies = createStubDependencies({
+            exec: createStubExec({
+                stdout: JSON.stringify({
+                    extends: reportedExtends,
+                }),
+            }),
+            importer: async () => ({}),
+        });
+        const config = createStubRawSettings({
+            eslint: "./custom/eslintrc.js",
+        });
+
+        // Act
+        const result = await findESLintConfiguration(dependencies, config);
+
+        // Assert
+        expect(result).toEqual({
+            full: {
+                env: {},
+                extends: ["reported"],
+                rules: {},
+            },
+            raw: {
+                extends: [],
+            },
+        });
+    });
+
+    it("applies raw extends on top of reported when they exist", async () => {
+        // Arrange
+        const raw = {
+            extends: ["raw", "duplicated"],
+        };
+        const reportedExtends = ["reported", "duplicated"];
+        const dependencies = createStubDependencies({
+            exec: createStubExec({
+                stdout: JSON.stringify({
+                    extends: reportedExtends,
+                }),
+            }),
+            importer: async () => raw,
+        });
         const config = createStubRawSettings({
             eslint: "./custom/eslintrc.js",
         });
@@ -64,9 +160,12 @@ describe("findESLintConfiguration", () => {
 
         // Assert
         expect(result).toEqual({
-            env: {},
-            extends: [],
-            rules: {},
+            full: {
+                env: {},
+                extends: ["raw", "duplicated", "reported"],
+                rules: {},
+            },
+            raw,
         });
     });
 });
diff --git a/src/input/findOriginalConfigurations.test.ts b/src/input/findOriginalConfigurations.test.ts
index 98a7939..7c47671 100644
--- a/src/input/findOriginalConfigurations.test.ts
+++ b/src/input/findOriginalConfigurations.test.ts
@@ -1,6 +1,7 @@
 import {
     findOriginalConfigurations,
     FindOriginalConfigurationsDependencies,
+    OriginalConfigurations,
 } from "./findOriginalConfigurations";
 import { ResultStatus, TSLintToESLintSettings } from "../types";
 import { TSLintConfiguration } from "./findTSLintConfiguration";
@@ -11,27 +12,37 @@ const createRawSettings = (overrides: Partial<TSLintToESLintSettings> = {}) => (
     ...overrides,
 });
 
-const createDependencies = (overrides: Partial<FindOriginalConfigurationsDependencies> = {}) => ({
+const createStubDependencies = (
+    overrides: Partial<FindOriginalConfigurationsDependencies> = {},
+) => ({
     findESLintConfiguration: async () => ({
-        env: {},
-        extends: [],
-        rules: {},
+        full: {
+            env: {},
+            extends: [],
+            rules: {},
+        },
+        raw: {},
     }),
     findPackagesConfiguration: async () => ({
         dependencies: {},
         devDependencies: {},
     }),
     findTSLintConfiguration: async () => ({
-        rulesDirectory: [],
-        rules: {},
+        full: {
+            rulesDirectory: [],
+            rules: {},
+        },
+        raw: {},
     }),
     findTypeScriptConfiguration: async () => ({
         compilerOptions: {
             target: "es3",
         },
     }),
-    mergeLintConfigurations: (_: ESLintConfiguration | Error, tslint: TSLintConfiguration) =>
-        tslint,
+    mergeLintConfigurations: (
+        _: OriginalConfigurations<ESLintConfiguration> | Error,
+        tslint: OriginalConfigurations<TSLintConfiguration>,
+    ) => tslint,
     ...overrides,
 });
 
@@ -39,7 +50,7 @@ describe("findOriginalConfigurations", () => {
     it("returns errors when the tslint finder returns an error", async () => {
         // Arrange
         const complaint = "Complaint from TSLint";
-        const dependencies = createDependencies({
+        const dependencies = createStubDependencies({
             findTSLintConfiguration: async () => new Error(complaint),
         });
 
@@ -55,7 +66,7 @@ describe("findOriginalConfigurations", () => {
 
     it("returns only tslint results when the other finders return errors", async () => {
         // Arrange
-        const dependencies = createDependencies({
+        const dependencies = createStubDependencies({
             findESLintConfiguration: async () => new Error("one"),
             findPackagesConfiguration: async () => new Error("two"),
             findTypeScriptConfiguration: async () => new Error("three"),
@@ -68,8 +79,11 @@ describe("findOriginalConfigurations", () => {
         expect(result).toEqual({
             data: {
                 tslint: {
-                    rulesDirectory: [],
-                    rules: {},
+                    full: {
+                        rulesDirectory: [],
+                        rules: {},
+                    },
+                    raw: {},
                 },
             },
             status: ResultStatus.Succeeded,
@@ -79,7 +93,7 @@ describe("findOriginalConfigurations", () => {
     it("returns an error when an optional configuration returns an error and the user asked for it", async () => {
         // Arrange
         const eslint = new Error("one");
-        const dependencies = createDependencies({
+        const dependencies = createStubDependencies({
             findESLintConfiguration: async () => eslint,
         });
 
@@ -100,7 +114,7 @@ describe("findOriginalConfigurations", () => {
 
     it("returns successful results when an optional configuration returns an error and the user didn't ask for it", async () => {
         // Arrange
-        const dependencies = createDependencies({
+        const dependencies = createStubDependencies({
             findESLintConfiguration: async () => new Error("one"),
         });
 
@@ -115,8 +129,11 @@ describe("findOriginalConfigurations", () => {
                     devDependencies: {},
                 },
                 tslint: {
-                    rulesDirectory: [],
-                    rules: {},
+                    full: {
+                        rulesDirectory: [],
+                        rules: {},
+                    },
+                    raw: {},
                 },
                 typescript: {
                     compilerOptions: {
@@ -130,7 +147,7 @@ describe("findOriginalConfigurations", () => {
 
     it("returns successful results when all finders succeed", async () => {
         // Arrange
-        const dependencies = createDependencies();
+        const dependencies = createStubDependencies();
 
         // Act
         const result = await findOriginalConfigurations(dependencies, createRawSettings());
@@ -139,17 +156,23 @@ describe("findOriginalConfigurations", () => {
         expect(result).toEqual({
             data: {
                 eslint: {
-                    env: {},
-                    extends: [],
-                    rules: {},
+                    full: {
+                        env: {},
+                        extends: [],
+                        rules: {},
+                    },
+                    raw: {},
                 },
                 packages: {
                     dependencies: {},
                     devDependencies: {},
                 },
                 tslint: {
-                    rulesDirectory: [],
-                    rules: {},
+                    full: {
+                        rulesDirectory: [],
+                        rules: {},
+                    },
+                    raw: {},
                 },
                 typescript: {
                     compilerOptions: {
diff --git a/src/input/findConfiguration.test.ts b/src/input/findReportedConfiguration.test.ts
similarity index 75%
rename from src/input/findConfiguration.test.ts
rename to src/input/findReportedConfiguration.test.ts
index d2a9f21..55a2390 100644
--- a/src/input/findConfiguration.test.ts
+++ b/src/input/findReportedConfiguration.test.ts
@@ -1,14 +1,14 @@
 import { createStubExec, createStubThrowingExec } from "../adapters/exec.stubs";
-import { findConfiguration } from "./findConfiguration";
+import { findReportedConfiguration } from "./findReportedConfiguration";
 
-describe("findConfiguration", () => {
+describe("findReportedConfiguration", () => {
     it("returns stderr as an error when the command fails with a zero exit code", async () => {
         // Arrange
         const stderr = "error";
         const exec = createStubExec({ stderr });
 
         // Act
-        const result = await findConfiguration(exec, "command", "sample.json");
+        const result = await findReportedConfiguration(exec, "command", "sample.json");
 
         // Assert
         expect(result).toEqual(new Error(stderr));
@@ -20,7 +20,7 @@ describe("findConfiguration", () => {
         const exec = createStubThrowingExec({ stderr });
 
         // Act
-        const result = await findConfiguration(exec, "command", "sample.json");
+        const result = await findReportedConfiguration(exec, "command", "sample.json");
 
         // Assert
         expect(result).toEqual(new Error(stderr));
@@ -32,7 +32,7 @@ describe("findConfiguration", () => {
         const exec = createStubExec({ stdout });
 
         // Act
-        const result = await findConfiguration(exec, "command", "sample.json");
+        const result = await findReportedConfiguration(exec, "command", "sample.json");
 
         // Assert
         expect(result).toEqual(
@@ -49,7 +49,7 @@ describe("findConfiguration", () => {
         const exec = createStubExec({ stdout });
 
         // Act
-        const result = await findConfiguration(exec, "command", "sample.json");
+        const result = await findReportedConfiguration(exec, "command", "sample.json");
 
         // Assert
         expect(result).toEqual({
diff --git a/src/input/findTslintConfiguration.test.ts b/src/input/findTslintConfiguration.test.ts
index a429a8f..557c711 100644
--- a/src/input/findTslintConfiguration.test.ts
+++ b/src/input/findTslintConfiguration.test.ts
@@ -1,11 +1,22 @@
-import { findTSLintConfiguration } from "./findTSLintConfiguration";
+import {
+    findTSLintConfiguration,
+    FindTSLintConfigurationDependencies,
+} from "./findTSLintConfiguration";
 import { createStubExec, createStubThrowingExec } from "../adapters/exec.stubs";
 
+const createStubDependencies = (overrides: Partial<FindTSLintConfigurationDependencies> = {}) => ({
+    exec: createStubExec({ stdout: "{}" }),
+    importer: async () => ({}),
+    ...overrides,
+});
+
 describe("findTSLintConfiguration", () => {
-    it("returns an error when one occurs", async () => {
+    it("returns an error when exec returns one", async () => {
         // Arrange
         const stderr = "error";
-        const dependencies = { exec: createStubThrowingExec({ stderr }) };
+        const dependencies = createStubDependencies({
+            exec: createStubThrowingExec({ stderr }),
+        });
 
         // Act
         const result = await findTSLintConfiguration(dependencies, undefined);
@@ -18,10 +29,32 @@ describe("findTSLintConfiguration", () => {
         );
     });
 
+    it("returns an error when importer returns one", async () => {
+        // Arrange
+        const message = "error";
+        const dependencies = createStubDependencies({
+            importer: async () => {
+                throw new Error(message);
+            },
+        });
+
+        // Act
+        const result = await findTSLintConfiguration(dependencies, undefined);
+
+        // Assert
+        expect(result).toEqual(
+            expect.objectContaining({
+                message,
+            }),
+        );
+    });
+
     it("replaces an error with a v5.18 request when the --print-config option is unsupported", async () => {
         // Arrange
         const stderr = "unknown option `--print-config";
-        const dependencies = { exec: createStubThrowingExec({ stderr }) };
+        const dependencies = createStubDependencies({
+            exec: createStubThrowingExec({ stderr }),
+        });
 
         // Act
         const result = await findTSLintConfiguration(dependencies, undefined);
@@ -36,7 +69,9 @@ describe("findTSLintConfiguration", () => {
 
     it("defaults the configuration file when one isn't provided", async () => {
         // Arrange
-        const dependencies = { exec: createStubExec() };
+        const dependencies = createStubDependencies({
+            exec: createStubExec(),
+        });
 
         // Act
         await findTSLintConfiguration(dependencies, undefined);
@@ -47,7 +82,9 @@ describe("findTSLintConfiguration", () => {
 
     it("includes a configuration file in the TSLint command when one is provided", async () => {
         // Arrange
-        const dependencies = { exec: createStubExec() };
+        const dependencies = createStubDependencies({
+            exec: createStubExec(),
+        });
         const config = "./custom/tslint.json";
 
         // Act
@@ -61,7 +98,18 @@ describe("findTSLintConfiguration", () => {
 
     it("applies TSLint defaults when none are provided", async () => {
         // Arrange
-        const dependencies = { exec: createStubExec({ stdout: "{}" }) };
+        const raw = {
+            extends: ["raw", "duplicated"],
+        };
+        const reportedExtends = ["reported", "duplicated"];
+        const dependencies = createStubDependencies({
+            exec: createStubExec({
+                stdout: JSON.stringify({
+                    extends: reportedExtends,
+                }),
+            }),
+            importer: async () => raw,
+        });
         const config = "./custom/tslint.json";
 
         // Act
@@ -69,8 +117,12 @@ describe("findTSLintConfiguration", () => {
 
         // Assert
         expect(result).toEqual({
-            rulesDirectory: [],
-            rules: {},
+            full: {
+                extends: ["raw", "duplicated", "reported"],
+                rulesDirectory: [],
+                rules: {},
+            },
+            raw,
         });
     });
 });
diff --git a/src/input/importer.test.ts b/src/input/importer.test.ts
new file mode 100644
index 0000000..861bc37
--- /dev/null
+++ b/src/input/importer.test.ts
@@ -0,0 +1,145 @@
+import * as path from "path";
+
+import { importer } from "./importer";
+
+const stubCwd = "/path/to/cwd";
+
+type StubImporterSettings = {
+    files?: Record<string, string | Error>;
+    modules?: Record<string, unknown>;
+};
+
+const createStubDependencies = ({ files = {}, modules = {} }: StubImporterSettings = {}) => {
+    const fileSystem = {
+        fileExists: async (filePath: string) => filePath in files,
+        readFile: async (filePath: string) => files[filePath],
+    };
+
+    const getCwd = () => stubCwd;
+    const nativeImporter = async (filePath: string) => modules[filePath];
+
+    return { fileSystem, getCwd, nativeImporter };
+};
+
+describe("importer", () => {
+    it("natively imports a non-JSON module when its relative path exists", async () => {
+        // Arrange
+        const moduleName = "./relative.js";
+        const contents = { key: "value" };
+        const dependencies = createStubDependencies({
+            modules: {
+                [moduleName]: contents,
+            },
+        });
+
+        // Act
+        const imported = await importer(dependencies, moduleName);
+
+        // Assert
+        expect(imported).toEqual(contents);
+    });
+
+    it("natively imports a non-JSON module when its cwd-joined path exists", async () => {
+        // Arrange
+        const moduleName = "./relative.js";
+        const contents = { key: "value" };
+        const dependencies = createStubDependencies({
+            modules: {
+                [path.join(stubCwd, moduleName)]: contents,
+            },
+        });
+
+        // Act
+        const imported = await importer(dependencies, moduleName);
+
+        // Assert
+        expect(imported).toEqual(contents);
+    });
+
+    it("reads a JSONC module when its relative path exists", async () => {
+        // Arrange
+        const moduleName = "./relative.json";
+        const contents = { key: "value" };
+        const dependencies = createStubDependencies({
+            files: {
+                [moduleName]: JSON.stringify(contents),
+            },
+        });
+
+        // Act
+        const imported = await importer(dependencies, moduleName);
+
+        // Assert
+        expect(imported).toEqual(contents);
+    });
+
+    it("reads a JSONC module when its cwd-joined path exists", async () => {
+        // Arrange
+        const moduleName = "./relative.json";
+        const contents = { key: "value" };
+        const dependencies = createStubDependencies({
+            files: {
+                [path.join(stubCwd, moduleName)]: JSON.stringify(contents),
+            },
+        });
+
+        // Act
+        const imported = await importer(dependencies, moduleName);
+
+        // Assert
+        expect(imported).toEqual(contents);
+    });
+
+    it("returns an error when reading a JSONC module gives an error", async () => {
+        // Arrange
+        const moduleName = "./relative.json";
+        const error = new Error("NOPE, INVALID");
+        const dependencies = createStubDependencies({
+            files: {
+                [moduleName]: error,
+            },
+        });
+
+        // Act
+        const imported = await importer(dependencies, moduleName);
+
+        // Assert
+        expect(imported).toEqual(error);
+    });
+
+    it("returns an error when parsing a JSONC module gives an error", async () => {
+        // Arrange
+        const moduleName = "./relative.json";
+        const dependencies = createStubDependencies({
+            files: {
+                [moduleName]: "NOPE, INVALID",
+            },
+        });
+
+        // Act
+        const imported = await importer(dependencies, moduleName);
+
+        // Assert
+        expect(imported).toEqual(
+            expect.objectContaining({
+                message: "Unexpected token N in JSON at position 0",
+            }),
+        );
+    });
+
+    it("returns an error when neither module locations exist for a path", async () => {
+        // Arrange
+        const moduleName = "./relative.json";
+        const dependencies = createStubDependencies();
+
+        // Act
+        const imported = await importer(dependencies, moduleName);
+
+        // Assert
+        expect(imported).toEqual(
+            expect.objectContaining({
+                message: expect.stringContaining(`Could not find '${moduleName}' after trying:`),
+            }),
+        );
+    });
+});
diff --git a/src/input/mergeLintConfigurations.test.ts b/src/input/mergeLintConfigurations.test.ts
index 56297d8..b6b1bf0 100644
--- a/src/input/mergeLintConfigurations.test.ts
+++ b/src/input/mergeLintConfigurations.test.ts
@@ -1,12 +1,17 @@
 import { mergeLintConfigurations } from "./mergeLintConfigurations";
 import { ESLintConfiguration } from "./findESLintConfiguration";
+import { OriginalConfigurations } from "./findOriginalConfigurations";
+import { TSLintConfiguration } from "./findTSLintConfiguration";
 
-const stubTSLintConfiguration = {
-    rulesDirectory: [],
-    rules: {
-        disabled: true,
-        enabled: true,
+const stubTSLintConfiguration: OriginalConfigurations<TSLintConfiguration> = {
+    full: {
+        rulesDirectory: [],
+        rules: {
+            disabled: true,
+            enabled: true,
+        },
     },
+    raw: {},
 };
 
 describe("mergeLintConfigurations", () => {
@@ -23,10 +28,13 @@ describe("mergeLintConfigurations", () => {
 
     it("returns the tslint configuration when the eslint configuration doesn't have tslint rules", () => {
         // Arrange
-        const eslint: ESLintConfiguration = {
-            env: {},
-            extends: [],
-            rules: {},
+        const eslint: OriginalConfigurations<ESLintConfiguration> = {
+            full: {
+                env: {},
+                extends: [],
+                rules: {},
+            },
+            raw: {},
         };
 
         // Act
@@ -38,17 +46,20 @@ describe("mergeLintConfigurations", () => {
 
     it("returns the tslint configuration when the eslint configuration's tslint rules are disabled", () => {
         // Arrange
-        const eslint: ESLintConfiguration = {
-            env: {},
-            extends: [],
-            rules: {
-                "@typescript-eslint/tslint/config": [
-                    "off",
-                    {
-                        extra: true,
-                    },
-                ],
+        const eslint: OriginalConfigurations<ESLintConfiguration> = {
+            full: {
+                env: {},
+                extends: [],
+                rules: {
+                    "@typescript-eslint/tslint/config": [
+                        "off",
+                        {
+                            extra: true,
+                        },
+                    ],
+                },
             },
+            raw: {},
         };
 
         // Act
@@ -63,17 +74,20 @@ describe("mergeLintConfigurations", () => {
         const extraRules = {
             extra: true,
         };
-        const eslint: ESLintConfiguration = {
-            env: {},
-            extends: [],
-            rules: {
-                "@typescript-eslint/tslint/config": [
-                    "error",
-                    {
-                        rules: extraRules,
-                    },
-                ],
+        const eslint: OriginalConfigurations<ESLintConfiguration> = {
+            full: {
+                env: {},
+                extends: [],
+                rules: {
+                    "@typescript-eslint/tslint/config": [
+                        "error",
+                        {
+                            rules: extraRules,
+                        },
+                    ],
+                },
             },
+            raw: {},
         };
 
         // Act
@@ -82,9 +96,12 @@ describe("mergeLintConfigurations", () => {
         // Assert
         expect(result).toEqual({
             ...stubTSLintConfiguration,
-            rules: {
-                ...stubTSLintConfiguration.rules,
-                ...extraRules,
+            full: {
+                ...stubTSLintConfiguration.full,
+                rules: {
+                    ...stubTSLintConfiguration.full.rules,
+                    ...extraRules,
+                },
             },
         });
     });
diff --git a/src/rules/converters/tests/comment-format.test.ts b/src/rules/converters/tests/comment-format.test.ts
new file mode 100644
index 0000000..539e5a6
--- /dev/null
+++ b/src/rules/converters/tests/comment-format.test.ts
@@ -0,0 +1,149 @@
+import { convertCommentFormat, CapitalizedIgnoreMessage } from "../comment-format";
+
+describe(convertCommentFormat, () => {
+    test("conversion without arguments", () => {
+        const result = convertCommentFormat({
+            ruleArguments: [],
+        });
+
+        expect(result).toEqual({
+            rules: [
+                {
+                    ruleName: "capitalized-comments",
+                    ruleArguments: [],
+                    notices: [],
+                },
+                {
+                    ruleName: "spaced-comment",
+                    ruleArguments: ["never"],
+                },
+            ],
+        });
+    });
+
+    test("conversion with check-space argument", () => {
+        const result = convertCommentFormat({
+            ruleArguments: ["check-space"],
+        });
+
+        expect(result).toEqual({
+            rules: [
+                {
+                    ruleName: "capitalized-comments",
+                    ruleArguments: [],
+                    notices: [],
+                },
+                {
+                    ruleName: "spaced-comment",
+                    ruleArguments: [],
+                },
+            ],
+        });
+    });
+
+    test("conversion with check-lowercase arguments", () => {
+        const result = convertCommentFormat({
+            ruleArguments: ["check-lowercase"],
+        });
+
+        expect(result).toEqual({
+            rules: [
+                {
+                    ruleName: "capitalized-comments",
+                    ruleArguments: ["never"],
+                    notices: [],
+                },
+                {
+                    ruleName: "spaced-comment",
+                    ruleArguments: ["never"],
+                },
+            ],
+        });
+    });
+
+    test("conversion with ignore-pattern argument", () => {
+        const result = convertCommentFormat({
+            ruleArguments: [{ "ignore-pattern": "STD\\w{2,3}\\b" }],
+        });
+
+        expect(result).toEqual({
+            rules: [
+                {
+                    ruleName: "capitalized-comments",
+                    ruleArguments: [],
+                    notices: [CapitalizedIgnoreMessage],
+                },
+                {
+                    ruleName: "spaced-comment",
+                    ruleArguments: ["never"],
+                },
+            ],
+        });
+    });
+
+    test("conversion with empty ignore-words argument", () => {
+        const result = convertCommentFormat({
+            ruleArguments: [{ "ignore-words": [] }],
+        });
+
+        expect(result).toEqual({
+            rules: [
+                {
+                    ruleName: "capitalized-comments",
+                    ruleArguments: [],
+                    notices: [],
+                },
+                {
+                    ruleName: "spaced-comment",
+                    ruleArguments: ["never"],
+                },
+            ],
+        });
+    });
+
+    test("conversion with ignore-words argument", () => {
+        const result = convertCommentFormat({
+            ruleArguments: [{ "ignore-words": ["TODO", "HACK"] }],
+        });
+
+        expect(result).toEqual({
+            rules: [
+                {
+                    ruleName: "capitalized-comments",
+                    ruleArguments: [],
+                    notices: [CapitalizedIgnoreMessage],
+                },
+                {
+                    ruleName: "spaced-comment",
+                    ruleArguments: ["never"],
+                },
+            ],
+        });
+    });
+
+    test("conversion with all arguments", () => {
+        const result = convertCommentFormat({
+            ruleArguments: [
+                "check-space",
+                "check-lowercase",
+                "check-uppercase",
+                "allow-trailing-lowercase",
+                { "ignore-words": ["TODO", "HACK"], "ignore-pattern": "STD\\w{2,3}\\b" },
+            ],
+        });
+
+        expect(result).toEqual({
+            rules: [
+                {
+                    ruleName: "capitalized-comments",
+                    ruleArguments: ["always"],
+                    notices: [CapitalizedIgnoreMessage],
+                },
+                {
+                    ruleName: "spaced-comment",
+                    ruleArguments: [],
+                },
+            ],
+        });
+    });
+});
diff --git a/src/rules/converters/tests/no-reference.test.ts b/src/rules/converters/tests/no-reference.test.ts
index 9353176..ea8017d 100644
--- a/src/rules/converters/tests/no-reference.test.ts
+++ b/src/rules/converters/tests/no-reference.test.ts
@@ -9,7 +9,7 @@ describe(convertNoReference, () => {
         expect(result).toEqual({
             rules: [
                 {
-                    ruleName: "@typescript-eslint/no-triple-slash-reference",
+                    ruleName: "@typescript-eslint/triple-slash-reference",
                 },
             ],
         });
diff --git a/src/rules/converters/tests/strict-boolean-expressions.test.ts b/src/rules/converters/tests/strict-boolean-expressions.test.ts
new file mode 100644
index 0000000..4768fa1
--- /dev/null
+++ b/src/rules/converters/tests/strict-boolean-expressions.test.ts
@@ -0,0 +1,109 @@
+import {
+    convertStrictBooleanExpressions,
+    ForbiddenOtherNonBooleanTypes,
+} from "../strict-boolean-expressions";
+
+describe(convertStrictBooleanExpressions, () => {
+    test("conversion without arguments", () => {
+        const result = convertStrictBooleanExpressions({
+            ruleArguments: [],
+        });
+
+        expect(result).toEqual({
+            rules: [
+                {
+                    ruleName: "@typescript-eslint/strict-boolean-expressions",
+                    ruleArguments: [],
+                    notices: [],
+                },
+            ],
+        });
+    });
+
+    test("conversion with true argument", () => {
+        const result = convertStrictBooleanExpressions({
+            ruleArguments: [true],
+        });
+
+        expect(result).toEqual({
+            rules: [
+                {
+                    ruleName: "@typescript-eslint/strict-boolean-expressions",
+                    ruleArguments: [],
+                    notices: [ForbiddenOtherNonBooleanTypes],
+                },
+            ],
+        });
+    });
+
+    test("conversion with allow-undefined-union argument", () => {
+        const result = convertStrictBooleanExpressions({
+            ruleArguments: [true, "allow-undefined-union"],
+        });
+
+        expect(result).toEqual({
+            rules: [
+                {
+                    ruleName: "@typescript-eslint/strict-boolean-expressions",
+                    ruleArguments: [
+                        {
+                            allowNullable: true,
+                        },
+                    ],
+                    notices: [ForbiddenOtherNonBooleanTypes],
+                },
+            ],
+        });
+    });
+
+    test("conversion with ignore-rhs argument", () => {
+        const result = convertStrictBooleanExpressions({
+            ruleArguments: [true, "ignore-rhs"],
+        });
+
+        expect(result).toEqual({
+            rules: [
+                {
+                    ruleName: "@typescript-eslint/strict-boolean-expressions",
+                    ruleArguments: [
+                        {
+                            ignoreRhs: true,
+                        },
+                    ],
+                    notices: [ForbiddenOtherNonBooleanTypes],
+                },
+            ],
+        });
+    });
+
+    test("conversion with all arguments", () => {
+        const result = convertStrictBooleanExpressions({
+            ruleArguments: [
+                true,
+                "allow-undefined-union",
+                "allow-boolean-or-undefined",
+                "allow-null-union",
+                "ignore-rhs",
+                "allow-string",
+                "allow-enum",
+                "allow-number",
+                "allow-mix",
+            ],
+        });
+
+        expect(result).toEqual({
+            rules: [
+                {
+                    ruleName: "@typescript-eslint/strict-boolean-expressions",
+                    ruleArguments: [
+                        {
+                            allowNullable: true,
+                            ignoreRhs: true,
+                        },
+                    ],
+                    notices: [ForbiddenOtherNonBooleanTypes],
+                },
+            ],
+        });
+    });
+});