Repository: typescript-eslint/tslint-to-eslint-config (ts)

Issue:
Missing tslint-microsoft-contrib converter: no-delete-expression
### 💥 Missing Converter

-   `tslint-to-eslint-config` version: 2.0.1
-   `typescript-eslint` version: 4.9.1

#### TSLint Rule Name

`no-delete-expression`

Per https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/ROADMAP.md, the equivalent rule is now `no-delete-var`.

#### Package Source

https://github.com/microsoft/tslint-microsoft-contrib

Pull request description:
Added converter for no-delete-expression
## PR Checklist

-   [x] Addresses an existing issue: fixes #871
-   [x] That issue was marked as [`status: accepting prs`](https://github.com/typescript-eslint/tslint-to-eslint-config/labels/status%3A%20accepting%20prs)

## Overview

https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-dynamic-delete.md

https://github.com/typescript-eslint/typescript-eslint/pull/3406


Patch:
diff --git a/src/converters/lintConfigs/rules/ruleConverters.ts b/src/converters/lintConfigs/rules/ruleConverters.ts
index 093edb2..829be8a 100644
--- a/src/converters/lintConfigs/rules/ruleConverters.ts
+++ b/src/converters/lintConfigs/rules/ruleConverters.ts
@@ -27,6 +27,7 @@ import { convertJSDocFormat } from "./ruleConverters/jsdoc-format";
 import { convertLabelPosition } from "./ruleConverters/label-position";
 import { convertLinebreakStyle } from "./ruleConverters/linebreak-style";
 import { convertMaxClassesPerFile } from "./ruleConverters/max-classes-per-file";
+import { convertMaxFuncBodyLength } from "./ruleConverters/max-func-body-length";
 import { convertMaxFileLineCount } from "./ruleConverters/max-file-line-count";
 import { convertMaxLineLength } from "./ruleConverters/max-line-length";
 import { convertMemberAccess } from "./ruleConverters/member-access";
@@ -49,6 +50,7 @@ import { convertNoConstruct } from "./ruleConverters/no-construct";
 import { convertNoControlRegex } from "./ruleConverters/no-control-regex";
 import { convertNoDebugger } from "./ruleConverters/no-debugger";
 import { convertNoDefaultExport } from "./ruleConverters/no-default-export";
+import { convertNoDeleteExpression } from "./ruleConverters/no-delete-expression";
 import { convertNoDuplicateImports } from "./ruleConverters/no-duplicate-imports";
 import { convertNoDuplicateSuper } from "./ruleConverters/no-duplicate-super";
 import { convertNoDuplicateSwitchCase } from "./ruleConverters/no-duplicate-switch-case";
@@ -56,6 +58,7 @@ import { convertNoDuplicateVariable } from "./ruleConverters/no-duplicate-variab
 import { convertNoDynamicDelete } from './ruleConverters/no-dynamic-delete';
 import { convertNoEmpty } from "./ruleConverters/no-empty";
 import { convertNoEmptyInterface } from "./ruleConverters/no-empty-interface";
+import { convertNoEmptyLineAfterOpeningBrace } from "./ruleConverters/no-empty-line-after-opening-brace";
 import { convertNoEval } from "./ruleConverters/no-eval";
 import { convertNoExplicitAny } from "./ruleConverters/no-explicit-any";
 import { convertNoFloatingPromises } from "./ruleConverters/no-floating-promises";
@@ -276,6 +279,7 @@ export const ruleConverters = new Map([
     ["label-position", convertLabelPosition],
     ["linebreak-style", convertLinebreakStyle],
     ["max-classes-per-file", convertMaxClassesPerFile],
+    ["max-func-body-length", convertMaxFuncBodyLength],
     ["max-file-line-count", convertMaxFileLineCount],
     ["max-line-length", convertMaxLineLength],
     ["member-access", convertMemberAccess],
@@ -301,12 +305,14 @@ export const ruleConverters = new Map([
     ["no-control-regex", convertNoControlRegex],
     ["no-debugger", convertNoDebugger],
     ["no-default-export", convertNoDefaultExport],
+    ["no-delete-expression", convertNoDeleteExpression],
     ["no-duplicate-imports", convertNoDuplicateImports],
     ["no-duplicate-super", convertNoDuplicateSuper],
     ["no-duplicate-switch-case", convertNoDuplicateSwitchCase],
     ["no-duplicate-variable", convertNoDuplicateVariable],
     ["no-dynamic-delete", convertNoDynamicDelete],
     ["no-empty-interface", convertNoEmptyInterface],
+    ["no-empty-line-after-opening-brace", convertNoEmptyLineAfterOpeningBrace], // padded-blocks
     ["no-empty", convertNoEmpty],
     ["no-eval", convertNoEval],
     ["no-floating-promises", convertNoFloatingPromises],
@@ -463,7 +469,6 @@ export const ruleConverters = new Map([
 
     // tslint-microsoft-contrib rules:
     // ["max-func-body-length", convertMaxFuncBodyLength],
-    // ["no-empty-line-after-opening-brace", convertNoEmptyLineAfterOpeningBrace], // padded-blocks
     // ["no-function-expression", convertNoFunctionExpression], // ban-syntax config
     // ["no-suspicious-comment", convertNoSuspiciousComment],
     // ["no-with-statement", convertNoWithStatement],
diff --git a/src/converters/lintConfigs/rules/ruleConverters/max-func-body-length.ts b/src/converters/lintConfigs/rules/ruleConverters/max-func-body-length.ts
new file mode 100644
index 0000000..a4855cb
--- /dev/null
+++ b/src/converters/lintConfigs/rules/ruleConverters/max-func-body-length.ts
@@ -0,0 +1,42 @@
+import { isNumber } from "lodash";
+
+import { RuleConverter } from "../ruleConverter";
+
+const parseExtras = (ruleArguments: any[]) => {
+    if (ruleArguments.length === 0) {
+        return {};
+    }
+
+    const [max] = ruleArguments;
+    if (typeof max === "number") {
+        return {
+            ruleArguments: [max],
+        }
+    }
+
+    const notices = [
+        "ESLint's max-statements rule only supports a single maximum function length."
+    ];
+
+    if (max["ignore-comments"]) {
+        notices.push("ESLint's max-statements rule does not have an option to ignore comments.")
+    }
+
+    return {
+        notices,
+        ruleArguments: [
+            Math.max(...Object.values(max as Record<string, number | string>).filter(isNumber)),
+        ]
+    }
+}
+
+export const convertMaxFuncBodyLength: RuleConverter = (tslintRule) => {
+    return {
+        rules: [
+            {
+                ...parseExtras(tslintRule.ruleArguments),
+                ruleName: "max-statements",
+            },
+        ],
+    };
+};
diff --git a/src/converters/lintConfigs/rules/ruleConverters/no-delete-expression.ts b/src/converters/lintConfigs/rules/ruleConverters/no-delete-expression.ts
new file mode 100644
index 0000000..6e50934
--- /dev/null
+++ b/src/converters/lintConfigs/rules/ruleConverters/no-delete-expression.ts
@@ -0,0 +1,11 @@
+import { RuleConverter } from "../ruleConverter";
+
+export const convertNoDeleteExpression: RuleConverter = () => {
+    return {
+        rules: [
+            {
+                ruleName: "@typescript-eslint/no-dynamic-delete",
+            },
+        ],
+    };
+};
diff --git a/src/converters/lintConfigs/rules/ruleConverters/no-empty-line-after-opening-brace.ts b/src/converters/lintConfigs/rules/ruleConverters/no-empty-line-after-opening-brace.ts
new file mode 100644
index 0000000..664a400
--- /dev/null
+++ b/src/converters/lintConfigs/rules/ruleConverters/no-empty-line-after-opening-brace.ts
@@ -0,0 +1,20 @@
+import { RuleConverter } from "../ruleConverter";
+
+export const convertNoEmptyLineAfterOpeningBrace: RuleConverter = () => {
+    return {
+        rules: [
+            {
+                notices: ["ESLint's padded-blocks rule also bans a blank line before a closing brace."],
+                ruleArguments: [
+                    {
+                        blocks: "never",
+                    },
+                    {
+                        "allowSingleLineBlocks": true,
+                    }
+                ],
+                ruleName: "padded-blocks",
+            },
+        ],
+    };
+};


Test patch:
diff --git a/src/converters/lintConfigs/rules/ruleConverters/tests/max-func-body-length.test.ts b/src/converters/lintConfigs/rules/ruleConverters/tests/max-func-body-length.test.ts
new file mode 100644
index 0000000..ccef176
--- /dev/null
+++ b/src/converters/lintConfigs/rules/ruleConverters/tests/max-func-body-length.test.ts
@@ -0,0 +1,76 @@
+import { convertMaxFuncBodyLength } from "../max-func-body-length";
+
+describe(convertMaxFuncBodyLength, () => {
+    test("conversion without arguments", () => {
+        const result = convertMaxFuncBodyLength({
+            ruleArguments: [],
+        });
+
+        expect(result).toEqual({
+            rules: [
+                {
+                    ruleName: "max-statements",
+                },
+            ],
+        });
+    });
+
+    test("conversion with a max number", () => {
+        const result = convertMaxFuncBodyLength({
+            ruleArguments: [10],
+        });
+
+        expect(result).toEqual({
+            rules: [
+                {
+                    ruleArguments: [10],
+                    ruleName: "max-statements",
+                },
+            ],
+        });
+    });
+
+    test("conversion with a max object", () => {
+        const result = convertMaxFuncBodyLength({
+            ruleArguments: [{
+                "ctor-body-length": 15,
+                "func-body-length": 5,
+            }],
+        });
+
+        expect(result).toEqual({
+            rules: [
+                {
+                    notices: [
+                        "ESLint's max-statements rule only supports a single maximum function length."
+                    ],
+                    ruleArguments: [15],
+                    ruleName: "max-statements",
+                },
+            ],
+        });
+    });
+
+    test("conversion with the ignore-comments option", () => {
+        const result = convertMaxFuncBodyLength({
+            ruleArguments: [{
+                "ctor-body-length": 15,
+                "func-body-length": 5,
+                "ignore-comments": true,
+            }],
+        });
+
+        expect(result).toEqual({
+            rules: [
+                {
+                    notices: [
+                        "ESLint's max-statements rule only supports a single maximum function length.",
+                        "ESLint's max-statements rule does not have an option to ignore comments."
+                    ],
+                    ruleArguments: [15],
+                    ruleName: "max-statements",
+                },
+            ],
+        });
+    });
+});
diff --git a/src/converters/lintConfigs/rules/ruleConverters/tests/no-delete-expression.test.ts b/src/converters/lintConfigs/rules/ruleConverters/tests/no-delete-expression.test.ts
new file mode 100644
index 0000000..969cf5f
--- /dev/null
+++ b/src/converters/lintConfigs/rules/ruleConverters/tests/no-delete-expression.test.ts
@@ -0,0 +1,17 @@
+import { convertNoDeleteExpression } from "../no-delete-expression";
+
+describe(convertNoDeleteExpression, () => {
+    test("conversion", () => {
+        const result = convertNoDeleteExpression({
+            ruleArguments: [],
+        });
+
+        expect(result).toEqual({
+            rules: [
+                {
+                    ruleName: "@typescript-eslint/no-dynamic-delete",
+                },
+            ],
+        });
+    });
+});
diff --git a/src/converters/lintConfigs/rules/ruleConverters/tests/no-empty-line-after-opening-brace.test.ts b/src/converters/lintConfigs/rules/ruleConverters/tests/no-empty-line-after-opening-brace.test.ts
new file mode 100644
index 0000000..0940b81
--- /dev/null
+++ b/src/converters/lintConfigs/rules/ruleConverters/tests/no-empty-line-after-opening-brace.test.ts
@@ -0,0 +1,26 @@
+import { convertNoEmptyLineAfterOpeningBrace } from "../no-empty-line-after-opening-brace";
+
+describe(convertNoEmptyLineAfterOpeningBrace, () => {
+    test("conversion ", () => {
+        const result = convertNoEmptyLineAfterOpeningBrace({
+            ruleArguments: [],
+        });
+
+        expect(result).toEqual({
+            rules: [
+                {
+                    notices: ["ESLint's padded-blocks rule also bans a blank line before a closing brace."],
+                    ruleArguments: [
+                        {
+                            blocks: "never",
+                        },
+                        {
+                            "allowSingleLineBlocks": true,
+                        }
+                    ],
+                    ruleName: "padded-blocks",
+                },
+            ],
+        });
+    });
+});