Skip to content

fix(css): use unique key for cssEntriesMap to prevent same-basename collision#22039

Draft
teee32 wants to merge 4 commits intovitejs:mainfrom
teee32:fix/css-entries-same-basename
Draft

fix(css): use unique key for cssEntriesMap to prevent same-basename collision#22039
teee32 wants to merge 4 commits intovitejs:mainfrom
teee32:fix/css-entries-same-basename

Conversation

@teee32
Copy link
Copy Markdown

@teee32 teee32 commented Mar 26, 2026

Problem

When two or more CSS-only entry points in different directories share the same basename (e.g. store/skins/style.scss and store3/skins/style.scss), Vite's CSS post-processing incorrectly cross-links them in the build manifest. This causes both stylesheets to be loaded at runtime when only one was requested, leading to unintended style overrides.

Reported in #22013.

Root Cause

cssEntriesMap in packages/vite/src/node/plugins/css.ts uses chunk.name as the Map key. When entries share a basename, they share a chunk.name, so the second set() overwrites the first. Later in generateBundle, the lookup returns the wrong referenceId, causing one entry's CSS file to be transferred as a dependency of the other.

cssEntriesMap.set(chunk.name, referenceId)   // collision when basenames match
//                ^^^^^^^^^^

Change

Replace chunk.name with originalFileName (the normalized root-relative path produced by getChunkOriginalFileName, which is already computed in the same scope) as the Map key. This is always unique for distinct entry chunks. Falls back to chunk.name when facadeModuleId is unavailable.

The same key strategy is applied on both the write path (renderChunk) and the read path (generateBundle).

Why This Fix

  • originalFileName is derived from chunk.facadeModuleId via getChunkOriginalFileName, the same function used by the manifest plugin to identify chunks — so the key is consistent with existing naming conventions.
  • The change is minimal (2 locations in css.ts) and does not alter the module type or any public API.
  • cssEntriesMap is also consumed by manifest.ts, where the key is passed to Rolldown's nativeManifestPlugin as the CSS entry name. Changing from basename to relative path makes CSS entry naming consistent with JS chunk naming in the manifest.

Risk

  • Low. The fallback ?? chunk.name preserves existing behavior when facadeModuleId is absent.
  • No change to public API or plugin hook signatures.
  • The manifest name field for CSS entries changes from basename to relative path, which is consistent with how JS chunks are already named.

Validation

  • Added a regression test: two CSS entry files (a/index.css, b/index.css) sharing the basename index.css in different directories, each with a distinct asset. The test verifies the manifest does not cross-link their assets.
  • All 54 existing build tests pass.
  • Lint and prettier pass via pre-commit hooks.

Closes #22013

Co-Authored-By: Codex noreply@openai.com
Co-Authored-By: Claude Opus 4.6 noreply@anthropic.com

…ollision

When multiple CSS-only entry points in different directories share the
same basename, `cssEntriesMap` used `chunk.name` as the Map key, causing
the second entry to overwrite the first. This led to incorrect CSS
cross-linking in the build manifest and spurious style loading at runtime.

Use `originalFileName` (the normalized relative path from
`getChunkOriginalFileName`) instead of `chunk.name` as the Map key,
which is always unique per entry. Falls back to `chunk.name` when
`facadeModuleId` is unavailable.

Fixes vitejs#22013

Co-Authored-By: Codex <noreply@openai.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@teee32 teee32 force-pushed the fix/css-entries-same-basename branch from 9b38d08 to 166adde Compare March 26, 2026 12:01
@sapphi-red sapphi-red marked this pull request as draft April 10, 2026 08:14
@sapphi-red sapphi-red added p2-edge-case Bug, but has workaround or limited in scope (priority) feat: build labels Apr 10, 2026
@sapphi-red
Copy link
Copy Markdown
Member

I've updated the test to ensure isEntry is set and the test failed. To fix that, I made a change on the native plugin side: rolldown/rolldown#9059
I also changed the code to use chunk.filename instead of originalFileName as that will be simpler.

@sapphi-red
Copy link
Copy Markdown
Member

(I'll merge this once the next rolldown release is out)

shulaoda added a commit to rolldown/rolldown that referenced this pull request Apr 16, 2026
…CSS entries (#9059)

see vitejs/vite#22039 for the reason

Co-authored-by: dalaoshu <165626830+shulaoda@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feat: build p2-edge-case Bug, but has workaround or limited in scope (priority)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

CSS entry points sharing the same basename in different directories produce incorrect cross-linked css dependencies in manifest

2 participants