Fix Nuxt integration (#14319)
We noticed that Nuxt projects were not working with the tailwindcss project. The issue was traced down to the fact that Nuxt starts multiple Vite dev servers and calling the experimental `waitForRequestsIdle()` on one of the test servers would never resolve. This was fixed upstream and is part of the latest Vite/Nuxt release: https://github.com/vitejs/vite/issues/17980. We still need to handle the fact that Vite can spawn multiple dev servers. This is necessary because when we invalidate all roots, we need to find that module inside all of the spawned servers. If we only look at the _last server_ (what we have done before), we would not find the module and thus could not invalidate it.
This commit is contained in:
parent
e7ca667f76
commit
719a5351c1
3 changed files with 96 additions and 32 deletions
|
|
@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
|
||||
- Ensure content globs defined in `@config` files are relative to that file ([#14314](https://github.com/tailwindlabs/tailwindcss/pull/14314))
|
||||
- Ensure CSS `theme()` functions are evaluated in media query ranges with collapsed whitespace ((#14321)[https://github.com/tailwindlabs/tailwindcss/pull/14321])
|
||||
- Fix support for Nuxt projects in the Vite plugin (requires Nuxt 3.13.1+) ([#14319](https://github.com/tailwindlabs/tailwindcss/pull/14319))
|
||||
|
||||
## [4.0.0-alpha.21] - 2024-09-02
|
||||
|
||||
|
|
|
|||
64
integrations/vite/nuxt.test.ts
Normal file
64
integrations/vite/nuxt.test.ts
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
import { expect } from 'vitest'
|
||||
import { candidate, css, fetchStyles, html, json, retryAssertion, test, ts } from '../utils'
|
||||
|
||||
test(
|
||||
'dev mode',
|
||||
{
|
||||
fs: {
|
||||
'package.json': json`
|
||||
{
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"@tailwindcss/vite": "workspace:^",
|
||||
"nuxt": "^3.13.1",
|
||||
"tailwindcss": "workspace:^",
|
||||
"vue": "latest"
|
||||
}
|
||||
}
|
||||
`,
|
||||
'nuxt.config.ts': ts`
|
||||
import tailwindcss from '@tailwindcss/vite'
|
||||
|
||||
// https://nuxt.com/docs/api/configuration/nuxt-config
|
||||
export default defineNuxtConfig({
|
||||
vite: {
|
||||
plugins: [tailwindcss()],
|
||||
},
|
||||
|
||||
css: ['~/assets/css/main.css'],
|
||||
devtools: { enabled: true },
|
||||
compatibilityDate: '2024-08-30',
|
||||
})
|
||||
`,
|
||||
'app.vue': html`
|
||||
<template>
|
||||
<div class="underline">Hello world!</div>
|
||||
</template>
|
||||
`,
|
||||
'assets/css/main.css': css`@import 'tailwindcss';`,
|
||||
},
|
||||
},
|
||||
async ({ fs, spawn, getFreePort }) => {
|
||||
let port = await getFreePort()
|
||||
await spawn(`pnpm nuxt dev --port ${port}`)
|
||||
|
||||
await retryAssertion(async () => {
|
||||
let css = await fetchStyles(port)
|
||||
expect(css).toContain(candidate`underline`)
|
||||
})
|
||||
|
||||
await fs.write(
|
||||
'app.vue',
|
||||
html`
|
||||
<template>
|
||||
<div class="underline font-bold">Hello world!</div>
|
||||
</template>
|
||||
`,
|
||||
)
|
||||
await retryAssertion(async () => {
|
||||
let css = await fetchStyles(port)
|
||||
expect(css).toContain(candidate`underline`)
|
||||
expect(css).toContain(candidate`font-bold`)
|
||||
})
|
||||
},
|
||||
)
|
||||
|
|
@ -11,7 +11,7 @@ import postcssImport from 'postcss-import'
|
|||
import type { Plugin, ResolvedConfig, Rollup, Update, ViteDevServer } from 'vite'
|
||||
|
||||
export default function tailwindcss(): Plugin[] {
|
||||
let server: ViteDevServer | null = null
|
||||
let servers: ViteDevServer[] = []
|
||||
let config: ResolvedConfig | null = null
|
||||
|
||||
let isSSR = false
|
||||
|
|
@ -58,36 +58,35 @@ export default function tailwindcss(): Plugin[] {
|
|||
}
|
||||
|
||||
function invalidateAllRoots(isSSR: boolean) {
|
||||
// If we're building then we don't need to update anything
|
||||
if (!server) return
|
||||
|
||||
let updates: Update[] = []
|
||||
for (let id of roots.keys()) {
|
||||
let module = server.moduleGraph.getModuleById(id)
|
||||
if (!module) {
|
||||
// Note: Removing this during SSR is not safe and will produce
|
||||
// inconsistent results based on the timing of the removal and
|
||||
// the order / timing of transforms.
|
||||
if (!isSSR) {
|
||||
// It is safe to remove the item here since we're iterating on a copy
|
||||
// of the keys.
|
||||
roots.delete(id)
|
||||
for (let server of servers) {
|
||||
let updates: Update[] = []
|
||||
for (let id of roots.keys()) {
|
||||
let module = server.moduleGraph.getModuleById(id)
|
||||
if (!module) {
|
||||
// Note: Removing this during SSR is not safe and will produce
|
||||
// inconsistent results based on the timing of the removal and
|
||||
// the order / timing of transforms.
|
||||
if (!isSSR) {
|
||||
// It is safe to remove the item here since we're iterating on a copy
|
||||
// of the keys.
|
||||
roots.delete(id)
|
||||
}
|
||||
continue
|
||||
}
|
||||
continue
|
||||
|
||||
roots.get(id).requiresRebuild = false
|
||||
server.moduleGraph.invalidateModule(module)
|
||||
updates.push({
|
||||
type: `${module.type}-update`,
|
||||
path: module.url,
|
||||
acceptedPath: module.url,
|
||||
timestamp: Date.now(),
|
||||
})
|
||||
}
|
||||
|
||||
roots.get(id).requiresRebuild = false
|
||||
server.moduleGraph.invalidateModule(module)
|
||||
updates.push({
|
||||
type: `${module.type}-update`,
|
||||
path: module.url,
|
||||
acceptedPath: module.url,
|
||||
timestamp: Date.now(),
|
||||
})
|
||||
}
|
||||
|
||||
if (updates.length > 0) {
|
||||
server.hot.send({ type: 'update', updates })
|
||||
if (updates.length > 0) {
|
||||
server.hot.send({ type: 'update', updates })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -139,8 +138,8 @@ export default function tailwindcss(): Plugin[] {
|
|||
name: '@tailwindcss/vite:scan',
|
||||
enforce: 'pre',
|
||||
|
||||
configureServer(_server) {
|
||||
server = _server
|
||||
configureServer(server) {
|
||||
servers.push(server)
|
||||
},
|
||||
|
||||
async configResolved(_config) {
|
||||
|
|
@ -169,7 +168,7 @@ export default function tailwindcss(): Plugin[] {
|
|||
},
|
||||
transform(src, id, options) {
|
||||
let extension = getExtension(id)
|
||||
if (extension === '' || extension === 'css') return
|
||||
if (isPotentialCssRootFile(id)) return
|
||||
scanFile(id, src, extension, options?.ssr ?? false)
|
||||
},
|
||||
},
|
||||
|
|
@ -193,7 +192,7 @@ export default function tailwindcss(): Plugin[] {
|
|||
// The reason why we can not rely on the invalidation here is that the
|
||||
// users would otherwise see a flicker in the styles as the CSS might
|
||||
// be loaded with an invalid set of candidates first.
|
||||
await server?.waitForRequestsIdle?.(id)
|
||||
await Promise.all(servers.map((server) => server.waitForRequestsIdle(id)))
|
||||
}
|
||||
|
||||
let generated = await root.generate(src, (file) => this.addWatchFile(file))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue