Skip to content

Commit 250b9eb

Browse files
author
Yannick Croissant
committed
Add jsx-curly-spacing rule (fixes #142)
1 parent 81bde27 commit 250b9eb

File tree

5 files changed

+294
-0
lines changed

5 files changed

+294
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ Finally, enable all of the rules that you would like to use.
6868

6969
* [display-name](docs/rules/display-name.md): Prevent missing displayName in a React component definition
7070
* [jsx-boolean-value](docs/rules/jsx-boolean-value.md): Enforce boolean attributes notation in JSX
71+
* [jsx-curly-spacing](docs/rules/jsx-curly-spacing.md): Enforce or disallow spaces inside of curly braces in JSX attributes
7172
* [jsx-no-undef](docs/rules/jsx-no-undef.md): Disallow undeclared variables in JSX
7273
* [jsx-quotes](docs/rules/jsx-quotes.md): Enforce quote style for JSX attributes
7374
* [jsx-sort-prop-types](docs/rules/jsx-sort-prop-types.md): Enforce propTypes declarations alphabetical sorting

docs/rules/jsx-curly-spacing.md

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
# Enforce or disallow spaces inside of curly braces in JSX attributes. (jsx-curly-spacing)
2+
3+
While formatting preferences are very personal, a number of style guides require or disallow spaces between curly braces.
4+
5+
## Rule Details
6+
7+
This rule aims to maintain consistency around the spacing inside of JSX attributes.
8+
9+
It either requires or disallows spaces between those braces and the values inside of them.
10+
11+
### Options
12+
13+
There are two main options for the rule:
14+
15+
* `"always"` enforces a space inside of curly braces
16+
* `"never"` disallows spaces inside of curly braces (default)
17+
18+
Depending on your coding conventions, you can choose either option by specifying it in your configuration:
19+
20+
```json
21+
"jsx-curly-spacing": [2, "always"]
22+
```
23+
24+
#### never
25+
26+
When `"never"` is set, the following patterns are considered warnings:
27+
28+
```js
29+
<Hello name={ firstname } />;
30+
<Hello name={ firstname} />;
31+
<Hello name={firstname } />;
32+
<Hello name={
33+
firstname
34+
} />;
35+
```
36+
37+
The following patterns are not warnings:
38+
39+
```js
40+
<Hello name={firstname} />;
41+
<Hello name={{ firstname: 'John', lastname: 'Doe' }} />;
42+
```
43+
44+
#### always
45+
46+
When `"always"` is used, the following patterns are considered warnings:
47+
48+
```js
49+
<Hello name={firstname} />;
50+
<Hello name={ firstname} />;
51+
<Hello name={firstname } />;
52+
```
53+
54+
The following patterns are not warnings:
55+
56+
```js
57+
<Hello name={ firstname } />;
58+
<Hello name={ {firstname: 'John', lastname: 'Doe'} } />;
59+
<Hello name={
60+
firstname
61+
} />;
62+
```
63+
64+
## When Not To Use It
65+
66+
You can turn this rule off if you are not concerned with the consistency around the spacing inside of JSX attributes.
67+

index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ module.exports = {
1616
'jsx-no-undef': require('./lib/rules/jsx-no-undef'),
1717
'jsx-quotes': require('./lib/rules/jsx-quotes'),
1818
'no-unknown-property': require('./lib/rules/no-unknown-property'),
19+
'jsx-curly-spacing': require('./lib/rules/jsx-curly-spacing'),
1920
'jsx-sort-props': require('./lib/rules/jsx-sort-props'),
2021
'jsx-sort-prop-types': require('./lib/rules/jsx-sort-prop-types'),
2122
'jsx-boolean-value': require('./lib/rules/jsx-boolean-value'),
@@ -37,6 +38,7 @@ module.exports = {
3738
'jsx-no-undef': 0,
3839
'jsx-quotes': 0,
3940
'no-unknown-property': 0,
41+
'jsx-curly-spacing': 0,
4042
'jsx-sort-props': 0,
4143
'jsx-sort-prop-types': 0,
4244
'jsx-boolean-value': 0,

lib/rules/jsx-curly-spacing.js

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
/**
2+
* @fileoverview Enforce or disallow spaces inside of curly braces in JSX attributes.
3+
* @author Jamund Ferguson, Brandyn Bennett, Michael Ficarra, Vignesh Anand, Jamund Ferguson, Yannick Croissant
4+
*/
5+
'use strict';
6+
7+
// ------------------------------------------------------------------------------
8+
// Rule Definition
9+
// ------------------------------------------------------------------------------
10+
11+
module.exports = function(context) {
12+
var spaced = context.options[0] === 'always';
13+
14+
// --------------------------------------------------------------------------
15+
// Helpers
16+
// --------------------------------------------------------------------------
17+
18+
/**
19+
* Determines whether two adjacent tokens are have whitespace between them.
20+
* @param {Object} left - The left token object.
21+
* @param {Object} right - The right token object.
22+
* @returns {boolean} Whether or not there is space between the tokens.
23+
*/
24+
function isSpaced(left, right) {
25+
return left.range[1] < right.range[0];
26+
}
27+
28+
/**
29+
* Reports that there shouldn't be a space after the first token
30+
* @param {ASTNode} node - The node to report in the event of an error.
31+
* @param {Token} token - The token to use for the report.
32+
* @returns {void}
33+
*/
34+
function reportNoBeginningSpace(node, token) {
35+
context.report(node, token.loc.start,
36+
'There should be no space after \'' + token.value + '\'');
37+
}
38+
39+
/**
40+
* Reports that there shouldn't be a space before the last token
41+
* @param {ASTNode} node - The node to report in the event of an error.
42+
* @param {Token} token - The token to use for the report.
43+
* @returns {void}
44+
*/
45+
function reportNoEndingSpace(node, token) {
46+
context.report(node, token.loc.start,
47+
'There should be no space before \'' + token.value + '\'');
48+
}
49+
50+
/**
51+
* Reports that there should be a space after the first token
52+
* @param {ASTNode} node - The node to report in the event of an error.
53+
* @param {Token} token - The token to use for the report.
54+
* @returns {void}
55+
*/
56+
function reportRequiredBeginningSpace(node, token) {
57+
context.report(node, token.loc.start,
58+
'A space is required after \'' + token.value + '\'');
59+
}
60+
61+
/**
62+
* Reports that there should be a space before the last token
63+
* @param {ASTNode} node - The node to report in the event of an error.
64+
* @param {Token} token - The token to use for the report.
65+
* @returns {void}
66+
*/
67+
function reportRequiredEndingSpace(node, token) {
68+
context.report(node, token.loc.start,
69+
'A space is required before \'' + token.value + '\'');
70+
}
71+
72+
/**
73+
* Determines if spacing in curly braces is valid.
74+
* @param {ASTNode} node The AST node to check.
75+
* @param {Token} first The first token to check (should be the opening brace)
76+
* @param {Token} second The second token to check (should be first after the opening brace)
77+
* @param {Token} penultimate The penultimate token to check (should be last before closing brace)
78+
* @param {Token} last The last token to check (should be closing brace)
79+
* @returns {void}
80+
*/
81+
function validateBraceSpacing(node, first, second, penultimate, last) {
82+
if (spaced && !isSpaced(first, second)) {
83+
reportRequiredBeginningSpace(node, first);
84+
}
85+
if (!spaced && isSpaced(first, second)) {
86+
reportNoBeginningSpace(node, first);
87+
}
88+
if (spaced && !isSpaced(penultimate, last)) {
89+
reportRequiredEndingSpace(node, last);
90+
}
91+
if (!spaced && isSpaced(penultimate, last)) {
92+
reportNoEndingSpace(node, last);
93+
}
94+
}
95+
96+
// --------------------------------------------------------------------------
97+
// Public
98+
// --------------------------------------------------------------------------
99+
100+
return {
101+
JSXExpressionContainer: function(node) {
102+
var first = context.getFirstToken(node);
103+
var second = context.getFirstToken(node, 1);
104+
var penultimate = context.getLastToken(node, 1);
105+
var last = context.getLastToken(node);
106+
107+
validateBraceSpacing(node, first, second, penultimate, last);
108+
}
109+
};
110+
};
111+
112+
module.exports.schema = [{
113+
enum: ['always', 'never']
114+
}];

tests/lib/rules/jsx-curly-spacing.js

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
/**
2+
* @fileoverview Enforce or disallow spaces inside of curly braces in JSX attributes.
3+
* @author Yannick Croissant
4+
*/
5+
'use strict';
6+
7+
// ------------------------------------------------------------------------------
8+
// Requirements
9+
// ------------------------------------------------------------------------------
10+
11+
var eslint = require('eslint').linter;
12+
var ESLintTester = require('eslint-tester');
13+
14+
// ------------------------------------------------------------------------------
15+
// Tests
16+
// ------------------------------------------------------------------------------
17+
18+
var eslintTester = new ESLintTester(eslint);
19+
eslintTester.addRuleTest('lib/rules/jsx-curly-spacing', {
20+
valid: [{
21+
code: '<App foo={bar} />;',
22+
args: 1,
23+
ecmaFeatures: {jsx: true}
24+
}, {
25+
code: '<App foo={bar} />;',
26+
args: [1, 'never'],
27+
ecmaFeatures: {jsx: true}
28+
}, {
29+
code: '<App foo={ bar } />;',
30+
args: [1, 'always'],
31+
ecmaFeatures: {jsx: true}
32+
}, {
33+
code: '<App foo={{ bar:baz }} />;',
34+
args: [1, 'never'],
35+
ecmaFeatures: {jsx: true}
36+
}, {
37+
code: '<App foo={ {bar:baz} } />;',
38+
args: [1, 'always'],
39+
ecmaFeatures: {jsx: true}
40+
}, {
41+
code: [
42+
'<App foo={',
43+
'bar',
44+
'} />;'
45+
].join('\n'),
46+
args: [1, 'always'],
47+
ecmaFeatures: {jsx: true}
48+
}],
49+
50+
invalid: [{
51+
code: '<App foo={ bar } />;',
52+
args: [1, 'never'],
53+
errors: [{
54+
message: 'There should be no space after \'{\''
55+
}, {
56+
message: 'There should be no space before \'}\''
57+
}],
58+
ecmaFeatures: {jsx: true}
59+
}, {
60+
code: '<App foo={bar} />;',
61+
args: [1, 'always'],
62+
errors: [{
63+
message: 'A space is required after \'{\''
64+
}, {
65+
message: 'A space is required before \'}\''
66+
}],
67+
ecmaFeatures: {jsx: true}
68+
}, {
69+
code: '<App foo={ bar} />;',
70+
args: [1, 'always'],
71+
errors: [{
72+
message: 'A space is required before \'}\''
73+
}],
74+
ecmaFeatures: {jsx: true}
75+
}, {
76+
code: '<App foo={bar } />;',
77+
args: [1, 'always'],
78+
errors: [{
79+
message: 'A space is required after \'{\''
80+
}],
81+
ecmaFeatures: {jsx: true}
82+
}, {
83+
code: '<App foo={ bar} />;',
84+
args: [1, 'never'],
85+
errors: [{
86+
message: 'There should be no space after \'{\''
87+
}],
88+
ecmaFeatures: {jsx: true}
89+
}, {
90+
code: '<App foo={bar } />;',
91+
args: [1, 'never'],
92+
errors: [{
93+
message: 'There should be no space before \'}\''
94+
}],
95+
ecmaFeatures: {jsx: true}
96+
}, {
97+
code: [
98+
'<App foo={',
99+
'bar',
100+
'} />;'
101+
].join('\n'),
102+
args: [1, 'never'],
103+
errors: [{
104+
message: 'There should be no space after \'{\''
105+
}, {
106+
message: 'There should be no space before \'}\''
107+
}],
108+
ecmaFeatures: {jsx: true}
109+
}]
110+
});

0 commit comments

Comments
 (0)