Skip to content

Dev CSS sourcemaps: sources are written against the wrong base #23195

Description

@h-a-n-a

Describe the bug

With css.devSourcemap: true, the sources of dev CSS sourcemaps are produced with different base conventions depending on the code path. Per the sourcemap spec, a sources entry is a URL reference resolved against the map's location — and for an inline map in a <style> tag, that location falls back to the page hosting the tag. Several of the shapes Vite emits today do not survive that resolution.

Concrete example. An app at /Users/me/app with two pages and a nested css file:

index.html            <script type="module" src="/main.js"></script>
admin/index.html      <script type="module" src="/main.js"></script>
main.js               import './nested/dir/from-js.css'
nested/dir/from-js.css    @import './dep.css'; .from-js { color: red; }
nested/dir/dep.css        .dep { color: green; }

Shape 1 — style tag, postcss. The css injected by main.js carries this inline map:

{ "sources": ["/Users/me/app/nested/dir/dep.css",
              "/Users/me/app/nested/dir/from-js.css"] }

Absolute filesystem paths. The browser resolves them to http://localhost:5173/Users/me/app/nested/dir/dep.css. This happens to return the right file today only because the dev server serves in-root files at their fs path. It leaks the machine's directory layout into the browser, and for an out-of-root file (a monorepo package) the same shape produces a URL the server refuses.

Shape 2 — style tag, lightningcss. Same import, css.transformer: 'lightningcss':

{ "sources": ["nested/dir/from-js.css", "nested/dir/dep.css"] }

Relative to the project root — but the resolver's base is the page, not the root. The same map is injected into both pages:

from /                 → /nested/dir/dep.css          ✓ works, only because page base == root
from /admin/index.html → /admin/nested/dir/dep.css    ✗ answered by the HTML fallback (200, wrong content)

One map, two meanings. Any page that is not at the root resolves every source to a wrong URL.

Shape 3 — URL-served css, lightningcss. curl -H "Accept: text/css" /nested/dir/from-js.css returns the css with:

{ "sources": ["nested/dir/from-js.css", "nested/dir/dep.css"] }

Here the map's base is the css file's own URL, so the names double the path:

base /nested/dir/from-js.css + "nested/dir/dep.css" → /nested/dir/nested/dir/dep.css   ✗

This one is a plain bug: the rewrite in transformRequest.ts that re-bases served maps only handles path.isAbsolute sources, so lightningcss's root-relative names slip through unrewritten. The postcss equivalent of this request is correct — "sources": ["dep.css", "from-js.css"], relative to the file — because postcss maps are absolutized first and the rewrite catches them.

None of this is visible in everyday use, because dev maps embed sourcesContent and devtools then never fetch sources by name. The names only misbehave when something actually resolves them — workspace mapping, external tooling, or any consumer without the embedded content.

Expected behavior

sources should be correct relative to where the map is located:

  • Maps that have their own URL (URL-served css, chunk maps, build output) should use names relative to the map — as postcss URL-served responses and build output already do. Shape 3 should become ["from-js.css", "dep.css"].
  • Style-tag maps have no URL of their own and their page base varies, so the only page-independent reference is the path-absolute form:
{ "sources": ["/nested/dir/dep.css", "/nested/dir/from-js.css"] }

This resolves identically from /, /admin/index.html, or any other page. Out-of-root files cannot be expressed as a URL reference at all (resolution clamps ../ at the root); they stay labels, carried by sourcesContent — no /@fs/ in sources.

Reproduction

https://github.com/vitejs/vite/tree/test/css-sourcemap-sources-repro

Steps to reproduce

pnpm test-serve sources-resolution

System Info

System:
    OS: macOS 26.5.1
    CPU: (14) arm64 Apple M4 Pro
    Memory: 658.31 MB / 48.00 GB
    Shell: 5.9 - /bin/zsh
  Binaries:
    Node: 24.19.0 - /Users/shuyuan/.vite-plus/js_runtime/node/24.19.0/bin/node
    npm: 11.17.0 - /Users/shuyuan/.vite-plus/js_runtime/node/24.19.0/bin/npm
    pnpm: 10.34.5 - /Users/shuyuan/.local/state/fnm_multishells/6116_1785830242280/bin/pnpm
  Browsers:
    Chrome: 151.0.7922.77
    Safari: 26.5
  npmPackages:
    @vitejs/release-scripts: ^1.8.0 => 1.8.0
    rolldown: ~1.2.1 => 1.2.1
    rollup: ^4.59.0 => 4.59.0
    vite: workspace:* => 8.2.1

Used Package Manager

pnpm

Logs

No response

Validations

Metadata

Metadata

Assignees

No one assigned

    Labels

    feat: sourcemapSourcemap supportp3-minor-bugAn edge case that only affects very specific usage (priority)

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions