@@ -23,17 +23,25 @@ function getPropertyName(property) {
2323 return property . key . name || property . key . value || ''
2424}
2525
26- function getPropertyIndex ( property ) {
26+ function getPropertyIndex ( property , isTopLevel = false ) {
2727 const name = getPropertyName ( property )
2828
29- const propertyName = typeof name === 'string' ? name : ''
29+ if (
30+ isTopLevel &&
31+ ( property . key . type !== 'Identifier' ||
32+ name . startsWith ( '&' ) ||
33+ name . startsWith ( ':' ) ||
34+ name . startsWith ( '@' ) )
35+ ) {
36+ return null
37+ }
3038
3139 let lastGroupIndex = 0
3240 let maxPropIndex = 0
3341
3442 for ( let i = 0 ; i < propertyGroups . length ; i ++ ) {
3543 const group = propertyGroups [ i ]
36- const propIndex = group . properties . indexOf ( propertyName )
44+ const propIndex = group . properties . indexOf ( name )
3745
3846 if ( propIndex !== - 1 ) {
3947 return i * 1000 + propIndex
@@ -43,87 +51,77 @@ function getPropertyIndex(property) {
4351 maxPropIndex = Math . max ( maxPropIndex , group . properties . length )
4452 }
4553
46- if ( typeof propertyName === 'string' && propertyName . startsWith ( '&' ) )
47- return ( propertyGroups . length + 1 ) * 1000
48- if ( typeof propertyName === 'string' && propertyName . includes ( ':' ) )
49- return ( propertyGroups . length + 2 ) * 1000
50- if ( typeof propertyName === 'string' && propertyName . includes ( '@media' ) )
51- return ( propertyGroups . length + 3 ) * 1000
54+ if ( typeof name === 'string' ) {
55+ if ( name . startsWith ( '&' ) ) return ( propertyGroups . length + 1 ) * 1000
56+ if ( name . includes ( ':' ) ) return ( propertyGroups . length + 2 ) * 1000
57+ if ( name . includes ( '@media' ) ) return ( propertyGroups . length + 3 ) * 1000
58+ }
5259
5360 return lastGroupIndex * 1000 + maxPropIndex + 1
5461}
5562
56- /** @type {import('eslint').Rule.RuleModule } */
5763module . exports = {
5864 meta : {
5965 type : 'suggestion' ,
6066 docs : {
6167 description :
62- 'Sort CSS properties according to defined groups and specific rules. ' ,
68+ 'Sort CSS properties keeping original order for specific keys ' ,
6369 recommended : true ,
6470 } ,
6571 fixable : 'code' ,
6672 schema : [ ] ,
6773 messages : {
6874 sortProperties :
69- 'Property "{{property}}" is out of order. Expected position: {{position}}. ' ,
75+ 'Property "{{property}}" should be at position {{position}}' ,
7076 } ,
7177 } ,
7278
7379 create ( context ) {
7480 return {
7581 ObjectExpression ( node ) {
7682 const sourceCode = getSourceCode ( context )
83+ const isTopLevel = ! node . parent || node . parent . type !== 'Property'
7784 const properties = node . properties . filter ( ( prop ) => prop . key )
7885
79- const sortedProperties = [ ...properties ] . sort ( ( a , b ) => {
80- const indexA = getPropertyIndex ( a )
81- const indexB = getPropertyIndex ( b )
82- return indexA - indexB
86+ const sorted = [ ...properties ] . sort ( ( a , b ) => {
87+ const indexA = getPropertyIndex ( a , isTopLevel )
88+ const indexB = getPropertyIndex ( b , isTopLevel )
89+ return indexA === null || indexB === null ? 0 : indexA - indexB
8390 } )
8491
85- const misorderedProperties = properties . filter (
86- ( prop , index ) => prop !== sortedProperties [ index ] ,
87- )
92+ const misordered = properties . filter ( ( prop , i ) => prop !== sorted [ i ] )
8893
89- if ( misorderedProperties . length === 0 ) {
90- return
91- }
94+ if ( misordered . length === 0 ) return
9295
9396 const match = sourceCode . getText ( node ) . match ( / ^ { \s * \n ( \s * ) / )
94- const objectIndent = match ? match [ 1 ] : ''
95- const lineCase = match ? '\n' : ' '
96-
97- misorderedProperties . forEach ( ( prop ) => {
98- const correctIndex = sortedProperties . indexOf ( prop )
97+ const indent = match ? match [ 1 ] : ''
98+ const lineEnding = match ? '\n' : ' '
9999
100+ misordered . forEach ( ( prop ) => {
100101 context . report ( {
101102 node : prop ,
102103 messageId : 'sortProperties' ,
103104 data : {
104- position : correctIndex + 1 ,
105105 property : getPropertyName ( prop ) ,
106+ position : sorted . indexOf ( prop ) + 1 ,
106107 } ,
107108 fix ( fixer ) {
108- const formattedProps = sortedProperties
109- . map ( ( sortedProp ) => {
110- if ( Array . isArray ( getPropertyName ( sortedProp ) ) ) {
111- const arrayKey = sourceCode . getText ( sortedProp . key )
112- const arrayContent = sortedProp . value . properties
113- . map (
114- ( innerProp ) =>
115- `${ objectIndent } ${ sourceCode . getText ( innerProp ) } ` ,
116- )
117- . join ( `,${ lineCase } ` )
118- return `${ objectIndent } ${ arrayKey } : {\n${ arrayContent } \n${ objectIndent } }`
109+ const newText = sorted
110+ . map ( ( p ) => {
111+ if ( Array . isArray ( getPropertyName ( p ) ) ) {
112+ const arrayKey = sourceCode . getText ( p . key )
113+ const arrayContent = p . value . properties
114+ . map ( ( inner ) => `${ indent } ${ sourceCode . getText ( inner ) } ` )
115+ . join ( `,${ lineEnding } ` )
116+ return `${ indent } ${ arrayKey } : {\n${ arrayContent } \n${ indent } }`
119117 }
120- return `${ objectIndent } ${ sourceCode . getText ( sortedProp ) } `
118+ return `${ indent } ${ sourceCode . getText ( p ) } `
121119 } )
122- . join ( `,${ lineCase } ` )
120+ . join ( `,${ lineEnding } ` )
123121
124122 return fixer . replaceTextRange (
125123 [ node . range [ 0 ] + 1 , node . range [ 1 ] - 1 ] ,
126- `${ lineCase } ${ formattedProps } ${ lineCase } ` ,
124+ `${ lineEnding } ${ newText } ${ lineEnding } ` ,
127125 )
128126 } ,
129127 } )
0 commit comments