From 741611aab0e6bb15c9cbd994747d91924f04f5a7 Mon Sep 17 00:00:00 2001 From: Stanislav Lashmanov Date: Mon, 11 Aug 2025 02:39:25 +0400 Subject: [PATCH 1/2] fix(build): replace 'names' with unmangled 'name' for CSS assets This removes the 'names' field which had incorrect CSS entrypoint names. Instead, a 'name' field is added that uses CSS entrypoint name as-is. --- packages/vite/src/node/plugins/asset.ts | 4 ++-- packages/vite/src/node/plugins/css.ts | 2 +- packages/vite/src/node/plugins/manifest.ts | 16 ++++++++-------- .../__tests__/backend-integration.spec.ts | 4 +++- playground/backend-integration/dir/custom.css | 3 +++ playground/backend-integration/vite.config.js | 4 ++++ 6 files changed, 21 insertions(+), 12 deletions(-) create mode 100644 playground/backend-integration/dir/custom.css diff --git a/packages/vite/src/node/plugins/asset.ts b/packages/vite/src/node/plugins/asset.ts index 74f0220a4e9c19..5954e1cd92fa2e 100644 --- a/packages/vite/src/node/plugins/asset.ts +++ b/packages/vite/src/node/plugins/asset.ts @@ -46,7 +46,7 @@ export const inlineRE = /[?&]inline\b/ const assetCache = new WeakMap>() /** a set of referenceId for entry CSS assets for each environment */ -export const cssEntriesMap = new WeakMap>() +export const cssEntriesMap = new WeakMap>() // add own dictionary entry by directly assigning mrmime export function registerCustomMime(): void { @@ -148,7 +148,7 @@ export function assetPlugin(config: ResolvedConfig): Plugin { buildStart() { assetCache.set(this.environment, new Map()) - cssEntriesMap.set(this.environment, new Set()) + cssEntriesMap.set(this.environment, new Map()) }, resolveId: { diff --git a/packages/vite/src/node/plugins/css.ts b/packages/vite/src/node/plugins/css.ts index daac55989b4429..fff6ae63d9731a 100644 --- a/packages/vite/src/node/plugins/css.ts +++ b/packages/vite/src/node/plugins/css.ts @@ -863,7 +863,7 @@ export function cssPostPlugin(config: ResolvedConfig): Plugin { source: chunkCSS, }) if (isEntry) { - cssEntriesMap.get(this.environment)!.add(referenceId) + cssEntriesMap.get(this.environment)!.set(chunk.name, referenceId) } chunk.viteMetadata!.importedCss.add(this.getFileName(referenceId)) } else if (this.environment.config.consumer === 'client') { diff --git a/packages/vite/src/node/plugins/manifest.ts b/packages/vite/src/node/plugins/manifest.ts index 064e20d2142818..043129ee55dbd8 100644 --- a/packages/vite/src/node/plugins/manifest.ts +++ b/packages/vite/src/node/plugins/manifest.ts @@ -122,25 +122,25 @@ export function manifestPlugin(): Plugin { function createAsset( asset: OutputAsset, src: string, - isEntry?: boolean, + name?: string, ): ManifestChunk { const manifestChunk: ManifestChunk = { file: asset.fileName, src, } - if (isEntry) { + if (name) { manifestChunk.isEntry = true - manifestChunk.names = asset.names + manifestChunk.name = name } return manifestChunk } const entryCssReferenceIds = cssEntriesMap.get(this.environment)! - const entryCssAssetFileNames = new Set() - for (const id of entryCssReferenceIds) { + const entryCssAssetFileNames = new Map() + for (const [name, id] of entryCssReferenceIds) { try { const fileName = this.getFileName(id) - entryCssAssetFileNames.add(fileName) + entryCssAssetFileNames.set(fileName, name) } catch { // The asset was generated as part of a different output option. // It was already handled during the previous run of this plugin. @@ -157,8 +157,8 @@ export function manifestPlugin(): Plugin { chunk.originalFileNames.length > 0 ? chunk.originalFileNames[0] : `_${path.basename(chunk.fileName)}` - const isEntry = entryCssAssetFileNames.has(chunk.fileName) - const asset = createAsset(chunk, src, isEntry) + const name = entryCssAssetFileNames.get(chunk.fileName) + const asset = createAsset(chunk, src, name) // If JS chunk and asset chunk are both generated from the same source file, // prioritize JS chunk as it contains more information diff --git a/playground/backend-integration/__tests__/backend-integration.spec.ts b/playground/backend-integration/__tests__/backend-integration.spec.ts index 5a314d6b9d5a76..e0aeef00d1a793 100644 --- a/playground/backend-integration/__tests__/backend-integration.spec.ts +++ b/playground/backend-integration/__tests__/backend-integration.spec.ts @@ -54,6 +54,7 @@ describe.runIf(isBuild)('build', () => { const scssAssetEntry = manifest['nested/blue.scss'] const imgAssetEntry = manifest['../images/logo.png'] const dirFooAssetEntry = manifest['../../dir/foo.css'] + const customNameAssetEntry = manifest['../../dir/custom.css'] const iconEntrypointEntry = manifest['icon.png'] const waterContainerEntry = manifest['water-container.svg'] expect(htmlEntry.css.length).toEqual(1) @@ -74,7 +75,8 @@ describe.runIf(isBuild)('build', () => { expect(dirFooAssetEntry).not.toBeUndefined() // '\\' should not be used even on windows // use the entry name expect(dirFooAssetEntry.file).toMatch('assets/bar-') - expect(dirFooAssetEntry.names).toStrictEqual(['bar.css']) + expect(dirFooAssetEntry.name).toStrictEqual('bar.css') + expect(customNameAssetEntry.name).toStrictEqual('bar.custom') expect(iconEntrypointEntry?.file).not.toBeUndefined() expect(waterContainerEntry?.file).not.toBeUndefined() }) diff --git a/playground/backend-integration/dir/custom.css b/playground/backend-integration/dir/custom.css new file mode 100644 index 00000000000000..086058a71f67e2 --- /dev/null +++ b/playground/backend-integration/dir/custom.css @@ -0,0 +1,3 @@ +.custom { + color: red; +} diff --git a/playground/backend-integration/vite.config.js b/playground/backend-integration/vite.config.js index f432f139a4f6d9..7560c31541ac22 100644 --- a/playground/backend-integration/vite.config.js +++ b/playground/backend-integration/vite.config.js @@ -23,6 +23,10 @@ function BackendIntegrationExample() { entrypoints.push(['tailwindcss-colors', 'tailwindcss/colors.js']) entrypoints.push(['bar.css', path.resolve(__dirname, './dir/foo.css')]) + entrypoints.push([ + 'bar.custom', + path.resolve(__dirname, './dir/custom.css'), + ]) return { server: { From 5017e405f04b2c54fcad52a69f72167d59f4f2c3 Mon Sep 17 00:00:00 2001 From: sapphi-red <49056869+sapphi-red@users.noreply.github.com> Date: Fri, 19 Sep 2025 15:12:19 +0900 Subject: [PATCH 2/2] chore: keep `names` field for compat --- packages/vite/src/node/plugins/manifest.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/vite/src/node/plugins/manifest.ts b/packages/vite/src/node/plugins/manifest.ts index 043129ee55dbd8..1b94bd7f28e619 100644 --- a/packages/vite/src/node/plugins/manifest.ts +++ b/packages/vite/src/node/plugins/manifest.ts @@ -21,7 +21,7 @@ export interface ManifestChunk { assets?: string[] isEntry?: boolean name?: string - names?: string[] + // names field is deprecated (removed from types, but still emitted for backward compatibility) isDynamicEntry?: boolean imports?: string[] dynamicImports?: string[] @@ -131,12 +131,14 @@ export function manifestPlugin(): Plugin { if (name) { manifestChunk.isEntry = true manifestChunk.name = name + // @ts-expect-error keep names field for backward compatibility + manifestChunk.names = asset.names } return manifestChunk } const entryCssReferenceIds = cssEntriesMap.get(this.environment)! - const entryCssAssetFileNames = new Map() + const entryCssAssetFileNames = new Map() for (const [name, id] of entryCssReferenceIds) { try { const fileName = this.getFileName(id)