Improve performance of `@tailwindcss/postcss` and `@tailwindcss/vite` (#15226)
This PR improves the performance of the `@tailwindcss/postcss` and
`@tailwindcss/vite` implementations.
The issue is that in some scenarios, if you have multiple `.css` files,
then all of the CSS files are ran through the Tailwind CSS compiler. The
issue with this is that in a lot of cases, the CSS files aren't even
related to Tailwind CSS at all.
E.g.: in a Next.js project, if you use the `next/font/local` tool, then
every font you used will be in a separate CSS file. This means that we
run Tailwind CSS in all these files as well.
That said, running Tailwind CSS on these files isn't the end of the
world because we still need to handle `@import` in case `@tailwind
utilities` is being used. However, we also run the auto source detection
logic for every CSS file in the system. This part is bad.
To solve this, this PR introduces an internal `features` to collect what
CSS features are used throughout the system (`@import`, `@plugin`,
`@apply`, `@tailwind utilities`, etc…)
The `@tailwindcss/postcss` and `@tailwindcss/vite` plugin can use that
information to decide if they can take some shortcuts or not.
---
Overall, this means that we don't run the slow parts of Tailwind CSS if
we don't need to.
---------
Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2024-11-29 16:59:29 +01:00
|
|
|
import dedent from 'dedent'
|
2025-04-03 17:07:38 +02:00
|
|
|
import { mkdir, mkdtemp, readFile, rm, unlink, writeFile } from 'node:fs/promises'
|
2025-03-31 15:26:01 +02:00
|
|
|
import { tmpdir } from 'node:os'
|
|
|
|
|
import path from 'path'
|
2024-03-05 14:23:26 +01:00
|
|
|
import postcss from 'postcss'
|
2025-03-31 15:26:01 +02:00
|
|
|
import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest'
|
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.
|
Improve performance of `@tailwindcss/postcss` and `@tailwindcss/vite` (#15226)
This PR improves the performance of the `@tailwindcss/postcss` and
`@tailwindcss/vite` implementations.
The issue is that in some scenarios, if you have multiple `.css` files,
then all of the CSS files are ran through the Tailwind CSS compiler. The
issue with this is that in a lot of cases, the CSS files aren't even
related to Tailwind CSS at all.
E.g.: in a Next.js project, if you use the `next/font/local` tool, then
every font you used will be in a separate CSS file. This means that we
run Tailwind CSS in all these files as well.
That said, running Tailwind CSS on these files isn't the end of the
world because we still need to handle `@import` in case `@tailwind
utilities` is being used. However, we also run the auto source detection
logic for every CSS file in the system. This part is bad.
To solve this, this PR introduces an internal `features` to collect what
CSS features are used throughout the system (`@import`, `@plugin`,
`@apply`, `@tailwind utilities`, etc…)
The `@tailwindcss/postcss` and `@tailwindcss/vite` plugin can use that
information to decide if they can take some shortcuts or not.
---
Overall, this means that we don't run the slow parts of Tailwind CSS if
we don't need to.
---------
Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2024-11-29 16:59:29 +01:00
|
|
|
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
|
|
|
|
Improve performance of `@tailwindcss/postcss` and `@tailwindcss/vite` (#15226)
This PR improves the performance of the `@tailwindcss/postcss` and
`@tailwindcss/vite` implementations.
The issue is that in some scenarios, if you have multiple `.css` files,
then all of the CSS files are ran through the Tailwind CSS compiler. The
issue with this is that in a lot of cases, the CSS files aren't even
related to Tailwind CSS at all.
E.g.: in a Next.js project, if you use the `next/font/local` tool, then
every font you used will be in a separate CSS file. This means that we
run Tailwind CSS in all these files as well.
That said, running Tailwind CSS on these files isn't the end of the
world because we still need to handle `@import` in case `@tailwind
utilities` is being used. However, we also run the auto source detection
logic for every CSS file in the system. This part is bad.
To solve this, this PR introduces an internal `features` to collect what
CSS features are used throughout the system (`@import`, `@plugin`,
`@apply`, `@tailwind utilities`, etc…)
The `@tailwindcss/postcss` and `@tailwindcss/vite` plugin can use that
information to decide if they can take some shortcuts or not.
---
Overall, this means that we don't run the slow parts of Tailwind CSS if
we don't need to.
---------
Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2024-11-29 16:59:29 +01:00
|
|
|
const css = dedent
|
2024-03-05 14:23:26 +01:00
|
|
|
|
|
|
|
|
test("`@import 'tailwindcss'` is replaced with the generated CSS", async () => {
|
2024-03-08 18:36:07 +01:00
|
|
|
let processor = postcss([
|
|
|
|
|
tailwindcss({ base: `${__dirname}/fixtures/example-project`, optimize: { minify: false } }),
|
|
|
|
|
])
|
2024-03-05 14:23:26 +01:00
|
|
|
|
Improve performance of `@tailwindcss/postcss` and `@tailwindcss/vite` (#15226)
This PR improves the performance of the `@tailwindcss/postcss` and
`@tailwindcss/vite` implementations.
The issue is that in some scenarios, if you have multiple `.css` files,
then all of the CSS files are ran through the Tailwind CSS compiler. The
issue with this is that in a lot of cases, the CSS files aren't even
related to Tailwind CSS at all.
E.g.: in a Next.js project, if you use the `next/font/local` tool, then
every font you used will be in a separate CSS file. This means that we
run Tailwind CSS in all these files as well.
That said, running Tailwind CSS on these files isn't the end of the
world because we still need to handle `@import` in case `@tailwind
utilities` is being used. However, we also run the auto source detection
logic for every CSS file in the system. This part is bad.
To solve this, this PR introduces an internal `features` to collect what
CSS features are used throughout the system (`@import`, `@plugin`,
`@apply`, `@tailwind utilities`, etc…)
The `@tailwindcss/postcss` and `@tailwindcss/vite` plugin can use that
information to decide if they can take some shortcuts or not.
---
Overall, this means that we don't run the slow parts of Tailwind CSS if
we don't need to.
---------
Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2024-11-29 16:59:29 +01:00
|
|
|
let result = await processor.process(`@import 'tailwindcss'`, { from: inputCssFilePath() })
|
2024-03-05 14:23:26 +01:00
|
|
|
|
|
|
|
|
expect(result.css.trim()).toMatchSnapshot()
|
|
|
|
|
|
|
|
|
|
// 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',
|
2024-07-29 16:50:06 +02:00
|
|
|
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 () => {
|
2024-03-08 18:36:07 +01:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
`,
|
Improve performance of `@tailwindcss/postcss` and `@tailwindcss/vite` (#15226)
This PR improves the performance of the `@tailwindcss/postcss` and
`@tailwindcss/vite` implementations.
The issue is that in some scenarios, if you have multiple `.css` files,
then all of the CSS files are ran through the Tailwind CSS compiler. The
issue with this is that in a lot of cases, the CSS files aren't even
related to Tailwind CSS at all.
E.g.: in a Next.js project, if you use the `next/font/local` tool, then
every font you used will be in a separate CSS file. This means that we
run Tailwind CSS in all these files as well.
That said, running Tailwind CSS on these files isn't the end of the
world because we still need to handle `@import` in case `@tailwind
utilities` is being used. However, we also run the auto source detection
logic for every CSS file in the system. This part is bad.
To solve this, this PR introduces an internal `features` to collect what
CSS features are used throughout the system (`@import`, `@plugin`,
`@apply`, `@tailwind utilities`, etc…)
The `@tailwindcss/postcss` and `@tailwindcss/vite` plugin can use that
information to decide if they can take some shortcuts or not.
---
Overall, this means that we don't run the slow parts of Tailwind CSS if
we don't need to.
---------
Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2024-11-29 16:59:29 +01:00
|
|
|
{ from: inputCssFilePath() },
|
2024-03-05 14:23:26 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
expect(result.css.trim()).toMatchInlineSnapshot(`
|
|
|
|
|
"@layer utilities {
|
|
|
|
|
.foo {
|
|
|
|
|
color: #000;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.bar {
|
|
|
|
|
color: red;
|
|
|
|
|
}
|
|
|
|
|
}"
|
|
|
|
|
`)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
test('@apply can be used without emitting the theme in the CSS file', async () => {
|
2024-03-08 18:36:07 +01:00
|
|
|
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`
|
2025-02-25 11:36:43 +01:00
|
|
|
@reference 'tailwindcss/theme.css';
|
2024-03-05 14:23:26 +01:00
|
|
|
.foo {
|
|
|
|
|
@apply text-red-500;
|
|
|
|
|
}
|
|
|
|
|
`,
|
Improve performance of `@tailwindcss/postcss` and `@tailwindcss/vite` (#15226)
This PR improves the performance of the `@tailwindcss/postcss` and
`@tailwindcss/vite` implementations.
The issue is that in some scenarios, if you have multiple `.css` files,
then all of the CSS files are ran through the Tailwind CSS compiler. The
issue with this is that in a lot of cases, the CSS files aren't even
related to Tailwind CSS at all.
E.g.: in a Next.js project, if you use the `next/font/local` tool, then
every font you used will be in a separate CSS file. This means that we
run Tailwind CSS in all these files as well.
That said, running Tailwind CSS on these files isn't the end of the
world because we still need to handle `@import` in case `@tailwind
utilities` is being used. However, we also run the auto source detection
logic for every CSS file in the system. This part is bad.
To solve this, this PR introduces an internal `features` to collect what
CSS features are used throughout the system (`@import`, `@plugin`,
`@apply`, `@tailwind utilities`, etc…)
The `@tailwindcss/postcss` and `@tailwindcss/vite` plugin can use that
information to decide if they can take some shortcuts or not.
---
Overall, this means that we don't run the slow parts of Tailwind CSS if
we don't need to.
---------
Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2024-11-29 16:59:29 +01:00
|
|
|
{ from: inputCssFilePath() },
|
2024-03-05 14:23:26 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
expect(result.css.trim()).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));
|
2024-03-05 14:23:26 +01:00
|
|
|
}"
|
|
|
|
|
`)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
describe('processing without specifying a base path', () => {
|
2025-03-31 15:26:01 +02:00
|
|
|
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 () => {
|
2025-03-31 15:26:01 +02:00
|
|
|
const spy = vi.spyOn(process, 'cwd')
|
|
|
|
|
spy.mockReturnValue(dir)
|
|
|
|
|
|
2024-03-08 18:36:07 +01:00
|
|
|
let processor = postcss([tailwindcss({ optimize: { minify: false } })])
|
2024-03-05 14:23:26 +01:00
|
|
|
|
Improve performance of `@tailwindcss/postcss` and `@tailwindcss/vite` (#15226)
This PR improves the performance of the `@tailwindcss/postcss` and
`@tailwindcss/vite` implementations.
The issue is that in some scenarios, if you have multiple `.css` files,
then all of the CSS files are ran through the Tailwind CSS compiler. The
issue with this is that in a lot of cases, the CSS files aren't even
related to Tailwind CSS at all.
E.g.: in a Next.js project, if you use the `next/font/local` tool, then
every font you used will be in a separate CSS file. This means that we
run Tailwind CSS in all these files as well.
That said, running Tailwind CSS on these files isn't the end of the
world because we still need to handle `@import` in case `@tailwind
utilities` is being used. However, we also run the auto source detection
logic for every CSS file in the system. This part is bad.
To solve this, this PR introduces an internal `features` to collect what
CSS features are used throughout the system (`@import`, `@plugin`,
`@apply`, `@tailwind utilities`, etc…)
The `@tailwindcss/postcss` and `@tailwindcss/vite` plugin can use that
information to decide if they can take some shortcuts or not.
---
Overall, this means that we don't run the slow parts of Tailwind CSS if
we don't need to.
---------
Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2024-11-29 16:59:29 +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),
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
})
|
2024-07-11 09:47:26 -04:00
|
|
|
|
|
|
|
|
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';
|
2024-08-07 16:38:44 +02:00
|
|
|
@plugin './plugin.js';
|
2024-07-11 09:47:26 -04:00
|
|
|
`,
|
Improve performance of `@tailwindcss/postcss` and `@tailwindcss/vite` (#15226)
This PR improves the performance of the `@tailwindcss/postcss` and
`@tailwindcss/vite` implementations.
The issue is that in some scenarios, if you have multiple `.css` files,
then all of the CSS files are ran through the Tailwind CSS compiler. The
issue with this is that in a lot of cases, the CSS files aren't even
related to Tailwind CSS at all.
E.g.: in a Next.js project, if you use the `next/font/local` tool, then
every font you used will be in a separate CSS file. This means that we
run Tailwind CSS in all these files as well.
That said, running Tailwind CSS on these files isn't the end of the
world because we still need to handle `@import` in case `@tailwind
utilities` is being used. However, we also run the auto source detection
logic for every CSS file in the system. This part is bad.
To solve this, this PR introduces an internal `features` to collect what
CSS features are used throughout the system (`@import`, `@plugin`,
`@apply`, `@tailwind utilities`, etc…)
The `@tailwindcss/postcss` and `@tailwindcss/vite` plugin can use that
information to decide if they can take some shortcuts or not.
---
Overall, this means that we don't run the slow parts of Tailwind CSS if
we don't need to.
---------
Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2024-11-29 16:59:29 +01:00
|
|
|
{ from: inputCssFilePath() },
|
2024-07-11 09:47:26 -04:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
expect(result.css.trim()).toMatchInlineSnapshot(`
|
|
|
|
|
".underline {
|
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;
|
|
|
|
|
}"
|
|
|
|
|
`)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
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` },
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
expect(result.css.trim()).toMatchInlineSnapshot(`
|
|
|
|
|
".underline {
|
2024-07-11 09:47:26 -04:00
|
|
|
text-decoration-line: underline;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@media (inverted-colors: inverted) {
|
|
|
|
|
.inverted\\:flex {
|
|
|
|
|
display: flex;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.hocus\\:underline:focus, .hocus\\:underline:hover {
|
|
|
|
|
text-decoration-line: underline;
|
|
|
|
|
}"
|
|
|
|
|
`)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
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';
|
2024-07-29 16:58:07 +02:00
|
|
|
@plugin 'internal-example-plugin';
|
2024-07-11 09:47:26 -04:00
|
|
|
`,
|
Improve performance of `@tailwindcss/postcss` and `@tailwindcss/vite` (#15226)
This PR improves the performance of the `@tailwindcss/postcss` and
`@tailwindcss/vite` implementations.
The issue is that in some scenarios, if you have multiple `.css` files,
then all of the CSS files are ran through the Tailwind CSS compiler. The
issue with this is that in a lot of cases, the CSS files aren't even
related to Tailwind CSS at all.
E.g.: in a Next.js project, if you use the `next/font/local` tool, then
every font you used will be in a separate CSS file. This means that we
run Tailwind CSS in all these files as well.
That said, running Tailwind CSS on these files isn't the end of the
world because we still need to handle `@import` in case `@tailwind
utilities` is being used. However, we also run the auto source detection
logic for every CSS file in the system. This part is bad.
To solve this, this PR introduces an internal `features` to collect what
CSS features are used throughout the system (`@import`, `@plugin`,
`@apply`, `@tailwind utilities`, etc…)
The `@tailwindcss/postcss` and `@tailwindcss/vite` plugin can use that
information to decide if they can take some shortcuts or not.
---
Overall, this means that we don't run the slow parts of Tailwind CSS if
we don't need to.
---------
Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2024-11-29 16:59:29 +01:00
|
|
|
{ from: inputCssFilePath() },
|
2024-07-11 09:47:26 -04:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
expect(result.css.trim()).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 performance of `@tailwindcss/postcss` and `@tailwindcss/vite` (#15226)
This PR improves the performance of the `@tailwindcss/postcss` and
`@tailwindcss/vite` implementations.
The issue is that in some scenarios, if you have multiple `.css` files,
then all of the CSS files are ran through the Tailwind CSS compiler. The
issue with this is that in a lot of cases, the CSS files aren't even
related to Tailwind CSS at all.
E.g.: in a Next.js project, if you use the `next/font/local` tool, then
every font you used will be in a separate CSS file. This means that we
run Tailwind CSS in all these files as well.
That said, running Tailwind CSS on these files isn't the end of the
world because we still need to handle `@import` in case `@tailwind
utilities` is being used. However, we also run the auto source detection
logic for every CSS file in the system. This part is bad.
To solve this, this PR introduces an internal `features` to collect what
CSS features are used throughout the system (`@import`, `@plugin`,
`@apply`, `@tailwind utilities`, etc…)
The `@tailwindcss/postcss` and `@tailwindcss/vite` plugin can use that
information to decide if they can take some shortcuts or not.
---
Overall, this means that we don't run the slow parts of Tailwind CSS if
we don't need to.
---------
Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2024-11-29 16:59:29 +01: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 {')
|
|
|
|
|
|
|
|
|
|
expect(result.css.trim()).toMatchInlineSnapshot(`
|
|
|
|
|
".custom-css {
|
|
|
|
|
color: red;
|
|
|
|
|
}"
|
|
|
|
|
`)
|
|
|
|
|
})
|
2024-12-03 10:28:51 +01:00
|
|
|
|
2025-01-30 16:29:08 +01: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() },
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
expect(result.css.trim()).toMatchInlineSnapshot(`
|
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
|
|
|
"@media (min-width: 48rem) {
|
2025-01-30 16:29:08 +01:00
|
|
|
.foo {
|
|
|
|
|
bar: baz;
|
|
|
|
|
}
|
|
|
|
|
}"
|
|
|
|
|
`)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
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() },
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
expect(result.css.trim()).toMatchInlineSnapshot(`
|
|
|
|
|
".foo[data-is-hoverable] {
|
|
|
|
|
bar: baz;
|
|
|
|
|
}"
|
|
|
|
|
`)
|
|
|
|
|
})
|
|
|
|
|
|
2024-12-03 10:28:51 +01: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() },
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
expect(result.css.trim()).toMatchInlineSnapshot(`
|
Re-enable: Only expose used CSS variables (#16676)
This PR re-enables the changes necessary to remove unused theme
variables and keyframes form your CSS.
This change was initially landed as #16211 and then later reverted in
#16403 because we found some unexpected interactions with using `@apply`
and CSS variables in multi-root setups like CSS modules or Vue inline
`<style>` blocks that were no longer seeing their required variables
defined.
This issue is fixed by now ensuring that theme variables that are
defined within an `@reference "…"` boundary will still be emitted in the
generated CSS when used (as this would otherwise not generate a valid
stylesheet).
So given the following input CSS:
```css
@reference "tailwindcss";
.text-red {
@apply text-red-500;
}
```
We will now compile this to:
```css
@layer theme {
:root, :host {
--text-red-500: oklch(0.637 0.237 25.331);
}
}
.text-red {
color: var(--text-red-500);
}
```
This PR also improves the initial implementation to not mark theme
variables as used if they are only used to define other theme variables.
For example:
```css
@theme {
--font-sans:
ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol',
'Noto Color Emoji';
--font-mono:
ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New',
monospace;
--default-font-family: var(--font-sans);
--default-mono-font-family: var(--font-mono);
}
.default-font-family {
font-family: var(--default-font-family);
}
```
This would be reduced to the following now as `--font-mono` is only used
to define another variable and never used outside the theme block:
```css
:root, :host {
--font-sans:
ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol',
'Noto Color Emoji';
--default-font-family: var(--font-sans);
}
.default-font-family {
font-family: var(--default-font-family);
}
```
## Test plan
- See updated unit and integration tests
- Validated it works end-to-end by using a SvelteKit example
2025-02-21 10:45:22 +01:00
|
|
|
".custom-css {
|
2024-12-03 10:28:51 +01:00
|
|
|
color: red;
|
|
|
|
|
}"
|
|
|
|
|
`)
|
|
|
|
|
expect(before).toMatchInlineSnapshot(`
|
|
|
|
|
"@theme {
|
|
|
|
|
--color-red-500: red;
|
|
|
|
|
}
|
|
|
|
|
.custom-css {
|
|
|
|
|
color: theme(--color-red-500);
|
|
|
|
|
}"
|
|
|
|
|
`)
|
|
|
|
|
expect(after).toMatchInlineSnapshot(`
|
Re-enable: Only expose used CSS variables (#16676)
This PR re-enables the changes necessary to remove unused theme
variables and keyframes form your CSS.
This change was initially landed as #16211 and then later reverted in
#16403 because we found some unexpected interactions with using `@apply`
and CSS variables in multi-root setups like CSS modules or Vue inline
`<style>` blocks that were no longer seeing their required variables
defined.
This issue is fixed by now ensuring that theme variables that are
defined within an `@reference "…"` boundary will still be emitted in the
generated CSS when used (as this would otherwise not generate a valid
stylesheet).
So given the following input CSS:
```css
@reference "tailwindcss";
.text-red {
@apply text-red-500;
}
```
We will now compile this to:
```css
@layer theme {
:root, :host {
--text-red-500: oklch(0.637 0.237 25.331);
}
}
.text-red {
color: var(--text-red-500);
}
```
This PR also improves the initial implementation to not mark theme
variables as used if they are only used to define other theme variables.
For example:
```css
@theme {
--font-sans:
ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol',
'Noto Color Emoji';
--font-mono:
ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New',
monospace;
--default-font-family: var(--font-sans);
--default-mono-font-family: var(--font-mono);
}
.default-font-family {
font-family: var(--default-font-family);
}
```
This would be reduced to the following now as `--font-mono` is only used
to define another variable and never used outside the theme block:
```css
:root, :host {
--font-sans:
ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol',
'Noto Color Emoji';
--default-font-family: var(--font-sans);
}
.default-font-family {
font-family: var(--default-font-family);
}
```
## Test plan
- See updated unit and integration tests
- Validated it works end-to-end by using a SvelteKit example
2025-02-21 10:45:22 +01:00
|
|
|
".custom-css {
|
2024-12-03 10:28:51 +01:00
|
|
|
color: red;
|
|
|
|
|
}"
|
|
|
|
|
`)
|
|
|
|
|
})
|
2025-04-03 17:07:38 +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 }))
|
|
|
|
|
|
2025-04-03 17:24:26 +02:00
|
|
|
test('does experience a race-condition when calling the plugin two times for the same change', async () => {
|
2025-04-03 17:07:38 +02:00
|
|
|
const 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, { result: { opts: { from }, messages: [] } })
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return ast.toString()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let result = await run(input)
|
|
|
|
|
|
|
|
|
|
expect(result).toContain('.underline')
|
|
|
|
|
|
2025-04-03 17:24:26 +02:00
|
|
|
// Ensure that the mtime is updated
|
|
|
|
|
await new Promise((resolve) => setTimeout(resolve, 100))
|
2025-04-03 17:07:38 +02:00
|
|
|
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')
|
|
|
|
|
})
|
|
|
|
|
})
|
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' })
|
|
|
|
|
|
|
|
|
|
expect(result.css.trim()).toMatchInlineSnapshot(`
|
|
|
|
|
".underline {
|
|
|
|
|
text-decoration-line: underline;
|
|
|
|
|
}"
|
|
|
|
|
`)
|
|
|
|
|
|
|
|
|
|
// 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),
|
|
|
|
|
})
|
|
|
|
|
})
|