Skip to content
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ Each of these flags is optional:
- **[`editor`](#editor)**: Path to an editor configuration file to convert linter settings within.
- **[`eslint`](#eslint)**: Path to an ESLint configuration file to read settings from.
- **[`package`](#package)**: Path to a package.json file to read dependencies from.
- **[`prettier`](#prettier)**: Add `eslint-config-prettier` to the plugins list.
- **[`tslint`](#tslint)**: Path to a TSLint configuration file to read settings from.
- **[`typescript`](#typescript)**: Path to a TypeScript configuration file to read TypeScript compiler options from.

Expand Down Expand Up @@ -97,6 +98,17 @@ _Default: `package.json`_
Path to a `package.json` file to read dependencies from.
This will help inform the generated ESLint configuration file's [env](https://eslint.org/docs/user-guide/configuring#specifying-parser-options) settings.

#### `prettier`

```shell
npx tslint-to-eslint-config --prettier
```

_Default: `false`_

Add [`eslint-config-prettier`](https://github.com/prettier/eslint-config-prettier) to the list of ESLint plugins.
We [highly recommend](./docs/FAQs.md#should-i-use-prettier) you use [Prettier](http://prettier.io) for code formatting.

#### `tslint`

```shell
Expand Down
3 changes: 2 additions & 1 deletion src/cli/runCli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,12 @@ export const runCli = async (
const command = new Command()
.usage("[options] <file ...> --language [language]")
.option("--config [config]", "eslint configuration file to output to")
.option("--editor [editor]", "editor configuration file to convert")
.option("--eslint [eslint]", "eslint configuration file to convert using")
.option("--package [package]", "package configuration file to convert using")
.option("--prettier [prettier]", "add eslint-config-prettier to the plugins list")
.option("--tslint [tslint]", "tslint configuration file to convert using")
.option("--typescript [typescript]", "typescript configuration file to convert using")
.option("--editor [editor]", "editor configuration file to convert")
.option("-V --version", "output the package version");

const parsedArgv = {
Expand Down
3 changes: 3 additions & 0 deletions src/conversion/convertConfig.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@ const createStubDependencies = (
simplifyPackageRules: async (_configurations, data) => ({
...data,
converted: new Map(),
extends: [],
failed: [],
missing: [],
plugins: new Set(),
}),
writeConversionResults: jest.fn().mockReturnValue(Promise.resolve()),
...overrides,
Expand Down
14 changes: 6 additions & 8 deletions src/conversion/convertConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,12 @@ export const convertConfig = async (
);

// 3. ESLint configurations are simplified based on extended ESLint and TSLint presets
const simplifiedConfiguration = {
...ruleConversionResults,
...(await dependencies.simplifyPackageRules(
originalConfigurations.data.eslint,
originalConfigurations.data.tslint,
ruleConversionResults,
)),
};
const simplifiedConfiguration = await dependencies.simplifyPackageRules(
originalConfigurations.data.eslint,
originalConfigurations.data.tslint,
ruleConversionResults,
settings.prettier,
);

// 4. The simplified configuration is written to the output config file
const fileWriteError = await dependencies.writeConversionResults(
Expand Down
35 changes: 30 additions & 5 deletions src/creation/simplification/simplifyPackageRules.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ const createStubTSLintConfiguration = () => ({
});

describe("simplifyPackageRules", () => {
it("returns the conversion results directly when there is no loaded ESLint configuration and no TSLint extensions", async () => {
it("returns equivalent conversion results when there is no loaded ESLint configuration and no TSLint extensions", async () => {
// Arrange
const dependencies = createStubDependencies();
const eslint = undefined;
Expand All @@ -41,10 +41,34 @@ describe("simplifyPackageRules", () => {
);

// Assert
expect(simplifiedResults).toBe(ruleConversionResults);
expect(simplifiedResults).toEqual(ruleConversionResults);
});

it("returns the conversion results directly when there is an empty ESLint configuration and no TSLint extensions", async () => {
it("adds Prettier extensions when the prettier setting is enabled", async () => {
// Arrange
const dependencies = createStubDependencies();
const eslint = undefined;
const tslint = createStubTSLintConfiguration();
const ruleConversionResults = createEmptyConversionResults();

// Act
const simplifiedResults = await simplifyPackageRules(
dependencies,
eslint,
tslint,
ruleConversionResults,
true,
);

// Assert
expect(simplifiedResults).toEqual({
...ruleConversionResults,
converted: undefined,
extends: ["eslint-config-prettier", "eslint-config-prettier/@typescript-eslint"],
});
});

it("returns equivalent conversion results when there is an empty ESLint configuration and no TSLint extensions", async () => {
// Arrange
const dependencies = createStubDependencies();
const eslint = createStubESLintConfiguration([]);
Expand All @@ -60,7 +84,7 @@ describe("simplifyPackageRules", () => {
);

// Assert
expect(simplifiedResults).toBe(ruleConversionResults);
expect(simplifiedResults).toEqual(ruleConversionResults);
});

it("includes deduplicated rules and extension failures when the ESLint configuration extends", async () => {
Expand Down Expand Up @@ -98,8 +122,9 @@ describe("simplifyPackageRules", () => {

// Assert
expect(simplifiedResults).toEqual({
...ruleConversionResults,
converted: deduplicatedRules,
extends: eslintExtends,
extends: [...eslintExtends],
failed: configurationErrors,
});
});
Expand Down
16 changes: 10 additions & 6 deletions src/creation/simplification/simplifyPackageRules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,7 @@ export type SimplifyPackageRulesDependencies = {
retrieveExtendsValues: SansDependencies<typeof retrieveExtendsValues>;
};

export type SimplifiedRuleConversionResults = Pick<
RuleConversionResults,
"converted" | "failed"
> & {
export type SimplifiedResultsConfiguration = RuleConversionResults & {
extends?: string[];
};

Expand All @@ -28,11 +25,17 @@ export const simplifyPackageRules = async (
dependencies: SimplifyPackageRulesDependencies,
eslint: Pick<OriginalConfigurations<ESLintConfiguration>, "full"> | undefined,
tslint: OriginalConfigurations<Pick<TSLintConfiguration, "extends">>,
ruleConversionResults: SimplifiedRuleConversionResults,
): Promise<SimplifiedRuleConversionResults> => {
ruleConversionResults: RuleConversionResults,
usePrettier?: boolean,
): Promise<SimplifiedResultsConfiguration> => {
const extendedESLintRulesets = eslint?.full.extends ?? [];
const extendedTSLintRulesets = collectTSLintRulesets(tslint);
const allExtensions = uniqueFromSources(extendedESLintRulesets, extendedTSLintRulesets);

if (usePrettier) {
allExtensions.push("eslint-config-prettier", "eslint-config-prettier/@typescript-eslint");
}

if (allExtensions.length === 0) {
return ruleConversionResults;
}
Expand All @@ -47,6 +50,7 @@ export const simplifyPackageRules = async (
);

return {
...ruleConversionResults,
converted,
extends: allExtensions,
failed: [...ruleConversionResults.failed, ...configurationErrors],
Expand Down
6 changes: 3 additions & 3 deletions src/creation/writeConversionResults.test.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { createEmptyConversionResults } from "../conversion/conversionResults.stubs";
import { writeConversionResults } from "./writeConversionResults";
import { AllOriginalConfigurations } from "../input/findOriginalConfigurations";
import { formatJsonOutput } from "./formatting/formatters/formatJsonOutput";
import { SimplifiedRuleConversionResults } from "./simplification/simplifyPackageRules";
import { SimplifiedResultsConfiguration } from "./simplification/simplifyPackageRules";
import { writeConversionResults } from "./writeConversionResults";

const createStubOriginalConfigurations = (
overrides: Partial<AllOriginalConfigurations & SimplifiedRuleConversionResults> = {},
overrides: Partial<AllOriginalConfigurations & SimplifiedResultsConfiguration> = {},
) => ({
tslint: {
full: {
Expand Down
5 changes: 2 additions & 3 deletions src/creation/writeConversionResults.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { FileSystem } from "../adapters/fileSystem";
import { RuleConversionResults } from "../rules/convertRules";
import { AllOriginalConfigurations } from "../input/findOriginalConfigurations";
import { createEnv } from "./eslint/createEnv";
import { formatConvertedRules } from "./formatConvertedRules";
import { formatOutput } from "./formatting/formatOutput";
import { SimplifiedRuleConversionResults } from "./simplification/simplifyPackageRules";
import { SimplifiedResultsConfiguration } from "./simplification/simplifyPackageRules";

export type WriteConversionResultsDependencies = {
fileSystem: Pick<FileSystem, "writeFile">;
Expand All @@ -13,7 +12,7 @@ export type WriteConversionResultsDependencies = {
export const writeConversionResults = async (
dependencies: WriteConversionResultsDependencies,
outputPath: string,
ruleConversionResults: RuleConversionResults & SimplifiedRuleConversionResults,
ruleConversionResults: SimplifiedResultsConfiguration,
originalConfigurations: AllOriginalConfigurations,
) => {
const plugins = ["@typescript-eslint"];
Expand Down
6 changes: 3 additions & 3 deletions src/input/findESLintConfiguration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,9 @@ export type FindESLintConfigurationDependencies = {

export const findESLintConfiguration = async (
dependencies: FindESLintConfigurationDependencies,
rawSettings: Pick<TSLintToESLintSettings, "config" | "eslint">,
config: Pick<TSLintToESLintSettings, "config" | "eslint">,
): Promise<OriginalConfigurations<ESLintConfiguration> | Error> => {
const filePath = rawSettings.eslint ?? rawSettings.config;
const filePath = config.eslint ?? config.config;
const [rawConfiguration, reportedConfiguration] = await Promise.all([
findRawConfiguration<ESLintConfiguration>(dependencies.importer, filePath, {
extends: [],
Expand All @@ -67,7 +67,7 @@ export const findESLintConfiguration = async (
full: {
...defaultESLintConfiguration,
...reportedConfiguration,
extends: Array.from(new Set(extensions)),
extends: extensions,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Slight cleanup: uniqueFromSources already deduplicates with Array.from(new Set(.

},
raw: rawConfiguration,
};
Expand Down
12 changes: 6 additions & 6 deletions src/reporting/reportConversionResults.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ describe("reportConversionResults", () => {
logger.stdout.write,
`✨ 1 rule replaced with its ESLint equivalent. ✨`,
``,
`⚡ 3 packages are required for running with ESLint. ⚡`,
`⚡ 3 packages are required for this ESLint configuration. ⚡`,
` yarn add @typescript-eslint/eslint-plugin @typescript-eslint/parser eslint --dev`,
);
});
Expand Down Expand Up @@ -80,7 +80,7 @@ describe("reportConversionResults", () => {
`❗ 1 ESLint rule behaves differently from its TSLint counterpart ❗`,
` Check ${logger.debugFileName} for details.`,
``,
`⚡ 3 packages are required for running with ESLint. ⚡`,
`⚡ 3 packages are required for this ESLint configuration. ⚡`,
` yarn add @typescript-eslint/eslint-plugin @typescript-eslint/parser eslint --dev`,
);
expectEqualWrites(
Expand Down Expand Up @@ -134,7 +134,7 @@ describe("reportConversionResults", () => {
`❗ 2 ESLint rules behave differently from their TSLint counterparts ❗`,
` Check ${logger.debugFileName} for details.`,
``,
`⚡ 3 packages are required for running with ESLint. ⚡`,
`⚡ 3 packages are required for this ESLint configuration. ⚡`,
` yarn add @typescript-eslint/eslint-plugin @typescript-eslint/parser eslint --dev`,
);
expectEqualWrites(
Expand Down Expand Up @@ -223,7 +223,7 @@ describe("reportConversionResults", () => {
` The "@typescript-eslint/tslint/config" section of .eslintrc.js configures eslint-plugin-tslint to run it in TSLint within ESLint.`,
` Check ${logger.debugFileName} for details.`,
``,
`⚡ 4 packages are required for running with ESLint. ⚡`,
`⚡ 4 packages are required for this ESLint configuration. ⚡`,
` yarn add @typescript-eslint/eslint-plugin @typescript-eslint/parser @typescript-eslint/tslint-eslint-plugin eslint --dev`,
);
expectEqualWrites(
Expand Down Expand Up @@ -266,7 +266,7 @@ describe("reportConversionResults", () => {
` The "@typescript-eslint/tslint/config" section of .eslintrc.js configures eslint-plugin-tslint to run them in TSLint within ESLint.`,
` Check ${logger.debugFileName} for details.`,
``,
`⚡ 4 packages are required for running with ESLint. ⚡`,
`⚡ 4 packages are required for this ESLint configuration. ⚡`,
` yarn add @typescript-eslint/eslint-plugin @typescript-eslint/parser @typescript-eslint/tslint-eslint-plugin eslint --dev`,
);
expectEqualWrites(
Expand Down Expand Up @@ -295,7 +295,7 @@ describe("reportConversionResults", () => {
// Assert
expectEqualWrites(
logger.stdout.write,
`⚡ 5 packages are required for running with ESLint. ⚡`,
`⚡ 5 packages are required for this ESLint configuration. ⚡`,
` yarn add @typescript-eslint/eslint-plugin @typescript-eslint/parser eslint plugin-one plugin-two --dev`,
);
});
Expand Down
30 changes: 25 additions & 5 deletions src/reporting/reportConversionResults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { EOL } from "os";

import { Logger } from "../adapters/logger";
import { SansDependencies } from "../binding";
import { RuleConversionResults } from "../rules/convertRules";
import { SimplifiedResultsConfiguration } from "../creation/simplification/simplifyPackageRules";
import { ESLintRuleOptions, TSLintRuleOptions } from "../rules/types";
import { choosePackageManager } from "./packages/choosePackageManager";
import {
Expand All @@ -21,7 +21,7 @@ export type ReportConversionResultsDependencies = {
export const reportConversionResults = async (
dependencies: ReportConversionResultsDependencies,
outputPath: string,
ruleConversionResults: RuleConversionResults,
ruleConversionResults: SimplifiedResultsConfiguration,
) => {
const packageManager = await dependencies.choosePackageManager();

Expand Down Expand Up @@ -49,6 +49,7 @@ export const reportConversionResults = async (
}

logMissingPackages(ruleConversionResults, packageManager, dependencies.logger);
logPrettierExtension(ruleConversionResults, dependencies.logger);
};

type RuleWithNotices = {
Expand All @@ -70,9 +71,9 @@ const logNotices = (converted: Map<string, ESLintRuleOptions>, logger: Logger) =
? " behaves differently from its TSLint counterpart"
: "s behave differently from their TSLint counterparts";

logger.stdout.write(
chalk.blueBright(`${EOL}❗ ${rulesWithNotices.length} ESLint rule${behavior} ❗${EOL}`),
);
logger.stdout.write(chalk.blueBright(`${EOL}❗ ${rulesWithNotices.length}`));
logger.stdout.write(chalk.blue(` ESLint rule${behavior} `));
logger.stdout.write(chalk.blueBright(`❗${EOL}`));
logger.stdout.write(chalk.blue(` Check ${logger.debugFileName} for details.${EOL}`));
logger.info.write(`${rulesWithNotices.length} ESLint rule${behavior}:${EOL}`);

Expand All @@ -86,3 +87,22 @@ const logNotices = (converted: Map<string, ESLintRuleOptions>, logger: Logger) =

logger.info.write(EOL);
};

const logPrettierExtension = (
ruleConversionResults: SimplifiedResultsConfiguration,
logger: Logger,
) => {
if (!ruleConversionResults.extends?.includes("eslint-config-prettier")) {
logger.stdout.write(chalk.redBright(`${EOL}🔥 Prettier`));
logger.stdout.write(chalk.red(` plugins are missing from your configuration. `));
logger.stdout.write(chalk.redBright(`🔥${EOL}`));
logger.stdout.write(chalk.red(` We highly recommend running `));
logger.stdout.write(chalk.redBright(`tslint-to-eslint-config --prettier`));
logger.stdout.write(chalk.red(` to disable formatting ESLint rules.${EOL}`));
logger.stdout.write(
chalk.red(
` See https://github/typescript-eslint/tslint-to-eslint-config/docs/FAQs.md#should-i-use-prettier.${EOL}`,
),
);
}
};
Loading