@@ -46,6 +46,14 @@ function isReservedPropName(name, list) {
46
46
return list . indexOf ( name ) >= 0 ;
47
47
}
48
48
49
+ let attributeMap ;
50
+ // attributeMap = [endrange, true||false if comment in between nodes exists, it needs to be sorted to end]
51
+
52
+ function shouldSortToEnd ( node ) {
53
+ const attr = attributeMap . get ( node ) ;
54
+ return ! ! attr && ! ! attr [ 1 ] ;
55
+ }
56
+
49
57
function contextCompare ( a , b , options ) {
50
58
let aProp = propName ( a ) ;
51
59
let bProp = propName ( b ) ;
@@ -98,6 +106,17 @@ function contextCompare(a, b, options) {
98
106
return 0 ;
99
107
}
100
108
109
+ if ( options . commentbetween ) {
110
+ const aSortToEnd = shouldSortToEnd ( a ) ;
111
+ const bSortToEnd = shouldSortToEnd ( b ) ;
112
+ if ( aSortToEnd && ! bSortToEnd ) {
113
+ return 1 ;
114
+ }
115
+ if ( ! aSortToEnd && bSortToEnd ) {
116
+ return - 1 ;
117
+ }
118
+ }
119
+
101
120
const actualLocale = options . locale === 'auto' ? undefined : options . locale ;
102
121
103
122
if ( options . ignoreCase ) {
@@ -118,32 +137,77 @@ function contextCompare(a, b, options) {
118
137
* Create an array of arrays where each subarray is composed of attributes
119
138
* that are considered sortable.
120
139
* @param {Array<JSXSpreadAttribute|JSXAttribute> } attributes
140
+ * @param {Object } context The context of the rule
121
141
* @return {Array<Array<JSXAttribute>> }
122
142
*/
123
- function getGroupsOfSortableAttributes ( attributes ) {
143
+ function getGroupsOfSortableAttributes ( attributes , context ) {
144
+ const sourceCode = context . getSourceCode ( ) ;
145
+
124
146
const sortableAttributeGroups = [ ] ;
125
147
let groupCount = 0 ;
148
+ function addtoSortableAttributeGroups ( attribute ) {
149
+ sortableAttributeGroups [ groupCount - 1 ] . push ( attribute ) ;
150
+ }
151
+
126
152
for ( let i = 0 ; i < attributes . length ; i ++ ) {
153
+ const attribute = attributes [ i ] ;
154
+ const nextAttribute = attributes [ i + 1 ] ;
155
+ const attributeline = attribute . loc . start . line ;
156
+ const comment = sourceCode . getCommentsAfter ( attribute ) ;
127
157
const lastAttr = attributes [ i - 1 ] ;
158
+
159
+ const attrIsSpread = attribute . type === 'JSXSpreadAttribute' ;
160
+
128
161
// If we have no groups or if the last attribute was JSXSpreadAttribute
129
162
// then we start a new group. Append attributes to the group until we
130
163
// come across another JSXSpreadAttribute or exhaust the array.
131
164
if (
132
165
! lastAttr
133
- || ( lastAttr . type === 'JSXSpreadAttribute'
134
- && attributes [ i ] . type !== 'JSXSpreadAttribute' )
166
+ || ( lastAttr . type === 'JSXSpreadAttribute' && ! attrIsSpread )
135
167
) {
136
168
groupCount += 1 ;
137
169
sortableAttributeGroups [ groupCount - 1 ] = [ ] ;
138
170
}
139
- if ( attributes [ i ] . type !== 'JSXSpreadAttribute' ) {
140
- sortableAttributeGroups [ groupCount - 1 ] . push ( attributes [ i ] ) ;
171
+ if ( ! attrIsSpread ) {
172
+ if ( comment . length === 0 ) {
173
+ attributeMap . set ( attribute , [ attribute . range [ 1 ] , false ] ) ;
174
+ addtoSortableAttributeGroups ( attribute ) ;
175
+ } else {
176
+ const firstComment = comment [ 0 ] ;
177
+ const commentline = firstComment . loc . start . line ;
178
+ if ( comment . length === 1 ) {
179
+ if ( attributeline + 1 === commentline && nextAttribute ) {
180
+ attributeMap . set ( attribute , [ nextAttribute . range [ 1 ] , true ] ) ;
181
+ addtoSortableAttributeGroups ( attribute ) ;
182
+ i ++ ;
183
+ } else if ( attributeline === commentline ) {
184
+ if ( firstComment . type === 'Block' ) {
185
+ attributeMap . set ( attribute , [ nextAttribute . range [ 1 ] , true ] ) ;
186
+ i ++ ;
187
+ } else {
188
+ attributeMap . set ( attribute , [ firstComment . range [ 1 ] , false ] ) ;
189
+ }
190
+ addtoSortableAttributeGroups ( attribute ) ;
191
+ }
192
+ } else if ( comment . length > 1 && attributeline + 1 === comment [ 1 ] . loc . start . line && nextAttribute ) {
193
+ const commentNextAttribute = sourceCode . getCommentsAfter ( nextAttribute ) ;
194
+ attributeMap . set ( attribute , [ nextAttribute . range [ 1 ] , true ] ) ;
195
+ if (
196
+ commentNextAttribute . length === 1
197
+ && nextAttribute . loc . start . line === commentNextAttribute [ 0 ] . loc . start . line
198
+ ) {
199
+ attributeMap . set ( attribute , [ commentNextAttribute [ 0 ] . range [ 1 ] , true ] ) ;
200
+ }
201
+ addtoSortableAttributeGroups ( attribute ) ;
202
+ i ++ ;
203
+ }
204
+ }
141
205
}
142
206
}
143
207
return sortableAttributeGroups ;
144
208
}
145
209
146
- const generateFixerFunction = ( node , context , reservedList ) => {
210
+ function generateFixerFunction ( node , context , reservedList ) {
147
211
const sourceCode = context . getSourceCode ( ) ;
148
212
const attributes = node . attributes . slice ( 0 ) ;
149
213
const configuration = context . options [ 0 ] || { } ;
@@ -155,6 +219,7 @@ const generateFixerFunction = (node, context, reservedList) => {
155
219
const noSortAlphabetically = configuration . noSortAlphabetically || false ;
156
220
const reservedFirst = configuration . reservedFirst || false ;
157
221
const locale = configuration . locale || 'auto' ;
222
+ const commentbetween = configuration . commentbetween || true ;
158
223
159
224
// Sort props according to the context. Only supports ignoreCase.
160
225
// Since we cannot safely move JSXSpreadAttribute (due to potential variable overrides),
@@ -169,8 +234,9 @@ const generateFixerFunction = (node, context, reservedList) => {
169
234
reservedFirst,
170
235
reservedList,
171
236
locale,
237
+ commentbetween,
172
238
} ;
173
- const sortableAttributeGroups = getGroupsOfSortableAttributes ( attributes ) ;
239
+ const sortableAttributeGroups = getGroupsOfSortableAttributes ( attributes , context ) ;
174
240
const sortedAttributeGroups = sortableAttributeGroups
175
241
. slice ( 0 )
176
242
. map ( ( group ) => group . slice ( 0 ) . sort ( ( a , b ) => contextCompare ( a , b , options ) ) ) ;
@@ -179,13 +245,13 @@ const generateFixerFunction = (node, context, reservedList) => {
179
245
const fixers = [ ] ;
180
246
let source = sourceCode . getText ( ) ;
181
247
182
- // Replace each unsorted attribute with the sorted one.
183
248
sortableAttributeGroups . forEach ( ( sortableGroup , ii ) => {
184
249
sortableGroup . forEach ( ( attr , jj ) => {
185
250
const sortedAttr = sortedAttributeGroups [ ii ] [ jj ] ;
186
- const sortedAttrText = sourceCode . getText ( sortedAttr ) ;
251
+ const sortedAttrText = source . substring ( sortedAttr . range [ 0 ] , attributeMap . get ( sortedAttr ) [ 0 ] ) ;
252
+ const attrrangeEnd = attributeMap . get ( attr ) [ 0 ] ;
187
253
fixers . push ( {
188
- range : [ attr . range [ 0 ] , attr . range [ 1 ] ] ,
254
+ range : [ attr . range [ 0 ] , attrrangeEnd ] ,
189
255
text : sortedAttrText ,
190
256
} ) ;
191
257
} ) ;
@@ -202,7 +268,7 @@ const generateFixerFunction = (node, context, reservedList) => {
202
268
203
269
return fixer . replaceTextRange ( [ rangeStart , rangeEnd ] , source . substr ( rangeStart , rangeEnd - rangeStart ) ) ;
204
270
} ;
205
- } ;
271
+ }
206
272
207
273
/**
208
274
* Checks if the `reservedFirst` option is valid
@@ -331,15 +397,17 @@ module.exports = {
331
397
const noSortAlphabetically = configuration . noSortAlphabetically || false ;
332
398
const reservedFirst = configuration . reservedFirst || false ;
333
399
const reservedFirstError = validateReservedFirstConfig ( context , reservedFirst ) ;
334
- let reservedList = Array . isArray ( reservedFirst ) ? reservedFirst : RESERVED_PROPS_LIST ;
400
+ const reservedList = Array . isArray ( reservedFirst ) ? reservedFirst : RESERVED_PROPS_LIST ;
335
401
const locale = configuration . locale || 'auto' ;
336
402
337
403
return {
404
+ Program ( ) {
405
+ attributeMap = new WeakMap ( ) ;
406
+ } ,
407
+
338
408
JSXOpeningElement ( node ) {
339
409
// `dangerouslySetInnerHTML` is only "reserved" on DOM components
340
- if ( reservedFirst && ! jsxUtil . isDOMComponent ( node ) ) {
341
- reservedList = reservedList . filter ( ( prop ) => prop !== 'dangerouslySetInnerHTML' ) ;
342
- }
410
+ const nodeReservedList = reservedFirst && ! jsxUtil . isDOMComponent ( node ) ? reservedList . filter ( ( prop ) => prop !== 'dangerouslySetInnerHTML' ) : reservedList ;
343
411
344
412
node . attributes . reduce ( ( memo , decl , idx , attrs ) => {
345
413
if ( decl . type === 'JSXSpreadAttribute' ) {
@@ -352,8 +420,6 @@ module.exports = {
352
420
const currentValue = decl . value ;
353
421
const previousIsCallback = isCallbackPropName ( previousPropName ) ;
354
422
const currentIsCallback = isCallbackPropName ( currentPropName ) ;
355
- const previousIsMultiline = isMultilineProp ( memo ) ;
356
- const currentIsMultiline = isMultilineProp ( decl ) ;
357
423
358
424
if ( ignoreCase ) {
359
425
previousPropName = previousPropName . toLowerCase ( ) ;
@@ -366,14 +432,14 @@ module.exports = {
366
432
return memo ;
367
433
}
368
434
369
- const previousIsReserved = isReservedPropName ( previousPropName , reservedList ) ;
370
- const currentIsReserved = isReservedPropName ( currentPropName , reservedList ) ;
435
+ const previousIsReserved = isReservedPropName ( previousPropName , nodeReservedList ) ;
436
+ const currentIsReserved = isReservedPropName ( currentPropName , nodeReservedList ) ;
371
437
372
438
if ( previousIsReserved && ! currentIsReserved ) {
373
439
return decl ;
374
440
}
375
441
if ( ! previousIsReserved && currentIsReserved ) {
376
- reportNodeAttribute ( decl , 'listReservedPropsFirst' , node , context , reservedList ) ;
442
+ reportNodeAttribute ( decl , 'listReservedPropsFirst' , node , context , nodeReservedList ) ;
377
443
378
444
return memo ;
379
445
}
@@ -386,7 +452,7 @@ module.exports = {
386
452
}
387
453
if ( previousIsCallback && ! currentIsCallback ) {
388
454
// Encountered a non-callback prop after a callback prop
389
- reportNodeAttribute ( memo , 'listCallbacksLast' , node , context , reservedList ) ;
455
+ reportNodeAttribute ( memo , 'listCallbacksLast' , node , context , nodeReservedList ) ;
390
456
391
457
return memo ;
392
458
}
@@ -397,7 +463,7 @@ module.exports = {
397
463
return decl ;
398
464
}
399
465
if ( ! currentValue && previousValue ) {
400
- reportNodeAttribute ( decl , 'listShorthandFirst' , node , context , reservedList ) ;
466
+ reportNodeAttribute ( decl , 'listShorthandFirst' , node , context , nodeReservedList ) ;
401
467
402
468
return memo ;
403
469
}
@@ -408,33 +474,33 @@ module.exports = {
408
474
return decl ;
409
475
}
410
476
if ( currentValue && ! previousValue ) {
411
- reportNodeAttribute ( memo , 'listShorthandLast' , node , context , reservedList ) ;
477
+ reportNodeAttribute ( memo , 'listShorthandLast' , node , context , nodeReservedList ) ;
412
478
413
479
return memo ;
414
480
}
415
481
}
416
482
483
+ const previousIsMultiline = isMultilineProp ( memo ) ;
484
+ const currentIsMultiline = isMultilineProp ( decl ) ;
417
485
if ( multiline === 'first' ) {
418
486
if ( previousIsMultiline && ! currentIsMultiline ) {
419
487
// Exiting the multiline prop section
420
488
return decl ;
421
489
}
422
490
if ( ! previousIsMultiline && currentIsMultiline ) {
423
491
// Encountered a non-multiline prop before a multiline prop
424
- reportNodeAttribute ( decl , 'listMultilineFirst' , node , context , reservedList ) ;
492
+ reportNodeAttribute ( decl , 'listMultilineFirst' , node , context , nodeReservedList ) ;
425
493
426
494
return memo ;
427
495
}
428
- }
429
-
430
- if ( multiline === 'last' ) {
496
+ } else if ( multiline === 'last' ) {
431
497
if ( ! previousIsMultiline && currentIsMultiline ) {
432
498
// Entering the multiline prop section
433
499
return decl ;
434
500
}
435
501
if ( previousIsMultiline && ! currentIsMultiline ) {
436
502
// Encountered a non-multiline prop after a multiline prop
437
- reportNodeAttribute ( memo , 'listMultilineLast' , node , context , reservedList ) ;
503
+ reportNodeAttribute ( memo , 'listMultilineLast' , node , context , nodeReservedList ) ;
438
504
439
505
return memo ;
440
506
}
@@ -448,7 +514,7 @@ module.exports = {
448
514
: previousPropName > currentPropName
449
515
)
450
516
) {
451
- reportNodeAttribute ( decl , 'sortPropsByAlpha' , node , context , reservedList ) ;
517
+ reportNodeAttribute ( decl , 'sortPropsByAlpha' , node , context , nodeReservedList ) ;
452
518
453
519
return memo ;
454
520
}
0 commit comments