- A dirty disconnected derived registered newly reached dependencies
during evaluation, then `reconnect()` registered them again and retained
disposed graphs.
- The connection model from
[#17105](https://github.com/sveltejs/svelte/pull/17105) and reconnection
added in [#17362](https://github.com/sveltejs/svelte/pull/17362) restore
skipped old dependencies; recursive unfreezing from
[#17682](https://github.com/sveltejs/svelte/pull/17682) requires
preserving that lifecycle.
- Made reconnection idempotent instead of skipping or moving it,
preserving unchanged subscriptions and frozen-effect ordering.
- Covered direct and nested derived graphs, existing and newly reached
dependencies, and complete teardown across client rendering modes.
Fixes https://github.com/sveltejs/svelte/issues/18898
---------
Co-authored-by: svelte-triage-bot <team@svelte.com>
Fixes#15742
With `<svelte:document bind:activeElement />`, clicking a button that
removes itself throws `state_unsafe_mutation`. Removing the focused
element makes the browser fire `focusout` while the `{#if}` block effect
is running, and the binding's listener sets the state from inside that
effect.
`bind_active_element` now calls `update` inside
`without_reactive_context`, like event handlers created with
`create_event` and `bind_window_scroll` already do.
Fixes#18924
---------
Co-authored-by: nimbly1 <258366730+nimbly1@users.noreply.github.com>
Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com>
also remove an outdated note about needing a pending snippet for await
---------
Co-authored-by: Satvik Mishra <satvikmishra08@gmail.com>
Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com>
### Before submitting the PR, please make sure you do the following
- [x] It's really useful if your PR references an issue where it is
discussed ahead of time. In many cases, features are absent for a
reason. For large changes, please create an RFC:
https://github.com/sveltejs/rfcs
- [x] Prefix your PR title with `feat:`, `fix:`, `chore:`, or `docs:`.
- [ ] This message body should clearly illustrate what problems it
solves.
- [ ] Ideally, include a test that fails without this PR but passes with
it.
- [ ] If this PR changes code within `packages/svelte/src`, add a
changeset (`npx changeset`).
### Tests and linting
- [x] Run the tests with `pnpm test` and lint the project with `pnpm
lint`
---
Avoids creating an unintended reactive dependency when
`SvelteURLSearchParams` synchronizes its owning `SvelteURL`.
`#update_url()` now uses `super.toString()` instead of the reactive
`toString()` method. URL synchronization only needs native serialization
and should not subscribe the current reaction to the params version
signal.
Before this change, code such as:
```js
render_effect(() => {
url.searchParams.set('foo', 'bar');
});
```
could cause the effect to run twice:
1. set() calls #update_url()
2. #update_url() calls reactive toString()
3. toString() subscribes the effect to #version
4. set() increments #version
5. The effect is scheduled again
Signed-off-by: cuishuang <imcusg@gmail.com>
We had this discussion with @teemingc about Astra using a key block as a
way to re-render the whole component, which is very inefficient.
When you think about it, a key block is actually almost never the right
solution unless you want, for example, to rerun the animations.
I propose to add this line here in the best practices skill/doc, which,
in my opinion, makes sense.
We should probably also add the same suggestions whenever we encounter a
key block in the MCP Auto Fixer. What do you think about it?
---------
Co-authored-by: Rich Harris <rich.harris@vercel.com>
### Problem
While hydrating custom elements, set_attributes temporarily disables
global hydration state via set_hydrating(false) and restores it only at
the end. If a prop setter or attribute operation throws partway through,
hydration mode can remain disabled globally.
### Fix
Wrap the temporary hydration-state override in a try/finally block in
set_attributes so set_hydrating(true) always runs when
is_hydrating_custom_element is true.
---------
Co-authored-by: Simon Holthausen <simon.holthausen@vercel.com>
`{const item = 1}` inside `{#snippet row(item)}` compiles, and the
output declares `item` twice in one function, so it does not parse:
```js
const row = ($$anchor, item = $.noop) => {
const item = 1;
```
`{let}`, `{@const}` and a nested snippet with the parameter's name do
the same. In `phases/scope.js`, `EachBlock` visits its body's children
in the item's own scope, so a tag that reuses the item name is a
`declaration_duplicate`. `SnippetBlock` calls `context.next({ scope:
child_scope })`, the `Fragment` visitor opens another scope for the
body, and the tag never meets the parameter.
After visiting the body, `SnippetBlock` now reports any name in the
body's scope that `child_scope` also declares. A declaration in a nested
block or element still shadows the parameter.
---------
Co-authored-by: Simon Holthausen <simon.holthausen@vercel.com>
`migrate()` dropped a reactive assignment that has no dependencies and
put nothing in its place:
```svelte
$: count = 7; → (gone, `{count}` now undeclared)
$: obj.x = 1; → (gone)
```
A binding the statement declares now always gets its `let`, and the
statement is only removed once every target has one; otherwise it
becomes `run()`:
```svelte
let count = $state(7);
run(() => { obj.x = 1; });
```
`migrate()` never returned when a declaration shared a line with its
`<script>` tag:
```svelte
<script>export let answer</script>
```
The scan that removes a migrated `export let` line looked for a newline
in both directions with no bound. It now stops at the script's edges.
- `destroy_effect` cleared a boundary's component context while retained
reset callbacks and queued error work could still re-enter its run path.
- Reset handling is intentionally shared with hydration by
[#18556](https://github.com/sveltejs/svelte/pull/18556), while `onerror`
is deferred for safe state mutation by
[#17561](https://github.com/sveltejs/svelte/pull/17561); neither path
checked the boundary lifetime.
- Make retained and deferred boundary continuations inert during
destruction instead of masking null contexts in `bind:this` or `#run`,
which would still let dead effects and callbacks restart.
- Cover resets from `onerror` and failed snippets, cleanup ordering,
delayed error transforms, DOM rendering, and hydration.
Fixes https://github.com/sveltejs/svelte/issues/18885
- make deferred transition facade methods safe before the inner
animation initializes
- report the transition's initial position from the facade until
initialization
- add a runtime regression test for an out-only `crossfade` branch
restored in the same update
Fixes https://github.com/sveltejs/svelte/issues/18773
---------
Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com>
Fixes https://github.com/sveltejs/svelte/issues/18827.
A disconnected derived re-evaluated through `untrack` inside an effect
could connect descendant deriveds because `is_updating_effect` was true,
even though the active derived reader was not connected. Those
descendants registered on long-lived dependencies without gaining a
reaction that could later trigger their disconnect cascade.
This changes `get()` to connect a derived only when its active reader is
itself `CONNECTED`. Effects already carry that flag, and deriveds
reached through connected readers receive it before evaluation, so
tracked behavior remains intact. The now-unused `is_updating_effect`
state is removed.
Closes#13864
A `let:` directive on a component only applies to the default slot
content. If the content is an explicit `{#snippet children()}` block,
the value is never provided, so code like this compiles but fails at
runtime with "value is not defined":
```svelte
<Child let:value>
{#snippet children()}Value is: {value}{/snippet}
</Child>
```
This adds a compile error on the directive that points to snippet
parameters instead (`{#snippet children({ value })}`, or `{ item: x }`
for `let:item={x}`). Components with a `slot` attribute are not
affected, since there the directive applies to the component itself. An
unused `let:` next to a `children` snippet is also flagged now, but it
never had any effect.
### Before submitting the PR, please make sure you do the following
- [x] It's really useful if your PR references an issue where it is
discussed ahead of time. In many cases, features are absent for a
reason. For large changes, please create an RFC:
https://github.com/sveltejs/rfcs
- [x] Prefix your PR title with `feat:`, `fix:`, `chore:`, or `docs:`.
- [x] This message body should clearly illustrate what problems it
solves.
- [x] Ideally, include a test that fails without this PR but passes with
it.
- [x] If this PR changes code within `packages/svelte/src`, add a
changeset (`npx changeset`).
### Tests and linting
- [x] Run the tests with `pnpm test` and lint the project with `pnpm
lint`
Co-authored-by: waltu <182410443+waltuov@users.noreply.github.com>
Since #11737, `<a role="link">` without an `href` no longer triggers
`a11y_no_redundant_roles`, but `<area role="link">` without an `href`
still does. Per [ARIA in
HTML](https://www.w3.org/TR/html-aria/#el-area-no-href), an `<area>`
without `href` has the implicit role `generic` and `link` is one of its
allowed roles, so the role isn't redundant. This applies the existing
`<a>` exception to `<area>` and adds test cases for `<area>` with and
without `href`.
Closes#9261. The `<a>` example in that issue was already fixed by
#11737, but the issue was left open.
### Before submitting the PR, please make sure you do the following
- [x] It's really useful if your PR references an issue where it is
discussed ahead of time. In many cases, features are absent for a
reason. For large changes, please create an RFC:
https://github.com/sveltejs/rfcs
- [x] Prefix your PR title with `feat:`, `fix:`, `chore:`, or `docs:`.
- [x] This message body should clearly illustrate what problems it
solves.
- [x] Ideally, include a test that fails without this PR but passes with
it.
- [x] If this PR changes code within `packages/svelte/src`, add a
changeset (`npx changeset`).
### Tests and linting
- [x] Run the tests with `pnpm test` and lint the project with `pnpm
lint`
Another extraction from #18861 which I'm pretty sure you can also
construct a buggy reproduction for today, but with #18861 it's strictly
necessary so we don't need to come up with one now
Fixes#18862
When a `value` is applied through a spread (`<input {...props} />`),
`set_attributes` always writes `element.value`, even if the element
already has that value. Writing `value` on a number input clears any
incomplete text: typing `250.` makes `input.value` `''` (the text is
still visible, but `validity.badInput` is `true`). If the spread `value`
then becomes `''` too, writing `''` erases what the user typed.
This is what happens with SvelteKit remote forms:
`fields.quantity.as('number')` coerces `''` to `undefined`, the spread
`value` becomes `''`, and Svelte writes it back to the input. When `.`
is typed:
- Chromium clears the input in every locale, because `250.` is always
incomplete input there.
- WebKit/Safari clears it in locales where `.` is not the decimal
separator (e.g. `fr-CH`), which is where the reporter saw it.
A plain `<input type="number">` never clears. Neither does the same
`value` in Svelte without a spread (`value={...}`), because `set_value`
already skips writing when `element.value === value`. This PR adds the
same check to the spread path. Svelte 3 had this check too (#3426,
#3495); it was lost in Svelte 5.
The first write still always happens. Writing `value` sets the input's
dirty value flag, and without that flag a `defaultValue` applied
afterwards would change the current value (covered by
`form-default-value-spread`).
This also likely affects sveltejs/kit#16270 (same symptom, plus a cursor
jump caused by redundant writes). That issue is handled on the kit side
in sveltejs/kit#16320; this PR is independent of it.
The test runs in a real browser (`runtime-browser`). It types with
`document.execCommand('insertText')`, which goes through Chromium's own
editing, so `250.` is real incomplete input. The locale is not mocked.
The test fails without this change and passes with it.
### Before submitting the PR, please make sure you do the following
- [x] It's really useful if your PR references an issue where it is
discussed ahead of time. In many cases, features are absent for a
reason. For large changes, please create an RFC:
https://github.com/sveltejs/rfcs
- [x] Prefix your PR title with `feat:`, `fix:`, `chore:`, or `docs:`.
- [x] This message body should clearly illustrate what problems it
solves.
- [x] Ideally, include a test that fails without this PR but passes with
it.
- [x] If this PR changes code within `packages/svelte/src`, add a
changeset (`npx changeset`).
### Tests and linting
- [x] Run the tests with `pnpm test` and lint the project with `pnpm
lint`
---------
Co-authored-by: Paolo Ricciuti <ricciutipaolo@gmail.com>
Fixes#18840
This PR rethrows the hydration error rather than catching it at the
boundaries so that the svelte hydration warning can surface
### Before submitting the PR, please make sure you do the following
- [x] It's really useful if your PR references an issue where it is
discussed ahead of time. In many cases, features are absent for a
reason. For large changes, please create an RFC:
https://github.com/sveltejs/rfcs
- [x] Prefix your PR title with `feat:`, `fix:`, `chore:`, or `docs:`.
- [x] This message body should clearly illustrate what problems it
solves.
- [x] Ideally, include a test that fails without this PR but passes with
it.
- [x] If this PR changes code within `packages/svelte/src`, add a
changeset (`npx changeset`).
### Tests and linting
- [x] Run the tests with `pnpm test` and lint the project with `pnpm
lint`
---------
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Hydrating a `<svelte:element>` currently removes and reinserts the DOM
node that was already claimed from the server-rendered HTML. This can
restart CSS animations and disconnect/reconnect custom elements inside
it.
Keep claimed elements in place while preserving insertion for
client-created elements and later tag changes. Capture the hydration
state before rendering children, since empty or void elements can
temporarily turn hydration off.
Fixes#18852.
### Before submitting the PR, please make sure you do the following
- [x] It's really useful if your PR references an issue where it is
discussed ahead of time. In many cases, features are absent for a
reason. For large changes, please create an RFC:
https://github.com/sveltejs/rfcs
- [x] Prefix your PR title with `feat:`, `fix:`, `chore:`, or `docs:`.
- [x] This message body should clearly illustrate what problems it
solves.
- [x] Ideally, include a test that fails without this PR but passes with
it.
- [x] If this PR changes code within `packages/svelte/src`, add a
changeset (`npx changeset`).
### Tests and linting
- [x] Run the tests with `pnpm test` and lint the project with `pnpm
lint`
Validation:
- The real Chromium regression fails on unchanged `636eaaaa`: hydration
removes all four server-rendered dynamic elements. Client mounting and
server rendering pass.
- The regression checks node removals and custom-element connection
callbacks for ordinary, empty, void, and custom dynamic elements, then
changes the tags and removes/recreates an element.
- A hydration mismatch regression verifies that different client/server
child branches recover while retaining the outer element and its
following sibling.
- `pnpm test hydration runtime-browser`: 219 passed.
- Full `CI=true pnpm test`: 34 test files passed, 7,790 tests passed, 55
existing skips. The real-browser suite ran with Chromium.
- `pnpm check`: passed, including build, generated type checks, and
treeshakeability checks.
- `pnpm lint`: passed.
AI assistance: This change and its tests were prepared with OpenAI
Codex. The PR description is also AI-assisted.
---------
Co-authored-by: paoloricciuti <ricciutipaolo@gmail.com>
#18793 added special handling for `:export` selectors, but this is
incorrect. `:export` is (apparently) part of [Interoperable
CSS](https://github.com/css-modules/icss) but Svelte doesn't (and
shouldn't) know what that is. No changeset because #18793 is unreleased,
and the changeset in that PR didn't mention ICSS
Fixes#18837
### What changed
State proxies now establish a per-property reactive dependency when
`Object.hasOwn(proxy, prop)` invokes the `getOwnPropertyDescriptor`
trap. Property additions and deletions therefore invalidate the
expression, consistently with the `prop in proxy` operator.
The source creation uses the same eligibility constraints as the
existing `has` trap so inherited properties retain their existing
semantics.
A runtime-runes regression sample covers both adding and deleting the
observed property in DOM and hydration modes.
### Tests and linting
- `FILTER=object-has-own-reactive pnpm test runtime-runes
--reporter=dot` — 4 passed
- `pnpm exec vitest run
packages/svelte/src/internal/client/proxy.test.ts --reporter=dot` — 8
passed
- `pnpm test runtime-runes --reporter=dot` — 2707 passed, 34 skipped
- `pnpm test --reporter=dot` — 7786 passed, 55 skipped
- `cd packages/svelte && pnpm check` — passed
- `pnpm format` — passed
- `pnpm lint` — passed
Includes a patch changeset for `svelte`.
<!--
svelte-triage-bot:feedback-baseline:81ef2357a99a2592312d5483e38be3f6ddeb4be5
-->
---------
Co-authored-by: svelte-triage-bot <team@svelte.com>
Co-authored-by: Rich Harris <rich.harris@vercel.com>
`phases/scope.js` imported `validate_identifier_name` from
`2-analyze/visitors/shared/utils.js`, which imports `get_rune` back from
`scope.js`. The check reads a `Binding` and `Scope.declare` is its main
caller, so it now lives in `scope.js`. No behaviour change.
Analysis imported `should_proxy` from `3-transform/client/utils.js` and
transform imported `check_graph_for_cycles` from `2-analyze/utils`. The
graph search now lives in `compiler/utils`, `should_proxy` in
`phases/scope.js` next to the bindings it reads. No behaviour change.
Phase 1 imported `disallow_children` from phase 2, `fuzzymatch` sat in
the parser's utils while only analysis and `extract_svelte_ignore` used
it, `create_fragment` had a file to itself although `create_attribute`
lives in `phases/nodes.js`, and `push_array` had a single caller. Each
now sits next to its users: `create_fragment` and `disallow_children` in
`phases/nodes.js`, `fuzzymatch` in `compiler/utils`, `push_array` inside
`mapped_code.js`. Four files fewer, no behaviour change.
## Summary
- mark fully global selectors as used without requiring a scopable
component element
- preserve exact ICSS `:export` rules under the same conditions
- keep elements inside `<svelte:head>` excluded from CSS hash injection
- add a regression test covering both reported selector forms
Fixes#18792
Fixes#18797.
### Problem
SSR `render()` is lazy, but the wrapper benchmark discarded its result
and therefore measured almost no rendering work. When `.body` was
consumed, each render result's own accessor properties caused
substantial garbage-collection overhead.
### Fix
- Consume `render(App).body` in both the warmup and measured SSR
benchmark loops.
- Move lazy render-result properties onto a shared `RenderResult`
prototype.
- Memoize synchronous output and the asynchronous render promise while
preserving lazy sync/async API behavior.
- Add a regression test ensuring exposed render-result properties are
inherited.
- Add a patch changeset for `svelte`.
The benchmark now deliberately selects the synchronous lazy-render path
by reading `.body`, so the process-wide async flag imported by the
reactivity benchmarks does not determine this benchmark's render mode.
### Performance on this host
| Case | Time | GC time |
| --- | ---: | ---: |
| main, output discarded | 0.20 ms | 0.00 ms |
| main, `.body` consumed before fix | 163.04 ms | 62.68 ms |
| `svelte@5.38.10` | 110.05 ms | 1.39 ms |
| fixed, `.body` consumed | 104.79 ms | 2.43 ms |
### Tests
- `pnpm test packages/svelte/src/internal/server/renderer.test.ts` — 53
passed
- `pnpm test server-side-rendering` — 234 passed, 2 skipped
- `pnpm test` — 7,771 passed, 55 skipped
- `pnpm lint`
- `pnpm check`
- `pnpm format`
- `git diff --check`
<!--
svelte-triage-bot:feedback-baseline:116724e3b641c84ed56b2a60d494b6cb2298bbb3
-->
Co-authored-by: svelte-triage-bot <team@svelte.com>
In `phases/scope.js`, `EachBlock` visits `node.fallback` with the
block's own scope, so `{:else}` resolves the loop's name to the each
context. With `let item = $state('outer')` outside and `{#each items as
item}…{:else}<button onclick={() => (item = 'changed')}>` the compiler
reports "Cannot reassign or bind to each block argument" for a variable
that is not the block's argument.
The fallback renders when there is nothing to iterate, so it is visited
in the enclosing scope, as an await block's `pending` is.
fixes#18808
If you write `let x = $state.eager(...)`, the client compiler used to
delete that line and then still read `x`. The page crashed with `x is
not defined`. Server rendering kept the binding.
Using `$state.eager(...)` directly in the markup already worked. This
puts the `let` / `const` form on the same path as `$state.snapshot`.
Added a runtime-runes sample that fails without this change.
Co-authored-by: Xia Chao <236466140+bun-unsafe@users.noreply.github.com>
While working on another Svelte feature I noticed a bug that boils down
to batches interfering with each other through the effect tree: If batch
A schedules an effect, it is walked up to the root (possibly). Now if in
the meantime batch B also wants to schedule effects, it can have
unintended consequences.
Normally this does not happen, since it's extremely hard to run into
this situation. There's basically two cases: Either an unfortunate
timing of microtasks, or during flushing effects are scheduling new
effects which messes with a `#commit()` right after (the test case does
this).
To fix this we now defer walking up the tree until the batch is actually
processed. That way we set + unset the markers on the branches
synchronously so there's no chance of another batch interfering.
Removes the `WAS_MARKED` logic in favor of a simple `Set` heuristic: If
`mark_reactions` goes beyond a certain count, initialize it, otherwise
keep it `null`. Balances the common case of not having many transitive
dependencies with the edge case of cyclic dependencies as was seen in
#16658 (I rechecked that reproduction and it remains fast with this
change).
I ran the benchmark against this and it doesn't hit the `Seen` heuristic
once (i.e. even the benchmark doesn't have this extreme level of
dependencies where it becomes noticeable).
Fixes#18123, supersedes #18124
---------
Co-authored-by: Rich Harris <rich.harris@vercel.com>
fix: #16814
in non-async mode, when the derived inside `SvelteDate` is created and
evaluated, the `#reaction` is from the `active_reaction` when the
`SvelteDate` instance is created, which could possible in NON_UPDATING
state, so the derived should not be added to `current_sources` to
prevent re-run.
---------
Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com>
Co-authored-by: Simon Holthausen <simon.holthausen@vercel.com>