@@ -23,17 +23,25 @@ function getPropertyName(property) {
23
23
return property . key . name || property . key . value || ''
24
24
}
25
25
26
- function getPropertyIndex ( property ) {
26
+ function getPropertyIndex ( property , isTopLevel = false ) {
27
27
const name = getPropertyName ( property )
28
28
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
+ }
30
38
31
39
let lastGroupIndex = 0
32
40
let maxPropIndex = 0
33
41
34
42
for ( let i = 0 ; i < propertyGroups . length ; i ++ ) {
35
43
const group = propertyGroups [ i ]
36
- const propIndex = group . properties . indexOf ( propertyName )
44
+ const propIndex = group . properties . indexOf ( name )
37
45
38
46
if ( propIndex !== - 1 ) {
39
47
return i * 1000 + propIndex
@@ -43,87 +51,77 @@ function getPropertyIndex(property) {
43
51
maxPropIndex = Math . max ( maxPropIndex , group . properties . length )
44
52
}
45
53
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
+ }
52
59
53
60
return lastGroupIndex * 1000 + maxPropIndex + 1
54
61
}
55
62
56
- /** @type {import('eslint').Rule.RuleModule } */
57
63
module . exports = {
58
64
meta : {
59
65
type : 'suggestion' ,
60
66
docs : {
61
67
description :
62
- 'Sort CSS properties according to defined groups and specific rules. ' ,
68
+ 'Sort CSS properties keeping original order for specific keys ' ,
63
69
recommended : true ,
64
70
} ,
65
71
fixable : 'code' ,
66
72
schema : [ ] ,
67
73
messages : {
68
74
sortProperties :
69
- 'Property "{{property}}" is out of order. Expected position: {{position}}. ' ,
75
+ 'Property "{{property}}" should be at position {{position}}' ,
70
76
} ,
71
77
} ,
72
78
73
79
create ( context ) {
74
80
return {
75
81
ObjectExpression ( node ) {
76
82
const sourceCode = getSourceCode ( context )
83
+ const isTopLevel = ! node . parent || node . parent . type !== 'Property'
77
84
const properties = node . properties . filter ( ( prop ) => prop . key )
78
85
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
83
90
} )
84
91
85
- const misorderedProperties = properties . filter (
86
- ( prop , index ) => prop !== sortedProperties [ index ] ,
87
- )
92
+ const misordered = properties . filter ( ( prop , i ) => prop !== sorted [ i ] )
88
93
89
- if ( misorderedProperties . length === 0 ) {
90
- return
91
- }
94
+ if ( misordered . length === 0 ) return
92
95
93
96
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' : ' '
99
99
100
+ misordered . forEach ( ( prop ) => {
100
101
context . report ( {
101
102
node : prop ,
102
103
messageId : 'sortProperties' ,
103
104
data : {
104
- position : correctIndex + 1 ,
105
105
property : getPropertyName ( prop ) ,
106
+ position : sorted . indexOf ( prop ) + 1 ,
106
107
} ,
107
108
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 } }`
119
117
}
120
- return `${ objectIndent } ${ sourceCode . getText ( sortedProp ) } `
118
+ return `${ indent } ${ sourceCode . getText ( p ) } `
121
119
} )
122
- . join ( `,${ lineCase } ` )
120
+ . join ( `,${ lineEnding } ` )
123
121
124
122
return fixer . replaceTextRange (
125
123
[ node . range [ 0 ] + 1 , node . range [ 1 ] - 1 ] ,
126
- `${ lineCase } ${ formattedProps } ${ lineCase } ` ,
124
+ `${ lineEnding } ${ newText } ${ lineEnding } ` ,
127
125
)
128
126
} ,
129
127
} )
0 commit comments