Skip to content

fix(css): handle dynamic CSS manual chunks#22553

Open
Xavrir wants to merge 1 commit into
vitejs:mainfrom
Xavrir:fix/css-dynamic-import-manual-chunk-22244
Open

fix(css): handle dynamic CSS manual chunks#22553
Xavrir wants to merge 1 commit into
vitejs:mainfrom
Xavrir:fix/css-dynamic-import-manual-chunk-22244

Conversation

@Xavrir
Copy link
Copy Markdown

@Xavrir Xavrir commented May 30, 2026

What is this PR solving?

Fixes #22244.

A dynamic import of a plain CSS file can be forced into a manual chunk. With Rolldown, that CSS-only chunk may get synthetic facade exports. Vite was using chunk.exports.length === 0 to decide whether a chunk was pure CSS, so this case stayed in the JS bundle and could emit code that referenced an undeclared binding such as lib_exports.

This PR classifies pure CSS chunks from their rendered module content instead. CSS modules and chunks with real JS still stay out of the pure CSS removal path.

Alternatives considered

I considered handling this in the import-analysis replacement path, but the bad chunk is created before that step. Keeping the fix in CSS chunk classification also preserves the existing flow where Vite removes pure CSS JS placeholders, records them in removedPureCssFilesCache, and lets build import analysis replace the dynamic import with Promise.resolve({}) while preserving CSS preload deps.

Tests

Added a css-dynamic-import playground case that dynamically imports manual.css in production and forces it into a manual chunk. The test checks that the CSS loads and that the built JS does not contain the bad generated export.

Manual inspection of the playground build shows the CSS file is emitted, the dynamic import is rewritten to Promise.resolve({}), and the CSS file remains in the preload dependency list.

Checks run locally:

  • PATH=/tmp/codex-pnpm-bin:$PATH pnpm run build
  • PATH=/tmp/codex-pnpm-bin:$PATH pnpm run test-build css-dynamic-import
  • PATH=/tmp/codex-pnpm-bin:$PATH pnpm run test-serve css-dynamic-import
  • PATH=/tmp/codex-pnpm-bin:$PATH pnpm run test-unit css
  • PATH=/tmp/codex-pnpm-bin:$PATH pnpm lint
  • PATH=/tmp/codex-pnpm-bin:$PATH pnpm typecheck
  • PATH=/tmp/codex-pnpm-bin:$PATH pnpm exec oxfmt --check packages/vite/src/node/plugins/css.ts playground/css-dynamic-import/__tests__/css-dynamic-import.spec.ts playground/css-dynamic-import/index.js

Local note: pnpm was not on PATH in my shell, so I ran these through a temporary Corepack wrapper.

Reviewer notes

The main behavior change is that pure CSS chunk detection no longer treats synthetic exports as proof that a chunk contains JS. The existing per-module checks still reject CSS modules and real JS chunks.

@Xavrir Xavrir marked this pull request as ready for review May 30, 2026 04:31
@Xavrir Xavrir force-pushed the fix/css-dynamic-import-manual-chunk-22244 branch from a93f8a9 to af35486 Compare May 30, 2026 04:36
@Xavrir
Copy link
Copy Markdown
Author

Xavrir commented May 30, 2026

Updated the regression assertion after an external review pointed out that checking for lib_exports was too specific to the original reproduction.

The test now asserts that the forced CSS-only manual chunk does not emit a manual-css*.js asset at all. I verified the guard by temporarily restoring the old chunk.exports.length === 0 behavior; pnpm run test-build css-dynamic-import then failed with:

expected 'import{t as e}from"./rolldown-runtime…' to be undefined

Restoring the production fix makes the targeted build/serve/unit checks pass again.

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.

[Vite 8] CSS dynamic import generates an exception module

1 participant