Migrate arbitrary values to bare values (#14669)
This PR adds a codemod that can convert arbitrary values to the cleaner bare values if we can. For example, some classes use arbitrary values such as `col-start-[16]`, but in v4 we have bare values for some plugins that don't really need to adhere to your design system. In this case, we can convert `col-start-[16]` to just `col-start-16`. Another use case is for utilities that use fractions. For example the `aspect-*` plugin. A custom aspect ratio such as `aspect-[16/9]` can be converted to `aspect-16/9`. There are some rules attached to this migration: 1. We can only migrate arbitrary values that is a single positive integer, or an arbitrary value that is a fraction where the numerator and denominator are both positive integers. 2. We make sure that some CSS can be generated once its converted to a bare value.
This commit is contained in:
parent
36acad0306
commit
2b13547a0d
3 changed files with 92 additions and 0 deletions
|
|
@ -20,6 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
- _Upgrade (experimental)_: Migrate `@media screen(…)` when running codemods ([#14603](https://github.com/tailwindlabs/tailwindcss/pull/14603))
|
||||
- _Upgrade (experimental)_: Inject `@config "…"` when a `tailwind.config.{js,ts,…}` is detected ([#14635](https://github.com/tailwindlabs/tailwindcss/pull/14635))
|
||||
- _Upgrade (experimental)_: Migrate `aria-*`, `data-*`, and `supports-*` variants from arbitrary values to bare values ([#14644](https://github.com/tailwindlabs/tailwindcss/pull/14644))
|
||||
- _Upgrade (experimental)_: Migrate arbitrary values to bare values ([#14669](https://github.com/tailwindlabs/tailwindcss/pull/14669))
|
||||
|
||||
### Fixed
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,24 @@ import { expect, test } from 'vitest'
|
|||
import { arbitraryValueToBareValue } from './arbitrary-value-to-bare-value'
|
||||
|
||||
test.each([
|
||||
['aspect-[12/34]', 'aspect-12/34'],
|
||||
['aspect-[1.2/34]', 'aspect-[1.2/34]'],
|
||||
['col-start-[7]', 'col-start-7'],
|
||||
['flex-[2]', 'flex-2'], // `flex` is implemented as static and functional utilities
|
||||
|
||||
// Only 50-200% (inclusive) are valid:
|
||||
// https://developer.mozilla.org/en-US/docs/Web/CSS/font-stretch#percentage
|
||||
['font-stretch-[50%]', 'font-stretch-50%'],
|
||||
['font-stretch-[50.5%]', 'font-stretch-[50.5%]'],
|
||||
['font-stretch-[201%]', 'font-stretch-[201%]'],
|
||||
['font-stretch-[49%]', 'font-stretch-[49%]'],
|
||||
// Should stay as-is
|
||||
['font-stretch-[1/2]', 'font-stretch-[1/2]'],
|
||||
|
||||
// This test in itself is a bit flawed because `text-[1/2]` currently
|
||||
// generates something. Converting it to `text-1/2` doesn't produce anything.
|
||||
['text-[1/2]', 'text-[1/2]'],
|
||||
|
||||
['data-[selected]:flex', 'data-selected:flex'],
|
||||
['data-[foo=bar]:flex', 'data-[foo=bar]:flex'],
|
||||
|
||||
|
|
@ -22,6 +40,10 @@ test.each([
|
|||
['group-has-aria-[selected]:flex', 'group-has-aria-[selected]:flex'],
|
||||
|
||||
['max-lg:hover:data-[selected]:flex!', 'max-lg:hover:data-selected:flex!'],
|
||||
[
|
||||
'data-[selected]:aria-[selected="true"]:aspect-[12/34]',
|
||||
'data-selected:aria-selected:aspect-12/34',
|
||||
],
|
||||
])('%s => %s', async (candidate, result) => {
|
||||
let designSystem = await __unstable__loadDesignSystem('@import "tailwindcss";', {
|
||||
base: __dirname,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import type { Config } from 'tailwindcss'
|
||||
import { parseCandidate, type Candidate, type Variant } from '../../../../tailwindcss/src/candidate'
|
||||
import type { DesignSystem } from '../../../../tailwindcss/src/design-system'
|
||||
import { isPositiveInteger } from '../../../../tailwindcss/src/utils/infer-data-type'
|
||||
import { segment } from '../../../../tailwindcss/src/utils/segment'
|
||||
import { printCandidate } from '../candidates'
|
||||
|
||||
|
|
@ -12,6 +13,74 @@ export function arbitraryValueToBareValue(
|
|||
for (let candidate of parseCandidate(rawCandidate, designSystem)) {
|
||||
let clone = structuredClone(candidate)
|
||||
let changed = false
|
||||
|
||||
// Convert font-stretch-* utilities
|
||||
if (
|
||||
clone.kind === 'functional' &&
|
||||
clone.value?.kind === 'arbitrary' &&
|
||||
clone.value.dataType === null &&
|
||||
clone.root === 'font-stretch'
|
||||
) {
|
||||
if (clone.value.value.endsWith('%') && isPositiveInteger(clone.value.value.slice(0, -1))) {
|
||||
let percentage = parseInt(clone.value.value)
|
||||
if (percentage >= 50 && percentage <= 200) {
|
||||
changed = true
|
||||
clone.value = {
|
||||
kind: 'named',
|
||||
value: clone.value.value,
|
||||
fraction: null,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Convert arbitrary values with positive integers to bare values
|
||||
// Convert arbitrary values with fractions to bare values
|
||||
else if (
|
||||
clone.kind === 'functional' &&
|
||||
clone.value?.kind === 'arbitrary' &&
|
||||
clone.value.dataType === null
|
||||
) {
|
||||
let parts = segment(clone.value.value, '/')
|
||||
if (parts.every((part) => isPositiveInteger(part))) {
|
||||
changed = true
|
||||
|
||||
let currentValue = clone.value
|
||||
let currentModifier = clone.modifier
|
||||
|
||||
// E.g.: `col-start-[12]`
|
||||
// ^^
|
||||
if (parts.length === 1) {
|
||||
clone.value = {
|
||||
kind: 'named',
|
||||
value: clone.value.value,
|
||||
fraction: null,
|
||||
}
|
||||
}
|
||||
|
||||
// E.g.: `aspect-[12/34]`
|
||||
// ^^ ^^
|
||||
else {
|
||||
clone.value = {
|
||||
kind: 'named',
|
||||
value: parts[0],
|
||||
fraction: clone.value.value,
|
||||
}
|
||||
clone.modifier = {
|
||||
kind: 'named',
|
||||
value: parts[1],
|
||||
}
|
||||
}
|
||||
|
||||
// Double check that the new value compiles correctly
|
||||
if (designSystem.compileAstNodes(clone).length === 0) {
|
||||
clone.value = currentValue
|
||||
clone.modifier = currentModifier
|
||||
changed = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (let variant of variants(clone)) {
|
||||
// Convert `data-[selected]` to `data-selected`
|
||||
if (
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue