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>
Instead of having batches coordinate whether they should block on other batches, or rebase earlier/later ones, we move the coordination into the async deriveds.
More concretely, we always run async operations in order on the async_derived level. During function invocation we temporarily remove batch_values so that the latest value across all batches is retrieved. Because we do that we have to wait on prior runs to resolve before resolving ours, except when the batch in question is a subset of the current batch; in that case we can resolve directly and reject the async derived of the other batch as stale. This combination of things (latest value across all batches but resolving in order except when we don't need to wait) allows us to get rid of the `batch.#commit()` logic.
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`
While we don't officially document it, `untrack` also allows to opt out
of the "unsafe mutation" validation, which is what we test here.
For anyone coming across this: USE WITH CAUTION. This can cause graph
inconsistencies leading to wrong values on initial render
Doing this because we're gonna make use of it ourselves for remote
functions, and this ensures we don't accidentally regress.
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
If you had any output files from previous sandbox runs, these would get
checked and failed by Prettier. We were already ignore `src`, so now
we're ignoring `dist` and `output` as well.
### 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`
## 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
Bumps [devalue](https://github.com/sveltejs/devalue) from 5.6.3 to
5.6.4.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/sveltejs/devalue/releases">devalue's
releases</a>.</em></p>
<blockquote>
<h2>v5.6.4</h2>
<h3>Patch Changes</h3>
<ul>
<li>
<p>87c1f3c: fix: reject <code>__proto__</code> keys in malformed
<code>Object</code> wrapper payloads</p>
<p>This validates the <code>"Object"</code> parse path and
throws when the wrapped value has an own <code>__proto__</code> key.</p>
</li>
<li>
<p>40f1db1: fix: ensure sparse array indices are integers</p>
</li>
<li>
<p>87c1f3c: fix: disallow <code>__proto__</code> keys in null-prototype
object parsing</p>
<p>This disallows <code>__proto__</code> keys in the
<code>"null"</code> parse path so null-prototype object
hydration cannot carry that key through parse/unflatten.</p>
</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/sveltejs/devalue/blob/main/CHANGELOG.md">devalue's
changelog</a>.</em></p>
<blockquote>
<h2>5.6.4</h2>
<h3>Patch Changes</h3>
<ul>
<li>
<p>87c1f3c: fix: reject <code>__proto__</code> keys in malformed
<code>Object</code> wrapper payloads</p>
<p>This validates the <code>"Object"</code> parse path and
throws when the wrapped value has an own <code>__proto__</code> key.</p>
</li>
<li>
<p>40f1db1: fix: ensure sparse array indices are integers</p>
</li>
<li>
<p>87c1f3c: fix: disallow <code>__proto__</code> keys in null-prototype
object parsing</p>
<p>This disallows <code>__proto__</code> keys in the
<code>"null"</code> parse path so null-prototype object
hydration cannot carry that key through parse/unflatten.</p>
</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="6cbb3f5125"><code>6cbb3f5</code></a>
Version Packages (<a
href="https://redirect.github.com/sveltejs/devalue/issues/133">#133</a>)</li>
<li><a
href="40f1db13af"><code>40f1db1</code></a>
Merge commit from fork</li>
<li><a
href="87c1f3ce37"><code>87c1f3c</code></a>
Merge commit from fork</li>
<li>See full diff in <a
href="https://github.com/sveltejs/devalue/compare/v5.6.3...v5.6.4">compare
view</a></li>
</ul>
</details>
<br />
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
<details>
<summary>Dependabot commands and options</summary>
<br />
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/sveltejs/svelte/network/alerts).
</details>
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
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>
### Summary
Fix spelling in the v5 migration guide ("useable" -> "usable").
### 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`). (Not applicable; docs-only change.)
### Tests and linting
- [ ] Run the tests with `pnpm test` and lint the project with `pnpm
lint`. (Not run; docs-only change.)
Co-authored-by: rohan436 <rohan.santhoshkumar@googlemail.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
This adds a `skill: true` to the frontmatter of the bestpractices
skill/doc. This way when we sync we can concern ourselves to only the
documents that need to be skills (we will write one for sveltekit too)
without targeting specific files in the `ai-tools` repo
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>
The combination of #17873 and #17805 resulted in a bad merge of the
latter because it wasn't up to date with main. This fixes it. No
changeset because not released yet.
---------
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>