## Description
**While investigating the compiler analysis phase for errors or missing
lines, I located an explicit `TODO fix the message here` comment in the
source code
(`packages/svelte/src/compiler/phases/2-analyze/visitors/shared/utils.js`).**
### Triggering Code
```svelte
<script module>
import { foo } from './somewhere.js';
</script>
<script>
let foo = 'conflict'; // Triggers the duplicate module import error
</script>
```
## The Bug
When a component author creates a local `let` declaration in the
instance `<script>` block that shadows a variable imported in the
`<script module>` block, the compiler emits a
`declaration_duplicate_module_import` error.
However, the error message text was misleading. It stated:
```diff
- Cannot declare a variable with the same name as an import inside `<script module>`
+ Cannot declare a variable with the same name as an import from `<script module>`
```
This incorrectly implies that the duplicate declaration itself is
happening *inside* the module script block, rather than colliding with
an import *from* that block.
## Changes Made
| Action | Target (File / Function) | Description |
| :--- | :--- | :--- |
| **Template Fix** | `packages/svelte/messages/compile-errors/script.md`
| Changed "inside" to "from" to accurately reflect the nature of the
shadow collision. |
| **Regenerated Errors** | `packages/svelte/src/compiler/errors.js` |
Ran `pnpm generate` to apply the updated markdown template into the
dictionary list. |
| **Removed TODO** | `ensure_no_module_import_conflict` function |
Removed the original `// TODO fix the message here` notation from the
validator. |
| **Test Adjustments** | `illegal-variable-declaration/errors.json` |
Updated the validator test suite snapshots to assert against the correct
error message format. |
## Validation Results
* The error correctly throws when a user replicates this component
structure.
* Vitest suite `tests/validator/test.ts` successfully asserts the new,
clearer message.
---
### 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`
# THANK YOU
Closes#18012
We were removing `superTypeArguments` but the AST showed
`superTypeParameters`.
Luckily, I was able to pinpoint `esrap@2.2.4` as the cause... I've only
bumped `esrap` and that made the test fail (so that I could fix it)
And of course it was that...there's literally a commit that explicitly
print them lol
f9137c4101
If a batch creates a new branch (e.g. through an if block becoming true)
the previous batches so far do not know about the new effects created
through that. This can lead to stale values being shown. We therefore
schedule those new effects on prior batches if they are touched by a
`current` value of that batch
Fixes#17099
extracted from #17971
This PR was opened by the [Changesets
release](https://github.com/changesets/action) GitHub action. When
you're ready to do a release, you can merge this and the packages will
be published to npm automatically. If you're not ready to do a release
yet, that's fine, whenever you add more changesets to main, this PR will
be updated.
# Releases
## svelte@5.55.0
### Minor Changes
- feat: export TweenOptions, SpringOptions, SpringUpdateOptions and
Updater from svelte/motion
([#17967](https://github.com/sveltejs/svelte/pull/17967))
### Patch Changes
- fix: ensure HMR wrapper forwards correct start/end nodes to active
effect ([#17985](https://github.com/sveltejs/svelte/pull/17985))
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Fixes#17982 (two issues reported there)
Adding the start/end statically at the end once does not work because
it's going to be stale when there's a HMR reload of the wrapped
component. For reasons not completely clear to me it also fails in
another case.
So instead of wrapping the HMR with comments we just forward the nodes
of the inner effect to the outer active effect, pretending the wrapper
isn't there from a "remove dom nodes"-perspective.
Exports `TweenedOptions`, `SpringOpts`, `SpringUpdateOpts`, and
`Updater` from `svelte/motion`.
These types are required for the public method signatures of `spring`
and `tweened` (e.g., as parameters for `.set()` and `.update()`). This
PR makes them accessible to TypeScript users, following the established
pattern in modules like `svelte/store` and `svelte/transition`.
Internal implementation details like `TickContext` remain private as
they do not appear in any public-facing signatures.
Fixes#16151
### 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: Rich Harris <rich.harris@vercel.com>
Co-authored-by: Rich Harris <hello@rich-harris.dev>
Fixes#17720
## Problem
The `{@const}` tag was being printed with a trailing semicolon,
producing invalid Svelte syntax like:
{@const a = 1;}
This happened because the `ConstTag` visitor in the printer was
delegating to esrap's `VariableDeclaration` handler, which always
appends a semicolon (correct for JS, but wrong for Svelte template
syntax).
## Solution
Instead of delegating to `VariableDeclaration`, the `ConstTag` visitor
now manually prints the tag by:
- Writing `{@const ` directly
- Iterating through declarators and visiting each one
- Separating multiple declarations with commas
- Closing with `}` — no trailing semicolon
## Before
{@const a = 1;}
{@const a = 1, b = 2;}
## After
{@const a = 1}
{@const a = 1, b = 2}
## Changes
- `packages/svelte/src/compiler/print/index.js` — fixed `ConstTag`
visitor
- `packages/svelte/tests/print/samples/const-tag/output.svelte` —
updated expected test output
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Rich Harris <hello@rich-harris.dev>
Co-authored-by: Rich Harris <rich.harris@vercel.com>
## Fix: #17881
### Root Cause
When a dynamic component switches (e.g. `<Component />`), branch effects
are destroyed via `destroy_effect()`.
However, the `effect.b` (Boundary reference) field was not cleared
alongside other references (`next`, `prev`, `ctx`, `deps`, `fn`,
`nodes`, `ac`).
As a result, destroyed effects retained a reference to the `Boundary`
instance, which holds references to component state and child effects.
This prevented destroyed component subtrees from being garbage
collected, causing memory usage to grow during repeated component
switching.
### Solution
Clear the boundary reference during effect destruction.
```diff
effect.next =
effect.prev =
effect.teardown =
effect.ctx =
effect.deps =
effect.fn =
effect.nodes =
effect.ac =
+ effect.b =
null;
```
### Test Plan
* All existing tests pass:
* runtime-runes: 2,459
* runtime-legacy: 3,294
* signals: 96
* Total: 5,849 tests
* Added a dynamic component switching test that toggles components
repeatedly and verifies correct DOM output.
### Validation
Reproduced the issue using the example from #17881.
After applying the fix:
* Memory usage stabilizes during repeated component switching
* Destroyed components are properly reclaimed by the garbage collector
* No behavioral regressions observed
### Impact
* Fixes memory leak in dynamic component switching
* Minimal, safe change
* No API or behavior changes
---------
Co-authored-by: Rich Harris <rich.harris@vercel.com>
We were just putting each statement into its own promise. Besides this
being bad for perf, it also introduces subtle timing issues - the
execution order of the code could change in bad ways. Fixes#17940
Closes#17972
Claude found this fix I had a look and I think it makes sense (we are
injecting a new comment which messes up the marching during hydration).
I'm slightly confused why this only applied to `css_props` but I guess
that's because there's an "hidden" element there which makes it special.
I don't super-like the `if` situation, but I guess it is what it is.
Also the test was using `hmr` without `dev` and this brought to my
attention that we were just assuming components return something while
it's not the case...I guess it doesn't really matter unless you are
using `hmr` in prod which is not a thing but fixing this is simple so we
might just as well doing it
This fixes an awkward bug with `each` blocks containing `await`,
especially keyed `each` blocks.
If you do `array.push(...)` multiple times in distinct batches,
something weird happens — to the `each` block, the array looks this...
```js
[1]
```
...then this...
```js
[undefined, 2]
```
...then this...
```js
[undefined, undefined, 3]
```
...and so on. That's because as far as Svelte's reactivity is concerned,
what we're _really_ doing is assigning to `array[0]` then `array[1]`
then `array[2]`. Those (along with `array.length`) are each backed by
independent sources, which we can rewind individually when we need to
'apply' a batch. When it comes to sources that actually _are_
independent, this is useful, since we apply the changes from batch B to
the DOM while we're still waiting for a promise in batch A to resolve.
But in this case it's not ideal, because you would expect these changes
to accumulate.
In particular, this fails with keyed `each` blocks because duplicate
keys are disallowed (assuming the key function doesn't break on
`undefined` _before_ the duplicate check happens).
This PR fixes it by stacking batches that are interconnected.
Specifically, if a later batch has some (but not all) sources in common
with an earlier batch, then when we apply the batch we include the
sources from the earlier batch, and block it until the earlier batch
commits. When the earlier batch commits, it will check to see if doing
so unblocks any later batches, and if so process them.
In the course of working on this I realised that `SvelteSet` and
`SvelteMap` aren't async-ready — will follow up this PR with ones for
those.
Fixes#17050
---------
Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com>
Co-authored-by: Simon Holthausen <simon.holthausen@vercel.com>
This PR was opened by the [Changesets
release](https://github.com/changesets/action) GitHub action. When
you're ready to do a release, you can merge this and the packages will
be published to npm automatically. If you're not ready to do a release
yet, that's fine, whenever you add more changesets to main, this PR will
be updated.
# Releases
## svelte@5.54.0
### Minor Changes
- feat: allow `css`, `runes`, `customElement` compiler options to be
functions ([#17951](https://github.com/sveltejs/svelte/pull/17951))
### Patch Changes
- fix: reinstate reactivity loss tracking
([#17801](https://github.com/sveltejs/svelte/pull/17801))
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Alternative to #17950. Closes#17952
The goal of this is to allow svelte.config.js to contain functions for
setting certain options, so that there's a single source of truth for
everything that needs to interact with Svelte config (plugins, editor
extensions, etc):
```js
// svelte.config.js
export default {
compilerOptions: {
css: ({ filename }) => filename.endsWith('/OG.svelte') ? 'injected' : 'external',
experimental: {
async: true
},
runes: ({ filename }) => !filename.split(/\/\\/).includes('node_modules')
}
};
```
Once this ships, we can deprecate `dynamicCompileOptions` in
`vite-plugin-svelte`.
### 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.
- [ ] 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`
We commented out this code in #17038 because it was broken. I suspect it
was broken because we weren't correctly calling `unset_context` inside
`run`, leading to false positives — this is now fixed, and as such I
_think_ we can safely reinstate it.
One small change — I got rid of the `was_read` check. I assume this
existed to prevent duplicate warnings, but it actually causes false
negatives in the case where you read a signal while the reaction is
being tracked then again while it's untracked.
### 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.
- [ ] 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: Tee Ming <chewteeming01@gmail.com>
The offscreen branch was missing the "resume inert effects" logic that
was just below; it never reached that because of the early continue.
Fixes#17851
Fixes
https://github.com/sveltejs/svelte/issues/17918#issuecomment-4054067024.
The issue here was that in `boundary.#render`, if `this.#pending_count >
0` we yoink the content out of the DOM so we can replace it with the
`pending` fragment. This works by taking everything from
`effect.nodes.start` to `effect.nodes.end` and putting it in a
`DocumentFragment`.
With HMR, that doesn't work, because the effect with the nodes is buried
inside the HMR effect. This fixes it.
Draft because I'd like to try this out in a few more places before
merging.
Our "hey this is a thunk invoking a function, let's flatten that" logic
caused a bug where lazily-initialized functions where eagerly referenced
in the template effect. That causes a nullpointer.
Ensuring these variables are always referenced in a closure fixes#17404
---------
Co-authored-by: Rich Harris <rich.harris@vercel.com>
Batches that are made stale (because of a `STALE_REACTION`) can end up
sticking around indefinitely, forcing every subsequent batch into
time-traveling mode and causing incorrect `previous` values to be
rendered.
This partially fixes it, by discarding any older batches that are
subsets of a batch currently being committed. It's not a complete fix,
though — if an earlier batch is stale but is _not_ a subset of the
committed batch, it becomes a zombie, and its changes will never be
applied. Haven't quite figured out how to think about that yet.
### 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`
Before #17805, all batches drew from the same `queued_root_effects` and
did reset them to the empty array when starting a flush. After the
refactoring roots are scheduled per batch. This introduces a possible
race condition where the same root is scheduled multiple times. It was
possible because of the rebase logic in `#commit` not clearing the array
of roots, so if you somehow flush that same batch later, you will end up
traversing a clean root.
(it is possible a bug like this always existed with rebasing it was just
impossible hard to trigger it before because everyone drew from the same
root effects array)
The fix is a bit more complicated than just checking if new roots where
added, we gotta check if we actually created async work before
traversing.
Fixes#17918
---------
Co-authored-by: Rich Harris <rich.harris@vercel.com>
Fixes#17924. This also DRYs stuff a bit by making `operator` an
argument to the runtime helper function, which means we only need two
variants of it: regular and async. It also makes it so that `=`
assignments don't use the getter, because they don't need to be done
lazily.
I've added `skip_no_async` to the new test, but I'm not entirely clear
on why it was failing the TestNoAsync run to begin with.
### 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: Simon Holthausen <simon.holthausen@vercel.com>
With this, we can add invariants to the codebase so we can identify
problems like 'this batch already has roots scheduled', which indicate a
bug somewhere, without a) needing tests for scenarios that are
inherently hard to anticipate, or b) cluttering people's prod bundles
## Summary
Fixes#17148
When a `<select>` is focused inside an async boundary, the
`bind_select_value` effect gets deferred by the batch system, leaving
`select.__value` stale. If options then change dynamically (e.g. via
`{#each}`), the `MutationObserver` in `init_select` uses the stale
`__value`, snapping the select to the wrong option.
- Update `__value` in the change handler so it's always current, even
when the effect is deferred
- Update `__value` in the effect's early-return path (defensive fix for
when the effect runs but skips the DOM update)
## Test plan
- Added `select-dynamic-options-while-focused` test that renders a
`<select>` with dynamic `{#each}` options inside an async boundary,
selects a non-initial option while focused, adds another option, and
verifies the select retains the user's choice
- Verified existing `async-binding-update-while-focused-3` test still
passes
- All 7151 tests pass (`pnpm test`)
---------
Co-authored-by: Tee Ming <chewteeming01@gmail.com>
Co-authored-by: Rich Harris <rich.harris@vercel.com>
Co-authored-by: Rich Harris <hello@rich-harris.dev>
capture derived updates aswell so they become part of current/previous
so that `batch_values` computation is correct when e.g. using
`$state.eager` with a derived. Fixes#17849
Fixes#17907. When hydrating, we were resolving the boundary in the
hydration batch rather than the batch created inside the
`queue_micro_task` inside `#hydrate_pending_content`. This meant that
effects got scheduled inside a batch that was already resolved.
### 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`
https://github.com/sveltejs/svelte/pull/17680#issuecomment-3888440736.
Errors that occur during traversal (not inside a template effect etc)
can leave dirty effects inside the effect tree, but with clean parents.
This means that
a) subsequent changes to their dependencies won't schedule them to
re-run
b) subsequent batch flushes won't 'reach' them unless a sibling effect
happens to be made dirty
The easiest way to fix this is to just repair the tree if traversal
fails. If you had a truly ginormous tree this could conceivably take a
noticeable amount of time, but that's probably better than the app just
being broken.
Note that this doesn't apply to errors that occur inside an error
boundary, because in that case the offending subtree gets destroyed.
This is just for errors that bubble all the way to the root.
Closes#17680, closes#17679.
Closes#17899 by importing `untrack` from the actual file instead of the
`index-client.js`. Verified by packing the library and launching a build
with it.
Fixes#17904 by wrapping the RHS in a `() =>`.
### 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`
This is part of me trying to figure out #17162. It feels less confusing
to rebase other branches after the current batch has been processed,
rather than sort of doing it in the middle (which is an artifact of
historical constraints that no longer apply).
No test because it doesn't change any user-observable behaviour (but I
added a changeset just in case)
### 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.
- [ ] 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`
This PR was opened by the [Changesets
release](https://github.com/changesets/action) GitHub action. When
you're ready to do a release, you can merge this and the packages will
be published to npm automatically. If you're not ready to do a release
yet, that's fine, whenever you add more changesets to main, this PR will
be updated.
# Releases
## svelte@5.53.10
### Patch Changes
- fix: re-process batch if new root effects were scheduled
([#17895](https://github.com/sveltejs/svelte/pull/17895))
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
In some cases a new branch might create effects which via
reading/writing reschedule an effect, causing `this.#roots` to become
populated again. In this case we need to re-process the batch. Most of
the time this will just result in a cleanup of the dirtied branches
since other work is already handled via running the effects etc. - it's
still crucial, else the reactive graph becomes frozen since no new root
effects are scheduled.
Fixes#17891
---------
Co-authored-by: Rich Harris <rich.harris@vercel.com>
This PR was opened by the [Changesets
release](https://github.com/changesets/action) GitHub action. When
you're ready to do a release, you can merge this and the packages will
be published to npm automatically. If you're not ready to do a release
yet, that's fine, whenever you add more changesets to main, this PR will
be updated.
# Releases
## svelte@5.53.9
### Patch Changes
- fix: better `bind:this` cleanup timing
([#17885](https://github.com/sveltejs/svelte/pull/17885))
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
This removes the `queue_micro_task`-workaround we employed in
`bind:this` in favor of a search for the nearest component effect /
effect that is still getting destroyed, whichever comes first.
We used `queue_micro_task` mainly due to timing issues with components
wanting to access the bound property on teardown still, and when nulling
it out on cleanup of the bind-this-effect itself, that was too early.
The microtask is too late though in some cases, when accessing
properties of objects that are no longer there. The targeted
upwards-walk solves this while keeping the binding around as long as
needed.
For that I had to add a new `DESTROYING` flag. We _could_ have done it
without one and by deleting code in `props.js` where we don't do
`get(d)` when the prop derived is destroyed, but I wanted to keep that
because you could still run into an access error if you e.g. access the
property in a timeout.
Alternative to #17862
In #17837 we added logic to not schedule another batch during
resumption. The logic in there turns out to be flawed - it's dangerous
to keep accessing inert block effects, because if they're nested they
could access properties that no longer exist (because the outer if makes
the inner if obsolete).
So this PR basically reverts #17837 and instead schedules another batch
again under the assumption that this will only happen during the commit
phase, and all that's gonna happen is that it will schedule another
batch, which is safe.
Fixes#17866Fixes#17878
This reverts commit 2f12b60701.
Co-authored-by: Rich Harris <rich.harris@vercel.com>
This simplifies the scheduling logic and will likely improve performance
in some cases. Previously, there was a global `queued_root_effects`
array, and we would cycle through the batch flushing logic as long as it
was non-empty. This was a very loosey-goosey approach that was
appropriate in the pre-async world, but has gradually become a source of
confusion.
Now, effects are scheduled within the context of a specific batch. The
lifecycle is more rigorous and debuggable. This opens the door to
explorations of alternative approaches, such as only scheduling effects
when we call `batch.flush()`, which _may_ be better than the eager
status quo.
The layout of the `Batch` class is extremely chaotic —
public/private/static fields/methods are all jumbled up together — and I
would like to get a grip of it. In the interests of minimising diff
noise that ought to be a follow-up rather than part of this PR.
### 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.
- [ ] 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: Simon H <5968653+dummdidumm@users.noreply.github.com>
When an async value is updated inside the boundary while the pending
snippet is shown, we previously didn't notice that update and instead
showed an outdated value once it resolved. This fixes that by rejecting
all deferreds inside an async_derived while the pending snippet is
shown.
---------
Co-authored-by: Rich Harris <rich.harris@vercel.com>
Fixes `{@html}` content duplication when used inside a contenteditable
element.
When `{@html content}` is inside a contenteditable element and the user
types, the browser inserts DOM nodes directly into the {@html} managed
region. On re-render (e.g. triggered by a blur handler setting `content
= e.currentTarget.innerText`, the `{@html} `block only removed nodes it
previously created via` effect.nodes`, leaving browser-inserted nodes in
place. This caused content to appear twice — once as leftover text nodes
and once as the new `{@html}` output.
The fix tracks the boundary node (`previousSibling `of the anchor at
init) and removes all nodes between the boundary and the anchor on
re-render, ensuring externally-added nodes are also cleaned up.
Closes: #16993
---------
Co-authored-by: 7nik <kfiiranet@gmail.com>
Co-authored-by: vercel[bot] <35613825+vercel[bot]@users.noreply.github.com>
If an assignment happens in a `$:` statement, any affected effects are
rescheduled while the traversal is ongoing. But this is wasteful — it
results in the `flush_effects` loop running another time, even though
the affected effects are guaranteed to be visited _later_ in the
traversal (unless the thing being updated is a store).
This PR fixes it: inside a `legacy_pre_effect`, we temporarily pretend
that the branch _containing_ the component with the `$:` statement is
the `active_effect`, such that Svelte understands that any marked
effects are about to be visited and thus don't need to be scheduled. We
deal with the store case by temporarily pretending that there _is_ no
`active_effect`.
I will be delighted when we can rip all this legacy stuff out of the
codebase.
### 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.
- [ ] 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`
this is #17850 with changes (for whatever reason I wasn't able to push
direct to the fork) — same test but simplified, and a simpler fix that
doesn't undo the recent (necessary!) changes to the scheduling logic
---------
Co-authored-by: Mattias Granlund <mtsgrd@gmail.com>
Fixes a runtime edge case where keyed #each reconciliation can hit a
missing item during deferred async updates, causing an internal crash
and masking the original boundary error.
Fixes#17841
### 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
- [] Run the tests with `pnpm test` and lint the project with `pnpm
lint`
---------
Co-authored-by: Rich Harris <rich.harris@vercel.com>
## Summary
`Scope.generate()` and `ScopeRoot.unique()` search for available names
by iterating from suffix `_1` upward. When the same preferred name is
generated many times (e.g. `text` is generated 482 times in a large
component), the Nth call re-scans all N-1 already-taken names — O(n²)
total work.
This adds a `#name_counters` Map to `ScopeRoot` that tracks the next
suffix to try per name, so each call resumes from where the last one
left off. Generated names are identical to before.
## Benchmark (interleaved, best-of-3 rounds)
| Component | Min | Median |
|---|---|---|
| Realistic (~80 lines) | ~1% | ~7% |
| Medium (316 lines) | ~1% | ~5% |
| Large (642 lines) | ~7% | ~2% |
| XLarge (1302 lines) | **~11%** | **~10%** |
## Test plan
- [x] All snapshot tests pass (name generation unchanged)
- [x] All validator, compiler-error, runtime-runes, runtime-legacy tests
pass
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
If a dirty effect is resumed — for example `condition` becomes `false`
then `count` changes then `condition` becomes `true` again...
```svelte
{#if condition}
<div transition:fade>
{count}
</div>
{/if}
```
...then the effect is rescheduled. This happens when branches are
committed (after the effect tree is traversed, before effects are
flushed).
That's undesirable, because it causes another turn of the
`flush_effects` loop. It's better if we can handle everything in a
single pass, which is what happens in this PR. The trade-off is that we
have to traverse the entire effect tree, instead of skipping inert
subtrees, which is a trade-off that I think makes sense.
The real agenda here is that I'm trying to eliminate all
`schedule_effect` calls that happen at inconvenient times, because I
have a hunch that if we do that we can return to #17805, which I'm
increasingly convinced will be important. (You might have to trust me on
this; a full explanation would look a bit charlie-day-meme.jpg. Call it
a hunch.)
### 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.
- [ ] 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: Vercel <vercel[bot]@users.noreply.github.com>
While looking into something else I spotted the fact that we use `false`
to indicate 'else' in an `{#if ...}` block (whether there's an `else`
block to render or not). If we instead use `-1`, and use `<!--[0-->` to
indicate that the first block in an if-elseif chain was rendered...
- instead of `<!--[-->`, we do `<!--[0-->`
- instead of `<!--[!-->`, we do `<!--[-1-->`
- all others stay the same
...we can simplify things a bit — the `key` argument to `update_branch`
is always a number (which probably has some microscopic benefits in
terms of making it monomorphic when `else` is defined, and less
polymorphic when it isn't), and the hydration mismatch code only needs
to consider one type of hydration marker.
In the process, I discovered a bug — the dev-time `add_locations`
function fails on hydration markers like `<!--[1-->`. This PR fixes it.
This fixes a longstanding TODO with each blocks: currently, if any
effects aren't used in the current batch at the moment of
reconciliation, they are destroyed. Subsequent batches therefore end up
recreating them.
This is wasteful at the best of times, but if the effect contains any
async work, that work has to be restarted.
This PR fixes it by preserving any effects that correspond to the keys
of pending batches. It _does_ mean that we need to iterate over each
`keys` map for each pending batch in which an each block re-ran, but
that is a rare scenario. This feels preferable to the alternative
approaches.
## Summary
Two small compiler optimizations that reduce redundant work:
- **Cache `element_interactivity` per element in a11y checks**:
`check_element` was calling `element_interactivity()` up to 10 times per
element (via `is_interactive_element`, `is_non_interactive_element`,
`is_static_element` wrappers), each time re-iterating schema arrays. Now
computed once after building the attribute map and reused. The
now-unused wrapper functions are removed.
- **Split source lines once in `state.set_source`**: Every compiler
warning called `get_code_frame` which split the entire source string
with `source.split('\n')`. Now the split happens once in `set_source()`
and is exported as `state.source_lines`, naturally cleared by `reset()`.
## Benchmark
Synthetic component (80 state vars, 30 each blocks, ~1300 lines):
```
Min Best3 Median
Before 66.55ms 67.03ms 73.44ms
After 61.14ms 61.90ms 70.63ms
Improvement 8.1% 7.7% 3.8%
```
Realistic component (~80 lines, ~25 elements, few warnings): **~1-4%**
improvement. The a11y cache scales with element count, the source.split
saving scales with warning count.
## Test plan
- [x] All 326 validator tests pass (includes all a11y tests)
- [x] All 5671 runtime tests pass
- [x] 145 compiler-error tests pass
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
## Summary
- The `SvelteURL` `search` setter stored the raw input `value` instead
of `super.search`, unlike every other setter in the class
- This caused `url.search` to return incorrect values when the URL API
normalizes the input (e.g. adding the `?` prefix, or stripping a lone
`?`)
- For example: `url.search = 'foo=bar'` would return `'foo=bar'` instead
of `'?foo=bar'`
## Test plan
- [x] Added `url.search normalizes value` test covering:
- Setting search without `?` prefix
- Setting search with `?` prefix (existing behavior)
- Setting search to lone `?` (normalized to `""`)
- [x] All existing reactivity tests pass (46/46)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
## Summary
Two optimizations to the compiler's analysis phase:
- **Cache `ignore_stack` snapshots instead of `structuredClone` on every
node.** The universal `_` visitor in the analysis walk runs on every AST
node and calls `structuredClone(ignore_stack)` each time. In practice,
`svelte-ignore` comments are rare (0–5 per component), so 99%+ of nodes
deep-clone an unchanged stack. This adds a copy-on-write cache that only
re-creates the snapshot when `push_ignore`/`pop_ignore` actually change
the stack.
- **Walk the CSS stylesheet once instead of once per element.**
`prune()` was called in a loop for each element, each time doing a full
`walk()` of the stylesheet AST. This restructures the loop so the
stylesheet is walked once, and the element iteration happens inside the
`ComplexSelector` visitor.
## Benchmarks
Compiled each component 500 times (after 50 warmup iterations),
measuring average time per `compile()` call:
| Component | Before | After | Speedup |
|---|---|---|---|
| `has` (80+ CSS selectors, 12 elements) | 3.405 ms | 2.680 ms | **21%
faster** |
| `siblings-combinator-each-nested` (65 CSS rules, 15 elements) | 2.034
ms | 1.575 ms | **23% faster** |
| synthetic (100 CSS rules, 50 elements) | 10.099 ms | 4.564 ms | **55%
faster** |
The CSS pruning optimization scales with `elements × CSS rules` — the
more elements a component has, the bigger the win since we go from N
stylesheet walks down to 1. The `structuredClone` fix helps every
component regardless of CSS, eliminating ~500–2000 deep clones per
compile (one per AST node) and replacing them with 0–5 (one per
`svelte-ignore` comment).
For typical real-world components with 10–20 elements and some CSS,
expect roughly **20–30% faster compilation** in the analysis phase.
## Test plan
- [x] Full test suite passes (7329 tests, 0 failures)
- [x] CSS pruning tests pass (selector matching, scoping, unused rule
detection)
- [x] `svelte-ignore` behavior unchanged (snapshot is consumed read-only
via `.has()`/`.some()`)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Fixes events not being stripped on svg, mathml and custom elements.
### 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: Simon H <5968653+dummdidumm@users.noreply.github.com>
Closes#17821
### 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`
## Summary
`SvelteMap` had two bugs related to how it checked for key existence
internally:
### 1. `has()` and `get()` returned wrong results for keys with
`undefined` values
Both methods used `super.get(key) !== undefined` to determine if a key
existed before creating a per-key reactive source. This fails for keys
whose value is legitimately `undefined`, causing:
- `has(key)` to return `false` for existing keys with `undefined` values
- `get(key)` to skip creating a per-key source and fall back to tracking
`version`, resulting in over-notification
**Fix:** Replace `super.get(key) !== undefined` with `super.has(key)` in
both `has()` and `get()`, matching the pattern already used in
`SvelteSet`.
### 2. `delete()` skipped reactive updates when a key had no per-key
source
The `size` and `version` reactive updates were inside the `if (s !==
undefined)` block, meaning they only fired when a per-key source existed
(i.e., someone had previously called `has()` or `get()` on that specific
key). If a key was added via the constructor or `set()` but never
individually read, deleting it would not trigger reactive updates for
effects depending on `size` or iterators.
**Fix:** Move `set(this.#size, super.size)` and
`increment(this.#version)` to a separate `if (res)` block so they fire
whenever a key is actually deleted, regardless of whether a per-key
source existed.
### Before fix
```js
const map = new SvelteMap([['foo', undefined]]);
map.has('foo'); // false (should be true)
map.get('foo'); // undefined but tracks version instead of per-key source
```
### After fix
```js
const map = new SvelteMap([['foo', undefined]]);
map.has('foo'); // true
map.get('foo'); // undefined with correct per-key tracking
```
## Test plan
Tests are in `packages/svelte/src/reactivity/map.test.ts`:
- `map.has()` returns `true` for constructor-initialized keys with
`undefined` values
- `map.get()` returns `undefined` with proper per-key reactive tracking
- `map.delete()` triggers `has()`/`get()` reactivity for
undefined-valued keys
- `map.set(key, undefined)` followed by `has()`/`get()` works correctly
- `map.delete()` triggers `size` reactivity for keys that were never
individually read (no per-key source)
- All existing tests pass unchanged
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
tiny fix — just realised we're calling `this.apply()` (via
`this.activate()`) unnecessarily, since it will happen again immediately
after in `flush_effects`
This extracts part of #17805 into its own PR that can be merged
independently.
Today, if a (non-render) effect is created during traversal (e.g. an
`{#if condition}` block becomes true, and an `$effect` is created
somewhere inside it) then it goes through `schedule_effect`, ultimately
causing the loop in `flush_effects` to run again. This is wasteful. We
can instead push to an array — `collected_effects` — which is flushed
following the first traversal.
By using `collected_effects !== null` as a proxy for 'is traversing', we
can also simplify the bail-out logic inside `schedule_effect` and make
it work in more cases. Bailing out means that in the case that a signal
is written to during traversal (which is the case for `each` blocks, for
example), we can avoid triggering another turn of the loop because we
know that the affected effects are about to be discovered as a result of
the ongoing traversal.
All this brings us slightly closer to the intermediate goal in #17805 of
ensuring that scheduled effects always belong to a specific batch.
No test for this because it shouldn't have any user-observable impact,
though I've added a changeset out of an abundance of caution.
Another small tweak extracted from #17805, just to make that diff a bit
more legible.
By passing the `batch` to the branch commit callback, we don't need to
rely on the value of `current_batch` being the same as the batch
currently being processed. That gives us more control over the order of
operations — for example we can null out `current_batch` _before_
committing branches, which is important (at present, if a state change
occurs while those branches are being committed, it will belong to the
current batch, but the resulting effects will happen in the context of a
_new_ batch, which is something we need to avoid for the sake of
#17805).
### 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.
- [ ] 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`
This PR was opened by the [Changesets
release](https://github.com/changesets/action) GitHub action. When
you're ready to do a release, you can merge this and the packages will
be published to npm automatically. If you're not ready to do a release
yet, that's fine, whenever you add more changesets to main, this PR will
be updated.
# Releases
## svelte@5.53.5
### Patch Changes
- fix: escape `innerText` and `textContent` bindings of
`contenteditable`
([`0df5abcae223058ceb95491470372065fb87951d`](0df5abcae2))
- fix: sanitize `transformError` values prior to embedding in HTML
comments
([`0298e979371bb583855c9810db79a70a551d22b9`](0298e97937))
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
We never cleared the list of (maybe)dirty_effects on the assumption that
once a batch has run them it's complete. But that's not the case when a
boundary has a pending snippet, in which case the pending snippet shows
up, so `blocking_pending` is already 0 and effects are flushed. That can
lead to effects being run unnecessarily, even leading to infinite loops.
So we clear them. This is safe because any additional effects would
either be scheduled by the boundary (which keeps track of the offscreen
effects created while the pending snippet is shown, and schedules them
once the pending snippet goes away) or by unskipping skipped branches
(which reschedules the effects inside it)
Fixes#17717
After creating the test I noticed it fails when run together with other
tests, but not alone, which lead me to discover that we're missing an
`unset_context`. I also added clearing of `#skipped_branches` just to be
safe.
Use separate scopes for function declarations/expressions and function
bodies. This prevents variable declarations from leaking into default
parameter initialization expressions.
Closes#17785.
### 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`
This PR was opened by the [Changesets
release](https://github.com/changesets/action) GitHub action. When
you're ready to do a release, you can merge this and the packages will
be published to npm automatically. If you're not ready to do a release
yet, that's fine, whenever you add more changesets to main, this PR will
be updated.
# Releases
## svelte@5.53.3
### Patch Changes
- fix: render `:catch` of `#await` block with correct key
([#17769](https://github.com/sveltejs/svelte/pull/17769))
- chore: pin aria-query@5.3.1
([#17772](https://github.com/sveltejs/svelte/pull/17772))
- fix: make string coercion consistent to `toString`
([#17774](https://github.com/sveltejs/svelte/pull/17774))
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Closes#17758
### 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`
The only change between 5.3.1 and 5.3.2 is
2106d5e872
to support versions of Node before 4, with the cost of a minuscule but
pointless performance hit. This is a pretty stable library, and there
have been no further changes in the ensuing year and a half, so it seems
pretty safe to pin to the previous version.
### Before submitting the PR, please make sure you do the following
- [ ] 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.
- [ ] 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`
This PR was opened by the [Changesets
release](https://github.com/changesets/action) GitHub action. When
you're ready to do a release, you can merge this and the packages will
be published to npm automatically. If you're not ready to do a release
yet, that's fine, whenever you add more changesets to main, this PR will
be updated.
# Releases
## svelte@5.53.2
### Patch Changes
- fix: update expressions on server deriveds
([#17767](https://github.com/sveltejs/svelte/pull/17767))
- fix: further obfuscate `node:crypto` import from overzealous static
analysis ([#17763](https://github.com/sveltejs/svelte/pull/17763))
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
This PR was opened by the [Changesets
release](https://github.com/changesets/action) GitHub action. When
you're ready to do a release, you can merge this and the packages will
be published to npm automatically. If you're not ready to do a release
yet, that's fine, whenever you add more changesets to main, this PR will
be updated.
# Releases
## svelte@5.53.1
### Patch Changes
- fix: handle shadowed function names correctly
([#17753](https://github.com/sveltejs/svelte/pull/17753))
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Fixes#17750 (though the change that causes the issue only surfaced this
more general bug)
We were not adding the correct scope to function ids. Instead it was
part of the function body/params scope, which leads to bugs when the
function name is shadowed within the function.
Alternative to #17188. I prefer this syntax, it's lighter and feels much
more natural to me. For me it's less about commenting things _out_ than
about just, well... commenting — I frequently want to do this sort of
thing:
```svelte
<button
// when the user clicks the button, the thing should happen
onclick={doTheThing}
>click me</button>
```
One difference between this and #17188 is that this doesn't add a node
to the AST, just like comments in CSS/JS. Haven't decided if that's
desirable or not. I think it's more correct (it's an AST, not a CST;
HTML comments are different insofar as they _can_ represent 'real'
nodes) but it might be less convenient when (for example)
pretty-printing.
### 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: Simon H <5968653+dummdidumm@users.noreply.github.com>
This makes error boundaries run on the server if a new `onerror` handler
is passed to `render`. `onerror` can either synchronously or
asynchronously return a value. It should be a sanitized
JSON.stringify-able value so that it can be passed to the client for
hydration via a comment. `mount/hydrate` also get the `onerror`
property.
If no `onerror` is passed to `render` it will just throw just like
before, hence this is backwards compatible.
This work is important for SvelteKit to allow `+error.svelte` to make
use of them and in general to make boundaries properly work during SSR
(also see https://github.com/sveltejs/kit/issues/14398).
closes#15370
### 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: Rich Harris <rich.harris@vercel.com>
Fixes#17726
The problem was that the head effect had no "keep me around"-flag, which
it needs because its children are not guaranteed to be present
immediately - as shown in the related issue, where the child effect is
only created once async work has completed.
### 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`
Small tweak: except for `{#await ...}` blocks, which are a bit of an
anomaly, I'm pretty sure we _always_ want to deactivate the current
batch when unsetting context, otherwise it could incorrectly pick up
unrelated state changes. There might even be some subtle bugs lurking in
the system at present because we _don't_ always do this
### 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.
- [ ] 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`
This PR was opened by the [Changesets
release](https://github.com/changesets/action) GitHub action. When
you're ready to do a release, you can merge this and the packages will
be published to npm automatically. If you're not ready to do a release
yet, that's fine, whenever you add more changesets to main, this PR will
be updated.
# Releases
## svelte@5.52.0
### Minor Changes
- feat: support TrustedHTML in `{@html}` expressions
([#17701](https://github.com/sveltejs/svelte/pull/17701))
### Patch Changes
- fix: repair dynamic component truthy/falsy hydration mismatches
([#17737](https://github.com/sveltejs/svelte/pull/17737))
- fix: re-run non-render-bound deriveds on the server
([#17674](https://github.com/sveltejs/svelte/pull/17674))
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
I tried to bring #14977 up-to-date but it's slipped too far. Also, I
wanted to try a slightly different approach.
In this PR, deriveds are memoized if they're created during render — in
other words if you have something like this...
```svelte
<script>
let thing = $derived(expensivelyComputeThing());
</script>
```
...`thing` will only be computed once. This seems correct since the
inputs should never change during render.
For deriveds created _outside_ render, we re-run the derived each time
it is accessed, which fixes#14954. This way, there's still _some_
overhead compared to how deriveds work in the browser (where they only
recompute when their dependencies have changed), but only in the rare
places where it is necessary.
There is one wrinkle: writable deriveds. On `main` these are just
regular old variables, which means they can be written to during render.
This PR currently preserves that behaviour, but I'm not sure it's
desirable. It prevents the values of non-render-bound deriveds from ever
updating, and makes no sense in the context of render-bound deriveds
since they shouldn't be changing during render _anyway_. So my
preference would be to disallow writes to deriveds on the server, but
I'm not sure if we would need to consider that a breaking change.
Draft because of that question, and also because I think we might be
able to tidy up some stuff around class fields.
- [x] figure out if we can delete some existing code around derived
class fields
- [x] figure out what to do about writable deriveds
- [x] add a test
### 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`
Follow-up to #16271.
## Summary
- Allow `{@html}` blocks to accept `TrustedHTML` objects (from
TrustedTypes policies) without coercing them to strings
- This enables usage like `{@html myPolicy.createHTML(someHTML)}`
- Works in regular HTML, SVG, and MathML contexts
## Changes
- **`html.js`**: Instead of calling `create_fragment_from_html`, create
the wrapper element directly (`<template>`, `<svg>`, or `<math>`
depending on context) and assign the value to `innerHTML`. This
preserves `TrustedHTML` objects.
- **`reconciler.js`**: Removed the `trusted` parameter from
`create_fragment_from_html` since it's no longer used by `{@html}` and
all remaining callers want trusted HTML.
- **`template.js`** and **`snippet.js`**: Removed the second argument
from `create_fragment_from_html` calls.
## Notes
No tests added because JSDOM doesn't implement TrustedTypes.
Fixes#17735
Use the if/else hydration markers to know what "branch" (component or no
component) was rendered, and repair if differing.
### 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`
This PR was opened by the [Changesets
release](https://github.com/changesets/action) GitHub action. When
you're ready to do a release, you can merge this and the packages will
be published to npm automatically. If you're not ready to do a release
yet, that's fine, whenever you add more changesets to main, this PR will
be updated.
# Releases
## svelte@5.51.5
### Patch Changes
- fix: check to make sure `svelte:element` tags are valid during SSR
([`73098bb26c6f06e7fd1b0746d817d2c5ee90755f`](73098bb26c))
- fix: misc option escaping and backwards compatibility
([#17741](https://github.com/sveltejs/svelte/pull/17741))
- fix: strip event handlers during SSR
([`a0c7f289156e9fafaeaf5ca14af6c06fe9b9eae5`](a0c7f28915))
- fix: replace usage of `for in` with `for of Object.keys`
([`f89c7ddd7eebaa1ef3cc540400bec2c9140b330c`](f89c7ddd7e))
- fix: always escape option body in SSR
([`f7c80da18c215e3727c2a611b0b8744cc6e504c5`](f7c80da18c))
- chore: upgrade `devalue`
([#17739](https://github.com/sveltejs/svelte/pull/17739))
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
### Before submitting the PR, please make sure you do the following
- [ ] 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
- [ ] 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
- [ ] Run the tests with `pnpm test` and lint the project with `pnpm
lint`
### Before submitting the PR, please make sure you do the following
- [ ] 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
- [ ] 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
- [ ] Run the tests with `pnpm test` and lint the project with `pnpm
lint`
This PR was opened by the [Changesets
release](https://github.com/changesets/action) GitHub action. When
you're ready to do a release, you can merge this and the packages will
be published to npm automatically. If you're not ready to do a release
yet, that's fine, whenever you add more changesets to main, this PR will
be updated.
# Releases
## svelte@5.51.4
### Patch Changes
- chore: proactively defer effects in pending boundary
([#17734](https://github.com/sveltejs/svelte/pull/17734))
- fix: detect and error on non-idempotent each block keys in dev mode
([#17732](https://github.com/sveltejs/svelte/pull/17732))
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Currently, (render/template) effects inside pending boundaries are
deferred, but in an indirect manner: first we schedule them, then we
`flush` the current batch, and in the course of traversing the effect
tree we find any dirty effects and defer them at the level of the
topmost pending boundary.
This doesn't really make sense — we can just skip to the end state and
skip the scheduling/traversal, since the effects don't become relevant
until the boundary resolves.
This PR implements that. It is a stepping stone towards a larger
refactor, in which scheduling becomes batch-centric and lazier. While it
shouldn't change any observable behaviour, I've added a changeset out of
an abundance of caution.
### 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.
- [ ] 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`
## Summary
Fixes#17721
In dev mode, detect when a keyed each block has a key function that
returns different values when called multiple times for the same item
(non-idempotent). This catches the common mistake of using array
literals like `[thing.group, thing.id]` as keys, which creates a new
array object each time and will never match by reference.
- Adds new `each_key_volatile` error with helpful message explaining the
issue
- Checks key idempotency in the each block loop during dev mode
- Provides a clear error instead of the cryptic "Cannot read properties
of undefined" that occurred previously
---------
Co-authored-by: 7nik <kifiranet@gmail.com>
This PR was opened by the [Changesets
release](https://github.com/changesets/action) GitHub action. When
you're ready to do a release, you can merge this and the packages will
be published to npm automatically. If you're not ready to do a release
yet, that's fine, whenever you add more changesets to main, this PR will
be updated.
# Releases
## svelte@5.51.3
### Patch Changes
- fix: prevent event delegation logic conflicting between svelte
instances ([#17728](https://github.com/sveltejs/svelte/pull/17728))
- fix: treat CSS attribute selectors as case-insensitive for HTML
enumerated attributes
([#17712](https://github.com/sveltejs/svelte/pull/17712))
- fix: locate Rollup annontaion friendly to JS downgraders
([#17724](https://github.com/sveltejs/svelte/pull/17724))
- fix: run effects in pending snippets
([#17719](https://github.com/sveltejs/svelte/pull/17719))
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Fixes https://github.com/sveltejs/svelte.dev/issues/1793. There are
actually two fixes here, and either is sufficient to fix the playground,
but they are complementary. First, we only add the delegated event
handler _after_ the component has successfully mounted, otherwise it
will never get cleaned up if an error occurs during mount.
Second, instead of storing data on `event.__root` (which leaks between
instances), we reuse the existing `event_symbol` to provide the
necessary encapsulation. (I'll be honest I don't totally understand what
this property is for anyway and can't be bothered to figure it out right
now, but I'm sure it's important.)
No test because I'm not really sure how you _would_ test this; it
requires a fairly esoteric setup.
### 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.
- [ ] 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: Simon H <5968653+dummdidumm@users.noreply.github.com>
Fixes#17207
CSS attribute selectors for HTML enumerated attributes (like `method`,
`type`, `dir`, etc.) are supposed to match case-insensitively per the
HTML spec. Browsers handle this correctly — `form[method="get"]` matches
`<form method="GET">`. But Svelte's CSS pruning was doing a strict
case-sensitive comparison, which meant:
1. The selector got incorrectly flagged as unused (no
`css_unused_selector` warning was shown when spreads were involved, but
the selector was still pruned)
2. The scoping class wasn't applied to the matching element
3. Styles silently disappeared in production builds
The fix adds a set of known HTML attributes with case-insensitive
enumerated values (sourced from the HTML spec) and uses it during CSS
attribute selector matching. The explicit CSS `s` flag still overrides
this behavior, as expected.
### Before
```svelte
<form method="GET">
<h1>Hello</h1>
</form>
<style>
form[method="get"] h1 { color: red; }
/* ^ incorrectly pruned, <h1> not styled */
</style>
```
### After
The selector correctly matches and styles are applied.
### Test plan
- Added `attribute-selector-html-case-insensitive` CSS test covering
`form[method]` and `input[type]` cases
- All 179 existing CSS tests pass
- Verified the existing `attribute-selector-case-sensitive` test (using
`s` flag) still works correctly
- Compiler error tests and validator tests all pass
---------
Co-authored-by: Rich Harris <hello@rich-harris.dev>
Co-authored-by: Rich Harris <rich.harris@vercel.com>
Boundaries are buggy: if the `pending` snippet contains state, changes
to that state [won't cause
updates](https://svelte.dev/playground/hello-world?version=5.51.2#H4sIAAAAAAAAE22SQW-DMAyF_0qUTSpoE92uFJh223Hate0hJWaNliZRYsoqxH9fQtJW6noD-33Pz4aRKnYAWtIPkFKTQVvJSQZcIPCcPtNOSHC0XI8UTyboQsHXE_VuTOGOIDHUdszBvXqrFYJCb0Mr11phsNmoDUpAYsFpeQTrSE3W25Uv-0bXqxaFVsT0bp8dmewhJ2PobNB7OSQcOrAWuKc-rT4IB8UgcP91dsvyVZRf_IvZK8tJ3VzoInXTiCuDvVVXlYkT5u50k9DtRYfZJd11XGq8FSlKAsPOre4V-uSPDhlC9hIE1fJ6GFXtekRvrlUrRftTjzF25J5q8jrN9xOqtXDwhw14RO7jc5bIzI-3-vihyp3358yeZmFlmpENTGD8CIu0GV_kU7U0TdxmfHBKGON3MqC4UN9ZPsVDBHzOe1bjuEzaad7230j_nyD8Ii3R9jBt_RsTchCK07Jj0sH0B6hNF6aqAgAA):
```svelte
<script>
let resolvers = [];
function push(value) {
const deferred = Promise.withResolvers();
resolvers.push(() => deferred.resolve(value));
return deferred.promise;
}
function shift() {
resolvers.shift()?.();
}
let count = $state(0);
</script>
<button onclick={() => count += 1}>
increment
</button>
<button onclick={shift}>
shift
</button>
<svelte:boundary>
<p>{await push('resolved')}</p>
{#snippet pending()}
<p>{count}</p>
{/snippet}
</svelte:boundary>
```
The issue is that the boundary's `this.#effect` has the
`BOUNDARY_EFFECT` flag, and `this.#pending_effect` is a child thereof.
Instead, `this.#main_effect` should have the flag. (It turns out
`this.#failed_effect` _also_ needs the flag, because errors that occur
in a `failed` snippet cause the boundary to re-render in its `failed`
state, which I found somewhat confusing to be honest. Probably the right
choice though.)
I was able to simplify the code a bit, too.
~~(Actually now that I think about it do we need `this.#effect` at all?
Will check.)~~
### 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: Simon H <5968653+dummdidumm@users.noreply.github.com>
Closes#17722
Looks like after downgrading `?.`, `/* @__PURE__ */` may happen in an
invalid location.
---------
Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com>
This PR was opened by the [Changesets
release](https://github.com/changesets/action) GitHub action. When
you're ready to do a release, you can merge this and the packages will
be published to npm automatically. If you're not ready to do a release
yet, that's fine, whenever you add more changesets to main, this PR will
be updated.
# Releases
## svelte@5.51.2
### Patch Changes
- fix: take async into consideration for dev delegated handlers
([#17710](https://github.com/sveltejs/svelte/pull/17710))
- fix: emit state_referenced_locally warning for non-destructured props
([#17708](https://github.com/sveltejs/svelte/pull/17708))
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Non-destructured `$props()` access in runes mode silently skipped the
`state_referenced_locally` warning, leading to missed guidance when
users read `props` via identifiers or member expressions.
- **Analyzer behavior**
- Include `rest_prop` bindings in `state_referenced_locally` detection
so reads of `$props()` identifiers warn consistently with destructured
props.
- **Validation coverage**
- Add a validator fixture for `$props()` identifiers and update the
`props-identifier` snapshot expectations to capture the new warnings.
Example:
```svelte
<script>
const props = $props();
const { model } = props; // now warns
const value = props.model.value; // now warns
</script>
```
<!-- START COPILOT ORIGINAL PROMPT -->
<details>
<summary>Original prompt</summary>
>
> ----
>
> *This section details on the original issue you should resolve*
>
> <issue_title>False negative for `state_referenced_locally` warning on
not destructured `$props` access?</issue_title>
> <issue_description>### Describe the bug
>
> I was looking for a workaround for sveltejs/svelte#17669 and thought
of not destructuring the `$props` directly; to my surprise there were no
warnings at all.
>
>
> ### Reproduction
>
> ```js
> const props = $props();
> const { model } = props; // missing warning
>
> const value = props.model.value; // missing warning
> ```
>
>
[Playground](https://svelte.dev/playground/untitled?version=5.50.2#H4sIAAAAAAAACn2QT4vCQAzFv0oIe1CQ9l51YY97lj1tPYxtXAam6TAT_1H63U0HUax1j3nvJeT3OmTTEBb4w2LFUY0L3FtHEYvfDuXiB28QVL8lv7zP4pGcDNrORJrSq5aFWPQMrmIVrJfPkktROQp00LQ1OehhDR8-tD7O5ku174GjcQdSM8WyNC0hz4HOniqhGk4msOW_klf54zrPNkTwzVUbgsZuz8z1G6GzYCHhQP3iDdV47Zltwv2XMEGN6Cbgk5vIGhujAj3AXstI4WxcycvivZFn7q1OxrqT5RqLvXGR-itXywVk_AEAAA)
>
> ### Logs
>
> ```shell
>
> ```
>
> ### System Info
>
> ```shell
> REPL - Svelte v.5.50.2
> ```
>
> ### Severity
>
> annoyance</issue_description>
>
> ## Comments on the Issue (you are @copilot in this section)
>
> <comments>
> </comments>
>
</details>
<!-- START COPILOT CODING AGENT SUFFIX -->
- Fixessveltejs/svelte#17685
<!-- START COPILOT CODING AGENT TIPS -->
---
💡 You can make Copilot smarter by setting up custom instructions,
customizing its development environment and configuring Model Context
Protocol (MCP) servers. Learn more [Copilot coding agent
tips](https://gh.io/copilot-coding-agent-tips) in the docs.
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Rich-Harris <1162160+Rich-Harris@users.noreply.github.com>
Co-authored-by: Rich Harris <rich.harris@vercel.com>
Co-authored-by: Paolo Ricciuti <ricciutipaolo@gmail.com>
Closes#17709
### 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`
This PR was opened by the [Changesets
release](https://github.com/changesets/action) GitHub action. When
you're ready to do a release, you can merge this and the packages will
be published to npm automatically. If you're not ready to do a release
yet, that's fine, whenever you add more changesets to main, this PR will
be updated.
# Releases
## svelte@5.51.1
### Patch Changes
- fix: don't crash on undefined `document.contentType`
([#17707](https://github.com/sveltejs/svelte/pull/17707))
- fix: use symbols for encapsulated event delegation
([#17703](https://github.com/sveltejs/svelte/pull/17703))
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
This PR was opened by the [Changesets
release](https://github.com/changesets/action) GitHub action. When
you're ready to do a release, you can merge this and the packages will
be published to npm automatically. If you're not ready to do a release
yet, that's fine, whenever you add more changesets to main, this PR will
be updated.
# Releases
## svelte@5.51.0
### Minor Changes
- feat: Use `TrustedTypes` for HTML handling where supported
([#16271](https://github.com/sveltejs/svelte/pull/16271))
### Patch Changes
- fix: sanitize template-literal-special-characters in SSR attribute
values ([#17692](https://github.com/sveltejs/svelte/pull/17692))
- fix: follow-up formatting in `print()` — flush block-level elements
into separate sequences
([#17699](https://github.com/sveltejs/svelte/pull/17699))
- fix: preserve delegated event handlers as long as one or more root
components are using them
([#17695](https://github.com/sveltejs/svelte/pull/17695))
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Follow-up to #17319.
The `Fragment` visitor in `print()` only flushed sequences on
`RegularElement`, causing block-level elements (`Component`,
`SvelteHead`, `SvelteBoundary`, etc.) to be lumped into the same
sequence as adjacent nodes. This broke tools that programmatically
manipulate the AST (e.g.
[sveltejs/cli#915](https://github.com/sveltejs/cli/pull/915)).
The fix flushes before and after all block-level element types, ensuring
they get their own sequence and proper line separation.
### Before
```svelte
<svelte:head><title>Page Title</title></svelte:head><div>no space</div>
<Component /><Component />
<Component><span>child</span></Component><div>after component</div>
<svelte:boundary><div>boundary content</div></svelte:boundary><div>after boundary</div>
<!--comment--><div>after comment</div>
<div>before comment</div>
<!--comment-->
{#each items as item}
<div>{item}</div>
{/each}<div>after each</div>
{@render children()}<div>after render</div>
<div>before render</div>
{@render children()}
```
### After
```svelte
<svelte:head><title>Page Title</title></svelte:head>
<div>no space</div>
<Component />
<Component />
<Component><span>child</span></Component>
<div>after component</div>
<svelte:boundary><div>boundary content</div></svelte:boundary>
<div>after boundary</div>
<!--comment-->
<div>after comment</div>
<div>before comment</div>
<!--comment-->
{#each items as item}
<div>{item}</div>
{/each}
<div>after each</div>
{@render children()}
<div>after render</div>
<div>before render</div>
{@render children()}
```
### 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.
- [ ] 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`
Fixes#17694
Not really sure how to add a failing test here.
The code was generated by Codex 5.3, but I cleaned it up and manually
reviewed it myself. Not sure if this is the best approach to solving the
issue, though. It does add some overhead with the extra maps.
---------
Co-authored-by: Rich Harris <rich.harris@vercel.com>
Co-authored-by: Rich Harris <hello@rich-harris.dev>
### Before submitting the PR, please make sure you do the following
Resolves https://github.com/sveltejs/svelte/issues/14438
Resolves https://github.com/sveltejs/svelte/issues/10826
This PR makes it possible to use Svelte on pages which require
`TrustedTypes` support via their CSP by wrapping assignments to
`innerHTML` in a `TrustedTypePolicy` called `svelte-trusted-html` if the
`TrustedTypes` API exists.
Servers can allowlist the policy by setting `require-trusted-types-for
'script'; trusted-types svelte-trusted-html` in their
`Content-Security-Policy` header.
- [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.
- [ ] 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
Note: I haven't run the tests since I don't have `pnpm` setup properly.
I have tested that:
1. A project with a CSP fails with Tip of Tree Svelte
2. That project works when installing this revision of Svelte
3. The project (with this revision) works in Browsers with no
`TrustedTypes` support (i.e. Firefox, Safari)
- [ ] Run the tests with `pnpm test` and lint the project with `pnpm
lint`
My test project is here:
https://github.com/fallaciousreasoning/svelte-tt-test/blob/master/src/routes/%2Bpage.server.js
The only changes to the default project is adding the CSP in
`src/routes/page.server.js`
---------
Co-authored-by: 7nik <kfiiranet@gmail.com>
Co-authored-by: Rich Harris <rich.harris@vercel.com>
Co-authored-by: Rich Harris <hello@rich-harris.dev>
Fixes a minor bug where HTML entities could be decoded into significant
characters in the template literal we output for SSR, leading to weird
effects. Not a security issue because it has to be literally written
into the svelte file you're compiling, but still wrong.
---------
Co-authored-by: Rich Harris <rich.harris@vercel.com>
Fixes#16342.
Errors thrown inside `$effect` were previously treated as
subtree-creation errors when `EFFECT_RAN === 0`, which caused them to be
rethrown instead of propagating to the nearest `<svelte:boundary>`. As a
result, `$effect` errors bypassed boundaries and appeared as uncaught
runtime errors. This change ensures that errors originating from effects
(`EFFECT`) are routed through `invoke_error_boundary`, allowing them to
bubble up the effect tree and be handled correctly by the closest
boundary. Existing subtree-creation behavior for non-effect cases
remains unchanged.
---
### 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: Rich Harris <rich.harris@vercel.com>
It should be the last piece of XHTML compliance.
We missed fixing `mutltiple` and `selected` attributes on `<select>` and
`<option>`.
Also, it seems the test runtime-legacy/select-multiple-spread was
broken.
I just copied to runtime-xhtml and updated tests that fail when a
`nodeName` comparison is broken. For `<progress>` no test due to JSDOM
quirks (at least in the past), and for `<template>` and `<script>`
nothing got broken 🤔
### 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: Rich Harris <rich.harris@vercel.com>
This supersedes #16595, and fixes the issue by 'freezing' effects inside
deriveds when those deriveds are disconnected, and unfreezing them when
they reconnect. This is preferable to the current asymmetric behaviour
on `main` (in which effects are destroyed when the derived is
disconnected, and never recreated) and #16595, which causes the derived
itself to be re-evaluated.
### 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 XHTML thing, per
https://github.com/sveltejs/svelte/pull/17418#issuecomment-3863029273
(that PR doesn't address this issue)
### 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`
## Summary
Fixes#13768
`<select bind:value={derived.prop}>` in legacy (non-runes) components
throws `effect_update_depth_exceeded` when the bound value comes from a
`$:` reactive statement.
**Root cause:** `setup_select_synchronization` created a
`template_effect` that called `invalidate_inner_signals`, which reads
and writes the same signals on every change — creating an infinite
update loop when those signals feed back into derived state.
**Fix:** Remove the effect-based synchronization entirely. Instead,
populate `legacy_indirect_bindings` during the analyze phase for
`<select bind:value>` elements, and call `invalidate_inner_signals`
inline at the mutation point in `AssignmentExpression` — only when the
binding is actually mutated, avoiding the read-write cycle.
Based on the approach outlined in #16200.
## Changes
- **`scope.js`**: Add `legacy_indirect_bindings` field to `Binding`
class
- **`RegularElement.js` (analyze)**: For `<select bind:value={foo}>`,
collect scope references as indirect bindings on the bound variable
- **`RegularElement.js` (transform)**: Remove
`setup_select_synchronization` function and its call site
- **`AssignmentExpression.js` (transform)**: When mutating a binding
with indirect bindings, append `invalidate_inner_signals` call after the
mutation
## Test plan
- Added `binding-select-reactive-derived` test that reproduces the exact
scenario from #13768
- All 3291 runtime-legacy tests pass (0 regressions)
- All 2312 runtime-runes tests pass
- All snapshot and compiler tests pass
---------
Co-authored-by: Rich Harris <rich.harris@vercel.com>
Currently, the [newly introduced `parseCss` from
`svelte/compiler`](https://github.com/sveltejs/svelte/pull/17496)
returns `Omit<AST.CSS.StyleSheet, 'attributes' | 'content'>`. If you try
to work with this in external tooling, everywhere where you pass around
the result of this method, you need to use that type as well, which is
quite cumbersome. (I'm trying to integrate this into `sv` to get rid of
a workaround)
This creates a new type in the CSS AST to differentiate between one
stylesheet only having the roles, and one beeing the full one that is
used in `parse` itself. Im 100% open on the name of the new type or any
better ideas.
### Before submitting the PR, please make sure you do the following
- [ ] 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.
- [ ] 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: Rich Harris <rich.harris@vercel.com>
This was actually several bugs:
- We used `scopes` for the blockers, that's actually the template
scopes, should be `instance.scopes` instead
- We missed setting the scope for `touch`
- We didn't take return statements into account when calculating
blockers. We cannot know when/if something within the return statement
is called, so we gotta assume it is and touch everything transitively
from it
Combined this fixes#17667 (and possibly other cases not showing up in
the issue tracker yet)
Initially I just thought "ok I guess we have to traverse into functions,
too" but then I thought that feels too unoptimized and came up with the
return-statement-inspection, at which point I discovered the other bugs.
### 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`
* fix: reduce if block nesting
This reduces if block nesting similar to how we did it in #15250 (which got lost during the `await` feature introduction): If the if expression doesn't contain an await expression or is not dependent on a blocker that is not already resolved, then we can avoid creating a separate `$.if()` statement. The one trade-off is that we'll do re-invocations for all the conditions leading up to the condition that matches. Therefore non-simple if expressions are wrapper in `$.derived` to avoid excessive recomputations.
closes#17659 (~320 markers in prod mode possible now; less in dev because of our "wrap this component with devtime info" method)
helps with #15200
* tweak
* feedback
If the render tag is wrapped in `$.async`, that `$.async` call already contains surrounding markers, so we must not add our own to avoid hydration mismatches. Related to #17641, fixes#17225
* docs: wrap JSDoc URLs in @see and @link tags
* fix: move curly brace to end of URL
* chore: add changeset
* add link text
* regenerate
---------
Co-authored-by: Rich Harris <rich.harris@vercel.com>
* fix: allow NaN in key blocks
* lol whoops
* Update packages/svelte/src/internal/client/dom/blocks/key.js
Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com>
---------
Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com>
* treat menu element like ul/ol for a11y role checks
The <menu> element has the same implicit role (list) as <ul> and <ol>,
so it should receive the same treatment in a11y checks:
- Allow <menu role="list"> without redundant role warning (CSS
list-style:none can remove semantics, role restores them)
- Allow <menu> with interactive roles like menu, menubar, radiogroup,
tablist, tree, treegrid (same exceptions as ul/ol)
Fixes#8529
* changeset
---------
Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com>
* fix: exit resolved async blocks on correct node when hydrating
* expand test + fix
* tweak, add note to self
---------
Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com>
Co-authored-by: Simon Holthausen <simon.holthausen@vercel.com>
This is basically #17611, minus #17640, plus #17639. We need to add the $.next() call after render tags as well as components; rather than duplicating the logic, we can use is_standalone to determine when this is necessary (since this is what prevents $.append(...) from being used).
Fixes#17261Fixes#17608
---------
Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com>
The store invalidation detection in each blocks only checked for
Identifier and MemberExpression AST node types. This caused bind:
on iteration variables to silently fail when the expression used
logical operators (e.g. `{#each $store.items ?? [] as item}`).
Use expression metadata dependencies instead of AST type checking
to find store_sub bindings, which correctly handles all expression
shapes.
Fixes#14625
* fix: emit `each_key_duplicate` error in production
* fix: preserve key
* Update packages/svelte/src/internal/client/dom/blocks/each.js
Co-authored-by: Rich Harris <rich.harris@vercel.com>
* Update packages/svelte/src/internal/client/dom/blocks/each.js
Co-authored-by: Rich Harris <rich.harris@vercel.com>
* fix: ensure keys are validated
* fix silly test name
* fix: cover other case of duplicate keys
* emit error on hydration
* ensure the error is handled
* drop useless tests
* unused
* finish merge
* add lost check back
* chore: bump playwright (#17565)
* chore: bump playwright
* maybe this will help somehow?
* err whatever
* fix
* chore: allow testing in production env 2 (#17590)
* Revert "chore: allow testing in production env (#16840)"
This reverts commit ffd65e90fe.
* new approach
* fix: handle renderer.run rejections (#17591)
* fix: handle renderer run rejections
* add test
* changeset
* simplify
* explanatory comment
---------
Co-authored-by: Antonio Bennett <abennett@mabelslabels.com>
Co-authored-by: Rich Harris <rich.harris@vercel.com>
* fix: only create async functions in SSR output when necessary (#17593)
* fix: only create async functions in SSR output when necessary
* actually...
* simplify generated code a bit more
* simplify
* fix: merge consecutive text nodes during hydration for large text content (#17587)
* fix: merge consecutive text nodes during hydration for large text content
Fixes#17582
Browsers automatically split text nodes exceeding 65536 characters into
multiple consecutive text nodes during HTML parsing. This causes hydration
mismatches when Svelte expects a single text node.
The fix merges consecutive text nodes during hydration by:
- Detecting when the current node is a text node
- Finding all consecutive text node siblings
- Merging their content into the first text node
- Removing the extra text nodes
This restores correct hydration behavior for large text content.
* add test, fix
* fix
* fix
* changeset
---------
Co-authored-by: Miner <miner@example.com>
Co-authored-by: Rich Harris <rich.harris@vercel.com>
* Version Packages (#17585)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* Revert "drop useless tests"
This reverts commit 65f77ef840.
* update tests
* fix test
* we don't need to expose this function any more
* figured it out... we cant have errors during reconcile
* simplify
* tweak
* unused
* revert no-longer-needed change
* unused
---------
Co-authored-by: Rich Harris <rich.harris@vercel.com>
Co-authored-by: Antonio Bennett <31296212+Antonio-Bennett@users.noreply.github.com>
Co-authored-by: Antonio Bennett <abennett@mabelslabels.com>
Co-authored-by: FORMI <239411042+Richman018@users.noreply.github.com>
Co-authored-by: Miner <miner@example.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* Remove SvelteKit data attributes from elements.d.ts
Removed SvelteKit specific data attributes from elements.d.ts.
* Remove SvelteKit data attributes from elements.d.ts
Removed SvelteKit data attributes from elements.d.ts.
---------
Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com>
fixes#17595
When an if/key/etc block has an expression that depends on an async blocker (e.g., is inside a component with top level `await`), the compiler incorrectly treats the expression as async - even when the expression itself contains no `await`.
This causes the expression to be added to `$.async`'s `expressions` array, which wraps it in an `async_derived`. This is not only unnecessary but also buggy: it breaks the direct reactive connection between the source and its dependent effects, causing inconsistent effect executions.
The fix is to only add expressions to `$.async`'s `expressions` array when they actually contain an `await`.
When a branch is speculatively marked for destruction (condition temporarily falsy), its child effects are reset to `CLEAN` to prevent them running in a doomed branch (as of #17581). However, if the branch survives (condition becomes truthy again), those effects remain `CLEAN` and never run - the source was already marked dirty before the reset, so no new dirty marking occurs.
The fix is to change `skipped_effects` from a `Set` to a `Map` that tracks which child effects were dirty/maybe_dirty before being reset. When a branch is unskipped (survives), restore their status and reschedule them.
---------
Co-authored-by: Rich Harris <rich.harris@vercel.com>