Skip to content

Commit 279c850

Browse files
committed
Make prop sort lints fixable
Not all the prop sort lints were fixable. There were cases when fix would mess other things up. For example, in case when normal props were not sorted but callback functions were in the end(As they are supposed to be), sorting lint fix would mix oncallback props in between the other props. Updated the tests accordingly.
1 parent f0c0b4d commit 279c850

File tree

2 files changed

+87
-28
lines changed

2 files changed

+87
-28
lines changed

lib/rules/jsx-sort-props.js

Lines changed: 55 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -27,22 +27,54 @@ function isReservedPropName(name, list) {
2727
return list.indexOf(name) >= 0;
2828
}
2929

30-
function propNameCompare(a, b, options) {
31-
if (options.ignoreCase) {
32-
a = a.toLowerCase();
33-
b = b.toLowerCase();
34-
}
30+
function contextCompare(a, b, options) {
31+
let aProp = propName(a);
32+
let bProp = propName(b);
33+
3534
if (options.reservedFirst) {
36-
const aIsReserved = isReservedPropName(a, options.reservedList);
37-
const bIsReserved = isReservedPropName(b, options.reservedList);
35+
const aIsReserved = isReservedPropName(aProp, options.reservedList);
36+
const bIsReserved = isReservedPropName(bProp, options.reservedList);
3837
if ((aIsReserved && bIsReserved) || (!aIsReserved && !bIsReserved)) {
39-
return a.localeCompare(b);
38+
// pass
4039
} else if (aIsReserved && !bIsReserved) {
4140
return -1;
41+
} else {
42+
return 1;
43+
}
44+
}
45+
46+
if (options.callbacksLast) {
47+
const aIsCallback = isCallbackPropName(aProp);
48+
const bIsCallback = isCallbackPropName(bProp);
49+
if ((aIsCallback && bIsCallback) || (!aIsCallback && !bIsCallback)) {
50+
// pass
51+
} else if (aIsCallback && !bIsCallback) {
52+
return 1;
53+
} else {
54+
return -1;
55+
}
56+
}
57+
58+
if (options.shorthandFirst || options.shorthandLast) {
59+
const shorthandSign = options.shorthandFirst ? -1 : 1;
60+
if (!a.value && !b.value) {
61+
// pass
62+
} else if (!a.value) {
63+
return shorthandSign;
64+
} else {
65+
return -shorthandSign;
4266
}
43-
return 1;
4467
}
45-
return a.localeCompare(b);
68+
69+
if (options.noSortAlphabetically) {
70+
return 0;
71+
}
72+
73+
if (options.ignoreCase) {
74+
aProp = aProp.toLowerCase();
75+
bProp = bProp.toLowerCase();
76+
}
77+
return aProp.localeCompare(bProp);
4678
}
4779

4880
/**
@@ -79,15 +111,21 @@ const generateFixerFunction = (node, context, reservedList) => {
79111
const attributes = node.attributes.slice(0);
80112
const configuration = context.options[0] || {};
81113
const ignoreCase = configuration.ignoreCase || false;
114+
const callbacksLast = configuration.callbacksLast || false;
115+
const shorthandFirst = configuration.shorthandFirst || false;
116+
const shorthandLast = configuration.shorthandLast || false;
117+
const noSortAlphabetically = configuration.noSortAlphabetically || false;
82118
const reservedFirst = configuration.reservedFirst || false;
83119

84120
// Sort props according to the context. Only supports ignoreCase.
85121
// Since we cannot safely move JSXSpreadAttribute (due to potential variable overrides),
86122
// we only consider groups of sortable attributes.
123+
const options = {ignoreCase, callbacksLast, shorthandFirst, shorthandLast,
124+
noSortAlphabetically, reservedFirst, reservedList};
87125
const sortableAttributeGroups = getGroupsOfSortableAttributes(attributes);
88126
const sortedAttributeGroups = sortableAttributeGroups.slice(0).map(group =>
89127
group.slice(0).sort((a, b) =>
90-
propNameCompare(propName(a), propName(b), {ignoreCase, reservedFirst, reservedList})
128+
contextCompare(a, b, options)
91129
)
92130
);
93131

@@ -267,7 +305,8 @@ module.exports = {
267305
// Encountered a non-callback prop after a callback prop
268306
context.report({
269307
node: memo,
270-
message: 'Callbacks must be listed after all other props'
308+
message: 'Callbacks must be listed after all other props',
309+
fix: generateFixerFunction(node, context, reservedList)
271310
});
272311
return memo;
273312
}
@@ -280,7 +319,8 @@ module.exports = {
280319
if (!currentValue && previousValue) {
281320
context.report({
282321
node: memo,
283-
message: 'Shorthand props must be listed before all other props'
322+
message: 'Shorthand props must be listed before all other props',
323+
fix: generateFixerFunction(node, context, reservedList)
284324
});
285325
return memo;
286326
}
@@ -293,7 +333,8 @@ module.exports = {
293333
if (currentValue && !previousValue) {
294334
context.report({
295335
node: memo,
296-
message: 'Shorthand props must be listed after all other props'
336+
message: 'Shorthand props must be listed after all other props',
337+
fix: generateFixerFunction(node, context, reservedList)
297338
});
298339
return memo;
299340
}

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

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -336,59 +336,75 @@ ruleTester.run('jsx-sort-props', rule, {
336336
{
337337
code: '<App key="key" b c="c" />',
338338
errors: [expectedShorthandLastError],
339-
options: reservedFirstWithShorthandLast
339+
options: reservedFirstWithShorthandLast,
340+
output: '<App key="key" c="c" b />'
340341
},
341342
{
342343
code: '<App ref="ref" key="key" isShorthand veryLastAttribute="yes" />',
343344
errors: [expectedError, expectedShorthandLastError],
344-
options: reservedFirstWithShorthandLast
345+
options: reservedFirstWithShorthandLast,
346+
output: '<App ref="ref" key="key" veryLastAttribute="yes" isShorthand />'
345347
},
346348
{
347349
code: '<App a z onFoo onBar />;',
348350
errors: [expectedError],
349-
options: callbacksLastArgs
351+
options: callbacksLastArgs,
352+
output: '<App a z onBar onFoo />;'
350353
},
351354
{
352355
code: '<App a onBar onFoo z />;',
353356
errors: [expectedCallbackError],
354-
options: callbacksLastArgs
357+
options: callbacksLastArgs,
358+
output: '<App a z onBar onFoo />;'
355359
},
356360
{
357361
code: '<App a="a" b />;',
358362
errors: [expectedShorthandFirstError],
359-
options: shorthandFirstArgs
363+
options: shorthandFirstArgs,
364+
output: '<App b a="a" />;'
360365
},
361366
{
362367
code: '<App z x a="a" />;',
363368
errors: [expectedError],
364-
options: shorthandFirstArgs
369+
options: shorthandFirstArgs,
370+
output: '<App x z a="a" />;'
365371
},
366372
{
367373
code: '<App b a="a" />;',
368374
errors: [expectedShorthandLastError],
369-
options: shorthandLastArgs
375+
options: shorthandLastArgs,
376+
output: '<App a="a" b />;'
370377
},
371378
{
372379
code: '<App a="a" onBar onFoo z x />;',
373380
errors: [shorthandAndCallbackLastArgs],
374-
options: shorthandLastArgs
381+
options: shorthandLastArgs,
382+
output: '<App a="a" onBar onFoo x z />;'
383+
},
384+
{
385+
code: '<App b a />;',
386+
errors: [expectedError],
387+
options: sortAlphabeticallyArgs,
388+
output: '<App a b />;'
375389
},
376-
{code: '<App b a />;', errors: [expectedError], options: sortAlphabeticallyArgs},
377390
// reservedFirst
378391
{
379392
code: '<App a key={1} />',
380393
options: reservedFirstAsBooleanArgs,
381-
errors: [expectedReservedFirstError]
394+
errors: [expectedReservedFirstError],
395+
output: '<App key={1} a />'
382396
},
383397
{
384398
code: '<div a dangerouslySetInnerHTML={{__html: "EPR"}} />',
385399
options: reservedFirstAsBooleanArgs,
386-
errors: [expectedReservedFirstError]
400+
errors: [expectedReservedFirstError],
401+
output: '<div dangerouslySetInnerHTML={{__html: "EPR"}} a />'
387402
},
388403
{
389404
code: '<App ref="r" key={2} b />',
390405
options: reservedFirstAsBooleanArgs,
391-
errors: [expectedError]
406+
errors: [expectedError],
407+
output: '<App key={2} ref="r" b />'
392408
},
393409
{
394410
code: '<App key={2} b a />',
@@ -411,12 +427,14 @@ ruleTester.run('jsx-sort-props', rule, {
411427
{
412428
code: '<App key={3} children={<App />} />',
413429
options: reservedFirstAsArrayArgs,
414-
errors: [expectedError]
430+
errors: [expectedError],
431+
output: '<App children={<App />} key={3} />'
415432
},
416433
{
417434
code: '<App z ref="r" />',
418435
options: reservedFirstWithNoSortAlphabeticallyArgs,
419-
errors: [expectedReservedFirstError]
436+
errors: [expectedReservedFirstError],
437+
output: '<App ref="r" z />'
420438
},
421439
{
422440
code: '<App key={4} />',

0 commit comments

Comments
 (0)