Repository: jsx-eslint/eslint-plugin-react (JavaScript)

Issue:
jsx-closing-bracket-location think of 'space' and 'tab' as the same thing
my config

```
"react/jsx-indent": ["warn", "tab"],
"react/jsx-indent-props": ["warn", "tab"],
"react/jsx-closing-bracket-location": ["warn", {
  "nonEmpty": "tag-aligned"
}],
```

my code

``` jsx
        <div
            className={ styles.header_title }
  >
            { props.title }
        </div>
```

opening tag has 2 tabs, closing bracket has 2 spaces but this case doesn't have any warning or error.



Discussion:
Using `--fix` also results in an output like above.

I'm still seeing this issue.  It's causing a lot of problems on a big team, some people aren't used to working with tabs and the only way to catch the issue is via code review.
May I try to solve this bug?
@Moong0122 yes, anything with the "help wanted" label that doesn't already have a PR, you can just make a PR for it :-)
I understand that if the rule `react/jsx-indent": ["warn", "tab"]` is used, an error occurs when the tab is not used.
But there's no error in the code here.
Where do you think the problem occurred between `react/jsx-indent` and `jsx-closing-bracket-location`?
I would expect jsx-closing-bracket-location to be the thing that warns, because jsx-indent would determine where `<div` is, and then closing-bracket-location would use *that* to determine where `>` should be.



Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index bc3c26092f..05fe8c8a30 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -16,7 +16,9 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel
 * [`jsx-handler-names`]: handle whitespace ([#2789][] @AriPerkkio)
 * [`prop-types`]: Detect TypeScript types for destructured default prop values ([#2780][] @sunghyunjo)
 * [`jsx-pascal-case`]: Handle single character namespaced component ([#2791][] @daviferreira)
+* [`jsx-closing-bracket-location`]: In `tag-aligned`, made a distinction between tabs and spaces ([#2796][] @Moong0122)
 
+[#2796]: https://github.com/yannickcr/eslint-plugin-react/pull/2796
 [#2791]: https://github.com/yannickcr/eslint-plugin-react/pull/2791
 [#2789]: https://github.com/yannickcr/eslint-plugin-react/pull/2789
 [#2780]: https://github.com/yannickcr/eslint-plugin-react/pull/2780
diff --git a/lib/rules/jsx-closing-bracket-location.js b/lib/rules/jsx-closing-bracket-location.js
index b49abdc0bf..77bab9131a 100644
--- a/lib/rules/jsx-closing-bracket-location.js
+++ b/lib/rules/jsx-closing-bracket-location.js
@@ -201,11 +201,17 @@ module.exports = {
         };
       }
       const openingLine = sourceCode.lines[opening.line - 1];
+      const closingLine = sourceCode.lines[closing.line - 1];
+      const isTab = {
+        openTab: /^\t/.test(openingLine),
+        closeTab: /^\t/.test(closingLine)
+      };
       const openingStartOfLine = {
         column: /^\s*/.exec(openingLine)[0].length,
         line: opening.line
       };
       return {
+        isTab,
         tag,
         opening,
         closing,
@@ -239,11 +245,17 @@ module.exports = {
       'JSXOpeningElement:exit'(node) {
         const attributeNode = lastAttributeNode[getOpeningElementId(node)];
         const cachedLastAttributeEndPos = attributeNode ? attributeNode.range[1] : null;
+
         let expectedNextLine;
         const tokens = getTokensLocations(node);
         const expectedLocation = getExpectedLocation(tokens);
+        let usingSameIndentation = true;
+
+        if (expectedLocation === 'tag-aligned') {
+          usingSameIndentation = tokens.isTab.openTab === tokens.isTab.closeTab;
+        }
 
-        if (hasCorrectLocation(tokens, expectedLocation)) {
+        if (hasCorrectLocation(tokens, expectedLocation) && usingSameIndentation) {
           return;
         }
 


Test patch:
diff --git a/tests/lib/rules/jsx-closing-bracket-location.js b/tests/lib/rules/jsx-closing-bracket-location.js
index cafcd9ebfd..e415b685d8 100644
--- a/tests/lib/rules/jsx-closing-bracket-location.js
+++ b/tests/lib/rules/jsx-closing-bracket-location.js
@@ -1689,5 +1689,81 @@ ruleTester.run('jsx-closing-bracket-location', rule, {
       line: 4,
       column: 6
     }]
+  }, {
+    code: [
+      '\t\t<div',
+      '\t\t\tclassName={styles}',
+      '  >',
+      '\t\t\t{props}',
+      '\t\t</div>'
+    ].join('\n'),
+    output: [
+      '\t\t<div',
+      '\t\t\tclassName={styles}',
+      '\t\t>',
+      '\t\t\t{props}',
+      '\t\t</div>'
+    ].join('\n'),
+    options: [{location: 'tag-aligned'}],
+    errors: [{
+      message: messageWithDetails(MESSAGE_TAG_ALIGNED, 3, false),
+      line: 3,
+      column: 3
+    }]
+  }, {
+    code: [
+      '  <div',
+      '    className={styles}',
+      '\t\t>',
+      '    {props}',
+      '  </div>'
+    ].join('\n'),
+    output: [
+      '  <div',
+      '    className={styles}',
+      '  >',
+      '    {props}',
+      '  </div>'
+    ].join('\n'),
+    options: [{location: 'tag-aligned'}],
+    errors: [{
+      message: messageWithDetails(MESSAGE_TAG_ALIGNED, 3, false),
+      line: 3,
+      column: 3
+    }]
+  }, {
+    code: [
+      '  <App',
+      '    foo',
+      '\t\t/>'
+    ].join('\n'),
+    output: [
+      '  <App',
+      '    foo',
+      '  />'
+    ].join('\n'),
+    options: [{location: 'tag-aligned'}],
+    errors: [{
+      message: messageWithDetails(MESSAGE_TAG_ALIGNED, 3, false),
+      line: 3,
+      column: 3
+    }]
+  }, {
+    code: [
+      '\t\t<App',
+      '\t\t\tfoo',
+      '  />'
+    ].join('\n'),
+    output: [
+      '\t\t<App',
+      '\t\t\tfoo',
+      '\t\t/>'
+    ].join('\n'),
+    options: [{location: 'tag-aligned'}],
+    errors: [{
+      message: messageWithDetails(MESSAGE_TAG_ALIGNED, 3, false),
+      line: 3,
+      column: 3
+    }]
   }]
 });