Skip to content

Commit 5464190

Browse files
sapphi-redthy486
andauthored
fix(html): cache unfiltered CSS list to prevent missing styles across entries (#22017)
Co-authored-by: thy486 <83344148+thy486@users.noreply.github.com>
1 parent 77c95bf commit 5464190

File tree

2 files changed

+89
-12
lines changed

2 files changed

+89
-12
lines changed

packages/vite/src/node/__tests__/html.spec.ts

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,74 @@ describe('getCssFilesForChunk', () => {
161161
])
162162
})
163163

164+
test('cached chunk does not lose CSS that was already in seenCss during first entry (#21298)', () => {
165+
// entry → chunk1 (chunk1.css, chunk-shared.css)
166+
// → chunk2 (chunk2.css, chunk-shared.css)
167+
// entry2 → chunk2
168+
const chunk1 = createChunk(
169+
'chunk1.js',
170+
[],
171+
['chunk1.css', 'chunk-shared.css'],
172+
)
173+
const chunk2 = createChunk(
174+
'chunk2.js',
175+
[],
176+
['chunk2.css', 'chunk-shared.css'],
177+
)
178+
const entry = createChunk(
179+
'entry.js',
180+
['chunk1.js', 'chunk2.js'],
181+
['entry.css'],
182+
)
183+
const entry2 = createChunk('entry2.js', ['chunk2.js'], ['entry2.css'])
184+
const bundle = createBundle(entry, entry2, chunk1, chunk2)
185+
const cache = new Map<OutputChunk, string[]>()
186+
187+
expect(getCssFilesForChunk(entry, bundle, cache)).toStrictEqual([
188+
'chunk1.css',
189+
'chunk-shared.css',
190+
'chunk2.css',
191+
'entry.css',
192+
])
193+
expect(getCssFilesForChunk(entry2, bundle, cache)).toStrictEqual([
194+
'chunk2.css',
195+
'chunk-shared.css',
196+
'entry2.css',
197+
])
198+
})
199+
200+
test('dirty leaf chunk CSS is not lost through cached parent (#21298 edge case)', () => {
201+
// entry1 → other (shared.css)
202+
// → mid → leaf (shared.css, leaf.css)
203+
// entry2 → mid → leaf (shared.css, leaf.css)
204+
const leaf = createChunk('leaf.js', [], ['shared.css', 'leaf.css'])
205+
const mid = createChunk('mid.js', ['leaf.js'], ['mid.css'])
206+
const other = createChunk('other.js', [], ['shared.css'])
207+
const entry1 = createChunk(
208+
'entry1.js',
209+
['other.js', 'mid.js'],
210+
['entry1.css'],
211+
)
212+
const entry2 = createChunk('entry2.js', ['mid.js'], ['entry2.css'])
213+
const bundle = createBundle(entry1, entry2, other, mid, leaf)
214+
const cache = new Map<OutputChunk, string[]>()
215+
216+
expect(getCssFilesForChunk(entry1, bundle, cache)).toStrictEqual([
217+
'shared.css',
218+
'leaf.css',
219+
'mid.css',
220+
'entry1.css',
221+
])
222+
// entry2 must still get shared.css via leaf, even though mid's cache
223+
// was built while shared.css was already seen
224+
expect(getCssFilesForChunk(entry2, bundle, cache)).toStrictEqual([
225+
'shared.css',
226+
'leaf.css',
227+
'mid.css',
228+
'entry2.css',
229+
])
230+
})
231+
164232
test('circular imports do not cause infinite loop', () => {
165233
const a = createChunk('a.js', ['b.js'], ['a.css'])
166234
const b = createChunk('b.js', ['a.js'], ['b.css'])

packages/vite/src/node/plugins/html.ts

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -370,31 +370,40 @@ export function getCssFilesForChunk(
370370
return additionals
371371
}
372372

373-
const files: string[] = []
373+
// Collect all CSS from imports (unfiltered for caching, filtered for return)
374+
const allFiles: string[] = []
375+
const filteredFiles: string[] = []
374376
chunk.imports.forEach((file) => {
375377
const importee = bundle[file]
376378
if (importee?.type === 'chunk') {
377-
files.push(
378-
...getCssFilesForChunk(
379-
importee,
380-
bundle,
381-
analyzedImportedCssFiles,
382-
seenChunks,
383-
seenCss,
384-
),
379+
const importeeCss = getCssFilesForChunk(
380+
importee,
381+
bundle,
382+
analyzedImportedCssFiles,
383+
seenChunks,
384+
seenCss,
385385
)
386+
filteredFiles.push(...importeeCss)
387+
// For cache: use the importee's full cached list
388+
if (analyzedImportedCssFiles.has(importee)) {
389+
allFiles.push(...analyzedImportedCssFiles.get(importee)!)
390+
} else {
391+
allFiles.push(...importeeCss)
392+
}
386393
}
387394
})
388-
analyzedImportedCssFiles.set(chunk, files)
389395

390396
chunk.viteMetadata!.importedCss.forEach((file) => {
397+
allFiles.push(file)
391398
if (!seenCss.has(file)) {
392399
seenCss.add(file)
393-
files.push(file)
400+
filteredFiles.push(file)
394401
}
395402
})
396403

397-
return files
404+
analyzedImportedCssFiles.set(chunk, unique(allFiles))
405+
406+
return filteredFiles
398407
}
399408

400409
/**

0 commit comments

Comments
 (0)