Skip to content

Commit 35d2bb2

Browse files
Merge branch 'next' into fix/v4-always-emit-add-utilities-keyframes
2 parents bb1a981 + c7b190f commit 35d2bb2

File tree

25 files changed

+212
-90
lines changed

25 files changed

+212
-90
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1111

1212
- _Upgrade (experimental)_: Migrate `plugins` with options to CSS ([#14700](https://github.com/tailwindlabs/tailwindcss/pull/14700))
1313
- _Upgrade (experimental)_: Allow JS configuration files with `corePlugins` options to be migrated to CSS ([#14742](https://github.com/tailwindlabs/tailwindcss/pull/14742))
14+
- _Upgrade (experimental)_: Migrate `@variants` and `@responsive` directives ([#14748](https://github.com/tailwindlabs/tailwindcss/pull/14748))
15+
- _Upgrade (experimental)_: Migrate `@screen` directive ([#14749](https://github.com/tailwindlabs/tailwindcss/pull/14749))
1416

1517
### Fixed
1618

@@ -19,6 +21,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1921
- Ensure changes to the input CSS file result in a full rebuild ([#14744](https://github.com/tailwindlabs/tailwindcss/pull/14744))
2022
- Add `postcss` as a dependency of `@tailwindcss/postcss` ([#14750](https://github.com/tailwindlabs/tailwindcss/pull/14750))
2123
- Always emit keyframes registered in `addUtilities` ([#14747](https://github.com/tailwindlabs/tailwindcss/pull/14747))
24+
- Ensure loading stylesheets via the `?raw` and `?url` static asset query works when using the Vite plugin ([#14716](https://github.com/tailwindlabs/tailwindcss/pull/14716))
2225
- _Upgrade (experimental)_: Migrate `flex-grow` to `grow` and `flex-shrink` to `shrink` ([#14721](https://github.com/tailwindlabs/tailwindcss/pull/14721))
2326
- _Upgrade (experimental)_: Minify arbitrary values when printing candidates ([#14720](https://github.com/tailwindlabs/tailwindcss/pull/14720))
2427
- _Upgrade (experimental)_: Ensure legacy theme values ending in `1` (like `theme(spacing.1)`) are correctly migrated to custom properties ([#14724](https://github.com/tailwindlabs/tailwindcss/pull/14724))

integrations/upgrade/index.test.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,12 @@ test(
2626
@tailwind base;
2727
@tailwind components;
2828
@tailwind utilities;
29+
30+
@variants hover, focus {
31+
.foo {
32+
color: red;
33+
}
34+
}
2935
`,
3036
},
3137
},
@@ -40,6 +46,10 @@ test(
4046
4147
--- ./src/input.css ---
4248
@import 'tailwindcss';
49+
50+
@utility foo {
51+
color: red;
52+
}
4353
"
4454
`)
4555

integrations/vite/index.test.ts

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -504,3 +504,64 @@ test(
504504
})
505505
},
506506
)
507+
508+
test(
509+
`does not interfere with ?raw and ?url static asset handling`,
510+
{
511+
fs: {
512+
'package.json': json`
513+
{
514+
"type": "module",
515+
"dependencies": {
516+
"@tailwindcss/vite": "workspace:^",
517+
"tailwindcss": "workspace:^"
518+
},
519+
"devDependencies": {
520+
"vite": "^5.3.5"
521+
}
522+
}
523+
`,
524+
'vite.config.ts': ts`
525+
import tailwindcss from '@tailwindcss/vite'
526+
import { defineConfig } from 'vite'
527+
528+
export default defineConfig({
529+
build: { cssMinify: false },
530+
plugins: [tailwindcss()],
531+
})
532+
`,
533+
'index.html': html`
534+
<head>
535+
<script type="module" src="./src/index.js"></script>
536+
</head>
537+
`,
538+
'src/index.js': js`
539+
import url from './index.css?url'
540+
import raw from './index.css?raw'
541+
`,
542+
'src/index.css': css`@import 'tailwindcss';`,
543+
},
544+
},
545+
async ({ spawn, getFreePort }) => {
546+
let port = await getFreePort()
547+
await spawn(`pnpm vite dev --port ${port}`)
548+
549+
await retryAssertion(async () => {
550+
// We have to load the .js file first so that the static assets are
551+
// resolved
552+
await fetch(`http://localhost:${port}/src/index.js`).then((r) => r.text())
553+
554+
let [raw, url] = await Promise.all([
555+
fetch(`http://localhost:${port}/src/index.css?raw`).then((r) => r.text()),
556+
fetch(`http://localhost:${port}/src/index.css?url`).then((r) => r.text()),
557+
])
558+
559+
expect(firstLine(raw)).toBe(`export default "@import 'tailwindcss';"`)
560+
expect(firstLine(url)).toBe(`export default "/src/index.css"`)
561+
})
562+
},
563+
)
564+
565+
function firstLine(str: string) {
566+
return str.split('\n')[0]
567+
}

packages/@tailwindcss-postcss/src/__snapshots__/index.test.ts.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -514,7 +514,7 @@ exports[`\`@import 'tailwindcss'\` is replaced with the generated CSS 1`] = `
514514
}
515515
516516
input:where(:not([type="button"], [type="reset"], [type="submit"])), select, textarea {
517-
border: 1px solid;
517+
border-width: 1px;
518518
}
519519
520520
button, input:where([type="button"], [type="reset"], [type="submit"]) {

packages/@tailwindcss-upgrade/src/codemods/migrate-media-screen.test.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,24 @@ it('should migrate a built-in breakpoint', async () => {
4141
`)
4242
})
4343

44+
it('should migrate `@screen` with a built-in breakpoint', async () => {
45+
expect(
46+
await migrate(css`
47+
@screen md {
48+
.foo {
49+
color: red;
50+
}
51+
}
52+
`),
53+
).toMatchInlineSnapshot(`
54+
"@media (width >= theme(--breakpoint-md)) {
55+
.foo {
56+
color: red;
57+
}
58+
}"
59+
`)
60+
})
61+
4462
it('should migrate a custom min-width screen (string)', async () => {
4563
expect(
4664
await migrate(

packages/@tailwindcss-upgrade/src/codemods/migrate-media-screen.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,13 @@ export function migrateMediaScreen({
2424
return value ? buildMediaQuery(value) : null
2525
})
2626

27+
// First migrate `@screen md` to `@media screen(md)`
28+
root.walkAtRules('screen', (node) => {
29+
node.name = 'media'
30+
node.params = `screen(${node.params})`
31+
})
32+
33+
// Then migrate the `screen(…)` function
2734
root.walkAtRules((rule) => {
2835
if (rule.name !== 'media') return
2936

packages/@tailwindcss-upgrade/src/codemods/migrate-tailwind-directives.test.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -400,3 +400,19 @@ it('should drop `@tailwind variants;`', async () => {
400400
`),
401401
).toEqual('')
402402
})
403+
404+
it('should replace `@responsive` with its children', async () => {
405+
expect(
406+
await migrate(css`
407+
@responsive {
408+
.foo {
409+
color: red;
410+
}
411+
}
412+
`),
413+
).toMatchInlineSnapshot(`
414+
".foo {
415+
color: red;
416+
}"
417+
`)
418+
})

packages/@tailwindcss-upgrade/src/codemods/migrate-tailwind-directives.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,18 @@ export function migrateTailwindDirectives(options: { newPrefix: string | null })
5454
) {
5555
node.remove()
5656
}
57+
58+
// Replace Tailwind CSS v2 directives that still worked in v3.
59+
else if (node.name === 'responsive') {
60+
if (node.nodes) {
61+
for (let child of node.nodes) {
62+
child.raws.tailwind_pretty = true
63+
}
64+
node.replaceWith(node.nodes)
65+
} else {
66+
node.remove()
67+
}
68+
}
5769
})
5870

5971
// Insert default import if all directives are present
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import dedent from 'dedent'
2+
import postcss from 'postcss'
3+
import { expect, it } from 'vitest'
4+
import { formatNodes } from './format-nodes'
5+
import { migrateVariantsDirective } from './migrate-variants-directive'
6+
7+
const css = dedent
8+
9+
function migrate(input: string) {
10+
return postcss()
11+
.use(migrateVariantsDirective())
12+
.use(formatNodes())
13+
.process(input, { from: expect.getState().testPath })
14+
.then((result) => result.css)
15+
}
16+
17+
it('should replace `@variants` with `@layer utilities`', async () => {
18+
expect(
19+
await migrate(css`
20+
@variants hover, focus {
21+
.foo {
22+
color: red;
23+
}
24+
}
25+
`),
26+
).toMatchInlineSnapshot(`
27+
"@layer utilities {
28+
.foo {
29+
color: red;
30+
}
31+
}"
32+
`)
33+
})
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { type Plugin, type Root } from 'postcss'
2+
3+
export function migrateVariantsDirective(): Plugin {
4+
function migrate(root: Root) {
5+
root.walkAtRules('variants', (node) => {
6+
// Migrate `@variants` to `@utility` because `@variants` make the classes
7+
// an actual utility.
8+
// ```css
9+
// @variants hover {
10+
// .foo {}
11+
// }
12+
// ```
13+
//
14+
// Means that you can do this in your HTML:
15+
// ```html
16+
// <div class="focus:foo"></div>
17+
// ```
18+
//
19+
// Notice the `focus:`, even though we _only_ configured the `hover`
20+
// variant.
21+
//
22+
// This means that we can convert it to an `@layer utilities` rule. Later,
23+
// this will get converted to an `@utility` rule.
24+
if (node.name === 'variants') {
25+
node.name = 'layer'
26+
node.params = 'utilities'
27+
}
28+
})
29+
}
30+
31+
return {
32+
postcssPlugin: '@tailwindcss/upgrade/migrate-variants-directive',
33+
OnceExit: migrate,
34+
}
35+
}

0 commit comments

Comments
 (0)