Skip to content

Commit

Permalink
[New] jsx-sort-props: add locale option
Browse files Browse the repository at this point in the history
Fixes #3002
  • Loading branch information
ljharb committed Feb 22, 2022
1 parent 69ac0a0 commit 6d6f5bd
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 6 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange
* [`no-did-mount-set-state`], [`no-did-update-set-state`]: no-op with react >= 16.3 ([#1754][] @ljharb)
* [`jsx-sort-props`]: support multiline prop groups ([#3198][] @duhamelgm)
* [`jsx-key`]: add `warnDuplicates` option to warn on duplicate jsx keys in an array ([#2614][] @ljharb)
* [`jsx-sort-props`]: add `locale` option ([#3002][] @ljharb)

### Fixed
* [`prop-types`], `propTypes`: add support for exported type inference ([#3163][] @vedadeepta)
Expand Down Expand Up @@ -58,6 +59,7 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange
[#3163]: https://github.com/yannickcr/eslint-plugin-react/pull/3163
[#3160]: https://github.com/yannickcr/eslint-plugin-react/pull/3160
[#3133]: https://github.com/yannickcr/eslint-plugin-react/pull/3133
[#3002]: https://github.com/yannickcr/eslint-plugin-react/issues/3002
[#2945]: https://github.com/yannickcr/eslint-plugin-react/issues/2945
[#2921]: https://github.com/yannickcr/eslint-plugin-react/pull/2921
[#2861]: https://github.com/yannickcr/eslint-plugin-react/issues/2861
Expand Down
7 changes: 7 additions & 0 deletions docs/rules/jsx-sort-props.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ Examples of **correct** code for this rule:
"ignoreCase": <boolean>,
"noSortAlphabetically": <boolean>,
"reservedFirst": <boolean>|<array<string>>,
"locale": "auto" | "any valid locale"
}]
...
```
Expand Down Expand Up @@ -135,6 +136,12 @@ With `reservedFirst: ["key"]`, the following will **not** warn:
<Hello key={'uuid'} name="John" ref="ref" />
```

### `locale`

Defaults to `"auto"`, meaning, the locale of the current environment.

Any other string provided here may be passed to `String.prototype.localeCompare` - note that an unknown or invalid locale may throw an exception and crash.

## When Not To Use It

This rule is a formatting preference and not following it won't negatively affect the quality of your code. If alphabetizing props isn't a part of your coding standards, then you can leave this rule off.
20 changes: 16 additions & 4 deletions lib/rules/jsx-sort-props.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,15 +98,20 @@ function contextCompare(a, b, options) {
return 0;
}

const actualLocale = options.locale === 'auto' ? undefined : options.locale;

if (options.ignoreCase) {
aProp = aProp.toLowerCase();
bProp = bProp.toLowerCase();
return aProp.localeCompare(bProp);
return aProp.localeCompare(bProp, actualLocale);
}
if (aProp === bProp) {
return 0;
}
return aProp < bProp ? -1 : 1;
if (options.locale === 'auto') {
return aProp < bProp ? -1 : 1;
}
return aProp.localeCompare(bProp, actualLocale);
}

/**
Expand Down Expand Up @@ -149,6 +154,7 @@ const generateFixerFunction = (node, context, reservedList) => {
const multiline = configuration.multiline || 'ignore';
const noSortAlphabetically = configuration.noSortAlphabetically || false;
const reservedFirst = configuration.reservedFirst || false;
const locale = configuration.locale || 'auto';

// Sort props according to the context. Only supports ignoreCase.
// Since we cannot safely move JSXSpreadAttribute (due to potential variable overrides),
Expand All @@ -162,6 +168,7 @@ const generateFixerFunction = (node, context, reservedList) => {
noSortAlphabetically,
reservedFirst,
reservedList,
locale,
};
const sortableAttributeGroups = getGroupsOfSortableAttributes(attributes);
const sortedAttributeGroups = sortableAttributeGroups
Expand Down Expand Up @@ -305,6 +312,10 @@ module.exports = {
reservedFirst: {
type: ['array', 'boolean'],
},
locale: {
type: 'string',
default: 'auto',
},
},
additionalProperties: false,
}],
Expand All @@ -321,6 +332,7 @@ module.exports = {
const reservedFirst = configuration.reservedFirst || false;
const reservedFirstError = validateReservedFirstConfig(context, reservedFirst);
let reservedList = Array.isArray(reservedFirst) ? reservedFirst : RESERVED_PROPS_LIST;
const locale = configuration.locale || 'auto';

return {
JSXOpeningElement(node) {
Expand Down Expand Up @@ -431,8 +443,8 @@ module.exports = {
if (
!noSortAlphabetically
&& (
ignoreCase
? previousPropName.localeCompare(currentPropName) > 0
(ignoreCase || locale !== 'auto')
? previousPropName.localeCompare(currentPropName, locale === 'auto' ? undefined : locale) > 0
: previousPropName > currentPropName
)
) {
Expand Down
24 changes: 22 additions & 2 deletions tests/lib/rules/jsx-sort-props.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
// -----------------------------------------------------------------------------

const RuleTester = require('eslint').RuleTester;
const semver = require('semver');
const rule = require('../../../lib/rules/jsx-sort-props');

const parsers = require('../../helpers/parsers');
Expand Down Expand Up @@ -120,7 +121,7 @@ const multilineAndShorthandAndCallbackLastArgs = [
];

ruleTester.run('jsx-sort-props', rule, {
valid: parsers.all([
valid: parsers.all([].concat(
{ code: '<App />;' },
{ code: '<App {...this.props} />;' },
{ code: '<App a b c />;' },
Expand Down Expand Up @@ -276,7 +277,26 @@ ruleTester.run('jsx-sort-props', rule, {
code: '<App key="key" c="c" b />',
options: reservedFirstWithShorthandLast,
},
]),
{
code: `
<RawFileField
onChange={handleChange}
onFileRemove={asMedia ? null : handleRemove}
{...props}
/>
`,
},
semver.satisfies(process.version, '>= 13') ? {
code: `
<RawFileField
onFileRemove={asMedia ? null : handleRemove}
onChange={handleChange}
{...props}
/>
`,
options: [{ locale: 'sk-SK' }],
} : []
)),
invalid: parsers.all([
{
code: '<App b a />;',
Expand Down

0 comments on commit 6d6f5bd

Please sign in to comment.