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.56.8
### Patch Changes
- fix: call `onerror` and provide a working `reset` when hydrating a
failed boundary
([#18556](https://github.com/sveltejs/svelte/pull/18556))
- fix: preserve select selection when spread attributes omit value
([#18561](https://github.com/sveltejs/svelte/pull/18561))
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Fixes#18555
A boundary that failed during SSR hydrates via
`#hydrate_failed_content`, which never calls `onerror` and passes the
`failed` snippet a no-op `reset`. Both came in with #17672, whose docs
say `onerror` "will be called upon hydration with the deserialized error
object". Once hydrated as failed, the boundary can never leave that
state. Downstream this is what keeps SvelteKit's `+error.svelte` mounted
after navigating away from a server-rendered error page
(sveltejs/kit#16345).
This extracts the reset/onerror machinery from `#handle_error` into
`#create_reset` and uses it in the hydration path too. `onerror` is
invoked in a microtask because it may mutate state, which is disallowed
while hydrating. `#handle_error` already invokes it asynchronously, so
the timing matches the error path.
The tests flip a `recovered` flag before calling `reset`, since the
child would otherwise throw again. That is the intended retry pattern,
and the same one kit uses when it resets route boundaries on navigation.
Verified against kit end to end, hydrating a server-rendered error page
and navigating away now tears it down with no kit changes needed.
Fixes#18557
A `<select>` with spread attributes initializes the option mutation
observer even when those attributes never provide a `value`. In that
case the element has no internal `__value`, but the observer previously
treated the missing property as an explicit `undefined` value and
deselected every option after an option was added or removed.
Only reapply the programmatic selection when `__value` is actually
present. This preserves the browser's current selection for spreads
without a value while keeping the existing behavior for bindings,
including an explicitly stored `undefined` value.
A runtime-legacy regression fixture covers both adding and removing an
option in client and hydration modes.
### Before submitting
- [x] References the existing issue
- [x] Uses a `fix:` title
- [x] Includes a regression test
- [x] Includes a patch changeset for `svelte`
### Test plan
- [x] `FILTER=select-spread-preserve-selection pnpm test runtime-legacy
--maxWorkers=1` (2 tests passed: client and hydrate)
- [x] `git diff --check`
- [ ] Full `pnpm test` was not run because the shared host was under
resource pressure and broad test processes were explicitly avoided
- [ ] `pnpm lint` and `pnpm check` were not run for the same reason
- [ ] Targeted repository Prettier check was attempted, but could not
start because `prettier-plugin-svelte` required the ungenerated
`packages/svelte/compiler/index.js`; no broad build or dependency
reinstall was run
### AI disclosure
AI-assisted: the implementation, regression test, and pull request text
were prepared with OpenAI Codex and reviewed by the contributor.
Co-authored-by: Paolo Ricciuti <ricciutipaolo@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.56.7
### Patch Changes
- chore: provide `indent` option for `print`
([#18474](https://github.com/sveltejs/svelte/pull/18474))
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Relevant for https://github.com/sveltejs/cli/pull/1138.
This is basically just an option from `esrap` that we pass through. That
will allow tools like `sv migrate` to provide a guessed indent based on
the other file contents and therefore allow us to produce way smaller
diffs. Since we are just passing an option, there is no need for a test
here.
Technically a `feat:` but i dont think this is relevant enough.
### 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.56.6
### Patch Changes
- perf: skip unnecessary blocker analysis when compiling components
without top-level await
([#18548](https://github.com/sveltejs/svelte/pull/18548))
- fix: rerun derived that had an abort controller on reconnection
([#18551](https://github.com/sveltejs/svelte/pull/18551))
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Skip function reference tracing in `calculate_blockers` when a component
has no top-level `await`.
Blockers only represent dependencies on top-level async statements.
Without a top-level `await`, no binding can have a blocker, so tracing
every top-level function cannot affect the generated output. In large
components with many functions and transitive assignments, that
unnecessary work can become quadratic.
Follow-up to #18400 - we need to mark a derived with an abort signal
that is frozen as dirty so it is guaranteed to rerun when it
reconnects/is re-requested. Else you could return a stale value, or
worse, you returned a promise from the derived which you aborted, and
it's now in the rejected state until you update one of its dependencies.
Fixes https://github.com/sveltejs/svelte/issues/18301
---------
Co-authored-by: Fedor Nezhivoi <f.nezhivoi@corp.vk.com>
Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com>
Co-authored-by: Simon Holthausen <simon.holthausen@vercel.com>
fixes#18438
Currently the `mark` method inside `#merge` for `earlier_batch` will
schedule effect for undirty derived. Add the condition for derived to
prevent unnecessary effect be scheduled.
```
if ((flags & DERIVED) !== 0) {
mark(/** @type {Derived} */ (reaction));
}
```
## Summary
Fixes#18491 — the compiler discards an upstream plugin's sourcemap when
it's generated without a `source` option (e.g. `new
MagicString(code).generateMap()`), producing wrong devtools/stack-trace
positions.
### Root cause
`compile()` already supports composing an incoming sourcemap into its
output via `options.sourcemap` (`merge_with_preprocessor_map` →
`apply_preprocessor_sourcemap` → `combine_sourcemaps` in
`packages/svelte/src/compiler/utils/mapped_code.js`). This is the same
mechanism `preprocess()` uses internally, and it's the documented
contract for tools that transform a `.svelte` file before compiling it
(see `CompileOptions.sourcemap`'s doc comment).
`combine_sourcemaps` composes maps by matching `sourcefile === filename`
(the basename of the file being compiled). A sourcemap produced by `new
MagicString(code).generateMap()` **without** a `source` option — which
is exactly what the reporter's Vite plugin does — has `sources: ['']`.
That empty string never equals `filename`, so `remapping()` treats the
node as a leaf (the "original" file) instead of a branch to keep
chaining through, and the whole incoming map is silently dropped. The
result: every position that flows through that segment resolves to `{
source: null, line: null, column: null }`, which is what produces the
wrong/missing devtools mapping described in the issue.
Vite itself already has to handle this exact ambiguity: in
`pluginContainer.ts`'s `_getCombinedSourcemap`, an empty `sources[0]`
from a MagicString-based transform is patched to refer to the file being
transformed before Vite uses it internally. This PR applies the same
normalization on svelte's side, so the contract holds regardless of
whether the caller happens to pass a `source` option to `generateMap()`.
### Fix
In `apply_preprocessor_sourcemap`, normalize an incoming map's `sources:
['']` (or `[null]`/`[undefined]`) to `[filename]` before calling
`combine_sourcemaps`, so the chain-matching step can actually find it.
Closes#18506
Declaration tags in the scope of an each block were considered "parts"
and transformed to use their value instead of the signal itself. We can
safely check on the `kind` of the binding since a `kind` of `state`,
`state_raw` or `derived` in the each scope needs to be a declaration tag
(and it's a stable reference so we can use them directly)
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.56.4
### Patch Changes
- fix: include wrapping parentheses in `{@const}` declarator `end`
position ([#18436](https://github.com/sveltejs/svelte/pull/18436))
- fix: always unset reactivity context after restoring it
([#18453](https://github.com/sveltejs/svelte/pull/18453))
- fix: don't notify `searchParams` subscribers when the URL changes
without affecting the search string
([#18425](https://github.com/sveltejs/svelte/pull/18425))
- fix: strip `?` from optional parameters in `<script lang="ts">` so
generated JavaScript is valid
([#18448](https://github.com/sveltejs/svelte/pull/18448))
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
When calling `save`, we restore the context after the promise resolves.
But we do not unset it after the subsequent synchronous execution. That
means that until `unset_context` runs in `async_derived` we will not
have the correct (nulled) context. That causes problems if the `save`
isn't the last promise contributing to the `async_derived`, because it
means the context is not properly unset until the promise _after_ the
`save` within the `async_derived` settles. This can cause all sorts of
mixups, including a wrong mutation error.
fixes#18441
`{@const x = (a)}` (and any `{@const}` whose initializer is wrapped in
parentheses, e.g. `(a = b)`, `({ ... })`) produced a
`VariableDeclarator` whose `end` fell before the closing `)`.
`read_expression` strips the wrapping parens from the returned node but
advances the parser past them, so `init.end` excludes the `)` while the
parser position does not. Use the parser position for the declarator
`end` instead. Fixes source slices / tooling that rely on the node range
(the range previously yielded invalid, unbalanced source).
<img width="2064" height="1746" alt="image"
src="https://github.com/user-attachments/assets/64153ee5-d543-442d-8018-d3668acfd7a7"
/>
### 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`
`SvelteURLSearchParams` has two notification bugs that surface through
`SvelteURL`.
The first is over-notification. Setting `url.href` always rebuilds the
params and bumps their version signal, even when the search string
didn't change at all. Effects that read `size` (or any params method)
re-run on every unrelated `href` write, which is #17218, where it caused
infinite effect loops in a router. The fix is a guard in the internal
replace path that bails out when the incoming params serialize
identically to the current ones.
The second is under-notification, and it's sneakier. Every read method
tracks the version signal, `get`, `getAll`, `has`, `keys`, `values`,
`entries`, `toString`, `size`, the iterator... except `forEach`, which
was never overridden. The inherited platform method reads the internal
list directly and subscribes to nothing, so a template that renders
params via `forEach` never updates:
```js
$effect(() => {
url.searchParams.forEach((value, key) => entries.push(`${key}=${value}`)); // never re-runs
});
```
We found this one in the wild while making `page.url` a `SvelteURL` in
sveltejs/kit#16031. Kit's own test suite renders query params with
`forEach`, and the coarse object-identity signal that previously masked
the gap went away, leaving stale UI after client-side navigations. The
fix is the same one-line tracking pattern every other read method
already uses.
Fixes#17218
---
- [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.56.3
### Patch Changes
- fix: ignore errors that occur in destroyed effects
([#18384](https://github.com/sveltejs/svelte/pull/18384))
- fix: type BigInts in `$state.snapshot(...)` return values
([#18388](https://github.com/sveltejs/svelte/pull/18388))
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
fixes#18383
I traced the issue to
`packages/svelte/src/internal/client/error-handling.js`, specifically
the `invoke_error_boundary` function. The problem is at line 56 where
`effect.b.error(error)` is called without checking if `effect.b` exists.
What happens is: when an async `$derived` rejects inside a
`<svelte:boundary>`, the error is handled correctly the first time. But
if the boundary is destroyed before the rejection fully settles (e.g.
component unmounts), a subsequent settle tries to call
`effect.b.error()` on a destroyed boundary where `effect.b` is `null`,
causing a `TypeError`.
The fix adds a null check for `effect.b` before calling `error()`. If
the boundary has been destroyed, we skip it and continue bubbling up to
the parent boundary. This way the error still gets handled properly
instead of crashing.
This could also be tested by creating a component with an async
`$derived` that rejects after the component unmounts, but I wanted to
get the fix up first for review.
---------
Co-authored-by: Rich Harris <rich.harris@vercel.com>
Fixes#18385
## Problem
`$state.snapshot` infers BigInt properties as `never` because `bigint`
is missing
from the `Primitive` type definition. BigInt is a valid JavaScript
primitive and is
supported by `structuredClone`.
## Solution
Added `bigint` to the `Primitive` type in both
`packages/svelte/src/ambient.d.ts` and
`packages/svelte/types/index.d.ts`.
## Before
```ts
const object = $state({ number: 1n });
const snapshot = $state.snapshot(object);
// snapshot.number is inferred as never ❌
After
const object = $state({ number: 1n });
const snapshot = $state.snapshot(object);
// snapshot.number is correctly inferred as bigint ✅
Tests and linting
- [x] This is a type-only change, no runtime behavior affected
---------
Co-authored-by: Rich Harris <hello@rich-harris.dev>
By checking that current/previous batch are null we filter out false
positives. `reactivity_loss_tracker` is only reset after a microtask, so
if a flush happens before that, we get warnings for things we shouldn't
warn on.
Fixes#18370
---------
Co-authored-by: Rich Harris <rich.harris@vercel.com>
If a block has a sole component child, we apply an optimization to not
need a template. But if that sole child is wrapped in `$.async`, it can
happens that the block's effect is assigned the wrong end node.
In the reproduction this happens because we have two components inside a
Child which is rendered only after an async block resolves. In
`$.append` we have logic to not reassign nodes when the active effect
didn't already run, which it has by the time we come across it (because
the async work had to resolve first; we added this in
https://github.com/sveltejs/svelte/pull/17120 to prevent the opposite,
nodes being "rewinded" to an earlier position) and so the second sibling
component's `$.append` will not set its end node to the effect. As a
result the end node is wrong (too early).
To fix this we assign the nodes in `$.async`. We could also use
`compareDocumentPosition` in `$.append` to only ever go forward, but
that feels a bit heavier performance-wise.
---------
Co-authored-by: paoloricciuti <ricciutipaolo@gmail.com>
Co-authored-by: Rich Harris <rich.harris@vercel.com>
The rejects all async deriveds of a batch as `OBSOLETE`, so they don't
hang around and bail early without triggering the batch.
If we don't do this, an async derived can trigger the already done
batch, which schedules an effect that is never flushed. Because it is
never flushed the branches it touched on its way up are never cleared,
and so anything else in that subtree is now unreactive.
Mimic what `parseExpressionAt` does
---------
Co-authored-by: hjaber <hjaber@users.noreply.github.com>
Co-authored-by: Simon Holthausen <simon.holthausen@vercel.com>
Detect TypeScript `type` declarations by actually parsing instead of by
character-class blacklists, so that expressions like `{type === 'all' ?
a : b}` or `{type instanceof Foo}` aren't misclassified as malformed
declarations.
Closes#18328
---------
Co-authored-by: Simon Holthausen <simon.holthausen@vercel.com>
Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com>
### Before submitting the PR, please make sure you do the following
- [x] It's really useful if your PR references an issue where it is
discussed ahead of time. In many cases, features are absent for a
reason. For large changes, please create an RFC:
https://github.com/sveltejs/rfcs
- [x] Prefix your PR title with `feat:`, `fix:`, `chore:`, or `docs:`.
- [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`
Fixes#18332.
When spread props put `value` before a later `type="hidden"` on an
`<input>`, `set_attributes` currently writes the value while the element
is still a text input. Browsers sanitize text input values by removing
newlines, so the hidden input permanently loses them before `type` is
applied.
This sets an input's `type` first whenever the same spread update also
includes `value` or `__value`, so the existing value handling runs with
the final input type. The new runtime-browser sample covers both the
problematic spread-first order and the already-working type-first order.
Validation:
- `corepack pnpm lint`
- `CI=1 corepack pnpm test`
- `corepack pnpm vitest run
packages/svelte/tests/runtime-browser/test.ts -t
input-type-before-value-spread`
---------
Co-authored-by: Rich Harris <rich.harris@vercel.com>
Co-authored-by: Rich Harris <hello@rich-harris.dev>
## Summary
- detect `SvelteURLSearchParams#set` changes by comparing duplicate
value lists instead of joined strings
- update reactive subscribers when duplicate params collapse to a single
value with the same concatenated text
- cover both `SvelteURLSearchParams` and `SvelteURL.searchParams`
synchronization
## Tests
- pnpm exec vitest run
packages/svelte/src/reactivity/url-search-params.test.ts -t
"URLSearchParams.set updates when duplicate values collapse to the same
joined string"
- pnpm exec vitest run
packages/svelte/src/reactivity/url-search-params.test.ts
packages/svelte/src/reactivity/url.test.ts
---------
Co-authored-by: Rich Harris <hello@rich-harris.dev>
Byte-identical templates hoisted from different fragments, elements, or
branches now share a single `$.from_html` factory instead of each
emitting its own module-scope variable - this shrinks generated output
and avoids redundant runtime template parsing. Dedup is keyed on
`(content, flags)` and is skipped in dev mode (templates there are
wrapped in `$.add_locations`, which embeds per-call-site info).
### 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.
- [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: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Rich Harris <rich.harris@vercel.com>
The compiler emitted an inline string array as the second argument to
`$.rest_props(...)`, and the runtime did a linear
`Array.prototype.includes` on it on every property access via the
rest-props Proxy.
The exclude list only depends on the component definition, not on the
instance, so it can be hoisted to module scope and shared by every
instance. Switching it to a `Set` at the same time makes each lookup
O(1).
For a component like `<Button ...rest />` rendered N times, this turns
one per-instance allocation (plus a linear search on every rest-prop
access) into one module-scope allocation plus O(1) lookups.
The legacy `$$restProps` path is unchanged — it mutates the exclude list
in its `deleteProperty` trap, so it can't share a hoisted Set across
instances.
---------
Co-authored-by: Rich Harris <rich.harris@vercel.com>