Skip to content

Commit b6f6f4f

Browse files
committed
fix(sort-properties.js): Do not sort top level keys
1 parent d35d762 commit b6f6f4f

File tree

1 file changed

+40
-42
lines changed

1 file changed

+40
-42
lines changed

lib/rules/sort-properties.js

Lines changed: 40 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -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} */
5763
module.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

Comments
 (0)