Migrate @media screen(…) (#14603)
This PR adds a codemod that migrates the `@media screen(…)` to the
properly expanded `@media (…)` syntax.
```css
@media screen(md) {
.foo {
color: red;
}
}
```
Will be converted to:
```css
@media (width >= 48rem) {
.foo {
color: red;
}
}
```
If you happen to have custom screens (even complex ones), the screen
will be converted to a custom media query.
```css
@media screen(foo) {
.foo {
color: red;
}
}
```
With a custom `tailwind.config.js` file with a config like this:
```js
module.exports = {
// …
theme: {
screens: {
foo: { min: '100px', max: '123px' },
},
}
}
```
Then the codemod will convert it to:
```css
@media (123px >= width >= 100px) {
.foo {
color: red;
}
}
```
This commit is contained in:
parent
958bfc9d89
commit
3ae22f1f9d
4 changed files with 224 additions and 0 deletions
|
|
@ -25,6 +25,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
- Pass options when using `addComponents` and `matchComponents` ([#14590](https://github.com/tailwindlabs/tailwindcss/pull/14590))
|
||||
- _Upgrade (experimental)_: Ensure CSS before a layer stays unlayered when running codemods ([#14596](https://github.com/tailwindlabs/tailwindcss/pull/14596))
|
||||
- _Upgrade (experimental)_: Resolve issues where some prefixed candidates were not properly migrated ([#14600](https://github.com/tailwindlabs/tailwindcss/pull/14600))
|
||||
- _Upgrade (experimental)_: Migrate `@media screen(…)` when running codemods ([#14603](https://github.com/tailwindlabs/tailwindcss/pull/14603))
|
||||
|
||||
## [4.0.0-alpha.26] - 2024-10-03
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,177 @@
|
|||
import { __unstable__loadDesignSystem } from '@tailwindcss/node'
|
||||
import dedent from 'dedent'
|
||||
import postcss from 'postcss'
|
||||
import { expect, it } from 'vitest'
|
||||
import type { UserConfig } from '../../../tailwindcss/src/compat/config/types'
|
||||
import { formatNodes } from './format-nodes'
|
||||
import { migrateMediaScreen } from './migrate-media-screen'
|
||||
|
||||
const css = dedent
|
||||
|
||||
async function migrate(input: string, userConfig: UserConfig = {}) {
|
||||
return postcss()
|
||||
.use(
|
||||
migrateMediaScreen({
|
||||
designSystem: await __unstable__loadDesignSystem(`@import 'tailwindcss';`, {
|
||||
base: __dirname,
|
||||
}),
|
||||
userConfig,
|
||||
}),
|
||||
)
|
||||
.use(formatNodes())
|
||||
.process(input, { from: expect.getState().testPath })
|
||||
.then((result) => result.css)
|
||||
}
|
||||
|
||||
it('should migrate a built-in breakpoint', async () => {
|
||||
expect(
|
||||
await migrate(css`
|
||||
@media screen(md) {
|
||||
.foo {
|
||||
color: red;
|
||||
}
|
||||
}
|
||||
`),
|
||||
).toMatchInlineSnapshot(`
|
||||
"@media (width >= theme(--breakpoint-md)) {
|
||||
.foo {
|
||||
color: red;
|
||||
}
|
||||
}"
|
||||
`)
|
||||
})
|
||||
|
||||
it('should migrate a custom min-width screen (string)', async () => {
|
||||
expect(
|
||||
await migrate(
|
||||
css`
|
||||
@media screen(foo) {
|
||||
.foo {
|
||||
color: red;
|
||||
}
|
||||
}
|
||||
`,
|
||||
{
|
||||
theme: {
|
||||
screens: {
|
||||
foo: '123px',
|
||||
},
|
||||
},
|
||||
},
|
||||
),
|
||||
).toMatchInlineSnapshot(`
|
||||
"@media (width >= theme(--breakpoint-foo)) {
|
||||
.foo {
|
||||
color: red;
|
||||
}
|
||||
}"
|
||||
`)
|
||||
})
|
||||
|
||||
it('should migrate a custom min-width screen (object)', async () => {
|
||||
expect(
|
||||
await migrate(
|
||||
css`
|
||||
@media screen(foo) {
|
||||
.foo {
|
||||
color: red;
|
||||
}
|
||||
}
|
||||
`,
|
||||
{
|
||||
theme: {
|
||||
screens: {
|
||||
foo: { min: '123px' },
|
||||
},
|
||||
},
|
||||
},
|
||||
),
|
||||
).toMatchInlineSnapshot(`
|
||||
"@media (width >= theme(--breakpoint-foo)) {
|
||||
.foo {
|
||||
color: red;
|
||||
}
|
||||
}"
|
||||
`)
|
||||
})
|
||||
|
||||
it('should migrate a custom max-width screen', async () => {
|
||||
expect(
|
||||
await migrate(
|
||||
css`
|
||||
@media screen(foo) {
|
||||
.foo {
|
||||
color: red;
|
||||
}
|
||||
}
|
||||
`,
|
||||
{
|
||||
theme: {
|
||||
screens: {
|
||||
foo: { max: '123px' },
|
||||
},
|
||||
},
|
||||
},
|
||||
),
|
||||
).toMatchInlineSnapshot(`
|
||||
"@media (123px >= width) {
|
||||
.foo {
|
||||
color: red;
|
||||
}
|
||||
}"
|
||||
`)
|
||||
})
|
||||
|
||||
it('should migrate a custom min and max-width screen', async () => {
|
||||
expect(
|
||||
await migrate(
|
||||
css`
|
||||
@media screen(foo) {
|
||||
.foo {
|
||||
color: red;
|
||||
}
|
||||
}
|
||||
`,
|
||||
{
|
||||
theme: {
|
||||
screens: {
|
||||
foo: { min: '100px', max: '123px' },
|
||||
},
|
||||
},
|
||||
},
|
||||
),
|
||||
).toMatchInlineSnapshot(`
|
||||
"@media (123px >= width >= 100px) {
|
||||
.foo {
|
||||
color: red;
|
||||
}
|
||||
}"
|
||||
`)
|
||||
})
|
||||
|
||||
it('should migrate a raw media query', async () => {
|
||||
expect(
|
||||
await migrate(
|
||||
css`
|
||||
@media screen(foo) {
|
||||
.foo {
|
||||
color: red;
|
||||
}
|
||||
}
|
||||
`,
|
||||
{
|
||||
theme: {
|
||||
screens: {
|
||||
foo: { raw: 'only screen and (min-width: 123px)' },
|
||||
},
|
||||
},
|
||||
},
|
||||
),
|
||||
).toMatchInlineSnapshot(`
|
||||
"@media only screen and (min-width: 123px) {
|
||||
.foo {
|
||||
color: red;
|
||||
}
|
||||
}"
|
||||
`)
|
||||
})
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
import { type Plugin, type Root } from 'postcss'
|
||||
import type { Config } from 'tailwindcss'
|
||||
import { resolveConfig } from '../../../tailwindcss/src/compat/config/resolve-config'
|
||||
import { buildMediaQuery } from '../../../tailwindcss/src/compat/screens-config'
|
||||
import type { DesignSystem } from '../../../tailwindcss/src/design-system'
|
||||
import { DefaultMap } from '../../../tailwindcss/src/utils/default-map'
|
||||
|
||||
export function migrateMediaScreen({
|
||||
designSystem,
|
||||
userConfig,
|
||||
}: {
|
||||
designSystem?: DesignSystem
|
||||
userConfig?: Config
|
||||
} = {}): Plugin {
|
||||
function migrate(root: Root) {
|
||||
if (!designSystem || !userConfig) return
|
||||
|
||||
let resolvedUserConfig = resolveConfig(designSystem, [{ base: '', config: userConfig }])
|
||||
let screens = resolvedUserConfig?.theme?.screens || {}
|
||||
|
||||
let mediaQueries = new DefaultMap<string, string | null>((name) => {
|
||||
let value = designSystem?.resolveThemeValue(`--breakpoint-${name}`) ?? screens?.[name]
|
||||
if (typeof value === 'string') return `(width >= theme(--breakpoint-${name}))`
|
||||
return value ? buildMediaQuery(value) : null
|
||||
})
|
||||
|
||||
root.walkAtRules((rule) => {
|
||||
if (rule.name !== 'media') return
|
||||
|
||||
let screen = rule.params.match(/screen\(([^)]+)\)/)
|
||||
if (!screen) return
|
||||
|
||||
let value = mediaQueries.get(screen[1])
|
||||
if (!value) return
|
||||
|
||||
rule.params = value
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
postcssPlugin: '@tailwindcss/upgrade/migrate-media-screen',
|
||||
OnceExit: migrate,
|
||||
}
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@ import type { DesignSystem } from '../../tailwindcss/src/design-system'
|
|||
import { formatNodes } from './codemods/format-nodes'
|
||||
import { migrateAtApply } from './codemods/migrate-at-apply'
|
||||
import { migrateAtLayerUtilities } from './codemods/migrate-at-layer-utilities'
|
||||
import { migrateMediaScreen } from './codemods/migrate-media-screen'
|
||||
import { migrateMissingLayers } from './codemods/migrate-missing-layers'
|
||||
import { migrateTailwindDirectives } from './codemods/migrate-tailwind-directives'
|
||||
|
||||
|
|
@ -18,6 +19,7 @@ export interface MigrateOptions {
|
|||
export async function migrateContents(contents: string, options: MigrateOptions, file?: string) {
|
||||
return postcss()
|
||||
.use(migrateAtApply(options))
|
||||
.use(migrateMediaScreen(options))
|
||||
.use(migrateAtLayerUtilities())
|
||||
.use(migrateMissingLayers())
|
||||
.use(migrateTailwindDirectives(options))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue