@@ -4,44 +4,61 @@ import type { SortingNode } from '../types/sorting-node'
4
4
import type { Options } from './sort-maps/types'
5
5
6
6
import {
7
+ buildCustomGroupsArrayJsonSchema ,
7
8
partitionByCommentJsonSchema ,
8
9
partitionByNewLineJsonSchema ,
9
10
specialCharactersJsonSchema ,
11
+ newlinesBetweenJsonSchema ,
10
12
ignoreCaseJsonSchema ,
11
13
buildTypeJsonSchema ,
12
14
alphabetJsonSchema ,
13
15
localesJsonSchema ,
16
+ groupsJsonSchema ,
14
17
orderJsonSchema ,
15
18
} from '../utils/common-json-schemas'
19
+ import { validateGeneratedGroupsConfiguration } from '../utils/validate-generated-groups-configuration'
16
20
import { validateCustomSortConfiguration } from '../utils/validate-custom-sort-configuration'
21
+ import { getCustomGroupsCompareOptions } from '../utils/get-custom-groups-compare-options'
17
22
import { getEslintDisabledLines } from '../utils/get-eslint-disabled-lines'
23
+ import { doesCustomGroupMatch } from './sort-maps/does-custom-group-match'
18
24
import { isNodeEslintDisabled } from '../utils/is-node-eslint-disabled'
19
25
import { hasPartitionComment } from '../utils/has-partition-comment'
20
26
import { createNodeIndexMap } from '../utils/create-node-index-map'
27
+ import { sortNodesByGroups } from '../utils/sort-nodes-by-groups'
21
28
import { getCommentsBefore } from '../utils/get-comments-before'
29
+ import { getNewlinesErrors } from '../utils/get-newlines-errors'
30
+ import { singleCustomGroupJsonSchema } from './sort-maps/types'
22
31
import { createEslintRule } from '../utils/create-eslint-rule'
23
32
import { getLinesBetween } from '../utils/get-lines-between'
33
+ import { getGroupNumber } from '../utils/get-group-number'
24
34
import { getSourceCode } from '../utils/get-source-code'
25
35
import { toSingleLine } from '../utils/to-single-line'
26
36
import { rangeToDiff } from '../utils/range-to-diff'
27
37
import { getSettings } from '../utils/get-settings'
28
38
import { isSortable } from '../utils/is-sortable'
29
39
import { makeFixes } from '../utils/make-fixes'
30
- import { sortNodes } from '../utils/sort-nodes '
40
+ import { useGroups } from '../utils/use-groups '
31
41
import { complete } from '../utils/complete'
32
42
import { pairwise } from '../utils/pairwise'
33
43
34
- type MESSAGE_ID = 'unexpectedMapElementsOrder'
44
+ type MESSAGE_ID =
45
+ | 'missedSpacingBetweenMapElementsMembers'
46
+ | 'extraSpacingBetweenMapElementsMembers'
47
+ | 'unexpectedMapElementsGroupOrder'
48
+ | 'unexpectedMapElementsOrder'
35
49
36
50
let defaultOptions : Required < Options [ 0 ] > = {
37
51
specialCharacters : 'keep' ,
38
52
partitionByComment : false ,
39
53
partitionByNewLine : false ,
54
+ newlinesBetween : 'ignore' ,
40
55
type : 'alphabetical' ,
41
56
ignoreCase : true ,
57
+ customGroups : [ ] ,
42
58
locales : 'en-US' ,
43
59
alphabet : '' ,
44
60
order : 'asc' ,
61
+ groups : [ ] ,
45
62
}
46
63
47
64
export default createEslintRule < Options , MESSAGE_ID > ( {
@@ -63,6 +80,12 @@ export default createEslintRule<Options, MESSAGE_ID>({
63
80
let settings = getSettings ( context . settings )
64
81
let options = complete ( context . options . at ( 0 ) , settings , defaultOptions )
65
82
validateCustomSortConfiguration ( options )
83
+ validateGeneratedGroupsConfiguration ( {
84
+ customGroups : options . customGroups ,
85
+ groups : options . groups ,
86
+ selectors : [ ] ,
87
+ modifiers : [ ] ,
88
+ } )
66
89
67
90
let sourceCode = getSourceCode ( context )
68
91
let eslintDisabledLines = getEslintDisabledLines ( {
@@ -104,12 +127,33 @@ export default createEslintRule<Options, MESSAGE_ID>({
104
127
}
105
128
106
129
let lastSortingNode = formattedMembers . at ( - 1 ) ?. at ( - 1 )
130
+
131
+ let { defineGroup, getGroup } = useGroups ( options )
132
+ for ( let customGroup of options . customGroups ) {
133
+ if (
134
+ doesCustomGroupMatch ( {
135
+ elementName : name ,
136
+ customGroup,
137
+ } )
138
+ ) {
139
+ defineGroup ( customGroup . groupName , true )
140
+ /**
141
+ * If the custom group is not referenced in the `groups` option, it
142
+ * will be ignored
143
+ */
144
+ if ( getGroup ( ) === customGroup . groupName ) {
145
+ break
146
+ }
147
+ }
148
+ }
149
+
107
150
let sortingNode : SortingNode = {
108
151
isEslintDisabled : isNodeEslintDisabled (
109
152
element ,
110
153
eslintDisabledLines ,
111
154
) ,
112
155
size : rangeToDiff ( element , sourceCode ) ,
156
+ group : getGroup ( ) ,
113
157
node : element ,
114
158
name,
115
159
}
@@ -136,7 +180,11 @@ export default createEslintRule<Options, MESSAGE_ID>({
136
180
let sortNodesExcludingEslintDisabled = (
137
181
ignoreEslintDisabledNodes : boolean ,
138
182
) : SortingNode [ ] =>
139
- sortNodes ( nodes , options , { ignoreEslintDisabledNodes } )
183
+ sortNodesByGroups ( nodes , options , {
184
+ getGroupCompareOptions : groupNumber =>
185
+ getCustomGroupsCompareOptions ( options , groupNumber ) ,
186
+ ignoreEslintDisabledNodes,
187
+ } )
140
188
let sortedNodes = sortNodesExcludingEslintDisabled ( false )
141
189
let sortedNodesExcludingEslintDisabled =
142
190
sortNodesExcludingEslintDisabled ( true )
@@ -147,31 +195,59 @@ export default createEslintRule<Options, MESSAGE_ID>({
147
195
let leftIndex = nodeIndexMap . get ( left ) !
148
196
let rightIndex = nodeIndexMap . get ( right ) !
149
197
198
+ let leftNumber = getGroupNumber ( options . groups , left )
199
+ let rightNumber = getGroupNumber ( options . groups , right )
200
+
150
201
let indexOfRightExcludingEslintDisabled =
151
202
sortedNodesExcludingEslintDisabled . indexOf ( right )
203
+
204
+ let messageIds : MESSAGE_ID [ ] = [ ]
205
+
152
206
if (
153
- leftIndex < rightIndex &&
154
- leftIndex < indexOfRightExcludingEslintDisabled
207
+ leftIndex > rightIndex ||
208
+ leftIndex >= indexOfRightExcludingEslintDisabled
155
209
) {
156
- return
210
+ messageIds . push (
211
+ leftNumber === rightNumber
212
+ ? 'unexpectedMapElementsOrder'
213
+ : 'unexpectedMapElementsGroupOrder' ,
214
+ )
157
215
}
158
216
159
- context . report ( {
160
- fix : fixer =>
161
- makeFixes ( {
162
- sortedNodes : sortedNodesExcludingEslintDisabled ,
163
- sourceCode,
164
- options,
165
- fixer,
166
- nodes,
167
- } ) ,
168
- data : {
169
- right : toSingleLine ( right . name ) ,
170
- left : toSingleLine ( left . name ) ,
171
- } ,
172
- messageId : 'unexpectedMapElementsOrder' ,
173
- node : right . node ,
174
- } )
217
+ messageIds = [
218
+ ...messageIds ,
219
+ ...getNewlinesErrors ( {
220
+ missedSpacingError : 'missedSpacingBetweenMapElementsMembers' ,
221
+ extraSpacingError : 'extraSpacingBetweenMapElementsMembers' ,
222
+ rightNum : rightNumber ,
223
+ leftNum : leftNumber ,
224
+ sourceCode,
225
+ options,
226
+ right,
227
+ left,
228
+ } ) ,
229
+ ]
230
+
231
+ for ( let messageId of messageIds ) {
232
+ context . report ( {
233
+ fix : fixer =>
234
+ makeFixes ( {
235
+ sortedNodes : sortedNodesExcludingEslintDisabled ,
236
+ sourceCode,
237
+ options,
238
+ fixer,
239
+ nodes,
240
+ } ) ,
241
+ data : {
242
+ right : toSingleLine ( right . name ) ,
243
+ left : toSingleLine ( left . name ) ,
244
+ rightGroup : right . group ,
245
+ leftGroup : left . group ,
246
+ } ,
247
+ node : right . node ,
248
+ messageId,
249
+ } )
250
+ }
175
251
} )
176
252
}
177
253
}
@@ -186,27 +262,38 @@ export default createEslintRule<Options, MESSAGE_ID>({
186
262
description :
187
263
'Allows you to use comments to separate the maps members into logical groups.' ,
188
264
} ,
265
+ customGroups : buildCustomGroupsArrayJsonSchema ( {
266
+ singleCustomGroupJsonSchema,
267
+ } ) ,
189
268
partitionByNewLine : partitionByNewLineJsonSchema ,
190
269
specialCharacters : specialCharactersJsonSchema ,
270
+ newlinesBetween : newlinesBetweenJsonSchema ,
191
271
ignoreCase : ignoreCaseJsonSchema ,
192
272
alphabet : alphabetJsonSchema ,
193
273
type : buildTypeJsonSchema ( ) ,
194
274
locales : localesJsonSchema ,
275
+ groups : groupsJsonSchema ,
195
276
order : orderJsonSchema ,
196
277
} ,
197
278
additionalProperties : false ,
198
279
type : 'object' ,
199
280
} ,
200
281
] ,
282
+ messages : {
283
+ unexpectedMapElementsGroupOrder :
284
+ 'Expected "{{right}}" ({{rightGroup}}) to come before "{{left}}" ({{leftGroup}}).' ,
285
+ missedSpacingBetweenMapElementsMembers :
286
+ 'Missed spacing between "{{left}}" and "{{right}}" members.' ,
287
+ extraSpacingBetweenMapElementsMembers :
288
+ 'Extra spacing between "{{left}}" and "{{right}}" members.' ,
289
+ unexpectedMapElementsOrder :
290
+ 'Expected "{{right}}" to come before "{{left}}".' ,
291
+ } ,
201
292
docs : {
202
293
url : 'https://perfectionist.dev/rules/sort-maps' ,
203
294
description : 'Enforce sorted Map elements.' ,
204
295
recommended : true ,
205
296
} ,
206
- messages : {
207
- unexpectedMapElementsOrder :
208
- 'Expected "{{right}}" to come before "{{left}}".' ,
209
- } ,
210
297
type : 'suggestion' ,
211
298
fixable : 'code' ,
212
299
} ,
0 commit comments