Skip to content

Commit d0a6b58

Browse files
committed
Merge pull request #417 from ryym/jsx-space-before-closing
Add jsx-space-before-closing rule
2 parents dedf67a + cb84dba commit d0a6b58

File tree

5 files changed

+170
-0
lines changed

5 files changed

+170
-0
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ Finally, enable all of the rules that you would like to use.
7676
"react/forbid-prop-types": 1,
7777
"react/jsx-boolean-value": 1,
7878
"react/jsx-closing-bracket-location": 1,
79+
"react/jsx-space-before-closing": 1,
7980
"react/jsx-curly-spacing": 1,
8081
"react/jsx-equals-spacing": 1,
8182
"react/jsx-handler-names": 1,
@@ -120,6 +121,7 @@ Finally, enable all of the rules that you would like to use.
120121
* [forbid-prop-types](docs/rules/forbid-prop-types.md): Forbid certain propTypes
121122
* [jsx-boolean-value](docs/rules/jsx-boolean-value.md): Enforce boolean attributes notation in JSX (fixable)
122123
* [jsx-closing-bracket-location](docs/rules/jsx-closing-bracket-location.md): Validate closing bracket location in JSX
124+
* [jsx-space-before-closing](docs/rules/jsx-space-before-closing.md): Validate spacing before closing bracket in JSX (fixable)
123125
* [jsx-curly-spacing](docs/rules/jsx-curly-spacing.md): Enforce or disallow spaces inside of curly braces in JSX attributes (fixable)
124126
* [jsx-equals-spacing](docs/rules/jsx-equals-spacing.md): Enforce or disallow spaces around equal signs in JSX attributes
125127
* [jsx-handler-names](docs/rules/jsx-handler-names.md): Enforce event handler naming conventions in JSX
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Validate spacing before closing bracket in JSX (jsx-space-before-closing)
2+
3+
Enforce a space before the closing bracket of self-closing JSX elements.
4+
5+
## Rule Details
6+
7+
This rule checks if there is one or more spaces before the closing bracket of self-closing JSX elements.
8+
9+
The following patterns are considered warnings:
10+
11+
```js
12+
<Hello/>
13+
<Hello firstname="John"/>
14+
```
15+
16+
The following patterns are not considered warnings:
17+
18+
```js
19+
<Hello />
20+
<Hello firstName="John" />
21+
<Hello
22+
firstName="John"
23+
lastName="Smith"
24+
/>
25+
```
26+
27+
## When Not To Use It
28+
29+
You can turn this rule off if you are not concerned with the consistency of spacing before closing brackets.

