Skip to content

Commit 2c28d11

Browse files
authored
feat(sort-named-imports): support conditional config by ast selector and name pattern
1 parent 498bef9 commit 2c28d11

23 files changed

Lines changed: 1182 additions & 570 deletions

docs/content/rules/sort-named-exports.mdx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,8 @@ Specifies filters to match a particular options configuration for a given named
218218

219219
The first matching options configuration will be used. If no configuration matches, the default options configuration will be used.
220220

221-
- `allNamesMatchPattern` — A regexp pattern that all named export names must match.
221+
- `allNamesMatchPattern` — A regexp pattern that all named export names must match. If [`ignoreAlias`](#ignorealias)
222+
is `true`, the pattern will be tested against the export aliases, otherwise against the local names.
222223

223224
Example configuration:
224225
```ts

docs/content/rules/sort-named-imports.mdx

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,65 @@ Specifies whether to use the import alias as the name for sorting instead of the
198198
- `true` — Use the import alias for sorting.
199199
- `false` — Use the exported name for sorting.
200200

201+
### useConfigurationIf
202+
203+
<sub>
204+
type:
205+
```ts
206+
{
207+
allNamesMatchPattern?:
208+
| string
209+
| string[]
210+
| { pattern: string; flags: string }
211+
| { pattern: string; flags: string }[]
212+
matchesAstSelector?: string
213+
}
214+
```
215+
</sub>
216+
<sub>default: `{}`</sub>
217+
218+
Specifies filters to match a particular options configuration for a given named import declaration.
219+
220+
The first matching options configuration will be used. If no configuration matches, the default options configuration will be used.
221+
222+
- `allNamesMatchPattern` — A regexp pattern that all named import names must match. If [`ignoreAlias`](#ignorealias)
223+
is `true`, the pattern will be tested against the import aliases, otherwise against the exported names.
224+
225+
Example configuration:
226+
```ts
227+
{
228+
'perfectionist/sort-named-imports': [
229+
'error',
230+
{
231+
groups: ['r', 'g', 'b'], // Sort colors by RGB
232+
customGroups: [
233+
{
234+
elementNamePattern: '^r$',
235+
groupName: 'r',
236+
},
237+
{
238+
elementNamePattern: '^g$',
239+
groupName: 'g',
240+
},
241+
{
242+
elementNamePattern: '^b$',
243+
groupName: 'b',
244+
},
245+
],
246+
useConfigurationIf: {
247+
allNamesMatchPattern: '^[rgb]$',
248+
},
249+
},
250+
{
251+
type: 'alphabetical' // Fallback configuration
252+
}
253+
],
254+
}
255+
```
256+
257+
- `matchesAstSelector` — An [AST selector](https://eslint.org/docs/latest/extend/selectors) matching an `ImportDeclaration` node.
258+
To avoid unexpected behavior, do not use `:exit` or `:enter` pseudo-selectors.
259+
201260
### partitionByComment
202261

203262
<sub>default: `false`</sub>

rules/sort-array-includes/compute-matched-context-options.ts

Lines changed: 26 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import type { TSESTree } from '@typescript-eslint/types'
33

44
import type { Options } from './types'
55

6-
import { filterOptionsByAllNamesMatch } from '../../utils/context-matching/filter-options-by-all-names-match'
6+
import { passesAllNamesMatchPatternFilter } from '../../utils/context-matching/passes-all-names-match-pattern-filter'
77
import { passesAstSelectorFilter } from '../../utils/context-matching/passes-ast-selector-filter'
88
import { computeNodeName } from './compute-node-name'
99

@@ -33,27 +33,32 @@ export function computeMatchedContextOptions<MessageIds extends string>({
3333
computeNodeName({ sourceCode: context.sourceCode, node: element }),
3434
)
3535

36-
let matchedContextOptions = filterOptionsByAllNamesMatch({
37-
contextOptions: context.options,
38-
nodeNames,
39-
})
40-
41-
return matchedContextOptions.find(isContextOptionMatching)
42-
43-
function isContextOptionMatching(options: Options[number]): boolean {
44-
if (!options.useConfigurationIf) {
45-
return true
46-
}
47-
48-
if (
49-
!passesAstSelectorFilter({
50-
matchesAstSelector: options.useConfigurationIf.matchesAstSelector,
51-
matchedAstSelectors,
52-
})
53-
) {
54-
return false
55-
}
36+
return context.options.find(options =>
37+
isContextOptionMatching({ matchedAstSelectors, nodeNames, options }),
38+
)
39+
}
5640

41+
function isContextOptionMatching({
42+
matchedAstSelectors,
43+
nodeNames,
44+
options,
45+
}: {
46+
matchedAstSelectors: ReadonlySet<string>
47+
options: Options[number]
48+
nodeNames: string[]
49+
}): boolean {
50+
if (!options.useConfigurationIf) {
5751
return true
5852
}
53+
54+
return (
55+
passesAllNamesMatchPatternFilter({
56+
allNamesMatchPattern: options.useConfigurationIf.allNamesMatchPattern,
57+
nodeNames,
58+
}) &&
59+
passesAstSelectorFilter({
60+
matchesAstSelector: options.useConfigurationIf.matchesAstSelector,
61+
matchedAstSelectors,
62+
})
63+
)
5964
}

rules/sort-classes/compute-matched-context-options.ts

Lines changed: 26 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { AST_NODE_TYPES } from '@typescript-eslint/utils'
55

66
import type { Options } from './types'
77

8-
import { filterOptionsByAllNamesMatch } from '../../utils/context-matching/filter-options-by-all-names-match'
8+
import { passesAllNamesMatchPatternFilter } from '../../utils/context-matching/passes-all-names-match-pattern-filter'
99
import { computeMethodOrPropertyNameDetails } from './node-info/compute-method-or-property-name-details'
1010
import { passesAstSelectorFilter } from '../../utils/context-matching/passes-ast-selector-filter'
1111

@@ -38,27 +38,32 @@ export function computeMatchedContextOptions<MessageIds extends string>({
3838
computeMethodOrPropertyNameDetails(element, context.sourceCode).name,
3939
)
4040

41-
let matchedContextOptions = filterOptionsByAllNamesMatch({
42-
contextOptions: context.options,
43-
nodeNames,
44-
})
45-
46-
return matchedContextOptions.find(isContextOptionMatching)
47-
48-
function isContextOptionMatching(options: Options[number]): boolean {
49-
if (!options.useConfigurationIf) {
50-
return true
51-
}
52-
53-
if (
54-
!passesAstSelectorFilter({
55-
matchesAstSelector: options.useConfigurationIf.matchesAstSelector,
56-
matchedAstSelectors,
57-
})
58-
) {
59-
return false
60-
}
41+
return context.options.find(options =>
42+
isContextOptionMatching({ matchedAstSelectors, nodeNames, options }),
43+
)
44+
}
6145

46+
function isContextOptionMatching({
47+
matchedAstSelectors,
48+
nodeNames,
49+
options,
50+
}: {
51+
matchedAstSelectors: ReadonlySet<string>
52+
options: Options[number]
53+
nodeNames: string[]
54+
}): boolean {
55+
if (!options.useConfigurationIf) {
6256
return true
6357
}
58+
59+
return (
60+
passesAllNamesMatchPatternFilter({
61+
allNamesMatchPattern: options.useConfigurationIf.allNamesMatchPattern,
62+
nodeNames,
63+
}) &&
64+
passesAstSelectorFilter({
65+
matchesAstSelector: options.useConfigurationIf.matchesAstSelector,
66+
matchedAstSelectors,
67+
})
68+
)
6469
}

rules/sort-enums/compute-matched-context-options.ts

Lines changed: 26 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import type { TSESTree } from '@typescript-eslint/types'
33

44
import type { Options } from './types'
55

6-
import { filterOptionsByAllNamesMatch } from '../../utils/context-matching/filter-options-by-all-names-match'
6+
import { passesAllNamesMatchPatternFilter } from '../../utils/context-matching/passes-all-names-match-pattern-filter'
77
import { passesAstSelectorFilter } from '../../utils/context-matching/passes-ast-selector-filter'
88
import { computeNodeName } from './compute-node-name'
99

@@ -30,27 +30,32 @@ export function computeMatchedContextOptions<MessageIds extends string>({
3030
computeNodeName({ sourceCode: context.sourceCode, node: enumMember }),
3131
)
3232

33-
let matchedContextOptions = filterOptionsByAllNamesMatch({
34-
contextOptions: context.options,
35-
nodeNames,
36-
})
37-
38-
return matchedContextOptions.find(isContextOptionMatching)
39-
40-
function isContextOptionMatching(options: Options[number]): boolean {
41-
if (!options.useConfigurationIf) {
42-
return true
43-
}
44-
45-
if (
46-
!passesAstSelectorFilter({
47-
matchesAstSelector: options.useConfigurationIf.matchesAstSelector,
48-
matchedAstSelectors,
49-
})
50-
) {
51-
return false
52-
}
33+
return context.options.find(options =>
34+
isContextOptionMatching({ matchedAstSelectors, nodeNames, options }),
35+
)
36+
}
5337

38+
function isContextOptionMatching({
39+
matchedAstSelectors,
40+
nodeNames,
41+
options,
42+
}: {
43+
matchedAstSelectors: ReadonlySet<string>
44+
options: Options[number]
45+
nodeNames: string[]
46+
}): boolean {
47+
if (!options.useConfigurationIf) {
5448
return true
5549
}
50+
51+
return (
52+
passesAllNamesMatchPatternFilter({
53+
allNamesMatchPattern: options.useConfigurationIf.allNamesMatchPattern,
54+
nodeNames,
55+
}) &&
56+
passesAstSelectorFilter({
57+
matchesAstSelector: options.useConfigurationIf.matchesAstSelector,
58+
matchedAstSelectors,
59+
})
60+
)
5661
}

rules/sort-heritage-clauses/compute-matched-context-options.ts

Lines changed: 26 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import type { TSESTree } from '@typescript-eslint/types'
33

44
import type { Options } from './types'
55

6-
import { filterOptionsByAllNamesMatch } from '../../utils/context-matching/filter-options-by-all-names-match'
6+
import { passesAllNamesMatchPatternFilter } from '../../utils/context-matching/passes-all-names-match-pattern-filter'
77
import { passesAstSelectorFilter } from '../../utils/context-matching/passes-ast-selector-filter'
88
import { computeNodeName } from './compute-node-name'
99

@@ -30,27 +30,32 @@ export function computeMatchedContextOptions<MessageIds extends string>({
3030
computeNodeName(clause.expression),
3131
)
3232

33-
let matchedContextOptions = filterOptionsByAllNamesMatch({
34-
contextOptions: context.options,
35-
nodeNames,
36-
})
37-
38-
return matchedContextOptions.find(isContextOptionMatching)
39-
40-
function isContextOptionMatching(options: Options[number]): boolean {
41-
if (!options.useConfigurationIf) {
42-
return true
43-
}
44-
45-
if (
46-
!passesAstSelectorFilter({
47-
matchesAstSelector: options.useConfigurationIf.matchesAstSelector,
48-
matchedAstSelectors,
49-
})
50-
) {
51-
return false
52-
}
33+
return context.options.find(options =>
34+
isContextOptionMatching({ matchedAstSelectors, nodeNames, options }),
35+
)
36+
}
5337

38+
function isContextOptionMatching({
39+
matchedAstSelectors,
40+
nodeNames,
41+
options,
42+
}: {
43+
matchedAstSelectors: ReadonlySet<string>
44+
options: Options[number]
45+
nodeNames: string[]
46+
}): boolean {
47+
if (!options.useConfigurationIf) {
5448
return true
5549
}
50+
51+
return (
52+
passesAllNamesMatchPatternFilter({
53+
allNamesMatchPattern: options.useConfigurationIf.allNamesMatchPattern,
54+
nodeNames,
55+
}) &&
56+
passesAstSelectorFilter({
57+
matchesAstSelector: options.useConfigurationIf.matchesAstSelector,
58+
matchedAstSelectors,
59+
})
60+
)
5661
}

0 commit comments

Comments
 (0)