Background
PR #968 bumped ui/dashboard from Svelte v4 to Svelte v5 at the minimum-to-compile depth — closes 12 Dependabot alerts for the svelte/kit/devalue CVE cluster. To keep the diff surgical and security-driven, the migration uses Svelte 5's legacy/compat mode for every component. No runes-mode conversion was performed.
This issue tracks the deferred cleanup work so the deprecation warnings don't get lost in production console logs.
Scope
88 .svelte components under ui/dashboard/src/ still use Svelte 4 syntax in legacy mode:
| Pattern |
Svelte 5 runes equivalent |
export let foo |
let { foo } = $props() |
$: derived = expr |
const derived = $derived(expr) |
$: { sideEffect() } |
$effect(() => { sideEffect() }) |
on:click={fn} |
onclick={fn} |
<slot /> / <slot name="x" /> |
{@render children?.()} / {@render x?.()} |
$$slots, $$props, $$restProps |
$props() destructure |
<svelte:component this={X}> |
Direct dynamic component (<X> works in v5) |
Reactive store auto-subscribe ($store) |
Still works in v5; lower priority |
Plus implicit removals:
createEventDispatcher → callback props
- Component instantiation API changes (already required
mount/hydrate in some entry points)
Why not now
- Each pattern conversion touches semantics, not just syntax — a single bad migration can break interactivity in ways that don't surface at build time, only at runtime.
- 88 components × ~5 syntax patterns = large diff with high regression risk.
- No CVE pressure: legacy mode is supported through Svelte 5's lifetime and the security patches are already applied.
Acceptance
- All components opt into runes mode (via
<svelte:options runes={true}> or equivalent global compiler option).
- No deprecation warnings emitted by
svelte-check or in the runtime console.
- Existing playwright integration tests pass.
- Manual smoke pass on home, network, viewer, settings, p2p, admin routes.
Suggested phases
- Global compiler flag flip — turn on
compilerOptions.runes: true in svelte.config.js, see how many files now warn or fail. Triage by depth.
- Leaf components first — props-only components with no reactive statements convert in 1-2 lines each.
- Stateful components —
$: to $derived / $effect, watch for side-effect ordering changes.
- Slots → snippets — biggest mechanical conversion, last because cross-component contract change.
Tooling: the sv migrate svelte-5 CLI helper is the canonical path (already available; was not used in #968 because it requires interactive consent and we wanted a small, mechanical diff).
References
Background
PR #968 bumped
ui/dashboardfrom Svelte v4 to Svelte v5 at the minimum-to-compile depth — closes 12 Dependabot alerts for the svelte/kit/devalue CVE cluster. To keep the diff surgical and security-driven, the migration uses Svelte 5's legacy/compat mode for every component. No runes-mode conversion was performed.This issue tracks the deferred cleanup work so the deprecation warnings don't get lost in production console logs.
Scope
88
.sveltecomponents underui/dashboard/src/still use Svelte 4 syntax in legacy mode:export let foolet { foo } = $props()$: derived = exprconst derived = $derived(expr)$: { sideEffect() }$effect(() => { sideEffect() })on:click={fn}onclick={fn}<slot />/<slot name="x" />{@render children?.()}/{@render x?.()}$$slots,$$props,$$restProps$props()destructure<svelte:component this={X}><X>works in v5)$store)Plus implicit removals:
createEventDispatcher→ callback propsmount/hydratein some entry points)Why not now
Acceptance
<svelte:options runes={true}>or equivalent global compiler option).svelte-checkor in the runtime console.Suggested phases
compilerOptions.runes: trueinsvelte.config.js, see how many files now warn or fail. Triage by depth.$:to$derived/$effect, watch for side-effect ordering changes.Tooling: the
sv migrate svelte-5CLI helper is the canonical path (already available; was not used in #968 because it requires interactive consent and we wanted a small, mechanical diff).References
docs/superpowers/specs/2026-05-21-vuln-triage/report.md(uncommitted, internal)