index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ module.exports = {
3535
'jsx-indent-props': require('./lib/rules/jsx-indent-props'),
3636
'jsx-indent': require('./lib/rules/jsx-indent'),
3737
'jsx-closing-bracket-location': require('./lib/rules/jsx-closing-bracket-location'),
38+
'jsx-space-before-closing': require('./lib/rules/jsx-space-before-closing'),
3839
'no-direct-mutation-state': require('./lib/rules/no-direct-mutation-state'),
3940
'forbid-prop-types': require('./lib/rules/forbid-prop-types'),
4041
'prefer-es6-class': require('./lib/rules/prefer-es6-class'),
@@ -75,6 +76,7 @@ module.exports = {
7576
'jsx-indent-props': 0,
7677
'jsx-indent': 0,
7778
'jsx-closing-bracket-location': 0,
79+
'jsx-space-before-closing': 0,
7880
'no-direct-mutation-state': 0,
7981
'forbid-prop-types': 0,
8082
'prefer-es6-class': 0,

lib/rules/jsx-space-before-closing.js

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/**
2+
* @fileoverview Enforce a space before closing bracket of self-closing JSX elements.
3+
* @author ryym
4+
*/
5+
'use strict';
6+
7+
// ------------------------------------------------------------------------------
8+
// Rule Definition
9+
// ------------------------------------------------------------------------------
10+
11+
module.exports = function(context) {
12+
var sourceCode = context.getSourceCode();
13+
14+
/**
15+
* Find the token before the closing bracket.
16+
* @param {ASTNode} node - The JSX element node.
17+
* @returns {Token} The token before the closing bracket.
18+
*/
19+
function getTokenBeforeClosingBracket(node) {
20+
var attributes = node.attributes;
21+
if (attributes.length === 0) {
22+
return node.name;
23+
}
24+
return attributes[ attributes.length - 1 ];
25+
}
26+
27+
// --------------------------------------------------------------------------
28+
// Public
29+
// --------------------------------------------------------------------------
30+
31+
return {
32+
JSXOpeningElement: function(node) {
33+
if (!node.selfClosing) {
34+
return;
35+
}
36+
37+
var leftToken = getTokenBeforeClosingBracket(node);
38+
var closingSlash = sourceCode.getTokenAfter(leftToken);
39+
40+
if (sourceCode.isSpaceBetweenTokens(leftToken, closingSlash)) {
41+
return;
42+
}
43+
44+
context.report({
45+
loc: closingSlash.loc.start,
46+
message: 'A space is required before \'/>\'',
47+
fix: function(fixer) {
48+
return fixer.insertTextBefore(closingSlash, ' ');
49+
}
50+
});
51+
}
52+
};
53+
54+
};
55+
56+
module.exports.schema = [];
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
/**
2+
* @fileoverview Enforce a space before closing bracket of self-closing JSX elements.
3+
* @author ryym
4+
*/
5+
'use strict';
6+
7+
// ------------------------------------------------------------------------------
8+
// Requirements
9+
// ------------------------------------------------------------------------------
10+
11+
var rule = require('../../../lib/rules/jsx-space-before-closing');
12+
var RuleTester = require('eslint').RuleTester;
13+
14+
var parserOptions = {
15+
ecmaVersion: 6,
16+
ecmaFeatures: {
17+
jsx: true
18+
}
19+
};
20+
21+
// ------------------------------------------------------------------------------
22+
// Tests
23+
// ------------------------------------------------------------------------------
24+
25+
var ruleTester = new RuleTester();
26+
ruleTester.run('jsx-space-before-closing', rule, {
27+
valid: [{
28+
code: '<App />',
29+
parserOptions: parserOptions
30+
}, {
31+
code: '<App foo />',
32+
parserOptions: parserOptions
33+
}, {
34+
code: '<App foo={bar} />',
35+
parserOptions: parserOptions
36+
}, {
37+
code: '<App {...props} />',
38+
parserOptions: parserOptions
39+
}, {
40+
code: '<App></App>',
41+
parserOptions: parserOptions
42+
}, {
43+
code: [
44+
'<App',
45+
' foo={bar}',
46+
'/>'
47+
].join('\n'),
48+
parserOptions: parserOptions
49+
}],
50+
51+
invalid: [{
52+
code: '<App/>',
53+
output: '<App />',
54+
parserOptions: parserOptions,
55+
errors: [
56+
{message: 'A space is required before \'/>\''}
57+
]
58+
}, {
59+
code: '<App foo/>',
60+
output: '<App foo />',
61+
parserOptions: parserOptions,
62+
errors: [
63+
{message: 'A space is required before \'/>\''}
64+
]
65+
}, {
66+
code: '<App foo={bar}/>',
67+
output: '<App foo={bar} />',
68+
parserOptions: parserOptions,
69+
errors: [
70+
{message: 'A space is required before \'/>\''}
71+
]
72+
}, {
73+
code: '<App {...props}/>',
74+
output: '<App {...props} />',
75+
parserOptions: parserOptions,
76+
errors: [
77+
{message: 'A space is required before \'/>\''}
78+
]
79+
}]
80+
});
81+

0 commit comments

Comments
 (0)