Skip to content

Commit 3a4ab82

Browse files
Stop suggesting legacy utilities (#19169)
It was already supposed to work this way — and it appeared via the tests that it did — but the tests weren't loading the design system normally so when we split the legacy utilities into a separate file it stopped testing this properly. The setup here is a bit iffy but the gist is: - We allow static utilities to be suggested by default - A static utility can *opt out* of suggestions by explicitly registering themselves to return none.
1 parent 7537e34 commit 3a4ab82

File tree

6 files changed

+74
-36
lines changed

6 files changed

+74
-36
lines changed

packages/tailwindcss/src/__snapshots__/intellisense.test.ts.snap

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7576,7 +7576,6 @@ exports[`getClassList 1`] = `
75767576
"max-w-min",
75777577
"max-w-none",
75787578
"max-w-px",
7579-
"max-w-screen",
75807579
"max-w-svh",
75817580
"max-w-svw",
75827581
"mb-0",

packages/tailwindcss/src/canonicalize-candidates.test.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -581,6 +581,17 @@ describe.each([['default'], ['with-variant'], ['important'], ['prefix']])('%s',
581581
await expectCanonicalization(input, candidate, expected)
582582
})
583583

584+
test('`[overflow-wrap:break-word]` → `wrap-break-word`', async () => {
585+
let candidate = '[overflow-wrap:break-word]'
586+
let expected = 'wrap-break-word'
587+
588+
let input = css`
589+
@import 'tailwindcss';
590+
`
591+
592+
await expectCanonicalization(input, candidate, expected)
593+
})
594+
584595
test('`break-words` → `break-words` with custom implementation', async () => {
585596
let candidate = 'break-words'
586597
let expected = 'break-words'

packages/tailwindcss/src/compat/legacy-utilities.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,32 +13,42 @@ export function registerLegacyUtilities(designSystem: DesignSystem) {
1313
['l', 'left'],
1414
['tl', 'top left'],
1515
]) {
16+
designSystem.utilities.suggest(`bg-gradient-to-${value}`, () => [])
1617
designSystem.utilities.static(`bg-gradient-to-${value}`, () => [
1718
decl('--tw-gradient-position', `to ${direction} in oklab`),
1819
decl('background-image', `linear-gradient(var(--tw-gradient-stops))`),
1920
])
2021
}
2122

2223
// Legacy `background-position` utilities for compatibility with v4.0 and earlier
24+
designSystem.utilities.suggest('bg-left-top', () => [])
2325
designSystem.utilities.static('bg-left-top', () => [decl('background-position', 'left top')])
26+
designSystem.utilities.suggest('bg-right-top', () => [])
2427
designSystem.utilities.static('bg-right-top', () => [decl('background-position', 'right top')])
28+
designSystem.utilities.suggest('bg-left-bottom', () => [])
2529
designSystem.utilities.static('bg-left-bottom', () => [
2630
decl('background-position', 'left bottom'),
2731
])
32+
designSystem.utilities.suggest('bg-right-bottom', () => [])
2833
designSystem.utilities.static('bg-right-bottom', () => [
2934
decl('background-position', 'right bottom'),
3035
])
3136

3237
// Legacy `object-position` utilities for compatibility with v4.0 and earlier
38+
designSystem.utilities.suggest('object-left-top', () => [])
3339
designSystem.utilities.static('object-left-top', () => [decl('object-position', 'left top')])
40+
designSystem.utilities.suggest('object-right-top', () => [])
3441
designSystem.utilities.static('object-right-top', () => [decl('object-position', 'right top')])
42+
designSystem.utilities.suggest('object-left-bottom', () => [])
3543
designSystem.utilities.static('object-left-bottom', () => [
3644
decl('object-position', 'left bottom'),
3745
])
46+
designSystem.utilities.suggest('object-right-bottom', () => [])
3847
designSystem.utilities.static('object-right-bottom', () => [
3948
decl('object-position', 'right bottom'),
4049
])
4150

51+
designSystem.utilities.suggest('max-w-screen', () => [])
4252
designSystem.utilities.functional('max-w-screen', (candidate) => {
4353
if (!candidate.value) return
4454
if (candidate.value.kind === 'arbitrary') return
@@ -47,18 +57,22 @@ export function registerLegacyUtilities(designSystem: DesignSystem) {
4757
return [decl('max-width', value)]
4858
})
4959

60+
designSystem.utilities.suggest('overflow-ellipsis', () => [])
5061
designSystem.utilities.static(`overflow-ellipsis`, () => [decl('text-overflow', `ellipsis`)])
5162

63+
designSystem.utilities.suggest('decoration-slice', () => [])
5264
designSystem.utilities.static(`decoration-slice`, () => [
5365
decl('-webkit-box-decoration-break', `slice`),
5466
decl('box-decoration-break', `slice`),
5567
])
5668

69+
designSystem.utilities.suggest('decoration-clone', () => [])
5770
designSystem.utilities.static(`decoration-clone`, () => [
5871
decl('-webkit-box-decoration-break', `clone`),
5972
decl('box-decoration-break', `clone`),
6073
])
6174

75+
designSystem.utilities.suggest('flex-shrink', () => [])
6276
designSystem.utilities.functional('flex-shrink', (candidate) => {
6377
if (candidate.modifier) return
6478

@@ -75,6 +89,7 @@ export function registerLegacyUtilities(designSystem: DesignSystem) {
7589
}
7690
})
7791

92+
designSystem.utilities.suggest('flex-grow', () => [])
7893
designSystem.utilities.functional('flex-grow', (candidate) => {
7994
if (candidate.modifier) return
8095

@@ -91,6 +106,9 @@ export function registerLegacyUtilities(designSystem: DesignSystem) {
91106
}
92107
})
93108

109+
designSystem.utilities.suggest('order-none', () => [])
94110
designSystem.utilities.static('order-none', () => [decl('order', '0')])
111+
112+
designSystem.utilities.suggest('break-words', () => [])
95113
designSystem.utilities.static('break-words', () => [decl('overflow-wrap', 'break-word')])
96114
}

packages/tailwindcss/src/intellisense.test.ts

Lines changed: 36 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,36 @@
11
import { expect, test } from 'vitest'
22
import { __unstable__loadDesignSystem } from '.'
3-
import { buildDesignSystem } from './design-system'
43
import plugin from './plugin'
5-
import { Theme, ThemeOptions } from './theme'
4+
import { ThemeOptions } from './theme'
65

76
const css = String.raw
87

98
function loadDesignSystem() {
10-
let theme = new Theme()
11-
theme.add('--spacing', '0.25rem')
12-
theme.add('--colors-red-500', 'red')
13-
theme.add('--colors-blue-500', 'blue')
14-
theme.add('--breakpoint-sm', '640px')
15-
theme.add('--aspect-video', '16 / 9')
16-
theme.add('--font-sans', 'sans-serif')
17-
theme.add('--font-weight-superbold', '900')
18-
theme.add('--text-xs', '0.75rem')
19-
theme.add('--text-xs--line-height', '1rem')
20-
theme.add('--perspective-dramatic', '100px')
21-
theme.add('--perspective-normal', '500px')
22-
theme.add('--opacity-background', '0.3')
23-
theme.add('--drop-shadow-sm', '0 1px 1px rgb(0 0 0 / 0.05)')
24-
theme.add('--inset-shadow-sm', 'inset 0 1px 1px rgb(0 0 0 / 0.05)')
25-
theme.add('--font-weight-bold', '700')
26-
theme.add('--container-md', '768px')
27-
theme.add('--container-lg', '1024px')
28-
return buildDesignSystem(theme)
9+
return __unstable__loadDesignSystem(`
10+
@theme {
11+
--spacing: 0.25rem;
12+
--colors-red-500: red;
13+
--colors-blue-500: blue;
14+
--breakpoint-sm: 640px;
15+
--aspect-video: 16 / 9;
16+
--font-sans: sans-serif;
17+
--font-weight-superbold: 900;
18+
--text-xs: 0.75rem;
19+
--text-xs--line-height: 1rem;
20+
--perspective-dramatic: 100px;
21+
--perspective-normal: 500px;
22+
--opacity-background: 0.3;
23+
--drop-shadow-sm: 0 1px 1px rgb(0 0 0 / 0.05);
24+
--inset-shadow-sm: inset 0 1px 1px rgb(0 0 0 / 0.05);
25+
--font-weight-bold: 700;
26+
--container-md: 768px;
27+
--container-lg: 1024px;
28+
}
29+
`)
2930
}
3031

31-
test('getClassList', () => {
32-
let design = loadDesignSystem()
32+
test('getClassList', async () => {
33+
let design = await loadDesignSystem()
3334
let classList = design.getClassList()
3435
let classNames = classList.flatMap(([name, meta]) => [
3536
name,
@@ -39,8 +40,8 @@ test('getClassList', () => {
3940
expect(classNames).toMatchSnapshot()
4041
})
4142

42-
test('Spacing utilities do not suggest bare values when not using the multiplier-based spacing scale', () => {
43-
let design = loadDesignSystem()
43+
test('Spacing utilities do not suggest bare values when not using the multiplier-based spacing scale', async () => {
44+
let design = await loadDesignSystem()
4445

4546
// Remove spacing scale
4647
design.theme.clearNamespace('--spacing', ThemeOptions.NONE)
@@ -58,22 +59,22 @@ test('Spacing utilities do not suggest bare values when not using the multiplier
5859
expect(classNames).not.toContain('p-4')
5960
})
6061

61-
test('Theme values with underscores are converted back to decimal points', () => {
62-
let design = loadDesignSystem()
62+
test('Theme values with underscores are converted back to decimal points', async () => {
63+
let design = await loadDesignSystem()
6364
let classes = design.getClassList()
6465

6566
expect(classes).toContainEqual(['inset-0.5', { modifiers: [] }])
6667
})
6768

68-
test('getVariants', () => {
69-
let design = loadDesignSystem()
69+
test('getVariants', async () => {
70+
let design = await loadDesignSystem()
7071
let variants = design.getVariants()
7172

7273
expect(variants).toMatchSnapshot()
7374
})
7475

75-
test('getVariants compound', () => {
76-
let design = loadDesignSystem()
76+
test('getVariants compound', async () => {
77+
let design = await loadDesignSystem()
7778
let variants = design.getVariants()
7879
let group = variants.find((v) => v.name === 'group')!
7980

@@ -130,16 +131,16 @@ test('variant selectors are in the correct order', async () => {
130131
`)
131132
})
132133

