Repository navigation
Commit 6f50993
fix(svelte): bundle compiled CSS for island pages and their layouts
Bug: `vp build` / `void deploy` of a Svelte pages app fails when an
`.island.svelte` page, or any layout in its chain (plain `layout.svelte`
included), has a `<style>` block. The build stops with
`[plugin vite-plugin-svelte:compile] .../pages/layout.svelte?island-style&index=0&lang.css ... Expected token }`.
`vp dev` does not use this path, so it looks fine there.
Repro: in playground/svelte-pages add `<style>nav { padding: 8px; }</style>`
to pages/layout.svelte (or a `<style>` block to
pages/blog/index.island.svelte) and run `vp build`.
Cause: generateIslandClientEntry in packages/svelte/src/plugin-islands.ts
imported each raw `<style>` body as `<file>.svelte?island-style&index=N&lang.css`.
That id still matches vite-plugin-svelte's `.svelte(?|#|$)` transform
filter, so the CSS text was compiled as a Svelte component and the
compiler failed on the first `{`. Even without that, the CSS was the raw
unscoped source: it would not match the `svelte-xxxx` classes in the SSR
markup, and `:global(...)` would ship as an invalid selector. When every
page is an island page, this entry is the only CSS source in production.
Fix: the client entry now imports one `<file>.svelte.css?island-style`
per component that has a `<style>` (Svelte allows one top-level style).
The build-only loader reads the component file and compiles it with
vite-plugin-svelte's own API (taken from its `vite-plugin-svelte:config`
plugin): the app's `preprocess` runs first, then `compileSvelte` with the
resolved compiler options. It returns only the compiled CSS, so the scope
hashes match the SSR markup.
The component is compiled outside the module graph on purpose. Island
pages and their layouts render only on the server, so they may import
server-only code (`void/durable`, packages with only `node`/`workerd`
exports). Loading them with `this.load` would resolve those imports in
the client build and fail it. No island page JS enters the client bundle.
The id keeps the component's directory, so CSS url() resolves as before,
and it no longer matches the `.svelte?` filter.
vite-plugin-svelte's own `?svelte&type=style` id is not reused: it only
reads CSS already cached on the module, and its cssScopeTo drops the CSS
when the component is not imported in the client build.
Files the preprocessor reads, such as Sass partials, go to
`this.addWatchFile`, as in vite-plugin-svelte's preprocess plugin, so
`vite build --watch` rebuilds the island CSS when they change.
Test: packages/svelte/test/unit/plugin-islands.test.ts, "bundles the
scoped CSS of island pages and their layouts". The styled island page
and layout both import a package with only `node`/`workerd` exports. It
fails without the fix with the same `Expected token }` errors, and with a
`this.load` based loader with `"." is not exported under the conditions`.
"watches files the style preprocessor reads" checks that a style
preprocessor's `dependencies` are watch files; without the loop, only
the component file is watched.
Note: the style loader uses vite-plugin-svelte's internal plugin API,
which is not in its public types. If `compileSvelte` or `idParser` is
missing there, the build stops and asks for the
@sveltejs/vite-plugin-svelte version that @void/svelte depends on. A
changed call signature (as between 7.2 and 7.3) still fails the build,
with a TypeError. "bundles the scoped CSS of island pages and their
layouts" runs a real build against the locked version, so a lockfile
bump that changes this API fails it.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0123r5HnanmW2VUrZ7VWDufH1 parent fd7837c commit 6f50993
2 files changed
Lines changed: 156 additions & 42 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
| 3 | + | |
3 | 4 | | |
4 | 5 | | |
5 | 6 | | |
| |||
31 | 32 | | |
32 | 33 | | |
33 | 34 | | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
34 | 48 | | |
35 | 49 | | |
36 | 50 | | |
| |||
238 | 252 | | |
239 | 253 | | |
240 | 254 | | |
241 | | - | |
242 | | - | |
243 | | - | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
244 | 267 | | |
245 | 268 | | |
246 | 269 | | |
247 | | - | |
| 270 | + | |
248 | 271 | | |
249 | 272 | | |
250 | 273 | | |
251 | 274 | | |
252 | 275 | | |
253 | 276 | | |
254 | | - | |
255 | | - | |
256 | | - | |
257 | | - | |
258 | | - | |
259 | | - | |
260 | | - | |
261 | | - | |
262 | | - | |
263 | | - | |
264 | | - | |
265 | | - | |
266 | | - | |
| 277 | + | |
| 278 | + | |
| 279 | + | |
| 280 | + | |
| 281 | + | |
| 282 | + | |
| 283 | + | |
| 284 | + | |
| 285 | + | |
| 286 | + | |
| 287 | + | |
| 288 | + | |
| 289 | + | |
| 290 | + | |
| 291 | + | |
| 292 | + | |
| 293 | + | |
| 294 | + | |
| 295 | + | |
| 296 | + | |
| 297 | + | |
| 298 | + | |
267 | 299 | | |
268 | | - | |
269 | 300 | | |
270 | | - | |
| 301 | + | |
| 302 | + | |
| 303 | + | |
| 304 | + | |
| 305 | + | |
| 306 | + | |
| 307 | + | |
| 308 | + | |
271 | 309 | | |
272 | 310 | | |
273 | 311 | | |
| |||
371 | 409 | | |
372 | 410 | | |
373 | 411 | | |
374 | | - | |
375 | | - | |
376 | | - | |
377 | | - | |
378 | | - | |
379 | | - | |
380 | | - | |
381 | | - | |
382 | | - | |
383 | | - | |
384 | | - | |
385 | | - | |
| 412 | + | |
| 413 | + | |
| 414 | + | |
386 | 415 | | |
387 | 416 | | |
388 | 417 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
3 | 6 | | |
4 | 7 | | |
5 | 8 | | |
| |||
180 | 183 | | |
181 | 184 | | |
182 | 185 | | |
183 | | - | |
184 | | - | |
185 | | - | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
186 | 198 | | |
187 | | - | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
188 | 209 | | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
| 229 | + | |
| 230 | + | |
| 231 | + | |
| 232 | + | |
| 233 | + | |
| 234 | + | |
189 | 235 | | |
190 | | - | |
191 | | - | |
192 | | - | |
193 | | - | |
| 236 | + | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
194 | 241 | | |
195 | | - | |
196 | | - | |
197 | | - | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
| 269 | + | |
| 270 | + | |
| 271 | + | |
| 272 | + | |
| 273 | + | |
| 274 | + | |
| 275 | + | |
| 276 | + | |
| 277 | + | |
| 278 | + | |
| 279 | + | |
| 280 | + | |
| 281 | + | |
| 282 | + | |
198 | 283 | | |
199 | 284 | | |
0 commit comments