Skip to content

fix: transform CSS url() references in ?url imports for bundledDev (fix #22863)#22880

Open
AmariahAK wants to merge 2 commits into
vitejs:mainfrom
AmariahAK:main
Open

fix: transform CSS url() references in ?url imports for bundledDev (fix #22863)#22880
AmariahAK wants to merge 2 commits into
vitejs:mainfrom
AmariahAK:main

Conversation

@AmariahAK

Copy link
Copy Markdown

PR Description

What is this PR solving?

When bundledDev: true is enabled, CSS files imported with ?url don't have their url() references transformed. Assets referenced inside the CSS (e.g., url("./vite.webp")) resolve to incorrect paths that the dev server doesn't serve.

Root cause: Three code paths converge:

  1. CSS plugin load hook — returned undefined for ?url CSS in dev mode, delegating to the asset plugin which reads the raw file
  2. CSS plugin transform filter — excludes ?url via SPECIAL_QUERY_RE, so compileCSS() URL rewriting never runs
  3. Asset plugin fileToBuiltUrl — in bundledDev, emits the raw CSS as-is via emitFile(), and memoryFilesMiddleware serves it directly without any transform

In normal dev mode this works because fileToDevUrl returns the raw file path — the browser then makes a follow-up request that goes through transformMiddleware, which invokes the CSS transform. In bundledDev there's no follow-up request; the raw CSS is served directly from in-memory Rolldown output.

Fixes #22863.

What other alternatives have you explored?

  • Fix in fileToBuiltUrl (asset plugin): Would import CSS transformation logic into the asset plugin — cross-cutting concern, violates single responsibility. Rejected.
  • Remove ?url from the CSS transform filter exclusion: The transform handler outputs CSS JS-module code (applying styles), not URL exports. Would require two fundamentally different code paths in one handler. Rejected.
  • Handle in the CSS load hook (chosen): Mirrors the existing build-mode ?url handling pattern; all CSS logic stays in one plugin; emitFile / getFileName are natively available in Rolldown load hooks; minimal, non-invasive diff.

Are there any parts you think require more attention from reviewers?

  • HMR: This fix does not include HMR support for CSS ?url imports in bundledDev. A file change will require a full page reload (which already happens for the importing module). HMR can be addressed as a follow-up.
  • preprocessorWorkerController: The load hook uses preprocessorWorkerController! (non-null assertion), matching the existing pattern in the transform handler. In bundledDev, buildStart is called by the Rolldown DevEngine before any load hooks, so this is safe.
  • fileToUrl type cast: The shared createCssUrlResolver helper uses pluginContext as any for the fileToUrl call because this has different types in bundledDev (Rolldown PluginContext) vs normal dev (Vite TransformPluginContext). This is an internal helper, not a public API.

atlarix-agent and others added 2 commits July 6, 2026 16:06
vitejs#22863)

When bundledDev is enabled, CSS files imported with ?url had their
url() references left untransformed because:
- The CSS load hook returned undefined (delegating to the asset plugin)
- The CSS transform hook excluded ?url via SPECIAL_QUERY_RE
- The asset plugin emitted the raw CSS as-is

Fix by handling ?url CSS in the CSS plugin's load hook for bundledDev:
read the file, run through compileCSS to transform url() references,
emit the transformed CSS as a Rolldown asset, and return the output URL.

Co-authored-by: atlarix-agent <agent@atlarix.dev>
@AmariahAK

Copy link
Copy Markdown
Author

@sapphi-red kindly review

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.

Asset transform is missing on URL imports with bundledDev

2 participants