diff --git a/packages/vite/src/node/plugins/css.ts b/packages/vite/src/node/plugins/css.ts index 2333a721df8d4f..872bb14168a245 100644 --- a/packages/vite/src/node/plugins/css.ts +++ b/packages/vite/src/node/plugins/css.ts @@ -606,11 +606,12 @@ export function cssPostPlugin(config: ResolvedConfig): Plugin { } if (config.build.cssCodeSplit) { - if (isPureCssChunk) { - // this is a shared CSS-only chunk that is empty. - pureCssChunks.add(chunk) - } if (opts.format === 'es' || opts.format === 'cjs') { + if (isPureCssChunk) { + // this is a shared CSS-only chunk that is empty. + pureCssChunks.add(chunk) + } + const isEntry = chunk.isEntry && isPureCssChunk const cssAssetName = ensureFileExt(chunk.name, '.css') const originalFilename = getChunkOriginalFileName( diff --git a/packages/vite/src/node/plugins/manifest.ts b/packages/vite/src/node/plugins/manifest.ts index eff3212d2fc06c..d01a959efca5fa 100644 --- a/packages/vite/src/node/plugins/manifest.ts +++ b/packages/vite/src/node/plugins/manifest.ts @@ -111,8 +111,14 @@ export function manifestPlugin(config: ResolvedConfig): Plugin { const fileNameToAssetMeta = new Map() const assets = generatedAssets.get(config)! assets.forEach((asset, referenceId) => { - const fileName = this.getFileName(referenceId) - fileNameToAssetMeta.set(fileName, asset) + try { + const fileName = this.getFileName(referenceId) + fileNameToAssetMeta.set(fileName, asset) + } catch (error: unknown) { + // The asset was generated as part of a different output option. + // It was already handled during the previous run of this plugin. + assets.delete(referenceId) + } }) const fileNameToAsset = new Map() diff --git a/playground/legacy/__tests__/watch/styles-only-entry-watch.spec.ts b/playground/legacy/__tests__/watch/styles-only-entry-watch.spec.ts new file mode 100644 index 00000000000000..7fd7f9391316fa --- /dev/null +++ b/playground/legacy/__tests__/watch/styles-only-entry-watch.spec.ts @@ -0,0 +1,47 @@ +import { expect, test } from 'vitest' +import { + editFile, + findAssetFile, + isBuild, + notifyRebuildComplete, + readManifest, + watcher, +} from '~utils' + +test.runIf(isBuild)('rebuilds styles only entry on change', async () => { + expect(findAssetFile(/style-only-entry-.+\.css/, 'watch')).toContain( + 'hotpink', + ) + expect(findAssetFile(/style-only-entry-legacy-.+\.js/, 'watch')).toContain( + 'hotpink', + ) + expect(findAssetFile(/polyfills-legacy-.+\.js/, 'watch')).toBeTruthy() + const numberOfManifestEntries = Object.keys(readManifest('watch')).length + expect(numberOfManifestEntries).toBe(3) + + editFile( + 'style-only-entry.css', + (originalContents) => originalContents.replace('hotpink', 'lightpink'), + true, + ) + await notifyRebuildComplete(watcher) + + const updatedManifest = readManifest('watch') + expect(Object.keys(updatedManifest)).toHaveLength(numberOfManifestEntries) + + // We must use the file referenced in the manifest here, + // since there'll be different versions of the file with different hashes. + const reRenderedCssFile = findAssetFile( + updatedManifest['style-only-entry.css']!.file.substring('assets/'.length), + 'watch', + ) + expect(reRenderedCssFile).toContain('lightpink') + const reRenderedCssLegacyFile = findAssetFile( + updatedManifest['style-only-entry-legacy.css']!.file.substring( + 'assets/'.length, + ), + 'watch', + ) + expect(reRenderedCssLegacyFile).toContain('lightpink') + expect(findAssetFile(/polyfills-legacy-.+\.js/, 'watch')).toBeTruthy() +}) diff --git a/playground/legacy/package.json b/playground/legacy/package.json index 542fe20286f04a..85c9c72a3f960f 100644 --- a/playground/legacy/package.json +++ b/playground/legacy/package.json @@ -10,6 +10,7 @@ "build:multiple-output": "vite --config ./vite.config-multiple-output.js build", "build:no-polyfills": "vite --config ./vite.config-no-polyfills.js build", "build:no-polyfills-no-systemjs": "vite --config ./vite.config-no-polyfills-no-systemjs.js build", + "build:watch": "vite --config ./vite.config-watch.js build --debug legacy", "debug": "node --inspect-brk ../../packages/vite/bin/vite", "preview": "vite preview" }, diff --git a/playground/legacy/style-only-entry.css b/playground/legacy/style-only-entry.css new file mode 100644 index 00000000000000..d88f88426437a3 --- /dev/null +++ b/playground/legacy/style-only-entry.css @@ -0,0 +1,3 @@ +:root { + background: hotpink; +} diff --git a/playground/legacy/vite.config-watch.js b/playground/legacy/vite.config-watch.js new file mode 100644 index 00000000000000..48d57f3e988edc --- /dev/null +++ b/playground/legacy/vite.config-watch.js @@ -0,0 +1,17 @@ +import { resolve } from 'node:path' +import legacy from '@vitejs/plugin-legacy' +import { defineConfig } from 'vite' + +export default defineConfig({ + plugins: [legacy()], + build: { + manifest: true, + rollupOptions: { + input: { + 'style-only-entry': resolve(__dirname, 'style-only-entry.css'), + }, + }, + watch: {}, + outDir: 'dist/watch', + }, +})