Skip to content

Commit

Permalink
Allowed custom .js or .json file outputs with --config
Browse files Browse the repository at this point in the history
As before, the output path defaults to `--eslintrc.js`. `--eslint` will default to `--config` which has that default.
The format of the output path depends on its file extension, with JavaScript for `.js` files and JSON otherwise.

The README.md list of flags is getting a little long so I added a quick manual table of contents. Will try to remember to split that into a separate PR...
  • Loading branch information
Josh Goldberg committed Jul 5, 2019
1 parent 00ba52e commit 1ddc3ad
Show file tree
Hide file tree
Showing 15 changed files with 198 additions and 63 deletions.
27 changes: 24 additions & 3 deletions README.md
Expand Up @@ -35,14 +35,35 @@ TSLint rules without ESLint equivalents will be wrapped with [eslint-plugin-tsli

Each of these flags is optional.

#### `eslint`
- **[`config`](#config)**: Path to print the generated ESLint configuration file to.
- **[`eslint`](#eslint)**: Path to an ESLint configuration file to read settings from.
- **[`package`](#package)**: Path to a package.json file to read dependencies from.
- **[`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.

#### `config`

```shell
npx tslint-to-eslint-config --eslint ./path/to/seslintrc.json
npx tslint-to-eslint-config --config .eslintrc.json
```

_Default: `.eslintrc.js`_

Path to print the generated ESLint configuration file to.

The file extension of this path will be used to determine the format of the created file:

- `.js` file paths will be written `module.exports = ...` JavaScript
- Other file paths will default to JSON

#### `eslint`

```shell
npx tslint-to-eslint-config --eslint ./path/to/eslintrc.js
```

_Default: `--config`'s value_

Path to an ESLint configuration file to read settings from.
This isn't yet used for anything, but will eventually inform settings for the generated ESLint configuration file.

Expand Down Expand Up @@ -76,7 +97,7 @@ npx tslint-to-eslint-config --typescript ./path/to/tsconfig.json

_Default: `tsconfig.json`_

Path to a `tsconfig.json` file to read TypeScript compiler options from.
Path to a TypeScript configuration file to read TypeScript compiler options from.
This will help inform the generated ESLint configuration file's [env](https://eslint.org/docs/user-guide/configuring#specifying-parser-options) settings.

## Development
Expand Down
14 changes: 9 additions & 5 deletions src/cli/runCli.ts
Expand Up @@ -19,13 +19,17 @@ export const runCli = async (
): Promise<ResultStatus> => {
const command = new Command()
.usage("[options] <file ...> --language [language]")
.option("--eslint [eslint]", "eslint configuration file to convert")
.option("--package [package]", "package configuration file to convert")
.option("--tslint [tslint]", "tslint configuration file to convert")
.option("--typescript [typescript]", "typescript configuration file to convert")
.option("--config [config]", "eslint configuration file to output to")
.option("--eslint [eslint]", "eslint configuration file to convert using")
.option("--package [package]", "package configuration file to convert using")
.option("--tslint [tslint]", "tslint configuration file to convert using")
.option("--typescript [typescript]", "typescript configuration file to convert using")
.option("-V --version", "output the package version");

const parsedArgv = command.parse(rawArgv) as Partial<TSLintToESLintSettings>;
const parsedArgv = {
config: "./eslintrc.js",
...(command.parse(rawArgv) as Partial<TSLintToESLintSettings>),
};

if ({}.hasOwnProperty.call(parsedArgv, "version")) {
dependencies.logger.stdout.write(`${version}${EOL}`);
Expand Down
8 changes: 6 additions & 2 deletions src/conversion/convertConfig.test.ts
Expand Up @@ -28,7 +28,9 @@ describe("convertConfig", () => {
const dependencies = createStubDependencies({
findOriginalConfigurations: async () => findError,
});
const settings = {};
const settings = {
config: "./eslintrc.js",
};

// Act
const result = await convertConfig(dependencies, settings);
Expand All @@ -46,7 +48,9 @@ describe("convertConfig", () => {
const dependencies = createStubDependencies({
findOriginalConfigurations: async () => findSuccess,
});
const settings = {};
const settings = {
config: "./eslintrc.js",
};

// Act
const result = await convertConfig(dependencies, settings);
Expand Down
6 changes: 5 additions & 1 deletion src/conversion/convertConfig.ts
Expand Up @@ -25,7 +25,11 @@ export const convertConfig = async (
originalConfigurations.data.tslint.rules,
);

await dependencies.writeConversionResults(ruleConversionResults, originalConfigurations.data);
await dependencies.writeConversionResults(
settings.config,
ruleConversionResults,
originalConfigurations.data,
);
dependencies.reportConversionResults(ruleConversionResults);

return {
Expand Down
54 changes: 54 additions & 0 deletions src/creation/formatting/formatOutput.test.ts
@@ -0,0 +1,54 @@
import { formatOutput } from "./formatOutput";
import { EOL } from "os";

describe("formatOutput", () => {
it("formats output as JavaScript for a .js file path", () => {
// Arrange
const outputPath = ".eslintrc.js";
const configuration = { rules: {} };

// Act
const output = formatOutput(outputPath, configuration);

// Assert
expect(output).toBe(
`module.exports = ${JSON.stringify(configuration, undefined, 4)};${EOL}`,
);
});

it("formats output as JSON for a .json file path", () => {
// Arrange
const outputPath = ".eslintrc.json";
const configuration = { rules: {} };

// Act
const output = formatOutput(outputPath, configuration);

// Assert
expect(output).toBe(`${JSON.stringify(configuration, undefined, 4)}${EOL}`);
});

it("formats output as JSON for an unknown dot file path", () => {
// Arrange
const outputPath = ".eslintrc";
const configuration = { rules: {} };

// Act
const output = formatOutput(outputPath, configuration);

// Assert
expect(output).toBe(`${JSON.stringify(configuration, undefined, 4)}${EOL}`);
});

it("formats output as JSON for an unknown raw file path", () => {
// Arrange
const outputPath = "eslintrc";
const configuration = { rules: {} };

// Act
const output = formatOutput(outputPath, configuration);

// Assert
expect(output).toBe(`${JSON.stringify(configuration, undefined, 4)}${EOL}`);
});
});
17 changes: 17 additions & 0 deletions src/creation/formatting/formatOutput.ts
@@ -0,0 +1,17 @@
import { formatJsonOutput } from "./formatters/formatJsonOutput";
import { formatJsOutput } from "./formatters/formatJsOutput";

const formatters = new Map([["js", formatJsOutput]]);

export const formatOutput = (outputPath: string, configuration: unknown): string => {
const customFormatter = formatters.get(getExtension(outputPath));
const formatter = customFormatter === undefined ? formatJsonOutput : formatJsOutput;

return formatter(configuration);
};

const getExtension = (outputPath: string) => {
const periodIndex = outputPath.lastIndexOf(".");

return periodIndex === -1 ? outputPath : outputPath.slice(periodIndex + 1);
};
4 changes: 4 additions & 0 deletions src/creation/formatting/formatters/formatJsOutput.ts
@@ -0,0 +1,4 @@
import { EOL } from "os";

export const formatJsOutput = (configuration: unknown) =>
`module.exports = ${JSON.stringify(configuration, undefined, 4)};${EOL}`;
4 changes: 4 additions & 0 deletions src/creation/formatting/formatters/formatJsonOutput.ts
@@ -0,0 +1,4 @@
import { EOL } from "os";

export const formatJsonOutput = (configuration: unknown) =>
`${JSON.stringify(configuration, undefined, 4)}${EOL}`;
91 changes: 47 additions & 44 deletions src/creation/writeConversionResults.test.ts
@@ -1,5 +1,6 @@
import { createEmptyConversionResults } from "../conversion/conversionResults.stubs";
import { writeConversionResults } from "./writeConversionResults";
import { formatJsonOutput } from "./formatting/formatters/formatJsonOutput";

const originalConfigurations = {
tslint: {
Expand All @@ -17,29 +18,30 @@ describe("writeConversionResults", () => {
const fileSystem = { writeFile: jest.fn().mockReturnValue(Promise.resolve()) };

// Act
await writeConversionResults({ fileSystem }, conversionResults, originalConfigurations);
await writeConversionResults(
{ fileSystem },
".eslintrc.json",
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",
sourceType: "module",
},
plugins: ["@typescript-eslint"],
rules: {},
formatJsonOutput({
env: {
browser: true,
es6: true,
node: true,
},
undefined,
4,
),
parser: "@typescript-eslint/parser",
parserOptions: {
project: "tsconfig.json",
sourceType: "module",
},
plugins: ["@typescript-eslint"],
rules: {},
}),
);
});

Expand All @@ -58,38 +60,39 @@ describe("writeConversionResults", () => {
const fileSystem = { writeFile: jest.fn().mockReturnValue(Promise.resolve()) };

// Act
await writeConversionResults({ fileSystem }, conversionResults, originalConfigurations);
await writeConversionResults(
{ fileSystem },
".eslintrc.json",
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",
sourceType: "module",
},
plugins: ["@typescript-eslint", "@typescript-eslint/tslint"],
rules: {
"@typescript-eslint/tslint/config": [
"error",
{
rules: {
"tslint-rule-one": true,
},
formatJsonOutput({
env: {
browser: true,
es6: true,
node: true,
},
parser: "@typescript-eslint/parser",
parserOptions: {
project: "tsconfig.json",
sourceType: "module",
},
plugins: ["@typescript-eslint", "@typescript-eslint/tslint"],
rules: {
"@typescript-eslint/tslint/config": [
"error",
{
rules: {
"tslint-rule-one": true,
},
],
},
},
],
},
undefined,
4,
),
}),
);
});
});
4 changes: 3 additions & 1 deletion src/creation/writeConversionResults.ts
Expand Up @@ -3,13 +3,15 @@ import { RuleConversionResults } from "../rules/convertRules";
import { formatConvertedRules } from "./formatConvertedRules";
import { OriginalConfigurations } from "../input/findOriginalConfigurations";
import { createEnv } from "./eslint/createEnv";
import { formatOutput } from "./formatting/formatOutput";

export type WriteConversionResultsDependencies = {
fileSystem: Pick<FileSystem, "writeFile">;
};

export const writeConversionResults = async (
dependencies: WriteConversionResultsDependencies,
outputPath: string,
ruleConversionResults: RuleConversionResults,
originalConfigurations: OriginalConfigurations,
) => {
Expand All @@ -29,5 +31,5 @@ export const writeConversionResults = async (
rules: formatConvertedRules(ruleConversionResults, originalConfigurations.tslint),
};

await dependencies.fileSystem.writeFile(".eslintrc.json", JSON.stringify(output, undefined, 4));
await dependencies.fileSystem.writeFile(outputPath, formatOutput(outputPath, output));
};
19 changes: 15 additions & 4 deletions src/input/findESLintConfiguration.test.ts
@@ -1,5 +1,12 @@
import { findESLintConfiguration } from "./findESLintConfiguration";
import { createStubExec, createStubThrowingExec } from "../adapters/exec.stubs";
import { TSLintToESLintSettings } from "../types";

const createStubRawSettings = (overrides: Partial<TSLintToESLintSettings> = {}) => ({
config: "./eslintrc.js",
eslint: undefined,
...overrides,
});

describe("findESLintConfiguration", () => {
it("returns an error when one occurs", async () => {
Expand All @@ -8,7 +15,7 @@ describe("findESLintConfiguration", () => {
const dependencies = { exec: createStubThrowingExec({ stderr: message }) };

// Act
const result = await findESLintConfiguration(dependencies, undefined);
const result = await findESLintConfiguration(dependencies, createStubRawSettings());

// Assert
expect(result).toEqual(
Expand All @@ -23,7 +30,7 @@ describe("findESLintConfiguration", () => {
const dependencies = { exec: createStubExec() };

// Act
await findESLintConfiguration(dependencies, undefined);
await findESLintConfiguration(dependencies, createStubRawSettings());

// Assert
expect(dependencies.exec).toHaveBeenLastCalledWith("eslint --print-config ./eslintrc.js");
Expand All @@ -32,7 +39,9 @@ describe("findESLintConfiguration", () => {
it("includes a configuration file in the ESLint command when one is provided", async () => {
// Arrange
const dependencies = { exec: createStubExec() };
const config = "./custom/eslintrc.js";
const config = createStubRawSettings({
eslint: "./custom/eslintrc.js",
});

// Act
await findESLintConfiguration(dependencies, config);
Expand All @@ -46,7 +55,9 @@ describe("findESLintConfiguration", () => {
it("applies ESLint defaults when none are provided", async () => {
// Arrange
const dependencies = { exec: createStubExec({ stdout: "{}" }) };
const config = "./custom/eslintrc.js";
const config = createStubRawSettings({
eslint: "./custom/eslintrc.js",
});

// Act
const result = await findESLintConfiguration(dependencies, config);
Expand Down

0 comments on commit 1ddc3ad

Please sign in to comment.