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

482 lines
12 KiB
TypeScript
Raw Normal View History

import dedent from 'dedent'
import { mkdir, mkdtemp, readFile, rm, unlink, writeFile } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import path from 'path'
2024-03-05 14:23:26 +01:00
import postcss from 'postcss'
import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest'
Improve snapshot tests (#20013) This PR improves the snapshot tests. At first, this looks like a very silly PR, but I swear I have legit reasons for these changes: First, there are a few places where we use `.toMatchInlineSnapshot()` for tests where we expect that nothing is being generated. This is fine, but the issue is that this means that if you're not careful, and if we have a bug, then these snapshots could start producing something. Updating these snapshots is _too_ easy. So instead, we convert them to an explicit `.toEqual('')` Next, I introduced a `pretty` helper function which is used behind the scenes in the `compileCss(…)`, `run(…)`, and `optimizeCss(…)` test helpers. It's very silly and simple, it either returns `''` when the trimmed input is empty, or it will wrap the result in `\n…\n`. The reason for this is because of how (inline) snapshots works in Vitest. A snapshot result will be in double quotes, and inside backticks: ```ts expect(result.css.trim()).toMatchInlineSnapshot(` "@layer utilities { .foo { color: #000; } .bar { color: red; } }" `) ``` This CSS now starts with `"` and ends with `"`. While that is fine, it starts to get annoying when we have merge conflicts when CSS changes (this is what triggered me to make this PR because I ran into this a dozen times already). Because the first and last CSS line also contain a `"` that you have to keep into account. Instead we now use `\n` around the output, which makes the tests look like this: ```ts expect(pretty(result.css)).toMatchInlineSnapshot(` " @layer utilities { .foo { color: #000; } .bar { color: red; } } " `) ``` In a perfect world, I wish we could use something like: ```css expect(pretty(result.css)).toMatchInlineSnapshot(css` @layer utilities { .foo { color: #000; } .bar { color: red; } } `) ``` That way you are only dealing with CSS, nothing else. Most editors will show syntax highlighting, and even prettier will do formatting on the CSS to keep everything consistent. Unfortunately this also causes issues because when prettier formats this, then the input/output will not always match. We can solve that by parsing both sides and compare the ASTs but that would make things slower. The biggest issue is that Vitest doesn't support this. You can use custom serializers https://vitest.dev/guide/snapshot.html#custom-snapshot-matchers and domains https://vitest.dev/guide/snapshot.html#custom-snapshot-domain but this has an annoying issue around escaping values. When you use `css` it's typically implemented as `const css = String.raw`, which means that you can write actual CSS instead of JS: ```ts let input = css` .\[color:red\] { color: red; } ` ``` But vitest would double scape the `\`, which would make the snapshot test fail: ```ts let input = css` .\\[color:red\\] { color: red; } ` ``` **Edit**: It is possible with a custom snapshot environment! But this still introduces some levels of indirection. We are also not really testing the same thing anymore. Prettier will be formatting the CSS, we rely on our own CSS parser / printer, which is fine but subtle bugs could maybe be invisible or maybe it unlocks some hidden bugs, who knows. Funnily enough, running tests with this new matcher also goes from `23.60s` to `22.88s` (just 1 run comparison). <img width="1227" height="897" alt="image" src="https://github.com/user-attachments/assets/698a0c67-a1fc-46e3-ba5a-60e5873b32df" /> Long story short, simple `\n` and `\n` boundaries it is! ## Test plan - Everything still works as expected. - There are visual changes in tests, but no actual source code was updated either so there can not be an accidental diff
2026-05-05 22:24:25 +02:00
import { pretty } from '../../tailwindcss/src/test-utils/run'
2024-03-05 14:23:26 +01:00
import tailwindcss from './index'
// We give this file path to PostCSS for processing.
// This file doesn't exist, but the path is used to resolve imports.
// We place it in packages/ because Vitest runs in the monorepo root,
// and packages/tailwindcss must be a sub-folder for
// @import 'tailwindcss' to work.
function inputCssFilePath() {
// Including the current test name to ensure that the cache is invalidated per
// test otherwise the cache will be used across tests.
return `${__dirname}/fixtures/example-project/input.css?test=${expect.getState().currentTestName}`
}
2024-03-05 14:23:26 +01:00
const css = dedent
2024-03-05 14:23:26 +01:00
test("`@import 'tailwindcss'` is replaced with the generated CSS", async () => {
let processor = postcss([
tailwindcss({ base: `${__dirname}/fixtures/example-project`, optimize: { minify: false } }),
])
2024-03-05 14:23:26 +01:00
let result = await processor.process(`@import 'tailwindcss'`, { from: inputCssFilePath() })
2024-03-05 14:23:26 +01:00
Improve snapshot tests (#20013) This PR improves the snapshot tests. At first, this looks like a very silly PR, but I swear I have legit reasons for these changes: First, there are a few places where we use `.toMatchInlineSnapshot()` for tests where we expect that nothing is being generated. This is fine, but the issue is that this means that if you're not careful, and if we have a bug, then these snapshots could start producing something. Updating these snapshots is _too_ easy. So instead, we convert them to an explicit `.toEqual('')` Next, I introduced a `pretty` helper function which is used behind the scenes in the `compileCss(…)`, `run(…)`, and `optimizeCss(…)` test helpers. It's very silly and simple, it either returns `''` when the trimmed input is empty, or it will wrap the result in `\n…\n`. The reason for this is because of how (inline) snapshots works in Vitest. A snapshot result will be in double quotes, and inside backticks: ```ts expect(result.css.trim()).toMatchInlineSnapshot(` "@layer utilities { .foo { color: #000; } .bar { color: red; } }" `) ``` This CSS now starts with `"` and ends with `"`. While that is fine, it starts to get annoying when we have merge conflicts when CSS changes (this is what triggered me to make this PR because I ran into this a dozen times already). Because the first and last CSS line also contain a `"` that you have to keep into account. Instead we now use `\n` around the output, which makes the tests look like this: ```ts expect(pretty(result.css)).toMatchInlineSnapshot(` " @layer utilities { .foo { color: #000; } .bar { color: red; } } " `) ``` In a perfect world, I wish we could use something like: ```css expect(pretty(result.css)).toMatchInlineSnapshot(css` @layer utilities { .foo { color: #000; } .bar { color: red; } } `) ``` That way you are only dealing with CSS, nothing else. Most editors will show syntax highlighting, and even prettier will do formatting on the CSS to keep everything consistent. Unfortunately this also causes issues because when prettier formats this, then the input/output will not always match. We can solve that by parsing both sides and compare the ASTs but that would make things slower. The biggest issue is that Vitest doesn't support this. You can use custom serializers https://vitest.dev/guide/snapshot.html#custom-snapshot-matchers and domains https://vitest.dev/guide/snapshot.html#custom-snapshot-domain but this has an annoying issue around escaping values. When you use `css` it's typically implemented as `const css = String.raw`, which means that you can write actual CSS instead of JS: ```ts let input = css` .\[color:red\] { color: red; } ` ``` But vitest would double scape the `\`, which would make the snapshot test fail: ```ts let input = css` .\\[color:red\\] { color: red; } ` ``` **Edit**: It is possible with a custom snapshot environment! But this still introduces some levels of indirection. We are also not really testing the same thing anymore. Prettier will be formatting the CSS, we rely on our own CSS parser / printer, which is fine but subtle bugs could maybe be invisible or maybe it unlocks some hidden bugs, who knows. Funnily enough, running tests with this new matcher also goes from `23.60s` to `22.88s` (just 1 run comparison). <img width="1227" height="897" alt="image" src="https://github.com/user-attachments/assets/698a0c67-a1fc-46e3-ba5a-60e5873b32df" /> Long story short, simple `\n` and `\n` boundaries it is! ## Test plan - Everything still works as expected. - There are visual changes in tests, but no actual source code was updated either so there can not be an accidental diff
2026-05-05 22:24:25 +02:00
expect(pretty(result.css)).toMatchSnapshot()
2024-03-05 14:23:26 +01:00
// Check for dependency messages
expect(result.messages).toContainEqual({
type: 'dependency',
file: expect.stringMatching(/index.html$/g),
parent: expect.any(String),
plugin: expect.any(String),
})
expect(result.messages).toContainEqual({
type: 'dependency',
file: expect.stringMatching(/index.js$/g),
parent: expect.any(String),
plugin: expect.any(String),
})
expect(result.messages).toContainEqual({
type: 'dir-dependency',
dir: expect.stringMatching(/example-project[/|\\]src$/g),
2024-03-05 14:23:26 +01:00
glob: expect.stringMatching(/^\*\*\/\*/g),
parent: expect.any(String),
plugin: expect.any(String),
})
})
test('output is optimized by Lightning CSS', async () => {
let processor = postcss([
tailwindcss({ base: `${__dirname}/fixtures/example-project`, optimize: { minify: false } }),
])
2024-03-05 14:23:26 +01:00
let result = await processor.process(
css`
@layer utilities {
.foo {
@apply text-[black];
}
}
@layer utilities {
.bar {
color: red;
}
}
`,
{ from: inputCssFilePath() },
2024-03-05 14:23:26 +01:00
)
Improve snapshot tests (#20013) This PR improves the snapshot tests. At first, this looks like a very silly PR, but I swear I have legit reasons for these changes: First, there are a few places where we use `.toMatchInlineSnapshot()` for tests where we expect that nothing is being generated. This is fine, but the issue is that this means that if you're not careful, and if we have a bug, then these snapshots could start producing something. Updating these snapshots is _too_ easy. So instead, we convert them to an explicit `.toEqual('')` Next, I introduced a `pretty` helper function which is used behind the scenes in the `compileCss(…)`, `run(…)`, and `optimizeCss(…)` test helpers. It's very silly and simple, it either returns `''` when the trimmed input is empty, or it will wrap the result in `\n…\n`. The reason for this is because of how (inline) snapshots works in Vitest. A snapshot result will be in double quotes, and inside backticks: ```ts expect(result.css.trim()).toMatchInlineSnapshot(` "@layer utilities { .foo { color: #000; } .bar { color: red; } }" `) ``` This CSS now starts with `"` and ends with `"`. While that is fine, it starts to get annoying when we have merge conflicts when CSS changes (this is what triggered me to make this PR because I ran into this a dozen times already). Because the first and last CSS line also contain a `"` that you have to keep into account. Instead we now use `\n` around the output, which makes the tests look like this: ```ts expect(pretty(result.css)).toMatchInlineSnapshot(` " @layer utilities { .foo { color: #000; } .bar { color: red; } } " `) ``` In a perfect world, I wish we could use something like: ```css expect(pretty(result.css)).toMatchInlineSnapshot(css` @layer utilities { .foo { color: #000; } .bar { color: red; } } `) ``` That way you are only dealing with CSS, nothing else. Most editors will show syntax highlighting, and even prettier will do formatting on the CSS to keep everything consistent. Unfortunately this also causes issues because when prettier formats this, then the input/output will not always match. We can solve that by parsing both sides and compare the ASTs but that would make things slower. The biggest issue is that Vitest doesn't support this. You can use custom serializers https://vitest.dev/guide/snapshot.html#custom-snapshot-matchers and domains https://vitest.dev/guide/snapshot.html#custom-snapshot-domain but this has an annoying issue around escaping values. When you use `css` it's typically implemented as `const css = String.raw`, which means that you can write actual CSS instead of JS: ```ts let input = css` .\[color:red\] { color: red; } ` ``` But vitest would double scape the `\`, which would make the snapshot test fail: ```ts let input = css` .\\[color:red\\] { color: red; } ` ``` **Edit**: It is possible with a custom snapshot environment! But this still introduces some levels of indirection. We are also not really testing the same thing anymore. Prettier will be formatting the CSS, we rely on our own CSS parser / printer, which is fine but subtle bugs could maybe be invisible or maybe it unlocks some hidden bugs, who knows. Funnily enough, running tests with this new matcher also goes from `23.60s` to `22.88s` (just 1 run comparison). <img width="1227" height="897" alt="image" src="https://github.com/user-attachments/assets/698a0c67-a1fc-46e3-ba5a-60e5873b32df" /> Long story short, simple `\n` and `\n` boundaries it is! ## Test plan - Everything still works as expected. - There are visual changes in tests, but no actual source code was updated either so there can not be an accidental diff
2026-05-05 22:24:25 +02:00
expect(pretty(result.css)).toMatchInlineSnapshot(`
"
@layer utilities {
2024-03-05 14:23:26 +01:00
.foo {
color: #000;
}
.bar {
color: red;
}
Improve snapshot tests (#20013) This PR improves the snapshot tests. At first, this looks like a very silly PR, but I swear I have legit reasons for these changes: First, there are a few places where we use `.toMatchInlineSnapshot()` for tests where we expect that nothing is being generated. This is fine, but the issue is that this means that if you're not careful, and if we have a bug, then these snapshots could start producing something. Updating these snapshots is _too_ easy. So instead, we convert them to an explicit `.toEqual('')` Next, I introduced a `pretty` helper function which is used behind the scenes in the `compileCss(…)`, `run(…)`, and `optimizeCss(…)` test helpers. It's very silly and simple, it either returns `''` when the trimmed input is empty, or it will wrap the result in `\n…\n`. The reason for this is because of how (inline) snapshots works in Vitest. A snapshot result will be in double quotes, and inside backticks: ```ts expect(result.css.trim()).toMatchInlineSnapshot(` "@layer utilities { .foo { color: #000; } .bar { color: red; } }" `) ``` This CSS now starts with `"` and ends with `"`. While that is fine, it starts to get annoying when we have merge conflicts when CSS changes (this is what triggered me to make this PR because I ran into this a dozen times already). Because the first and last CSS line also contain a `"` that you have to keep into account. Instead we now use `\n` around the output, which makes the tests look like this: ```ts expect(pretty(result.css)).toMatchInlineSnapshot(` " @layer utilities { .foo { color: #000; } .bar { color: red; } } " `) ``` In a perfect world, I wish we could use something like: ```css expect(pretty(result.css)).toMatchInlineSnapshot(css` @layer utilities { .foo { color: #000; } .bar { color: red; } } `) ``` That way you are only dealing with CSS, nothing else. Most editors will show syntax highlighting, and even prettier will do formatting on the CSS to keep everything consistent. Unfortunately this also causes issues because when prettier formats this, then the input/output will not always match. We can solve that by parsing both sides and compare the ASTs but that would make things slower. The biggest issue is that Vitest doesn't support this. You can use custom serializers https://vitest.dev/guide/snapshot.html#custom-snapshot-matchers and domains https://vitest.dev/guide/snapshot.html#custom-snapshot-domain but this has an annoying issue around escaping values. When you use `css` it's typically implemented as `const css = String.raw`, which means that you can write actual CSS instead of JS: ```ts let input = css` .\[color:red\] { color: red; } ` ``` But vitest would double scape the `\`, which would make the snapshot test fail: ```ts let input = css` .\\[color:red\\] { color: red; } ` ``` **Edit**: It is possible with a custom snapshot environment! But this still introduces some levels of indirection. We are also not really testing the same thing anymore. Prettier will be formatting the CSS, we rely on our own CSS parser / printer, which is fine but subtle bugs could maybe be invisible or maybe it unlocks some hidden bugs, who knows. Funnily enough, running tests with this new matcher also goes from `23.60s` to `22.88s` (just 1 run comparison). <img width="1227" height="897" alt="image" src="https://github.com/user-attachments/assets/698a0c67-a1fc-46e3-ba5a-60e5873b32df" /> Long story short, simple `\n` and `\n` boundaries it is! ## Test plan - Everything still works as expected. - There are visual changes in tests, but no actual source code was updated either so there can not be an accidental diff
2026-05-05 22:24:25 +02:00
}
"
2024-03-05 14:23:26 +01:00
`)
})
test('@apply can be used without emitting the theme in the CSS file', async () => {
let processor = postcss([
tailwindcss({ base: `${__dirname}/fixtures/example-project`, optimize: { minify: false } }),
])
2024-03-05 14:23:26 +01:00
let result = await processor.process(
css`
@reference 'tailwindcss/theme.css';
2024-03-05 14:23:26 +01:00
.foo {
@apply text-red-500;
}
`,
{ from: inputCssFilePath() },
2024-03-05 14:23:26 +01:00
)
Improve snapshot tests (#20013) This PR improves the snapshot tests. At first, this looks like a very silly PR, but I swear I have legit reasons for these changes: First, there are a few places where we use `.toMatchInlineSnapshot()` for tests where we expect that nothing is being generated. This is fine, but the issue is that this means that if you're not careful, and if we have a bug, then these snapshots could start producing something. Updating these snapshots is _too_ easy. So instead, we convert them to an explicit `.toEqual('')` Next, I introduced a `pretty` helper function which is used behind the scenes in the `compileCss(…)`, `run(…)`, and `optimizeCss(…)` test helpers. It's very silly and simple, it either returns `''` when the trimmed input is empty, or it will wrap the result in `\n…\n`. The reason for this is because of how (inline) snapshots works in Vitest. A snapshot result will be in double quotes, and inside backticks: ```ts expect(result.css.trim()).toMatchInlineSnapshot(` "@layer utilities { .foo { color: #000; } .bar { color: red; } }" `) ``` This CSS now starts with `"` and ends with `"`. While that is fine, it starts to get annoying when we have merge conflicts when CSS changes (this is what triggered me to make this PR because I ran into this a dozen times already). Because the first and last CSS line also contain a `"` that you have to keep into account. Instead we now use `\n` around the output, which makes the tests look like this: ```ts expect(pretty(result.css)).toMatchInlineSnapshot(` " @layer utilities { .foo { color: #000; } .bar { color: red; } } " `) ``` In a perfect world, I wish we could use something like: ```css expect(pretty(result.css)).toMatchInlineSnapshot(css` @layer utilities { .foo { color: #000; } .bar { color: red; } } `) ``` That way you are only dealing with CSS, nothing else. Most editors will show syntax highlighting, and even prettier will do formatting on the CSS to keep everything consistent. Unfortunately this also causes issues because when prettier formats this, then the input/output will not always match. We can solve that by parsing both sides and compare the ASTs but that would make things slower. The biggest issue is that Vitest doesn't support this. You can use custom serializers https://vitest.dev/guide/snapshot.html#custom-snapshot-matchers and domains https://vitest.dev/guide/snapshot.html#custom-snapshot-domain but this has an annoying issue around escaping values. When you use `css` it's typically implemented as `const css = String.raw`, which means that you can write actual CSS instead of JS: ```ts let input = css` .\[color:red\] { color: red; } ` ``` But vitest would double scape the `\`, which would make the snapshot test fail: ```ts let input = css` .\\[color:red\\] { color: red; } ` ``` **Edit**: It is possible with a custom snapshot environment! But this still introduces some levels of indirection. We are also not really testing the same thing anymore. Prettier will be formatting the CSS, we rely on our own CSS parser / printer, which is fine but subtle bugs could maybe be invisible or maybe it unlocks some hidden bugs, who knows. Funnily enough, running tests with this new matcher also goes from `23.60s` to `22.88s` (just 1 run comparison). <img width="1227" height="897" alt="image" src="https://github.com/user-attachments/assets/698a0c67-a1fc-46e3-ba5a-60e5873b32df" /> Long story short, simple `\n` and `\n` boundaries it is! ## Test plan - Everything still works as expected. - There are visual changes in tests, but no actual source code was updated either so there can not be an accidental diff
2026-05-05 22:24:25 +02:00
expect(pretty(result.css)).toMatchInlineSnapshot(`
"
.foo {
Improve compatibility with Safari 15 (#17435) This PR improves the compatibility with Tailwind CSS v4 with unsupported browsers with the goal to greatly improve compatibility with Safari 15. To make this work, this PR makes the following changes to all code - Change `oklab(…)` default theme values to use a percentage in the first place (so instead of `--color-red-500: oklch(0.637 0.237 25.331);` we now define it as `--color-red-500: oklch(63.7% 0.237 25.331);` since this syntax has much broader support on Safari). - Polyfill `@property` with a `@supports` query targeting older versions of Safari and Firefox * - Create fallbacks for the `color-mix(…)` function that use _inlined color values from your theme_ so that they can be computed a compile time by `lightningcss`. These fallbacks will convert to srgb to increase compatibility. - Create fallbacks for the _relative color_ feature used in the new shadow utilities and using `color-mix(…)` in case _relative color_ is applied on `currentcolor` (due to limited browser support) - Create fallbacks for gradient interpolation methods (e.g. to support `bg-linear-to-r/oklab`) - Polyfill `@media` queries range syntax. ## A simplified example Given this example CSS input: ```css @import 'tailwindcss'; @source inline('from-cyan-500/50 bg-linear-45'); ``` Here's the updated output CSS including the newly added polyfills and updated `oklab` values: ```css .bg-linear-45 { --tw-gradient-position: 45deg; background-image: linear-gradient(var(--tw-gradient-stops)); } @supports (background-image: linear-gradient(in lab, red, red)) { .bg-linear-45 { --tw-gradient-position: 45deg in oklab; } } .from-cyan-500\\/50 { --tw-gradient-from: oklab(71.5% -.11682 -.08247 / .5); --tw-gradient-stops: var(--tw-gradient-via-stops, var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position)); } @supports (color: color-mix(in lab, red, red)) { .from-cyan-500\\/50 { --tw-gradient-from: color-mix(in oklab, var(--color-cyan-500) 50%, transparent); } } :root, :host { --color-cyan-500: oklch(71.5% .143 215.221); } @supports (((-webkit-hyphens: none)) and (not (margin-trim: 1lh))) or ((-moz-orient: inline) and (not (color: rgb(from red r g b)))) { @layer base { *, :before, :after, ::backdrop { --tw-gradient-position: initial; --tw-gradient-from: #0000; --tw-gradient-via: #0000; --tw-gradient-to: #0000; --tw-gradient-stops: initial; --tw-gradient-via-stops: initial; --tw-gradient-from-position: 0%; --tw-gradient-via-position: 50%; --tw-gradient-to-position: 100%; } } } @property --tw-gradient-position { syntax: "*"; inherits: false } @property --tw-gradient-from { syntax: "<color>"; inherits: false; initial-value: #0000; } @property --tw-gradient-via { syntax: "<color>"; inherits: false; initial-value: #0000; } @property --tw-gradient-to { syntax: "<color>"; inherits: false; initial-value: #0000; } @property --tw-gradient-stops { syntax: "*"; inherits: false } @property --tw-gradient-via-stops { syntax: "*"; inherits: false } @property --tw-gradient-from-position { syntax: "<length-percentage>"; inherits: false; initial-value: 0%; } @property --tw-gradient-via-position { syntax: "<length-percentage>"; inherits: false; initial-value: 50%; } @property --tw-gradient-to-position { syntax: "<length-percentage>"; inherits: false; initial-value: 100%; } ``` ## \* A note on `@property` polyfills and CSS modules On Next.js, CSS module files are required to be _pure_, meaning that all selectors must either be scoped to a class or an ID. Fortunatnyl for us, this does not apply to `@property` rules which we've been using before to initialize CSS variables. However, since we're now bringing back the `@property` polyfills, that would cause unexpected rules to be exported from the CSS file as this: ```css @reference "tailwindcss"; .skew { @apply skew-7; } ``` Would turn to the following file: ```css .skew { /* … */ } @supports (/*…*/) { @layer base { *, :before, :after, ::backdrop { --tw-gradient-position: initial; } } } @property /* … */ ``` Notice that this adds a `*` selector which is not considered pure. Unfortunately there is no way for us to silence this warning or work around it, as the dependency causing this errors ([`postcss-modules-local-by-default`](https://github.com/css-modules/postcss-modules-local-by-default)) is bundled into Next.js. To work around crashes, these polyfills will not apply to CSS modules processed by the PostCSS extension for now. ## Testing on tailwindcss.com To see the changes in effect, take a look at this screencast that compares tailwindcss.com on iOS 15.5 with a version that has the patches of this PR applied: https://github.com/user-attachments/assets/1279d6f5-3c63-4f30-839c-198a789f4292 ## Test plan - Tested on tailwindcss.com via a preview build: https://tailwindcss-com-git-legacy-browsers-tailwindlabs.vercel.app/ - Updated tests - Ensure we also test on Chrome 111, Safari 16.4, Firefox 128 to make sure we have no regressions. Also tested on Safari 16.4, 15.5, 18.0
2025-04-01 13:33:22 +02:00
color: var(--color-red-500, oklch(63.7% .237 25.331));
Improve snapshot tests (#20013) This PR improves the snapshot tests. At first, this looks like a very silly PR, but I swear I have legit reasons for these changes: First, there are a few places where we use `.toMatchInlineSnapshot()` for tests where we expect that nothing is being generated. This is fine, but the issue is that this means that if you're not careful, and if we have a bug, then these snapshots could start producing something. Updating these snapshots is _too_ easy. So instead, we convert them to an explicit `.toEqual('')` Next, I introduced a `pretty` helper function which is used behind the scenes in the `compileCss(…)`, `run(…)`, and `optimizeCss(…)` test helpers. It's very silly and simple, it either returns `''` when the trimmed input is empty, or it will wrap the result in `\n…\n`. The reason for this is because of how (inline) snapshots works in Vitest. A snapshot result will be in double quotes, and inside backticks: ```ts expect(result.css.trim()).toMatchInlineSnapshot(` "@layer utilities { .foo { color: #000; } .bar { color: red; } }" `) ``` This CSS now starts with `"` and ends with `"`. While that is fine, it starts to get annoying when we have merge conflicts when CSS changes (this is what triggered me to make this PR because I ran into this a dozen times already). Because the first and last CSS line also contain a `"` that you have to keep into account. Instead we now use `\n` around the output, which makes the tests look like this: ```ts expect(pretty(result.css)).toMatchInlineSnapshot(` " @layer utilities { .foo { color: #000; } .bar { color: red; } } " `) ``` In a perfect world, I wish we could use something like: ```css expect(pretty(result.css)).toMatchInlineSnapshot(css` @layer utilities { .foo { color: #000; } .bar { color: red; } } `) ``` That way you are only dealing with CSS, nothing else. Most editors will show syntax highlighting, and even prettier will do formatting on the CSS to keep everything consistent. Unfortunately this also causes issues because when prettier formats this, then the input/output will not always match. We can solve that by parsing both sides and compare the ASTs but that would make things slower. The biggest issue is that Vitest doesn't support this. You can use custom serializers https://vitest.dev/guide/snapshot.html#custom-snapshot-matchers and domains https://vitest.dev/guide/snapshot.html#custom-snapshot-domain but this has an annoying issue around escaping values. When you use `css` it's typically implemented as `const css = String.raw`, which means that you can write actual CSS instead of JS: ```ts let input = css` .\[color:red\] { color: red; } ` ``` But vitest would double scape the `\`, which would make the snapshot test fail: ```ts let input = css` .\\[color:red\\] { color: red; } ` ``` **Edit**: It is possible with a custom snapshot environment! But this still introduces some levels of indirection. We are also not really testing the same thing anymore. Prettier will be formatting the CSS, we rely on our own CSS parser / printer, which is fine but subtle bugs could maybe be invisible or maybe it unlocks some hidden bugs, who knows. Funnily enough, running tests with this new matcher also goes from `23.60s` to `22.88s` (just 1 run comparison). <img width="1227" height="897" alt="image" src="https://github.com/user-attachments/assets/698a0c67-a1fc-46e3-ba5a-60e5873b32df" /> Long story short, simple `\n` and `\n` boundaries it is! ## Test plan - Everything still works as expected. - There are visual changes in tests, but no actual source code was updated either so there can not be an accidental diff
2026-05-05 22:24:25 +02:00
}
"
2024-03-05 14:23:26 +01:00
`)
})
describe('processing without specifying a base path', () => {
let filepath: string
let dir: string
beforeEach(async () => {
dir = await mkdtemp(path.join(tmpdir(), 'tw-postcss'))
await mkdir(dir, { recursive: true })
filepath = path.join(dir, 'my-test-file.html')
await writeFile(filepath, `<div class="md:[&:hover]:content-['testing_default_base_path']">`)
})
2024-03-05 14:23:26 +01:00
afterEach(() => unlink(filepath))
test('the current working directory is used by default', async () => {
using spy = vi.spyOn(process, 'cwd')
spy.mockReturnValue(dir)
let processor = postcss([tailwindcss({ optimize: { minify: false } })])
2024-03-05 14:23:26 +01:00
let result = await processor.process(`@import "tailwindcss"`, { from: inputCssFilePath() })
2024-03-05 14:23:26 +01:00
expect(result.css).toContain(
".md\\:\\[\\&\\:hover\\]\\:content-\\[\\'testing_default_base_path\\'\\]",
)
expect(result.messages).toContainEqual({
type: 'dependency',
file: expect.stringMatching(/my-test-file.html$/g),
parent: expect.any(String),
plugin: expect.any(String),
})
})
})
Fall back to the plugin `base` when PostCSS has no `from` option (#19980) ## Summary `@tailwindcss/postcss` derives `inputBasePath` from `result.opts.from`: ```ts let inputFile = result.opts.from ?? '' let inputBasePath = path.dirname(path.resolve(inputFile)) ``` When PostCSS calls the plugin without `from` (some bundlers, including Turbopack, do this for certain CSS inputs), `inputFile` is `''`, `path.resolve('')` returns `process.cwd()`, and `path.dirname(...)` therefore returns the **parent of CWD**. The downstream `compileAst({ base: inputBasePath })` call then asks the resolver to find `tailwindcss` from one level above the project root, which fails with: ``` Can't resolve 'tailwindcss' in '<parent of CWD>' ``` The plugin already computes `base = opts.base ?? process.cwd()` near the top. Reusing that as the fallback gives a sensible default (CWD) and respects an explicit `opts.base` when set. ```diff -let inputBasePath = path.dirname(path.resolve(inputFile)) +let inputBasePath = inputFile + ? path.dirname(path.resolve(inputFile)) + : base ``` ## Test plan Added a test in `packages/@tailwindcss-postcss/src/index.test.ts` that processes `@import 'tailwindcss'` via `processor.process(input)` with no `from` option. Before the fix, this throws `Error: Can't resolve 'tailwindcss' in '<parent of CWD>'`; after the fix, the import resolves and the processor returns non-empty CSS. I wasn't able to run the suite locally — `pnpm build` requires `cargo` for `@tailwindcss/oxide` and I don't have a Rust toolchain set up — so the test has been written to match existing conventions in `index.test.ts` (vitest, plain `postcss([tailwindcss({...})]).process(...)`), and I'm relying on CI to verify. --------- Co-authored-by: rebasecase <rebasecase@localhost> Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2026-04-26 14:45:39 +01:00
test('fallback to `base` directory when `result.opts.from` is not provided', async () => {
using _ = vi.spyOn(console, 'warn').mockImplementation(() => {})
Fall back to the plugin `base` when PostCSS has no `from` option (#19980) ## Summary `@tailwindcss/postcss` derives `inputBasePath` from `result.opts.from`: ```ts let inputFile = result.opts.from ?? '' let inputBasePath = path.dirname(path.resolve(inputFile)) ``` When PostCSS calls the plugin without `from` (some bundlers, including Turbopack, do this for certain CSS inputs), `inputFile` is `''`, `path.resolve('')` returns `process.cwd()`, and `path.dirname(...)` therefore returns the **parent of CWD**. The downstream `compileAst({ base: inputBasePath })` call then asks the resolver to find `tailwindcss` from one level above the project root, which fails with: ``` Can't resolve 'tailwindcss' in '<parent of CWD>' ``` The plugin already computes `base = opts.base ?? process.cwd()` near the top. Reusing that as the fallback gives a sensible default (CWD) and respects an explicit `opts.base` when set. ```diff -let inputBasePath = path.dirname(path.resolve(inputFile)) +let inputBasePath = inputFile + ? path.dirname(path.resolve(inputFile)) + : base ``` ## Test plan Added a test in `packages/@tailwindcss-postcss/src/index.test.ts` that processes `@import 'tailwindcss'` via `processor.process(input)` with no `from` option. Before the fix, this throws `Error: Can't resolve 'tailwindcss' in '<parent of CWD>'`; after the fix, the import resolves and the processor returns non-empty CSS. I wasn't able to run the suite locally — `pnpm build` requires `cargo` for `@tailwindcss/oxide` and I don't have a Rust toolchain set up — so the test has been written to match existing conventions in `index.test.ts` (vitest, plain `postcss([tailwindcss({...})]).process(...)`), and I'm relying on CI to verify. --------- Co-authored-by: rebasecase <rebasecase@localhost> Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2026-04-26 14:45:39 +01:00
let processor = postcss([
tailwindcss({ base: `${__dirname}/fixtures/example-project`, optimize: { minify: false } }),
])
let result = await processor.process(`@import 'tailwindcss'`)
expect(result.css.length).toBeGreaterThan(0)
})
describe('plugins', () => {
test('local CJS plugin', async () => {
let processor = postcss([
tailwindcss({ base: `${__dirname}/fixtures/example-project`, optimize: { minify: false } }),
])
let result = await processor.process(
css`
@import 'tailwindcss/utilities';
Add `@source` support (#14078) This PR is an umbrella PR where we will add support for the new `@source` directive. This will allow you to add explicit content glob patterns if you want to look for Tailwind classes in other files that are not automatically detected yet. Right now this is an addition to the existing auto content detection that is automatically enabled in the `@tailwindcss/postcss` and `@tailwindcss/cli` packages. The `@tailwindcss/vite` package doesn't use the auto content detection, but uses the module graph instead. From an API perspective there is not a lot going on. There are only a few things that you have to know when using the `@source` directive, and you probably already know the rules: 1. You can use multiple `@source` directives if you want. 2. The `@source` accepts a glob pattern so that you can match multiple files at once 3. The pattern is relative to the current file you are in 4. The pattern includes all files it is matching, even git ignored files 1. The motivation for this is so that you can explicitly point to a `node_modules` folder if you want to look at `node_modules` for whatever reason. 6. Right now we don't support negative globs (starting with a `!`) yet, that will be available in the near future. Usage example: ```css /* ./src/input.css */ @import "tailwindcss"; @source "../laravel/resources/views/**/*.blade.php"; @source "../../packages/monorepo-package/**/*.js"; ``` It looks like the PR introduced a lot of changes, but this is a side effect of all the other plumbing work we had to do to make this work. For example: 1. We added dedicated integration tests that run on Linux and Windows in CI (just to make sure that all the `path` logic is correct) 2. We Have to make sure that the glob patterns are always correct even if you are using `@import` in your CSS and use `@source` in an imported file. This is because we receive the flattened CSS contents where all `@import`s are inlined. 3. We have to make sure that we also listen for changes in the files that match any of these patterns and trigger a rebuild. PRs: - [x] https://github.com/tailwindlabs/tailwindcss/pull/14063 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14085 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14079 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14067 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14076 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14080 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14127 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14135 Once all the PRs are merged, then this umbrella PR can be merged. > [!IMPORTANT] > Make sure to merge this without rebasing such that each individual PR ends up on the main branch. --------- Co-authored-by: Philipp Spiess <hello@philippspiess.com> Co-authored-by: Jordan Pittman <jordan@cryptica.me> Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2024-08-07 16:38:44 +02:00
@plugin './plugin.js';
`,
{ from: inputCssFilePath() },
)
Improve snapshot tests (#20013) This PR improves the snapshot tests. At first, this looks like a very silly PR, but I swear I have legit reasons for these changes: First, there are a few places where we use `.toMatchInlineSnapshot()` for tests where we expect that nothing is being generated. This is fine, but the issue is that this means that if you're not careful, and if we have a bug, then these snapshots could start producing something. Updating these snapshots is _too_ easy. So instead, we convert them to an explicit `.toEqual('')` Next, I introduced a `pretty` helper function which is used behind the scenes in the `compileCss(…)`, `run(…)`, and `optimizeCss(…)` test helpers. It's very silly and simple, it either returns `''` when the trimmed input is empty, or it will wrap the result in `\n…\n`. The reason for this is because of how (inline) snapshots works in Vitest. A snapshot result will be in double quotes, and inside backticks: ```ts expect(result.css.trim()).toMatchInlineSnapshot(` "@layer utilities { .foo { color: #000; } .bar { color: red; } }" `) ``` This CSS now starts with `"` and ends with `"`. While that is fine, it starts to get annoying when we have merge conflicts when CSS changes (this is what triggered me to make this PR because I ran into this a dozen times already). Because the first and last CSS line also contain a `"` that you have to keep into account. Instead we now use `\n` around the output, which makes the tests look like this: ```ts expect(pretty(result.css)).toMatchInlineSnapshot(` " @layer utilities { .foo { color: #000; } .bar { color: red; } } " `) ``` In a perfect world, I wish we could use something like: ```css expect(pretty(result.css)).toMatchInlineSnapshot(css` @layer utilities { .foo { color: #000; } .bar { color: red; } } `) ``` That way you are only dealing with CSS, nothing else. Most editors will show syntax highlighting, and even prettier will do formatting on the CSS to keep everything consistent. Unfortunately this also causes issues because when prettier formats this, then the input/output will not always match. We can solve that by parsing both sides and compare the ASTs but that would make things slower. The biggest issue is that Vitest doesn't support this. You can use custom serializers https://vitest.dev/guide/snapshot.html#custom-snapshot-matchers and domains https://vitest.dev/guide/snapshot.html#custom-snapshot-domain but this has an annoying issue around escaping values. When you use `css` it's typically implemented as `const css = String.raw`, which means that you can write actual CSS instead of JS: ```ts let input = css` .\[color:red\] { color: red; } ` ``` But vitest would double scape the `\`, which would make the snapshot test fail: ```ts let input = css` .\\[color:red\\] { color: red; } ` ``` **Edit**: It is possible with a custom snapshot environment! But this still introduces some levels of indirection. We are also not really testing the same thing anymore. Prettier will be formatting the CSS, we rely on our own CSS parser / printer, which is fine but subtle bugs could maybe be invisible or maybe it unlocks some hidden bugs, who knows. Funnily enough, running tests with this new matcher also goes from `23.60s` to `22.88s` (just 1 run comparison). <img width="1227" height="897" alt="image" src="https://github.com/user-attachments/assets/698a0c67-a1fc-46e3-ba5a-60e5873b32df" /> Long story short, simple `\n` and `\n` boundaries it is! ## Test plan - Everything still works as expected. - There are visual changes in tests, but no actual source code was updated either so there can not be an accidental diff
2026-05-05 22:24:25 +02:00
expect(pretty(result.css)).toMatchInlineSnapshot(`
"
.underline {
Add `@source` support (#14078) This PR is an umbrella PR where we will add support for the new `@source` directive. This will allow you to add explicit content glob patterns if you want to look for Tailwind classes in other files that are not automatically detected yet. Right now this is an addition to the existing auto content detection that is automatically enabled in the `@tailwindcss/postcss` and `@tailwindcss/cli` packages. The `@tailwindcss/vite` package doesn't use the auto content detection, but uses the module graph instead. From an API perspective there is not a lot going on. There are only a few things that you have to know when using the `@source` directive, and you probably already know the rules: 1. You can use multiple `@source` directives if you want. 2. The `@source` accepts a glob pattern so that you can match multiple files at once 3. The pattern is relative to the current file you are in 4. The pattern includes all files it is matching, even git ignored files 1. The motivation for this is so that you can explicitly point to a `node_modules` folder if you want to look at `node_modules` for whatever reason. 6. Right now we don't support negative globs (starting with a `!`) yet, that will be available in the near future. Usage example: ```css /* ./src/input.css */ @import "tailwindcss"; @source "../laravel/resources/views/**/*.blade.php"; @source "../../packages/monorepo-package/**/*.js"; ``` It looks like the PR introduced a lot of changes, but this is a side effect of all the other plumbing work we had to do to make this work. For example: 1. We added dedicated integration tests that run on Linux and Windows in CI (just to make sure that all the `path` logic is correct) 2. We Have to make sure that the glob patterns are always correct even if you are using `@import` in your CSS and use `@source` in an imported file. This is because we receive the flattened CSS contents where all `@import`s are inlined. 3. We have to make sure that we also listen for changes in the files that match any of these patterns and trigger a rebuild. PRs: - [x] https://github.com/tailwindlabs/tailwindcss/pull/14063 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14085 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14079 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14067 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14076 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14080 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14127 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14135 Once all the PRs are merged, then this umbrella PR can be merged. > [!IMPORTANT] > Make sure to merge this without rebasing such that each individual PR ends up on the main branch. --------- Co-authored-by: Philipp Spiess <hello@philippspiess.com> Co-authored-by: Jordan Pittman <jordan@cryptica.me> Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2024-08-07 16:38:44 +02:00
text-decoration-line: underline;
}
@media (inverted-colors: inverted) {
.inverted\\:flex {
display: flex;
}
}
.hocus\\:underline:focus, .hocus\\:underline:hover {
text-decoration-line: underline;
Improve snapshot tests (#20013) This PR improves the snapshot tests. At first, this looks like a very silly PR, but I swear I have legit reasons for these changes: First, there are a few places where we use `.toMatchInlineSnapshot()` for tests where we expect that nothing is being generated. This is fine, but the issue is that this means that if you're not careful, and if we have a bug, then these snapshots could start producing something. Updating these snapshots is _too_ easy. So instead, we convert them to an explicit `.toEqual('')` Next, I introduced a `pretty` helper function which is used behind the scenes in the `compileCss(…)`, `run(…)`, and `optimizeCss(…)` test helpers. It's very silly and simple, it either returns `''` when the trimmed input is empty, or it will wrap the result in `\n…\n`. The reason for this is because of how (inline) snapshots works in Vitest. A snapshot result will be in double quotes, and inside backticks: ```ts expect(result.css.trim()).toMatchInlineSnapshot(` "@layer utilities { .foo { color: #000; } .bar { color: red; } }" `) ``` This CSS now starts with `"` and ends with `"`. While that is fine, it starts to get annoying when we have merge conflicts when CSS changes (this is what triggered me to make this PR because I ran into this a dozen times already). Because the first and last CSS line also contain a `"` that you have to keep into account. Instead we now use `\n` around the output, which makes the tests look like this: ```ts expect(pretty(result.css)).toMatchInlineSnapshot(` " @layer utilities { .foo { color: #000; } .bar { color: red; } } " `) ``` In a perfect world, I wish we could use something like: ```css expect(pretty(result.css)).toMatchInlineSnapshot(css` @layer utilities { .foo { color: #000; } .bar { color: red; } } `) ``` That way you are only dealing with CSS, nothing else. Most editors will show syntax highlighting, and even prettier will do formatting on the CSS to keep everything consistent. Unfortunately this also causes issues because when prettier formats this, then the input/output will not always match. We can solve that by parsing both sides and compare the ASTs but that would make things slower. The biggest issue is that Vitest doesn't support this. You can use custom serializers https://vitest.dev/guide/snapshot.html#custom-snapshot-matchers and domains https://vitest.dev/guide/snapshot.html#custom-snapshot-domain but this has an annoying issue around escaping values. When you use `css` it's typically implemented as `const css = String.raw`, which means that you can write actual CSS instead of JS: ```ts let input = css` .\[color:red\] { color: red; } ` ``` But vitest would double scape the `\`, which would make the snapshot test fail: ```ts let input = css` .\\[color:red\\] { color: red; } ` ``` **Edit**: It is possible with a custom snapshot environment! But this still introduces some levels of indirection. We are also not really testing the same thing anymore. Prettier will be formatting the CSS, we rely on our own CSS parser / printer, which is fine but subtle bugs could maybe be invisible or maybe it unlocks some hidden bugs, who knows. Funnily enough, running tests with this new matcher also goes from `23.60s` to `22.88s` (just 1 run comparison). <img width="1227" height="897" alt="image" src="https://github.com/user-attachments/assets/698a0c67-a1fc-46e3-ba5a-60e5873b32df" /> Long story short, simple `\n` and `\n` boundaries it is! ## Test plan - Everything still works as expected. - There are visual changes in tests, but no actual source code was updated either so there can not be an accidental diff
2026-05-05 22:24:25 +02:00
}
"
Add `@source` support (#14078) This PR is an umbrella PR where we will add support for the new `@source` directive. This will allow you to add explicit content glob patterns if you want to look for Tailwind classes in other files that are not automatically detected yet. Right now this is an addition to the existing auto content detection that is automatically enabled in the `@tailwindcss/postcss` and `@tailwindcss/cli` packages. The `@tailwindcss/vite` package doesn't use the auto content detection, but uses the module graph instead. From an API perspective there is not a lot going on. There are only a few things that you have to know when using the `@source` directive, and you probably already know the rules: 1. You can use multiple `@source` directives if you want. 2. The `@source` accepts a glob pattern so that you can match multiple files at once 3. The pattern is relative to the current file you are in 4. The pattern includes all files it is matching, even git ignored files 1. The motivation for this is so that you can explicitly point to a `node_modules` folder if you want to look at `node_modules` for whatever reason. 6. Right now we don't support negative globs (starting with a `!`) yet, that will be available in the near future. Usage example: ```css /* ./src/input.css */ @import "tailwindcss"; @source "../laravel/resources/views/**/*.blade.php"; @source "../../packages/monorepo-package/**/*.js"; ``` It looks like the PR introduced a lot of changes, but this is a side effect of all the other plumbing work we had to do to make this work. For example: 1. We added dedicated integration tests that run on Linux and Windows in CI (just to make sure that all the `path` logic is correct) 2. We Have to make sure that the glob patterns are always correct even if you are using `@import` in your CSS and use `@source` in an imported file. This is because we receive the flattened CSS contents where all `@import`s are inlined. 3. We have to make sure that we also listen for changes in the files that match any of these patterns and trigger a rebuild. PRs: - [x] https://github.com/tailwindlabs/tailwindcss/pull/14063 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14085 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14079 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14067 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14076 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14080 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14127 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14135 Once all the PRs are merged, then this umbrella PR can be merged. > [!IMPORTANT] > Make sure to merge this without rebasing such that each individual PR ends up on the main branch. --------- Co-authored-by: Philipp Spiess <hello@philippspiess.com> Co-authored-by: Jordan Pittman <jordan@cryptica.me> Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2024-08-07 16:38:44 +02:00
`)
})
test('local CJS plugin from `@import`-ed file', async () => {
let processor = postcss([
tailwindcss({ base: `${__dirname}/fixtures/example-project`, optimize: { minify: false } }),
])
let result = await processor.process(
css`
@import 'tailwindcss/utilities';
@import '../example-project/src/relative-import.css';
`,
{ from: `${__dirname}/fixtures/another-project/input.css` },
)
Improve snapshot tests (#20013) This PR improves the snapshot tests. At first, this looks like a very silly PR, but I swear I have legit reasons for these changes: First, there are a few places where we use `.toMatchInlineSnapshot()` for tests where we expect that nothing is being generated. This is fine, but the issue is that this means that if you're not careful, and if we have a bug, then these snapshots could start producing something. Updating these snapshots is _too_ easy. So instead, we convert them to an explicit `.toEqual('')` Next, I introduced a `pretty` helper function which is used behind the scenes in the `compileCss(…)`, `run(…)`, and `optimizeCss(…)` test helpers. It's very silly and simple, it either returns `''` when the trimmed input is empty, or it will wrap the result in `\n…\n`. The reason for this is because of how (inline) snapshots works in Vitest. A snapshot result will be in double quotes, and inside backticks: ```ts expect(result.css.trim()).toMatchInlineSnapshot(` "@layer utilities { .foo { color: #000; } .bar { color: red; } }" `) ``` This CSS now starts with `"` and ends with `"`. While that is fine, it starts to get annoying when we have merge conflicts when CSS changes (this is what triggered me to make this PR because I ran into this a dozen times already). Because the first and last CSS line also contain a `"` that you have to keep into account. Instead we now use `\n` around the output, which makes the tests look like this: ```ts expect(pretty(result.css)).toMatchInlineSnapshot(` " @layer utilities { .foo { color: #000; } .bar { color: red; } } " `) ``` In a perfect world, I wish we could use something like: ```css expect(pretty(result.css)).toMatchInlineSnapshot(css` @layer utilities { .foo { color: #000; } .bar { color: red; } } `) ``` That way you are only dealing with CSS, nothing else. Most editors will show syntax highlighting, and even prettier will do formatting on the CSS to keep everything consistent. Unfortunately this also causes issues because when prettier formats this, then the input/output will not always match. We can solve that by parsing both sides and compare the ASTs but that would make things slower. The biggest issue is that Vitest doesn't support this. You can use custom serializers https://vitest.dev/guide/snapshot.html#custom-snapshot-matchers and domains https://vitest.dev/guide/snapshot.html#custom-snapshot-domain but this has an annoying issue around escaping values. When you use `css` it's typically implemented as `const css = String.raw`, which means that you can write actual CSS instead of JS: ```ts let input = css` .\[color:red\] { color: red; } ` ``` But vitest would double scape the `\`, which would make the snapshot test fail: ```ts let input = css` .\\[color:red\\] { color: red; } ` ``` **Edit**: It is possible with a custom snapshot environment! But this still introduces some levels of indirection. We are also not really testing the same thing anymore. Prettier will be formatting the CSS, we rely on our own CSS parser / printer, which is fine but subtle bugs could maybe be invisible or maybe it unlocks some hidden bugs, who knows. Funnily enough, running tests with this new matcher also goes from `23.60s` to `22.88s` (just 1 run comparison). <img width="1227" height="897" alt="image" src="https://github.com/user-attachments/assets/698a0c67-a1fc-46e3-ba5a-60e5873b32df" /> Long story short, simple `\n` and `\n` boundaries it is! ## Test plan - Everything still works as expected. - There are visual changes in tests, but no actual source code was updated either so there can not be an accidental diff
2026-05-05 22:24:25 +02:00
expect(pretty(result.css)).toMatchInlineSnapshot(`
"
.underline {
text-decoration-line: underline;
}
@media (inverted-colors: inverted) {
.inverted\\:flex {
display: flex;
}
}
.hocus\\:underline:focus, .hocus\\:underline:hover {
text-decoration-line: underline;
Improve snapshot tests (#20013) This PR improves the snapshot tests. At first, this looks like a very silly PR, but I swear I have legit reasons for these changes: First, there are a few places where we use `.toMatchInlineSnapshot()` for tests where we expect that nothing is being generated. This is fine, but the issue is that this means that if you're not careful, and if we have a bug, then these snapshots could start producing something. Updating these snapshots is _too_ easy. So instead, we convert them to an explicit `.toEqual('')` Next, I introduced a `pretty` helper function which is used behind the scenes in the `compileCss(…)`, `run(…)`, and `optimizeCss(…)` test helpers. It's very silly and simple, it either returns `''` when the trimmed input is empty, or it will wrap the result in `\n…\n`. The reason for this is because of how (inline) snapshots works in Vitest. A snapshot result will be in double quotes, and inside backticks: ```ts expect(result.css.trim()).toMatchInlineSnapshot(` "@layer utilities { .foo { color: #000; } .bar { color: red; } }" `) ``` This CSS now starts with `"` and ends with `"`. While that is fine, it starts to get annoying when we have merge conflicts when CSS changes (this is what triggered me to make this PR because I ran into this a dozen times already). Because the first and last CSS line also contain a `"` that you have to keep into account. Instead we now use `\n` around the output, which makes the tests look like this: ```ts expect(pretty(result.css)).toMatchInlineSnapshot(` " @layer utilities { .foo { color: #000; } .bar { color: red; } } " `) ``` In a perfect world, I wish we could use something like: ```css expect(pretty(result.css)).toMatchInlineSnapshot(css` @layer utilities { .foo { color: #000; } .bar { color: red; } } `) ``` That way you are only dealing with CSS, nothing else. Most editors will show syntax highlighting, and even prettier will do formatting on the CSS to keep everything consistent. Unfortunately this also causes issues because when prettier formats this, then the input/output will not always match. We can solve that by parsing both sides and compare the ASTs but that would make things slower. The biggest issue is that Vitest doesn't support this. You can use custom serializers https://vitest.dev/guide/snapshot.html#custom-snapshot-matchers and domains https://vitest.dev/guide/snapshot.html#custom-snapshot-domain but this has an annoying issue around escaping values. When you use `css` it's typically implemented as `const css = String.raw`, which means that you can write actual CSS instead of JS: ```ts let input = css` .\[color:red\] { color: red; } ` ``` But vitest would double scape the `\`, which would make the snapshot test fail: ```ts let input = css` .\\[color:red\\] { color: red; } ` ``` **Edit**: It is possible with a custom snapshot environment! But this still introduces some levels of indirection. We are also not really testing the same thing anymore. Prettier will be formatting the CSS, we rely on our own CSS parser / printer, which is fine but subtle bugs could maybe be invisible or maybe it unlocks some hidden bugs, who knows. Funnily enough, running tests with this new matcher also goes from `23.60s` to `22.88s` (just 1 run comparison). <img width="1227" height="897" alt="image" src="https://github.com/user-attachments/assets/698a0c67-a1fc-46e3-ba5a-60e5873b32df" /> Long story short, simple `\n` and `\n` boundaries it is! ## Test plan - Everything still works as expected. - There are visual changes in tests, but no actual source code was updated either so there can not be an accidental diff
2026-05-05 22:24:25 +02:00
}
"
`)
})
test('published CJS plugin', async () => {
let processor = postcss([
tailwindcss({ base: `${__dirname}/fixtures/example-project`, optimize: { minify: false } }),
])
let result = await processor.process(
css`
@import 'tailwindcss/utilities';
@plugin 'internal-example-plugin';
`,
{ from: inputCssFilePath() },
)
Improve snapshot tests (#20013) This PR improves the snapshot tests. At first, this looks like a very silly PR, but I swear I have legit reasons for these changes: First, there are a few places where we use `.toMatchInlineSnapshot()` for tests where we expect that nothing is being generated. This is fine, but the issue is that this means that if you're not careful, and if we have a bug, then these snapshots could start producing something. Updating these snapshots is _too_ easy. So instead, we convert them to an explicit `.toEqual('')` Next, I introduced a `pretty` helper function which is used behind the scenes in the `compileCss(…)`, `run(…)`, and `optimizeCss(…)` test helpers. It's very silly and simple, it either returns `''` when the trimmed input is empty, or it will wrap the result in `\n…\n`. The reason for this is because of how (inline) snapshots works in Vitest. A snapshot result will be in double quotes, and inside backticks: ```ts expect(result.css.trim()).toMatchInlineSnapshot(` "@layer utilities { .foo { color: #000; } .bar { color: red; } }" `) ``` This CSS now starts with `"` and ends with `"`. While that is fine, it starts to get annoying when we have merge conflicts when CSS changes (this is what triggered me to make this PR because I ran into this a dozen times already). Because the first and last CSS line also contain a `"` that you have to keep into account. Instead we now use `\n` around the output, which makes the tests look like this: ```ts expect(pretty(result.css)).toMatchInlineSnapshot(` " @layer utilities { .foo { color: #000; } .bar { color: red; } } " `) ``` In a perfect world, I wish we could use something like: ```css expect(pretty(result.css)).toMatchInlineSnapshot(css` @layer utilities { .foo { color: #000; } .bar { color: red; } } `) ``` That way you are only dealing with CSS, nothing else. Most editors will show syntax highlighting, and even prettier will do formatting on the CSS to keep everything consistent. Unfortunately this also causes issues because when prettier formats this, then the input/output will not always match. We can solve that by parsing both sides and compare the ASTs but that would make things slower. The biggest issue is that Vitest doesn't support this. You can use custom serializers https://vitest.dev/guide/snapshot.html#custom-snapshot-matchers and domains https://vitest.dev/guide/snapshot.html#custom-snapshot-domain but this has an annoying issue around escaping values. When you use `css` it's typically implemented as `const css = String.raw`, which means that you can write actual CSS instead of JS: ```ts let input = css` .\[color:red\] { color: red; } ` ``` But vitest would double scape the `\`, which would make the snapshot test fail: ```ts let input = css` .\\[color:red\\] { color: red; } ` ``` **Edit**: It is possible with a custom snapshot environment! But this still introduces some levels of indirection. We are also not really testing the same thing anymore. Prettier will be formatting the CSS, we rely on our own CSS parser / printer, which is fine but subtle bugs could maybe be invisible or maybe it unlocks some hidden bugs, who knows. Funnily enough, running tests with this new matcher also goes from `23.60s` to `22.88s` (just 1 run comparison). <img width="1227" height="897" alt="image" src="https://github.com/user-attachments/assets/698a0c67-a1fc-46e3-ba5a-60e5873b32df" /> Long story short, simple `\n` and `\n` boundaries it is! ## Test plan - Everything still works as expected. - There are visual changes in tests, but no actual source code was updated either so there can not be an accidental diff
2026-05-05 22:24:25 +02:00
expect(pretty(result.css)).toMatchInlineSnapshot(`
"
.underline {
text-decoration-line: underline;
}
@media (inverted-colors: inverted) {
.inverted\\:flex {
display: flex;
}
}
.hocus\\:underline:focus, .hocus\\:underline:hover {
text-decoration-line: underline;
Improve snapshot tests (#20013) This PR improves the snapshot tests. At first, this looks like a very silly PR, but I swear I have legit reasons for these changes: First, there are a few places where we use `.toMatchInlineSnapshot()` for tests where we expect that nothing is being generated. This is fine, but the issue is that this means that if you're not careful, and if we have a bug, then these snapshots could start producing something. Updating these snapshots is _too_ easy. So instead, we convert them to an explicit `.toEqual('')` Next, I introduced a `pretty` helper function which is used behind the scenes in the `compileCss(…)`, `run(…)`, and `optimizeCss(…)` test helpers. It's very silly and simple, it either returns `''` when the trimmed input is empty, or it will wrap the result in `\n…\n`. The reason for this is because of how (inline) snapshots works in Vitest. A snapshot result will be in double quotes, and inside backticks: ```ts expect(result.css.trim()).toMatchInlineSnapshot(` "@layer utilities { .foo { color: #000; } .bar { color: red; } }" `) ``` This CSS now starts with `"` and ends with `"`. While that is fine, it starts to get annoying when we have merge conflicts when CSS changes (this is what triggered me to make this PR because I ran into this a dozen times already). Because the first and last CSS line also contain a `"` that you have to keep into account. Instead we now use `\n` around the output, which makes the tests look like this: ```ts expect(pretty(result.css)).toMatchInlineSnapshot(` " @layer utilities { .foo { color: #000; } .bar { color: red; } } " `) ``` In a perfect world, I wish we could use something like: ```css expect(pretty(result.css)).toMatchInlineSnapshot(css` @layer utilities { .foo { color: #000; } .bar { color: red; } } `) ``` That way you are only dealing with CSS, nothing else. Most editors will show syntax highlighting, and even prettier will do formatting on the CSS to keep everything consistent. Unfortunately this also causes issues because when prettier formats this, then the input/output will not always match. We can solve that by parsing both sides and compare the ASTs but that would make things slower. The biggest issue is that Vitest doesn't support this. You can use custom serializers https://vitest.dev/guide/snapshot.html#custom-snapshot-matchers and domains https://vitest.dev/guide/snapshot.html#custom-snapshot-domain but this has an annoying issue around escaping values. When you use `css` it's typically implemented as `const css = String.raw`, which means that you can write actual CSS instead of JS: ```ts let input = css` .\[color:red\] { color: red; } ` ``` But vitest would double scape the `\`, which would make the snapshot test fail: ```ts let input = css` .\\[color:red\\] { color: red; } ` ``` **Edit**: It is possible with a custom snapshot environment! But this still introduces some levels of indirection. We are also not really testing the same thing anymore. Prettier will be formatting the CSS, we rely on our own CSS parser / printer, which is fine but subtle bugs could maybe be invisible or maybe it unlocks some hidden bugs, who knows. Funnily enough, running tests with this new matcher also goes from `23.60s` to `22.88s` (just 1 run comparison). <img width="1227" height="897" alt="image" src="https://github.com/user-attachments/assets/698a0c67-a1fc-46e3-ba5a-60e5873b32df" /> Long story short, simple `\n` and `\n` boundaries it is! ## Test plan - Everything still works as expected. - There are visual changes in tests, but no actual source code was updated either so there can not be an accidental diff
2026-05-05 22:24:25 +02:00
}
"
`)
})
})
test('bail early when Tailwind is not used', async () => {
let processor = postcss([
tailwindcss({ base: `${__dirname}/fixtures/example-project`, optimize: { minify: false } }),
])
let result = await processor.process(
css`
.custom-css {
color: red;
}
`,
{ from: inputCssFilePath() },
)
// `fixtures/example-project` includes an `underline` candidate. But since we
// didn't use `@tailwind utilities` we didn't scan for utilities.
expect(result.css).not.toContain('.underline {')
Improve snapshot tests (#20013) This PR improves the snapshot tests. At first, this looks like a very silly PR, but I swear I have legit reasons for these changes: First, there are a few places where we use `.toMatchInlineSnapshot()` for tests where we expect that nothing is being generated. This is fine, but the issue is that this means that if you're not careful, and if we have a bug, then these snapshots could start producing something. Updating these snapshots is _too_ easy. So instead, we convert them to an explicit `.toEqual('')` Next, I introduced a `pretty` helper function which is used behind the scenes in the `compileCss(…)`, `run(…)`, and `optimizeCss(…)` test helpers. It's very silly and simple, it either returns `''` when the trimmed input is empty, or it will wrap the result in `\n…\n`. The reason for this is because of how (inline) snapshots works in Vitest. A snapshot result will be in double quotes, and inside backticks: ```ts expect(result.css.trim()).toMatchInlineSnapshot(` "@layer utilities { .foo { color: #000; } .bar { color: red; } }" `) ``` This CSS now starts with `"` and ends with `"`. While that is fine, it starts to get annoying when we have merge conflicts when CSS changes (this is what triggered me to make this PR because I ran into this a dozen times already). Because the first and last CSS line also contain a `"` that you have to keep into account. Instead we now use `\n` around the output, which makes the tests look like this: ```ts expect(pretty(result.css)).toMatchInlineSnapshot(` " @layer utilities { .foo { color: #000; } .bar { color: red; } } " `) ``` In a perfect world, I wish we could use something like: ```css expect(pretty(result.css)).toMatchInlineSnapshot(css` @layer utilities { .foo { color: #000; } .bar { color: red; } } `) ``` That way you are only dealing with CSS, nothing else. Most editors will show syntax highlighting, and even prettier will do formatting on the CSS to keep everything consistent. Unfortunately this also causes issues because when prettier formats this, then the input/output will not always match. We can solve that by parsing both sides and compare the ASTs but that would make things slower. The biggest issue is that Vitest doesn't support this. You can use custom serializers https://vitest.dev/guide/snapshot.html#custom-snapshot-matchers and domains https://vitest.dev/guide/snapshot.html#custom-snapshot-domain but this has an annoying issue around escaping values. When you use `css` it's typically implemented as `const css = String.raw`, which means that you can write actual CSS instead of JS: ```ts let input = css` .\[color:red\] { color: red; } ` ``` But vitest would double scape the `\`, which would make the snapshot test fail: ```ts let input = css` .\\[color:red\\] { color: red; } ` ``` **Edit**: It is possible with a custom snapshot environment! But this still introduces some levels of indirection. We are also not really testing the same thing anymore. Prettier will be formatting the CSS, we rely on our own CSS parser / printer, which is fine but subtle bugs could maybe be invisible or maybe it unlocks some hidden bugs, who knows. Funnily enough, running tests with this new matcher also goes from `23.60s` to `22.88s` (just 1 run comparison). <img width="1227" height="897" alt="image" src="https://github.com/user-attachments/assets/698a0c67-a1fc-46e3-ba5a-60e5873b32df" /> Long story short, simple `\n` and `\n` boundaries it is! ## Test plan - Everything still works as expected. - There are visual changes in tests, but no actual source code was updated either so there can not be an accidental diff
2026-05-05 22:24:25 +02:00
expect(pretty(result.css)).toMatchInlineSnapshot(`
"
.custom-css {
color: red;
Improve snapshot tests (#20013) This PR improves the snapshot tests. At first, this looks like a very silly PR, but I swear I have legit reasons for these changes: First, there are a few places where we use `.toMatchInlineSnapshot()` for tests where we expect that nothing is being generated. This is fine, but the issue is that this means that if you're not careful, and if we have a bug, then these snapshots could start producing something. Updating these snapshots is _too_ easy. So instead, we convert them to an explicit `.toEqual('')` Next, I introduced a `pretty` helper function which is used behind the scenes in the `compileCss(…)`, `run(…)`, and `optimizeCss(…)` test helpers. It's very silly and simple, it either returns `''` when the trimmed input is empty, or it will wrap the result in `\n…\n`. The reason for this is because of how (inline) snapshots works in Vitest. A snapshot result will be in double quotes, and inside backticks: ```ts expect(result.css.trim()).toMatchInlineSnapshot(` "@layer utilities { .foo { color: #000; } .bar { color: red; } }" `) ``` This CSS now starts with `"` and ends with `"`. While that is fine, it starts to get annoying when we have merge conflicts when CSS changes (this is what triggered me to make this PR because I ran into this a dozen times already). Because the first and last CSS line also contain a `"` that you have to keep into account. Instead we now use `\n` around the output, which makes the tests look like this: ```ts expect(pretty(result.css)).toMatchInlineSnapshot(` " @layer utilities { .foo { color: #000; } .bar { color: red; } } " `) ``` In a perfect world, I wish we could use something like: ```css expect(pretty(result.css)).toMatchInlineSnapshot(css` @layer utilities { .foo { color: #000; } .bar { color: red; } } `) ``` That way you are only dealing with CSS, nothing else. Most editors will show syntax highlighting, and even prettier will do formatting on the CSS to keep everything consistent. Unfortunately this also causes issues because when prettier formats this, then the input/output will not always match. We can solve that by parsing both sides and compare the ASTs but that would make things slower. The biggest issue is that Vitest doesn't support this. You can use custom serializers https://vitest.dev/guide/snapshot.html#custom-snapshot-matchers and domains https://vitest.dev/guide/snapshot.html#custom-snapshot-domain but this has an annoying issue around escaping values. When you use `css` it's typically implemented as `const css = String.raw`, which means that you can write actual CSS instead of JS: ```ts let input = css` .\[color:red\] { color: red; } ` ``` But vitest would double scape the `\`, which would make the snapshot test fail: ```ts let input = css` .\\[color:red\\] { color: red; } ` ``` **Edit**: It is possible with a custom snapshot environment! But this still introduces some levels of indirection. We are also not really testing the same thing anymore. Prettier will be formatting the CSS, we rely on our own CSS parser / printer, which is fine but subtle bugs could maybe be invisible or maybe it unlocks some hidden bugs, who knows. Funnily enough, running tests with this new matcher also goes from `23.60s` to `22.88s` (just 1 run comparison). <img width="1227" height="897" alt="image" src="https://github.com/user-attachments/assets/698a0c67-a1fc-46e3-ba5a-60e5873b32df" /> Long story short, simple `\n` and `\n` boundaries it is! ## Test plan - Everything still works as expected. - There are visual changes in tests, but no actual source code was updated either so there can not be an accidental diff
2026-05-05 22:24:25 +02:00
}
"
`)
})
test('handle CSS when only using a `@reference` (we should not bail early)', async () => {
let processor = postcss([
tailwindcss({ base: `${__dirname}/fixtures/example-project`, optimize: { minify: false } }),
])
let result = await processor.process(
css`
@reference "tailwindcss/theme.css";
.foo {
@variant md {
bar: baz;
}
}
`,
{ from: inputCssFilePath() },
)
Improve snapshot tests (#20013) This PR improves the snapshot tests. At first, this looks like a very silly PR, but I swear I have legit reasons for these changes: First, there are a few places where we use `.toMatchInlineSnapshot()` for tests where we expect that nothing is being generated. This is fine, but the issue is that this means that if you're not careful, and if we have a bug, then these snapshots could start producing something. Updating these snapshots is _too_ easy. So instead, we convert them to an explicit `.toEqual('')` Next, I introduced a `pretty` helper function which is used behind the scenes in the `compileCss(…)`, `run(…)`, and `optimizeCss(…)` test helpers. It's very silly and simple, it either returns `''` when the trimmed input is empty, or it will wrap the result in `\n…\n`. The reason for this is because of how (inline) snapshots works in Vitest. A snapshot result will be in double quotes, and inside backticks: ```ts expect(result.css.trim()).toMatchInlineSnapshot(` "@layer utilities { .foo { color: #000; } .bar { color: red; } }" `) ``` This CSS now starts with `"` and ends with `"`. While that is fine, it starts to get annoying when we have merge conflicts when CSS changes (this is what triggered me to make this PR because I ran into this a dozen times already). Because the first and last CSS line also contain a `"` that you have to keep into account. Instead we now use `\n` around the output, which makes the tests look like this: ```ts expect(pretty(result.css)).toMatchInlineSnapshot(` " @layer utilities { .foo { color: #000; } .bar { color: red; } } " `) ``` In a perfect world, I wish we could use something like: ```css expect(pretty(result.css)).toMatchInlineSnapshot(css` @layer utilities { .foo { color: #000; } .bar { color: red; } } `) ``` That way you are only dealing with CSS, nothing else. Most editors will show syntax highlighting, and even prettier will do formatting on the CSS to keep everything consistent. Unfortunately this also causes issues because when prettier formats this, then the input/output will not always match. We can solve that by parsing both sides and compare the ASTs but that would make things slower. The biggest issue is that Vitest doesn't support this. You can use custom serializers https://vitest.dev/guide/snapshot.html#custom-snapshot-matchers and domains https://vitest.dev/guide/snapshot.html#custom-snapshot-domain but this has an annoying issue around escaping values. When you use `css` it's typically implemented as `const css = String.raw`, which means that you can write actual CSS instead of JS: ```ts let input = css` .\[color:red\] { color: red; } ` ``` But vitest would double scape the `\`, which would make the snapshot test fail: ```ts let input = css` .\\[color:red\\] { color: red; } ` ``` **Edit**: It is possible with a custom snapshot environment! But this still introduces some levels of indirection. We are also not really testing the same thing anymore. Prettier will be formatting the CSS, we rely on our own CSS parser / printer, which is fine but subtle bugs could maybe be invisible or maybe it unlocks some hidden bugs, who knows. Funnily enough, running tests with this new matcher also goes from `23.60s` to `22.88s` (just 1 run comparison). <img width="1227" height="897" alt="image" src="https://github.com/user-attachments/assets/698a0c67-a1fc-46e3-ba5a-60e5873b32df" /> Long story short, simple `\n` and `\n` boundaries it is! ## Test plan - Everything still works as expected. - There are visual changes in tests, but no actual source code was updated either so there can not be an accidental diff
2026-05-05 22:24:25 +02:00
expect(pretty(result.css)).toMatchInlineSnapshot(`
"
@media (min-width: 48rem) {
.foo {
bar: baz;
}
Improve snapshot tests (#20013) This PR improves the snapshot tests. At first, this looks like a very silly PR, but I swear I have legit reasons for these changes: First, there are a few places where we use `.toMatchInlineSnapshot()` for tests where we expect that nothing is being generated. This is fine, but the issue is that this means that if you're not careful, and if we have a bug, then these snapshots could start producing something. Updating these snapshots is _too_ easy. So instead, we convert them to an explicit `.toEqual('')` Next, I introduced a `pretty` helper function which is used behind the scenes in the `compileCss(…)`, `run(…)`, and `optimizeCss(…)` test helpers. It's very silly and simple, it either returns `''` when the trimmed input is empty, or it will wrap the result in `\n…\n`. The reason for this is because of how (inline) snapshots works in Vitest. A snapshot result will be in double quotes, and inside backticks: ```ts expect(result.css.trim()).toMatchInlineSnapshot(` "@layer utilities { .foo { color: #000; } .bar { color: red; } }" `) ``` This CSS now starts with `"` and ends with `"`. While that is fine, it starts to get annoying when we have merge conflicts when CSS changes (this is what triggered me to make this PR because I ran into this a dozen times already). Because the first and last CSS line also contain a `"` that you have to keep into account. Instead we now use `\n` around the output, which makes the tests look like this: ```ts expect(pretty(result.css)).toMatchInlineSnapshot(` " @layer utilities { .foo { color: #000; } .bar { color: red; } } " `) ``` In a perfect world, I wish we could use something like: ```css expect(pretty(result.css)).toMatchInlineSnapshot(css` @layer utilities { .foo { color: #000; } .bar { color: red; } } `) ``` That way you are only dealing with CSS, nothing else. Most editors will show syntax highlighting, and even prettier will do formatting on the CSS to keep everything consistent. Unfortunately this also causes issues because when prettier formats this, then the input/output will not always match. We can solve that by parsing both sides and compare the ASTs but that would make things slower. The biggest issue is that Vitest doesn't support this. You can use custom serializers https://vitest.dev/guide/snapshot.html#custom-snapshot-matchers and domains https://vitest.dev/guide/snapshot.html#custom-snapshot-domain but this has an annoying issue around escaping values. When you use `css` it's typically implemented as `const css = String.raw`, which means that you can write actual CSS instead of JS: ```ts let input = css` .\[color:red\] { color: red; } ` ``` But vitest would double scape the `\`, which would make the snapshot test fail: ```ts let input = css` .\\[color:red\\] { color: red; } ` ``` **Edit**: It is possible with a custom snapshot environment! But this still introduces some levels of indirection. We are also not really testing the same thing anymore. Prettier will be formatting the CSS, we rely on our own CSS parser / printer, which is fine but subtle bugs could maybe be invisible or maybe it unlocks some hidden bugs, who knows. Funnily enough, running tests with this new matcher also goes from `23.60s` to `22.88s` (just 1 run comparison). <img width="1227" height="897" alt="image" src="https://github.com/user-attachments/assets/698a0c67-a1fc-46e3-ba5a-60e5873b32df" /> Long story short, simple `\n` and `\n` boundaries it is! ## Test plan - Everything still works as expected. - There are visual changes in tests, but no actual source code was updated either so there can not be an accidental diff
2026-05-05 22:24:25 +02:00
}
"
`)
})
test('handle CSS when using a `@variant` using variants that do not rely on the `@theme`', async () => {
let processor = postcss([
tailwindcss({ base: `${__dirname}/fixtures/example-project`, optimize: { minify: false } }),
])
let result = await processor.process(
css`
.foo {
@variant data-is-hoverable {
bar: baz;
}
}
`,
{ from: inputCssFilePath() },
)
Improve snapshot tests (#20013) This PR improves the snapshot tests. At first, this looks like a very silly PR, but I swear I have legit reasons for these changes: First, there are a few places where we use `.toMatchInlineSnapshot()` for tests where we expect that nothing is being generated. This is fine, but the issue is that this means that if you're not careful, and if we have a bug, then these snapshots could start producing something. Updating these snapshots is _too_ easy. So instead, we convert them to an explicit `.toEqual('')` Next, I introduced a `pretty` helper function which is used behind the scenes in the `compileCss(…)`, `run(…)`, and `optimizeCss(…)` test helpers. It's very silly and simple, it either returns `''` when the trimmed input is empty, or it will wrap the result in `\n…\n`. The reason for this is because of how (inline) snapshots works in Vitest. A snapshot result will be in double quotes, and inside backticks: ```ts expect(result.css.trim()).toMatchInlineSnapshot(` "@layer utilities { .foo { color: #000; } .bar { color: red; } }" `) ``` This CSS now starts with `"` and ends with `"`. While that is fine, it starts to get annoying when we have merge conflicts when CSS changes (this is what triggered me to make this PR because I ran into this a dozen times already). Because the first and last CSS line also contain a `"` that you have to keep into account. Instead we now use `\n` around the output, which makes the tests look like this: ```ts expect(pretty(result.css)).toMatchInlineSnapshot(` " @layer utilities { .foo { color: #000; } .bar { color: red; } } " `) ``` In a perfect world, I wish we could use something like: ```css expect(pretty(result.css)).toMatchInlineSnapshot(css` @layer utilities { .foo { color: #000; } .bar { color: red; } } `) ``` That way you are only dealing with CSS, nothing else. Most editors will show syntax highlighting, and even prettier will do formatting on the CSS to keep everything consistent. Unfortunately this also causes issues because when prettier formats this, then the input/output will not always match. We can solve that by parsing both sides and compare the ASTs but that would make things slower. The biggest issue is that Vitest doesn't support this. You can use custom serializers https://vitest.dev/guide/snapshot.html#custom-snapshot-matchers and domains https://vitest.dev/guide/snapshot.html#custom-snapshot-domain but this has an annoying issue around escaping values. When you use `css` it's typically implemented as `const css = String.raw`, which means that you can write actual CSS instead of JS: ```ts let input = css` .\[color:red\] { color: red; } ` ``` But vitest would double scape the `\`, which would make the snapshot test fail: ```ts let input = css` .\\[color:red\\] { color: red; } ` ``` **Edit**: It is possible with a custom snapshot environment! But this still introduces some levels of indirection. We are also not really testing the same thing anymore. Prettier will be formatting the CSS, we rely on our own CSS parser / printer, which is fine but subtle bugs could maybe be invisible or maybe it unlocks some hidden bugs, who knows. Funnily enough, running tests with this new matcher also goes from `23.60s` to `22.88s` (just 1 run comparison). <img width="1227" height="897" alt="image" src="https://github.com/user-attachments/assets/698a0c67-a1fc-46e3-ba5a-60e5873b32df" /> Long story short, simple `\n` and `\n` boundaries it is! ## Test plan - Everything still works as expected. - There are visual changes in tests, but no actual source code was updated either so there can not be an accidental diff
2026-05-05 22:24:25 +02:00
expect(pretty(result.css)).toMatchInlineSnapshot(`
"
.foo[data-is-hoverable] {
bar: baz;
Improve snapshot tests (#20013) This PR improves the snapshot tests. At first, this looks like a very silly PR, but I swear I have legit reasons for these changes: First, there are a few places where we use `.toMatchInlineSnapshot()` for tests where we expect that nothing is being generated. This is fine, but the issue is that this means that if you're not careful, and if we have a bug, then these snapshots could start producing something. Updating these snapshots is _too_ easy. So instead, we convert them to an explicit `.toEqual('')` Next, I introduced a `pretty` helper function which is used behind the scenes in the `compileCss(…)`, `run(…)`, and `optimizeCss(…)` test helpers. It's very silly and simple, it either returns `''` when the trimmed input is empty, or it will wrap the result in `\n…\n`. The reason for this is because of how (inline) snapshots works in Vitest. A snapshot result will be in double quotes, and inside backticks: ```ts expect(result.css.trim()).toMatchInlineSnapshot(` "@layer utilities { .foo { color: #000; } .bar { color: red; } }" `) ``` This CSS now starts with `"` and ends with `"`. While that is fine, it starts to get annoying when we have merge conflicts when CSS changes (this is what triggered me to make this PR because I ran into this a dozen times already). Because the first and last CSS line also contain a `"` that you have to keep into account. Instead we now use `\n` around the output, which makes the tests look like this: ```ts expect(pretty(result.css)).toMatchInlineSnapshot(` " @layer utilities { .foo { color: #000; } .bar { color: red; } } " `) ``` In a perfect world, I wish we could use something like: ```css expect(pretty(result.css)).toMatchInlineSnapshot(css` @layer utilities { .foo { color: #000; } .bar { color: red; } } `) ``` That way you are only dealing with CSS, nothing else. Most editors will show syntax highlighting, and even prettier will do formatting on the CSS to keep everything consistent. Unfortunately this also causes issues because when prettier formats this, then the input/output will not always match. We can solve that by parsing both sides and compare the ASTs but that would make things slower. The biggest issue is that Vitest doesn't support this. You can use custom serializers https://vitest.dev/guide/snapshot.html#custom-snapshot-matchers and domains https://vitest.dev/guide/snapshot.html#custom-snapshot-domain but this has an annoying issue around escaping values. When you use `css` it's typically implemented as `const css = String.raw`, which means that you can write actual CSS instead of JS: ```ts let input = css` .\[color:red\] { color: red; } ` ``` But vitest would double scape the `\`, which would make the snapshot test fail: ```ts let input = css` .\\[color:red\\] { color: red; } ` ``` **Edit**: It is possible with a custom snapshot environment! But this still introduces some levels of indirection. We are also not really testing the same thing anymore. Prettier will be formatting the CSS, we rely on our own CSS parser / printer, which is fine but subtle bugs could maybe be invisible or maybe it unlocks some hidden bugs, who knows. Funnily enough, running tests with this new matcher also goes from `23.60s` to `22.88s` (just 1 run comparison). <img width="1227" height="897" alt="image" src="https://github.com/user-attachments/assets/698a0c67-a1fc-46e3-ba5a-60e5873b32df" /> Long story short, simple `\n` and `\n` boundaries it is! ## Test plan - Everything still works as expected. - There are visual changes in tests, but no actual source code was updated either so there can not be an accidental diff
2026-05-05 22:24:25 +02:00
}
"
`)
})
test('runs `Once` plugins in the right order', async () => {
let before = ''
let after = ''
let processor = postcss([
{
postcssPlugin: 'before',
Once(root) {
before = root.toString()
},
},
tailwindcss({ base: `${__dirname}/fixtures/example-project`, optimize: { minify: false } }),
{
postcssPlugin: 'after',
Once(root) {
after = root.toString()
},
},
])
let result = await processor.process(
css`
@theme {
--color-red-500: red;
}
.custom-css {
color: theme(--color-red-500);
}
`,
{ from: inputCssFilePath() },
)
Improve snapshot tests (#20013) This PR improves the snapshot tests. At first, this looks like a very silly PR, but I swear I have legit reasons for these changes: First, there are a few places where we use `.toMatchInlineSnapshot()` for tests where we expect that nothing is being generated. This is fine, but the issue is that this means that if you're not careful, and if we have a bug, then these snapshots could start producing something. Updating these snapshots is _too_ easy. So instead, we convert them to an explicit `.toEqual('')` Next, I introduced a `pretty` helper function which is used behind the scenes in the `compileCss(…)`, `run(…)`, and `optimizeCss(…)` test helpers. It's very silly and simple, it either returns `''` when the trimmed input is empty, or it will wrap the result in `\n…\n`. The reason for this is because of how (inline) snapshots works in Vitest. A snapshot result will be in double quotes, and inside backticks: ```ts expect(result.css.trim()).toMatchInlineSnapshot(` "@layer utilities { .foo { color: #000; } .bar { color: red; } }" `) ``` This CSS now starts with `"` and ends with `"`. While that is fine, it starts to get annoying when we have merge conflicts when CSS changes (this is what triggered me to make this PR because I ran into this a dozen times already). Because the first and last CSS line also contain a `"` that you have to keep into account. Instead we now use `\n` around the output, which makes the tests look like this: ```ts expect(pretty(result.css)).toMatchInlineSnapshot(` " @layer utilities { .foo { color: #000; } .bar { color: red; } } " `) ``` In a perfect world, I wish we could use something like: ```css expect(pretty(result.css)).toMatchInlineSnapshot(css` @layer utilities { .foo { color: #000; } .bar { color: red; } } `) ``` That way you are only dealing with CSS, nothing else. Most editors will show syntax highlighting, and even prettier will do formatting on the CSS to keep everything consistent. Unfortunately this also causes issues because when prettier formats this, then the input/output will not always match. We can solve that by parsing both sides and compare the ASTs but that would make things slower. The biggest issue is that Vitest doesn't support this. You can use custom serializers https://vitest.dev/guide/snapshot.html#custom-snapshot-matchers and domains https://vitest.dev/guide/snapshot.html#custom-snapshot-domain but this has an annoying issue around escaping values. When you use `css` it's typically implemented as `const css = String.raw`, which means that you can write actual CSS instead of JS: ```ts let input = css` .\[color:red\] { color: red; } ` ``` But vitest would double scape the `\`, which would make the snapshot test fail: ```ts let input = css` .\\[color:red\\] { color: red; } ` ``` **Edit**: It is possible with a custom snapshot environment! But this still introduces some levels of indirection. We are also not really testing the same thing anymore. Prettier will be formatting the CSS, we rely on our own CSS parser / printer, which is fine but subtle bugs could maybe be invisible or maybe it unlocks some hidden bugs, who knows. Funnily enough, running tests with this new matcher also goes from `23.60s` to `22.88s` (just 1 run comparison). <img width="1227" height="897" alt="image" src="https://github.com/user-attachments/assets/698a0c67-a1fc-46e3-ba5a-60e5873b32df" /> Long story short, simple `\n` and `\n` boundaries it is! ## Test plan - Everything still works as expected. - There are visual changes in tests, but no actual source code was updated either so there can not be an accidental diff
2026-05-05 22:24:25 +02:00
expect(pretty(result.css)).toMatchInlineSnapshot(`
"
.custom-css {
color: red;
Improve snapshot tests (#20013) This PR improves the snapshot tests. At first, this looks like a very silly PR, but I swear I have legit reasons for these changes: First, there are a few places where we use `.toMatchInlineSnapshot()` for tests where we expect that nothing is being generated. This is fine, but the issue is that this means that if you're not careful, and if we have a bug, then these snapshots could start producing something. Updating these snapshots is _too_ easy. So instead, we convert them to an explicit `.toEqual('')` Next, I introduced a `pretty` helper function which is used behind the scenes in the `compileCss(…)`, `run(…)`, and `optimizeCss(…)` test helpers. It's very silly and simple, it either returns `''` when the trimmed input is empty, or it will wrap the result in `\n…\n`. The reason for this is because of how (inline) snapshots works in Vitest. A snapshot result will be in double quotes, and inside backticks: ```ts expect(result.css.trim()).toMatchInlineSnapshot(` "@layer utilities { .foo { color: #000; } .bar { color: red; } }" `) ``` This CSS now starts with `"` and ends with `"`. While that is fine, it starts to get annoying when we have merge conflicts when CSS changes (this is what triggered me to make this PR because I ran into this a dozen times already). Because the first and last CSS line also contain a `"` that you have to keep into account. Instead we now use `\n` around the output, which makes the tests look like this: ```ts expect(pretty(result.css)).toMatchInlineSnapshot(` " @layer utilities { .foo { color: #000; } .bar { color: red; } } " `) ``` In a perfect world, I wish we could use something like: ```css expect(pretty(result.css)).toMatchInlineSnapshot(css` @layer utilities { .foo { color: #000; } .bar { color: red; } } `) ``` That way you are only dealing with CSS, nothing else. Most editors will show syntax highlighting, and even prettier will do formatting on the CSS to keep everything consistent. Unfortunately this also causes issues because when prettier formats this, then the input/output will not always match. We can solve that by parsing both sides and compare the ASTs but that would make things slower. The biggest issue is that Vitest doesn't support this. You can use custom serializers https://vitest.dev/guide/snapshot.html#custom-snapshot-matchers and domains https://vitest.dev/guide/snapshot.html#custom-snapshot-domain but this has an annoying issue around escaping values. When you use `css` it's typically implemented as `const css = String.raw`, which means that you can write actual CSS instead of JS: ```ts let input = css` .\[color:red\] { color: red; } ` ``` But vitest would double scape the `\`, which would make the snapshot test fail: ```ts let input = css` .\\[color:red\\] { color: red; } ` ``` **Edit**: It is possible with a custom snapshot environment! But this still introduces some levels of indirection. We are also not really testing the same thing anymore. Prettier will be formatting the CSS, we rely on our own CSS parser / printer, which is fine but subtle bugs could maybe be invisible or maybe it unlocks some hidden bugs, who knows. Funnily enough, running tests with this new matcher also goes from `23.60s` to `22.88s` (just 1 run comparison). <img width="1227" height="897" alt="image" src="https://github.com/user-attachments/assets/698a0c67-a1fc-46e3-ba5a-60e5873b32df" /> Long story short, simple `\n` and `\n` boundaries it is! ## Test plan - Everything still works as expected. - There are visual changes in tests, but no actual source code was updated either so there can not be an accidental diff
2026-05-05 22:24:25 +02:00
}
"
`)
Improve snapshot tests (#20013) This PR improves the snapshot tests. At first, this looks like a very silly PR, but I swear I have legit reasons for these changes: First, there are a few places where we use `.toMatchInlineSnapshot()` for tests where we expect that nothing is being generated. This is fine, but the issue is that this means that if you're not careful, and if we have a bug, then these snapshots could start producing something. Updating these snapshots is _too_ easy. So instead, we convert them to an explicit `.toEqual('')` Next, I introduced a `pretty` helper function which is used behind the scenes in the `compileCss(…)`, `run(…)`, and `optimizeCss(…)` test helpers. It's very silly and simple, it either returns `''` when the trimmed input is empty, or it will wrap the result in `\n…\n`. The reason for this is because of how (inline) snapshots works in Vitest. A snapshot result will be in double quotes, and inside backticks: ```ts expect(result.css.trim()).toMatchInlineSnapshot(` "@layer utilities { .foo { color: #000; } .bar { color: red; } }" `) ``` This CSS now starts with `"` and ends with `"`. While that is fine, it starts to get annoying when we have merge conflicts when CSS changes (this is what triggered me to make this PR because I ran into this a dozen times already). Because the first and last CSS line also contain a `"` that you have to keep into account. Instead we now use `\n` around the output, which makes the tests look like this: ```ts expect(pretty(result.css)).toMatchInlineSnapshot(` " @layer utilities { .foo { color: #000; } .bar { color: red; } } " `) ``` In a perfect world, I wish we could use something like: ```css expect(pretty(result.css)).toMatchInlineSnapshot(css` @layer utilities { .foo { color: #000; } .bar { color: red; } } `) ``` That way you are only dealing with CSS, nothing else. Most editors will show syntax highlighting, and even prettier will do formatting on the CSS to keep everything consistent. Unfortunately this also causes issues because when prettier formats this, then the input/output will not always match. We can solve that by parsing both sides and compare the ASTs but that would make things slower. The biggest issue is that Vitest doesn't support this. You can use custom serializers https://vitest.dev/guide/snapshot.html#custom-snapshot-matchers and domains https://vitest.dev/guide/snapshot.html#custom-snapshot-domain but this has an annoying issue around escaping values. When you use `css` it's typically implemented as `const css = String.raw`, which means that you can write actual CSS instead of JS: ```ts let input = css` .\[color:red\] { color: red; } ` ``` But vitest would double scape the `\`, which would make the snapshot test fail: ```ts let input = css` .\\[color:red\\] { color: red; } ` ``` **Edit**: It is possible with a custom snapshot environment! But this still introduces some levels of indirection. We are also not really testing the same thing anymore. Prettier will be formatting the CSS, we rely on our own CSS parser / printer, which is fine but subtle bugs could maybe be invisible or maybe it unlocks some hidden bugs, who knows. Funnily enough, running tests with this new matcher also goes from `23.60s` to `22.88s` (just 1 run comparison). <img width="1227" height="897" alt="image" src="https://github.com/user-attachments/assets/698a0c67-a1fc-46e3-ba5a-60e5873b32df" /> Long story short, simple `\n` and `\n` boundaries it is! ## Test plan - Everything still works as expected. - There are visual changes in tests, but no actual source code was updated either so there can not be an accidental diff
2026-05-05 22:24:25 +02:00
expect(pretty(before)).toMatchInlineSnapshot(`
"
@theme {
--color-red-500: red;
}
.custom-css {
color: theme(--color-red-500);
Improve snapshot tests (#20013) This PR improves the snapshot tests. At first, this looks like a very silly PR, but I swear I have legit reasons for these changes: First, there are a few places where we use `.toMatchInlineSnapshot()` for tests where we expect that nothing is being generated. This is fine, but the issue is that this means that if you're not careful, and if we have a bug, then these snapshots could start producing something. Updating these snapshots is _too_ easy. So instead, we convert them to an explicit `.toEqual('')` Next, I introduced a `pretty` helper function which is used behind the scenes in the `compileCss(…)`, `run(…)`, and `optimizeCss(…)` test helpers. It's very silly and simple, it either returns `''` when the trimmed input is empty, or it will wrap the result in `\n…\n`. The reason for this is because of how (inline) snapshots works in Vitest. A snapshot result will be in double quotes, and inside backticks: ```ts expect(result.css.trim()).toMatchInlineSnapshot(` "@layer utilities { .foo { color: #000; } .bar { color: red; } }" `) ``` This CSS now starts with `"` and ends with `"`. While that is fine, it starts to get annoying when we have merge conflicts when CSS changes (this is what triggered me to make this PR because I ran into this a dozen times already). Because the first and last CSS line also contain a `"` that you have to keep into account. Instead we now use `\n` around the output, which makes the tests look like this: ```ts expect(pretty(result.css)).toMatchInlineSnapshot(` " @layer utilities { .foo { color: #000; } .bar { color: red; } } " `) ``` In a perfect world, I wish we could use something like: ```css expect(pretty(result.css)).toMatchInlineSnapshot(css` @layer utilities { .foo { color: #000; } .bar { color: red; } } `) ``` That way you are only dealing with CSS, nothing else. Most editors will show syntax highlighting, and even prettier will do formatting on the CSS to keep everything consistent. Unfortunately this also causes issues because when prettier formats this, then the input/output will not always match. We can solve that by parsing both sides and compare the ASTs but that would make things slower. The biggest issue is that Vitest doesn't support this. You can use custom serializers https://vitest.dev/guide/snapshot.html#custom-snapshot-matchers and domains https://vitest.dev/guide/snapshot.html#custom-snapshot-domain but this has an annoying issue around escaping values. When you use `css` it's typically implemented as `const css = String.raw`, which means that you can write actual CSS instead of JS: ```ts let input = css` .\[color:red\] { color: red; } ` ``` But vitest would double scape the `\`, which would make the snapshot test fail: ```ts let input = css` .\\[color:red\\] { color: red; } ` ``` **Edit**: It is possible with a custom snapshot environment! But this still introduces some levels of indirection. We are also not really testing the same thing anymore. Prettier will be formatting the CSS, we rely on our own CSS parser / printer, which is fine but subtle bugs could maybe be invisible or maybe it unlocks some hidden bugs, who knows. Funnily enough, running tests with this new matcher also goes from `23.60s` to `22.88s` (just 1 run comparison). <img width="1227" height="897" alt="image" src="https://github.com/user-attachments/assets/698a0c67-a1fc-46e3-ba5a-60e5873b32df" /> Long story short, simple `\n` and `\n` boundaries it is! ## Test plan - Everything still works as expected. - There are visual changes in tests, but no actual source code was updated either so there can not be an accidental diff
2026-05-05 22:24:25 +02:00
}
"
`)
Improve snapshot tests (#20013) This PR improves the snapshot tests. At first, this looks like a very silly PR, but I swear I have legit reasons for these changes: First, there are a few places where we use `.toMatchInlineSnapshot()` for tests where we expect that nothing is being generated. This is fine, but the issue is that this means that if you're not careful, and if we have a bug, then these snapshots could start producing something. Updating these snapshots is _too_ easy. So instead, we convert them to an explicit `.toEqual('')` Next, I introduced a `pretty` helper function which is used behind the scenes in the `compileCss(…)`, `run(…)`, and `optimizeCss(…)` test helpers. It's very silly and simple, it either returns `''` when the trimmed input is empty, or it will wrap the result in `\n…\n`. The reason for this is because of how (inline) snapshots works in Vitest. A snapshot result will be in double quotes, and inside backticks: ```ts expect(result.css.trim()).toMatchInlineSnapshot(` "@layer utilities { .foo { color: #000; } .bar { color: red; } }" `) ``` This CSS now starts with `"` and ends with `"`. While that is fine, it starts to get annoying when we have merge conflicts when CSS changes (this is what triggered me to make this PR because I ran into this a dozen times already). Because the first and last CSS line also contain a `"` that you have to keep into account. Instead we now use `\n` around the output, which makes the tests look like this: ```ts expect(pretty(result.css)).toMatchInlineSnapshot(` " @layer utilities { .foo { color: #000; } .bar { color: red; } } " `) ``` In a perfect world, I wish we could use something like: ```css expect(pretty(result.css)).toMatchInlineSnapshot(css` @layer utilities { .foo { color: #000; } .bar { color: red; } } `) ``` That way you are only dealing with CSS, nothing else. Most editors will show syntax highlighting, and even prettier will do formatting on the CSS to keep everything consistent. Unfortunately this also causes issues because when prettier formats this, then the input/output will not always match. We can solve that by parsing both sides and compare the ASTs but that would make things slower. The biggest issue is that Vitest doesn't support this. You can use custom serializers https://vitest.dev/guide/snapshot.html#custom-snapshot-matchers and domains https://vitest.dev/guide/snapshot.html#custom-snapshot-domain but this has an annoying issue around escaping values. When you use `css` it's typically implemented as `const css = String.raw`, which means that you can write actual CSS instead of JS: ```ts let input = css` .\[color:red\] { color: red; } ` ``` But vitest would double scape the `\`, which would make the snapshot test fail: ```ts let input = css` .\\[color:red\\] { color: red; } ` ``` **Edit**: It is possible with a custom snapshot environment! But this still introduces some levels of indirection. We are also not really testing the same thing anymore. Prettier will be formatting the CSS, we rely on our own CSS parser / printer, which is fine but subtle bugs could maybe be invisible or maybe it unlocks some hidden bugs, who knows. Funnily enough, running tests with this new matcher also goes from `23.60s` to `22.88s` (just 1 run comparison). <img width="1227" height="897" alt="image" src="https://github.com/user-attachments/assets/698a0c67-a1fc-46e3-ba5a-60e5873b32df" /> Long story short, simple `\n` and `\n` boundaries it is! ## Test plan - Everything still works as expected. - There are visual changes in tests, but no actual source code was updated either so there can not be an accidental diff
2026-05-05 22:24:25 +02:00
expect(pretty(after)).toMatchInlineSnapshot(`
"
.custom-css {
color: red;
Improve snapshot tests (#20013) This PR improves the snapshot tests. At first, this looks like a very silly PR, but I swear I have legit reasons for these changes: First, there are a few places where we use `.toMatchInlineSnapshot()` for tests where we expect that nothing is being generated. This is fine, but the issue is that this means that if you're not careful, and if we have a bug, then these snapshots could start producing something. Updating these snapshots is _too_ easy. So instead, we convert them to an explicit `.toEqual('')` Next, I introduced a `pretty` helper function which is used behind the scenes in the `compileCss(…)`, `run(…)`, and `optimizeCss(…)` test helpers. It's very silly and simple, it either returns `''` when the trimmed input is empty, or it will wrap the result in `\n…\n`. The reason for this is because of how (inline) snapshots works in Vitest. A snapshot result will be in double quotes, and inside backticks: ```ts expect(result.css.trim()).toMatchInlineSnapshot(` "@layer utilities { .foo { color: #000; } .bar { color: red; } }" `) ``` This CSS now starts with `"` and ends with `"`. While that is fine, it starts to get annoying when we have merge conflicts when CSS changes (this is what triggered me to make this PR because I ran into this a dozen times already). Because the first and last CSS line also contain a `"` that you have to keep into account. Instead we now use `\n` around the output, which makes the tests look like this: ```ts expect(pretty(result.css)).toMatchInlineSnapshot(` " @layer utilities { .foo { color: #000; } .bar { color: red; } } " `) ``` In a perfect world, I wish we could use something like: ```css expect(pretty(result.css)).toMatchInlineSnapshot(css` @layer utilities { .foo { color: #000; } .bar { color: red; } } `) ``` That way you are only dealing with CSS, nothing else. Most editors will show syntax highlighting, and even prettier will do formatting on the CSS to keep everything consistent. Unfortunately this also causes issues because when prettier formats this, then the input/output will not always match. We can solve that by parsing both sides and compare the ASTs but that would make things slower. The biggest issue is that Vitest doesn't support this. You can use custom serializers https://vitest.dev/guide/snapshot.html#custom-snapshot-matchers and domains https://vitest.dev/guide/snapshot.html#custom-snapshot-domain but this has an annoying issue around escaping values. When you use `css` it's typically implemented as `const css = String.raw`, which means that you can write actual CSS instead of JS: ```ts let input = css` .\[color:red\] { color: red; } ` ``` But vitest would double scape the `\`, which would make the snapshot test fail: ```ts let input = css` .\\[color:red\\] { color: red; } ` ``` **Edit**: It is possible with a custom snapshot environment! But this still introduces some levels of indirection. We are also not really testing the same thing anymore. Prettier will be formatting the CSS, we rely on our own CSS parser / printer, which is fine but subtle bugs could maybe be invisible or maybe it unlocks some hidden bugs, who knows. Funnily enough, running tests with this new matcher also goes from `23.60s` to `22.88s` (just 1 run comparison). <img width="1227" height="897" alt="image" src="https://github.com/user-attachments/assets/698a0c67-a1fc-46e3-ba5a-60e5873b32df" /> Long story short, simple `\n` and `\n` boundaries it is! ## Test plan - Everything still works as expected. - There are visual changes in tests, but no actual source code was updated either so there can not be an accidental diff
2026-05-05 22:24:25 +02:00
}
"
`)
})
describe('concurrent builds', () => {
let dir: string
beforeEach(async () => {
dir = await mkdtemp(path.join(tmpdir(), 'tw-postcss'))
await writeFile(path.join(dir, 'index.html'), `<div class="underline"></div>`)
await writeFile(
path.join(dir, 'index.css'),
css`
@import './dependency.css';
`,
)
await writeFile(
path.join(dir, 'dependency.css'),
css`
@tailwind utilities;
`,
)
})
afterEach(() => rm(dir, { recursive: true, force: true }))
test('does experience a race-condition when calling the plugin two times for the same change', async () => {
using spy = vi.spyOn(process, 'cwd')
spy.mockReturnValue(dir)
let from = path.join(dir, 'index.css')
let input = (await readFile(path.join(dir, 'index.css'))).toString()
let plugin = tailwindcss({ optimize: { minify: false } })
async function run(input: string): Promise<string> {
let ast = postcss.parse(input)
for (let runner of (plugin as any).plugins) {
if (runner.Once) {
await runner.Once(ast, { postcss, result: { opts: { from }, messages: [] } })
}
}
return ast.toString()
}
let result = await run(input)
expect(result).toContain('.underline')
// Ensure that the mtime is updated
await new Promise((resolve) => setTimeout(resolve, 100))
await writeFile(
path.join(dir, 'dependency.css'),
css`
@tailwind utilities;
.red {
color: red;
}
`,
)
let promise1 = run(input)
let promise2 = run(input)
expect(await promise1).toContain('.red')
expect(await promise2).toContain('.red')
})
})
PostCSS: Fix Turbopack 'one-revision-behind' bug (#17554) Closes #17508 This PR fixes another issue we found that caused dev builds with Next.js and Turbopack to resolve the CSS file that was saved one revision before the latest update. When debugging this we noticed that the PostCSS entry is called twice for every one update when changing the input CSS file directly. That was caused by the input file itself being added as a _dependency_ so you would first get the callback that a _dependency_ has updated (at which point we look at the file system and figure out we need a full-rebuild because the input.css file has changed) and then another callback for when the _input file_ has updated. The problem with the second callback was that the file-system was already scanned for updates and since this includes the `mtimes` for the input file, we seemingly thought that the input file did not change. However, the issue is that the first callback actually came with an outdated PostCSS input AST... We found that this problem arises when you register the input CSS as a dependency of itself. This is not expected and we actually guard against this in the PostCSS client. However, we found that the input `from` argument is _a relative path when using Next.js with Turbopack_ so that check was not working as expected. ## Test plan Added the change to the repro from #17508 and it seems to work fine now. https://github.com/user-attachments/assets/2acb0078-f961-4498-be1a-b1c72d5ceda1 Also added a unit test to ensure we document that the input file path can be a relative path. Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2025-04-04 14:58:50 +02:00
test('does not register the input file as a dependency, even if it is passed in as relative path', async () => {
let processor = postcss([
tailwindcss({ base: `${__dirname}/fixtures/example-project`, optimize: { minify: false } }),
])
let result = await processor.process(`@tailwind utilities`, { from: './input.css' })
Improve snapshot tests (#20013) This PR improves the snapshot tests. At first, this looks like a very silly PR, but I swear I have legit reasons for these changes: First, there are a few places where we use `.toMatchInlineSnapshot()` for tests where we expect that nothing is being generated. This is fine, but the issue is that this means that if you're not careful, and if we have a bug, then these snapshots could start producing something. Updating these snapshots is _too_ easy. So instead, we convert them to an explicit `.toEqual('')` Next, I introduced a `pretty` helper function which is used behind the scenes in the `compileCss(…)`, `run(…)`, and `optimizeCss(…)` test helpers. It's very silly and simple, it either returns `''` when the trimmed input is empty, or it will wrap the result in `\n…\n`. The reason for this is because of how (inline) snapshots works in Vitest. A snapshot result will be in double quotes, and inside backticks: ```ts expect(result.css.trim()).toMatchInlineSnapshot(` "@layer utilities { .foo { color: #000; } .bar { color: red; } }" `) ``` This CSS now starts with `"` and ends with `"`. While that is fine, it starts to get annoying when we have merge conflicts when CSS changes (this is what triggered me to make this PR because I ran into this a dozen times already). Because the first and last CSS line also contain a `"` that you have to keep into account. Instead we now use `\n` around the output, which makes the tests look like this: ```ts expect(pretty(result.css)).toMatchInlineSnapshot(` " @layer utilities { .foo { color: #000; } .bar { color: red; } } " `) ``` In a perfect world, I wish we could use something like: ```css expect(pretty(result.css)).toMatchInlineSnapshot(css` @layer utilities { .foo { color: #000; } .bar { color: red; } } `) ``` That way you are only dealing with CSS, nothing else. Most editors will show syntax highlighting, and even prettier will do formatting on the CSS to keep everything consistent. Unfortunately this also causes issues because when prettier formats this, then the input/output will not always match. We can solve that by parsing both sides and compare the ASTs but that would make things slower. The biggest issue is that Vitest doesn't support this. You can use custom serializers https://vitest.dev/guide/snapshot.html#custom-snapshot-matchers and domains https://vitest.dev/guide/snapshot.html#custom-snapshot-domain but this has an annoying issue around escaping values. When you use `css` it's typically implemented as `const css = String.raw`, which means that you can write actual CSS instead of JS: ```ts let input = css` .\[color:red\] { color: red; } ` ``` But vitest would double scape the `\`, which would make the snapshot test fail: ```ts let input = css` .\\[color:red\\] { color: red; } ` ``` **Edit**: It is possible with a custom snapshot environment! But this still introduces some levels of indirection. We are also not really testing the same thing anymore. Prettier will be formatting the CSS, we rely on our own CSS parser / printer, which is fine but subtle bugs could maybe be invisible or maybe it unlocks some hidden bugs, who knows. Funnily enough, running tests with this new matcher also goes from `23.60s` to `22.88s` (just 1 run comparison). <img width="1227" height="897" alt="image" src="https://github.com/user-attachments/assets/698a0c67-a1fc-46e3-ba5a-60e5873b32df" /> Long story short, simple `\n` and `\n` boundaries it is! ## Test plan - Everything still works as expected. - There are visual changes in tests, but no actual source code was updated either so there can not be an accidental diff
2026-05-05 22:24:25 +02:00
expect(pretty(result.css)).toMatchInlineSnapshot(`
"
.underline {
PostCSS: Fix Turbopack 'one-revision-behind' bug (#17554) Closes #17508 This PR fixes another issue we found that caused dev builds with Next.js and Turbopack to resolve the CSS file that was saved one revision before the latest update. When debugging this we noticed that the PostCSS entry is called twice for every one update when changing the input CSS file directly. That was caused by the input file itself being added as a _dependency_ so you would first get the callback that a _dependency_ has updated (at which point we look at the file system and figure out we need a full-rebuild because the input.css file has changed) and then another callback for when the _input file_ has updated. The problem with the second callback was that the file-system was already scanned for updates and since this includes the `mtimes` for the input file, we seemingly thought that the input file did not change. However, the issue is that the first callback actually came with an outdated PostCSS input AST... We found that this problem arises when you register the input CSS as a dependency of itself. This is not expected and we actually guard against this in the PostCSS client. However, we found that the input `from` argument is _a relative path when using Next.js with Turbopack_ so that check was not working as expected. ## Test plan Added the change to the repro from #17508 and it seems to work fine now. https://github.com/user-attachments/assets/2acb0078-f961-4498-be1a-b1c72d5ceda1 Also added a unit test to ensure we document that the input file path can be a relative path. Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2025-04-04 14:58:50 +02:00
text-decoration-line: underline;
Improve snapshot tests (#20013) This PR improves the snapshot tests. At first, this looks like a very silly PR, but I swear I have legit reasons for these changes: First, there are a few places where we use `.toMatchInlineSnapshot()` for tests where we expect that nothing is being generated. This is fine, but the issue is that this means that if you're not careful, and if we have a bug, then these snapshots could start producing something. Updating these snapshots is _too_ easy. So instead, we convert them to an explicit `.toEqual('')` Next, I introduced a `pretty` helper function which is used behind the scenes in the `compileCss(…)`, `run(…)`, and `optimizeCss(…)` test helpers. It's very silly and simple, it either returns `''` when the trimmed input is empty, or it will wrap the result in `\n…\n`. The reason for this is because of how (inline) snapshots works in Vitest. A snapshot result will be in double quotes, and inside backticks: ```ts expect(result.css.trim()).toMatchInlineSnapshot(` "@layer utilities { .foo { color: #000; } .bar { color: red; } }" `) ``` This CSS now starts with `"` and ends with `"`. While that is fine, it starts to get annoying when we have merge conflicts when CSS changes (this is what triggered me to make this PR because I ran into this a dozen times already). Because the first and last CSS line also contain a `"` that you have to keep into account. Instead we now use `\n` around the output, which makes the tests look like this: ```ts expect(pretty(result.css)).toMatchInlineSnapshot(` " @layer utilities { .foo { color: #000; } .bar { color: red; } } " `) ``` In a perfect world, I wish we could use something like: ```css expect(pretty(result.css)).toMatchInlineSnapshot(css` @layer utilities { .foo { color: #000; } .bar { color: red; } } `) ``` That way you are only dealing with CSS, nothing else. Most editors will show syntax highlighting, and even prettier will do formatting on the CSS to keep everything consistent. Unfortunately this also causes issues because when prettier formats this, then the input/output will not always match. We can solve that by parsing both sides and compare the ASTs but that would make things slower. The biggest issue is that Vitest doesn't support this. You can use custom serializers https://vitest.dev/guide/snapshot.html#custom-snapshot-matchers and domains https://vitest.dev/guide/snapshot.html#custom-snapshot-domain but this has an annoying issue around escaping values. When you use `css` it's typically implemented as `const css = String.raw`, which means that you can write actual CSS instead of JS: ```ts let input = css` .\[color:red\] { color: red; } ` ``` But vitest would double scape the `\`, which would make the snapshot test fail: ```ts let input = css` .\\[color:red\\] { color: red; } ` ``` **Edit**: It is possible with a custom snapshot environment! But this still introduces some levels of indirection. We are also not really testing the same thing anymore. Prettier will be formatting the CSS, we rely on our own CSS parser / printer, which is fine but subtle bugs could maybe be invisible or maybe it unlocks some hidden bugs, who knows. Funnily enough, running tests with this new matcher also goes from `23.60s` to `22.88s` (just 1 run comparison). <img width="1227" height="897" alt="image" src="https://github.com/user-attachments/assets/698a0c67-a1fc-46e3-ba5a-60e5873b32df" /> Long story short, simple `\n` and `\n` boundaries it is! ## Test plan - Everything still works as expected. - There are visual changes in tests, but no actual source code was updated either so there can not be an accidental diff
2026-05-05 22:24:25 +02:00
}
"
PostCSS: Fix Turbopack 'one-revision-behind' bug (#17554) Closes #17508 This PR fixes another issue we found that caused dev builds with Next.js and Turbopack to resolve the CSS file that was saved one revision before the latest update. When debugging this we noticed that the PostCSS entry is called twice for every one update when changing the input CSS file directly. That was caused by the input file itself being added as a _dependency_ so you would first get the callback that a _dependency_ has updated (at which point we look at the file system and figure out we need a full-rebuild because the input.css file has changed) and then another callback for when the _input file_ has updated. The problem with the second callback was that the file-system was already scanned for updates and since this includes the `mtimes` for the input file, we seemingly thought that the input file did not change. However, the issue is that the first callback actually came with an outdated PostCSS input AST... We found that this problem arises when you register the input CSS as a dependency of itself. This is not expected and we actually guard against this in the PostCSS client. However, we found that the input `from` argument is _a relative path when using Next.js with Turbopack_ so that check was not working as expected. ## Test plan Added the change to the repro from #17508 and it seems to work fine now. https://github.com/user-attachments/assets/2acb0078-f961-4498-be1a-b1c72d5ceda1 Also added a unit test to ensure we document that the input file path can be a relative path. Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2025-04-04 14:58:50 +02:00
`)
// Check for dependency messages
expect(result.messages).not.toContainEqual({
type: 'dependency',
file: expect.stringMatching(/input.css$/g),
parent: expect.any(String),
plugin: expect.any(String),
})
})