Skip to content

Commit 37f72fb

Browse files
authored
fix: inline server stylesheets instead of client stylesheets (#13068)
fixes #6720 This PR changes the inlined stylesheet from client to server so that the paths for imported assets in the CSS such as fonts are correct when the document first loads. In comparison, the client stylesheet links are relative by default, so they always link to the wrong place
1 parent e541a40 commit 37f72fb

File tree

6 files changed

+77
-6
lines changed

6 files changed

+77
-6
lines changed

.changeset/sixty-days-think.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@sveltejs/kit': patch
3+
---
4+
5+
fix: correctly link to assets inlined by the `inlineStyleThreshold` option

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

+36-6
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { mkdirp } from '../../../utils/filesystem.js';
33
import { find_deps, resolve_symlinks } from './utils.js';
44
import { s } from '../../../utils/misc.js';
55
import { normalizePath } from 'vite';
6+
import { basename } from 'node:path';
67

78
/**
89
* @param {string} out
@@ -17,18 +18,47 @@ export function build_server_nodes(out, kit, manifest_data, server_manifest, cli
1718
mkdirp(`${out}/server/nodes`);
1819
mkdirp(`${out}/server/stylesheets`);
1920

21+
/** @type {Map<string, string>} */
2022
const stylesheet_lookup = new Map();
2123

2224
if (css) {
23-
css.forEach((asset) => {
24-
if (asset.source.length < kit.inlineStyleThreshold) {
25-
const index = stylesheet_lookup.size;
26-
const file = `${out}/server/stylesheets/${index}.js`;
25+
/** @type {Set<string>} */
26+
const client_stylesheets = new Set();
27+
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+
}
32+
}
33+
34+
/** @type {Map<number, string>} */
35+
const server_stylesheets = new Map();
2736

28-
fs.writeFileSync(file, `// ${asset.fileName}\nexport default ${s(asset.source)};`);
29-
stylesheet_lookup.set(asset.fileName, index);
37+
const component_stylesheet_map = new Map(Object.values(server_manifest).map((file) => [file.src, file.css?.[0]]));
38+
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);
3043
}
3144
});
45+
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+
const [index] = basename(asset.fileName).split('.');
51+
const server_stylesheet = server_stylesheets.get(+index);
52+
const file = `${out}/server/stylesheets/${index}.js`;
53+
54+
// we need to inline the server stylesheet instead of the client one
55+
// so that asset paths are correct on document load
56+
const source = fs.readFileSync(`${out}/server/${server_stylesheet}`, 'utf-8');
57+
58+
fs.writeFileSync(file, `// ${server_stylesheet}\nexport default ${s(source)};`);
59+
stylesheet_lookup.set(asset.fileName, index);
60+
}
61+
});
3262
}
3363

3464
manifest_data.nodes.forEach((node, i) => {

packages/kit/test/apps/options/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
"test:build": "playwright test"
1414
},
1515
"devDependencies": {
16+
"@fontsource/libre-barcode-128-text": "^5.1.0",
1617
"@sveltejs/kit": "workspace:^",
1718
"@sveltejs/vite-plugin-svelte": "^5.0.1",
1819
"cross-env": "^7.0.3",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<script>
2+
import '@fontsource/libre-barcode-128-text';
3+
</script>
4+
5+
<p>Hello world!</p>
6+
7+
<style>
8+
p {
9+
font-family: 'Libre Barcode 128 Text', sans-serif;
10+
}
11+
</style>

packages/kit/test/apps/options/test/test.js

+16
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,22 @@ if (!process.env.DEV) {
303303
expect(await page.content()).not.toMatch('navigator.serviceWorker');
304304
});
305305
});
306+
307+
test.describe('inlineStyleThreshold', () => {
308+
test('loads asset', async ({ page }) => {
309+
let fontLoaded = false;
310+
311+
page.on('response', (response) => {
312+
if (response.url().endsWith('.woff2') || response.url().endsWith('.woff')) {
313+
fontLoaded = response.ok();
314+
}
315+
});
316+
317+
await page.goto('/path-base/inline-assets');
318+
319+
expect(fontLoaded).toBeTruthy();
320+
});
321+
});
306322
}
307323

308324
test.describe('Vite options', () => {

pnpm-lock.yaml

+8
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)