Skip to content

Commit 63750c9

Browse files
committed
Teach sort-comp rule about static class methods
A number of people have said that they think it makes sense for static class methods to appear at the very top of classes. This commit allows this to be configured by adding the `static-methods` keyword to the sort-comp rule. Fixes #128
1 parent dedf67a commit 63750c9

File tree

2 files changed

+49
-25
lines changed

2 files changed

+49
-25
lines changed

docs/rules/sort-comp.md

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,10 @@ When creating React components it is more convenient to always follow the same o
88

99
With default configuration the following organisation must be followed:
1010

11-
1. lifecycle methods: `displayName`, `propTypes`, `contextTypes`, `childContextTypes`, `mixins`, `statics`,`defaultProps`, `constructor`, `getDefaultProps`, `getInitialState`, `state`, `getChildContext`, `componentWillMount`, `componentDidMount`, `componentWillReceiveProps`, `shouldComponentUpdate`, `componentWillUpdate`, `componentDidUpdate`, `componentWillUnmount` (in this order).
12-
2. custom methods
13-
3. `render` method
11+
1. static methods
12+
2. lifecycle methods: `displayName`, `propTypes`, `contextTypes`, `childContextTypes`, `mixins`, `statics`,`defaultProps`, `constructor`, `getDefaultProps`, `getInitialState`, `state`, `getChildContext`, `componentWillMount`, `componentDidMount`, `componentWillReceiveProps`, `shouldComponentUpdate`, `componentWillUpdate`, `componentDidUpdate`, `componentWillUnmount` (in this order).
13+
3. custom methods
14+
4. `render` method
1415

1516
The following patterns are considered warnings:
1617

@@ -53,6 +54,7 @@ The default configuration is:
5354
```js
5455
{
5556
order: [
57+
'static-methods',
5658
'lifecycle',
5759
'everything-else',
5860
'render'
@@ -83,6 +85,7 @@ The default configuration is:
8385
}
8486
```
8587

