Skip to content

Commit 82ea71a

Browse files
Only remove duplicate Tailwind classes (#277)
* improve remove duplicate classes * replace indexOf by Set * Simplify code * Refactor * Remove duplicates after sorting * Only remove duplicates of known classes * Rename vars * Tweak comment * Tweak var names * wip * Move duplicate removal to sorting routine * Refactor * Refactor * Tweak comment * Cleanup * Update changelog --------- Co-authored-by: Jordan Pittman <[email protected]>
1 parent 731ae22 commit 82ea71a

File tree

4 files changed

+65
-31
lines changed

4 files changed

+65
-31
lines changed

CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10-
- Nothing yet!
10+
### Changed
11+
12+
- Only remove duplicate Tailwind classes ([#277](https://github.com/tailwindlabs/prettier-plugin-tailwindcss/pull/277))
1113

1214
## [0.6.1] - 2024-05-31
1315

src/index.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -878,7 +878,11 @@ function transformPug(ast, { env }) {
878878
const classes = ast.tokens
879879
.slice(startIdx, endIdx + 1)
880880
.map((token) => token.val)
881-
const classList = sortClassList(classes, { env })
881+
882+
const { classList } = sortClassList(classes, {
883+
env,
884+
removeDuplicates: false,
885+
})
882886

883887
for (let i = startIdx; i <= endIdx; i++) {
884888
ast.tokens[i].val = classList[i - startIdx]

src/sorting.js

Lines changed: 52 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,19 @@ function getClassOrderPolyfill(classes, { env }) {
3939
return classNamesWithOrder
4040
}
4141

42+
function reorderClasses(classList, { env }) {
43+
let orderedClasses = env.context.getClassOrder
44+
? env.context.getClassOrder(classList)
45+
: getClassOrderPolyfill(classList, { env })
46+
47+
return orderedClasses.sort(([, a], [, z]) => {
48+
if (a === z) return 0
49+
if (a === null) return -1
50+
if (z === null) return 1
51+
return bigSign(a - z)
52+
})
53+
}
54+
4255
/**
4356
* @param {string} classStr
4457
* @param {object} opts
@@ -75,10 +88,6 @@ export function sortClasses(
7588
collapseWhitespace = false
7689
}
7790

78-
if (env.options.tailwindPreserveDuplicates) {
79-
removeDuplicates = false
80-
}
81-
8291
// This class list is purely whitespace
8392
// Collapse it to a single space if the option is enabled
8493
if (/^[\t\r\f\n ]+$/.test(classStr) && collapseWhitespace) {
@@ -108,22 +117,16 @@ export function sortClasses(
108117
suffix = `${whitespace.pop() ?? ''}${classes.pop() ?? ''}`
109118
}
110119

111-
if (removeDuplicates) {
112-
classes = classes.filter((cls, index, arr) => {
113-
if (arr.indexOf(cls) === index) {
114-
return true
115-
}
116-
117-
whitespace.splice(index - 1, 1)
118-
119-
return false
120-
})
121-
}
120+
let { classList, removedIndices } = sortClassList(classes, {
121+
env,
122+
removeDuplicates,
123+
})
122124

123-
classes = sortClassList(classes, { env })
125+
// Remove whitespace that appeared before a removed classes
126+
whitespace = whitespace.filter((_, index) => !removedIndices.has(index + 1))
124127

125-
for (let i = 0; i < classes.length; i++) {
126-
result += `${classes[i]}${whitespace[i] ?? ''}`
128+
for (let i = 0; i < classList.length; i++) {
129+
result += `${classList[i]}${whitespace[i] ?? ''}`
127130
}
128131

129132
if (collapseWhitespace) {
@@ -138,17 +141,37 @@ export function sortClasses(
138141
return prefix + result + suffix
139142
}
140143

141-
export function sortClassList(classList, { env }) {
142-
let classNamesWithOrder = env.context.getClassOrder
143-
? env.context.getClassOrder(classList)
144-
: getClassOrderPolyfill(classList, { env })
144+
export function sortClassList(classList, { env, removeDuplicates }) {
145+
// Re-order classes based on the Tailwind CSS configuration
146+
let orderedClasses = reorderClasses(classList, { env })
145147

146-
return classNamesWithOrder
147-
.sort(([, a], [, z]) => {
148-
if (a === z) return 0
149-
if (a === null) return -1
150-
if (z === null) return 1
151-
return bigSign(a - z)
148+
// Remove duplicate Tailwind classes
149+
if (env.options.tailwindPreserveDuplicates) {
150+
removeDuplicates = false
151+
}
152+
153+
let removedIndices = new Set()
154+
155+
if (removeDuplicates) {
156+
let seenClasses = new Set()
157+
158+
orderedClasses = orderedClasses.filter(([cls, order], index) => {
159+
if (seenClasses.has(cls)) {
160+
removedIndices.add(index)
161+
return false
162+
}
163+
164+
// Only consider known classes when removing duplicates
165+
if (order !== null) {
166+
seenClasses.add(cls)
167+
}
168+
169+
return true
152170
})
153-
.map(([className]) => className)
171+
}
172+
173+
return {
174+
classList: orderedClasses.map(([className]) => className),
175+
removedIndices,
176+
}
154177
}

tests/format.test.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@ let html = [
1010
t`<div class=""></div>`,
1111
// Ensure duplicate classes are removed
1212
['<div class="sm:p-0 p-0 p-0"></div>', '<div class="p-0 sm:p-0"></div>'],
13+
// Duplicates are not removed for unknown classes
14+
[
15+
'<div class="idonotexist sm:p-0 p-0 idonotexist p-0 idonotexist"></div>',
16+
'<div class="idonotexist idonotexist idonotexist p-0 sm:p-0"></div>',
17+
],
1318
// Ensure duplicate can be kept
1419
[
1520
'<div class="sm:p-0 p-0 p-0"></div>',

0 commit comments

Comments
 (0)