From 158172c6b035991d16bef2f7a85cff9c90b47eef Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Mon, 21 Feb 2022 19:22:33 -0800 Subject: [PATCH] [New] `jsx-sort-props`: add `locale` option Fixes #3002 --- CHANGELOG.md | 2 ++ docs/rules/jsx-sort-props.md | 7 +++++++ lib/rules/jsx-sort-props.js | 20 ++++++++++++++++---- tests/lib/rules/jsx-sort-props.js | 19 +++++++++++++++++++ 4 files changed, 44 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d1cde93796..85dd7eb92e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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) @@ -57,6 +58,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 diff --git a/docs/rules/jsx-sort-props.md b/docs/rules/jsx-sort-props.md index b0c98fe23f..acafa87b5e 100644 --- a/docs/rules/jsx-sort-props.md +++ b/docs/rules/jsx-sort-props.md @@ -33,6 +33,7 @@ Examples of **correct** code for this rule: "ignoreCase": , "noSortAlphabetically": , "reservedFirst": |>, + "locale": "auto" | "any valid locale" }] ... ``` @@ -135,6 +136,12 @@ With `reservedFirst: ["key"]`, the following will **not** warn: ``` +### `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. diff --git a/lib/rules/jsx-sort-props.js b/lib/rules/jsx-sort-props.js index 533f0b3e19..8daf981908 100644 --- a/lib/rules/jsx-sort-props.js +++ b/lib/rules/jsx-sort-props.js @@ -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); } /** @@ -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), @@ -162,6 +168,7 @@ const generateFixerFunction = (node, context, reservedList) => { noSortAlphabetically, reservedFirst, reservedList, + locale, }; const sortableAttributeGroups = getGroupsOfSortableAttributes(attributes); const sortedAttributeGroups = sortableAttributeGroups @@ -305,6 +312,10 @@ module.exports = { reservedFirst: { type: ['array', 'boolean'], }, + locale: { + type: 'string', + default: 'auto', + }, }, additionalProperties: false, }], @@ -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) { @@ -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 ) ) { diff --git a/tests/lib/rules/jsx-sort-props.js b/tests/lib/rules/jsx-sort-props.js index 519d143947..dedc43f549 100644 --- a/tests/lib/rules/jsx-sort-props.js +++ b/tests/lib/rules/jsx-sort-props.js @@ -276,6 +276,25 @@ ruleTester.run('jsx-sort-props', rule, { code: '', options: reservedFirstWithShorthandLast, }, + { + code: ` + + `, + }, + { + code: ` + + `, + options: [{ locale: 'sk-SK' }], + }, ]), invalid: parsers.all([ {