-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
- Loading branch information
Showing
5 changed files
with
408 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
# Validate closing bracket location in JSX (jsx-closing-bracket-location) | ||
|
||
Enforce the closing bracket location for JSX multiline elements. | ||
|
||
## Rule Details | ||
|
||
This rule checks all JSX multiline elements and verifies the location of the closing bracket. By default this one must be aligned with the opening tag. | ||
|
||
The following patterns are considered warnings: | ||
|
||
```jsx | ||
<Hello | ||
lastName="Smith" | ||
firstName="John" />; | ||
|
||
<Hello | ||
lastName="Smith" | ||
firstName="John" | ||
/>; | ||
``` | ||
|
||
The following patterns are not considered warnings: | ||
|
||
```jsx | ||
<Hello firstName="John" lastName="Smith" />; | ||
|
||
<Hello | ||
firstName="John" | ||
lastName="Smith" | ||
/>; | ||
``` | ||
|
||
## Rule Options | ||
|
||
```js | ||
... | ||
"jsx-closing-bracket-location": [<enabled>, { "location": <string> }] | ||
... | ||
``` | ||
|
||
### `location` | ||
|
||
Enforced location for the closing bracket. | ||
|
||
* `tag-aligned`: must be aligned with the opening tag. | ||
* `after-props`: must be placed right after the last prop. | ||
* `props-aligned`: must be aligned with the last prop. | ||
|
||
Default to `tag-aligned`. | ||
|
||
The following patterns are considered warnings: | ||
|
||
```jsx | ||
// [1, {location: 'tag-aligned'}] | ||
<Hello | ||
firstName="John" | ||
lastName="Smith" | ||
/>; | ||
|
||
// [1, {location: 'after-props'}] | ||
<Hello | ||
firstName="John" | ||
lastName="Smith" | ||
/>; | ||
|
||
// [1, {location: 'props-aligned'}] | ||
<Hello | ||
firstName="John" | ||
lastName="Smith" />; | ||
``` | ||
|
||
The following patterns are not considered warnings: | ||
|
||
```jsx | ||
// [1, {location: 'tag-aligned'}] | ||
<Hello | ||
firstName="John" | ||
lastName="Smith" | ||
/>; | ||
|
||
// [1, {location: 'after-props'}] | ||
<Hello | ||
firstName="John" | ||
lastName="Smith" />; | ||
|
||
// [1, {location: 'props-aligned'}] | ||
<Hello | ||
firstName="John" | ||
lastName="Smith" | ||
/>; | ||
``` | ||
|
||
## When not to use | ||
|
||
If you are not using JSX then you can disable this rule. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
/** | ||
* @fileoverview Validate closing bracket location in JSX | ||
* @author Yannick Croissant | ||
*/ | ||
'use strict'; | ||
|
||
// ------------------------------------------------------------------------------ | ||
// Rule Definition | ||
// ------------------------------------------------------------------------------ | ||
module.exports = function(context) { | ||
|
||
var MESSAGE = 'The closing bracket must be {{location}}'; | ||
var MESSAGE_LOCATION = { | ||
'after-props': 'placed after the last prop', | ||
'after-tag': 'placed after the opening tag', | ||
'props-aligned': 'aligned with the last prop', | ||
'tag-aligned': 'aligned with the opening tag' | ||
}; | ||
|
||
/** | ||
* Get expected location for the closing bracket | ||
* @param {Object} tokens Locations of the opening bracket, closing bracket and last prop | ||
* @return {String} Expected location for the closing bracket | ||
*/ | ||
function getExpectedLocation(tokens) { | ||
var location; | ||
// Is always after the opening tag if there is no props | ||
if (typeof tokens.lastProp === 'undefined') { | ||
location = 'after-tag'; | ||
// Is always after the last prop if this one is on the same line as the opening bracket | ||
} else if (tokens.opening.line === tokens.lastProp.line) { | ||
location = 'after-props'; | ||
// Else use configuration, or default value | ||
} else { | ||
location = context.options[0] && context.options[0].location || 'tag-aligned'; | ||
} | ||
return location; | ||
} | ||
|
||
/** | ||
* Check if the closing bracket is correctly located | ||
* @param {Object} tokens Locations of the opening bracket, closing bracket and last prop | ||
* @param {String} expectedLocation Expected location for the closing bracket | ||
* @return {Boolean} True if the closing bracket is correctly located, false if not | ||
*/ | ||
function hasCorrectLocation(tokens, expectedLocation) { | ||
switch (expectedLocation) { | ||
case 'after-tag': | ||
return tokens.tag.line === tokens.closing.line; | ||
case 'after-props': | ||
return tokens.lastProp.line === tokens.closing.line; | ||
case 'props-aligned': | ||
return tokens.lastProp.column === tokens.closing.column; | ||
case 'tag-aligned': | ||
return tokens.opening.column === tokens.closing.column; | ||
default: | ||
return true; | ||
} | ||
} | ||
|
||
/** | ||
* Get the locations of the opening bracket, closing bracket and last prop | ||
* @param {ASTNode} node The node to check | ||
* @return {Object} Locations of the opening bracket, closing bracket and last prop | ||
*/ | ||
function getTokensLocations(node) { | ||
var opening = context.getFirstToken(node).loc.start; | ||
var closing = context.getLastTokens(node, node.selfClosing ? 2 : 1)[0].loc.start; | ||
var tag = context.getFirstToken(node.name).loc.start; | ||
var lastProp; | ||
if (node.attributes.length) { | ||
lastProp = context.getFirstToken(node.attributes[node.attributes.length - 1]).loc.start; | ||
} | ||
return { | ||
tag: tag, | ||
opening: opening, | ||
closing: closing, | ||
lastProp: lastProp | ||
}; | ||
} | ||
|
||
return { | ||
JSXOpeningElement: function(node) { | ||
var tokens = getTokensLocations(node); | ||
var expectedLocation = getExpectedLocation(tokens); | ||
if (hasCorrectLocation(tokens, expectedLocation)) { | ||
return; | ||
} | ||
context.report(node, MESSAGE, { | ||
location: MESSAGE_LOCATION[expectedLocation] | ||
}); | ||
} | ||
}; | ||
|
||
}; | ||
|
||
module.exports.schema = [{ | ||
type: 'object', | ||
properties: { | ||
location: { | ||
enum: ['after-props', 'props-aligned', 'tag-aligned'] | ||
} | ||
} | ||
}]; |
Oops, something went wrong.