Skip to content

fix: correctly map shared entry-point CSS files during inlining #13431

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Feb 25, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/ten-planes-relax.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@sveltejs/kit': patch
---

fix: correctly find shared entry-point CSS files during inlining
67 changes: 37 additions & 30 deletions packages/kit/src/exports/vite/build/build_server.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,45 +25,52 @@ export function build_server_nodes(out, kit, manifest_data, server_manifest, cli
/** @type {Set<string>} */
const client_stylesheets = new Set();
for (const key in client_manifest) {
const file = client_manifest[key];
if (file.css?.[0]) {
client_stylesheets.add(file.css[0]);
}
client_manifest[key].css?.forEach((filename) => {
client_stylesheets.add(filename);
});
}

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

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

manifest_data.nodes.forEach((node, i) => {
const server_stylesheet = component_stylesheet_map.get(node.component);
if (node.component && server_stylesheet) {
server_stylesheets.set(i, server_stylesheet);
if (stylesheets.length) {
server_stylesheets.set(i, stylesheets);
}
});

// ignore dynamically imported stylesheets since we can't inline those
css.filter(asset => client_stylesheets.has(asset.fileName))
.forEach((asset) => {
if (asset.source.length < kit.inlineStyleThreshold) {
// We know that the names for entry points are numbers.
const [index] = basename(asset.fileName).split('.');
// There can also be other CSS files from shared components
// for example, which we need to ignore here.
if (isNaN(+index)) return;

const server_stylesheet = server_stylesheets.get(+index);
const file = `${out}/server/stylesheets/${index}.js`;

// we need to inline the server stylesheet instead of the client one
// so that asset paths are correct on document load
const source = fs.readFileSync(`${out}/server/${server_stylesheet}`, 'utf-8');

fs.writeFileSync(file, `// ${server_stylesheet}\nexport default ${s(source)};`);
stylesheet_lookup.set(asset.fileName, index);
}
for (const asset of css) {
// ignore dynamically imported stylesheets since we don't need to inline those
if (!client_stylesheets.has(asset.fileName) || asset.source.length >= kit.inlineStyleThreshold) {
continue;
}

// We know that the names for entry points are numbers.
const [index] = basename(asset.fileName).split('.');
// There can also be other CSS files from shared components
// for example, which we need to ignore here.
if (isNaN(+index)) continue;

const file = `${out}/server/stylesheets/${index}.js`;

// we need to inline the server stylesheet instead of the client one
// so that asset paths are correct on document load
const filenames = server_stylesheets.get(+index);

if (!filenames) {
throw new Error('This should never happen, but if it does, it means we failed to find the server stylesheet for a node.');
}

const sources = filenames.map((filename) => {
return fs.readFileSync(`${out}/server/${filename}`, 'utf-8');
});
fs.writeFileSync(file, `// ${filenames.join(', ')}\nexport default ${s(sources.join('\n'))};`);

stylesheet_lookup.set(asset.fileName, index);
}
}

manifest_data.nodes.forEach((node, i) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// test that the server-client stylesheet map is constructed correctly when
// a page is imported. Importing a page causes Vite to associate the css with
// a separate chunk instead of the page component itself
import.meta.glob('./import-meta/+page.svelte', { eager: true });
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<div>this page is being imported so its css is associated with a separate chunk</div>

<style>
div {
color: pink;
}
</style>
Loading