Closes#18447
---------
Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com>
Co-authored-by: Simon Holthausen <simon.holthausen@vercel.com>
`<p>{text}</p>` and friends compile to `var x = $.child(p, true);
$.reset(p);`. Across the 4,020 components in `packages/svelte/tests`,
that exact pair occurs 1,885 times, more than any other adjacent
statement pair.
`$.only_child` does both, reducing the number of statements in the compiled output
Move the "single comment, we can make this faster" optimization into `transform_template` so all usage locations can benefit (e.g. select/option elements)
---------
Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com>
Emits less code and skips two live DOM traversals per instance.
`is_static_element` treats `<img>` with a `loading` attribute as
non-static. That was added in #14237 so `RegularElement` could emit
`$.handle_lazy_img(node_id)`, which needed a reference to the element.
#15272 deleted `handle_lazy_img` (replaced by `importNode`) and removed
the call site, but left this check behind.
---------
Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com>
Instead of checking the given nodes at startup and only injecting those
into the inner Svelte component constructor, we are now detecting (via
the `$$host` property) whether or not the component is rendered as a
custom element, and in that case create all slots as real slots right
away.
Fixes#13638Fixes#8997 (therefore closes#8999)
Per the WHATWG URL spec, assigning a new protocol can clear the URL's port when the current port equals the new scheme's default port. Therefore also `set` the port when the protocol is updated.
radio buttons were missing in the type check - remove the check instead (aligns with how we do it in the compiler, see `has_default_value_attribute`). Strictly speaking not fully correct but pragmatic solution that doesn't have any impact in practise
Needed by sveltejs/kit#16926.
Fixes#18414 and a second bug with the same cause.
`update_reaction` in `runtime.js` commits a reaction's dependencies
after `fn()` returns: it swaps in `new_deps`, removes stale reactions
and registers the reaction into each new dep's `reactions`. When `fn()`
throws, the whole block is skipped. Two things go wrong from that.
A derived that the run read for the first time was already set
`CONNECTED` in `get` and registered itself into its own deps'
`reactions` during `update_derived`, but the throwing reaction never
registers as its reader. It sits in its deps' `reactions` with
`reactions === null` of its own, `remove_reaction`'s disconnect cascade
can never reach it, and it retains its `ctx`, closures and DOM past
component destruction and `unmount()`.
A derived that throws during its own run ends up with no dependencies at
all. It is not in its sources' `reactions`, so a later change to them
never re-runs it, and for an unowned derived `update_derived` then marks
the error value CLEAN because the derived has no deps. Anything reading
it keeps getting the first error forever.
The commit block is now `update_dependencies` and the `catch` calls it
before `handle_error`, so a failing reaction keeps the deps it read up
to the throw. That subscribes it to the inputs that produced the error,
and lets the normal disconnect cascade clean up when it is destroyed
(`Boundary.#handle_error` destroys the failed effect).
Co-authored-by: Randy Murphy <randalmurphal@users.noreply.github.com>
fix#16404
Currently one file can have multiple exports but only one `cssScopeTo` variable, which is hard coded to `default` inside vite-plugin-svelte.
Therefore set hasGlobal to `true` in case we export a snippet to not have this scoping enabled in v-p-s
---------
Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com>
Co-authored-by: Simon Holthausen <simon.holthausen@vercel.com>
When an async expression resumes after a pickled `await`, the thunk
returned by `save()` in `reactivity/async.js` calls `restore()` to
re-arm `active_reaction` for the rest of the expression, then disarms it
with `queue_micro_task(unset_context)`. Any microtask already queued
before that one runs inside the restored context. If it writes to a
source, `set()` throws `state_unsafe_mutation` in production, since the
guard is not dev-only. #18453 introduced the queued disarm and noted
this case in review as unavoidable. SvelteKit hits it in practice: its
fetch continuations write to internal `$state` (sveltejs/kit#16914), and
a user's `$derived((await q()).length)` resuming in the same tick makes
that write throw and drops the update signal.
The context restored by a `save` thunk now ends with the synchronous
segment it was restored in. A `restored` flag is set by the thunk and
consumed on entry to `save` and `track_reactivity_loss`, so every
suspension ends it; once an expression contains a pickled await, the
analysis pickles every later await in it too (`has_pickled_await` on
`ExpressionMetadata`), so a trailing await compiles to `$.save` rather
than a bare `await`. At the end of the body, `async_thunk` in
`3-transform/client/utils.js` wraps the return expression in
`$.unsave(...)` when the metadata has a pickled await. If the body
throws instead, the context is unset by `async_derived`'s existing
`finally`, as before. The queued microtask in `save` is removed.
Output is unchanged for expressions that pickle nothing (`$derived(await
a)` compiles byte for byte the same). Expressions with a pickled await
gain one `$.unsave(` call per body, and their trailing await becomes a
`$.save`, 4 to 6 bytes gzipped in the added tests. At runtime a boolean
write replaces a queued microtask per resume. `bench:compare` shows no
difference outside run-to-run noise.
Two runtime tests reproduce the throw without any library involved, one
in dev and one with the prod `await` shape, and fail on `main`.
---------
Co-authored-by: Simon Holthausen <simon.holthausen@vercel.com>
Read the Date value through `getTime()` before cloning it to get reactive updates from `SvelteDate`.
Closes#18698
---------
Co-authored-by: svelte-triage-bot <team@svelte.com>
Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com>
include client-created <svelte:head> anchors in the head effect’s DOM range
remove anchors through the existing HEAD_EFFECT teardown
add client and hydration regression coverage for repeated mount/unmount cycles
Fixes#18695
---------
Co-authored-by: svelte-triage-bot <team@svelte.com>
Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com>
closes#15992
This is a minor update to the easing documents. It adds an overview
description for the module and short descriptions for each of the easing
functions.
---------
Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com>
Co-authored-by: vercel[bot] <35613825+vercel[bot]@users.noreply.github.com>
Co-authored-by: Simon Holthausen <simon.holthausen@vercel.com>
skip elements inside head, they should not get hashes
---------
Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com>
Co-authored-by: Simon Holthausen <simon.holthausen@vercel.com>
This PR resolves a `TODO` in `ConstTag.js` regarding the optimization of
simple object pattern matching cases like `{@const { x } = y}`.
---------
Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com>
Fixes#15604
Given two nested blocks that both transition:
```svelte
{#if fetching}
<p>loading</p>
{:else}
<div transition:fade>
{#if shown}
<div class="red" transition:fade|global>red square</div>
{/if}
</div>
{/if}
```
and this sequence, where each step happens before the previous fade
finished:
```js
shown = false; // red square starts fading out
fetching = true; // outer block starts fading out too
fetching = false; // 50ms later
```
the red square should finish fading out and be removed, since `shown` is
still false. Instead it fades back in and stays on screen: resuming the
outer block walks the whole subtree, clears `INERT` on every effect and
plays `in()` on every transition it finds — including the inner block's,
so the square's outro is aborted and the removal callback waiting on it
never runs. The inner block has no reason to re-run on its own, `shown`
never changed again.
The root issue is that `INERT` records no ownership — it can't
distinguish "paused by the ancestor currently being resumed" (revive)
from "paused by its own block for its own reasons" (leave alone). The
pause side already has this restraint: `pause_children` refuses to touch
a subtree that is already `INERT`. The resume side had nothing to check.
This PR marks the one effect `pause_effect` was actually called on — the
root of the paused subtree — with a `PAUSED` flag, and gives resume the
same restraint:
```js
function resume_children(effect, local) {
if ((effect.f & PAUSED) !== 0) return;
```
so a resume can only ever undo its own pause; the flag is only cleared
by `resume_effect` on that exact effect. If `shown` flips back to true
while the outer block is paused, this still works: the inner block
effect carries no `PAUSED` itself, so it is resumed and rescheduled,
re-evaluates its condition and revives its own branch.
Fixes#18683.
The printer previously trimmed leading whitespace after an inline
element and failed to replace it when the fragment stayed on one line.
Track that whitespace while grouping fragment nodes and emit a space for
inline output, while retaining newline-based separation for multiline
output.
Adds regression coverage for whitespace following an inline element and
updates existing print snapshots that exposed the same behavior.
Fixes#14205
When a `slide` transition runs on an element without a layout box, for example inside a `display: none` parent, `getComputedStyle` returns values like `auto` for the animated dimensions. These were passed through `parseFloat` unguarded, so the generated css contained `height: NaNpx` and the browser rejected the keyframe property with an "Invalid keyframe value" warning on every transition.
Fix it by omitting properties if they have unparseable numbers - it's the same outcome but you don't see a warning.
---------
Co-authored-by: Simon Holthausen <simon.holthausen@vercel.com>
Fix#18465
The style and classe directives are generated using an unique memoizer for all the class/style directive, which means if you have multiple of the same time on one element you have overly broad invalidation/reruns. This fixes it by having one memoizer call per directive instead of collecting them.
---------
Co-authored-by: Simon Holthausen <simon.holthausen@vercel.com>
Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com>
Fixes#18485
Two parts to this:
1. explicitly invoke error boundary during teardown errors, else they go missing/bubble up outside the render tree
2. skip destroying/destroyed boundaries will searching a handler
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Simon Holthausen <simon.holthausen@vercel.com>
Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com>
Basically, in a situation like this
```svelte
<script lang="ts">
let value = $state('A');
</script>
<input oninput={() => {}} />
<select bind:value>
<button><selectedcontent></selectedcontent></button>
<option>A</option>
<option>B</option>
<option>C</option>
</select>
<style>
select,::picker(select){
appearance: base-select;
}
</style>
```
what happens is that the `oninput` is delegated and so it registers the
global listener (which means it listen on every `oninput` not just the
one from the input). When the select change, the `input` event is
dispatched first, the listener runs, doesn't find an `__input` handler
and returns. Now before the `change` event is emitted, the
`MutationObserver` in `init_select` is triggered by the browser updating
`selectedcontent` and invokes `select_option` with `select.__value`.
However, since the `change` event has yet to fire, `select.__value`
still has the old value, so we "reselect" that. When the change event
runs, the selected option is effectively the old one and the whole thing
breaks.
I had to add the test in `runtime-browser` because JSDom doesn't support
`selectedcontent`
Fixes#18584.
There's multiple parts to this
- abort signal was buggy. It wasn't scoped per render, so cross-talk was possible. Fix by scoping to renderer
- onDestroy callbacks were skipped when something throws. Fix by carefully aborting the rest of the tree, waiting for settle, collect all callbacks and then call them
---------
Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com>
Co-authored-by: Simon Holthausen <simon.holthausen@vercel.com>
Blockers didn't include analyzing implicit store subscriptions, which could also only happen in the template.
Also needs to defer store unsubscribe until after the async template has settled in case the store value is read after an async blocker, in which case unsubscribe synchronously is too soon.
Fixes https://github.com/sveltejs/kit/issues/15119
Fixes#18416.
We previously said that we don't want to handle component instances specifically when they're wrapped with state in #16747 - though the use case presented back then was much more arcane than the one in #18416. Therefore we now don't proxify component instances anymore, which also makes the dev time proxy warning obsolete.
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Simon Holthausen <simon.holthausen@vercel.com>
Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com>
Each `<svelte:boundary>` with:
```svelte
{#snippet failed(error)}
...
{/snippet}
```
generated a function named failed. Sibling boundaries placed both
functions in the same SSR scope:
```js
{
function failed() {}
function failed() {} // Identifier `failed` has already been declared
}
```
Resulting in a `[PARSE_ERROR] Identifier `failed` has already been
declared` error during build time.
A let declaration such as `{const x = 0}` can create a nested lexical
block, exposing the collision.
The fix gives each boundary its own scope:
```js
{
function failed() {}
$$renderer.boundary({ failed }, ...);
}
{
function failed() {}
$$renderer.boundary({ failed }, ...);
}
``
`render` returns `RenderOutput` and takes `csp: Csp`, but both are
declared in `src/internal/server/types.d.ts` and never exported. The
generated `svelte/server` block has them without `export`, so nothing
outside the repo can import them and the docs page names `RenderOutput`
as the return type with nothing on the page defining it.
Moves them into `src/server/public.d.ts` the way `svelte/motion` went in
#17967, rather than re-exporting internals.
Fixes#18440
`hmr()` in `packages/svelte/src/internal/client/dev/hmr.js` wrapped a
component in a `block` effect containing a `branch` effect. It forwarded
the inner effect's `nodes` object to the outer block:
This shared the same reference, meaning `outer_block.nodes ===
inner_branch.nodes`. When `pause_children` collected transitions for
unmounting, Each collected transition had `out(check)` called multiple times,
starting multiple animations and firing `outroend` multiple times.
Fix by only copying `start`/`end` (the DOM range info the outer block needs)
without sharing the transitions array:
`trace_references` recreated a fresh seen set for every CallExpression,
so `touch` re-walked the same transitive assignment graph once per call.
For N calls reaching an N-deep binding chain, this was $O(N^2)$.
Share one seen set per trace_references invocation. Use separate sets
for the write-directed CallExpression touches and the read-directed
ReturnStatement touches. The shared set ensures each assignment-value
expression is walked at most once, making the traversal linear. `touch`
only ever adds to a fixed target set, so skipping an already-seen
expression never drops a binding: compiler output is byte-identical.
Complements #18548.
---------
Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com>
`to_style()` strips CSS comments from an inline `style` value with
`/\s*\/\*.*?\*\/\s*/g`. The leading `\s*` makes the match retry from
every position, so a long run of whitespace backtracks in O(n^2). When a
dynamic `style={value}` sits on an element that also has a `style:`
directive, that regex runs on `value`, so a large whitespace string can
stall rendering (server) or the main thread (client).
### Fix
Drop the surrounding `\s*` and match only the comment: `/\/\*.*?\*\//g` to prevent quadratic regex.
The surrounding whitespace was already removed by the `.trim()`
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com>
Fixes#18568
`last_propagated_event` was added in #16527 to stop Firefox from garbage collecting the event wrapper mid-propagation. The problem with this is that the even is now retained until the next event, which could be a while. This removes it after a macrotask.
Co-authored-by: rmurphy <rmurphy@fortressinfosec.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Fixes#7916. Context lookup starts at the current component, not the
closest parent. `setContext` followed by `getContext` with the same key
in the same component returns the value.
---------
Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com>
The server copy of `createContext` shipped without the `missing_context`
throw and #17580 had to hand-mirror the client body back in, so this
duplication has already cost a bug. This moves the realm-independent
parts, the `createContext` tuple, `get_parent_context`, and
`get_or_init_context_map`, into `internal/shared/context.js`, and each
realm keeps its public context functions as thin wrappers over its own
state.
Fixes#18609
A `<an+b> of <selector>` CSS statement does not need whitespace after `of` if it's followed by a CSS-known syntax (like a `.` which marks a class identifier). Adjust the regex accordingly.
Fixes#18612.
A concise arrow body does not contain a `ReturnStatement` which we traverse in full calculate_blockers (else we bail on functions), so short-cut to `touch` there.
---------
Co-authored-by: Simon Holthausen <simon.holthausen@vercel.com>
Fixes#18288
Check the surrounding branch effect's start node to retrieve the correct root node instead of just the anchor, since the latter could come from each.js/branch.js and be a text node that is never going to get connected
---------
Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com>
Co-authored-by: Simon Holthausen <simon.holthausen@vercel.com>
Fixes#18404
`<svelte:head>` combined with two or more components that each use `bind:` on a child component's prop corrupts a renderer's `.type` during SSR, misplacing the head hydration marker in `<body>` instead of `<head>`.
Reason: `copy()` (`packages/svelte/src/internal/server/renderer.js`) rebuilds via `new Renderer(this.global, this.#parent)`. That constructor defaults `.type` from the *parent's* current type, not from `this.type`. To fix it we therefore reassign it (like already do for the boundary elsewhere)