Skip to content

Commit 0c0172e

Browse files
teemingcdummdidumm
andauthored
fix: correctly map shared entry-point CSS files during inlining (#13431)
fixes #13423 This PR fixes the case where we fail to find a server stylesheet when Vite associates a page's CSS with a different chunk instead of the component itself. This can happen when the page is imported by another file. So, we need to crawl the imports property in the manifest for CSS too. --------- Co-authored-by: Simon H <[email protected]>
1 parent 28cf645 commit 0c0172e

File tree

4 files changed

+53
-30
lines changed

4 files changed

+53
-30
lines changed

.changeset/ten-planes-relax.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@sveltejs/kit': patch
3+
---
4+
5+
fix: correctly find shared entry-point CSS files during inlining

packages/kit/src/exports/vite/build/build_server.js

Lines changed: 37 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -25,45 +25,52 @@ export function build_server_nodes(out, kit, manifest_data, server_manifest, cli
2525
/** @type {Set<string>} */
2626
const client_stylesheets = new Set();
2727
for (const key in client_manifest) {
28-
const file = client_manifest[key];
29-
if (file.css?.[0]) {
30-
client_stylesheets.add(file.css[0]);
31-
}
28+
client_manifest[key].css?.forEach((filename) => {
29+
client_stylesheets.add(filename);
30+
});
3231
}
3332

34-
/** @type {Map<number, string>} */
33+
/** @type {Map<number, string[]>} */
3534
const server_stylesheets = new Map();
35+
manifest_data.nodes.forEach((node, i) => {
36+
if (!node.component || !server_manifest[node.component]) return;
3637

37-
const component_stylesheet_map = new Map(Object.values(server_manifest).map((file) => [file.src, file.css?.[0]]));
38+
const { stylesheets } = find_deps(server_manifest, node.component, false);
3839

39-
manifest_data.nodes.forEach((node, i) => {
40-
const server_stylesheet = component_stylesheet_map.get(node.component);
41-
if (node.component && server_stylesheet) {
42-
server_stylesheets.set(i, server_stylesheet);
40+
if (stylesheets.length) {
41+
server_stylesheets.set(i, stylesheets);
4342
}
4443
});
4544

46-
// ignore dynamically imported stylesheets since we can't inline those
47-
css.filter(asset => client_stylesheets.has(asset.fileName))
48-
.forEach((asset) => {
49-
if (asset.source.length < kit.inlineStyleThreshold) {
50-
// We know that the names for entry points are numbers.
51-
const [index] = basename(asset.fileName).split('.');
52-
// There can also be other CSS files from shared components
53-
// for example, which we need to ignore here.
54-
if (isNaN(+index)) return;
55-
56-
const server_stylesheet = server_stylesheets.get(+index);
57-
const file = `${out}/server/stylesheets/${index}.js`;
58-
59-
// we need to inline the server stylesheet instead of the client one
60-
// so that asset paths are correct on document load
61-
const source = fs.readFileSync(`${out}/server/${server_stylesheet}`, 'utf-8');
62-
63-
fs.writeFileSync(file, `// ${server_stylesheet}\nexport default ${s(source)};`);
64-
stylesheet_lookup.set(asset.fileName, index);
65-
}
45+
for (const asset of css) {
46+
// ignore dynamically imported stylesheets since we don't need to inline those
47+
if (!client_stylesheets.has(asset.fileName) || asset.source.length >= kit.inlineStyleThreshold) {
48+
continue;
49+
}
50+
51+
// We know that the names for entry points are numbers.
52+
const [index] = basename(asset.fileName).split('.');
53+
// There can also be other CSS files from shared components
54+
// for example, which we need to ignore here.
55+
if (isNaN(+index)) continue;
56+
57+
const file = `${out}/server/stylesheets/${index}.js`;
58+
59+
// we need to inline the server stylesheet instead of the client one
60+
// so that asset paths are correct on document load
61+
const filenames = server_stylesheets.get(+index);
62+
63+
if (!filenames) {
64+
throw new Error('This should never happen, but if it does, it means we failed to find the server stylesheet for a node.');
65+
}
66+
67+
const sources = filenames.map((filename) => {
68+
return fs.readFileSync(`${out}/server/${filename}`, 'utf-8');
6669
});
70+
fs.writeFileSync(file, `// ${filenames.join(', ')}\nexport default ${s(sources.join('\n'))};`);
71+
72+
stylesheet_lookup.set(asset.fileName, index);
73+
}
6774
}
6875

6976
manifest_data.nodes.forEach((node, i) => {
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
// test that the server-client stylesheet map is constructed correctly when
2+
// a page is imported. Importing a page causes Vite to associate the css with
3+
// a separate chunk instead of the page component itself
4+
import.meta.glob('./import-meta/+page.svelte', { eager: true });
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<div>this page is being imported so its css is associated with a separate chunk</div>
2+
3+
<style>
4+
div {
5+
color: pink;
6+
}
7+
</style>

0 commit comments

Comments
 (0)