Skip to content

[vite] Global CSS HMR misses utilities discovered in lazy modules in 66.9.1 #5293

Description

@ntnyq

UnoCSS version

66.9.1

Describe the bug

In Vite development mode, utilities that are first discovered in a
dynamically imported module are generated by UnoCSS, but the browser does not
reload the global UnoCSS virtual module. The newly rendered content therefore
remains unstyled until the page is manually reloaded.

This is a regression from 66.8.1:

Version Five fresh dev-server runs
66.8.1 display: grid in 5/5 runs
66.9.1 display: block in 5/5 runs

Production builds include the expected utilities. The problem is specific to
development-mode incremental extraction/HMR.

Reproduction

https://github.com/ntnyq/unocss-vite-hmr-lazy-repro

pnpm install --frozen-lockfile
pnpm exec playwright install chromium
pnpm reproduce

The reproduction uses vanilla Vite and one dynamic import; Vue and routing are
not involved. The script starts five fresh Vite dev servers so that UnoCSS's
in-memory token cache cannot hide the issue.

Actual output with 66.9.1:

run 1: display=block, grid-template-columns=none
run 2: display=block, grid-template-columns=none
run 3: display=block, grid-template-columns=none
run 4: display=block, grid-template-columns=none
run 5: display=block, grid-template-columns=none
Bug reproduced 5/5 times.

Expected: after src/lazy.tsx is imported and renders an element with
grid grid-cols-3 gap-4, its computed display should become grid without a
full-page reload.

Suspected cause

This appears to be a follow-up regression from #5284 / commit 75103d5.

Global mode now stores the UnoCSS entry using the internal virtual-module ID:

const virtualId = toViteVirtualId(entry)
entries.add(virtualId)
return virtualId

However, sendUpdate() still sends mod.url directly for both HMR paths:

return {
  acceptedPath: mod.url,
  path: mod.url,
  timestamp: lastServedTime,
  type: 'js-update',
}

The observed WebSocket update uses the internal ID:

\0/__uno.css

The Vite browser client registered the URL-safe public path instead:

/@id/__x00__/__uno.css

hotModulesMap.get(path) therefore misses and silently ignores the update. No
timestamped CSS request is made. Rewriting path and acceptedPath to
/@id/__x00__/__uno.css makes the reproduction update immediately without a
reload.

The per-module implementation in the same commit already keeps the internal
ID for moduleGraph.getModuleById() while using a public path in the HMR
payload. Global mode likely needs the same internal-ID/public-URL separation.

Relevant source:

System Info

  • macOS
  • Node.js 24.19.0
  • pnpm 11.24.0
  • Vite 8.2.2
  • Chromium via Playwright 1.62.1

Validations

  • Read the contributing guidelines.
  • Searched existing issues and pull requests for the same bug.
  • Confirmed this is a concrete development-mode HMR regression.
  • Provided a minimal reproducible example.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingvite

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

    Sponsor
    SponsoredKunjungi sekarang
    Promo