Repository: yannickcr/eslint-plugin-react
Pull request #417 (2016-01-29 13:25:30)

Add jsx-space-before-closing rule
I implemented a new rule proposed at #244.
The rule simply enforces a space before `/>` on self closing elements like `<div />`.

* no option
* fixable


Patch:
diff --git a/README.md b/README.md
index a9c82af0..3cc02f68 100644
--- a/README.md
+++ b/README.md
@@ -76,6 +76,7 @@ Finally, enable all of the rules that you would like to use.
     "react/forbid-prop-types": 1,
     "react/jsx-boolean-value": 1,
     "react/jsx-closing-bracket-location": 1,
+    "react/jsx-space-before-closing": 1,
     "react/jsx-curly-spacing": 1,
     "react/jsx-equals-spacing": 1,
     "react/jsx-handler-names": 1,
@@ -120,6 +121,7 @@ Finally, enable all of the rules that you would like to use.
 * [forbid-prop-types](docs/rules/forbid-prop-types.md): Forbid certain propTypes
 * [jsx-boolean-value](docs/rules/jsx-boolean-value.md): Enforce boolean attributes notation in JSX (fixable)
 * [jsx-closing-bracket-location](docs/rules/jsx-closing-bracket-location.md): Validate closing bracket location in JSX
+* [jsx-space-before-closing](docs/rules/jsx-space-before-closing.md): Validate spacing before closing bracket in JSX (fixable)
 * [jsx-curly-spacing](docs/rules/jsx-curly-spacing.md): Enforce or disallow spaces inside of curly braces in JSX attributes (fixable)
 * [jsx-equals-spacing](docs/rules/jsx-equals-spacing.md): Enforce or disallow spaces around equal signs in JSX attributes
 * [jsx-handler-names](docs/rules/jsx-handler-names.md): Enforce event handler naming conventions in JSX
