Vite: Emit build dependencies on partial rebuilds (#17347)

Closes #17339

This PR fixes an issue that caused changes to `@import`-ed CSS files to
no longer rebuild the stylesheet after a change was made to a template
file.

The change in the template file causes a fast-path in the Vite plugin
now after changes in 4.0.8: _partial rebuilds_. For that branch we do
not need to re-evaluate your input CSS since we know only the candidate
list changed. However, we still need to emit all build dependencies as
via `addWatchFile(…)`, otherwise Vite will not correctly register
updates for these dependencies anymore.

## Test plan

- Updated the kitchen-sink Vite update tests to ensure that an
`@import`-ed CSS file can be updated even after a partial rebuild.
- Ensure this works in our Vite playground
This commit is contained in:
Philipp Spiess 2025-03-24 11:54:55 +01:00 committed by GitHub
parent 42f68bb359
commit fac8f7df62
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 53 additions and 0 deletions

View File

@ -24,6 +24,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Fixed
- Vite: Ensure that updates to an imported CSS file are properly propagated after updating templates ([#17347](https://github.com/tailwindlabs/tailwindcss/pull/17347))
- Fix class extraction followed by `(` in Pug ([#17320](https://github.com/tailwindlabs/tailwindcss/pull/17320))
### [4.0.15] - 2025-03-20

View File

@ -149,9 +149,15 @@ describe.each(['postcss', 'lightningcss'])('%s', (transformer) => {
'project-a/src/index.css': css`
@reference 'tailwindcss/theme';
@import 'tailwindcss/utilities';
@import './imported.css';
@config '../tailwind.config.js';
@source '../../project-b/src/**/*.html';
`,
'project-a/src/imported.css': css`
.imported {
color: red;
}
`,
'project-b/src/index.html': html`
<div class="flex" />
`,
@ -179,6 +185,7 @@ describe.each(['postcss', 'lightningcss'])('%s', (transformer) => {
expect(styles).toContain(candidate`underline`)
expect(styles).toContain(candidate`flex`)
expect(styles).toContain(candidate`font-bold`)
expect(styles).toContain(candidate`imported`)
})
await retryAssertion(async () => {
@ -199,6 +206,7 @@ describe.each(['postcss', 'lightningcss'])('%s', (transformer) => {
expect(styles).toContain(candidate`underline`)
expect(styles).toContain(candidate`flex`)
expect(styles).toContain(candidate`font-bold`)
expect(styles).toContain(candidate`imported`)
expect(styles).toContain(candidate`m-2`)
})
@ -216,6 +224,7 @@ describe.each(['postcss', 'lightningcss'])('%s', (transformer) => {
expect(styles).toContain(candidate`underline`)
expect(styles).toContain(candidate`flex`)
expect(styles).toContain(candidate`font-bold`)
expect(styles).toContain(candidate`imported`)
expect(styles).toContain(candidate`m-2`)
expect(styles).toContain(candidate`[.changed_&]:content-['project-b/src/index.js']`)
})
@ -237,11 +246,50 @@ describe.each(['postcss', 'lightningcss'])('%s', (transformer) => {
let styles = await fetchStyles(url)
expect(styles).toContain(candidate`red`)
expect(styles).toContain(candidate`flex`)
expect(styles).toContain(candidate`imported`)
expect(styles).toContain(candidate`m-2`)
expect(styles).toContain(candidate`underline`)
expect(styles).toContain(candidate`[.changed_&]:content-['project-b/src/index.js']`)
expect(styles).toContain(candidate`font-bold`)
})
await retryAssertion(async () => {
// Trigger a partial rebuild for the next test
await fs.write(
'project-a/index.html',
html`
<head>
<link rel="stylesheet" href="./src/index.css" />
</head>
<body>
<div class="m-4">Hello, world!</div>
</body>
`,
)
let styles = await fetchStyles(url)
expect(styles).toContain(candidate`m-4`)
})
await retryAssertion(async () => {
// Changing an `@imported` CSS file after a partial rebuild also triggers the correct update
await fs.write(
'project-a/src/imported.css',
css`
.imported-updated {
color: red;
}
`,
)
let styles = await fetchStyles(url)
expect(styles).toContain(candidate`red`)
expect(styles).toContain(candidate`flex`)
expect(styles).toContain(candidate`m-2`)
expect(styles).toContain(candidate`underline`)
expect(styles).toContain(candidate`[.changed_&]:content-['project-b/src/index.js']`)
expect(styles).toContain(candidate`font-bold`)
expect(styles).toContain(candidate`imported-updated`)
})
},
)

View File

@ -277,6 +277,10 @@ class Root {
this.scanner = new Scanner({ sources })
DEBUG && I.end('Setup scanner')
} else {
for (let buildDependency of this.buildDependencies.keys()) {
addWatchFile(buildDependency)
}
}
if (