From 2f1c3d39e27be1bbd980795cd694c4f288579df8 Mon Sep 17 00:00:00 2001 From: Ferdinand Thiessen Date: Fri, 3 May 2024 15:31:57 +0200 Subject: [PATCH 1/2] fix(css): ensure order of extracted CSS Without this the order of the extracted CSS rules is defined by the order `renderChunk` of the css plugin is called. So with this the order of CSS rules is always in order of the output chunks of the bundle. Signed-off-by: Ferdinand Thiessen --- packages/vite/src/node/plugins/css.ts | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/packages/vite/src/node/plugins/css.ts b/packages/vite/src/node/plugins/css.ts index 8fbea570e1ad24..5a802117cc33c1 100644 --- a/packages/vite/src/node/plugins/css.ts +++ b/packages/vite/src/node/plugins/css.ts @@ -7,6 +7,7 @@ import postcssrc from 'postcss-load-config' import type { ExistingRawSourceMap, ModuleFormat, + OutputAsset, OutputChunk, RenderedChunk, RollupError, @@ -844,23 +845,28 @@ export function cssPostPlugin(config: ResolvedConfig): Plugin { function extractCss() { let css = '' const collected = new Set() - const prelimaryNameToChunkMap = new Map( - Object.values(bundle) - .filter((chunk): chunk is OutputChunk => chunk.type === 'chunk') - .map((chunk) => [chunk.preliminaryFileName, chunk]), - ) - function collect(fileName: string) { - const chunk = bundle[fileName] + function collect(chunk: OutputChunk | OutputAsset) { if (!chunk || chunk.type !== 'chunk' || collected.has(chunk)) return collected.add(chunk) - chunk.imports.forEach(collect) + chunk.imports.forEach((importName) => collect(bundle[importName])) + chunk.dynamicImports.forEach((importName) => + collect(bundle[importName]), + ) css += chunkCSSMap.get(chunk.preliminaryFileName) ?? '' } - for (const chunkName of chunkCSSMap.keys()) - collect(prelimaryNameToChunkMap.get(chunkName)?.fileName ?? '') + // The bundle is guaranteed to be deterministic, if not then we have a bug in rollup. + // So we use it to ensure a deterministic order of styles + for (const chunk of Object.values(bundle)) { + if ( + chunk.type === 'chunk' && + (chunk.isEntry || chunk.isDynamicEntry) + ) { + collect(chunk) + } + } return css } From f34bc556c58d06aa754d4715f88d0bc30ab395b6 Mon Sep 17 00:00:00 2001 From: Ferdinand Thiessen Date: Sat, 8 Jun 2024 15:17:43 +0200 Subject: [PATCH 2/2] fix(css): Ensure dynamic chunks are always extracted last to overwrite the styles Signed-off-by: Ferdinand Thiessen --- packages/vite/src/node/plugins/css.ts | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/packages/vite/src/node/plugins/css.ts b/packages/vite/src/node/plugins/css.ts index 5a802117cc33c1..7626b45a80af58 100644 --- a/packages/vite/src/node/plugins/css.ts +++ b/packages/vite/src/node/plugins/css.ts @@ -845,28 +845,34 @@ export function cssPostPlugin(config: ResolvedConfig): Plugin { function extractCss() { let css = '' const collected = new Set() + // will be populated in order they are used by entry points + const dynamicImports = new Set() function collect(chunk: OutputChunk | OutputAsset) { if (!chunk || chunk.type !== 'chunk' || collected.has(chunk)) return collected.add(chunk) + // First collect all styles from the synchronous imports (lowest priority) chunk.imports.forEach((importName) => collect(bundle[importName])) + // Save dynamic imports in deterministic order to add the styles later (to have the highest priority) chunk.dynamicImports.forEach((importName) => - collect(bundle[importName]), + dynamicImports.add(importName), ) + // Then collect the styles of the current chunk (might overwrite some styles from previous imports) css += chunkCSSMap.get(chunk.preliminaryFileName) ?? '' } // The bundle is guaranteed to be deterministic, if not then we have a bug in rollup. // So we use it to ensure a deterministic order of styles for (const chunk of Object.values(bundle)) { - if ( - chunk.type === 'chunk' && - (chunk.isEntry || chunk.isDynamicEntry) - ) { + if (chunk.type === 'chunk' && chunk.isEntry) { collect(chunk) } } + // Now collect the dynamic chunks, this is done last to have the styles overwrite the previous ones + for (const chunkName of dynamicImports) { + collect(bundle[chunkName]) + } return css }