Skip to content

Commit

Permalink
Merge pull request #429 from lencioni/sort-comp-static-methods
Browse files Browse the repository at this point in the history
Add static method support to sort-comp (fixes #128)
  • Loading branch information
yannickcr committed Feb 14, 2016
2 parents e716f99 + b862347 commit 003eed4
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 27 deletions.
15 changes: 10 additions & 5 deletions docs/rules/sort-comp.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ When creating React components it is more convenient to always follow the same o

With default configuration the following organisation must be followed:

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).
2. custom methods
3. `render` method
1. static methods
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).
3. custom methods
4. `render` method

The following patterns are considered warnings:

Expand Down Expand Up @@ -53,6 +54,7 @@ The default configuration is:
```js
{
order: [
'static-methods',
'lifecycle',
'everything-else',
'render'
Expand Down Expand Up @@ -83,9 +85,10 @@ The default configuration is:
}
```

* `lifecycle` is refering to the `lifecycle` group defined in `groups`.
* `static-methods` is a special keyword that refers to static class methods.
* `lifecycle` is referring to the `lifecycle` group defined in `groups`.
* `everything-else` is a special group that match all the methods that do not match any of the other groups.
* `render` is refering to the `render` method.
* `render` is referring to the `render` method.

You can override this configuration to match your needs.

Expand All @@ -94,6 +97,7 @@ For example, if you want to place your event handlers (`onClick`, `onSubmit`, et
```js
"react/sort-comp": [1, {
order: [
'static-methods',
'lifecycle',
'/^on.+$/',
'render',
Expand Down Expand Up @@ -129,6 +133,7 @@ If you want to split your `render` method into smaller ones and keep them just b
```js
"react/sort-comp": [1, {
order: [
'static-methods',
'lifecycle',
'everything-else',
'rendering',
Expand Down
65 changes: 43 additions & 22 deletions lib/rules/sort-comp.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ module.exports = Components.detect(function(context, components) {

var methodsOrder = getMethodsOrder({
order: [
'static-methods',
'lifecycle',
'everything-else',
'render'
Expand Down Expand Up @@ -83,7 +84,7 @@ module.exports = Components.detect(function(context, components) {

/**
* Get indexes of the matching patterns in methods order configuration
* @param {String} method - Method name.
* @param {Object} method - Method metadata.
* @returns {Array} The matching patterns indexes. Return [Infinity] if there is no match.
*/
function getRefPropIndexes(method) {
Expand All @@ -92,15 +93,29 @@ module.exports = Components.detect(function(context, components) {
var i;
var j;
var indexes = [];
for (i = 0, j = methodsOrder.length; i < j; i++) {
isRegExp = methodsOrder[i].match(regExpRegExp);
if (isRegExp) {
matching = new RegExp(isRegExp[1], isRegExp[2]).test(method);
} else {
matching = methodsOrder[i] === method;

if (method.static) {
for (i = 0, j = methodsOrder.length; i < j; i++) {
if (methodsOrder[i] === 'static-methods') {
indexes.push(i);
break;
}
}
if (matching) {
indexes.push(i);
}

// Either this is not a static method or static methods are not specified
// in the methodsOrder.
if (indexes.length === 0) {
for (i = 0, j = methodsOrder.length; i < j; i++) {
isRegExp = methodsOrder[i].match(regExpRegExp);
if (isRegExp) {
matching = new RegExp(isRegExp[1], isRegExp[2]).test(method.name);
} else {
matching = methodsOrder[i] === method.name;
}
if (matching) {
indexes.push(i);
}
}
}

Expand All @@ -109,6 +124,7 @@ module.exports = Components.detect(function(context, components) {
for (i = 0, j = methodsOrder.length; i < j; i++) {
if (methodsOrder[i] === 'everything-else') {
indexes.push(i);
break;
}
}
}
Expand All @@ -127,7 +143,6 @@ module.exports = Components.detect(function(context, components) {
* @returns {String} Property name.
*/
function getPropertyName(node) {

// Special case for class properties
// (babel-eslint does not expose property name so we have to rely on tokens)
if (node.type === 'ClassProperty') {
Expand Down Expand Up @@ -240,12 +255,12 @@ module.exports = Components.detect(function(context, components) {

/**
* Compare two properties and find out if they are in the right order
* @param {Array} propertiesNames Array containing all the properties names.
* @param {String} propA First property name.
* @param {String} propB Second property name.
* @param {Array} propertiesInfos Array containing all the properties metadata.
* @param {Object} propA First property name and metadata
* @param {Object} propB Second property name.
* @returns {Object} Object containing a correct true/false flag and the correct indexes for the two properties.
*/
function comparePropsOrder(propertiesNames, propA, propB) {
function comparePropsOrder(propertiesInfos, propA, propB) {
var i;
var j;
var k;
Expand All @@ -258,8 +273,8 @@ module.exports = Components.detect(function(context, components) {
var refIndexesB = getRefPropIndexes(propB);

// Get current indexes for given properties
var classIndexA = propertiesNames.indexOf(propA);
var classIndexB = propertiesNames.indexOf(propB);
var classIndexA = propertiesInfos.indexOf(propA);
var classIndexB = propertiesInfos.indexOf(propB);

// Loop around the references indexes for the 1st property
for (i = 0, j = refIndexesA.length; i < j; i++) {
Expand Down Expand Up @@ -300,7 +315,13 @@ module.exports = Components.detect(function(context, components) {
* @param {Array} properties Array containing all the properties.
*/
function checkPropsOrder(properties) {
var propertiesNames = properties.map(getPropertyName);
var propertiesInfos = properties.map(function(node) {
return {
name: getPropertyName(node),
static: node.static
};
});

var i;
var j;
var k;
Expand All @@ -310,15 +331,15 @@ module.exports = Components.detect(function(context, components) {
var order;

// Loop around the properties
for (i = 0, j = propertiesNames.length; i < j; i++) {
propA = propertiesNames[i];
for (i = 0, j = propertiesInfos.length; i < j; i++) {
propA = propertiesInfos[i];

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

// Compare the properties order
order = comparePropsOrder(propertiesNames, propA, propB);
order = comparePropsOrder(propertiesInfos, propA, propB);

// Continue to next comparison is order is correct
if (order.correct === true) {
Expand Down

0 comments on commit 003eed4

Please sign in to comment.