Skip to content

Commit 6fa6b6f

Browse files
Res42Josh Goldberg
and
Josh Goldberg
authored
Do not use merger if arguments are the same. (#1092)
* Do not use merger if arguments are the same. Also this adds a new feature: creating a merger is not needed if there are no configuration options for the rule. Removed unneeded mergers (for rules with no configuration options). * Fixes. * Fix docs. * Small docs touchups to match Co-authored-by: Josh Goldberg <[email protected]>
1 parent 1adb098 commit 6fa6b6f

File tree

8 files changed

+129
-69
lines changed

8 files changed

+129
-69
lines changed

Diff for: docs/Architecture/Linters.md

+4
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ Those are run by `src/converters/lintConfigs/rules/convertRules.ts`, which takes
2323
* The output rule name is added to the TSLint rule's equivalency set.
2424
* The TSLint rule's config severity is mapped to its ESLint equivalent.
2525
* If this is the first time the output ESLint rule is seen, it's directly marked as converted.
26+
* Notices are merged and deduplicated.
27+
* If the existing output has the same arguments as the new output, merge lookups are skipped.
2628
* If not, a rule merger is run to combine it with its existing output settings.
2729

2830
### Rule Converters
@@ -52,6 +54,8 @@ These are located in `src/rules/mergers/`, and keyed under their names by the ma
5254

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

57+
> A merger does not need to be created if the rule does not accept any configuration or all converters output exactly the same configuration.
58+
5559
## Package Summaries
5660

5761
ESLint configurations are summarized based on extended ESLint and TSLint presets.

Diff for: src/converters/lintConfigs/rules/convertRules.test.ts

+109-32
Original file line numberDiff line numberDiff line change
@@ -107,41 +107,98 @@ describe("convertRules", () => {
107107
expect(ruleEquivalents).toEqual(new Map([["tslint-rule-a", ["eslint-rule-a"]]]));
108108
});
109109

110-
it("reports a failure when two outputs exist for a converted rule without a merger", () => {
111-
// Arrange
112-
const conversionResult = {
113-
rules: [
114-
{
115-
ruleName: "eslint-rule-a",
116-
},
117-
{
118-
ruleName: "eslint-rule-a",
119-
},
120-
],
121-
};
122-
const { tslintRule, converters, mergers } = setupConversionEnvironment({
123-
conversionResult,
124-
});
125-
126-
// Act
127-
const { failed } = convertRules(
128-
{ ruleConverters: converters, ruleMergers: mergers },
129-
{ [tslintRule.ruleName]: tslintRule },
130-
new Map<string, string[]>(),
131-
);
132-
133-
// Assert
134-
expect(failed).toEqual([ConversionError.forMerger("eslint-rule-a")]);
135-
});
136-
137-
it("merges rule arguments when two outputs exist for a converted rule with a merger", () => {
110+
test.each([[undefined], [[5, "allow-something", { max: 50 }]]])(
111+
"runs without an argument merger when both rules have the same arguments",
112+
(ruleArguments) => {
113+
// Arrange
114+
const conversionResult = {
115+
rules: [
116+
{
117+
ruleArguments,
118+
ruleName: "eslint-rule-a",
119+
},
120+
{
121+
ruleArguments,
122+
ruleName: "eslint-rule-a",
123+
},
124+
],
125+
};
126+
const { tslintRule, converters, mergers } = setupConversionEnvironment({
127+
conversionResult,
128+
});
129+
130+
// Act
131+
const { converted, failed } = convertRules(
132+
{ ruleConverters: converters, ruleMergers: mergers },
133+
{ [tslintRule.ruleName]: tslintRule },
134+
new Map<string, string[]>(),
135+
);
136+
137+
// Assert
138+
expect(converted).toEqual(
139+
new Map([
140+
[
141+
"eslint-rule-a",
142+
{
143+
ruleArguments,
144+
ruleName: "eslint-rule-a",
145+
ruleSeverity: "error",
146+
notices: [],
147+
},
148+
],
149+
]),
150+
);
151+
expect(failed).toEqual([]);
152+
},
153+
);
154+
155+
test.each([
156+
[[[0]], [[1]]],
157+
[[[""]], [["allow-something"]]],
158+
[[[{ max: 0 }]], [[{ max: 50 }]]],
159+
[[[0, "", { max: 0 }]], [[5, "allow-something", { max: 50 }]]],
160+
])(
161+
"reports a failure when two outputs with different arguments exist for a converted rule without a merger",
162+
([existingArguments, newArguments]) => {
163+
// Arrange
164+
const conversionResult = {
165+
rules: [
166+
{
167+
ruleArguments: existingArguments,
168+
ruleName: "eslint-rule-a",
169+
},
170+
{
171+
ruleArguments: newArguments,
172+
ruleName: "eslint-rule-a",
173+
},
174+
],
175+
};
176+
const { tslintRule, converters, mergers } = setupConversionEnvironment({
177+
conversionResult,
178+
});
179+
180+
// Act
181+
const { failed } = convertRules(
182+
{ ruleConverters: converters, ruleMergers: mergers },
183+
{ [tslintRule.ruleName]: tslintRule },
184+
new Map<string, string[]>(),
185+
);
186+
187+
// Assert
188+
expect(failed).toEqual([ConversionError.forMerger("eslint-rule-a")]);
189+
},
190+
);
191+
192+
it("merges rule arguments when two outputs with different arguments exist for a converted rule with a merger", () => {
138193
// Arrange
139194
const conversionResult = {
140195
rules: [
141196
{
197+
ruleArguments: [0],
142198
ruleName: "eslint-rule-a",
143199
},
144200
{
201+
ruleArguments: [1],
145202
ruleName: "eslint-rule-a",
146203
},
147204
],
@@ -201,7 +258,7 @@ describe("convertRules", () => {
201258
);
202259

203260
// Assert
204-
expect(converted).toEqual(
261+
expect([
205262
new Map([
206263
[
207264
"eslint-rule-a",
@@ -213,7 +270,17 @@ describe("convertRules", () => {
213270
},
214271
],
215272
]),
216-
);
273+
new Map([
274+
[
275+
"eslint-rule-a",
276+
{
277+
ruleName: "eslint-rule-a",
278+
ruleSeverity: "error",
279+
notices: ["notice-1", "notice-2"],
280+
},
281+
],
282+
]),
283+
]).toContainEqual(converted);
217284
});
218285

219286
it("merges undefined notices", () => {
@@ -243,7 +310,7 @@ describe("convertRules", () => {
243310
);
244311

245312
// Assert
246-
expect(converted).toEqual(
313+
expect([
247314
new Map([
248315
[
249316
"eslint-rule-a",
@@ -255,7 +322,17 @@ describe("convertRules", () => {
255322
},
256323
],
257324
]),
258-
);
325+
new Map([
326+
[
327+
"eslint-rule-a",
328+
{
329+
ruleName: "eslint-rule-a",
330+
ruleSeverity: "error",
331+
notices: [],
332+
},
333+
],
334+
]),
335+
]).toContainEqual(converted);
259336
});
260337

261338
it("marks a new plugin when a conversion has a new plugin", () => {

Diff for: src/converters/lintConfigs/rules/convertRules.ts

+16-6
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { isEqual } from "lodash";
2+
13
import { ConversionError } from "../../../errors/conversionError";
24
import { ErrorSummary } from "../../../errors/errorSummary";
35
import { TSLintConfigurationRules } from "../../../input/findTSLintConfiguration";
@@ -7,7 +9,7 @@ import { formatRawTslintRule } from "./formats/formatRawTslintRule";
79
import { RuleMerger } from "./ruleMerger";
810
import { RuleConverter } from "./ruleConverter";
911
import { TSLintRuleOptions, ESLintRuleOptions } from "./types";
10-
import { Entries } from "../../../utils";
12+
import { Entries, uniqueFromSources } from "../../../utils";
1113

1214
export type ConvertRulesDependencies = {
1315
ruleConverters: Map<string, RuleConverter>;
@@ -78,21 +80,29 @@ export const convertRules = (
7880
continue;
7981
}
8082

81-
// 4d. If not, a rule merger is run to combine it with its existing output settings.
83+
// 4d. Notices are merged and deduplicated.
84+
existingConversion.notices = uniqueFromSources(
85+
existingConversion.notices,
86+
newConversion.notices,
87+
);
88+
converted.set(changes.ruleName, existingConversion);
89+
90+
// 4e. If the existing output has the same arguments as the new output, merge lookups are skipped.
91+
if (isEqual(existingConversion.ruleArguments, newConversion.ruleArguments)) {
92+
continue;
93+
}
94+
95+
// 4f. If not, a rule merger is run to combine it with its existing output settings.
8296
const merger = dependencies.ruleMergers.get(changes.ruleName);
8397
if (merger === undefined) {
8498
failed.push(ConversionError.forMerger(changes.ruleName));
8599
} else {
86-
const existingNotices = existingConversion.notices ?? [];
87-
const newNotices = newConversion.notices ?? [];
88-
89100
converted.set(changes.ruleName, {
90101
...existingConversion,
91102
ruleArguments: merger(
92103
existingConversion.ruleArguments,
93104
newConversion.ruleArguments,
94105
),
95-
notices: Array.from(new Set([...existingNotices, ...newNotices])),
96106
});
97107
}
98108
}

Diff for: src/converters/lintConfigs/rules/ruleMergers.ts

-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import { mergeBanTypes } from "./ruleMergers/ban-types";
22
import { mergeConsistentTypeAssertions } from "./ruleMergers/consistent-type-assertions";
33
import { mergeIndent } from "./ruleMergers/indent";
44
import { mergeNamingConvention } from "./ruleMergers/naming-convention";
5-
import { mergeNoCaller } from "./ruleMergers/no-caller";
65
import { mergeNoEval } from "./ruleMergers/no-eval";
76
import { mergeNoMemberDelimiterStyle } from "./ruleMergers/member-delimiter-style";
87
import { mergeNoUnnecessaryTypeAssertion } from "./ruleMergers/no-unnecessary-type-assertion";
@@ -16,6 +15,5 @@ export const ruleMergers = new Map([
1615
["@typescript-eslint/naming-convention", mergeNamingConvention],
1716
["@typescript-eslint/no-unnecessary-type-assertion", mergeNoUnnecessaryTypeAssertion],
1817
["@typescript-eslint/triple-slash-reference", mergeTripleSlashReference],
19-
["no-caller", mergeNoCaller],
2018
["no-eval", mergeNoEval],
2119
]);

Diff for: src/converters/lintConfigs/rules/ruleMergers/eslint-plugin-react/jsx-no-bind.ts

-5
This file was deleted.

Diff for: src/converters/lintConfigs/rules/ruleMergers/eslint-plugin-react/tests/jsx-no-bind.test.ts

-9
This file was deleted.

Diff for: src/converters/lintConfigs/rules/ruleMergers/no-caller.ts

-6
This file was deleted.

Diff for: src/converters/lintConfigs/rules/ruleMergers/tests/no-caller.test.ts

-9
This file was deleted.

0 commit comments

Comments
 (0)