From fac8f7df6246dd1d5c2f28798c2571d38925fca2 Mon Sep 17 00:00:00 2001 From: Philipp Spiess Date: Mon, 24 Mar 2025 11:54:55 +0100 Subject: [PATCH] Vite: Emit build dependencies on partial rebuilds (#17347) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- CHANGELOG.md | 1 + integrations/vite/index.test.ts | 48 +++++++++++++++++++++++++ packages/@tailwindcss-vite/src/index.ts | 4 +++ 3 files changed, 53 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a8017a753..a275a0134 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/integrations/vite/index.test.ts b/integrations/vite/index.test.ts index 59b304cf4..4c64f220e 100644 --- a/integrations/vite/index.test.ts +++ b/integrations/vite/index.test.ts @@ -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`
`, @@ -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` + + + + +
Hello, world!
+ + `, + ) + 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`) + }) }, ) diff --git a/packages/@tailwindcss-vite/src/index.ts b/packages/@tailwindcss-vite/src/index.ts index ec42bba0a..1d27ee168 100644 --- a/packages/@tailwindcss-vite/src/index.ts +++ b/packages/@tailwindcss-vite/src/index.ts @@ -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 (