diff --git a/src/cli/main.ts b/src/cli/main.ts index 94e023f67..fec86a327 100644 --- a/src/cli/main.ts +++ b/src/cli/main.ts @@ -5,7 +5,7 @@ import { promisify } from "util"; import { convertConfig } from "../convertConfig"; import { createNewConfiguration } from "../creation/createNewConfiguration"; -import { findTslintRules } from "../input/findTslintRules"; +import { findTslintConfiguration } from "../input/findTslintConfiguration"; import { TSLintToESLintSettings } from "../types"; import { runCli } from "./runCli"; @@ -18,22 +18,24 @@ const logger = { stdout: process.stdout, }; -const ruleFinder = (config: string) => findTslintRules(config, promisify(exec)); - const fileExists = (filePath: string) => Promise.resolve(fs.existsSync(filePath)); const runtime = { convertConfig: (settings: TSLintToESLintSettings) => convertConfig({ - createNewConfiguration: configConversionResults => - createNewConfiguration(configConversionResults, fs.promises.writeFile), + createNewConfiguration: (conversionResults, originalConfiguration) => + createNewConfiguration( + conversionResults, + originalConfiguration, + fs.promises.writeFile, + ), fileExists, + findTslintConfiguration: (config: string) => + findTslintConfiguration(config, promisify(exec)), logger, - ruleFinder, settings, }), logger, - ruleFinder, }; export const main = async (argv: string[]) => { diff --git a/src/convertConfig.test.ts b/src/convertConfig.test.ts index a089bd565..066f0693d 100644 --- a/src/convertConfig.test.ts +++ b/src/convertConfig.test.ts @@ -5,7 +5,7 @@ import { ResultStatus } from "./types"; const defaultRequest = { settings: {}, logger: createStubLogger(), - ruleFinder: jest.fn().mockReturnValue(Promise.resolve(new Error())), + findTslintConfiguration: jest.fn().mockReturnValue(Promise.resolve(new Error())), fileExists: jest.fn().mockReturnValue(Promise.resolve(true)), createNewConfiguration: jest.fn().mockReturnValue(Promise.resolve()), }; @@ -41,7 +41,7 @@ describe("convertConfig", () => { await convertConfig(request); // Assert - expect(request.ruleFinder).toHaveBeenLastCalledWith(request.settings.config); + expect(request.findTslintConfiguration).toHaveBeenLastCalledWith(request.settings.config); }); it("searches for ./tslint.json by default when no settings.config is provided", async () => { @@ -55,15 +55,15 @@ describe("convertConfig", () => { await convertConfig(request); // Assert - expect(request.ruleFinder).toHaveBeenLastCalledWith("./tslint.json"); + expect(request.findTslintConfiguration).toHaveBeenLastCalledWith("./tslint.json"); }); - it("returns a failure result when ruleFinder returns an error", async () => { + it("returns a failure result when findTslintConfiguration returns an error", async () => { // Arrange const error = new Error("oh no"); const request = { ...defaultRequest, - ruleFinder: jest.fn().mockReturnValue(Promise.resolve(error)), + findTslintConfiguration: jest.fn().mockReturnValue(Promise.resolve(error)), }; // Act @@ -76,11 +76,11 @@ describe("convertConfig", () => { }); }); - it("creates a new configuration when ruleFinder returns rules", async () => { + it("creates a new configuration when findTslintConfiguration returns rules", async () => { // Arrange const request = { ...defaultRequest, - ruleFinder: jest.fn().mockReturnValue({ + findTslintConfiguration: jest.fn().mockReturnValue({ rules: { "sample-rule": { ruleArguments: ["one", "two"], diff --git a/src/convertConfig.ts b/src/convertConfig.ts index 15e9b3030..a4556a717 100644 --- a/src/convertConfig.ts +++ b/src/convertConfig.ts @@ -1,29 +1,32 @@ -import { FoundTSLintRules } from "./input/findTslintRules"; +import { TSLintConfiguration } from "./input/findTslintConfiguration"; import { ProcessLogger } from "./logger"; import { reportConversionResults } from "./reportConversionResults"; import { convertRules, ConfigConversionResults } from "./rules/convertRules"; import { converters } from "./rules/converters"; import { TSLintToESLintSettings, TSLintToESLintResult, ResultStatus } from "./types"; -export type CreateNewConfiguration = (conversionResults: ConfigConversionResults) => Promise; +export type CreateNewConfiguration = ( + conversionResults: ConfigConversionResults, + originalConfiguration: TSLintConfiguration, +) => Promise; export type FileExists = (filePath: string) => Promise; -export type RuleFinder = (config: string) => Promise; +export type findTslintConfiguration = (config: string) => Promise; export type ConvertConfigRequest = { createNewConfiguration: CreateNewConfiguration; fileExists: FileExists; + findTslintConfiguration: findTslintConfiguration; logger: ProcessLogger; - ruleFinder: RuleFinder; settings: TSLintToESLintSettings; }; export const convertConfig = async ({ createNewConfiguration, fileExists, + findTslintConfiguration, logger, - ruleFinder, settings, }: ConvertConfigRequest): Promise => { const { config = "./tslint.json" } = settings; @@ -34,23 +37,23 @@ export const convertConfig = async ({ }; } - const originalRules = await ruleFinder(config); - if (originalRules instanceof Error) { + const originalConfiguration = await findTslintConfiguration(config); + if (originalConfiguration instanceof Error) { return { - error: originalRules, + error: originalConfiguration, status: ResultStatus.Failed, }; } const convertedRules = convertRules( - Object.entries(originalRules.rules).map(([ruleName, value]) => ({ + Object.entries(originalConfiguration.rules).map(([ruleName, value]) => ({ ruleName, ...value, })), converters, ); - await createNewConfiguration(convertedRules); + await createNewConfiguration(convertedRules, originalConfiguration); reportConversionResults(convertedRules, logger); return { diff --git a/src/creation/createNewConfiguration.test.ts b/src/creation/createNewConfiguration.test.ts index 1813313e9..eea955e7f 100644 --- a/src/creation/createNewConfiguration.test.ts +++ b/src/creation/createNewConfiguration.test.ts @@ -2,6 +2,11 @@ import { emptyConversionResults } from "../stubs"; import { createNewConfiguration } from "./createNewConfiguration"; import { ConfigConversionResults } from "../rules/convertRules"; +const originalConfiguration = { + ruleDirectories: [], + rules: {}, +}; + describe("createNewConfiguration", () => { it("excludes the tslint plugin when there are no missing rules", async () => { // Arrange @@ -12,7 +17,7 @@ describe("createNewConfiguration", () => { const writeFile = jest.fn().mockReturnValue(Promise.resolve()); // Act - await createNewConfiguration(conversionResults, writeFile); + await createNewConfiguration(conversionResults, originalConfiguration, writeFile); // Assert expect(writeFile).toHaveBeenLastCalledWith( @@ -47,7 +52,7 @@ describe("createNewConfiguration", () => { const writeFile = jest.fn().mockReturnValue(Promise.resolve()); // Act - await createNewConfiguration(conversionResults, writeFile); + await createNewConfiguration(conversionResults, originalConfiguration, writeFile); // Assert expect(writeFile).toHaveBeenLastCalledWith( diff --git a/src/creation/createNewConfiguration.ts b/src/creation/createNewConfiguration.ts index c02cfa26c..e8b3d7fdf 100644 --- a/src/creation/createNewConfiguration.ts +++ b/src/creation/createNewConfiguration.ts @@ -1,3 +1,4 @@ +import { TSLintConfiguration } from "../input/findTslintConfiguration"; import { ConfigConversionResults } from "../rules/convertRules"; import { formatConvertedRules } from "./formatConvertedRules"; @@ -5,6 +6,7 @@ export type WriteFile = (filePath: string, contents: string) => Promise; export const createNewConfiguration = async ( conversionResults: ConfigConversionResults, + originalConfiguration: TSLintConfiguration, writeFile: WriteFile, ) => { const output = { @@ -15,7 +17,7 @@ export const createNewConfiguration = async ( ...(conversionResults.missing.length && { plugins: ["@typescript-eslint/tslint"], }), - rules: formatConvertedRules(conversionResults), + rules: formatConvertedRules(conversionResults, originalConfiguration), }; await writeFile(".eslintrc.json", JSON.stringify(output, undefined, 4)); diff --git a/src/creation/formatConvertedRules.test.ts b/src/creation/formatConvertedRules.test.ts index da2916b3a..017546374 100644 --- a/src/creation/formatConvertedRules.test.ts +++ b/src/creation/formatConvertedRules.test.ts @@ -2,6 +2,11 @@ import { ConfigConversionResults } from "../rules/convertRules"; import { emptyConversionResults } from "../stubs"; import { formatConvertedRules } from "./formatConvertedRules"; +const originalConfiguration = { + ruleDirectories: [], + rules: {}, +}; + describe("formatConvertedRules", () => { it("prints rules sorted by name when there are multiple rules", () => { // Arrange @@ -26,7 +31,7 @@ describe("formatConvertedRules", () => { }; // Act - const output = formatConvertedRules(results); + const output = formatConvertedRules(results, originalConfiguration); // Assert expect(output).toEqual({ @@ -51,7 +56,7 @@ describe("formatConvertedRules", () => { }; // Act - const output = formatConvertedRules(results); + const output = formatConvertedRules(results, originalConfiguration); // Assert expect(output).toEqual({ @@ -76,7 +81,7 @@ describe("formatConvertedRules", () => { }; // Act - const output = formatConvertedRules(results); + const output = formatConvertedRules(results, originalConfiguration); // Assert expect(output).toEqual({ @@ -101,7 +106,7 @@ describe("formatConvertedRules", () => { }; // Act - const output = formatConvertedRules(results); + const output = formatConvertedRules(results, originalConfiguration); // Assert expect(output).toEqual({ @@ -133,7 +138,7 @@ describe("formatConvertedRules", () => { }; // Act - const output = formatConvertedRules(results); + const output = formatConvertedRules(results, originalConfiguration); // Assert expect(output).toEqual({ diff --git a/src/creation/formatConvertedRules.ts b/src/creation/formatConvertedRules.ts index 5224d147b..8d875e8c9 100644 --- a/src/creation/formatConvertedRules.ts +++ b/src/creation/formatConvertedRules.ts @@ -1,8 +1,12 @@ import { ConfigConversionResults } from "../rules/convertRules"; import { ESLintRuleOptions } from "../rules/types"; import { formatMissingRules } from "./formatMissingRules"; +import { TSLintConfiguration } from "../input/findTslintConfiguration"; -export const formatConvertedRules = (conversionResults: ConfigConversionResults) => { +export const formatConvertedRules = ( + conversionResults: ConfigConversionResults, + originalConfiguration: TSLintConfiguration, +) => { const output: { [i: string]: string | any[] } = {}; const sortedRuleEntries = Array.from(conversionResults.converted).sort( ([ruleNameA], [ruleNameB]) => ruleNameA.localeCompare(ruleNameB), @@ -13,7 +17,10 @@ export const formatConvertedRules = (conversionResults: ConfigConversionResults) } if (conversionResults.missing.length !== 0) { - output["@typescript-eslint/tslint/config"] = formatMissingRules(conversionResults.missing); + output["@typescript-eslint/tslint/config"] = formatMissingRules( + conversionResults.missing, + originalConfiguration.ruleDirectories, + ); } return output; diff --git a/src/creation/formatMissingRules.test.ts b/src/creation/formatMissingRules.test.ts index b8805ee10..2baf620cb 100644 --- a/src/creation/formatMissingRules.test.ts +++ b/src/creation/formatMissingRules.test.ts @@ -13,7 +13,7 @@ describe("formatMissingRules", () => { ]; // Act - const output = formatMissingRules(missing); + const output = formatMissingRules(missing, []); // Assert expect(output).toEqual([ @@ -37,7 +37,7 @@ describe("formatMissingRules", () => { ]; // Act - const output = formatMissingRules(missing); + const output = formatMissingRules(missing, []); // Assert expect(output).toEqual([ @@ -66,7 +66,7 @@ describe("formatMissingRules", () => { ]; // Act - const output = formatMissingRules(missing); + const output = formatMissingRules(missing, []); // Assert expect(output).toEqual([ @@ -96,7 +96,7 @@ describe("formatMissingRules", () => { ]; // Act - const output = formatMissingRules(missing); + const output = formatMissingRules(missing, []); // Assert expect(output).toEqual([ @@ -108,4 +108,30 @@ describe("formatMissingRules", () => { }, ]); }); + + it("includes rule directories when there are rule directories", () => { + // Arrange + const ruleDirectories = ["./path/to/rules"]; + const missing: TSLintRuleOptions[] = [ + { + ruleArguments: [], + ruleName: "tslint-rule-a", + ruleSeverity: "warning", + }, + ]; + + // Act + const output = formatMissingRules(missing, ruleDirectories); + + // Assert + expect(output).toEqual([ + "error", + { + ruleDirectories, + rules: { + "tslint-rule-a": true, + }, + }, + ]); + }); }); diff --git a/src/creation/formatMissingRules.ts b/src/creation/formatMissingRules.ts index 0d75e7b86..1bc924cd1 100644 --- a/src/creation/formatMissingRules.ts +++ b/src/creation/formatMissingRules.ts @@ -1,6 +1,6 @@ import { TSLintRuleOptions } from "../rules/types"; -export const formatMissingRules = (missing: TSLintRuleOptions[]) => { +export const formatMissingRules = (missing: TSLintRuleOptions[], ruleDirectories: string[]) => { const rules: { [i: string]: unknown } = {}; for (const rule of missing.sort((a, b) => a.ruleName.localeCompare(b.ruleName))) { @@ -12,8 +12,8 @@ export const formatMissingRules = (missing: TSLintRuleOptions[]) => { return [ "error", { + ...(ruleDirectories.length !== 0 && { ruleDirectories }), rules, - // TODO: rulesDirectory? }, ]; }; diff --git a/src/input/findTslintRules.test.ts b/src/input/findTslintConfiguration.test.ts similarity index 75% rename from src/input/findTslintRules.test.ts rename to src/input/findTslintConfiguration.test.ts index b2620a901..21a14e8d9 100644 --- a/src/input/findTslintRules.test.ts +++ b/src/input/findTslintConfiguration.test.ts @@ -1,13 +1,13 @@ -import { findTslintRules } from "./findTslintRules"; +import { findTslintConfiguration } from "./findTslintConfiguration"; -describe("findTslintRules", () => { +describe("findTslintConfiguration", () => { it("returns stderr as an error when the command fails", async () => { // Arrange const [stderr, stdout] = ["error", ""]; const childProcessExec = () => Promise.resolve({ stderr, stdout }); // Act - const result = await findTslintRules("tslint.json", childProcessExec); + const result = await findTslintConfiguration("tslint.json", childProcessExec); // Assert expect(result).toEqual(new Error(stderr)); @@ -19,7 +19,7 @@ describe("findTslintRules", () => { const childProcessExec = () => Promise.resolve({ stderr, stdout }); // Act - const result = await findTslintRules("tslint.json", childProcessExec); + const result = await findTslintConfiguration("tslint.json", childProcessExec); // Assert expect(result).toEqual( @@ -36,7 +36,7 @@ describe("findTslintRules", () => { const childProcessExec = () => Promise.resolve({ stderr, stdout }); // Act - const result = await findTslintRules("tslint.json", childProcessExec); + const result = await findTslintConfiguration("tslint.json", childProcessExec); // Assert expect(result).toEqual(rules); diff --git a/src/input/findTslintRules.ts b/src/input/findTslintConfiguration.ts similarity index 73% rename from src/input/findTslintRules.ts rename to src/input/findTslintConfiguration.ts index 57f462a08..77756c55c 100644 --- a/src/input/findTslintRules.ts +++ b/src/input/findTslintConfiguration.ts @@ -1,4 +1,5 @@ -export type FoundTSLintRules = { +export type TSLintConfiguration = { + ruleDirectories: string[]; rules: { [i: string]: any; }; @@ -6,10 +7,10 @@ export type FoundTSLintRules = { export type ChildProcessExec = (command: string) => Promise<{ stderr: string; stdout: string }>; -export const findTslintRules = async ( +export const findTslintConfiguration = async ( config: string, childProcessExec: ChildProcessExec, -): Promise => { +): Promise => { const command = buildCommand(config); const { stderr, stdout } = await childProcessExec(command); @@ -18,7 +19,7 @@ export const findTslintRules = async ( } try { - return JSON.parse(stdout) as FoundTSLintRules; + return JSON.parse(stdout) as TSLintConfiguration; } catch (error) { return new Error(`Error parsing TSLint configuration: ${error}`); }