-
Notifications
You must be signed in to change notification settings - Fork 134
/
sorting.js
99 lines (81 loc) · 2.9 KB
/
sorting.js
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
export function bigSign(bigIntValue) {
return (bigIntValue > 0n) - (bigIntValue < 0n)
}
function prefixCandidate(context, selector) {
let prefix = context.tailwindConfig.prefix
return typeof prefix === 'function' ? prefix(selector) : prefix + selector
}
// Polyfill for older Tailwind CSS versions
function getClassOrderPolyfill(classes, { env }) {
// A list of utilities that are used by certain Tailwind CSS utilities but
// that don't exist on their own. This will result in them "not existing" and
// sorting could be weird since you still require them in order to make the
// host utitlies work properly. (Thanks Biology)
let parasiteUtilities = new Set([
prefixCandidate(env.context, 'group'),
prefixCandidate(env.context, 'peer'),
])
let classNamesWithOrder = []
for (let className of classes) {
let order =
env
.generateRules(new Set([className]), env.context)
.sort(([a], [z]) => bigSign(z - a))[0]?.[0] ?? null
if (order === null && parasiteUtilities.has(className)) {
// This will make sure that it is at the very beginning of the
// `components` layer which technically means 'before any
// components'.
order = env.context.layerOrder.components
}
classNamesWithOrder.push([className, order])
}
return classNamesWithOrder
}
export function sortClasses(
classStr,
{ env, ignoreFirst = false, ignoreLast = false },
) {
if (typeof classStr !== 'string' || classStr === '') {
return classStr
}
// Ignore class attributes containing `{{`, to match Prettier behaviour:
// https://github.com/prettier/prettier/blob/main/src/language-html/embed.js#L83-L88
if (classStr.includes('{{')) {
return classStr
}
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()
}
let prefix = ''
if (ignoreFirst) {
prefix = `${classes.shift() ?? ''}${whitespace.shift() ?? ''}`
}
let suffix = ''
if (ignoreLast) {
suffix = `${whitespace.pop() ?? ''}${classes.pop() ?? ''}`
}
classes = sortClassList(classes, { env })
for (let i = 0; i < classes.length; i++) {
result += `${classes[i]}${whitespace[i] ?? ''}`
}
return prefix + result + suffix
}
export function sortClassList(classList, { env }) {
let classNamesWithOrder = env.context.getClassOrder
? env.context.getClassOrder(classList)
: getClassOrderPolyfill(classList, { env })
return classNamesWithOrder
.sort(([, a], [, z]) => {
if (a === z) return 0
// if (a === null) return options.unknownClassPosition === 'start' ? -1 : 1
// if (z === null) return options.unknownClassPosition === 'start' ? 1 : -1
if (a === null) return -1
if (z === null) return 1
return bigSign(a - z)
})
.map(([className]) => className)
}