mirror of
https://github.com/tailwindlabs/tailwindcss.git
synced 2025-12-08 21:36:08 +00:00
Prevent duplicate suggestions when using @theme and @utility together (#17675)
Fixes https://github.com/tailwindlabs/tailwindcss-intellisense/issues/1313 Right now given this CSS: ```css @theme reference { --text-header: 1.5rem; } @utility text-header { text-transform: uppercase; } ``` You'll see two entries for `text-header` in IntelliSense completions but we only want you to see one. This PR solves this by merging their modifier lists and de-duping by class name.
This commit is contained in:
parent
3386049b7b
commit
f0986ce127
@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
- Allow `_` before numbers during candidate extraction ([#17961](https://github.com/tailwindlabs/tailwindcss/pull/17961))
|
||||
- Upgrade: Fix error when using `@import … source(…)` ([#17963](https://github.com/tailwindlabs/tailwindcss/pull/17963))
|
||||
- Prevent duplicate suggestions when using `@theme` and `@utility` together ([#17675](https://github.com/tailwindlabs/tailwindcss/pull/17675))
|
||||
|
||||
## [4.1.6] - 2025-05-09
|
||||
|
||||
|
||||
@ -653,3 +653,25 @@ test('shadow utility default suggestions', async () => {
|
||||
expect(classNames).toContain('inset-shadow')
|
||||
expect(classNames).toContain('text-shadow')
|
||||
})
|
||||
|
||||
test('Custom @utility and existing utility with names matching theme keys dont give duplicate results', async () => {
|
||||
let input = css`
|
||||
@theme reference {
|
||||
--leading-sm: 0.25rem;
|
||||
--text-header: 1.5rem;
|
||||
}
|
||||
|
||||
@utility text-header {
|
||||
text-transform: uppercase;
|
||||
}
|
||||
`
|
||||
|
||||
let design = await __unstable__loadDesignSystem(input)
|
||||
|
||||
let classList = design.getClassList()
|
||||
let classMap = new Map(classList)
|
||||
let matches = classList.filter(([className]) => className === 'text-header')
|
||||
|
||||
expect(matches).toHaveLength(1)
|
||||
expect(classMap.get('text-header')?.modifiers).toEqual(['sm'])
|
||||
})
|
||||
|
||||
@ -20,16 +20,18 @@ export type ClassEntry = [string, ClassMetadata]
|
||||
const IS_FRACTION = /^\d+\/\d+$/
|
||||
|
||||
export function getClassList(design: DesignSystem): ClassEntry[] {
|
||||
let list: ClassItem[] = []
|
||||
let items = new DefaultMap<string, ClassItem>((utility) => ({
|
||||
name: utility,
|
||||
utility,
|
||||
fraction: false,
|
||||
modifiers: [],
|
||||
}))
|
||||
|
||||
// Static utilities only work as-is
|
||||
for (let utility of design.utilities.keys('static')) {
|
||||
list.push({
|
||||
name: utility,
|
||||
utility,
|
||||
fraction: false,
|
||||
modifiers: [],
|
||||
})
|
||||
let item = items.get(utility)
|
||||
item.fraction = false
|
||||
item.modifiers = []
|
||||
}
|
||||
|
||||
// Functional utilities have their own list of completions
|
||||
@ -42,28 +44,25 @@ export function getClassList(design: DesignSystem): ClassEntry[] {
|
||||
|
||||
let name = value === null ? utility : `${utility}-${value}`
|
||||
|
||||
list.push({
|
||||
name,
|
||||
utility,
|
||||
fraction,
|
||||
modifiers: group.modifiers,
|
||||
})
|
||||
let item = items.get(name)
|
||||
item.utility = utility
|
||||
item.fraction ||= fraction
|
||||
item.modifiers.push(...group.modifiers)
|
||||
|
||||
if (group.supportsNegative) {
|
||||
list.push({
|
||||
name: `-${name}`,
|
||||
utility: `-${utility}`,
|
||||
fraction,
|
||||
modifiers: group.modifiers,
|
||||
})
|
||||
let item = items.get(`-${name}`)
|
||||
item.utility = `-${utility}`
|
||||
item.fraction ||= fraction
|
||||
item.modifiers.push(...group.modifiers)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (list.length === 0) return []
|
||||
if (items.size === 0) return []
|
||||
|
||||
// Sort utilities by their class name
|
||||
let list = Array.from(items.values())
|
||||
list.sort((a, b) => compare(a.name, b.name))
|
||||
|
||||
let entries = sortFractionsLast(list)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user