Skip to content

Commit c90be43

Browse files
committed
Consider groups of sortable attributes
1 parent 0d72eb2 commit c90be43

File tree

2 files changed

+50
-25
lines changed

2 files changed

+50
-25
lines changed

lib/rules/jsx-sort-props.js

+46-21
Original file line numberDiff line numberDiff line change
@@ -48,40 +48,65 @@ function alphabeticalCompare(a, b, ignoreCase) {
4848
return a.localeCompare(b);
4949
}
5050

51+
/**
52+
* Create an array of arrays where each subarray is composed of attributes
53+
* that are considered sortable.
54+
* @param {Array<JSXSpreadAttribute|JSXAttribute>} attributes
55+
* @return {Array<Array<JSXAttribute>}
56+
*/
57+
function getGroupsOfSortableAttributes(attributes) {
58+
const sortableAttributeGroups = [];
59+
let groupCount = 0;
60+
for (var i = 0; i < attributes.length; i++) {
61+
const lastAttr = attributes[i - 1];
62+
// If we have no groups or if the last attribute was JSXSpreadAttribute
63+
// then we start a new group. Append attributes to the group until we
64+
// come across another JSXSpreadAttribute or exhaust the array.
65+
if (
66+
!lastAttr ||
67+
(lastAttr.type === 'JSXSpreadAttribute' &&
68+
attributes[i].type !== 'JSXSpreadAttribute')
69+
) {
70+
groupCount++;
71+
sortableAttributeGroups[groupCount - 1] = [];
72+
}
73+
if (attributes[i].type !== 'JSXSpreadAttribute') {
74+
sortableAttributeGroups[groupCount - 1].push(attributes[i]);
75+
}
76+
}
77+
return sortableAttributeGroups;
78+
}
79+
5180
function generateFixerFunction(node, context) {
5281
var sourceCode = context.getSourceCode();
5382
var attributes = node.attributes.slice(0);
5483
var configuration = context.options[0] || {};
5584
var ignoreCase = configuration.ignoreCase || false;
5685

5786
// Sort props according to the context. Only supports ignoreCase.
58-
// Since we cannot safely move JSXSpreadAttributes (due to potential variable overrides),
59-
// we only consider the sortable attributes.
60-
const sortableAttributes = attributes.filter(function(attr) {
61-
return attr.type !== 'JSXSpreadAttribute';
62-
});
63-
64-
sortableAttributes.sort(function(a, b) {
65-
return alphabeticalCompare(propName(a), propName(b), ignoreCase);
87+
// Since we cannot safely move JSXSpreadAttribute (due to potential variable overrides),
88+
// we only consider groups of sortable attributes.
89+
const sortableAttributeGroups = getGroupsOfSortableAttributes(attributes);
90+
const sortedAttributeGroups = sortableAttributeGroups.slice(0).map(function(group) {
91+
return group.slice(0).sort(function(a, b) {
92+
return alphabeticalCompare(propName(a), propName(b), ignoreCase);
93+
});
6694
});
6795

6896
return function(fixer) {
69-
// Replace each unsorted attribute with the sorted one.
7097
const fixers = [];
71-
let skipIndex = 0; // skip over spread attributes
72-
73-
node.attributes.forEach(function(attr, index) {
74-
if (attr.type === 'JSXSpreadAttribute') {
75-
skipIndex++;
76-
return;
77-
}
7898

79-
var sortedAttr = sortableAttributes[index - skipIndex];
80-
var sortedAttrText = sourceCode.getText(sortedAttr);
81-
fixers.push(
82-
fixer.replaceTextRange([attr.start, attr.end], sortedAttrText)
83-
);
99+
// Replace each unsorted attribute with the sorted one.
100+
sortableAttributeGroups.forEach(function(sortableGroup, ii) {
101+
sortableGroup.forEach(function(attr, jj) {
102+
var sortedAttr = sortedAttributeGroups[ii][jj];
103+
var sortedAttrText = sourceCode.getText(sortedAttr);
104+
fixers.push(
105+
fixer.replaceTextRange([attr.start, attr.end], sortedAttrText)
106+
);
107+
});
84108
});
109+
85110
return fixers;
86111
};
87112
}

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

+4-4
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ ruleTester.run('jsx-sort-props', rule, {
166166
{
167167
code: '<App c {...this.props} b a />;',
168168
errors: [expectedError],
169-
output: '<App a {...this.props} b c />;'
169+
output: '<App c {...this.props} a b />;'
170170
},
171171
{
172172
code: '<App a A />;',
@@ -197,7 +197,7 @@ ruleTester.run('jsx-sort-props', rule, {
197197
},
198198
{
199199
code: '<App d="d" b="b" {...this.props} c="a" a="c" />;',
200-
output: '<App a="c" b="b" {...this.props} c="a" d="d" />;',
200+
output: '<App b="b" d="d" {...this.props} a="c" c="a" />;',
201201
errors: 2
202202
},
203203
{
@@ -218,11 +218,11 @@ ruleTester.run('jsx-sort-props', rule, {
218218
'<App',
219219
'_onClick={function(){}}',
220220
'a={true}',
221-
'b={false}',
222221
'onHandle={function(){}}',
223222
'r',
224-
'{...this.props}',
225223
'z',
224+
'{...this.props}',
225+
'b={false}',
226226
'{...otherProps}>',
227227
' {test}',
228228
'</App>'

0 commit comments

Comments
 (0)