kit
kit copied to clipboard
Vite plugin has missing CSS
Describe the bug
Not sure what I'm doing wrong here, but I have a Vite plugin that uses the svelte.compile
API to generate a component, which all works fine serverside and clientside, except that on the serverside the CSS is missing. If you don't hydrate or you turn off JS in the browser, the style never appears. Otherwise it does appear after a flash.
Reproduction
Repro:
git clone [email protected]:johnnysprinkles/repro.git
git checkout vite_plugin_missing_css
npm i
npm run build
npm run preview
(The code I added is all in one commit, https://github.com/johnnysprinkles/repro/commit/b559335780314f94eb337251f75614d1e26a034b)
I added a "star" component that has some styled SVG, normally this would be an import but for a minimal test case I have it hard coded. If you go to http://localhost:3000/about you should see small red stars but you see huge black ones. On the index page which does hydrate, the stars look correct after a flash.
This is a real head scratcher. I can see the output of the compile()
call and it looks like it's doing the right thing for CSS. I see const css = {...}
and $$result.css.add(css);
in there, and I can even go into .svelte-kit/output/server/entries/pages/index.svelte.js
and see the $$result.css.add(css$1)
call there and console.log() the whole $$result.css
which is a Set() with several items including the CSS for this star in there.
Index page with correct CSS after hydration:
About page with missing CSS:
Logs
No response
System Info
System:
OS: macOS 12.1
CPU: (8) arm64 Apple M1
Memory: 163.56 MB / 8.00 GB
Shell: 5.8 - /bin/zsh
Binaries:
Node: 16.13.1 - ~/.nvm/versions/node/v16.13.1/bin/node
npm: 8.5.2 - ~/.nvm/versions/node/v16.13.1/bin/npm
Browsers:
Chrome: 102.0.5005.61
Firefox: 100.0
Safari: 15.2
npmPackages:
@sveltejs/adapter-auto: next => 1.0.0-next.50
@sveltejs/kit: next => 1.0.0-next.347
svelte: ^3.46.0 => 3.48.0
Severity
serious, but I can work around it
Additional Information
No response
css: true
adds the css via javascript. So if you disable js, that doesn't work.
May i ask why you opt to use a custom plugin calling svelte.compile yourself instead of leveraging vite-plugin-svelte with
Star.svg.svelte
<svelte:options namespace="svg"/>
<svg>...</svg>
<style>...</style>
or unplugin-icons with custom icons? https://github.com/antfu/unplugin-icons#custom-icons
Sure I can provide more detail, that hard coded stuff was just for the minimal repro case.
We have thousands of icons defined by JSON structures, each struct has like "path d" attribute, translateX, translateY, viewBox, etc. I'm transforming those into styleable SVG.
The real code is this, verbatim:
let code = `
<svg viewBox="${viewBox}" {...$$props}>
<path d="${svgPath}"></path>
</svg>
<style>
svg {
display: block;
width: var(--icon-size, 24px);
height: var(--icon-size, 24px);
fill: var(--icon-color);
}
svg:hover {
fill: var(--icon-color-hover);
}
</style>
`;
I think using css: true
to add via javascript is exactly what I'd want. For ssr, that code runs server-side, there is no "disabling javascript" in that context. Know what I mean? If I dig around in the outputted code, the stuff for generate: dom
looks like it has append_styles()
calls which is for the clientside, but the stuff for generate: ssr
has the $$result.css.add()
calls which should run server-side and emit CSS to be accumulated.
Digging around the source code, my guess is that SvelteKit's manifest wasn't able to detect the component's CSS to inline it into the initial html response. I'm not sure where exactly is the bug, or maybe the Vite plugin should do something different, but it's also worth trying to have the Vite plugin leverage vite-plugin-svelte by loading the SVG code in the load
hook, and let vite-plugin-svelte
's transform
hook do the rest.
That would actually be ideal, I just wasn't sure how to make my plugin run before vite-plugin-svelte. Let me play around with enforce: pre
.
Also I wonder if #5138 is related.
Did enforce: pre
work? vite-plugin-svelte
extracts the CSS into a separate .css
module which Vite knows how to extract for SSR; any replacement plugin would need to jump through the same hoops, so getting the two plugins to work together would be ideal.
@johnnysprinkles let us know if we're ok to close this issue — thanks
Sorry for the long delay here. I guess what I was trying to do is make an alternative "vite-plugin-svelte" by doing the compile() call myself, but it's missing a couple essential steps. To get the CSS in there for SSR I'd need to do the equivalent of this line:
https://github.com/sveltejs/vite-plugin-svelte/blob/4de85681b9e68cde16ae27ebf0a9b7322f635e11/packages/vite-plugin-svelte/src/utils/compile.ts#L74
So a two step transform sounds a lot better. 1) SVG transforms to SvelteScript, and 2) SvelteScript to JS via vite-plugin-svelte. This should be easier now that the plugins are exposed in vite.config.js, just preprend one like this:
- plugins: [sveltekit()],
+ plugins: [sfSymbols(), sveltekit()],
However I can't seem to figure out the rollup plugin mechanism. I'd need the first plugin to rename the ID to something ending in ".svelte" so the sveltekit() plugin picks it up. I bet @Rich-Harris could tell me in two seconds how to do that.
So, I don't know. It would be pretty slick to import one of the 5000 JSON files representing each of our SF Symbols, and have it automatically turn into a Svelte component. But might not be worth the trouble. I just made a general component and pass the data in as a prop, no plugin needed.
Let's close this as obsolete, I can't remember all the details.