diff --git a/docs/rules/order.md b/docs/rules/order.md index 7d91efd6f5..dcd4ac58a2 100644 --- a/docs/rules/order.md +++ b/docs/rules/order.md @@ -216,12 +216,12 @@ import index from './'; import sibling from './foo'; ``` -### `alphabetize: {order: asc|desc|ignore, caseInsensitive: true|false}`: +### `alphabetize: {order: asc|desc|ignore, caseInsensitive: true|false|'invert'}`: Sort the order within each group in alphabetical manner based on **import path**: - `order`: use `asc` to sort in ascending order, and `desc` to sort in descending order (default: `ignore`). -- `caseInsensitive`: use `true` to ignore case, and `false` to consider case (default: `false`). +- `caseInsensitive`: use `true` to ignore case, `false` to consider case, and `'invert'` to sort lowercase before uppercase (default: `false`). Example setting: ```js diff --git a/src/rules/order.js b/src/rules/order.js index bad382945c..05d6454c09 100644 --- a/src/rules/order.js +++ b/src/rules/order.js @@ -242,10 +242,18 @@ function makeOutOfOrderReport(context, imported) { reportOutOfOrder(context, imported, outOfOrder, 'before'); } -function getSorter(ascending) { - const multiplier = ascending ? 1 : -1; +function getSorter(alphabetizeOptions) { + const multiplier = alphabetizeOptions.order === 'asc' ? 1 : -1; + let collate; + if (alphabetizeOptions.caseInsensitive) { + if (alphabetizeOptions.caseFirst === 'lower') { + collate = swapCase; + } else { + collate = (s) => String(s).toLowerCase(); + } + } - return function importsSorter(importA, importB) { + function importsSorter(importA, importB) { let result; if (importA < importB) { @@ -257,7 +265,18 @@ function getSorter(ascending) { } return result * multiplier; - }; + } + return collate ? (a, b) => importsSorter(collate(a), collate(b)) : importsSorter; +} + +function swapCase(input) { + input = String(input); + let result = ''; + for (let i = 0; i < input.length; i++) { + const lower = input[i].toLowerCase(); + result += input[i] === lower ? input[i].toUpperCase() : lower; + } + return result; } function mutateRanksToAlphabetize(imported, alphabetizeOptions) { @@ -271,11 +290,10 @@ function mutateRanksToAlphabetize(imported, alphabetizeOptions) { const groupRanks = Object.keys(groupedByRanks); - const sorterFn = getSorter(alphabetizeOptions.order === 'asc'); - const comparator = alphabetizeOptions.caseInsensitive ? (a, b) => sorterFn(String(a).toLowerCase(), String(b).toLowerCase()) : (a, b) => sorterFn(a, b); + const sorterFn = getSorter(alphabetizeOptions); // sort imports locally within their group groupRanks.forEach(function(groupRank) { - groupedByRanks[groupRank].sort(comparator); + groupedByRanks[groupRank].sort(sorterFn); }); // assign globally unique rank to each import @@ -556,8 +574,10 @@ module.exports = { type: 'object', properties: { caseInsensitive: { - type: 'boolean', - default: false, + anyOf: [ + { type: 'boolean', default: false }, + { type: 'string', enum: ['invert'] }, + ], }, order: { enum: ['ignore', 'asc', 'desc'], diff --git a/tests/src/rules/order.js b/tests/src/rules/order.js index 42f99c2db8..42f05b5fb7 100644 --- a/tests/src/rules/order.js +++ b/tests/src/rules/order.js @@ -2121,6 +2121,54 @@ ruleTester.run('order', rule, { message: '`foo` import should occur before import of `Bar`', }], }), + // Option alphabetize {order: 'asc': caseInsensitive: 'invert'} + test({ + code: ` + import a from 'bar'; + import b from 'Foo'; + import c from 'foo'; + + import index from './'; + `, + output: ` + import a from 'bar'; + import c from 'foo'; + import b from 'Foo'; + + import index from './'; + `, + options: [{ + groups: ['external', 'index'], + alphabetize: { order: 'asc', caseInsensitive: 'invert' }, + }], + errors: [{ + message: '`foo` import should occur before import of `Foo`', + }], + }), + // Option alphabetize {order: 'desc': caseInsensitive: 'invert'} + test({ + code: ` + import a from 'foo'; + import b from 'Foo'; + import c from 'bar'; + + import index from './'; + `, + output: ` + import b from 'Foo'; + import a from 'foo'; + import c from 'bar'; + + import index from './'; + `, + options: [{ + groups: ['external', 'index'], + alphabetize: { order: 'desc', caseInsensitive: 'invert' }, + }], + errors: [{ + message: '`Foo` import should occur before import of `foo`', + }], + }), // Alphabetize with parent paths test({ code: `