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-03-31 15:26:01 +02:00
|
|
|
import { mkdir, mkdtemp, 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'
|
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 {
|
2025-02-25 11:36:43 +01:00
|
|
|
color: var(--color-red-500, oklch(.637 .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(`
|
|
|
|
|
"@media (width >= 48rem) {
|
|
|
|
|
.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;
|
|
|
|
|
}"
|
|
|
|
|
`)
|
|
|
|
|
})
|