Skip to content

Conversation

@lucalooz
Copy link
Contributor

@lucalooz lucalooz commented Oct 2, 2025

Fixes #15196

Description

vite:css-post is placed after vite:asset which cleans up assets in the generateBundle phase.

vite:css-post emits assets inside renderChunk but also in the generateBundle phase for cssCodeSplit=false, which is AFTER vite:asset execution.

So it's vite:css-post responsibility to respect the emitAssets config.

@sapphi-red sapphi-red added feat: css feat: ssr p2-edge-case Bug, but has workaround or limited in scope (priority) labels Oct 3, 2025
Copy link
Member

@sapphi-red sapphi-red left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we still need to run this part:

// remove empty css chunks and their imports
if (pureCssChunks.size) {
// map each pure css chunk (rendered chunk) to it's corresponding bundle
// chunk. we check that by `preliminaryFileName` as they have different
// `filename`s (rendered chunk has the !~{XXX}~ placeholder)
const prelimaryNameToChunkMap = Object.fromEntries(
Object.values(bundle)
.filter((chunk): chunk is OutputChunk => chunk.type === 'chunk')
.map((chunk) => [chunk.preliminaryFileName, chunk.fileName]),
)
// When running in watch mode the generateBundle is called once per output format
// in this case the `bundle` is not populated with the other output files
// but they are still in `pureCssChunks`.
// So we need to filter the names and only use those who are defined
const pureCssChunkNames = [...pureCssChunks]
.map((pureCssChunk) => prelimaryNameToChunkMap[pureCssChunk.fileName])
.filter(Boolean)
const replaceEmptyChunk = getEmptyChunkReplacer(
pureCssChunkNames,
opts.format,
)
for (const file in bundle) {
const chunk = bundle[file]
if (chunk.type === 'chunk') {
let chunkImportsPureCssChunk = false
// remove pure css chunk from other chunk's imports,
// and also register the emitted CSS files under the importer
// chunks instead.
chunk.imports = chunk.imports.filter((file) => {
if (pureCssChunkNames.includes(file)) {
const { importedCss, importedAssets } = (
bundle[file] as OutputChunk
).viteMetadata!
importedCss.forEach((file) =>
chunk.viteMetadata!.importedCss.add(file),
)
importedAssets.forEach((file) =>
chunk.viteMetadata!.importedAssets.add(file),
)
chunkImportsPureCssChunk = true
return false
}
return true
})
if (chunkImportsPureCssChunk) {
chunk.code = replaceEmptyChunk(chunk.code)
}
}
}
const removedPureCssFiles = removedPureCssFilesCache.get(config)!
pureCssChunkNames.forEach((fileName) => {
removedPureCssFiles.set(fileName, bundle[fileName] as RenderedChunk)
delete bundle[fileName]
delete bundle[`${fileName}.map`]
})
}
const cssAssets = Object.values(bundle).filter(
(asset): asset is OutputAsset =>
asset.type === 'asset' && asset.fileName.endsWith('.css'),
)
for (const cssAsset of cssAssets) {
if (typeof cssAsset.source === 'string') {
cssAsset.source = cssAsset.source.replace(viteHashUpdateMarkerRE, '')
}
}

To fix that, would you move the condition in this if?
if (!this.environment.config.build.cssCodeSplit && !hasEmitted) {

@lucalooz lucalooz force-pushed the css-respect-emitAssets branch from bcecf6d to 4180bd4 Compare October 3, 2025 08:22
@lucalooz lucalooz requested a review from sapphi-red October 3, 2025 08:25
Copy link
Member

@sapphi-red sapphi-red left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks

@sapphi-red sapphi-red merged commit d3e7eee into vitejs:main Oct 3, 2025
16 checks passed
renovate bot added a commit to andrei-picus-tink/auto-renovate that referenced this pull request Oct 15, 2025
| datasource | package | from  | to     |
| ---------- | ------- | ----- | ------ |
| npm        | vite    | 7.1.9 | 7.1.10 |


## [v7.1.10](https://github.com/vitejs/vite/blob/HEAD/packages/vite/CHANGELOG.md#small-7110-2025-10-14-small)

##### Bug Fixes

- **css:** avoid duplicate style for server rendered stylesheet link and client inline style during dev ([#20767](vitejs/vite#20767)) ([3a92bc7](vitejs/vite@3a92bc7))
- **css:** respect emitAssets when cssCodeSplit=false ([#20883](vitejs/vite#20883)) ([d3e7eee](vitejs/vite@d3e7eee))
- **deps:** update all non-major dependencies ([879de86](vitejs/vite@879de86))
- **deps:** update all non-major dependencies ([#20894](vitejs/vite#20894)) ([3213f90](vitejs/vite@3213f90))
- **dev:** allow aliases starting with `//` ([#20760](vitejs/vite#20760)) ([b95fa2a](vitejs/vite@b95fa2a))
- **dev:** remove timestamp query consistently ([#20887](vitejs/vite#20887)) ([6537d15](vitejs/vite@6537d15))
- **esbuild:** inject esbuild helpers correctly for esbuild 0.25.9+ ([#20906](vitejs/vite#20906)) ([446eb38](vitejs/vite@446eb38))
- normalize path before calling `fileToBuiltUrl` ([#20898](vitejs/vite#20898)) ([73b6d24](vitejs/vite@73b6d24))
- preserve original sourcemap file field when combining sourcemaps ([#20926](vitejs/vite#20926)) ([c714776](vitejs/vite@c714776))

##### Documentation

- correct `WebSocket` spelling ([#20890](vitejs/vite#20890)) ([29e98dc](vitejs/vite@29e98dc))

##### Miscellaneous Chores

- **deps:** update rolldown-related dependencies ([#20923](vitejs/vite#20923)) ([a5e3b06](vitejs/vite@a5e3b06))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feat: css feat: ssr 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.

Vite generates css code for SSR build if "cssCodeSplit" is disabled

2 participants