From 4187908cc764bc25bb4868c01855680732626df9 Mon Sep 17 00:00:00 2001 From: irontaek <13810291+irontaek@users.noreply.github.com> Date: Sun, 27 Sep 2026 19:47:37 +0900 Subject: [PATCH 1/2] Fix Vite input dependency rebuild race --- .../vite/input-dependency-race.test.ts | 83 +++++++++++++++++++ packages/@tailwindcss-vite/src/index.ts | 2 +- 2 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 integrations/vite/input-dependency-race.test.ts diff --git a/integrations/vite/input-dependency-race.test.ts b/integrations/vite/input-dependency-race.test.ts new file mode 100644 index 000000000000..f3aa529b04f8 --- /dev/null +++ b/integrations/vite/input-dependency-race.test.ts @@ -0,0 +1,83 @@ +import { candidate, css, fetchStyles, html, json, retryAssertion, test, ts } from '../utils' + +test( + 'rebuilds when the input stylesheet changes before its dependency stat finishes', + { + fs: { + 'package.json': json` + { + "type": "module", + "dependencies": { + "@tailwindcss/vite": "workspace:^", + "tailwindcss": "workspace:^", + "vite": "^7" + } + } + `, + 'vite.config.ts': ts` + import fs from 'node:fs/promises' + import tailwindcss from '@tailwindcss/vite' + import { defineConfig } from 'vite' + + let stat = fs.stat + let delayed = false + fs.stat = async (...args) => { + if ( + !delayed && + String(args[0]).endsWith('index.css') && + new Error().stack?.includes('addBuildDependency') + ) { + delayed = true + await fs.writeFile('input-stat-delayed', 'yes') + await new Promise((resolve) => setTimeout(resolve, 1500)) + } + return stat(...args) + } + + export default defineConfig({ plugins: [tailwindcss()] }) + `, + 'index.html': html` + +
+ `, + 'src/index.css': css` + @import 'tailwindcss'; + @theme { + --color-brand: red; + } + `, + }, + }, + async ({ fs, spawn, expect }) => { + let process = await spawn('pnpm vite dev') + await process.onStdout((m) => m.includes('ready in')) + + let url = '' + await process.onStdout((m) => { + let match = /Local:\s*(http.*)\//.exec(m) + if (match) url = match[1] + return Boolean(url) + }) + + await retryAssertion(async () => { + expect(await fetchStyles(url)).toContain('--color-brand: red') + }) + expect(await fs.read('input-stat-delayed')).toBe('yes') + + await fs.write( + 'src/index.css', + css` + @import 'tailwindcss'; + @theme { + --color-brand: blue; + } + `, + ) + + await retryAssertion(async () => { + let styles = await fetchStyles(url) + expect(styles).toContain(candidate`text-brand`) + expect(styles).toContain('--color-brand: blue') + }) + }, +) diff --git a/packages/@tailwindcss-vite/src/index.ts b/packages/@tailwindcss-vite/src/index.ts index 8fc660eaac18..47c5c9d33f9a 100644 --- a/packages/@tailwindcss-vite/src/index.ts +++ b/packages/@tailwindcss-vite/src/index.ts @@ -395,7 +395,7 @@ class Root { clearRequireCache(Array.from(this.buildDependencies.keys())) this.buildDependencies.clear() - this.addBuildDependency(idToPath(inputPath)) + await this.addBuildDependency(idToPath(inputPath)) DEBUG && I.start('Setup compiler') let addBuildDependenciesPromises: Promise