Vite: Don't register the current CSS file as a dependency on itself (#17533)

Closes #17512

One of the changes of the Oxide API in 4.1 is that it now emits the
input CSS file itself as a dependency. This was fine in most of our
testing but it turns out that certain integrations (in this case a Qwik
project) don't like this and will silently crash with no CSS file being
added anymore.

This PR fixes this by making sure we don't add the input file as a
dependency on itself and also adds an integration test to ensure this
won't regress again.

## Test plan

- Tested with the repro provided in #17512
- Added a minimal integration test based on that reproduction that I
also validated will _fail_, if the fix is reverted.
This commit is contained in:
Philipp Spiess
2025-04-03 18:50:16 +02:00
committed by GitHub
parent 932f9a8a00
commit 8aee79d714
3 changed files with 109 additions and 1 deletions
+1
View File
@@ -20,6 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- PostCSS: Ensure files containing `@tailwind utilities` are processed ([#17514](https://github.com/tailwindlabs/tailwindcss/pull/17514))
- Ensure the `color-mix(…)` polyfill creates fallbacks even when using colors that cannot be statically analyzed ([#17513](https://github.com/tailwindlabs/tailwindcss/pull/17513))
- Fix slow incremental builds with `@tailwindcss/vite` and `@tailwindcss/postscss` (especially on Windows) ([#17511](https://github.com/tailwindlabs/tailwindcss/pull/17511))
- Vite: Fix an issue with Qwik setups ([#17533](https://github.com/tailwindlabs/tailwindcss/pull/17533))
## [4.1.1] - 2025-04-02
+100
View File
@@ -0,0 +1,100 @@
import { candidate, css, fetchStyles, json, retryAssertion, test, ts } from '../utils'
test(
'dev mode',
{
fs: {
'package.json': json`
{
"type": "module",
"dependencies": {
"@builder.io/qwik": "^1",
"@builder.io/qwik-city": "^1",
"vite": "^5",
"@tailwindcss/vite": "workspace:^",
"tailwindcss": "workspace:^"
}
}
`,
'vite.config.ts': ts`
import { defineConfig } from 'vite'
import { qwikVite } from '@builder.io/qwik/optimizer'
import { qwikCity } from '@builder.io/qwik-city/vite'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig(() => {
return {
plugins: [tailwindcss(), qwikCity(), qwikVite()],
}
})
`,
'src/root.tsx': ts`
import { component$ } from '@builder.io/qwik'
import { QwikCityProvider, RouterOutlet } from '@builder.io/qwik-city'
import './global.css'
export default component$(() => {
return (
<QwikCityProvider>
<head></head>
<body>
<RouterOutlet />
</body>
</QwikCityProvider>
)
})
`,
'src/global.css': css`@import 'tailwindcss/utilities.css';`,
'src/entry.ssr.tsx': ts`
import { renderToStream, type RenderToStreamOptions } from '@builder.io/qwik/server'
import Root from './root'
export default function (opts: RenderToStreamOptions) {
return renderToStream(<Root />, opts)
}
`,
'src/routes/index.tsx': ts`
import { component$ } from '@builder.io/qwik'
export default component$(() => {
return <h1 class="underline">Hello World!</h1>
})
`,
},
},
async ({ fs, spawn, expect }) => {
let process = await spawn('pnpm vite --mode ssr')
await process.onStdout((m) => m.includes('ready in'))
let url = ''
await process.onStdout((m) => {
console.log(m)
let match = /Local:\s*(http.*)\//.exec(m)
if (match) url = match[1]
return Boolean(url)
})
await retryAssertion(async () => {
let css = await fetchStyles(url)
expect(css).toContain(candidate`underline`)
})
await retryAssertion(async () => {
await fs.write(
'src/routes/index.tsx',
ts`
import { component$ } from '@builder.io/qwik'
export default component$(() => {
return <h1 class="underline flex">Hello World!</h1>
})
`,
)
let css = await fetchStyles(url)
expect(css).toContain(candidate`underline`)
expect(css).toContain(candidate`flex`)
})
},
)
+8 -1
View File
@@ -184,7 +184,15 @@ class Root {
_addWatchFile: (file: string) => void,
I: Instrumentation,
): Promise<string | false> {
let inputPath = idToPath(this.id)
function addWatchFile(file: string) {
// Don't watch the input file since it's already a dependency anc causes
// issues with some setups (e.g. Qwik).
if (file === inputPath) {
return
}
// Scanning `.svg` file containing a `#` or `?` in the path will
// crash Vite. We work around this for now by ignoring updates to them.
//
@@ -196,7 +204,6 @@ class Root {
}
let requiresBuildPromise = this.requiresBuild()
let inputPath = idToPath(this.id)
let inputBase = path.dirname(path.resolve(inputPath))
if (!this.compiler || !this.scanner || (await requiresBuildPromise)) {