Skip to content

Commit 80b4e6f

Browse files
committed
feat: add perfectionist/sort-named-imports and sort-named-exports
https://perfectionist.dev/rules/sort-named-imports https://perfectionist.dev/rules/sort-named-exports We had named imports sorted in v1 when tslint was taking care of that. But it got lost during migration to eslint, as I wrongly assumed that import/order will take care of that. Later on, the support for sorting named imports (but still not exports) was added to the original eslint-plugin-import import-js/eslint-plugin-import#3043 but we have switched to eslint-plugin-import-x since and the porting request of that feature is still pending. un-ts/eslint-plugin-import-x#225 We could consider switching back to eslint-plugin-import but it still has a set of its own, still unresolved issues. Instead, I decided to try eslint-plugin-perfectionist and the sorting rules from that. Note we are sticking with import/order for sorting and grouping imports for the time being, as more time would be needed to convert the sorting and grouping config to achieve similar behaviour with the perfectionist/sort-imports rule. There is also perfectionist/sort-exports rule, but it does not provide config options to define grouping and custom sorting, to achieve similar behaviour to import/order with our custom config applied, so for now we won't enforce that rule.
1 parent afce3a2 commit 80b4e6f

File tree

2 files changed

+27
-2
lines changed

2 files changed

+27
-2
lines changed

eslint.ts

+26-2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import * as tsParser from '@typescript-eslint/parser';
55
import { Linter } from 'eslint';
66
import checkFilePlugin from 'eslint-plugin-check-file';
77
import * as importPlugin from 'eslint-plugin-import'; // aliased to eslint-plugin-import-x https://github.com/un-ts/eslint-plugin-import-x
8+
import perfectionist from 'eslint-plugin-perfectionist';
89
import globals from 'globals';
910

1011
type CustomizeOptions = {
@@ -82,6 +83,21 @@ function customize(options: CustomizeOptions = {}) {
8283
plugins: {
8384
// https://eslint.style/ providing replacement for formatting rules, which are now deprecated in eslint and @typescript-eslint
8485
'@stylistic': { rules: stylistic.rules },
86+
perfectionist,
87+
},
88+
settings: {
89+
// https://perfectionist.dev/guide/getting-started#settings
90+
perfectionist: {
91+
type: 'custom',
92+
ignoreCase: false,
93+
// 'perfectionist' uses .localeCompare() which by default which sorts '123..AaBbCc..'
94+
// we want to put uppercase before lowercase, the rest stays the same (esp. symbols)
95+
// Alphabet from 'perfectionist' could be used, such as
96+
// `Alphabet.generateRecommendedAlphabet().sortByNaturalSort('en-US').placeAllWithCaseBeforeAllWithOtherCase('uppercase').getCharacters()`
97+
// but that contains 128k chars, which is unnecessarily large
98+
// so recreated only the needed part of the alphabet, with uppercase before lowercase
99+
alphabet: '_-.@/#~$0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz',
100+
},
85101
},
86102
},
87103
// common settings for typescript files
@@ -163,7 +179,10 @@ function customize(options: CustomizeOptions = {}) {
163179
// we still sometimes want to use dynamic, sync `require()` instead of `await import()`
164180
'@typescript-eslint/no-require-imports': 'off',
165181
// allow `interface I extends Base<Param> {}` syntax
166-
'@typescript-eslint/no-empty-object-type': ['error', { allowInterfaces: 'with-single-extends' }],
182+
'@typescript-eslint/no-empty-object-type': [
183+
'error',
184+
{ allowInterfaces: 'with-single-extends' },
185+
],
167186
// even though the rules blow are already reconfigured for eslint:recommended,
168187
// they need to be reconfigured again for typescript files, with the same options repeated
169188
'@typescript-eslint/no-unused-expressions': shared['no-unused-expressions'],
@@ -355,6 +374,11 @@ function customize(options: CustomizeOptions = {}) {
355374
'no-param-reassign': 'error',
356375
'object-shorthand': 'error',
357376
'one-var': ['error', 'never'],
377+
'perfectionist/sort-named-exports': ['error', { groupKind: 'types-first' }],
378+
'perfectionist/sort-named-imports': [
379+
'error',
380+
{ ignoreAlias: true, groupKind: 'types-first' },
381+
],
358382
'prefer-arrow-callback': ['error', { allowNamedFunctions: true }],
359383
...(consoleUsage !== 'allow' && {
360384
'no-console':
@@ -583,7 +607,7 @@ function customize(options: CustomizeOptions = {}) {
583607
rules: {
584608
'mocha/no-exports': 'off',
585609
},
586-
},
610+
}
587611
);
588612
}
589613

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
"eslint-plugin-import": "npm:eslint-plugin-import-x@^4.6.1",
2727
"eslint-plugin-jest": "^28.10.0",
2828
"eslint-plugin-mocha": "^10.5.0",
29+
"eslint-plugin-perfectionist": "^4.6.0",
2930
"eslint-plugin-react": "^7.37.3",
3031
"eslint-plugin-react-hooks": "^5.1.0",
3132
"globals": "^15.14.0"

0 commit comments

Comments
 (0)