Skip to content

Commit bd3d6bc

Browse files
authored
Migrate legacy classes to the v4 alternative (#14643)
This PR adds a mapping from legacy classes to new classes. For example, the `flex-shrink-0` is still used in our projects, but is deprecated in v3. The migration does a tiny bit of parsing because we can't rely on `designSystem.parseCandidate(…)` because this requires the utility to be defined which is not the case for legacy classes. This migration runs _after_ the migration where we handle prefixes, so we don't have to worry about that. We do have to worry about the `!` location, because the `important` migration also relies on the `designSystem`. | Old | New | | ------------------- | ---------------------- | | `overflow-clip` | `text-clip` | | `overflow-ellipsis` | `text-ellipsis` | | `flex-grow-0` | `grow-0` | | `flex-shrink-0` | `shrink-0` | | `decoration-clone` | `box-decoration-clone` | | `decoration-slice` | `box-decoration-slice` |
1 parent f0b65e3 commit bd3d6bc

File tree

5 files changed

+68
-1
lines changed

5 files changed

+68
-1
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1313
- Support `keyframes` in JS config file themes ([#14594](https://github.com/tailwindlabs/tailwindcss/pull/14594))
1414
- _Upgrade (experimental)_: Migrate v3 PostCSS setups to v4 in some cases ([#14612](https://github.com/tailwindlabs/tailwindcss/pull/14612))
1515
- _Upgrade (experimental)_: The upgrade tool now automatically discovers your JavaScript config ([#14597](https://github.com/tailwindlabs/tailwindcss/pull/14597))
16+
- _Upgrade (experimental)_: Migrate legacy classes to the v4 alternative ([#14643](https://github.com/tailwindlabs/tailwindcss/pull/14643))
1617

1718
### Fixed
1819

packages/@tailwindcss-upgrade/src/template/candidates.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@ import type { DesignSystem } from '../../../tailwindcss/src/design-system'
55

66
export async function extractRawCandidates(
77
content: string,
8+
extension: string = 'html',
89
): Promise<{ rawCandidate: string; start: number; end: number }[]> {
910
let scanner = new Scanner({})
10-
let result = scanner.getCandidatesWithPositions({ content, extension: 'html' })
11+
let result = scanner.getCandidatesWithPositions({ content, extension })
1112

1213
let candidates: { rawCandidate: string; start: number; end: number }[] = []
1314
for (let { candidate: rawCandidate, position: start } of result) {
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { __unstable__loadDesignSystem } from '@tailwindcss/node'
2+
import { expect, test } from 'vitest'
3+
import { simpleLegacyClasses } from './simple-legacy-classes'
4+
5+
test.each([
6+
['overflow-clip', 'text-clip'],
7+
['overflow-ellipsis', 'text-ellipsis'],
8+
['flex-grow-0', 'grow-0'],
9+
['flex-shrink-0', 'shrink-0'],
10+
['decoration-clone', 'box-decoration-clone'],
11+
['decoration-slice', 'box-decoration-slice'],
12+
13+
['max-lg:hover:decoration-slice', 'max-lg:hover:box-decoration-slice'],
14+
['max-lg:hover:decoration-slice!', 'max-lg:hover:box-decoration-slice!'],
15+
['max-lg:hover:!decoration-slice', 'max-lg:hover:box-decoration-slice!'],
16+
])('%s => %s', async (candidate, result) => {
17+
let designSystem = await __unstable__loadDesignSystem('@import "tailwindcss";', {
18+
base: __dirname,
19+
})
20+
21+
expect(simpleLegacyClasses(designSystem, {}, candidate)).toEqual(result)
22+
})
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import type { Config } from 'tailwindcss'
2+
import type { DesignSystem } from '../../../../tailwindcss/src/design-system'
3+
import { printCandidate } from '../candidates'
4+
5+
// Classes that used to exist in Tailwind CSS v3, but do not exist in Tailwind
6+
// CSS v4 anymore.
7+
const LEGACY_CLASS_MAP = {
8+
'overflow-clip': 'text-clip',
9+
'overflow-ellipsis': 'text-ellipsis',
10+
'flex-grow-0': 'grow-0',
11+
'flex-shrink-0': 'shrink-0',
12+
'decoration-clone': 'box-decoration-clone',
13+
'decoration-slice': 'box-decoration-slice',
14+
}
15+
16+
const SEEDED = new WeakSet<DesignSystem>()
17+
18+
export function simpleLegacyClasses(
19+
designSystem: DesignSystem,
20+
_userConfig: Config,
21+
rawCandidate: string,
22+
): string {
23+
// Prepare design system with the legacy classes
24+
if (!SEEDED.has(designSystem)) {
25+
for (let old in LEGACY_CLASS_MAP) {
26+
designSystem.utilities.static(old, () => [])
27+
}
28+
SEEDED.add(designSystem)
29+
}
30+
31+
for (let candidate of designSystem.parseCandidate(rawCandidate)) {
32+
if (candidate.kind === 'static' && Object.hasOwn(LEGACY_CLASS_MAP, candidate.root)) {
33+
return printCandidate(designSystem, {
34+
...candidate,
35+
root: LEGACY_CLASS_MAP[candidate.root as keyof typeof LEGACY_CLASS_MAP],
36+
})
37+
}
38+
}
39+
40+
return rawCandidate
41+
}

packages/@tailwindcss-upgrade/src/template/migrate.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { automaticVarInjection } from './codemods/automatic-var-injection'
77
import { bgGradient } from './codemods/bg-gradient'
88
import { important } from './codemods/important'
99
import { prefix } from './codemods/prefix'
10+
import { simpleLegacyClasses } from './codemods/simple-legacy-classes'
1011
import { variantOrder } from './codemods/variant-order'
1112

1213
export type Migration = (
@@ -20,6 +21,7 @@ export const DEFAULT_MIGRATIONS: Migration[] = [
2021
important,
2122
automaticVarInjection,
2223
bgGradient,
24+
simpleLegacyClasses,
2325
variantOrder,
2426
]
2527

0 commit comments

Comments
 (0)