Skip to content

Commit c139931

Browse files
committed
Merge branch 'lencioni-sort-prop-types-rename'
2 parents 003eed4 + 7bf2b72 commit c139931

File tree

6 files changed

+786
-140
lines changed

6 files changed

+786
-140
lines changed

docs/rules/jsx-sort-prop-types.md renamed to docs/rules/sort-prop-types.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
# Enforce propTypes declarations alphabetical sorting (jsx-sort-prop-types)
1+
# Enforce propTypes declarations alphabetical sorting (sort-prop-types)
22

3-
Some developers prefer to sort propsTypes declarations alphabetically to be able to find necessary declaration easier at the later time. Others feel that it adds complexity and becomes burden to maintain.
3+
Some developers prefer to sort propTypes declarations alphabetically to be able to find necessary declaration easier at the later time. Others feel that it adds complexity and becomes burden to maintain.
44

55
## Rule Details
66

7-
This rule checks all JSX components and verifies that all propsTypes declarations are sorted alphabetically.
7+
This rule checks all components and verifies that all propTypes declarations are sorted alphabetically.
88
The default configuration of the rule is case-sensitive.
99
This rule is off by default.
1010

@@ -78,7 +78,7 @@ class Component extends React.Component {
7878

7979
```js
8080
...
81-
"jsx-sort-prop-types": [<enabled>, {
81+
"sort-prop-types": [<enabled>, {
8282
"callbacksLast": <boolean>,
8383
"ignoreCase": <boolean>,
8484
"requiredFirst": <boolean>,
@@ -92,7 +92,7 @@ When `true` the rule ignores the case-sensitivity of the declarations order.
9292

9393
### `callbacksLast`
9494

95-
When `true`, prop types for props beginning with "on" must be listed after all other props:
95+
When `true`, propTypes for props beginning with "on" must be listed after all other props:
9696

9797
```js
9898
var Component = React.createClass({

index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ module.exports = {
2525
'jsx-curly-spacing': require('./lib/rules/jsx-curly-spacing'),
2626
'jsx-equals-spacing': require('./lib/rules/jsx-equals-spacing'),
2727
'jsx-sort-props': require('./lib/rules/jsx-sort-props'),
28+
'sort-prop-types': require('./lib/rules/sort-prop-types'),
2829
'jsx-sort-prop-types': require('./lib/rules/jsx-sort-prop-types'),
2930
'jsx-boolean-value': require('./lib/rules/jsx-boolean-value'),
3031
'sort-comp': require('./lib/rules/sort-comp'),
@@ -66,6 +67,7 @@ module.exports = {
6667
'jsx-curly-spacing': 0,
6768
'jsx-equals-spacing': 0,
6869
'jsx-sort-props': 0,
70+
'sort-prop-types': 0,
6971
'jsx-sort-prop-types': 0,
7072
'jsx-boolean-value': 0,
7173
'sort-comp': 0,

lib/rules/jsx-sort-prop-types.js

Lines changed: 15 additions & 133 deletions
Original file line numberDiff line numberDiff line change
@@ -1,149 +1,31 @@
11
/**
22
* @fileoverview Enforce propTypes declarations alphabetical sorting
3+
* @deprecated
34
*/
45
'use strict';
56

67
// ------------------------------------------------------------------------------
78
// Rule Definition
89
// ------------------------------------------------------------------------------
910

10-
module.exports = function(context) {
11-
12-
var configuration = context.options[0] || {};
13-
var requiredFirst = configuration.requiredFirst || false;
14-
var callbacksLast = configuration.callbacksLast || false;
15-
var ignoreCase = configuration.ignoreCase || false;
16-
17-
/**
18-
* Checks if node is `propTypes` declaration
19-
* @param {ASTNode} node The AST node being checked.
20-
* @returns {Boolean} True if node is `propTypes` declaration, false if not.
21-
*/
22-
function isPropTypesDeclaration(node) {
23-
24-
// Special case for class properties
25-
// (babel-eslint does not expose property name so we have to rely on tokens)
26-
if (node.type === 'ClassProperty') {
27-
var tokens = context.getFirstTokens(node, 2);
28-
return (tokens[0] && tokens[0].value === 'propTypes') ||
29-
(tokens[1] && tokens[1].value === 'propTypes');
30-
}
31-
32-
return Boolean(
33-
node &&
34-
node.name === 'propTypes'
35-
);
36-
}
37-
38-
function getKey(node) {
39-
return node.key.type === 'Identifier' ? node.key.name : node.key.value;
40-
}
11+
var util = require('util');
12+
var sortPropTypes = require('./sort-prop-types');
13+
var isWarnedForDeprecation = false;
4114

42-
function getValueName(node) {
43-
return node.value.property && node.value.property.name;
44-
}
45-
46-
function isCallbackPropName(propName) {
47-
return /^on[A-Z]/.test(propName);
48-
}
49-
50-
function isRequiredProp(node) {
51-
return getValueName(node) === 'isRequired';
52-
}
53-
54-
/**
55-
* Checks if propTypes declarations are sorted
56-
* @param {Array} declarations The array of AST nodes being checked.
57-
* @returns {void}
58-
*/
59-
function checkSorted(declarations) {
60-
declarations.reduce(function(prev, curr) {
61-
var prevPropName = getKey(prev);
62-
var currentPropName = getKey(curr);
63-
var previousIsRequired = isRequiredProp(prev);
64-
var currentIsRequired = isRequiredProp(curr);
65-
var previousIsCallback = isCallbackPropName(prevPropName);
66-
var currentIsCallback = isCallbackPropName(currentPropName);
67-
68-
if (ignoreCase) {
69-
prevPropName = prevPropName.toLowerCase();
70-
currentPropName = currentPropName.toLowerCase();
71-
}
72-
73-
if (requiredFirst) {
74-
if (previousIsRequired && !currentIsRequired) {
75-
// Transition between required and non-required. Don't compare for alphabetical.
76-
return curr;
77-
}
78-
if (!previousIsRequired && currentIsRequired) {
79-
// Encountered a non-required prop after a required prop
80-
context.report({
81-
node: curr,
82-
message: 'Required prop types must be listed before all other prop types'
83-
});
84-
return curr;
85-
}
86-
}
87-
88-
if (callbacksLast) {
89-
if (!previousIsCallback && currentIsCallback) {
90-
// Entering the callback prop section
91-
return curr;
92-
}
93-
if (previousIsCallback && !currentIsCallback) {
94-
// Encountered a non-callback prop after a callback prop
95-
context.report({
96-
node: prev,
97-
message: 'Callback prop types must be listed after all other prop types'
98-
});
99-
return prev;
100-
}
101-
}
102-
103-
if (currentPropName < prevPropName) {
104-
context.report({
105-
node: curr,
106-
message: 'Prop types declarations should be sorted alphabetically'
107-
});
108-
return prev;
109-
}
110-
111-
return curr;
112-
}, declarations[0]);
113-
}
114-
115-
return {
116-
ClassProperty: function(node) {
117-
if (isPropTypesDeclaration(node) && node.value && node.value.type === 'ObjectExpression') {
118-
checkSorted(node.value.properties);
119-
}
120-
},
121-
122-
MemberExpression: function(node) {
123-
if (isPropTypesDeclaration(node.property)) {
124-
var right = node.parent.right;
125-
if (right && right.type === 'ObjectExpression') {
126-
checkSorted(right.properties);
127-
}
15+
module.exports = function(context) {
16+
return util._extend(sortPropTypes(context), {
17+
Program: function() {
18+
if (isWarnedForDeprecation || /\=-(f|-format)=/.test(process.argv.join('='))) {
19+
return;
12820
}
129-
},
130-
131-
ObjectExpression: function(node) {
132-
node.properties.forEach(function(property) {
133-
if (!property.key) {
134-
return;
135-
}
13621

137-
if (!isPropTypesDeclaration(property.key)) {
138-
return;
139-
}
140-
if (property.value.type === 'ObjectExpression') {
141-
checkSorted(property.value.properties);
142-
}
143-
});
22+
/* eslint-disable no-console */
23+
console.log('The react/jsx-sort-prop-types rule is deprecated. Please ' +
24+
'use the react/sort-prop-types rule instead.');
25+
/* eslint-enable no-console */
26+
isWarnedForDeprecation = true;
14427
}
145-
146-
};
28+
});
14729
};
14830

14931
module.exports.schema = [{

lib/rules/sort-prop-types.js

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
/**
2+
* @fileoverview Enforce propTypes declarations alphabetical sorting
3+
*/
4+
'use strict';
5+
6+
// ------------------------------------------------------------------------------
7+
// Rule Definition
8+
// ------------------------------------------------------------------------------
9+
10+
module.exports = function(context) {
11+
12+
var configuration = context.options[0] || {};
13+
var requiredFirst = configuration.requiredFirst || false;
14+
var callbacksLast = configuration.callbacksLast || false;
15+
var ignoreCase = configuration.ignoreCase || false;
16+
17+
/**
18+
* Checks if node is `propTypes` declaration
19+
* @param {ASTNode} node The AST node being checked.
20+
* @returns {Boolean} True if node is `propTypes` declaration, false if not.
21+
*/
22+
function isPropTypesDeclaration(node) {
23+
24+
// Special case for class properties
25+
// (babel-eslint does not expose property name so we have to rely on tokens)
26+
if (node.type === 'ClassProperty') {
27+
var tokens = context.getFirstTokens(node, 2);
28+
return (tokens[0] && tokens[0].value === 'propTypes') ||
29+
(tokens[1] && tokens[1].value === 'propTypes');
30+
}
31+
32+
return Boolean(
33+
node &&
34+
node.name === 'propTypes'
35+
);
36+
}
37+
38+
function getKey(node) {
39+
return node.key.type === 'Identifier' ? node.key.name : node.key.value;
40+
}
41+
42+
function getValueName(node) {
43+
return node.value.property && node.value.property.name;
44+
}
45+
46+
function isCallbackPropName(propName) {
47+
return /^on[A-Z]/.test(propName);
48+
}
49+
50+
function isRequiredProp(node) {
51+
return getValueName(node) === 'isRequired';
52+
}
53+
54+
/**
55+
* Checks if propTypes declarations are sorted
56+
* @param {Array} declarations The array of AST nodes being checked.
57+
* @returns {void}
58+
*/
59+
function checkSorted(declarations) {
60+
declarations.reduce(function(prev, curr) {
61+
var prevPropName = getKey(prev);
62+
var currentPropName = getKey(curr);
63+
var previousIsRequired = isRequiredProp(prev);
64+
var currentIsRequired = isRequiredProp(curr);
65+
var previousIsCallback = isCallbackPropName(prevPropName);
66+
var currentIsCallback = isCallbackPropName(currentPropName);
67+
68+
if (ignoreCase) {
69+
prevPropName = prevPropName.toLowerCase();
70+
currentPropName = currentPropName.toLowerCase();
71+
}
72+
73+
if (requiredFirst) {
74+
if (previousIsRequired && !currentIsRequired) {
75+
// Transition between required and non-required. Don't compare for alphabetical.
76+
return curr;
77+
}
78+
if (!previousIsRequired && currentIsRequired) {
79+
// Encountered a non-required prop after a required prop
80+
context.report({
81+
node: curr,
82+
message: 'Required prop types must be listed before all other prop types'
83+
});
84+
return curr;
85+
}
86+
}
87+
88+
if (callbacksLast) {
89+
if (!previousIsCallback && currentIsCallback) {
90+
// Entering the callback prop section
91+
return curr;
92+
}
93+
if (previousIsCallback && !currentIsCallback) {
94+
// Encountered a non-callback prop after a callback prop
95+
context.report({
96+
node: prev,
97+
message: 'Callback prop types must be listed after all other prop types'
98+
});
99+
return prev;
100+
}
101+
}
102+
103+
if (currentPropName < prevPropName) {
104+
context.report({
105+
node: curr,
106+
message: 'Prop types declarations should be sorted alphabetically'
107+
});
108+
return prev;
109+
}
110+
111+
return curr;
112+
}, declarations[0]);
113+
}
114+
115+
return {
116+
ClassProperty: function(node) {
117+
if (isPropTypesDeclaration(node) && node.value && node.value.type === 'ObjectExpression') {
118+
checkSorted(node.value.properties);
119+
}
120+
},
121+
122+
MemberExpression: function(node) {
123+
if (isPropTypesDeclaration(node.property)) {
124+
var right = node.parent.right;
125+
if (right && right.type === 'ObjectExpression') {
126+
checkSorted(right.properties);
127+
}
128+
}
129+
},
130+
131+
ObjectExpression: function(node) {
132+
node.properties.forEach(function(property) {
133+
if (!property.key) {
134+
return;
135+
}
136+
137+
if (!isPropTypesDeclaration(property.key)) {
138+
return;
139+
}
140+
if (property.value.type === 'ObjectExpression') {
141+
checkSorted(property.value.properties);
142+
}
143+
});
144+
}
145+
146+
};
147+
};
148+
149+
module.exports.schema = [{
150+
type: 'object',
151+
properties: {
152+
requiredFirst: {
153+
type: 'boolean'
154+
},
155+
callbacksLast: {
156+
type: 'boolean'
157+
},
158+
ignoreCase: {
159+
type: 'boolean'
160+
}
161+
},
162+
additionalProperties: false
163+
}];

tests/lib/rules/jsx-sort-prop-types.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* @fileoverview Tests for jsx-sort-prop-types
2+
* @fileoverview Tests for sort-prop-types
33
*/
44
'use strict';
55

@@ -27,7 +27,7 @@ require('babel-eslint');
2727
var ERROR_MESSAGE = 'Prop types declarations should be sorted alphabetically';
2828

2929
var ruleTester = new RuleTester();
30-
ruleTester.run('jsx-sort-prop-types', rule, {
30+
ruleTester.run('sort-prop-types', rule, {
3131

3232
valid: [{
3333
code: [

0 commit comments

Comments
 (0)