Skip to content

Commit 73c86c9

Browse files
author
CJ Skillingstad
committed
Add beforeClosing option to jsx-tag-spacing
1 parent 07345b4 commit 73c86c9

File tree

2 files changed

+105
-5
lines changed

2 files changed

+105
-5
lines changed

docs/rules/jsx-tag-spacing.md

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,23 @@
11
# Validate whitespace in and around the JSX opening and closing brackets (react/jsx-tag-spacing)
22

3-
Enforce or forbid spaces after the opening bracket, before the closing bracket of self-closing elements, and between the angle bracket and slash of JSX closing or self-closing elements.
3+
Enforce or forbid spaces after the opening bracket, before the closing bracket, before the closing bracket of self-closing elements, and between the angle bracket and slash of JSX closing or self-closing elements.
44

55
**Fixable:** This rule is automatically fixable using the `--fix` flag on the command line.
66

77
## Rule Details
88

99
This rule checks the whitespace inside and surrounding the JSX syntactic elements.
1010

11-
This rule takes one argument, an object with 3 possible keys: `closingSlash`, `beforeSelfClosing` and `afterOpening`. Each key can receive the value `"allow"` to disable that specific check.
11+
This rule takes one argument, an object with 4 possible keys: `closingSlash`, `beforeSelfClosing`, `afterOpening`, and `beforeClosing`. Each key can receive the value `"allow"` to disable that specific check.
1212

1313
The default values are:
1414

1515
```json
1616
{
1717
"closingSlash": "never",
1818
"beforeSelfClosing": "always",
19-
"afterOpening": "never"
19+
"afterOpening": "never",
20+
"beforeClosing": "never"
2021
}
2122
```
2223

@@ -176,6 +177,48 @@ The following patterns are **not** considered warnings when configured `"allow-m
176177
/>
177178
```
178179

180+
### `beforeClosing`
181+
182+
This check can be set to `"always"`, `"never"`, or `"allow"` (to disable it).
183+
184+
If it is `"always"` the check warns whenever whitespace is missing before the closing bracket of a JSX opening element or whenever a space is missing before the closing bracket closing element. If `"never"`, them it warns if a space is present before the closing bracket of either a JSX opening element or closing element. This rule will never warn for self closing JSX elements. The default value of this check is `"never"`.
185+
186+
The following patterns are considered warnings when configured `"always"`:
187+
188+
```jsx
189+
<Hello></Hello>
190+
<Hello></Hello >
191+
<Hello ></Hello>
192+
```
193+
194+
The following patterns are **not** considered warnings when configured `"always"`:
195+
196+
```jsx
197+
<Hello ></Hello >
198+
<Hello
199+
firstName="John"
200+
>
201+
</Hello >
202+
```
203+
204+
The following patterns are considered warnings when configured `"never"`:
205+
206+
```jsx
207+
<Hello ></Hello>
208+
<Hello></Hello >
209+
<Hello ></Hello >
210+
```
211+
212+
The following patterns are **not** considered warnings when configured `"never"`:
213+
214+
```jsx
215+
<Hello></Hello>
216+
<Hello
217+
firstName="John"
218+
>
219+
</Hello>
220+
```
221+
179222
## When Not To Use It
180223

181224
You can turn this rule off if you are not concerned with the consistency of spacing in or around JSX brackets.

lib/rules/jsx-tag-spacing.js

Lines changed: 59 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,52 @@ function validateAfterOpening(context, node, option) {
169169
}
170170
}
171171

172+
function validateBeforeClosing(context, node, option) {
173+
// Don't enforce this rule for self closing tags
174+
if (!node.selfClosing) {
175+
const sourceCode = context.getSourceCode();
176+
177+
const NEVER_MESSAGE = 'A space is forbidden before closing bracket';
178+
const ALWAYS_MESSAGE = 'Whitespace is required before closing bracket';
179+
180+
const lastTokens = sourceCode.getLastTokens(node, 2);
181+
const closingToken = lastTokens[1];
182+
const leftToken = lastTokens[0];
183+
184+
if (leftToken.loc.start.line !== closingToken.loc.start.line) {
185+
return;
186+
}
187+
188+
const adjacent = !sourceCode.isSpaceBetweenTokens(leftToken, closingToken);
189+
190+
if (option === 'never' && !adjacent) {
191+
context.report({
192+
node: node,
193+
loc: {
194+
start: leftToken.loc.end,
195+
end: closingToken.loc.start
196+
},
197+
message: NEVER_MESSAGE,
198+
fix: function(fixer) {
199+
return fixer.removeRange([leftToken.range[1], closingToken.range[0]]);
200+
}
201+
});
202+
} else if (option === 'always' && adjacent) {
203+
context.report({
204+
node: node,
205+
loc: {
206+
start: leftToken.loc.end,
207+
end: closingToken.loc.start
208+
},
209+
message: ALWAYS_MESSAGE,
210+
fix: function(fixer) {
211+
return fixer.insertTextBefore(closingToken, ' ');
212+
}
213+
});
214+
}
215+
}
216+
}
217+
172218
// ------------------------------------------------------------------------------
173219
// Rule Definition
174220
// ------------------------------------------------------------------------------
@@ -191,12 +237,16 @@ module.exports = {
191237
},
192238
afterOpening: {
193239
enum: ['always', 'allow-multiline', 'never', 'allow']
240+
},
241+
beforeClosing: {
242+
enum: ['always', 'never', 'allow']
194243
}
195244
},
196245
default: {
197246
closingSlash: 'never',
198247
beforeSelfClosing: 'always',
199-
afterOpening: 'never'
248+
afterOpening: 'never',
249+
beforeClosing: 'never'
200250
},
201251
additionalProperties: false
202252
}
@@ -206,7 +256,8 @@ module.exports = {
206256
const options = {
207257
closingSlash: 'never',
208258
beforeSelfClosing: 'always',
209-
afterOpening: 'never'
259+
afterOpening: 'never',
260+
beforeClosing: 'never'
210261
};
211262
for (const key in options) {
212263
if (has(options, key) && has(context.options[0] || {}, key)) {
@@ -225,6 +276,9 @@ module.exports = {
225276
if (options.beforeSelfClosing !== 'allow' && node.selfClosing) {
226277
validateBeforeSelfClosing(context, node, options.beforeSelfClosing);
227278
}
279+
if (options.beforeClosing !== 'allow') {
280+
validateBeforeClosing(context, node, options.beforeClosing);
281+
}
228282
},
229283
JSXClosingElement: function (node) {
230284
if (options.afterOpening !== 'allow') {
@@ -233,6 +287,9 @@ module.exports = {
233287
if (options.closingSlash !== 'allow') {
234288
validateClosingSlash(context, node, options.closingSlash);
235289
}
290+
if (options.beforeClosing !== 'allow') {
291+
validateBeforeClosing(context, node, options.beforeClosing);
292+
}
236293
}
237294
};
238295
}

0 commit comments

Comments
 (0)