Add codemod for migrating @variants and @responsive directives (#14748)
This PR migrates the `@variants` and `@responsive` directives.
In Tailwind CSS v2, these were used to generate certain variants of responsive variants for the give classes. In Tailwind CSS v3, these still worked but were implemented as a no-op such that these directives don't end up in your final CSS.
In Tailwind CSS v4, these don't exist at all anymore, so we can safely get rid of them by replacing them with their contents.
Input:
```css
@variants hover, focus {
.foo {
color: red;
}
}
@responsive {
.bar {
color: blue;
}
}
```
Output:
```css
.foo {
color: red;
}
.bar {
color: blue;
}
```
This commit is contained in:
parent
d59f1b3e5d
commit
5bf2efb521
7 changed files with 109 additions and 0 deletions
|
|
@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
|
||||
- _Upgrade (experimental)_: Migrate `plugins` with options to CSS ([#14700](https://github.com/tailwindlabs/tailwindcss/pull/14700))
|
||||
- _Upgrade (experimental)_: Allow JS configuration files with `corePlugins` options to be migrated to CSS ([#14742](https://github.com/tailwindlabs/tailwindcss/pull/14742))
|
||||
- _Upgrade (experimental)_: Migrate `@variants` and `@responsive` directives ([#14748](https://github.com/tailwindlabs/tailwindcss/pull/14748))
|
||||
|
||||
### Fixed
|
||||
|
||||
|
|
|
|||
|
|
@ -26,6 +26,12 @@ test(
|
|||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@variants hover, focus {
|
||||
.foo {
|
||||
color: red;
|
||||
}
|
||||
}
|
||||
`,
|
||||
},
|
||||
},
|
||||
|
|
@ -40,6 +46,10 @@ test(
|
|||
|
||||
--- ./src/input.css ---
|
||||
@import 'tailwindcss';
|
||||
|
||||
@utility foo {
|
||||
color: red;
|
||||
}
|
||||
"
|
||||
`)
|
||||
|
||||
|
|
|
|||
|
|
@ -400,3 +400,19 @@ it('should drop `@tailwind variants;`', async () => {
|
|||
`),
|
||||
).toEqual('')
|
||||
})
|
||||
|
||||
it('should replace `@responsive` with its children', async () => {
|
||||
expect(
|
||||
await migrate(css`
|
||||
@responsive {
|
||||
.foo {
|
||||
color: red;
|
||||
}
|
||||
}
|
||||
`),
|
||||
).toMatchInlineSnapshot(`
|
||||
".foo {
|
||||
color: red;
|
||||
}"
|
||||
`)
|
||||
})
|
||||
|
|
|
|||
|
|
@ -54,6 +54,18 @@ export function migrateTailwindDirectives(options: { newPrefix: string | null })
|
|||
) {
|
||||
node.remove()
|
||||
}
|
||||
|
||||
// Replace Tailwind CSS v2 directives that still worked in v3.
|
||||
else if (node.name === 'responsive') {
|
||||
if (node.nodes) {
|
||||
for (let child of node.nodes) {
|
||||
child.raws.tailwind_pretty = true
|
||||
}
|
||||
node.replaceWith(node.nodes)
|
||||
} else {
|
||||
node.remove()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// Insert default import if all directives are present
|
||||
|
|
|
|||
|
|
@ -0,0 +1,33 @@
|
|||
import dedent from 'dedent'
|
||||
import postcss from 'postcss'
|
||||
import { expect, it } from 'vitest'
|
||||
import { formatNodes } from './format-nodes'
|
||||
import { migrateVariantsDirective } from './migrate-variants-directive'
|
||||
|
||||
const css = dedent
|
||||
|
||||
function migrate(input: string) {
|
||||
return postcss()
|
||||
.use(migrateVariantsDirective())
|
||||
.use(formatNodes())
|
||||
.process(input, { from: expect.getState().testPath })
|
||||
.then((result) => result.css)
|
||||
}
|
||||
|
||||
it('should replace `@variants` with `@layer utilities`', async () => {
|
||||
expect(
|
||||
await migrate(css`
|
||||
@variants hover, focus {
|
||||
.foo {
|
||||
color: red;
|
||||
}
|
||||
}
|
||||
`),
|
||||
).toMatchInlineSnapshot(`
|
||||
"@layer utilities {
|
||||
.foo {
|
||||
color: red;
|
||||
}
|
||||
}"
|
||||
`)
|
||||
})
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
import { type Plugin, type Root } from 'postcss'
|
||||
|
||||
export function migrateVariantsDirective(): Plugin {
|
||||
function migrate(root: Root) {
|
||||
root.walkAtRules('variants', (node) => {
|
||||
// Migrate `@variants` to `@utility` because `@variants` make the classes
|
||||
// an actual utility.
|
||||
// ```css
|
||||
// @variants hover {
|
||||
// .foo {}
|
||||
// }
|
||||
// ```
|
||||
//
|
||||
// Means that you can do this in your HTML:
|
||||
// ```html
|
||||
// <div class="focus:foo"></div>
|
||||
// ```
|
||||
//
|
||||
// Notice the `focus:`, even though we _only_ configured the `hover`
|
||||
// variant.
|
||||
//
|
||||
// This means that we can convert it to an `@layer utilities` rule. Later,
|
||||
// this will get converted to an `@utility` rule.
|
||||
if (node.name === 'variants') {
|
||||
node.name = 'layer'
|
||||
node.params = 'utilities'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
postcssPlugin: '@tailwindcss/upgrade/migrate-variants-directive',
|
||||
OnceExit: migrate,
|
||||
}
|
||||
}
|
||||
|
|
@ -11,6 +11,7 @@ import { migrateMediaScreen } from './codemods/migrate-media-screen'
|
|||
import { migrateMissingLayers } from './codemods/migrate-missing-layers'
|
||||
import { migrateTailwindDirectives } from './codemods/migrate-tailwind-directives'
|
||||
import { migrateThemeToVar } from './codemods/migrate-theme-to-var'
|
||||
import { migrateVariantsDirective } from './codemods/migrate-variants-directive'
|
||||
import type { JSConfigMigration } from './migrate-js-config'
|
||||
import { Stylesheet, type StylesheetConnection, type StylesheetId } from './stylesheet'
|
||||
import { resolveCssId } from './utils/resolve'
|
||||
|
|
@ -38,6 +39,7 @@ export async function migrateContents(
|
|||
.use(migrateAtApply(options))
|
||||
.use(migrateThemeToVar(options))
|
||||
.use(migrateMediaScreen(options))
|
||||
.use(migrateVariantsDirective())
|
||||
.use(migrateAtLayerUtilities(stylesheet))
|
||||
.use(migrateMissingLayers())
|
||||
.use(migrateTailwindDirectives(options))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue