Skip to content

Commit

Permalink
Fix Nuxt integration (#14319)
Browse files Browse the repository at this point in the history
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:
vitejs/vite#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.
  • Loading branch information
philipp-spiess authored Sep 4, 2024
1 parent e7ca667 commit 719a535
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 32 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
64 changes: 64 additions & 0 deletions integrations/vite/nuxt.test.ts
Original file line number Diff line number Diff line change
@@ -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`)
})
},
)
63 changes: 31 additions & 32 deletions packages/@tailwindcss-vite/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 })
}
}
}

Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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)
},
},
Expand All @@ -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))
Expand Down

0 comments on commit 719a535

Please sign in to comment.