133-
test('The variant `has-force` does not crash', () => {
134-
let design = loadDesignSystem()
134+
test('The variant `has-force` does not crash', async () => {
135+
let design = await loadDesignSystem()
135136
let variants = design.getVariants()
136137
let has = variants.find((v) => v.name === 'has')!
137138

138139
expect(has.selectors({ value: 'force' })).toMatchInlineSnapshot(`[]`)
139140
})
140141

141-
test('Can produce CSS per candidate using `candidatesToCss`', () => {
142-
let design = loadDesignSystem()
142+
test('Can produce CSS per candidate using `candidatesToCss`', async () => {
143+
let design = await loadDesignSystem()
143144
design.invalidCandidates = new Set(['bg-[#fff]'])
144145

145146
expect(design.candidatesToCss(['underline', 'i-dont-exist', 'bg-[#fff]', 'bg-[#000]', 'text-xs']))

packages/tailwindcss/src/intellisense.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ export function getClassList(design: DesignSystem): ClassEntry[] {
3131

3232
// Static utilities only work as-is
3333
for (let utility of design.utilities.keys('static')) {
34+
let completions = design.utilities.getCompletions(utility)
35+
if (completions.length === 0) continue
36+
3437
let item = items.get(utility)
3538
item.fraction = false
3639
item.modifiers = []

packages/tailwindcss/src/utilities.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,12 @@ export class Utilities {
122122
}
123123

124124
getCompletions(name: string): SuggestionGroup[] {
125+
if (this.has(name, 'static')) {
126+
return (
127+
this.completions.get(name)?.() ?? [{ supportsNegative: false, values: [], modifiers: [] }]
128+
)
129+
}
130+
125131
return this.completions.get(name)?.() ?? []
126132
}
127133

0 commit comments

Comments
 (0)