From 3ac56f8a37b5a102f4e7a123b446ad75dea19164 Mon Sep 17 00:00:00 2001 From: clay jenson Date: Thu, 6 Nov 2025 01:02:15 +0800 Subject: [PATCH 1/2] fix(css-order): preserve import order for CSS chunks --- packages/vite/src/node/plugins/css.ts | 13 ++++++--- .../__tests__/order.spec.ts | 27 +++++++++++++++++++ playground/css-order-manual-chunks/base.css | 3 +++ playground/css-order-manual-chunks/index.html | 12 +++++++++ playground/css-order-manual-chunks/lib.css | 3 +++ playground/css-order-manual-chunks/main.js | 5 ++++ .../css-order-manual-chunks/vite.config.js | 13 +++++++++ 7 files changed, 73 insertions(+), 3 deletions(-) create mode 100644 playground/css-order-manual-chunks/__tests__/order.spec.ts create mode 100644 playground/css-order-manual-chunks/base.css create mode 100644 playground/css-order-manual-chunks/index.html create mode 100644 playground/css-order-manual-chunks/lib.css create mode 100644 playground/css-order-manual-chunks/main.js create mode 100644 playground/css-order-manual-chunks/vite.config.js diff --git a/packages/vite/src/node/plugins/css.ts b/packages/vite/src/node/plugins/css.ts index b80696ae6e4784..cc03b26e06cb3c 100644 --- a/packages/vite/src/node/plugins/css.ts +++ b/packages/vite/src/node/plugins/css.ts @@ -1049,9 +1049,16 @@ export function cssPostPlugin(config: ResolvedConfig): Plugin { const { importedCss, importedAssets } = ( bundle[file] as OutputChunk ).viteMetadata! - importedCss.forEach((file) => - chunk.viteMetadata!.importedCss.add(file), - ) + // Preserve original import order: ensure CSS coming from imported + // pure CSS chunks appears before the parent's existing CSS. + if (importedCss.size > 0) { + const parentCss = chunk.viteMetadata!.importedCss + const ordered = new Set([ + ...importedCss, + ...parentCss, + ]) + chunk.viteMetadata!.importedCss = ordered + } importedAssets.forEach((file) => chunk.viteMetadata!.importedAssets.add(file), ) diff --git a/playground/css-order-manual-chunks/__tests__/order.spec.ts b/playground/css-order-manual-chunks/__tests__/order.spec.ts new file mode 100644 index 00000000000000..90e81f64ce89fe --- /dev/null +++ b/playground/css-order-manual-chunks/__tests__/order.spec.ts @@ -0,0 +1,27 @@ +import { describe, expect, test } from 'vitest' +import { isBuild, page, readFile } from '~utils' + +describe.runIf(isBuild)('build', () => { + test('css links respect import order with manualChunks', async () => { + const html = readFile('dist/index.html') as string + // expect lib.css link appears before entry css link (index.css) + const libHref = /href="(.*?)\/assets\/lib-([\w-]+)\.css"/g + const indexHref = /href="(.*?)\/assets\/index-([\w-]+)\.css"/g + + const libMatch = libHref.exec(html) + const indexMatch = indexHref.exec(html) + + expect(libMatch, 'lib.css link should exist').toBeTruthy() + expect(indexMatch, 'index.css link should exist').toBeTruthy() + + expect(libMatch!.index).toBeLessThan(indexMatch!.index) + }) +}) + +test('runtime style precedence matches import order', async () => { + // lib.css sets .box { color: green }, base.css sets .box { color: red } + // If lib.css is loaded before index.css, final color should be red + const color = await page.$eval('#app', (el) => getComputedStyle(el).color) + // smoke check page loaded + expect(color).toBe('rgb(255, 0, 0)') +}) diff --git a/playground/css-order-manual-chunks/base.css b/playground/css-order-manual-chunks/base.css new file mode 100644 index 00000000000000..f8336b579532b1 --- /dev/null +++ b/playground/css-order-manual-chunks/base.css @@ -0,0 +1,3 @@ +.box { + color: red; +} diff --git a/playground/css-order-manual-chunks/index.html b/playground/css-order-manual-chunks/index.html new file mode 100644 index 00000000000000..da2cd3b5b73e9c --- /dev/null +++ b/playground/css-order-manual-chunks/index.html @@ -0,0 +1,12 @@ + + + + + + CSS Order Manual Chunks + + +
css order manual chunks
+ + + diff --git a/playground/css-order-manual-chunks/lib.css b/playground/css-order-manual-chunks/lib.css new file mode 100644 index 00000000000000..b099f7e48957d4 --- /dev/null +++ b/playground/css-order-manual-chunks/lib.css @@ -0,0 +1,3 @@ +.box { + color: green; +} diff --git a/playground/css-order-manual-chunks/main.js b/playground/css-order-manual-chunks/main.js new file mode 100644 index 00000000000000..850b0b4bc6ff35 --- /dev/null +++ b/playground/css-order-manual-chunks/main.js @@ -0,0 +1,5 @@ +// simulate third-party big css split via manualChunks +import './lib.css' +import './base.css' + +document.getElementById('app').className = 'box' diff --git a/playground/css-order-manual-chunks/vite.config.js b/playground/css-order-manual-chunks/vite.config.js new file mode 100644 index 00000000000000..e626fcf248434e --- /dev/null +++ b/playground/css-order-manual-chunks/vite.config.js @@ -0,0 +1,13 @@ +import { defineConfig } from 'vite' + +export default defineConfig({ + build: { + rollupOptions: { + output: { + manualChunks(id) { + if (id.endsWith('/lib.css')) return 'lib' + }, + }, + }, + }, +}) From 565fe782c82d28f77732fdd0f9792c06603d7494 Mon Sep 17 00:00:00 2001 From: clay jenson Date: Thu, 6 Nov 2025 01:17:19 +0800 Subject: [PATCH 2/2] remove false comment --- playground/css-order-manual-chunks/__tests__/order.spec.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/playground/css-order-manual-chunks/__tests__/order.spec.ts b/playground/css-order-manual-chunks/__tests__/order.spec.ts index 90e81f64ce89fe..6c59f7f81fd12e 100644 --- a/playground/css-order-manual-chunks/__tests__/order.spec.ts +++ b/playground/css-order-manual-chunks/__tests__/order.spec.ts @@ -22,6 +22,5 @@ test('runtime style precedence matches import order', async () => { // lib.css sets .box { color: green }, base.css sets .box { color: red } // If lib.css is loaded before index.css, final color should be red const color = await page.$eval('#app', (el) => getComputedStyle(el).color) - // smoke check page loaded expect(color).toBe('rgb(255, 0, 0)') })