Fix @apply and CSS functions inside imported files (#14576)

Part-of #14558

After handling `@import` of stylesheets in core, we can no longer gate
out features based on finding a specific sequence in the input CSS, as
this string will not contain contents from other stylesheets.

So, consider the following input CSS:

```css
@import "tailwindcsss";
@import "./styles.css";
```

We can't opt-out of the traversal to search for `@apply` based on it
because it, of course, does not contain the contents of `./styles.css`
yet.

---------

Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
This commit is contained in:
Philipp Spiess 2024-10-03 15:23:03 +02:00 • committed by GitHub
parent e4308da604
commit aa343a9445
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 69 additions and 10 deletions

View file

@ -24,7 +24,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Use the right import base path when using the CLI to reading files from stdin ([#14522](https://github.com/tailwindlabs/tailwindcss/pull/14522))
- Ensure that `@utility` is top-level and cannot be nested ([#14525](https://github.com/tailwindlabs/tailwindcss/pull/14525))
- Editing imported CSS files should trigger a rebuild ([#14561](https://github.com/tailwindlabs/tailwindcss/pull/14561))
- Ensure editing imported CSS files triggers a rebuild ([#14561](https://github.com/tailwindlabs/tailwindcss/pull/14561))
- Ensure `@apply` and CSS functions work inside imported stylesheets ([#14576](https://github.com/tailwindlabs/tailwindcss/pull/14576))
- _Experimental_: Improve codemod output, keep CSS after last Tailwind directive unlayered ([#14512](https://github.com/tailwindlabs/tailwindcss/pull/14512))
- _Experimental_: Fix incorrect empty `layer()` at the end of `@import` at-rules when running codemods ([#14513](https://github.com/tailwindlabs/tailwindcss/pull/14513))
- _Experimental_: Do not wrap comment nodes in `@layer` when running codemods ([#14517](https://github.com/tailwindlabs/tailwindcss/pull/14517))

View file

@ -901,3 +901,37 @@ describe('in JS config files', () => {
`)
})
})
test('replaces CSS theme() function with values inside imported stylesheets', async () => {
expect(
await compileCss(
css`
@theme {
--color-red-500: #f00;
}
@import './bar.css';
`,
[],
{
async loadStylesheet() {
return {
base: '/bar.css',
content: css`
.red {
color: theme(colors.red.500);
}
`,
}
},
},
),
).toMatchInlineSnapshot(`
":root {
--color-red-500: red;
}
.red {
color: red;
}"
`)
})

View file

@ -254,6 +254,34 @@ describe('@apply', () => {
`)
})
it('should replace @apply with the correct result inside imported stylesheets', async () => {
expect(
await compileCss(
css`
@import './bar.css';
@tailwind utilities;
`,
[],
{
async loadStylesheet() {
return {
base: '/bar.css',
content: css`
.foo {
@apply underline;
}
`,
}
},
},
),
).toMatchInlineSnapshot(`
".foo {
text-decoration-line: underline;
}"
`)
})
it('should @apply in order the utilities would be sorted in if they were used in HTML', async () => {
expect(
await compileCss(css`

View file

@ -16,7 +16,7 @@ import { applyCompatibilityHooks } from './compat/apply-compat-hooks'
import type { UserConfig } from './compat/config/types'
import { type Plugin } from './compat/plugin-api'
import { compileCandidates } from './compile'
import { substituteFunctions, THEME_FUNCTION_INVOCATION } from './css-functions'
import { substituteFunctions } from './css-functions'
import * as CSS from './css-parser'
import { buildDesignSystem, type DesignSystem } from './design-system'
import { Theme, ThemeOptions } from './theme'
@ -341,13 +341,9 @@ async function parseCss(
}
// Replace `@apply` rules with the actual utility classes.
if (css.includes('@apply')) {
substituteAtApply(ast, designSystem)
}
substituteAtApply(ast, designSystem)
if (css.includes(THEME_FUNCTION_INVOCATION)) {
substituteFunctions(ast, designSystem.resolveThemeValue)
}
substituteFunctions(ast, designSystem.resolveThemeValue)
// Remove `@utility`, we couldn't replace it before yet because we had to
// handle the nested `@apply` at-rules first.

View file

@ -1,8 +1,8 @@
import { Features, transform } from 'lightningcss'
import { compile } from '..'
export async function compileCss(css: string, candidates: string[] = []) {
let { build } = await compile(css)
export async function compileCss(css: string, candidates: string[] = [], options = {}) {
let { build } = await compile(css, options)
return optimizeCss(build(candidates)).trim()
}