Skip to content

Commit 92ea0c1

Browse files
committed
feat: add new customGroups API
1 parent 37924c9 commit 92ea0c1

4 files changed

Lines changed: 606 additions & 66 deletions

File tree

docs/content/rules/sort-imports.mdx

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,50 @@ This feature is only applicable when [`partitionByNewLine`](#partitionbynewline)
380380

381381
### customGroups
382382

383+
<Important title="Migrating from the old API">
384+
Support for the object-based `customGroups` option is deprecated.
385+
386+
Migrating from the old to the current API is easy:
387+
388+
Old API:
389+
```ts
390+
{
391+
value: {
392+
"keyForValue1": "value1",
393+
"keyForValue2": "value2"
394+
},
395+
type: {
396+
"keyForType1": "value1",
397+
"keyForType2": "value2"
398+
}
399+
}
400+
```
401+
402+
Current API:
403+
```ts
404+
[
405+
{
406+
"selector": "type",
407+
"groupName": "keyForType1",
408+
"elementNamePattern": "value1"
409+
},
410+
{
411+
"selector": "type",
412+
"groupName": "keyForType2",
413+
"elementNamePattern": "value2"
414+
},
415+
{
416+
"groupName": "keyForValue1",
417+
"elementNamePattern": "value1"
418+
},
419+
{
420+
"groupName": "keyForValue2",
421+
"elementNamePattern": "value2"
422+
}
423+
]
424+
```
425+
</Important>
426+
383427
<sub>
384428
type:
385429
```

rules/sort-imports.ts

Lines changed: 120 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,13 @@ import type {
88
Options,
99
Group,
1010
} from './sort-imports/types'
11-
import type { DeprecatedCustomGroupsOption } from '../types/common-options'
11+
import type {
12+
DeprecatedCustomGroupsOption,
13+
CustomGroupsOption,
14+
} from '../types/common-options'
1215

1316
import {
17+
buildCustomGroupsArrayJsonSchema,
1418
partitionByCommentJsonSchema,
1519
partitionByNewLineJsonSchema,
1620
newlinesBetweenJsonSchema,
@@ -25,8 +29,10 @@ import {
2529
ORDER_ERROR,
2630
} from '../utils/report-errors'
2731
import { validateNewlinesAndPartitionConfiguration } from '../utils/validate-newlines-and-partition-configuration'
32+
import { validateGeneratedGroupsConfiguration } from '../utils/validate-generated-groups-configuration'
2833
import { validateSideEffectsConfiguration } from './sort-imports/validate-side-effects-configuration'
2934
import { validateCustomSortConfiguration } from '../utils/validate-custom-sort-configuration'
35+
import { getCustomGroupOverriddenOptions } from '../utils/get-custom-groups-compare-options'
3036
import { readClosestTsConfigByPath } from './sort-imports/read-closest-ts-config-by-path'
3137
import { validateGroupsConfiguration } from '../utils/validate-groups-configuration'
3238
import { getOptionsWithCleanGroups } from '../utils/get-options-with-clean-groups'
@@ -35,8 +41,12 @@ import { isSideEffectOnlyGroup } from './sort-imports/is-side-effect-only-group'
3541
import { generatePredefinedGroups } from '../utils/generate-predefined-groups'
3642
import { getEslintDisabledLines } from '../utils/get-eslint-disabled-lines'
3743
import { isNodeEslintDisabled } from '../utils/is-node-eslint-disabled'
44+
import { doesCustomGroupMatch } from '../utils/does-custom-group-match'
45+
import { singleCustomGroupJsonSchema } from './sort-imports/types'
3846
import { sortNodesByGroups } from '../utils/sort-nodes-by-groups'
47+
import { allModifiers, allSelectors } from './sort-imports/types'
3948
import { createEslintRule } from '../utils/create-eslint-rule'
49+
import { allDeprecatedSelectors } from './sort-imports/types'
4050
import { reportAllErrors } from '../utils/report-all-errors'
4151
import { shouldPartition } from '../utils/should-partition'
4252
import { computeGroup } from '../utils/compute-group'
@@ -89,32 +99,34 @@ export default createEslintRule<Options, MESSAGE_ID>({
8999
} as const),
90100
)
91101

92-
validateGroupsConfiguration({
93-
allowedPredefinedGroups: [
94-
'side-effect-style',
95-
'external-type',
96-
'internal-type',
97-
'builtin-type',
98-
'sibling-type',
99-
'parent-type',
100-
'side-effect',
101-
'index-type',
102-
'internal',
103-
'external',
104-
'sibling',
105-
'unknown',
106-
'builtin',
107-
'parent',
108-
'index',
109-
'style',
110-
'type',
111-
],
112-
allowedCustomGroups: [
113-
...Object.keys(options.customGroups.type ?? {}),
114-
...Object.keys(options.customGroups.value ?? {}),
115-
],
116-
options,
117-
})
102+
if (Array.isArray(options.customGroups)) {
103+
validateGeneratedGroupsConfiguration({
104+
options: {
105+
...options,
106+
customGroups: options.customGroups,
107+
},
108+
selectors: allSelectors,
109+
modifiers: allModifiers,
110+
})
111+
} else {
112+
let generatedGroups = generatePredefinedGroups({
113+
cache: cachedGroupsByModifiersAndSelectors,
114+
selectors: allSelectors,
115+
modifiers: allModifiers,
116+
})
117+
validateGroupsConfiguration({
118+
allowedCustomGroups: [
119+
...Object.keys(options.customGroups.type ?? {}),
120+
...Object.keys(options.customGroups.value ?? {}),
121+
],
122+
allowedPredefinedGroups: [
123+
...generatedGroups,
124+
...allDeprecatedSelectors,
125+
'unknown',
126+
],
127+
options,
128+
})
129+
}
118130
validateCustomSortConfiguration(options)
119131
validateNewlinesAndPartitionConfiguration(options)
120132
validateSideEffectsConfiguration(options)
@@ -162,11 +174,14 @@ export default createEslintRule<Options, MESSAGE_ID>({
162174

163175
if (node.type !== 'VariableDeclaration' && node.importKind === 'type') {
164176
if (node.type === 'ImportDeclaration') {
165-
group = computeGroupExceptUnknown({
166-
customGroups: options.customGroups.type,
167-
options,
168-
name,
169-
})
177+
if (!Array.isArray(options.customGroups)) {
178+
// For deprecated `customGroups.type`
179+
group = computeGroupExceptUnknown({
180+
customGroups: options.customGroups.type,
181+
options,
182+
name,
183+
})
184+
}
170185

171186
for (let selector of commonSelectors) {
172187
selectors.push(`${selector}-type`)
@@ -176,23 +191,29 @@ export default createEslintRule<Options, MESSAGE_ID>({
176191
selectors.push('type')
177192
modifiers.push('type')
178193

179-
group ??= computeGroupExceptUnknown({
180-
selectors,
181-
modifiers,
182-
options,
183-
name,
184-
})
194+
if (!group && !Array.isArray(options.customGroups)) {
195+
group = computeGroupExceptUnknown({
196+
customGroups: [],
197+
selectors,
198+
modifiers,
199+
options,
200+
name,
201+
})
202+
}
185203
}
186204

187205
let isSideEffect = isSideEffectImport({ sourceCode, node })
188206
let isStyleValue = isStyle(name)
189207
let isStyleSideEffect = isSideEffect && isStyleValue
190208

191-
group ??= computeGroupExceptUnknown({
192-
customGroups: options.customGroups.value,
193-
options,
194-
name,
195-
})
209+
if (!group && !Array.isArray(options.customGroups)) {
210+
// For deprecated `customGroups.value`
211+
group = computeGroupExceptUnknown({
212+
customGroups: options.customGroups.value,
213+
options,
214+
name,
215+
})
216+
}
196217

197218
if (isStyleSideEffect) {
198219
selectors.push('side-effect-style')
@@ -212,8 +233,11 @@ export default createEslintRule<Options, MESSAGE_ID>({
212233

213234
group ??=
214235
computeGroupExceptUnknown({
215-
modifiers,
236+
customGroups: Array.isArray(options.customGroups)
237+
? options.customGroups
238+
: [],
216239
selectors,
240+
modifiers,
217241
options,
218242
name,
219243
}) ?? 'unknown'
@@ -269,19 +293,39 @@ export default createEslintRule<Options, MESSAGE_ID>({
269293
): SortImportsSortingNode[] =>
270294
sortNodesByGroups({
271295
getOptionsByGroupNumber: groupNumber => {
296+
let customGroupOverriddenOptions =
297+
getCustomGroupOverriddenOptions({
298+
options: {
299+
...options,
300+
customGroups: Array.isArray(options.customGroups)
301+
? options.customGroups
302+
: [],
303+
},
304+
groupNumber,
305+
})
306+
272307
if (options.sortSideEffects) {
273308
return {
274-
options,
309+
options: {
310+
...options,
311+
...customGroupOverriddenOptions,
312+
},
275313
}
276314
}
315+
let overriddenOptions = {
316+
...options,
317+
...customGroupOverriddenOptions,
318+
}
277319
return {
278320
options: {
279-
...options,
321+
...overriddenOptions,
280322
type:
281-
options.groups[groupNumber] &&
282-
isSideEffectOnlyGroup(options.groups[groupNumber])
323+
overriddenOptions.groups[groupNumber] &&
324+
isSideEffectOnlyGroup(
325+
overriddenOptions.groups[groupNumber],
326+
)
283327
? 'unsorted'
284-
: options.type,
328+
: overriddenOptions.type,
285329
},
286330
}
287331
},
@@ -300,7 +344,9 @@ export default createEslintRule<Options, MESSAGE_ID>({
300344
},
301345
options: {
302346
...options,
303-
customGroups: [],
347+
customGroups: Array.isArray(options.customGroups)
348+
? options.customGroups
349+
: [],
304350
},
305351
sortNodesExcludingEslintDisabled,
306352
sourceCode,
@@ -330,19 +376,24 @@ export default createEslintRule<Options, MESSAGE_ID>({
330376
properties: {
331377
...commonJsonSchemas,
332378
customGroups: {
333-
properties: {
334-
value: {
335-
description: 'Specifies custom groups for value imports.',
336-
type: 'object',
337-
},
338-
type: {
339-
description: 'Specifies custom groups for type imports.',
379+
oneOf: [
380+
{
381+
properties: {
382+
value: {
383+
description: 'Specifies custom groups for value imports.',
384+
type: 'object',
385+
},
386+
type: {
387+
description: 'Specifies custom groups for type imports.',
388+
type: 'object',
389+
},
390+
},
391+
description: 'Specifies custom groups.',
392+
additionalProperties: false,
340393
type: 'object',
341394
},
342-
},
343-
description: 'Specifies custom groups.',
344-
additionalProperties: false,
345-
type: 'object',
395+
buildCustomGroupsArrayJsonSchema({ singleCustomGroupJsonSchema }),
396+
],
346397
},
347398
maxLineLength: {
348399
description: 'Specifies the maximum line length.',
@@ -524,7 +575,7 @@ let computeGroupExceptUnknown = ({
524575
Required<Options[0]>,
525576
'tsconfigRootDir' | 'maxLineLength' | 'customGroups'
526577
>
527-
customGroups?: DeprecatedCustomGroupsOption | undefined
578+
customGroups: DeprecatedCustomGroupsOption | CustomGroupsOption | undefined
528579
selectors?: Selector[]
529580
modifiers?: Modifier[]
530581
name: string
@@ -538,6 +589,13 @@ let computeGroupExceptUnknown = ({
538589
})
539590
: []
540591
let computedCustomGroup = computeGroup({
592+
customGroupMatcher: customGroup =>
593+
doesCustomGroupMatch({
594+
modifiers: modifiers!,
595+
selectors: selectors!,
596+
elementName: name,
597+
customGroup,
598+
}),
541599
options: {
542600
...options,
543601
customGroups,

0 commit comments

Comments
 (0)