Skip to content

Allowed passing a tsconfig.json to --comments #713

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Sep 15, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ We **strongly** advise reading [docs/FAQs.md](./docs/FAQs.md) before planning yo

Each of these flags is optional:

- **[`comments`](#comments)**: File glob path(s) to convert TSLint rule flags to ESLint within.
- **[`comments`](#comments)**: TypeScript configuration or file glob path(s) to convert TSLint rule flags to ESLint within.
- **[`config`](#config)**: Path to print the generated ESLint configuration file to.
- **[`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.
Expand All @@ -64,7 +64,13 @@ Comments such as `// tslint:disable: tslint-rule-name` will be converted to equi

If passed without arguments, respects the `excludes`, `files`, and `includes` in your TypeScript configuration.

Alternately, you can specify which files to convert comments in as globs:
If passed a single file path ending with `.json`, that is treated as a TypeScript configuration file describing with files to convert.

```shell
npx tslint-to-eslint-config --comments tsconfig.json
```

If passed any other arguments, those are treated as glob paths for file paths to convert:

```shell
npx tslint-to-eslint-config --comments 'src/**/*.ts'
Expand Down
29 changes: 19 additions & 10 deletions src/cli/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ import { globAsync } from "../adapters/globAsync";
import { nativeImporter } from "../adapters/nativeImporter";
import { processLogger } from "../adapters/processLogger";
import { bind } from "../binding";
import {
collectCommentFileNames,
CollectCommentFileNamesDependencies,
} from "../comments/collectCommentFileNames";
import { convertComments, ConvertCommentsDependencies } from "../comments/convertComments";
import {
ConvertFileCommentsDependencies,
Expand Down Expand Up @@ -72,16 +76,6 @@ import { mergers } from "../rules/mergers";
import { rulesConverters } from "../rules/rulesConverters";
import { runCli, RunCliDependencies } from "./runCli";

const convertFileCommentsDependencies: ConvertFileCommentsDependencies = {
converters: rulesConverters,
fileSystem: fsFileSystem,
};

const convertCommentsDependencies: ConvertCommentsDependencies = {
convertFileComments: bind(convertFileComments, convertFileCommentsDependencies),
globAsync,
};

const convertRulesDependencies: ConvertRulesDependencies = {
converters: rulesConverters,
mergers,
Expand Down Expand Up @@ -117,6 +111,21 @@ const findOriginalConfigurationsDependencies: FindOriginalConfigurationsDependen
mergeLintConfigurations,
};

const convertFileCommentsDependencies: ConvertFileCommentsDependencies = {
converters: rulesConverters,
fileSystem: fsFileSystem,
};

const collectCommentFileNamesDependencies: CollectCommentFileNamesDependencies = {
findTypeScriptConfiguration: bind(findTypeScriptConfiguration, findConfigurationDependencies),
};

const convertCommentsDependencies: ConvertCommentsDependencies = {
convertFileComments: bind(convertFileComments, convertFileCommentsDependencies),
collectCommentFileNames: bind(collectCommentFileNames, collectCommentFileNamesDependencies),
globAsync,
};

const reportCommentResultsDependencies: ReportCommentResultsDependencies = {
logger: processLogger,
};
Expand Down
81 changes: 81 additions & 0 deletions src/comments/collectCommentFileNames.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { collectCommentFileNames } from "./collectCommentFileNames";

describe("collectCommentFileNames", () => {
it("returns an error result when filePathGlobs is true and typescriptConfiguration is undefined", async () => {
const findTypeScriptConfiguration = jest.fn();

const result = await collectCommentFileNames({ findTypeScriptConfiguration }, true);

expect(result).toEqual(expect.any(Error));
});

it("returns the typescript configuration when filePathGlobs is true and typescriptConfiguration exists", async () => {
const findTypeScriptConfiguration = jest.fn();
const typescriptConfiguration = {
include: ["a.ts"],
};

const result = await collectCommentFileNames(
{ findTypeScriptConfiguration },
true,
typescriptConfiguration,
);

expect(result).toEqual(typescriptConfiguration);
});

it("returns the input file paths when filePathGlobs is an array", async () => {
const findTypeScriptConfiguration = jest.fn();
const filePathGlobs = ["a.ts"];

const result = await collectCommentFileNames(
{ findTypeScriptConfiguration },
filePathGlobs,
);

expect(result).toEqual({
include: filePathGlobs,
});
});

it("returns the input file path when filePathGlobs is a source file path string", async () => {
const findTypeScriptConfiguration = jest.fn();
const filePathGlobs = "a.ts";

const result = await collectCommentFileNames(
{ findTypeScriptConfiguration },
filePathGlobs,
);

expect(result).toEqual({
include: [filePathGlobs],
});
});

it("returns the failure when filePathGlobs is a config file path string and reading it fails", async () => {
const error = new Error("Failure!");
const findTypeScriptConfiguration = jest.fn().mockResolvedValue(error);

const result = await collectCommentFileNames(
{ findTypeScriptConfiguration },
"tsconfig.json",
);

expect(result).toEqual(error);
});

it("returns the typescript configuration from disk when filePathGlobs is a config path string and reading it succeeds", async () => {
const findTypeScriptConfiguration = jest.fn().mockResolvedValue({
include: ["a.ts"],
});

const result = await collectCommentFileNames(
{ findTypeScriptConfiguration },
"tsconfig.json",
);

expect(result).toEqual({
include: ["a.ts"],
});
});
});
50 changes: 50 additions & 0 deletions src/comments/collectCommentFileNames.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { SansDependencies } from "../binding";
import {
findTypeScriptConfiguration,
TypeScriptConfiguration,
} from "../input/findTypeScriptConfiguration";
import { uniqueFromSources } from "../utils";

export type CollectCommentFileNamesDependencies = {
findTypeScriptConfiguration: SansDependencies<typeof findTypeScriptConfiguration>;
};

export type CommentFileNames = {
exclude?: string[];
include: string[];
};

export const collectCommentFileNames = async (
dependencies: CollectCommentFileNamesDependencies,
filePathGlobs: true | string | string[],
typescriptConfiguration?: TypeScriptConfiguration,
): Promise<CommentFileNames | Error> => {
if (filePathGlobs === true) {
if (!typescriptConfiguration) {
return new Error(
"--comments indicated to convert files listed in a tsconfig.json, but one was not found on disk or specified by with --typescript.",
);
}

return {
exclude: typescriptConfiguration.exclude,
include: uniqueFromSources(
typescriptConfiguration.files,
typescriptConfiguration.include,
),
};
}

if (typeof filePathGlobs === "string" && filePathGlobs.endsWith(".json")) {
const findResult = await dependencies.findTypeScriptConfiguration(filePathGlobs);
if (findResult instanceof Error) {
return findResult;
}

return await collectCommentFileNames(dependencies, true, findResult);
}

return {
include: uniqueFromSources(filePathGlobs),
};
};
86 changes: 33 additions & 53 deletions src/comments/convertComments.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,16 @@ import { convertComments, ConvertCommentsDependencies } from "./convertComments"
const createStubDependencies = (
overrides: Partial<ConvertCommentsDependencies> = {},
): ConvertCommentsDependencies => ({
collectCommentFileNames: async () => ({
include: ["a.ts"],
}),
convertFileComments: jest.fn(),
globAsync: jest.fn().mockResolvedValue(["src/a.ts", "src/b.ts"]),
globAsync: jest.fn().mockResolvedValue(["a.ts", "b.ts"]),
...overrides,
});

describe("convertComments", () => {
it("returns an empty result when --comments is not provided", async () => {
it("returns an empty result when filePathGlobs is undefined", async () => {
// Arrange
const dependencies = createStubDependencies();

Expand All @@ -24,74 +27,48 @@ describe("convertComments", () => {
});
});

it("returns an error when --comments is given as a boolean value without a TypeScript configuration", async () => {
it("returns the failure result when collectCommentFileNames fails", async () => {
// Arrange
const dependencies = createStubDependencies();
const error = new Error("Failure!");
const dependencies = createStubDependencies({
collectCommentFileNames: async () => error,
});

// Act
const result = await convertComments(dependencies, true);

// Assert
expect(result).toEqual({
errors: expect.arrayContaining([expect.any(Error)]),
errors: [error],
status: ResultStatus.Failed,
});
});

it("includes TypeScript files when --comments is given as a boolean value with a TypeScript files configuration", async () => {
it("returns the failure result when a file path glob fails", async () => {
// Arrange
const globAsyncError = new Error();
const dependencies = createStubDependencies({
globAsync: jest.fn().mockResolvedValue(["src/a.ts"]),
});

// Act
const result = await convertComments(dependencies, true, {
files: ["src/a.ts"],
});

// Assert
expect(result).toEqual({
data: ["src/a.ts"],
status: ResultStatus.Succeeded,
globAsync: jest.fn().mockResolvedValueOnce(globAsyncError),
});
});

it("includes TypeScript inclusions when --comments is given as a boolean value with a TypeScript include configuration", async () => {
// Arrange
const dependencies = createStubDependencies();

// Act
const result = await convertComments(dependencies, true, {
include: ["src/*.ts"],
});
const result = await convertComments(dependencies, ["*.ts"]);

// Assert
expect(result).toEqual({
data: ["src/a.ts", "src/b.ts"],
status: ResultStatus.Succeeded,
errors: [globAsyncError],
status: ResultStatus.Failed,
});
});

it("excludes TypeScript exclusions when --comments is given as a boolean value with a TypeScript excludes configuration", async () => {
it("returns an error when there are no resultant file paths", async () => {
// Arrange
const dependencies = createStubDependencies();

// Act
const result = await convertComments(dependencies, true, {
exclude: ["src/b.ts"],
include: ["src/*.ts"],
});

// Assert
expect(result).toEqual({
data: ["src/a.ts"],
status: ResultStatus.Succeeded,
const dependencies = createStubDependencies({
collectCommentFileNames: async () => ({
include: [],
}),
globAsync: jest.fn().mockResolvedValueOnce([]),
});
});

it("returns an error when there are no file path globs", async () => {
// Arrange
const dependencies = createStubDependencies();

// Act
const result = await convertComments(dependencies, []);
Expand All @@ -103,26 +80,29 @@ describe("convertComments", () => {
});
});

it("returns the failure result when a file path glob fails", async () => {
it("returns an error when there all globbed file paths are excluded", async () => {
// Arrange
const globAsyncError = new Error();
const dependencies = createStubDependencies({
globAsync: jest.fn().mockResolvedValueOnce(globAsyncError),
collectCommentFileNames: async () => ({
exclude: ["*.ts"],
include: ["a.ts"],
}),
globAsync: jest.fn().mockResolvedValueOnce(["a.ts"]),
});

// Act
const result = await convertComments(dependencies, ["*.ts"]);
const result = await convertComments(dependencies, []);

// Assert
expect(result).toEqual({
errors: [globAsyncError],
errors: expect.arrayContaining([expect.any(Error)]),
status: ResultStatus.Failed,
});
});

it("returns the failure result when a file conversion fails", async () => {
// Arrange
const fileConversionError = new Error();
const fileConversionError = new Error("Failure!");
const dependencies = createStubDependencies({
convertFileComments: jest.fn().mockResolvedValueOnce(fileConversionError),
});
Expand All @@ -146,7 +126,7 @@ describe("convertComments", () => {

// Assert
expect(result).toEqual({
data: ["src/a.ts", "src/b.ts"],
data: ["a.ts", "b.ts"],
status: ResultStatus.Succeeded,
});
});
Expand Down
Loading