forked from tailwindlabs/prettier-plugin-tailwindcss
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsorting.ts
More file actions
144 lines (118 loc) · 3.58 KB
/
sorting.ts
File metadata and controls
144 lines (118 loc) · 3.58 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
import type { TransformerEnv } from './types'
import { bigSign } from './utils'
function reorderClasses(classList: string[], { env }: { env: TransformerEnv }) {
let orderedClasses = env.context.getClassOrder(classList)
return orderedClasses.sort(([nameA, a], [nameZ, z]) => {
// Move `...` to the end of the list
if (nameA === '...' || nameA === '…') return 1
if (nameZ === '...' || nameZ === '…') return -1
if (a === z) return 0
if (a === null) return -1
if (z === null) return 1
return bigSign(a - z)
})
}
export function sortClasses(
classStr: string,
{
env,
ignoreFirst = false,
ignoreLast = false,
removeDuplicates = true,
collapseWhitespace = { start: true, end: true },
}: {
env: TransformerEnv
ignoreFirst?: boolean
ignoreLast?: boolean
removeDuplicates?: boolean
collapseWhitespace?: false | { start: boolean; end: boolean }
},
): string {
if (typeof classStr !== 'string' || classStr === '') {
return classStr
}
// Ignore class attributes containing `{{`, to match Prettier behaviour:
// https://github.com/prettier/prettier/blob/8a88cdce6d4605f206305ebb9204a0cabf96a070/src/language-html/embed/class-names.js#L9
if (classStr.includes('{{')) {
return classStr
}
if (env.options.tailwindPreserveWhitespace) {
collapseWhitespace = false
}
// This class list is purely whitespace
// Collapse it to a single space if the option is enabled
if (collapseWhitespace && /^[\t\r\f\n ]+$/.test(classStr)) {
return ' '
}
let result = ''
let parts = classStr.split(/([\t\r\f\n ]+)/)
let classes = parts.filter((_, i) => i % 2 === 0)
let whitespace = parts.filter((_, i) => i % 2 !== 0)
if (classes[classes.length - 1] === '') {
classes.pop()
}
if (collapseWhitespace) {
whitespace = whitespace.map(() => ' ')
}
let prefix = ''
if (ignoreFirst) {
prefix = `${classes.shift() ?? ''}${whitespace.shift() ?? ''}`
}
let suffix = ''
if (ignoreLast) {
suffix = `${whitespace.pop() ?? ''}${classes.pop() ?? ''}`
}
let { classList, removedIndices } = sortClassList(classes, {
env,
removeDuplicates,
})
// Remove whitespace that appeared before a removed classes
whitespace = whitespace.filter((_, index) => !removedIndices.has(index + 1))
for (let i = 0; i < classList.length; i++) {
result += `${classList[i]}${whitespace[i] ?? ''}`
}
if (collapseWhitespace) {
prefix = prefix.replace(/\s+$/g, ' ')
suffix = suffix.replace(/^\s+/g, ' ')
result = result
.replace(/^\s+/, collapseWhitespace.start ? '' : ' ')
.replace(/\s+$/, collapseWhitespace.end ? '' : ' ')
}
return prefix + result + suffix
}
export function sortClassList(
classList: string[],
{
env,
removeDuplicates,
}: {
env: TransformerEnv
removeDuplicates: boolean
},
) {
// Re-order classes based on the Tailwind CSS configuration
let orderedClasses = reorderClasses(classList, { env })
// Remove duplicate Tailwind classes
if (env.options.tailwindPreserveDuplicates) {
removeDuplicates = false
}
let removedIndices = new Set<number>()
if (removeDuplicates) {
let seenClasses = new Set<string>()
orderedClasses = orderedClasses.filter(([cls, order], index) => {
if (seenClasses.has(cls)) {
removedIndices.add(index)
return false
}
// Only consider known classes when removing duplicates
if (order !== null) {
seenClasses.add(cls)
}
return true
})
}
return {
classList: orderedClasses.map(([className]) => className),
removedIndices,
}
}