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

Issue:
Missing tslint-react rule: jsx-space-before-trailing-slash
### Missing jsx-space-before-trailing-slash

-   `tslint-to-eslint-config` version: 1.0.0-beta

-   `typescript-eslint` version: 2.31.0

#### TSLint Rule Name

jsx-space-before-trailing-slash

#### Package Source

**tslint-react**: https://github.com/palantir/tslint-react (see [#rules](https://github.com/palantir/tslint-react#rules))

Equivalents for tslint-react rules generally live in [eslint-plugin-react](https://github.com/yannickcr/eslint-plugin-react). If an ESLint rule equivalent doesn't yet exist, this tslint-to-eslint-config issue can be closed until that equivalent is created.

Pull request description:
Added jsx-space-before-trailing-slash converter
## PR Checklist

-   [x] Addresses an existing issue: fixes #526
-   [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/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-space-before-closing.md -> https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-tag-spacing.md

Patch:
diff --git a/package-lock.json b/package-lock.json
index d8317ab..dcd6028 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -5001,9 +5001,9 @@
             "dev": true
         },
         "husky": {
-            "version": "4.3.5",
-            "resolved": "https://registry.npmjs.org/husky/-/husky-4.3.5.tgz",
-            "integrity": "sha512-E5S/1HMoDDaqsH8kDF5zeKEQbYqe3wL9zJDyqyYqc8I4vHBtAoxkDBGXox0lZ9RI+k5GyB728vZdmnM4bYap+g==",
+            "version": "4.3.6",
+            "resolved": "https://registry.npmjs.org/husky/-/husky-4.3.6.tgz",
+            "integrity": "sha512-o6UjVI8xtlWRL5395iWq9LKDyp/9TE7XMOTvIpEVzW638UcGxTmV5cfel6fsk/jbZSTlvfGVJf2svFtybcIZag==",
             "dev": true,
             "requires": {
                 "chalk": "^4.0.0",
diff --git a/package.json b/package.json
index 8e6ddf5..d12d059 100644
--- a/package.json
+++ b/package.json
@@ -38,7 +38,7 @@
         "@typescript-eslint/parser": "4.9.1",
         "babel-jest": "26.6.3",
         "eslint": "7.15.0",
-        "husky": "4.3.5",
+        "husky": "4.3.6",
         "jest": "26.6.3",
         "lint-staged": "10.5.3",
         "prettier": "2.2.1",
diff --git a/src/converters/lintConfigs/rules/ruleConverters.ts b/src/converters/lintConfigs/rules/ruleConverters.ts
index 5f72f64..748fb77 100644
--- a/src/converters/lintConfigs/rules/ruleConverters.ts
+++ b/src/converters/lintConfigs/rules/ruleConverters.ts
@@ -176,10 +176,12 @@ import { convertUsePipeDecorator } from "./ruleConverters/codelyzer/use-pipe-dec
 import { convertUsePipeTransformInterface } from "./ruleConverters/codelyzer/use-pipe-transform-interface";
 
 // ESLint-React converters
+import { convertJsxBanProps } from "./ruleConverters/eslint-plugin-react/jsx-ban-props";
 import { convertJsxBooleanValue } from "./ruleConverters/eslint-plugin-react/jsx-boolean-value";
 import { convertJsxCurlySpacing } from "./ruleConverters/eslint-plugin-react/jsx-curly-spacing";
 import { convertJsxEqualsSpacing } from "./ruleConverters/eslint-plugin-react/jsx-equals-spacing";
 import { convertJsxKey } from "./ruleConverters/eslint-plugin-react/jsx-key";
+import { convertJsxSpaceBeforeTrailingSlash } from "./ruleConverters/eslint-plugin-react/jsx-space-before-trailing-slash";
 import { convertJsxNoBind } from "./ruleConverters/eslint-plugin-react/jsx-no-bind";
 import { convertJsxNoLambda } from "./ruleConverters/eslint-plugin-react/jsx-no-lambda";
 import { convertJsxSelfClose } from "./ruleConverters/eslint-plugin-react/jsx-self-close";
@@ -239,6 +241,7 @@ export const ruleConverters = new Map([
     ["interface-name", convertInterfaceName],
     ["interface-over-type-literal", convertInterfaceOverTypeLiteral],
     ["jsdoc-format", convertJSDocFormat],
+    ["jsx-ban-props", convertJsxBanProps],
     ["jsx-boolean-value", convertJsxBooleanValue],
     ["jsx-curly-spacing", convertJsxCurlySpacing],
     ["jsx-equals-spacing", convertJsxEqualsSpacing],
@@ -246,6 +249,7 @@ export const ruleConverters = new Map([
     ["jsx-no-bind", convertJsxNoBind],
     ["jsx-no-lambda", convertJsxNoLambda],
     ["jsx-self-close", convertJsxSelfClose],
+    ["jsx-space-before-trailing-slash", convertJsxSpaceBeforeTrailingSlash],
     ["jsx-wrap-multiline", convertJsxWrapMultiline],
     ["label-position", convertLabelPosition],
     ["linebreak-style", convertLinebreakStyle],
diff --git a/src/converters/lintConfigs/rules/ruleConverters/eslint-plugin-react/jsx-ban-props.ts b/src/converters/lintConfigs/rules/ruleConverters/eslint-plugin-react/jsx-ban-props.ts
new file mode 100644
index 0000000..d66dae7
--- /dev/null
+++ b/src/converters/lintConfigs/rules/ruleConverters/eslint-plugin-react/jsx-ban-props.ts
@@ -0,0 +1,23 @@
+import { RuleConverter } from "../../ruleConverter";
+
+export const convertJsxBanProps: RuleConverter = (tslintRule) => {
+    return {
+        rules: [
+            {
+                ...(tslintRule.ruleArguments.length !== 0 && {
+                    ruleArguments: tslintRule.ruleArguments.map((ruleArgument) => ({
+                        forbid:
+                            ruleArgument.length === 1
+                                ? ruleArgument[0]
+                                : {
+                                      message: ruleArgument[1],
+                                      propName: ruleArgument[0],
+                                  },
+                    })),
+                }),
+                ruleName: "react/forbid-component-props",
+            },
+        ],
+        plugins: ["eslint-plugin-react"],
+    };
+};
diff --git a/src/converters/lintConfigs/rules/ruleConverters/eslint-plugin-react/jsx-space-before-trailing-slash.ts b/src/converters/lintConfigs/rules/ruleConverters/eslint-plugin-react/jsx-space-before-trailing-slash.ts
new file mode 100644
index 0000000..8a00274
--- /dev/null
+++ b/src/converters/lintConfigs/rules/ruleConverters/eslint-plugin-react/jsx-space-before-trailing-slash.ts
@@ -0,0 +1,18 @@
+import { RuleConverter } from "../../ruleConverter";
+
+export const convertJsxSpaceBeforeTrailingSlash: RuleConverter = () => {
+    return {
+        rules: [
+            {
+                ruleArguments: [
+                    {
+                        afterOpening: "allow",
+                        closingSlash: "allow",
+                    },
+                ],
+                ruleName: "react/jsx-tag-spacing",
+            },
+        ],
+        plugins: ["eslint-plugin-react"],
+    };
+};


Test patch:
diff --git a/src/converters/lintConfigs/rules/ruleConverters/eslint-plugin-react/tests/jsx-ban-props.test.ts b/src/converters/lintConfigs/rules/ruleConverters/eslint-plugin-react/tests/jsx-ban-props.test.ts
new file mode 100644
index 0000000..ed95596
--- /dev/null
+++ b/src/converters/lintConfigs/rules/ruleConverters/eslint-plugin-react/tests/jsx-ban-props.test.ts
@@ -0,0 +1,64 @@
+import { convertJsxBanProps } from "../jsx-ban-props";
+
+describe(convertJsxBanProps, () => {
+    test("conversion without arguments", () => {
+        const result = convertJsxBanProps({
+            ruleArguments: [],
+        });
+
+        expect(result).toEqual({
+            rules: [
+                {
+                    ruleName: "react/forbid-component-props",
+                },
+            ],
+            plugins: ["eslint-plugin-react"],
+        });
+    });
+
+    test("conversion with a single prop", () => {
+        const result = convertJsxBanProps({
+            ruleArguments: [["someProp"]],
+        });
+
+        expect(result).toEqual({
+            rules: [
+                {
+                    ruleArguments: [
+                        {
+                            forbid: "someProp",
+                        },
+                    ],
+                    ruleName: "react/forbid-component-props",
+                },
+            ],
+            plugins: ["eslint-plugin-react"],
+        });
+    });
+
+    test("conversion with a message", () => {
+        const result = convertJsxBanProps({
+            ruleArguments: [["someProp"], ["anotherProp", "Optional explanation"]],
+        });
+
+        expect(result).toEqual({
+            rules: [
+                {
+                    ruleArguments: [
+                        {
+                            forbid: "someProp",
+                        },
+                        {
+                            forbid: {
+                                message: "Optional explanation",
+                                propName: "anotherProp",
+                            },
+                        },
+                    ],
+                    ruleName: "react/forbid-component-props",
+                },
+            ],
+            plugins: ["eslint-plugin-react"],
+        });
+    });
+});
diff --git a/src/converters/lintConfigs/rules/ruleConverters/eslint-plugin-react/tests/jsx-space-before-trailing-slash.test.ts b/src/converters/lintConfigs/rules/ruleConverters/eslint-plugin-react/tests/jsx-space-before-trailing-slash.test.ts
new file mode 100644
index 0000000..8b0fd54
--- /dev/null
+++ b/src/converters/lintConfigs/rules/ruleConverters/eslint-plugin-react/tests/jsx-space-before-trailing-slash.test.ts
@@ -0,0 +1,24 @@
+import { convertJsxSpaceBeforeTrailingSlash } from "../jsx-space-before-trailing-slash";
+
+describe(convertJsxSpaceBeforeTrailingSlash, () => {
+    test("conversion without arguments", () => {
+        const result = convertJsxSpaceBeforeTrailingSlash({
+            ruleArguments: [],
+        });
+
+        expect(result).toEqual({
+            rules: [
+                {
+                    ruleArguments: [
+                        {
+                            afterOpening: "allow",
+                            closingSlash: "allow",
+                        },
+                    ],
+                    ruleName: "react/jsx-tag-spacing",
+                },
+            ],
+            plugins: ["eslint-plugin-react"],
+        });
+    });
+});