Skip to content

Commit a2506b2

Browse files
Josh GoldbergKingDarBoja
Josh Goldberg
andauthored
Factored config rule conversions into comment conversions (#712)
Co-authored-by: KingDarBoja <stevenbojato04@gmail.com>
1 parent 56fd915 commit a2506b2

10 files changed

+115
-51
lines changed

docs/Architecture.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ Within `src/conversion/convertLintConfig.ts`, the following steps occur:
1717
- 3a. If no output rules conflict with `eslint-config-prettier`, it's added in
1818
- 3b. Any ESLint rules that are configured the same as an extended preset are trimmed
1919
4. The summarized configuration is written to the output config file
20-
5. Files to transform comments in have source text rewritten using the same rule conversion logic
20+
5. Files to transform comments in have source text rewritten using the cached rule conversion results
2121
6. A summary of the results is printed to the user's console
2222

2323
### Conversion Results
@@ -51,6 +51,17 @@ These are located in `src/rules/mergers/`, and keyed under their names by the ma
5151

5252
For example, `@typescript-eslint/ban-types` spreads both arguments' `types` members into one large `types` object.
5353

54+
## Comment Conversion
55+
56+
Comments are converted after rule conversion by `src/comments/convertComments.ts`.
57+
Source files are parsed into TypeScript files by `src/comments/parseFileComments.ts`, which then extracts their comment nodes.
58+
Those comments are parsed for TSLint rule disable or enable comments.
59+
60+
Comments that match will be rewritten in their their file to their new ESLint rule equivalent in `src/comments/replaceFileComments.ts`, as determined by:
61+
62+
1. First, if the `ruleEquivalents` cache received from configuration convertion has the TSLint rule's ESLint equivalents listed, those are used.
63+
2. Failing that, a comment-specific `ruleCommentsCache` is populated with rules converted ad-hoc with no arguments.
64+
5465
## Editor Configuration Conversion
5566

5667
Editor lint configurations are converted by `src/editorSettings/convertEditorSettings.ts`.

src/comments/convertComments.test.ts

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ describe("convertComments", () => {
1818
const dependencies = createStubDependencies();
1919

2020
// Act
21-
const result = await convertComments(dependencies, undefined);
21+
const result = await convertComments(dependencies, undefined, new Map());
2222

2323
// Assert
2424
expect(result).toEqual({
@@ -35,7 +35,7 @@ describe("convertComments", () => {
3535
});
3636

3737
// Act
38-
const result = await convertComments(dependencies, true);
38+
const result = await convertComments(dependencies, true, new Map());
3939

4040
// Assert
4141
expect(result).toEqual({
@@ -52,7 +52,9 @@ describe("convertComments", () => {
5252
});
5353

5454
// Act
55-
const result = await convertComments(dependencies, ["*.ts"]);
55+
const result = await convertComments(dependencies, true, new Map(), {
56+
include: ["src/*.ts"],
57+
});
5658

5759
// Assert
5860
expect(result).toEqual({
@@ -71,7 +73,7 @@ describe("convertComments", () => {
7173
});
7274

7375
// Act
74-
const result = await convertComments(dependencies, []);
76+
const result = await convertComments(dependencies, [], new Map());
7577

7678
// Assert
7779
expect(result).toEqual({
@@ -91,7 +93,7 @@ describe("convertComments", () => {
9193
});
9294

9395
// Act
94-
const result = await convertComments(dependencies, []);
96+
const result = await convertComments(dependencies, ["*.ts"], new Map());
9597

9698
// Assert
9799
expect(result).toEqual({
@@ -108,7 +110,7 @@ describe("convertComments", () => {
108110
});
109111

110112
// Act
111-
const result = await convertComments(dependencies, ["*.ts"]);
113+
const result = await convertComments(dependencies, ["*.ts"], new Map());
112114

113115
// Assert
114116
expect(result).toEqual({
@@ -122,7 +124,7 @@ describe("convertComments", () => {
122124
const dependencies = createStubDependencies();
123125

124126
// Act
125-
const result = await convertComments(dependencies, ["*.ts"]);
127+
const result = await convertComments(dependencies, ["*.ts"], new Map());
126128

127129
// Assert
128130
expect(result).toEqual({

src/comments/convertComments.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export type ConvertCommentsDependencies = {
1717
export const convertComments = async (
1818
dependencies: ConvertCommentsDependencies,
1919
filePathGlobs: true | string | string[] | undefined,
20+
ruleEquivalents: Map<string, string[]>,
2021
typescriptConfiguration?: TypeScriptConfiguration,
2122
): Promise<ResultWithDataStatus<string[] | undefined>> => {
2223
if (filePathGlobs === undefined) {
@@ -76,11 +77,11 @@ export const convertComments = async (
7677
};
7778
}
7879

79-
const ruleConversionCache = new Map<string, string | undefined>();
80+
const ruleCommentsCache = new Map<string, string[]>();
8081
const fileFailures = (
8182
await Promise.all(
8283
uniqueGlobbedFilePaths.map(async (filePath) =>
83-
dependencies.convertFileComments(filePath, ruleConversionCache),
84+
dependencies.convertFileComments(filePath, ruleCommentsCache, ruleEquivalents),
8485
),
8586
)
8687
).filter(isError);

src/comments/convertFileComments.test.ts

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ describe("convertFileComments", () => {
2626
const dependencies = createStubDependencies(readFileError);
2727

2828
// Act
29-
const result = await convertFileComments(dependencies, stubFileName, new Map());
29+
const result = await convertFileComments(dependencies, stubFileName, new Map(), new Map());
3030

3131
// Assert
3232
expect(result).toBe(readFileError);
@@ -39,7 +39,7 @@ describe("convertFileComments", () => {
3939
`);
4040

4141
// Act
42-
await convertFileComments(dependencies, stubFileName, new Map());
42+
await convertFileComments(dependencies, stubFileName, new Map(), new Map());
4343

4444
// Assert
4545
expect(dependencies.fileSystem.writeFile).not.toHaveBeenCalled();
@@ -70,7 +70,7 @@ export const g = true;
7070
`);
7171

7272
// Act
73-
await convertFileComments(dependencies, stubFileName, new Map());
73+
await convertFileComments(dependencies, stubFileName, new Map(), new Map());
7474

7575
// Assert
7676
expect(dependencies.fileSystem.writeFile).toHaveBeenCalledWith(
@@ -110,7 +110,7 @@ export const b = true;
110110
`);
111111

112112
// Act
113-
await convertFileComments(dependencies, stubFileName, new Map());
113+
await convertFileComments(dependencies, stubFileName, new Map(), new Map());
114114

115115
// Assert
116116
expect(dependencies.fileSystem.writeFile).toHaveBeenCalledWith(
@@ -125,15 +125,45 @@ export const b = true;
125125
);
126126
});
127127

128-
it("re-uses a rule conversion from cache when it was already converted", async () => {
128+
it("re-uses a rule conversion from conversion cache when it was already converted", async () => {
129129
// Arrange
130130
const dependencies = createStubDependencies(`
131131
/* tslint:disable:ts-a */
132132
export const a = true;
133133
`);
134134

135135
// Act
136-
await convertFileComments(dependencies, stubFileName, new Map([["ts-a", "es-cached"]]));
136+
await convertFileComments(
137+
dependencies,
138+
stubFileName,
139+
new Map(),
140+
new Map([["ts-a", ["es-cached"]]]),
141+
);
142+
143+
// Assert
144+
expect(dependencies.fileSystem.writeFile).toHaveBeenCalledWith(
145+
stubFileName,
146+
`
147+
/* eslint-disable es-cached */
148+
export const a = true;
149+
`,
150+
);
151+
});
152+
153+
it("re-uses a rule conversion from comments cache when it was already converted", async () => {
154+
// Arrange
155+
const dependencies = createStubDependencies(`
156+
/* tslint:disable:ts-a */
157+
export const a = true;
158+
`);
159+
160+
// Act
161+
await convertFileComments(
162+
dependencies,
163+
stubFileName,
164+
new Map([["ts-a", ["es-cached"]]]),
165+
new Map(),
166+
);
137167

138168
// Assert
139169
expect(dependencies.fileSystem.writeFile).toHaveBeenCalledWith(
@@ -153,7 +183,7 @@ export const a = true;
153183
`);
154184

155185
// Act
156-
await convertFileComments(dependencies, stubFileName, new Map());
186+
await convertFileComments(dependencies, stubFileName, new Map(), new Map());
157187

158188
// Assert
159189
expect(dependencies.fileSystem.writeFile).toHaveBeenCalledWith(
@@ -173,7 +203,7 @@ export const a = true;
173203
`);
174204

175205
// Act
176-
await convertFileComments(dependencies, stubFileName, new Map());
206+
await convertFileComments(dependencies, stubFileName, new Map(), new Map());
177207

178208
// Assert
179209
expect(dependencies.fileSystem.writeFile).toHaveBeenCalledWith(

src/comments/convertFileComments.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ export type ConvertFileCommentsDependencies = {
1111
export const convertFileComments = async (
1212
dependencies: ConvertFileCommentsDependencies,
1313
filePath: string,
14-
ruleConversionCache: Map<string, string | undefined>,
14+
ruleCommentsCache: Map<string, string[]>,
15+
ruleEquivalents: Map<string, string[]>,
1516
) => {
1617
const fileContent = await dependencies.fileSystem.readFile(filePath);
1718
if (fileContent instanceof Error) {
@@ -23,7 +24,8 @@ export const convertFileComments = async (
2324
fileContent,
2425
comments,
2526
dependencies.converters,
26-
ruleConversionCache,
27+
ruleCommentsCache,
28+
ruleEquivalents,
2729
);
2830

2931
return fileContent === newFileContent

src/comments/replaceFileComments.ts

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,23 +9,31 @@ export const replaceFileComments = (
99
content: string,
1010
comments: FileComment[],
1111
converters: Map<string, RuleConverter>,
12-
ruleConversionCache: Map<string, string | undefined>,
12+
ruleCommentsCache: Map<string, string[]>,
13+
ruleEquivalents: Map<string, string[]>,
1314
) => {
1415
const getNewRuleLists = (ruleName: string) => {
15-
if (ruleConversionCache.has(ruleName)) {
16-
return ruleConversionCache.get(ruleName);
16+
const cached = ruleEquivalents.get(ruleName) ?? ruleCommentsCache.get(ruleName);
17+
if (cached !== undefined) {
18+
return cached;
1719
}
1820

1921
const converter = converters.get(ruleName);
2022
if (converter === undefined) {
21-
ruleConversionCache.set(ruleName, undefined);
23+
ruleCommentsCache.set(ruleName, []);
2224
return undefined;
2325
}
2426

2527
const converted = converter({ ruleArguments: [] });
26-
return converted instanceof ConversionError
27-
? undefined
28-
: converted.rules.map((conversion) => conversion.ruleName).join(", ");
28+
if (converted instanceof ConversionError) {
29+
return undefined;
30+
}
31+
32+
const equivalents = converted.rules.map((conversion) => conversion.ruleName);
33+
34+
ruleCommentsCache.set(ruleName, equivalents);
35+
36+
return equivalents.join(", ");
2937
};
3038

3139
for (const comment of [...comments].reverse()) {

src/conversion/conversionResults.stubs.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ export const createEmptyConversionResults = (
1010
failed: [],
1111
missing: [],
1212
plugins: new Set(),
13+
ruleEquivalents: new Map(),
1314
...overrides,
1415
});
1516

src/conversion/convertLintConfig.test.ts

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { ResultStatus, FailedResult } from "../types";
2+
import { createEmptyConversionResults } from "./conversionResults.stubs";
23
import { convertLintConfig, ConvertLintConfigDependencies } from "./convertLintConfig";
34

45
const stubSettings = {
@@ -7,28 +8,27 @@ const stubSettings = {
78

89
const createStubDependencies = (
910
overrides: Partial<ConvertLintConfigDependencies> = {},
10-
): ConvertLintConfigDependencies => ({
11-
convertComments: jest.fn(),
12-
convertRules: jest.fn(),
13-
findOriginalConfigurations: jest.fn().mockResolvedValue({
14-
data: createStubOriginalConfigurationsData(),
15-
status: ResultStatus.Succeeded,
16-
}),
17-
reportCommentResults: jest.fn(),
18-
reportConversionResults: jest.fn(),
19-
summarizePackageRules: async (_configurations, data) => ({
20-
...data,
21-
converted: new Map(),
22-
extends: [],
23-
extensionRules: new Map(),
24-
failed: [],
25-
missing: [],
26-
plugins: new Set(),
27-
}),
28-
logMissingPackages: jest.fn().mockReturnValue(Promise.resolve()),
29-
writeConversionResults: jest.fn().mockReturnValue(Promise.resolve()),
30-
...overrides,
31-
});
11+
): ConvertLintConfigDependencies => {
12+
const ruleConversionResults = createEmptyConversionResults();
13+
14+
return {
15+
convertComments: jest.fn(),
16+
convertRules: jest.fn().mockReturnValue(ruleConversionResults),
17+
findOriginalConfigurations: jest.fn().mockResolvedValue({
18+
data: createStubOriginalConfigurationsData(),
19+
status: ResultStatus.Succeeded,
20+
}),
21+
reportCommentResults: jest.fn(),
22+
reportConversionResults: jest.fn(),
23+
summarizePackageRules: async (_configurations, data) => ({
24+
...ruleConversionResults,
25+
...data,
26+
}),
27+
logMissingPackages: jest.fn().mockReturnValue(Promise.resolve()),
28+
writeConversionResults: jest.fn().mockReturnValue(Promise.resolve()),
29+
...overrides,
30+
};
31+
};
3232

3333
const createStubOriginalConfigurationsData = () => ({
3434
tslint: {

src/conversion/convertLintConfig.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,10 @@ export const convertLintConfig = async (
6060
};
6161
}
6262

63-
// 5. Files to transform comments in have source text rewritten using the same rule conversion logic
63+
// 5. Files to transform comments in have source text rewritten using the cached rule conversion results
6464
const commentsResult = await dependencies.convertComments(
6565
settings.comments,
66+
ruleConversionResults.ruleEquivalents,
6667
originalConfigurations.data.typescript,
6768
);
6869

src/rules/convertRules.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ export type RuleConversionResults = {
1818
failed: ErrorSummary[];
1919
missing: TSLintRuleOptions[];
2020
plugins: Set<string>;
21+
ruleEquivalents: Map<string, string[]>;
2122
};
2223

2324
export const convertRules = (
@@ -28,6 +29,7 @@ export const convertRules = (
2829
const failed: ConversionError[] = [];
2930
const missing: TSLintRuleOptions[] = [];
3031
const plugins = new Set<string>();
32+
const ruleEquivalents = new Map<string, string[]>();
3133

3234
if (rawTslintRules !== undefined) {
3335
for (const [ruleName, value] of Object.entries(rawTslintRules)) {
@@ -47,7 +49,11 @@ export const convertRules = (
4749
continue;
4850
}
4951

52+
const equivalents = new Set<string>();
53+
5054
for (const changes of conversion.rules) {
55+
equivalents.add(changes.ruleName);
56+
5157
const existingConversion = converted.get(changes.ruleName);
5258
const newConversion = {
5359
...changes,
@@ -82,7 +88,9 @@ export const convertRules = (
8288
plugins.add(newPlugin);
8389
}
8490
}
91+
92+
ruleEquivalents.set(tslintRule.ruleName, Array.from(equivalents));
8593
}
8694
}
87-
return { converted, failed, missing, plugins };
95+
return { converted, failed, missing, plugins, ruleEquivalents };
8896
};

0 commit comments

Comments
 (0)