mirror of
https://github.com/tailwindlabs/tailwindcss.git
synced 2025-12-08 21:36:08 +00:00
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` |
This commit is contained in:
parent
f0b65e360c
commit
bd3d6bc09b
@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Support `keyframes` in JS config file themes ([#14594](https://github.com/tailwindlabs/tailwindcss/pull/14594))
|
||||
- _Upgrade (experimental)_: Migrate v3 PostCSS setups to v4 in some cases ([#14612](https://github.com/tailwindlabs/tailwindcss/pull/14612))
|
||||
- _Upgrade (experimental)_: The upgrade tool now automatically discovers your JavaScript config ([#14597](https://github.com/tailwindlabs/tailwindcss/pull/14597))
|
||||
- _Upgrade (experimental)_: Migrate legacy classes to the v4 alternative ([#14643](https://github.com/tailwindlabs/tailwindcss/pull/14643))
|
||||
|
||||
### Fixed
|
||||
|
||||
|
||||
@ -5,9 +5,10 @@ import type { DesignSystem } from '../../../tailwindcss/src/design-system'
|
||||
|
||||
export async function extractRawCandidates(
|
||||
content: string,
|
||||
extension: string = 'html',
|
||||
): Promise<{ rawCandidate: string; start: number; end: number }[]> {
|
||||
let scanner = new Scanner({})
|
||||
let result = scanner.getCandidatesWithPositions({ content, extension: 'html' })
|
||||
let result = scanner.getCandidatesWithPositions({ content, extension })
|
||||
|
||||
let candidates: { rawCandidate: string; start: number; end: number }[] = []
|
||||
for (let { candidate: rawCandidate, position: start } of result) {
|
||||
|
||||
@ -0,0 +1,22 @@
|
||||
import { __unstable__loadDesignSystem } from '@tailwindcss/node'
|
||||
import { expect, test } from 'vitest'
|
||||
import { simpleLegacyClasses } from './simple-legacy-classes'
|
||||
|
||||
test.each([
|
||||
['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'],
|
||||
|
||||
['max-lg:hover:decoration-slice', 'max-lg:hover:box-decoration-slice'],
|
||||
['max-lg:hover:decoration-slice!', 'max-lg:hover:box-decoration-slice!'],
|
||||
['max-lg:hover:!decoration-slice', 'max-lg:hover:box-decoration-slice!'],
|
||||
])('%s => %s', async (candidate, result) => {
|
||||
let designSystem = await __unstable__loadDesignSystem('@import "tailwindcss";', {
|
||||
base: __dirname,
|
||||
})
|
||||
|
||||
expect(simpleLegacyClasses(designSystem, {}, candidate)).toEqual(result)
|
||||
})
|
||||
@ -0,0 +1,41 @@
|
||||
import type { Config } from 'tailwindcss'
|
||||
import type { DesignSystem } from '../../../../tailwindcss/src/design-system'
|
||||
import { printCandidate } from '../candidates'
|
||||
|
||||
// Classes that used to exist in Tailwind CSS v3, but do not exist in Tailwind
|
||||
// CSS v4 anymore.
|
||||
const LEGACY_CLASS_MAP = {
|
||||
'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',
|
||||
}
|
||||
|
||||
const SEEDED = new WeakSet<DesignSystem>()
|
||||
|
||||
export function simpleLegacyClasses(
|
||||
designSystem: DesignSystem,
|
||||
_userConfig: Config,
|
||||
rawCandidate: string,
|
||||
): string {
|
||||
// Prepare design system with the legacy classes
|
||||
if (!SEEDED.has(designSystem)) {
|
||||
for (let old in LEGACY_CLASS_MAP) {
|
||||
designSystem.utilities.static(old, () => [])
|
||||
}
|
||||
SEEDED.add(designSystem)
|
||||
}
|
||||
|
||||
for (let candidate of designSystem.parseCandidate(rawCandidate)) {
|
||||
if (candidate.kind === 'static' && Object.hasOwn(LEGACY_CLASS_MAP, candidate.root)) {
|
||||
return printCandidate(designSystem, {
|
||||
...candidate,
|
||||
root: LEGACY_CLASS_MAP[candidate.root as keyof typeof LEGACY_CLASS_MAP],
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return rawCandidate
|
||||
}
|
||||
@ -7,6 +7,7 @@ import { automaticVarInjection } from './codemods/automatic-var-injection'
|
||||
import { bgGradient } from './codemods/bg-gradient'
|
||||
import { important } from './codemods/important'
|
||||
import { prefix } from './codemods/prefix'
|
||||
import { simpleLegacyClasses } from './codemods/simple-legacy-classes'
|
||||
import { variantOrder } from './codemods/variant-order'
|
||||
|
||||
export type Migration = (
|
||||
@ -20,6 +21,7 @@ export const DEFAULT_MIGRATIONS: Migration[] = [
|
||||
important,
|
||||
automaticVarInjection,
|
||||
bgGradient,
|
||||
simpleLegacyClasses,
|
||||
variantOrder,
|
||||
]
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user