55
66const variableUtil = require ( '../util/variable' ) ;
77const propsUtil = require ( '../util/props' ) ;
8+ const commentsUtil = require ( '../util/comments' ) ;
89const docsUrl = require ( '../util/docsUrl' ) ;
910
1011// ------------------------------------------------------------------------------
@@ -55,6 +56,7 @@ module.exports = {
5556 const noSortAlphabetically = configuration . noSortAlphabetically || false ;
5657 const sortShapeProp = configuration . sortShapeProp || false ;
5758 const propWrapperFunctions = new Set ( context . settings . propWrapperFunctions || [ ] ) ;
59+ const commentsAttachment = context . settings . comments || 'above' ;
5860
5961 function getKey ( node ) {
6062 return sourceCode . getText ( node . key || node . argument ) ;
@@ -117,6 +119,90 @@ module.exports = {
117119 return 0 ;
118120 }
119121
122+ function getRelatedComments ( node , nextNode ) {
123+ // check for an end of line comment
124+ const nextNodeComments = nextNode
125+ ? commentsUtil . getCommentsBefore ( nextNode , sourceCode )
126+ : commentsUtil . getCommentsAfter ( node , sourceCode ) ;
127+ if ( nextNodeComments . length === 1 ) {
128+ const comment = nextNodeComments [ 0 ] ;
129+ if ( comment . loc . start . line === comment . loc . end . line && comment . loc . end . line === node . loc . end . line ) {
130+ return [ nextNodeComments , true ] ;
131+ }
132+ }
133+
134+ if ( commentsAttachment === 'above' ) {
135+ return [ commentsUtil . getCommentsBefore ( node , sourceCode ) , false ] ;
136+ }
137+
138+ return [ nextNodeComments , false ] ;
139+ }
140+
141+ function replaceNodeWithText ( source , originalNode , sortedNodeText ) {
142+ return `${ source . slice ( 0 , originalNode . range [ 0 ] ) } ${ sortedNodeText } ${ source . slice ( originalNode . range [ 1 ] ) } ` ;
143+ }
144+
145+ function sortNodeWithComments ( source , originalAttr , originalComments , sortedAttrText , sortedComments ) {
146+ if ( sortedComments . length && originalComments . length ) {
147+ const swapComments = ( ) => {
148+ const sortedCommentsText = sourceCode . getText ( ) . slice (
149+ sortedComments [ 0 ] . range [ 0 ] ,
150+ sortedComments [ sortedComments . length - 1 ] . range [ 1 ]
151+ ) ;
152+ return `${ source . slice ( 0 , originalComments [ 0 ] . range [ 0 ] ) } ${ sortedCommentsText } ${ source . slice ( originalComments [ originalComments . length - 1 ] . range [ 1 ] ) } ` ;
153+ } ;
154+ if ( originalAttr . range [ 1 ] < originalComments [ 0 ] . range [ 0 ] ) {
155+ source = swapComments ( ) ;
156+ source = replaceNodeWithText ( source , originalAttr , sortedAttrText ) ;
157+ } else {
158+ source = replaceNodeWithText ( source , originalAttr , sortedAttrText ) ;
159+ source = swapComments ( ) ;
160+ }
161+ return source ;
162+ }
163+
164+ if ( sortedComments . length ) {
165+ const sortedCommentsText = sourceCode . getText ( ) . slice (
166+ sortedComments [ 0 ] . range [ 0 ] ,
167+ sortedComments [ sortedComments . length - 1 ] . range [ 1 ]
168+ ) ;
169+
170+ const indent = Array ( sortedComments [ 0 ] . loc . start . column + 1 ) . join ( ' ' ) ;
171+ if ( commentsAttachment === 'above' ) {
172+ source = replaceNodeWithText ( source , originalAttr , sortedAttrText ) ;
173+ source = `${ source . slice ( 0 , originalAttr . range [ 0 ] ) } ${ sortedCommentsText } \n${ indent } ${ source . slice ( originalAttr . range [ 0 ] ) } ` ;
174+ } else {
175+ const nextToken = sourceCode . getTokenAfter ( originalAttr ) ;
176+ const targetIndex = nextToken . value === ',' ? nextToken . range [ 1 ] : originalAttr . range [ 1 ] ;
177+ source = `${ source . slice ( 0 , targetIndex ) } \n${ indent } ${ sortedCommentsText } ${ source . slice ( targetIndex ) } ` ;
178+ source = replaceNodeWithText ( source , originalAttr , sortedAttrText ) ;
179+ }
180+ return source ;
181+ }
182+
183+ if ( originalComments . length ) {
184+ const removeComments = ( ) => {
185+ const startLoc = sourceCode . getLocFromIndex ( originalComments [ 0 ] . range [ 0 ] ) ;
186+ const lineStart = sourceCode . getIndexFromLoc ( { line : startLoc . line , column : 0 } ) ;
187+ const endLoc = sourceCode . getLocFromIndex ( originalComments [ originalComments . length - 1 ] . range [ 1 ] ) ;
188+ const lineEnd = sourceCode . getIndexFromLoc ( {
189+ line : endLoc . line ,
190+ column : sourceCode . lines [ endLoc . line - 1 ] . length - 1
191+ } ) ;
192+ return `${ source . slice ( 0 , lineStart ) } ${ source . slice ( lineEnd + 2 ) } ` ;
193+ } ;
194+ if ( originalAttr . range [ 1 ] < originalComments [ 0 ] . range [ 0 ] ) {
195+ source = removeComments ( ) ;
196+ source = replaceNodeWithText ( source , originalAttr , sortedAttrText ) ;
197+ } else {
198+ source = replaceNodeWithText ( source , originalAttr , sortedAttrText ) ;
199+ source = removeComments ( ) ;
200+ }
201+ return source ;
202+ }
203+
204+ return null ;
205+ }
120206
121207 /**
122208 * Checks if propTypes declarations are sorted
@@ -148,7 +234,16 @@ module.exports = {
148234 for ( let i = nodes . length - 1 ; i >= 0 ; i -- ) {
149235 const sortedAttr = sortedAttributes [ i ] ;
150236 const attr = nodes [ i ] ;
237+ if ( sortedAttr === attr ) {
238+ continue ;
239+ }
240+
241+ const [ sortedComments ] = getRelatedComments ( sortedAttr ,
242+ allNodes [ allNodes . indexOf ( sortedAttr ) + 1 ] ) ;
243+ const [ attrComments ] = getRelatedComments ( attr , nodes [ i + 1 ] ) ;
244+
151245 let sortedAttrText = sourceCode . getText ( sortedAttr ) ;
246+
152247 if ( sortShapeProp && isShapeProp ( sortedAttr . value ) ) {
153248 const shape = getShapeProperties ( sortedAttr . value ) ;
154249 if ( shape ) {
@@ -159,16 +254,24 @@ module.exports = {
159254 sortedAttrText = attrSource . slice ( sortedAttr . range [ 0 ] , sortedAttr . range [ 1 ] ) ;
160255 }
161256 }
162- source = `${ source . slice ( 0 , attr . range [ 0 ] ) } ${ sortedAttrText } ${ source . slice ( attr . range [ 1 ] ) } ` ;
257+
258+ const newSource = sortNodeWithComments ( source , attr , attrComments , sortedAttrText , sortedComments ) ;
259+ source = newSource || replaceNodeWithText ( source , attr , sortedAttrText ) ;
163260 }
164261 } ) ;
165262 return source ;
166263 }
167264
168265 const source = sortInSource ( declarations , context . getSourceCode ( ) . getText ( ) ) ;
169266
170- const rangeStart = declarations [ 0 ] . range [ 0 ] ;
171- const rangeEnd = declarations [ declarations . length - 1 ] . range [ 1 ] ;
267+ const [ startComments , isSameLineStart ] = getRelatedComments ( declarations [ 0 ] , declarations [ 1 ] ) ;
268+ const [ endComments , isSameLineEnd ] = getRelatedComments ( declarations [ declarations . length - 1 ] , null ) ;
269+ const rangeStart = ( commentsAttachment === 'above' && startComments . length && ! isSameLineStart )
270+ ? startComments [ 0 ] . range [ 0 ]
271+ : declarations [ 0 ] . range [ 0 ] ;
272+ const rangeEnd = ( commentsAttachment === 'below' && endComments . length || isSameLineEnd )
273+ ? endComments [ endComments . length - 1 ] . range [ 1 ]
274+ : declarations [ declarations . length - 1 ] . range [ 1 ] ;
172275 return fixer . replaceTextRange ( [ rangeStart , rangeEnd ] , source . slice ( rangeStart , rangeEnd ) ) ;
173276 }
174277
0 commit comments