Skip to content

Commit 0d2a9ea

Browse files
author
Josh Goldberg
authored
Allowed passing a tsconfig.json to --comments (#713)
* Allowed passing a tsconfig.json to --comments * Added mention in README.md
1 parent 7ecec27 commit 0d2a9ea

File tree

6 files changed

+227
-98
lines changed

6 files changed

+227
-98
lines changed

README.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ We **strongly** advise reading [docs/FAQs.md](./docs/FAQs.md) before planning yo
4242

4343
Each of these flags is optional:
4444

45-
- **[`comments`](#comments)**: File glob path(s) to convert TSLint rule flags to ESLint within.
45+
- **[`comments`](#comments)**: TypeScript configuration or file glob path(s) to convert TSLint rule flags to ESLint within.
4646
- **[`config`](#config)**: Path to print the generated ESLint configuration file to.
4747
- **[`editor`](#editor)**: Path to an editor configuration file to convert linter settings within.
4848
- **[`eslint`](#eslint)**: Path to an ESLint configuration file to read settings from.
@@ -64,7 +64,13 @@ Comments such as `// tslint:disable: tslint-rule-name` will be converted to equi
6464

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

67-
Alternately, you can specify which files to convert comments in as globs:
67+
If passed a single file path ending with `.json`, that is treated as a TypeScript configuration file describing with files to convert.
68+
69+
```shell
70+
npx tslint-to-eslint-config --comments tsconfig.json
71+
```
72+
73+
If passed any other arguments, those are treated as glob paths for file paths to convert:
6874

6975
```shell
7076
npx tslint-to-eslint-config --comments 'src/**/*.ts'

src/cli/main.ts

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ import { globAsync } from "../adapters/globAsync";
66
import { nativeImporter } from "../adapters/nativeImporter";
77
import { processLogger } from "../adapters/processLogger";
88
import { bind } from "../binding";
9+
import {
10+
collectCommentFileNames,
11+
CollectCommentFileNamesDependencies,
12+
} from "../comments/collectCommentFileNames";
913
import { convertComments, ConvertCommentsDependencies } from "../comments/convertComments";
1014
import {
1115
ConvertFileCommentsDependencies,
@@ -72,16 +76,6 @@ import { mergers } from "../rules/mergers";
7276
import { rulesConverters } from "../rules/rulesConverters";
7377
import { runCli, RunCliDependencies } from "./runCli";
7478

75-
const convertFileCommentsDependencies: ConvertFileCommentsDependencies = {
76-
converters: rulesConverters,
77-
fileSystem: fsFileSystem,
78-
};
79-
80-
const convertCommentsDependencies: ConvertCommentsDependencies = {
81-
convertFileComments: bind(convertFileComments, convertFileCommentsDependencies),
82-
globAsync,
83-
};
84-
8579
const convertRulesDependencies: ConvertRulesDependencies = {
8680
converters: rulesConverters,
8781
mergers,
@@ -117,6 +111,21 @@ const findOriginalConfigurationsDependencies: FindOriginalConfigurationsDependen
117111
mergeLintConfigurations,
118112
};
119113

114+
const convertFileCommentsDependencies: ConvertFileCommentsDependencies = {
115+
converters: rulesConverters,
116+
fileSystem: fsFileSystem,
117+
};
118+
119+
const collectCommentFileNamesDependencies: CollectCommentFileNamesDependencies = {
120+
findTypeScriptConfiguration: bind(findTypeScriptConfiguration, findConfigurationDependencies),
121+
};
122+
123+
const convertCommentsDependencies: ConvertCommentsDependencies = {
124+
convertFileComments: bind(convertFileComments, convertFileCommentsDependencies),
125+
collectCommentFileNames: bind(collectCommentFileNames, collectCommentFileNamesDependencies),
126+
globAsync,
127+
};
128+
120129
const reportCommentResultsDependencies: ReportCommentResultsDependencies = {
121130
logger: processLogger,
122131
};
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import { collectCommentFileNames } from "./collectCommentFileNames";
2+
3+
describe("collectCommentFileNames", () => {
4+
it("returns an error result when filePathGlobs is true and typescriptConfiguration is undefined", async () => {
5+
const findTypeScriptConfiguration = jest.fn();
6+
7+
const result = await collectCommentFileNames({ findTypeScriptConfiguration }, true);
8+
9+
expect(result).toEqual(expect.any(Error));
10+
});
11+
12+
it("returns the typescript configuration when filePathGlobs is true and typescriptConfiguration exists", async () => {
13+
const findTypeScriptConfiguration = jest.fn();
14+
const typescriptConfiguration = {
15+
include: ["a.ts"],
16+
};
17+
18+
const result = await collectCommentFileNames(
19+
{ findTypeScriptConfiguration },
20+
true,
21+
typescriptConfiguration,
22+
);
23+
24+
expect(result).toEqual(typescriptConfiguration);
25+
});
26+
27+
it("returns the input file paths when filePathGlobs is an array", async () => {
28+
const findTypeScriptConfiguration = jest.fn();
29+
const filePathGlobs = ["a.ts"];
30+
31+
const result = await collectCommentFileNames(
32+
{ findTypeScriptConfiguration },
33+
filePathGlobs,
34+
);
35+
36+
expect(result).toEqual({
37+
include: filePathGlobs,
38+
});
39+
});
40+
41+
it("returns the input file path when filePathGlobs is a source file path string", async () => {
42+
const findTypeScriptConfiguration = jest.fn();
43+
const filePathGlobs = "a.ts";
44+
45+
const result = await collectCommentFileNames(
46+
{ findTypeScriptConfiguration },
47+
filePathGlobs,
48+
);
49+
50+
expect(result).toEqual({
51+
include: [filePathGlobs],
52+
});
53+
});
54+
55+
it("returns the failure when filePathGlobs is a config file path string and reading it fails", async () => {
56+
const error = new Error("Failure!");
57+
const findTypeScriptConfiguration = jest.fn().mockResolvedValue(error);
58+
59+
const result = await collectCommentFileNames(
60+
{ findTypeScriptConfiguration },
61+
"tsconfig.json",
62+
);
63+
64+
expect(result).toEqual(error);
65+
});
66+
67+
it("returns the typescript configuration from disk when filePathGlobs is a config path string and reading it succeeds", async () => {
68+
const findTypeScriptConfiguration = jest.fn().mockResolvedValue({
69+
include: ["a.ts"],
70+
});
71+
72+
const result = await collectCommentFileNames(
73+
{ findTypeScriptConfiguration },
74+
"tsconfig.json",
75+
);
76+
77+
expect(result).toEqual({
78+
include: ["a.ts"],
79+
});
80+
});
81+
});
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { SansDependencies } from "../binding";
2+
import {
3+
findTypeScriptConfiguration,
4+
TypeScriptConfiguration,
5+
} from "../input/findTypeScriptConfiguration";
6+
import { uniqueFromSources } from "../utils";
7+
8+
export type CollectCommentFileNamesDependencies = {
9+
findTypeScriptConfiguration: SansDependencies<typeof findTypeScriptConfiguration>;
10+
};
11+
12+
export type CommentFileNames = {
13+
exclude?: string[];
14+
include: string[];
15+
};
16+
17+
export const collectCommentFileNames = async (
18+
dependencies: CollectCommentFileNamesDependencies,
19+
filePathGlobs: true | string | string[],
20+
typescriptConfiguration?: TypeScriptConfiguration,
21+
): Promise<CommentFileNames | Error> => {
22+
if (filePathGlobs === true) {
23+
if (!typescriptConfiguration) {
24+
return new Error(
25+
"--comments indicated to convert files listed in a tsconfig.json, but one was not found on disk or specified by with --typescript.",
26+
);
27+
}
28+
29+
return {
30+
exclude: typescriptConfiguration.exclude,
31+
include: uniqueFromSources(
32+
typescriptConfiguration.files,
33+
typescriptConfiguration.include,
34+
),
35+
};
36+
}
37+
38+
if (typeof filePathGlobs === "string" && filePathGlobs.endsWith(".json")) {
39+
const findResult = await dependencies.findTypeScriptConfiguration(filePathGlobs);
40+
if (findResult instanceof Error) {
41+
return findResult;
42+
}
43+
44+
return await collectCommentFileNames(dependencies, true, findResult);
45+
}
46+
47+
return {
48+
include: uniqueFromSources(filePathGlobs),
49+
};
50+
};

src/comments/convertComments.test.ts

Lines changed: 33 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,16 @@ import { convertComments, ConvertCommentsDependencies } from "./convertComments"
44
const createStubDependencies = (
55
overrides: Partial<ConvertCommentsDependencies> = {},
66
): ConvertCommentsDependencies => ({
7+
collectCommentFileNames: async () => ({
8+
include: ["a.ts"],
9+
}),
710
convertFileComments: jest.fn(),
8-
globAsync: jest.fn().mockResolvedValue(["src/a.ts", "src/b.ts"]),
11+
globAsync: jest.fn().mockResolvedValue(["a.ts", "b.ts"]),
912
...overrides,
1013
});
1114

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

@@ -24,74 +27,48 @@ describe("convertComments", () => {
2427
});
2528
});
2629

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

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

3440
// Assert
3541
expect(result).toEqual({
36-
errors: expect.arrayContaining([expect.any(Error)]),
42+
errors: [error],
3743
status: ResultStatus.Failed,
3844
});
3945
});
4046

41-
it("includes TypeScript files when --comments is given as a boolean value with a TypeScript files configuration", async () => {
47+
it("returns the failure result when a file path glob fails", async () => {
4248
// Arrange
49+
const globAsyncError = new Error();
4350
const dependencies = createStubDependencies({
44-
globAsync: jest.fn().mockResolvedValue(["src/a.ts"]),
45-
});
46-
47-
// Act
48-
const result = await convertComments(dependencies, true, {
49-
files: ["src/a.ts"],
50-
});
51-
52-
// Assert
53-
expect(result).toEqual({
54-
data: ["src/a.ts"],
55-
status: ResultStatus.Succeeded,
51+
globAsync: jest.fn().mockResolvedValueOnce(globAsyncError),
5652
});
57-
});
58-
59-
it("includes TypeScript inclusions when --comments is given as a boolean value with a TypeScript include configuration", async () => {
60-
// Arrange
61-
const dependencies = createStubDependencies();
6253

6354
// Act
64-
const result = await convertComments(dependencies, true, {
65-
include: ["src/*.ts"],
66-
});
55+
const result = await convertComments(dependencies, ["*.ts"]);
6756

6857
// Assert
6958
expect(result).toEqual({
70-
data: ["src/a.ts", "src/b.ts"],
71-
status: ResultStatus.Succeeded,
59+
errors: [globAsyncError],
60+
status: ResultStatus.Failed,
7261
});
7362
});
7463

75-
it("excludes TypeScript exclusions when --comments is given as a boolean value with a TypeScript excludes configuration", async () => {
64+
it("returns an error when there are no resultant file paths", async () => {
7665
// Arrange
77-
const dependencies = createStubDependencies();
78-
79-
// Act
80-
const result = await convertComments(dependencies, true, {
81-
exclude: ["src/b.ts"],
82-
include: ["src/*.ts"],
83-
});
84-
85-
// Assert
86-
expect(result).toEqual({
87-
data: ["src/a.ts"],
88-
status: ResultStatus.Succeeded,
66+
const dependencies = createStubDependencies({
67+
collectCommentFileNames: async () => ({
68+
include: [],
69+
}),
70+
globAsync: jest.fn().mockResolvedValueOnce([]),
8971
});
90-
});
91-
92-
it("returns an error when there are no file path globs", async () => {
93-
// Arrange
94-
const dependencies = createStubDependencies();
9572

9673
// Act
9774
const result = await convertComments(dependencies, []);
@@ -103,26 +80,29 @@ describe("convertComments", () => {
10380
});
10481
});
10582

106-
it("returns the failure result when a file path glob fails", async () => {
83+
it("returns an error when there all globbed file paths are excluded", async () => {
10784
// Arrange
108-
const globAsyncError = new Error();
10985
const dependencies = createStubDependencies({
110-
globAsync: jest.fn().mockResolvedValueOnce(globAsyncError),
86+
collectCommentFileNames: async () => ({
87+
exclude: ["*.ts"],
88+
include: ["a.ts"],
89+
}),
90+
globAsync: jest.fn().mockResolvedValueOnce(["a.ts"]),
11191
});
11292

11393
// Act
114-
const result = await convertComments(dependencies, ["*.ts"]);
94+
const result = await convertComments(dependencies, []);
11595

11696
// Assert
11797
expect(result).toEqual({
118-
errors: [globAsyncError],
98+
errors: expect.arrayContaining([expect.any(Error)]),
11999
status: ResultStatus.Failed,
120100
});
121101
});
122102

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

147127
// Assert
148128
expect(result).toEqual({
149-
data: ["src/a.ts", "src/b.ts"],
129+
data: ["a.ts", "b.ts"],
150130
status: ResultStatus.Succeeded,
151131
});
152132
});

0 commit comments

Comments
 (0)