From 4b84a2ed71e71b46c093bad87879183b287cb721 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Sun, 30 Jun 2019 20:02:10 -0400 Subject: [PATCH] Applied env and node presets per existing configurations Adds a package configuration lookup in addition to ESLint, TSLint, and TypeScript, and has them all fill in minimal defaults. Fixes #33. Fixes #35. --- src/cli/main.ts | 2 + src/conversion/conversionResults.stubs.ts | 6 +- src/conversion/convertConfig.test.ts | 6 +- src/conversion/convertConfig.ts | 9 +- src/creation/eslint/createEnv.test.ts | 188 ++++++++++++++++++ src/creation/eslint/createEnv.ts | 27 +++ src/creation/formatConvertedRules.ts | 4 +- src/creation/writeConversionResults.test.ts | 22 +- src/creation/writeConversionResults.ts | 14 +- ...tion.test.ts => findConfiguration.test.ts} | 38 +--- ...tConfiguration.ts => findConfiguration.ts} | 19 +- src/input/findESLintConfiguration.test.ts | 23 ++- src/input/findESLintConfiguration.ts | 37 ++-- src/input/findOriginalConfigurations.test.ts | 70 ++++--- src/input/findOriginalConfigurations.ts | 11 +- src/input/findPackagesConfiguration.test.ts | 42 ++++ src/input/findPackagesConfiguration.ts | 39 ++++ src/input/findTSLintConfiguration.ts | 17 +- src/input/findTslintConfiguration.test.ts | 18 +- src/input/findTypeScriptConfiguration.test.ts | 24 ++- src/input/findTypeScriptConfiguration.ts | 38 ++-- src/reporting/reportConversionResults.ts | 20 +- src/rules/convertRules.ts | 4 +- src/types.ts | 5 + 24 files changed, 543 insertions(+), 140 deletions(-) create mode 100644 src/creation/eslint/createEnv.test.ts create mode 100644 src/creation/eslint/createEnv.ts rename src/input/{findLintConfiguration.test.ts => findConfiguration.test.ts} (55%) rename src/input/{findLintConfiguration.ts => findConfiguration.ts} (66%) create mode 100644 src/input/findPackagesConfiguration.test.ts create mode 100644 src/input/findPackagesConfiguration.ts diff --git a/src/cli/main.ts b/src/cli/main.ts index 30e91abd2..15ae45bd7 100644 --- a/src/cli/main.ts +++ b/src/cli/main.ts @@ -24,6 +24,7 @@ import { import { converters } from "../rules/converters"; import { convertRules } from "../rules/convertRules"; import { mergers } from "../rules/mergers"; +import { findPackagesConfiguration } from "../input/findPackagesConfiguration"; const convertRulesDependencies = { converters, @@ -36,6 +37,7 @@ const findConfigurationDependencies = { const findOriginalConfigurationsDependencies: FindOriginalConfigurationsDependencies = { findESLintConfiguration: bind(findESLintConfiguration, findConfigurationDependencies), + findPackagesConfiguration: bind(findPackagesConfiguration, findConfigurationDependencies), findTypeScriptConfiguration: bind(findTypeScriptConfiguration, findConfigurationDependencies), findTSLintConfiguration: bind(findTSLintConfiguration, findConfigurationDependencies), }; diff --git a/src/conversion/conversionResults.stubs.ts b/src/conversion/conversionResults.stubs.ts index 17fc5b33d..0acb2d7f6 100644 --- a/src/conversion/conversionResults.stubs.ts +++ b/src/conversion/conversionResults.stubs.ts @@ -1,8 +1,8 @@ -import { ConfigConversionResults } from "../rules/convertRules"; +import { RuleConversionResults } from "../rules/convertRules"; export const createEmptyConversionResults = ( - overrides: Partial, -): ConfigConversionResults => ({ + overrides: Partial, +): RuleConversionResults => ({ converted: new Map(), failed: [], missing: [], diff --git a/src/conversion/convertConfig.test.ts b/src/conversion/convertConfig.test.ts index 179c96c9d..bb603d9cf 100644 --- a/src/conversion/convertConfig.test.ts +++ b/src/conversion/convertConfig.test.ts @@ -1,6 +1,6 @@ import { ResultStatus, FailedResult, SucceededDataResult } from "../types"; import { convertConfig, ConvertConfigDependencies } from "./convertConfig"; -import { OriginalConfigurationsData } from "../input/findOriginalConfigurations"; +import { OriginalConfigurations } from "../input/findOriginalConfigurations"; const createStubDependencies = ( overrides: Pick, @@ -12,12 +12,10 @@ const createStubDependencies = ( }); const createStubOriginalConfigurationsData = () => ({ - eslint: {}, tslint: { rules: [], ruleDirectories: [], }, - typescript: {}, }); describe("convertConfig", () => { @@ -41,7 +39,7 @@ describe("convertConfig", () => { it("returns a successful result when finding the original configurations succeeds", async () => { // Arrange - const findSuccess: SucceededDataResult = { + const findSuccess: SucceededDataResult = { data: createStubOriginalConfigurationsData(), status: ResultStatus.Succeeded, }; diff --git a/src/conversion/convertConfig.ts b/src/conversion/convertConfig.ts index 793387282..fc4046a34 100644 --- a/src/conversion/convertConfig.ts +++ b/src/conversion/convertConfig.ts @@ -21,15 +21,12 @@ export const convertConfig = async ( return originalConfigurations; } - const configConversonResults = dependencies.convertRules( + const ruleConversionResults = dependencies.convertRules( originalConfigurations.data.tslint.rules, ); - await dependencies.writeConversionResults( - configConversonResults, - originalConfigurations.data.tslint, - ); - dependencies.reportConversionResults(configConversonResults); + await dependencies.writeConversionResults(ruleConversionResults, originalConfigurations.data); + dependencies.reportConversionResults(ruleConversionResults); return { status: ResultStatus.Succeeded, diff --git a/src/creation/eslint/createEnv.test.ts b/src/creation/eslint/createEnv.test.ts new file mode 100644 index 000000000..c648ca567 --- /dev/null +++ b/src/creation/eslint/createEnv.test.ts @@ -0,0 +1,188 @@ +import { createEnv } from "./createEnv"; +import { TypeScriptConfiguration } from "../../input/findTypeScriptConfiguration"; + +const createTypeScriptCompilerOptions = ( + overrides: Partial = {}, +) => ({ + target: "es3", + ...overrides, +}); + +describe("createEnv", () => { + it("returns node, browser, and es6 as true if typescript is undefined", () => { + // Arrange + const packages = undefined; + const typescript = undefined; + + // Act + const env = createEnv({ packages, typescript }); + + // Assert + expect(env).toEqual( + expect.objectContaining({ + browser: true, + es6: true, + node: true, + }), + ); + }); + + it("returns browser as true if no typescript libs are provided", () => { + // Arrange + const packages = undefined; + const typescript = { + compilerOptions: createTypeScriptCompilerOptions(), + }; + + // Act + const env = createEnv({ packages, typescript }); + + // Assert + expect(env).toEqual( + expect.objectContaining({ + browser: true, + }), + ); + }); + + it("returns browser as false if typescript libs don't include dom", () => { + // Arrange + const packages = undefined; + const typescript = { + compilerOptions: createTypeScriptCompilerOptions({ + lib: ["esnext"], + }), + }; + + // Act + const env = createEnv({ packages, typescript }); + + // Assert + expect(env).not.toContain({ + browser: expect.any(Boolean), + }); + }); + + it("returns browser as true if a typescript lib is dom", () => { + // Arrange + const packages = undefined; + const typescript = { + compilerOptions: createTypeScriptCompilerOptions({ + lib: ["dom"], + }), + }; + + // Act + const env = createEnv({ packages, typescript }); + + // Assert + expect(env).toEqual( + expect.objectContaining({ + browser: true, + }), + ); + }); + + it("returns es6 as false if the typescript target is lower than es6", () => { + // Arrange + const packages = undefined; + const typescript = { + compilerOptions: createTypeScriptCompilerOptions({ + target: "es5", + }), + }; + + // Act + const env = createEnv({ packages, typescript }); + + // Assert + expect(env).not.toEqual( + expect.objectContaining({ + es6: expect.any(Boolean), + }), + ); + }); + + it("returns es6 as true if the typescript target includes es6", () => { + // Arrange + const packages = undefined; + const typescript = { + compilerOptions: createTypeScriptCompilerOptions({ + target: "es2015", + }), + }; + + // Act + const env = createEnv({ packages, typescript }); + + // Assert + expect(env).toEqual( + expect.objectContaining({ + es6: true, + }), + ); + }); + + it("returns node as false if package dependencies and devDependencies don't include @types/node", () => { + // Arrange + const packages = { + dependencies: {}, + devDependencies: { + "@types/other": "1.2.3", + }, + }; + const typescript = undefined; + + // Act + const env = createEnv({ packages, typescript }); + + // Assert + expect(env).not.toEqual( + expect.objectContaining({ + node: expect.any(Boolean), + }), + ); + }); + + it("returns node as true if package dependencies include @types/node", () => { + // Arrange + const packages = { + dependencies: { + "@types/node": "1.2.3", + }, + devDependencies: {}, + }; + const typescript = undefined; + + // Act + const env = createEnv({ packages, typescript }); + + // Assert + expect(env).toEqual( + expect.objectContaining({ + node: true, + }), + ); + }); + + it("returns node as true if package devDependencies include @types/node", () => { + // Arrange + const packages = { + dependencies: {}, + devDependencies: { + "@types/node": "1.2.3", + }, + }; + const typescript = undefined; + + // Act + const env = createEnv({ packages, typescript }); + + // Assert + expect(env).toEqual( + expect.objectContaining({ + node: true, + }), + ); + }); +}); diff --git a/src/creation/eslint/createEnv.ts b/src/creation/eslint/createEnv.ts new file mode 100644 index 000000000..4022ad4c8 --- /dev/null +++ b/src/creation/eslint/createEnv.ts @@ -0,0 +1,27 @@ +import { OriginalConfigurations } from "../../input/findOriginalConfigurations"; + +export const createEnv = ({ + packages, + typescript, +}: Pick) => { + const browser = + typescript === undefined || + typescript.compilerOptions.lib === undefined || + typescript.compilerOptions.lib.includes("dom"); + + const es6 = + typescript === undefined || + !["es3", "es5"].includes(typescript.compilerOptions.target.toLowerCase()); + + const node = + packages === undefined || + [...Object.keys(packages.dependencies), ...Object.keys(packages.devDependencies)].some( + dependency => dependency.toLowerCase() === "@types/node", + ); + + return { + ...(browser && { browser }), + ...(es6 && { es6 }), + ...(node && { node }), + }; +}; diff --git a/src/creation/formatConvertedRules.ts b/src/creation/formatConvertedRules.ts index 8712b2187..88364101c 100644 --- a/src/creation/formatConvertedRules.ts +++ b/src/creation/formatConvertedRules.ts @@ -1,10 +1,10 @@ import { TSLintConfiguration } from "../input/findTSLintConfiguration"; -import { ConfigConversionResults } from "../rules/convertRules"; +import { RuleConversionResults } from "../rules/convertRules"; import { ESLintRuleOptions } from "../rules/types"; import { formatMissingRules } from "./formatMissingRules"; export const formatConvertedRules = ( - conversionResults: ConfigConversionResults, + conversionResults: RuleConversionResults, tslintConfiguration: TSLintConfiguration, ) => { const output: { [i: string]: string | any[] } = {}; diff --git a/src/creation/writeConversionResults.test.ts b/src/creation/writeConversionResults.test.ts index 4e5bfc46a..64deb3596 100644 --- a/src/creation/writeConversionResults.test.ts +++ b/src/creation/writeConversionResults.test.ts @@ -1,9 +1,11 @@ import { createEmptyConversionResults } from "../conversion/conversionResults.stubs"; import { writeConversionResults } from "./writeConversionResults"; -const tslintConfiguration = { - ruleDirectories: [], - rules: {}, +const originalConfigurations = { + tslint: { + ruleDirectories: [], + rules: {}, + }, }; describe("writeConversionResults", () => { @@ -15,13 +17,18 @@ describe("writeConversionResults", () => { const fileSystem = { writeFile: jest.fn().mockReturnValue(Promise.resolve()) }; // Act - await writeConversionResults({ fileSystem }, conversionResults, tslintConfiguration); + await writeConversionResults({ fileSystem }, conversionResults, originalConfigurations); // Assert expect(fileSystem.writeFile).toHaveBeenLastCalledWith( ".eslintrc.json", JSON.stringify( { + env: { + browser: true, + es6: true, + node: true, + }, parser: "@typescript-eslint/parser", parserOptions: { project: "tsconfig.json", @@ -49,13 +56,18 @@ describe("writeConversionResults", () => { const fileSystem = { writeFile: jest.fn().mockReturnValue(Promise.resolve()) }; // Act - await writeConversionResults({ fileSystem }, conversionResults, tslintConfiguration); + await writeConversionResults({ fileSystem }, conversionResults, originalConfigurations); // Assert expect(fileSystem.writeFile).toHaveBeenLastCalledWith( ".eslintrc.json", JSON.stringify( { + env: { + browser: true, + es6: true, + node: true, + }, parser: "@typescript-eslint/parser", parserOptions: { project: "tsconfig.json", diff --git a/src/creation/writeConversionResults.ts b/src/creation/writeConversionResults.ts index 451f57fca..378b680ed 100644 --- a/src/creation/writeConversionResults.ts +++ b/src/creation/writeConversionResults.ts @@ -1,7 +1,8 @@ import { FileSystem } from "../adapters/fileSystem"; -import { TSLintConfiguration } from "../input/findTSLintConfiguration"; -import { ConfigConversionResults } from "../rules/convertRules"; +import { RuleConversionResults } from "../rules/convertRules"; import { formatConvertedRules } from "./formatConvertedRules"; +import { OriginalConfigurations } from "../input/findOriginalConfigurations"; +import { createEnv } from "./eslint/createEnv"; export type WriteConversionResultsDependencies = { fileSystem: Pick; @@ -9,18 +10,19 @@ export type WriteConversionResultsDependencies = { export const writeConversionResults = async ( dependencies: WriteConversionResultsDependencies, - conversionResults: ConfigConversionResults, - tslintConfiguration: TSLintConfiguration, + ruleConversionResults: RuleConversionResults, + originalConfigurations: OriginalConfigurations, ) => { const output = { + env: createEnv(originalConfigurations), parser: "@typescript-eslint/parser", parserOptions: { project: "tsconfig.json", }, - ...(conversionResults.missing.length && { + ...(ruleConversionResults.missing.length && { plugins: ["@typescript-eslint/tslint"], }), - rules: formatConvertedRules(conversionResults, tslintConfiguration), + rules: formatConvertedRules(ruleConversionResults, originalConfigurations.tslint), }; await dependencies.fileSystem.writeFile(".eslintrc.json", JSON.stringify(output, undefined, 4)); diff --git a/src/input/findLintConfiguration.test.ts b/src/input/findConfiguration.test.ts similarity index 55% rename from src/input/findLintConfiguration.test.ts rename to src/input/findConfiguration.test.ts index 131eeaa28..d2a9f214d 100644 --- a/src/input/findLintConfiguration.test.ts +++ b/src/input/findConfiguration.test.ts @@ -1,14 +1,14 @@ import { createStubExec, createStubThrowingExec } from "../adapters/exec.stubs"; -import { findLintConfiguration } from "./findLintConfiguration"; +import { findConfiguration } from "./findConfiguration"; -describe("findLintConfiguration", () => { +describe("findConfiguration", () => { 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 findLintConfiguration(exec, "command", "sample.json", {}); + const result = await findConfiguration(exec, "command", "sample.json"); // Assert expect(result).toEqual(new Error(stderr)); @@ -20,7 +20,7 @@ describe("findLintConfiguration", () => { const exec = createStubThrowingExec({ stderr }); // Act - const result = await findLintConfiguration(exec, "command", "sample.json", {}); + const result = await findConfiguration(exec, "command", "sample.json"); // Assert expect(result).toEqual(new Error(stderr)); @@ -32,7 +32,7 @@ describe("findLintConfiguration", () => { const exec = createStubExec({ stdout }); // Act - const result = await findLintConfiguration(exec, "command", "sample.json", {}); + const result = await findConfiguration(exec, "command", "sample.json"); // Assert expect(result).toEqual( @@ -49,37 +49,11 @@ describe("findLintConfiguration", () => { const exec = createStubExec({ stdout }); // Act - const result = await findLintConfiguration(exec, "command", "sample.json", {}); + const result = await findConfiguration(exec, "command", "sample.json"); // Assert expect(result).toEqual({ rules, }); }); - - it("fills in configuration defaults the command returns valid but empty JSON", async () => { - // Arrange - const defaultConfiguration = { - default: true, - }; - const originalConfiguration = { - original: true, - }; - const stdout = JSON.stringify(originalConfiguration); - const exec = createStubExec({ stdout }); - - // Act - const result = await findLintConfiguration( - exec, - "command", - "sample.json", - defaultConfiguration, - ); - - // Assert - expect(result).toEqual({ - ...defaultConfiguration, - ...originalConfiguration, - }); - }); }); diff --git a/src/input/findLintConfiguration.ts b/src/input/findConfiguration.ts similarity index 66% rename from src/input/findLintConfiguration.ts rename to src/input/findConfiguration.ts index d382cfcae..61260ddef 100644 --- a/src/input/findLintConfiguration.ts +++ b/src/input/findConfiguration.ts @@ -1,22 +1,27 @@ import { Exec } from "../adapters/exec"; -export const findLintConfiguration = async ( +export type DeepPartial = { + [P in keyof T]: T[P] extends {} ? DeepPartial : T[P]; +}; + +export type FindConfigurationDependencies = { + exec: Exec; +}; + +export const findConfiguration = async ( exec: Exec, command: string, config: string, - defaultValues: Configuration, -): Promise => { +): Promise | Error> => { const fullCommand = `${command} ${config}`; const stdout = await execAndCatch(exec, fullCommand); + if (stdout instanceof Error) { return stdout; } try { - return { - ...defaultValues, - ...(JSON.parse(stdout) as Partial), - }; + return JSON.parse(stdout) as DeepPartial; } catch (error) { return new Error(`Error parsing configuration: ${error}`); } diff --git a/src/input/findESLintConfiguration.test.ts b/src/input/findESLintConfiguration.test.ts index b6cd82f57..0615c990d 100644 --- a/src/input/findESLintConfiguration.test.ts +++ b/src/input/findESLintConfiguration.test.ts @@ -1,7 +1,23 @@ import { findESLintConfiguration } from "./findESLintConfiguration"; -import { createStubExec } from "../adapters/exec.stubs"; +import { createStubExec, createStubThrowingExec } from "../adapters/exec.stubs"; describe("findESLintConfiguration", () => { + it("returns an error when one occurs", async () => { + // Arrange + const message = "error"; + const dependencies = { exec: createStubThrowingExec({ stderr: message }) }; + + // Act + const result = await findESLintConfiguration(dependencies, undefined); + + // Assert + expect(result).toEqual( + expect.objectContaining({ + message, + }), + ); + }); + it("defaults the configuration file when one isn't provided", async () => { // Arrange const dependencies = { exec: createStubExec() }; @@ -36,6 +52,9 @@ describe("findESLintConfiguration", () => { const result = await findESLintConfiguration(dependencies, config); // Assert - expect(result).toEqual({}); + expect(result).toEqual({ + env: {}, + rules: {}, + }); }); }); diff --git a/src/input/findESLintConfiguration.ts b/src/input/findESLintConfiguration.ts index e544cfa70..179c82ccb 100644 --- a/src/input/findESLintConfiguration.ts +++ b/src/input/findESLintConfiguration.ts @@ -1,22 +1,33 @@ -import { Exec } from "../adapters/exec"; -import { findLintConfiguration } from "./findLintConfiguration"; +import { findConfiguration, FindConfigurationDependencies } from "./findConfiguration"; -// Soon, this will be filled out with real information... -export type ESLintConfiguration = unknown; - -const defaultESLintConfiguration = {}; +export type ESLintConfiguration = { + env: { + [i: string]: boolean; + }; + rules: { + [i: string]: number | [string, any]; + }; +}; -export type FindESLintConfigurationDependencies = { - exec: Exec; +const defaultESLintConfiguration = { + env: {}, + rules: {}, }; -export const findESLintConfiguration = ( - dependencies: FindESLintConfigurationDependencies, +export const findESLintConfiguration = async ( + dependencies: FindConfigurationDependencies, config: string | undefined, -) => - findLintConfiguration( +): Promise => { + const rawConfiguration = await findConfiguration( dependencies.exec, "eslint --print-config", config || "./eslintrc.js", - defaultESLintConfiguration, ); + + return rawConfiguration instanceof Error + ? rawConfiguration + : { + ...defaultESLintConfiguration, + ...rawConfiguration, + }; +}; diff --git a/src/input/findOriginalConfigurations.test.ts b/src/input/findOriginalConfigurations.test.ts index c18c6a3c1..4ace44007 100644 --- a/src/input/findOriginalConfigurations.test.ts +++ b/src/input/findOriginalConfigurations.test.ts @@ -1,4 +1,7 @@ -import { findOriginalConfigurations } from "./findOriginalConfigurations"; +import { + findOriginalConfigurations, + FindOriginalConfigurationsDependencies, +} from "./findOriginalConfigurations"; import { ResultStatus } from "../types"; const createRawSettings = () => ({ @@ -7,15 +10,34 @@ const createRawSettings = () => ({ typescriptConfig: "", }); +const createDependencies = (overrides: Partial = {}) => ({ + findESLintConfiguration: async () => ({ + env: {}, + rules: {}, + }), + findPackagesConfiguration: async () => ({ + dependencies: {}, + devDependencies: {}, + }), + findTSLintConfiguration: async () => ({ + ruleDirectories: [], + rules: {}, + }), + findTypeScriptConfiguration: async () => ({ + compilerOptions: { + target: "es3", + }, + }), + ...overrides, +}); + describe("findOriginalConfigurations", () => { it("returns an errors when the tslint finder returns an error", async () => { // Arrange const complaint = "Complaint from TSLint"; - const dependencies = { - findESLintConfiguration: async () => ({}), + const dependencies = createDependencies({ findTSLintConfiguration: async () => new Error(complaint), - findTypeScriptConfiguration: async () => ({}), - }; + }); // Act const result = await findOriginalConfigurations(dependencies, createRawSettings()); @@ -29,15 +51,11 @@ describe("findOriginalConfigurations", () => { it("returns only tslint results when the other finders return errors", async () => { // Arrange - const complaints = ["Complaint from ESLint", "Complaint from TypeScript"]; - const dependencies = { - findESLintConfiguration: async () => new Error(complaints[0]), - findTSLintConfiguration: async () => ({ - ruleDirectories: [], - rules: {}, - }), - findTypeScriptConfiguration: async () => new Error(complaints[1]), - }; + const dependencies = createDependencies({ + findESLintConfiguration: async () => new Error("one"), + findPackagesConfiguration: async () => new Error("two"), + findTypeScriptConfiguration: async () => new Error("three"), + }); // Act const result = await findOriginalConfigurations(dependencies, createRawSettings()); @@ -56,14 +74,7 @@ describe("findOriginalConfigurations", () => { it("returns successful results when all finders succeed", async () => { // Arrange - const dependencies = { - findESLintConfiguration: async () => ({}), - findTSLintConfiguration: async () => ({ - ruleDirectories: [], - rules: {}, - }), - findTypeScriptConfiguration: async () => ({}), - }; + const dependencies = createDependencies(); // Act const result = await findOriginalConfigurations(dependencies, createRawSettings()); @@ -71,12 +82,23 @@ describe("findOriginalConfigurations", () => { // Assert expect(result).toEqual({ data: { - eslint: {}, + eslint: { + env: {}, + rules: {}, + }, + packages: { + dependencies: {}, + devDependencies: {}, + }, tslint: { ruleDirectories: [], rules: {}, }, - typescript: {}, + typescript: { + compilerOptions: { + target: "es3", + }, + }, }, status: ResultStatus.Succeeded, }); diff --git a/src/input/findOriginalConfigurations.ts b/src/input/findOriginalConfigurations.ts index e96989123..08a356778 100644 --- a/src/input/findOriginalConfigurations.ts +++ b/src/input/findOriginalConfigurations.ts @@ -1,6 +1,7 @@ import { SansDependencies } from "../binding"; import { ResultStatus, TSLintToESLintSettings, ResultWithDataStatus } from "../types"; import { findESLintConfiguration, ESLintConfiguration } from "./findESLintConfiguration"; +import { PackagesConfiguration, findPackagesConfiguration } from "./findPackagesConfiguration"; import { findTypeScriptConfiguration, TypeScriptConfiguration, @@ -9,12 +10,14 @@ import { findTSLintConfiguration, TSLintConfiguration } from "./findTSLintConfig export type FindOriginalConfigurationsDependencies = { findESLintConfiguration: SansDependencies; + findPackagesConfiguration: SansDependencies; findTypeScriptConfiguration: SansDependencies; findTSLintConfiguration: SansDependencies; }; -export type OriginalConfigurationsData = { +export type OriginalConfigurations = { eslint?: ESLintConfiguration; + packages?: PackagesConfiguration; tslint: TSLintConfiguration; typescript?: TypeScriptConfiguration; }; @@ -22,9 +25,10 @@ export type OriginalConfigurationsData = { export const findOriginalConfigurations = async ( dependencies: FindOriginalConfigurationsDependencies, rawSettings: TSLintToESLintSettings, -): Promise> => { - const [eslint, tslint, typescript] = await Promise.all([ +): Promise> => { + const [eslint, packages, tslint, typescript] = await Promise.all([ dependencies.findESLintConfiguration(rawSettings.eslintConfig), + dependencies.findPackagesConfiguration(rawSettings.packages), dependencies.findTSLintConfiguration(rawSettings.tslintConfig), dependencies.findTypeScriptConfiguration(rawSettings.typescriptConfig), ]); @@ -39,6 +43,7 @@ export const findOriginalConfigurations = async ( return { data: { ...(!(eslint instanceof Error) && { eslint }), + ...(!(packages instanceof Error) && { packages }), tslint, ...(!(typescript instanceof Error) && { typescript }), }, diff --git a/src/input/findPackagesConfiguration.test.ts b/src/input/findPackagesConfiguration.test.ts new file mode 100644 index 000000000..01d23b474 --- /dev/null +++ b/src/input/findPackagesConfiguration.test.ts @@ -0,0 +1,42 @@ +import { findPackagesConfiguration } from "./findPackagesConfiguration"; +import { createStubExec } from "../adapters/exec.stubs"; + +describe("findPackagesConfiguration", () => { + it("defaults the configuration file when one isn't provided", async () => { + // Arrange + const dependencies = { exec: createStubExec() }; + + // Act + await findPackagesConfiguration(dependencies, undefined); + + // Assert + expect(dependencies.exec).toHaveBeenLastCalledWith("cat ./package.json"); + }); + + it("includes a configuration file in the packages command when one is provided", async () => { + // Arrange + const dependencies = { exec: createStubExec() }; + const config = "./custom/package.json"; + + // Act + await findPackagesConfiguration(dependencies, config); + + // Assert + expect(dependencies.exec).toHaveBeenLastCalledWith("cat ./custom/package.json"); + }); + + it("applies packages defaults when none are provided", async () => { + // Arrange + const dependencies = { exec: createStubExec({ stdout: "{}" }) }; + const config = "./package.json"; + + // Act + const result = await findPackagesConfiguration(dependencies, config); + + // Assert + expect(result).toEqual({ + dependencies: {}, + devDependencies: {}, + }); + }); +}); diff --git a/src/input/findPackagesConfiguration.ts b/src/input/findPackagesConfiguration.ts new file mode 100644 index 000000000..54ab88758 --- /dev/null +++ b/src/input/findPackagesConfiguration.ts @@ -0,0 +1,39 @@ +import { findConfiguration, FindConfigurationDependencies } from "./findConfiguration"; + +export type PackagesConfiguration = { + dependencies: { + [i: string]: string; + }; + devDependencies: { + [i: string]: string; + }; +}; + +const defaultPackagesConfiguration = { + dependencies: {}, + devDependencies: {}, +}; + +export const findPackagesConfiguration = async ( + dependencies: FindConfigurationDependencies, + config: string | undefined, +): Promise => { + const rawConfiguration = await findConfiguration( + dependencies.exec, + "cat", + config || "./package.json", + ); + + return rawConfiguration instanceof Error + ? rawConfiguration + : { + dependencies: { + ...rawConfiguration.dependencies, + ...defaultPackagesConfiguration.dependencies, + }, + devDependencies: { + ...rawConfiguration.devDependencies, + ...defaultPackagesConfiguration.devDependencies, + }, + }; +}; diff --git a/src/input/findTSLintConfiguration.ts b/src/input/findTSLintConfiguration.ts index ea0260545..87e92e51c 100644 --- a/src/input/findTSLintConfiguration.ts +++ b/src/input/findTSLintConfiguration.ts @@ -1,5 +1,5 @@ import { Exec } from "../adapters/exec"; -import { findLintConfiguration } from "./findLintConfiguration"; +import { findConfiguration } from "./findConfiguration"; export type TSLintConfiguration = { ruleDirectories: string[]; @@ -19,13 +19,20 @@ export type FindTSLintConfigurationDependencies = { exec: Exec; }; -export const findTSLintConfiguration = ( +export const findTSLintConfiguration = async ( dependencies: FindTSLintConfigurationDependencies, config: string | undefined, -) => - findLintConfiguration( +): Promise => { + const rawConfiguration = await findConfiguration( dependencies.exec, "tslint --print-config", config || "./tslint.json", - defaultTSLintConfiguration, ); + + return rawConfiguration instanceof Error + ? rawConfiguration + : { + ...defaultTSLintConfiguration, + ...rawConfiguration, + }; +}; diff --git a/src/input/findTslintConfiguration.test.ts b/src/input/findTslintConfiguration.test.ts index f2a64cd70..ccccf6165 100644 --- a/src/input/findTslintConfiguration.test.ts +++ b/src/input/findTslintConfiguration.test.ts @@ -1,7 +1,23 @@ import { findTSLintConfiguration } from "./findTSLintConfiguration"; -import { createStubExec } from "../adapters/exec.stubs"; +import { createStubExec, createStubThrowingExec } from "../adapters/exec.stubs"; describe("findTSLintConfiguration", () => { + it("returns an error when one occurs", async () => { + // Arrange + const stderr = "error"; + const dependencies = { exec: createStubThrowingExec({ stderr }) }; + + // Act + const result = await findTSLintConfiguration(dependencies, undefined); + + // Assert + expect(result).toEqual( + expect.objectContaining({ + message: stderr, + }), + ); + }); + it("defaults the configuration file when one isn't provided", async () => { // Arrange const dependencies = { exec: createStubExec() }; diff --git a/src/input/findTypeScriptConfiguration.test.ts b/src/input/findTypeScriptConfiguration.test.ts index 7303f70fb..4604faf10 100644 --- a/src/input/findTypeScriptConfiguration.test.ts +++ b/src/input/findTypeScriptConfiguration.test.ts @@ -1,7 +1,23 @@ import { findTypeScriptConfiguration } from "./findTypeScriptConfiguration"; -import { createStubExec } from "../adapters/exec.stubs"; +import { createStubExec, createStubThrowingExec } from "../adapters/exec.stubs"; describe("findTypeScriptConfiguration", () => { + it("returns an error when one occurs", async () => { + // Arrange + const message = "error"; + const dependencies = { exec: createStubThrowingExec({ stderr: message }) }; + + // Act + const result = await findTypeScriptConfiguration(dependencies, undefined); + + // Assert + expect(result).toEqual( + expect.objectContaining({ + message, + }), + ); + }); + it("defaults the configuration file when one isn't provided", async () => { // Arrange const dependencies = { exec: createStubExec() }; @@ -34,6 +50,10 @@ describe("findTypeScriptConfiguration", () => { const result = await findTypeScriptConfiguration(dependencies, config); // Assert - expect(result).toEqual({}); + expect(result).toEqual({ + compilerOptions: { + target: "es3", + }, + }); }); }); diff --git a/src/input/findTypeScriptConfiguration.ts b/src/input/findTypeScriptConfiguration.ts index 9e15b5382..f0e33ca76 100644 --- a/src/input/findTypeScriptConfiguration.ts +++ b/src/input/findTypeScriptConfiguration.ts @@ -1,22 +1,34 @@ -import { Exec } from "../adapters/exec"; -import { findLintConfiguration } from "./findLintConfiguration"; +import { findConfiguration, FindConfigurationDependencies } from "./findConfiguration"; -// Soon, this will be filled out with real information... -export type TypeScriptConfiguration = unknown; - -const defaultTypeScriptConfiguration = {}; +export type TypeScriptConfiguration = { + compilerOptions: { + lib?: string[]; + target: string; + }; +}; -export type FindTypeScriptConfigurationDependencies = { - exec: Exec; +const defaultTypeScriptConfiguration = { + compilerOptions: { + target: "es3", + }, }; -export const findTypeScriptConfiguration = ( - dependencies: FindTypeScriptConfigurationDependencies, +export const findTypeScriptConfiguration = async ( + dependencies: FindConfigurationDependencies, config: string | undefined, -) => - findLintConfiguration( +): Promise => { + const rawConfiguration = await findConfiguration( dependencies.exec, "cat", config || "./tsconfig.json", - defaultTypeScriptConfiguration, ); + + return rawConfiguration instanceof Error + ? rawConfiguration + : { + compilerOptions: { + ...defaultTypeScriptConfiguration.compilerOptions, + ...rawConfiguration.compilerOptions, + }, + }; +}; diff --git a/src/reporting/reportConversionResults.ts b/src/reporting/reportConversionResults.ts index 62ada890b..dd32b4217 100644 --- a/src/reporting/reportConversionResults.ts +++ b/src/reporting/reportConversionResults.ts @@ -3,7 +3,7 @@ import { EOL } from "os"; import { Logger } from "../adapters/logger"; import { ConversionError } from "../rules/conversionError"; -import { ConfigConversionResults } from "../rules/convertRules"; +import { RuleConversionResults } from "../rules/convertRules"; import { TSLintRuleOptions, ESLintRuleOptions } from "../rules/types"; export type ReportConversionResultsDependencies = { @@ -12,22 +12,22 @@ export type ReportConversionResultsDependencies = { export const reportConversionResults = ( dependencies: ReportConversionResultsDependencies, - conversionResults: ConfigConversionResults, + ruleConversionResults: RuleConversionResults, ) => { - if (conversionResults.converted.size !== 0) { - logSuccessfulConversions(conversionResults.converted, dependencies.logger); + if (ruleConversionResults.converted.size !== 0) { + logSuccessfulConversions(ruleConversionResults.converted, dependencies.logger); } - if (conversionResults.failed.length !== 0) { - logFailedConversions(conversionResults.failed, dependencies.logger); + if (ruleConversionResults.failed.length !== 0) { + logFailedConversions(ruleConversionResults.failed, dependencies.logger); } - if (conversionResults.missing.length !== 0) { - logMissingRules(conversionResults.missing, dependencies.logger); + if (ruleConversionResults.missing.length !== 0) { + logMissingRules(ruleConversionResults.missing, dependencies.logger); } - if (conversionResults.packages.size !== 0) { - logMissingPackages(conversionResults.packages, dependencies.logger); + if (ruleConversionResults.packages.size !== 0) { + logMissingPackages(ruleConversionResults.packages, dependencies.logger); } }; diff --git a/src/rules/convertRules.ts b/src/rules/convertRules.ts index b42bf65f4..0c29e7cbd 100644 --- a/src/rules/convertRules.ts +++ b/src/rules/convertRules.ts @@ -11,7 +11,7 @@ export type ConvertRulesDependencies = { mergers: Map; }; -export type ConfigConversionResults = { +export type RuleConversionResults = { converted: Map; failed: ConversionError[]; missing: TSLintRuleOptions[]; @@ -21,7 +21,7 @@ export type ConfigConversionResults = { export const convertRules = ( dependencies: ConvertRulesDependencies, rawTslintRules: TSLintConfigurationRules, -): ConfigConversionResults => { +): RuleConversionResults => { const converted = new Map(); const failed: ConversionError[] = []; const missing: TSLintRuleOptions[] = []; diff --git a/src/types.ts b/src/types.ts index 4faf571fd..d04b5acd5 100644 --- a/src/types.ts +++ b/src/types.ts @@ -4,6 +4,11 @@ export type TSLintToESLintSettings = { */ eslintConfig?: string; + /** + * Original packages configuration file path, such as `package.json`. + */ + packages?: string; + /** * Original TSLint configuration file path, such as `tslint.json`. */