Skip to content

Commit 7d0e187

Browse files
committed
Add jsx-sort-props rule (fixes #16)
1 parent 216af61 commit 7d0e187

File tree

5 files changed

+133
-2
lines changed

5 files changed

+133
-2
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ Finally, enable all of the rules that you would like to use.
4545
"react/display-name": 1,
4646
"react/jsx-quotes": 1,
4747
"react/jsx-no-undef": 1,
48+
"react/jsx-sort-props": 1,
4849
"react/jsx-uses-react": 1,
4950
"react/jsx-uses-vars": 1,
5051
"react/no-did-mount-set-state": 1,
@@ -64,6 +65,7 @@ Finally, enable all of the rules that you would like to use.
6465
* [display-name](docs/rules/display-name.md): Prevent missing displayName in a React component definition
6566
* [jsx-quotes](docs/rules/jsx-quotes.md): Enforce quote style for JSX attributes
6667
* [jsx-no-undef](docs/rules/jsx-no-undef.md): Disallow undeclared variables in JSX
68+
* [jsx-sort-props](docs/rules/jsx-sort-props.md): Enforce props alphabetical sorting
6769
* [jsx-uses-react](docs/rules/jsx-uses-react.md): Prevent React to be incorrectly marked as unused
6870
* [jsx-uses-vars](docs/rules/jsx-uses-vars.md): Prevent variables used in JSX to be incorrectly marked as unused
6971
* [no-did-mount-set-state](docs/rules/no-did-mount-set-state.md): Prevent usage of setState in componentDidMount

docs/rules/jsx-sort-props.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# Enforce props alphabetical sorting (jsx-sort-props)
2+
3+
Some developers prefer to sort props names alphabetically to be able to find necessary props easier at the later time. Others feel that it adds complexity and becomes burden to maintain.
4+
5+
## Rule Details
6+
7+
This rule checks all JSX components and verifies that all props are sorted alphabetically. This rule is off by default.
8+
The default configuration of the rule is case-sensitive.
9+
10+
The following patterns are considered warnings:
11+
12+
```js
13+
<Hello lastName="Smith" firstName="John" />;
14+
```
15+
16+
The following patterns are considered okay and do not cause warnings:
17+
18+
```js
19+
<Hello firstName="John" lastName="Smith" />;
20+
```
21+
22+
## Rule Options
23+
24+
```js
25+
...
26+
"jsx-sort-props": [<enabled>, { "ignoreCase": <boolean> }]
27+
...
28+
```
29+
30+
### `ignoreCase`
31+
32+
When `true` the rule ignores the case-sensitivity of the props order.
33+
34+
The following patterns are considered okay and do not cause warnings:
35+
36+
```js
37+
<Hello name="John" Number="2" />;
38+
```
39+
40+
## When not to use
41+
42+
This rule is a formatting preference and not following it won't negatively affect the quality of your code. If you alphabetizing props isn't a part of your coding standards, then you can leave this rule off.

index.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ module.exports = {
1414
'jsx-uses-vars': require('./lib/rules/jsx-uses-vars'),
1515
'jsx-no-undef': require('./lib/rules/jsx-no-undef'),
1616
'jsx-quotes': require('./lib/rules/jsx-quotes'),
17-
'no-unknown-property': require('./lib/rules/no-unknown-property')
17+
'no-unknown-property': require('./lib/rules/no-unknown-property'),
18+
'jsx-sort-props': require('./lib/rules/jsx-sort-props')
1819
},
1920
rulesConfig: {
2021
'jsx-uses-react': 0,
@@ -29,6 +30,7 @@ module.exports = {
2930
'jsx-uses-vars': 0,
3031
'jsx-no-undef': 0,
3132
'jsx-quotes': 0,
32-
'no-unknown-property': 0
33+
'no-unknown-property': 0,
34+
'jsx-sort-props': 0
3335
}
3436
};

lib/rules/jsx-sort-props.js

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/**
2+
* @fileoverview Enforce props alphabetical sorting
3+
* @author Ilya Volodin, Yannick Croissant
4+
*/
5+
'use strict';
6+
7+
// ------------------------------------------------------------------------------
8+
// Rule Definition
9+
// ------------------------------------------------------------------------------
10+
11+
module.exports = function(context) {
12+
13+
var configuration = context.options[0] || {};
14+
var ignoreCase = configuration.ignoreCase || false;
15+
16+
return {
17+
JSXOpeningElement: function(node) {
18+
node.attributes.reduce(function(memo, decl) {
19+
var lastPropName = memo.name.name;
20+
var currenPropName = decl.name.name;
21+
22+
if (ignoreCase) {
23+
lastPropName = lastPropName.toLowerCase();
24+
currenPropName = currenPropName.toLowerCase();
25+
}
26+
27+
if (currenPropName < lastPropName) {
28+
context.report(decl, 'Props should be sorted alphabetically');
29+
return memo;
30+
}
31+
32+
return decl;
33+
}, node.attributes[0]);
34+
}
35+
};
36+
};

tests/lib/rules/jsx-sort-props.js

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/**
2+
* @fileoverview Enforce props alphabetical sorting
3+
* @author Yannick Croissant
4+
*/
5+
6+
'use strict';
7+
8+
// -----------------------------------------------------------------------------
9+
// Requirements
10+
// -----------------------------------------------------------------------------
11+
12+
var eslint = require('eslint').linter;
13+
var ESLintTester = require('eslint-tester');
14+
15+
// -----------------------------------------------------------------------------
16+
// Tests
17+
// -----------------------------------------------------------------------------
18+
19+
var eslintTester = new ESLintTester(eslint);
20+
21+
var expectedError = {
22+
message: 'Props should be sorted alphabetically',
23+
type: 'JSXAttribute'
24+
};
25+
var ignoreCaseArgs = [1, {
26+
ignoreCase: true
27+
}];
28+
var features = {
29+
jsx: true
30+
};
31+
32+
eslintTester.addRuleTest('lib/rules/jsx-sort-props', {
33+
valid: [
34+
{code: '<App />;', ecmaFeatures: features},
35+
{code: '<App a b c />;', ecmaFeatures: features},
36+
{code: '<App a="c" b="b" c="a" />;', ecmaFeatures: features},
37+
{code: '<App A a />;', ecmaFeatures: features},
38+
{code: '<App a A />;', args: ignoreCaseArgs, ecmaFeatures: features},
39+
{code: '<App a B c />;', args: ignoreCaseArgs, ecmaFeatures: features},
40+
{code: '<App A b C />;', args: ignoreCaseArgs, ecmaFeatures: features}
41+
],
42+
invalid: [
43+
{code: '<App b a />;', errors: [expectedError], ecmaFeatures: features},
44+
{code: '<App a A />;', errors: [expectedError], ecmaFeatures: features},
45+
{code: '<App B a />;', args: ignoreCaseArgs, errors: [expectedError], ecmaFeatures: features},
46+
{code: '<App B A c />;', args: ignoreCaseArgs, errors: [expectedError], ecmaFeatures: features},
47+
{code: '<App c="a" a="c" b="b" />;', errors: 2, ecmaFeatures: features}
48+
]
49+
});

0 commit comments

Comments
 (0)