Skip to content

Commit

Permalink
[Fix] sort-prop-types: ensure sort-prop-types respects noSortAlphab…
Browse files Browse the repository at this point in the history
…etically
  • Loading branch information
caesar1030 authored and ljharb committed Jul 22, 2023
1 parent 302925b commit 3c11201
Show file tree
Hide file tree
Showing 4 changed files with 131 additions and 12 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Expand Up @@ -8,8 +8,10 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange
### Fixed
* [`require-default-props`]: fix config schema ([#3605][] @controversial)
* [`jsx-curly-brace-presence`]: Revert [#3538][] due to issues with intended string type casting usage ([#3611][] @taozhou-glean)
* [`sort-prop-types`]: ensure sort-prop-types respects noSortAlphabetically ([#3610][] @caesar1030)

[#3611]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3611
[#3610]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3610
[#3605]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3605

## [7.33.0] - 2023.07.19
Expand Down
1 change: 1 addition & 0 deletions lib/rules/sort-prop-types.js
Expand Up @@ -114,6 +114,7 @@ module.exports = {
ignoreCase,
requiredFirst,
callbacksLast,
noSortAlphabetically,
sortShapeProp
);
}
Expand Down
38 changes: 26 additions & 12 deletions lib/util/propTypesSort.js
Expand Up @@ -69,9 +69,10 @@ function getShapeProperties(node) {
* @param {Boolean=} ignoreCase whether or not to ignore case when comparing the two elements.
* @param {Boolean=} requiredFirst whether or not to sort required elements first.
* @param {Boolean=} callbacksLast whether or not to sort callbacks after everything else.
* @param {Boolean=} noSortAlphabetically whether or not to disable alphabetical sorting of the elements.
* @returns {Number} the sort order of the two elements.
*/
function sorter(a, b, context, ignoreCase, requiredFirst, callbacksLast) {
function sorter(a, b, context, ignoreCase, requiredFirst, callbacksLast, noSortAlphabetically) {
const aKey = String(astUtil.getKeyValue(context, a));
const bKey = String(astUtil.getKeyValue(context, b));

Expand All @@ -93,19 +94,23 @@ function sorter(a, b, context, ignoreCase, requiredFirst, callbacksLast) {
}
}

if (ignoreCase) {
return aKey.localeCompare(bKey);
}
if (!noSortAlphabetically) {
if (ignoreCase) {
return aKey.localeCompare(bKey);
}

if (aKey < bKey) {
return -1;
}
if (aKey > bKey) {
return 1;
if (aKey < bKey) {
return -1;
}
if (aKey > bKey) {
return 1;
}
}
return 0;
}

const commentnodeMap = new WeakMap(); // all nodes reference WeakMap for start and end range

/**
* Fixes sort order of prop types.
*
Expand All @@ -115,11 +120,20 @@ function sorter(a, b, context, ignoreCase, requiredFirst, callbacksLast) {
* @param {Boolean=} ignoreCase whether or not to ignore case when comparing the two elements.
* @param {Boolean=} requiredFirst whether or not to sort required elements first.
* @param {Boolean=} callbacksLast whether or not to sort callbacks after everything else.
* @param {Boolean=} noSortAlphabetically whether or not to disable alphabetical sorting of the elements.
* @param {Boolean=} sortShapeProp whether or not to sort propTypes defined in PropTypes.shape.
* @returns {Object|*|{range, text}} the sort order of the two elements.
*/
const commentnodeMap = new WeakMap(); // all nodes reference WeakMap for start and end range
function fixPropTypesSort(fixer, context, declarations, ignoreCase, requiredFirst, callbacksLast, sortShapeProp) {
function fixPropTypesSort(
fixer,
context,
declarations,
ignoreCase,
requiredFirst,
callbacksLast,
noSortAlphabetically,
sortShapeProp
) {
function sortInSource(allNodes, source) {
const originalSource = source;
const sourceCode = context.getSourceCode();
Expand Down Expand Up @@ -161,7 +175,7 @@ function fixPropTypesSort(fixer, context, declarations, ignoreCase, requiredFirs
nodeGroups.forEach((nodes) => {
const sortedAttributes = toSorted(
nodes,
(a, b) => sorter(a, b, context, ignoreCase, requiredFirst, callbacksLast)
(a, b) => sorter(a, b, context, ignoreCase, requiredFirst, callbacksLast, noSortAlphabetically)
);

source = nodes.reduceRight((acc, attr, index) => {
Expand Down
102 changes: 102 additions & 0 deletions tests/lib/rules/sort-prop-types.js
Expand Up @@ -467,6 +467,19 @@ ruleTester.run('sort-prop-types', rule, {
};
`,
options: [{ sortShapeProp: true }],
},
{
code: `
var Component = createReactClass({
propTypes: {
a: React.PropTypes.string,
c: React.PropTypes.string,
b: React.PropTypes.string,
onChange: React.PropTypes.func,
}
});
`,
options: [{ callbacksLast: true, noSortAlphabetically: true }],
}
)),
invalid: parsers.all([].concat(
Expand Down Expand Up @@ -1886,6 +1899,37 @@ ruleTester.run('sort-prop-types', rule, {
},
],
},
{
code: `
var Component = createReactClass({
propTypes: {
onChange: React.PropTypes.func,
a: React.PropTypes.string,
c: React.PropTypes.string,
b: React.PropTypes.string,
}
});
`,
output: `
var Component = createReactClass({
propTypes: {
a: React.PropTypes.string,
c: React.PropTypes.string,
b: React.PropTypes.string,
onChange: React.PropTypes.func,
}
});
`,
options: [{ callbacksLast: true, noSortAlphabetically: true }],
errors: [
{
messageId: 'callbackPropsLast',
line: 4,
column: 13,
type: 'Property',
},
],
},
semver.satisfies(eslintPkg.version, '> 3') ? {
code: `
var First = createReactClass({
Expand Down Expand Up @@ -2148,6 +2192,64 @@ ruleTester.run('sort-prop-types', rule, {
type: 'Property',
},
],
} : [],
semver.satisfies(eslintPkg.version, '> 3') ? {
code: `
var Component = createReactClass({
propTypes: {
/* onChange */ onChange: React.PropTypes.func,
/* a */ a: React.PropTypes.string,
/* c */ c: React.PropTypes.string,
/* b */ b: React.PropTypes.string,
}
});
`,
output: `
var Component = createReactClass({
propTypes: {
/* a */ a: React.PropTypes.string,
/* c */ c: React.PropTypes.string,
/* b */ b: React.PropTypes.string,
/* onChange */ onChange: React.PropTypes.func,
}
});
`,
options: [{ callbacksLast: true, noSortAlphabetically: true }],
errors: [
{
messageId: 'callbackPropsLast',
line: 4,
},
],
} : [],
semver.satisfies(eslintPkg.version, '> 3') ? {
code: `
var Component = createReactClass({
propTypes: {
/* onChange */ onChange: React.PropTypes.func /* onChange */,
/* a */ a: React.PropTypes.string /* a */,
/* c */ c: React.PropTypes.string /* c */,
/* b */ b: React.PropTypes.string /* b */,
}
});
`,
output: `
var Component = createReactClass({
propTypes: {
/* a */ a: React.PropTypes.string /* a */,
/* c */ c: React.PropTypes.string /* c */,
/* b */ b: React.PropTypes.string /* b */,
/* onChange */ onChange: React.PropTypes.func /* onChange */,
}
});
`,
options: [{ callbacksLast: true, noSortAlphabetically: true }],
errors: [
{
messageId: 'callbackPropsLast',
line: 4,
},
],
} : []
)),
});

0 comments on commit 3c11201

Please sign in to comment.