88+
* `static-methods` is a special keyword that refers to static class methods.
8689
* `lifecycle` is refering to the `lifecycle` group defined in `groups`.
8790
* `everything-else` is a special group that match all the methods that do not match any of the other groups.
8891
* `render` is refering to the `render` method.
@@ -94,6 +97,7 @@ For example, if you want to place your event handlers (`onClick`, `onSubmit`, et
9497
```js
9598
"react/sort-comp": [1, {
9699
order: [
100+
'static-methods',
97101
'lifecycle',
98102
'/^on.+$/',
99103
'render',
@@ -129,6 +133,7 @@ If you want to split your `render` method into smaller ones and keep them just b
129133
```js
130134
"react/sort-comp": [1, {
131135
order: [
136+
'static-methods',
132137
'lifecycle',
133138
'everything-else',
134139
'rendering',

lib/rules/sort-comp.js

Lines changed: 41 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ module.exports = Components.detect(function(context, components) {
4646

4747
var methodsOrder = getMethodsOrder({
4848
order: [
49+
'static-methods',
4950
'lifecycle',
5051
'everything-else',
5152
'render'
@@ -83,7 +84,7 @@ module.exports = Components.detect(function(context, components) {
8384

8485
/**
8586
* Get indexes of the matching patterns in methods order configuration
86-
* @param {String} method - Method name.
87+
* @param {Object} method - Method metadata.
8788
* @returns {Array} The matching patterns indexes. Return [Infinity] if there is no match.
8889
*/
8990
function getRefPropIndexes(method) {
@@ -92,15 +93,28 @@ module.exports = Components.detect(function(context, components) {
9293
var i;
9394
var j;
9495
var indexes = [];
95-
for (i = 0, j = methodsOrder.length; i < j; i++) {
96-
isRegExp = methodsOrder[i].match(regExpRegExp);
97-
if (isRegExp) {
98-
matching = new RegExp(isRegExp[1], isRegExp[2]).test(method);
99-
} else {
100-
matching = methodsOrder[i] === method;
96+
97+
if (method.static) {
98+
for (i = 0, j = methodsOrder.length; i < j; i++) {
99+
if (methodsOrder[i] === 'static-methods') {
100+
indexes.push(i);
101+
}
101102
}
102-
if (matching) {
103-
indexes.push(i);
103+
}
104+
105+
// Either this is not a static method or static methods are not specified
106+
// in the methodsOrder.
107+
if (indexes.length === 0) {
108+
for (i = 0, j = methodsOrder.length; i < j; i++) {
109+
isRegExp = methodsOrder[i].match(regExpRegExp);
110+
if (isRegExp) {
111+
matching = new RegExp(isRegExp[1], isRegExp[2]).test(method.name);
112+
} else {
113+
matching = methodsOrder[i] === method.name;
114+
}
115+
if (matching) {
116+
indexes.push(i);
117+
}
104118
}
105119
}
106120

@@ -127,7 +141,6 @@ module.exports = Components.detect(function(context, components) {
127141
* @returns {String} Property name.
128142
*/
129143
function getPropertyName(node) {
130-
131144
// Special case for class properties
132145
// (babel-eslint does not expose property name so we have to rely on tokens)
133146
if (node.type === 'ClassProperty') {
@@ -236,12 +249,12 @@ module.exports = Components.detect(function(context, components) {
236249

237250
/**
238251
* Compare two properties and find out if they are in the right order
239-
* @param {Array} propertiesNames Array containing all the properties names.
240-
* @param {String} propA First property name.
241-
* @param {String} propB Second property name.
252+
* @param {Array} propertiesInfos Array containing all the properties metadata.
253+
* @param {Object} propA First property name and metadata
254+
* @param {Object} propB Second property name.
242255
* @returns {Object} Object containing a correct true/false flag and the correct indexes for the two properties.
243256
*/
244-
function comparePropsOrder(propertiesNames, propA, propB) {
257+
function comparePropsOrder(propertiesInfos, propA, propB) {
245258
var i;
246259
var j;
247260
var k;
@@ -254,8 +267,8 @@ module.exports = Components.detect(function(context, components) {
254267
var refIndexesB = getRefPropIndexes(propB);
255268

256269
// Get current indexes for given properties
257-
var classIndexA = propertiesNames.indexOf(propA);
258-
var classIndexB = propertiesNames.indexOf(propB);
270+
var classIndexA = propertiesInfos.indexOf(propA);
271+
var classIndexB = propertiesInfos.indexOf(propB);
259272

260273
// Loop around the references indexes for the 1st property
261274
for (i = 0, j = refIndexesA.length; i < j; i++) {
@@ -296,7 +309,13 @@ module.exports = Components.detect(function(context, components) {
296309
* @param {Array} properties Array containing all the properties.
297310
*/
298311
function checkPropsOrder(properties) {
299-
var propertiesNames = properties.map(getPropertyName);
312+
var propertiesInfos = properties.map(function(node) {
313+
return {
314+
name: getPropertyName(node),
315+
static: node.static
316+
};
317+
});
318+
300319
var i;
301320
var j;
302321
var k;
@@ -306,15 +325,15 @@ module.exports = Components.detect(function(context, components) {
306325
var order;
307326

308327
// Loop around the properties
309-
for (i = 0, j = propertiesNames.length; i < j; i++) {
310-
propA = propertiesNames[i];
328+
for (i = 0, j = propertiesInfos.length; i < j; i++) {
329+
propA = propertiesInfos[i];
311330

312331
// Loop around the properties a second time (for comparison)
313-
for (k = 0, l = propertiesNames.length; k < l; k++) {
314-
propB = propertiesNames[k];
332+
for (k = 0, l = propertiesInfos.length; k < l; k++) {
333+
propB = propertiesInfos[k];
315334

316335
// Compare the properties order
317-
order = comparePropsOrder(propertiesNames, propA, propB);
336+
order = comparePropsOrder(propertiesInfos, propA, propB);
318337

319338
// Continue to next comparison is order is correct
320339
if (order.correct === true) {

0 commit comments

Comments
 (0)