diff --git a/docs/rules/jsx-space-before-closing.md b/docs/rules/jsx-space-before-closing.md
new file mode 100644
index 00000000..5f0dda7a
--- /dev/null
+++ b/docs/rules/jsx-space-before-closing.md
@@ -0,0 +1,29 @@
+# Validate spacing before closing bracket in JSX (jsx-space-before-closing)
+
+Enforce a space before the closing bracket of self-closing JSX elements.
+
+## Rule Details
+
+This rule checks if there is one or more spaces before the closing bracket of self-closing JSX elements.
+
+The following patterns are considered warnings:
+
+```js
+<Hello/>
+<Hello firstname="John"/>
+```
+
+The following patterns are not considered warnings:
+
+```js
+<Hello />
+<Hello firstName="John" />
+<Hello
+  firstName="John"
+  lastName="Smith"
+/>
+```
+
+## When Not To Use It
+
+You can turn this rule off if you are not concerned with the consistency of spacing before closing brackets.
diff --git a/index.js b/index.js
index ad33d96e..5dfbf5e6 100644
--- a/index.js
+++ b/index.js
@@ -35,6 +35,7 @@ module.exports = {
     'jsx-indent-props': require('./lib/rules/jsx-indent-props'),
     'jsx-indent': require('./lib/rules/jsx-indent'),
     'jsx-closing-bracket-location': require('./lib/rules/jsx-closing-bracket-location'),
+    'jsx-space-before-closing': require('./lib/rules/jsx-space-before-closing'),
     'no-direct-mutation-state': require('./lib/rules/no-direct-mutation-state'),
     'forbid-prop-types': require('./lib/rules/forbid-prop-types'),
     'prefer-es6-class': require('./lib/rules/prefer-es6-class'),
@@ -75,6 +76,7 @@ module.exports = {
     'jsx-indent-props': 0,
     'jsx-indent': 0,
     'jsx-closing-bracket-location': 0,
+    'jsx-space-before-closing': 0,
     'no-direct-mutation-state': 0,
     'forbid-prop-types': 0,
     'prefer-es6-class': 0,
diff --git a/lib/rules/jsx-space-before-closing.js b/lib/rules/jsx-space-before-closing.js
new file mode 100644
index 00000000..7d06651a
--- /dev/null
+++ b/lib/rules/jsx-space-before-closing.js
@@ -0,0 +1,56 @@
+/**
+ * @fileoverview Enforce a space before closing bracket of self-closing JSX elements.
+ * @author ryym
+ */
+'use strict';
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+module.exports = function(context) {
+  var sourceCode = context.getSourceCode();
+
+  /**
+   * Find the token before the closing bracket.
+   * @param {ASTNode} node - The JSX element node.
+   * @returns {Token} The token before the closing bracket.
+   */
+  function getTokenBeforeClosingBracket(node) {
+    var attributes = node.attributes;
+    if (attributes.length === 0) {
+      return node.name;
+    }
+    return attributes[ attributes.length - 1 ];
+  }
+
+  // --------------------------------------------------------------------------
+  // Public
+  // --------------------------------------------------------------------------
+
+  return {
+    JSXOpeningElement: function(node) {
+      if (!node.selfClosing) {
+        return;
+      }
+
+      var leftToken = getTokenBeforeClosingBracket(node);
+      var closingSlash = sourceCode.getTokenAfter(leftToken);
+
+      if (sourceCode.isSpaceBetweenTokens(leftToken, closingSlash)) {
+        return;
+      }
+
+      context.report({
+        loc: closingSlash.loc.start,
+        message: 'A space is required before \'/>\'',
+        fix: function(fixer) {
+          return fixer.insertTextBefore(closingSlash, ' ');
+        }
+      });
+    }
+  };
+
+};
+
+module.exports.schema = [];


Test patch:
diff --git a/tests/lib/rules/jsx-space-before-closing.js b/tests/lib/rules/jsx-space-before-closing.js
new file mode 100644
index 00000000..76d10c95
--- /dev/null
+++ b/tests/lib/rules/jsx-space-before-closing.js
@@ -0,0 +1,81 @@
+/**
+ * @fileoverview Enforce a space before closing bracket of self-closing JSX elements.
+ * @author ryym
+ */
+'use strict';
+
+// ------------------------------------------------------------------------------
+// Requirements
+// ------------------------------------------------------------------------------
+
+var rule = require('../../../lib/rules/jsx-space-before-closing');
+var RuleTester = require('eslint').RuleTester;
+
+var parserOptions = {
+  ecmaVersion: 6,
+  ecmaFeatures: {
+    jsx: true
+  }
+};
+
+// ------------------------------------------------------------------------------
+// Tests
+// ------------------------------------------------------------------------------
+
+var ruleTester = new RuleTester();
+ruleTester.run('jsx-space-before-closing', rule, {
+  valid: [{
+    code: '<App />',
+    parserOptions: parserOptions
+  }, {
+    code: '<App foo />',
+    parserOptions: parserOptions
+  }, {
+    code: '<App foo={bar} />',
+    parserOptions: parserOptions
+  }, {
+    code: '<App {...props} />',
+    parserOptions: parserOptions
+  }, {
+    code: '<App></App>',
+    parserOptions: parserOptions
+  }, {
+    code: [
+      '<App',
+      '  foo={bar}',
+      '/>'
+    ].join('\n'),
+    parserOptions: parserOptions
+  }],
+
+  invalid: [{
+    code: '<App/>',
+    output: '<App />',
+    parserOptions: parserOptions,
+    errors: [
+      {message: 'A space is required before \'/>\''}
+    ]
+  }, {
+    code: '<App foo/>',
+    output: '<App foo />',
+    parserOptions: parserOptions,
+    errors: [
+      {message: 'A space is required before \'/>\''}
+    ]
+  }, {
+    code: '<App foo={bar}/>',
+    output: '<App foo={bar} />',
+    parserOptions: parserOptions,
+    errors: [
+      {message: 'A space is required before \'/>\''}
+    ]
+  }, {
+    code: '<App {...props}/>',
+    output: '<App {...props} />',
+    parserOptions: parserOptions,
+    errors: [
+      {message: 'A space is required before \'/>\''}
+    ]
+  }]
+});
+