Skip to content

Commit 84b22ce

Browse files
author
Josh Goldberg
authored
Added obsolete rule support to convertRules.ts (#1148)
1 parent 037a446 commit 84b22ce

21 files changed

+203
-33
lines changed

docs/Architecture/Linters.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ Those are run by `src/converters/lintConfigs/rules/convertRules.ts`, which takes
1818

1919
1. The raw TSLint rule is converted to a standardized format.
2020
2. The appropriate converter is run for the rule.
21-
3. If the rule is missing or the conversion failed, this is marked.
21+
3. If the rule is missing or obsolete, or the conversion failed, this is marked.
2222
4. For each output rule equivalent given by the conversion:
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.
@@ -34,7 +34,7 @@ Each TSLint rule should output at least one ESLint rule as the equivalent.
3434

3535
Each converter for a TSLint rule takes an arguments object for the rule, and returns an array of objects containing:
3636

37-
- `rules`: At least one equivalent ESLint rule and options
37+
- `rules`: At least one equivalent ESLint rule and options, _or_ none if obsolete
3838
- `notices`: Any extra info that should be printed after conversion
3939
- `plugins`: Any plugins that should now be installed if not already
4040

docs/Creating a Rule Converter.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,9 @@ If the lint rule includes arguments, add the `--sameArguments` flag above to hav
1414
```shell
1515
node ./script/newConverter --eslint output-name --tslint input-name --sameArguments
1616
```
17+
18+
If the original TSLint rule is obsolete and does not have an ESLint equivalent, you can omit `--eslint`:
19+
20+
```shell
21+
node ./script/newConverter --tslint input-name
22+
```

script/newConverter/index.js

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,20 @@ const { writeConverterTest } = require("./writeConverterTest");
1313

1414
const args = command.parse(process.argv).opts();
1515

16-
for (const arg of ["eslint", "tslint"]) {
17-
if (!args[arg]) {
18-
throw new Error(`Missing --${arg} option.`);
19-
}
16+
if (!args.tslint) {
17+
throw new Error(`Missing --tslint option.`);
18+
}
19+
20+
if (args.sameArguments && !args.eslint) {
21+
throw new Error(`Cannot use --sameArguments without --eslint.`);
2022
}
2123

2224
const tslintPascalCase = upperFirst(camelCase(args.tslint)).replace("A11Y", "A11y");
23-
const plugins = args.eslint.includes("/")
24-
? `
25+
const plugins =
26+
args.eslint && args.eslint.includes("/")
27+
? `
2528
plugins: ["${args.eslint.split("/")[0]}"],`
26-
: "";
29+
: "";
2730

2831
await rewriteConvertersMap({ args, tslintPascalCase });
2932
await writeConverter({ args, plugins, tslintPascalCase });

script/newConverter/writeConverter.js

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,23 @@ module.exports.writeConverter = async ({ args, plugins, tslintPascalCase }) => {
1111
]
1212
: ["", ""];
1313

14+
const body = args.eslint
15+
? `{${plugins}
16+
rules: [
17+
{${ruleArguments}
18+
ruleName: "${args.eslint}",
19+
},
20+
],
21+
}`
22+
: `({})`;
23+
1424
await fs.writeFile(
1525
`./src/converters/lintConfigs/rules/ruleConverters/${args.tslint}.ts`,
1626
`
17-
import { RuleConverter } from "../ruleConverter";
27+
import { RuleConverter } from "../ruleConverter";
1828
1929
export const convert${tslintPascalCase}: RuleConverter = (${functionArguments}) => {
20-
return {${plugins}
21-
rules: [
22-
{${ruleArguments}
23-
ruleName: "${args.eslint}",
24-
},
25-
],
26-
};
30+
return ${body};
2731
};
2832
`.trimLeft(),
2933
);

script/newConverter/writeConverterTest.js

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,16 @@ module.exports.writeConverterTest = async ({ args, tslintPascalCase, plugins })
2121
`
2222
: "";
2323

24+
const body = args.eslint
25+
? `
26+
rules: [
27+
{
28+
ruleName: "${args.eslint}",
29+
},
30+
],
31+
`
32+
: "";
33+
2434
await fs.writeFile(
2535
`./src/converters/lintConfigs/rules/ruleConverters/tests/${args.tslint}.test.ts`,
2636
`
@@ -32,13 +42,7 @@ describe(convert${tslintPascalCase}, () => {
3242
ruleArguments: [],
3343
});
3444
35-
expect(result).toEqual({${plugins.replace("\n", "\n ")}
36-
rules: [
37-
{
38-
ruleName: "${args.eslint}",
39-
},
40-
],
41-
});
45+
expect(result).toEqual({${plugins.replace("\n", "\n ")}${body}});
4246
});${ruleArgumentsTest}
4347
});
4448
`.trimLeft(),

src/converters/comments/convertFileComments.test.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,40 @@ describe("convertFileComments", () => {
5555
expect(dependencies.fileSystem.writeFile).not.toHaveBeenCalled();
5656
});
5757

58+
it("ignores comment contents when an input rule is obsolete", async () => {
59+
// Arrange
60+
const dependencies = {
61+
...createStubDependencies(`
62+
// tslint:disable
63+
export const a = true;
64+
65+
// tslint:disable:obsolete
66+
export const b = true;
67+
`),
68+
converters: new Map([["obsolete", () => ({})]]),
69+
};
70+
71+
// Act
72+
await convertFileComments(
73+
dependencies,
74+
stubFileName,
75+
new Map<string, string[]>(),
76+
new Map<string, string[]>(),
77+
);
78+
79+
// Assert
80+
expect(dependencies.fileSystem.writeFile).toHaveBeenCalledWith(
81+
stubFileName,
82+
`
83+
/* eslint-disable */
84+
export const a = true;
85+
86+
/* eslint-disable */
87+
export const b = true;
88+
`,
89+
);
90+
});
91+
5892
it("parses TSLint directives to their matching ESLint directives", async () => {
5993
// Arrange
6094
const dependencies = createStubDependencies(`

src/converters/comments/replaceFileComments.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ export const replaceFileComments = (
2929
return undefined;
3030
}
3131

32-
const equivalents = converted.rules.map((conversion) => conversion.ruleName);
32+
const equivalents = converted.rules?.map((conversion) => conversion.ruleName) ?? [];
3333

3434
ruleCommentsCache.set(ruleName, equivalents);
3535

src/converters/lintConfigs/configConversionResults.stubs.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ export const createEmptyConfigConversionResults = (
88
extensionRules: new Map(),
99
failed: [],
1010
missing: [],
11+
obsolete: new Set(),
1112
plugins: new Set(),
1213
ruleEquivalents: new Map(),
1314
...overrides,

src/converters/lintConfigs/convertLintConfig.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ export type ConvertLintConfigDependencies = {
1717

1818
/**
1919
* Root-level driver to convert a TSLint configuration to ESLint.
20-
* @see `/docs/Architecture/Linting.md` for documentation.
20+
* @see `/docs/Architecture/Linters.md` for documentation.
2121
*/
2222
export const convertLintConfig = async (
2323
dependencies: ConvertLintConfigDependencies,

src/converters/lintConfigs/reporting/reportConfigConversionResults.test.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,44 @@ describe("reportConfigConversionResults", () => {
225225
);
226226
});
227227

228+
it("logs obsolete conversions when there is one obsolete conversion", async () => {
229+
// Arrange
230+
const logger = createStubLogger();
231+
const conversionResults = createEmptyConfigConversionResults({
232+
extends: basicExtends,
233+
obsolete: new Set(["obsolete"]),
234+
});
235+
236+
// Act
237+
await reportConfigConversionResults({ logger }, ".eslintrc.js", conversionResults);
238+
239+
// Assert
240+
expectEqualWrites(
241+
logger.stdout.write,
242+
`🦖 1 rule is obsolete and does not have an ESLint equivalent. 🦖`,
243+
` Check ${logger.debugFileName} for details.`,
244+
);
245+
});
246+
247+
it("logs obsolete conversions when there are multiple obsolete conversions", async () => {
248+
// Arrange
249+
const logger = createStubLogger();
250+
const conversionResults = createEmptyConfigConversionResults({
251+
extends: basicExtends,
252+
obsolete: new Set(["obsolete-a", "obsolete-b"]),
253+
});
254+
255+
// Act
256+
await reportConfigConversionResults({ logger }, ".eslintrc.js", conversionResults);
257+
258+
// Assert
259+
expectEqualWrites(
260+
logger.stdout.write,
261+
`🦖 2 rules are obsolete and do not have ESLint equivalents. 🦖`,
262+
` Check ${logger.debugFileName} for details.`,
263+
);
264+
});
265+
228266
it("logs a Prettier recommendation when extends doesn't include eslint-config-prettier", async () => {
229267
// Arrange
230268
const logger = createStubLogger();

0 commit comments

Comments
 (0)