Skip to content

Prevent @tailwindcss/vite crash under Vite's experimental bundledDev - #20379

Merged
RobinMalfait merged 7 commits into
tailwindlabs:mainfrom
lazerg:fix/issue-20378-bundleddev-hotupdate
Aug 3, 2026
Merged

Prevent @tailwindcss/vite crash under Vite's experimental bundledDev#20379
RobinMalfait merged 7 commits into
tailwindlabs:mainfrom
lazerg:fix/issue-20378-bundleddev-hotupdate

Conversation

@lazerg

@lazerg lazerg commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Summary

Under Vite's experimental bundledDev mode, the hotUpdate hook in @tailwindcss/vite gets called without a server. Vite only passes { type, file, modules } here, but the hook loops over Object.values(server.environments), so editing any file (JS, CSS, or HTML) throws TypeError: Cannot read properties of undefined (reading 'environments') and the dev server build fails.

The fix returns early when server is missing. Those environment loops only look at environments other than the current one, and the server-level hot/ws reload channels don't exist in this mode, so bailing out leaves the classic (non-bundledDev) dev path untouched.

Fixes #20378

Test plan

  • Added a unit test that calls hotUpdate without a server and checks it doesn't throw. It fails on the current code and passes with the guard.
  • Reproduced with a Vite 8 project using experimental.bundledDev: true: before the change, editing any JS/CSS/HTML file crashed the dev server; after it, edits work.
  • pnpm run test and the @tailwindcss/vite integration suite both pass.

[ci-all]

@lazerg
lazerg requested a review from a team as a code owner August 3, 2026 10:27
@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: f5fe27bb-22b9-4d58-a370-0d0002c0422c

📥 Commits

Reviewing files that changed from the base of the PR and between fdc4fa0 and b09bd43.

📒 Files selected for processing (1)
  • integrations/vite/bundled-dev.test.ts

Walkthrough

The Vite plugin now returns immediately when hotUpdate receives no server. A regression test verifies that the handler does not throw. An integration test covers initial and incremental CSS output, watched @source updates, and plugin errors in bundledDev mode. The changelog records the fix.

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the primary fix for the @tailwindcss/vite crash under Vite's experimental bundledDev mode.
Description check ✅ Passed The description directly explains the crash, the guard-based fix, affected mode, tests, and issue addressed.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@greptile-apps

greptile-apps Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Reviews (4): Last reviewed commit: "check all chunks" | Re-trigger Greptile

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: f155b7b0-e0e7-4f94-b96b-240d78b5f3c0

📥 Commits

Reviewing files that changed from the base of the PR and between 276d1bb and fdc4fa0.

📒 Files selected for processing (3)
  • integrations/vite/bundled-dev.test.ts
  • packages/@tailwindcss-vite/src/index.test.ts
  • packages/@tailwindcss-vite/src/index.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/@tailwindcss-vite/src/index.ts
  • packages/@tailwindcss-vite/src/index.test.ts

Comment on lines +107 to +125
await retryAssertion(async () => {
// Updates are additive and cause new candidates to be added.
await fs.write(
'project-a/index.html',
html`
<head>
<link rel="stylesheet" href="./src/index.css" />
</head>
<body>
<div class="underline m-2">Hello, world!</div>
</body>
`,
)

let styles = await fetchBundledStyles()
expect(styles).toContain(candidate`underline`)
expect(styles).toContain(candidate`flex`)
expect(styles).toContain(candidate`m-2`)
})

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Write each watched file before the retry loop.

retryAssertion reruns its callback after each failed assertion. Lines 109-119 and 129-134 therefore rewrite watched files on every retry. Each write can trigger another Vite rebuild. The test can keep observing a fallback or stale bundle and fail intermittently.

Move each fs.write call before its related retryAssertion call. The retry callback should only fetch and assert the rebuilt bundle.

Proposed change
-    await retryAssertion(async () => {
-      // Updates are additive and cause new candidates to be added.
-      await fs.write(
+    // Updates are additive and cause new candidates to be added.
+    await fs.write(
         'project-a/index.html',
         html`
           <head>
             <link rel="stylesheet" href="./src/index.css" />
           </head>
           <body>
             <div class="underline m-2">Hello, world!</div>
           </body>
         `,
-      )
-
+      )
+    await retryAssertion(async () => {
       let styles = await fetchBundledStyles()
       expect(styles).toContain(candidate`m-2`)
     })

The retry behavior is defined in integrations/utils.ts:663-679.

Also applies to: 127-141

@RobinMalfait RobinMalfait left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

Made a few small changes to make TypeScript happy.
Also added an integration test to verify that this mode also works, and not just not crashes.

@RobinMalfait
RobinMalfait enabled auto-merge (rebase) August 3, 2026 11:40
auto-merge was automatically disabled August 3, 2026 11:44

Rebase failed

@RobinMalfait
RobinMalfait merged commit 50daebd into tailwindlabs:main Aug 3, 2026
24 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

@tailwindcss/vite crashes on every edit under Vite's experimental.bundledDev

2 participants