From a52ed1d640d5bc8afaac394e2326a514c0537fa6 Mon Sep 17 00:00:00 2001 From: Ferdinand Thiessen Date: Mon, 10 Jun 2024 11:34:12 +0200 Subject: [PATCH] fix(css): ensure order of extracted CSS (#16588) --- packages/vite/src/node/plugins/css.ts | 32 ++++++++++++++++++--------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/packages/vite/src/node/plugins/css.ts b/packages/vite/src/node/plugins/css.ts index 8fbea570e1ad24..7626b45a80af58 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,34 @@ 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]), - ) + // will be populated in order they are used by entry points + const dynamicImports = new Set() - 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) + // 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) => + dynamicImports.add(importName), + ) + // Then collect the styles of the current chunk (might overwrite some styles from previous imports) 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) { + 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 }