While looking into #17954 I realised that a) our code for handling
parentheses in expressions is unnecessarily convoluted and b) it doesn't
handle the case where you have an opening parent outside the first
comment — this fails to parse:
```svelte
{(/**/ 42)}
```
This fixes it and simplifies the code a good bit.
### 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#17571 where the situation is the following:
A derived creates a new query. That query initializes loading with true.
This means the if block is marked for destruction (therefore effects
inside branch are skipped), but it's not doing that yet because the
query promise is pending. Then query resolves and loading is set back to
false right before resolving, but it's not the same tick so
`loading=false` is a separate thing. Because that later batch doesn't
see any overlap with an earlier batch (the earlier batch did set loading
to true but not via set but indirectly via recreating the query) it
doesn't wait on it and flushes right away. Now the if block is marked as
visible again but the earlier batch doesn't know that if noone unskips
its branch. If we don't do that the render effect that is now dirty as
part of that batch will not run.
Fixes part of #17940 (the hydration->error thing still needs a repro).
Essentially in sync mode render effects are executed during traversing
the effect tree, and when flushSync is called during that it can cause
the timing of things getting out of sync such that you end up wanting to
rebase another branch, which is never needed in sync mode.
So we just skip that logic in sync mode. Another way would be to adjust
flushSync such that it does appends itself to the end of the current
flushing cycle if it notices processing is already ongoing. This will
not work when you pass a callback function to `flushSync` though -
another indicator that we should probably remove that callback argument.
---------
Co-authored-by: Rich Harris <rich.harris@vercel.com>
Co-authored-by: Rich Harris <hello@rich-harris.dev>
If a new branch is created (e.g. if block becomes truthy) inside a fork
and a derived is read inside the new branch for the first time, it will
get reactions but its true value will stay uninitialized due to the
fork. If the fork then commits, it will NOT change its value because it
will not reexecute: there is no effect running after commit telling it
to do that, because it was only read inside new effects which already
ran while still inside the fork.
Now, when that branch is removed (e.g. if block becomes falsy), the
derived loses its reactions again and becomes disconnected, at which
point the status is reset. So we end up with a maybe_dirty or clean
derived and an uninitialized value, causing breakage if it's called
again afterwards.
The fix is to not reset the status in this case.
Fixes https://github.com/sveltejs/kit/issues/15126
Fixes https://github.com/sveltejs/kit/issues/15318
Fixes https://github.com/sveltejs/kit/issues/15061
`<details>` elements fire `ontoggle` as ToggleEvents
([source](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/toggle_event)),
but they're currently just typed as Event.
### 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:`.
- [ ] 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`
Adds documentation for using CSS custom properties with the `style:`
directive. I think this was lost at some point during the great
transition. It's only mentioned in the [best
practices](https://svelte.dev/docs/svelte/best-practices#Using-JavaScript-variables-in-CSS)
section, so I thought it would make sense to include it.
Gets rid of some annoying console spam. I don't totally understand why
`warnings` isn't populated by the time the assertions happen (it seems
to happen just afterwards, except if I sleep for a few milliseconds and
then do the assertion the warnings get swallowed altogether?) but for
right now I don't much care
This makes the benchmarks slightly more honest — it adds the async flag,
and a benchmark that tests the (common) scenario where most effects are
clean. Relevant to #18035, will self-merge so we can do a fresh
comparison
extracted from #18035 — this should have been part of #18047 but I
missed it off, oops. Will self-merge so it doesn't get in the way of
#18035, and because the `performance-investigation` skill is predicated
on this code existing
---------
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
This adds some hints in an `AGENTS.md` for how to conduct performance
investigations, along with a utility for comparing profiles from
different branches to identify hotspots. We will likely want to put
other stuff in `AGENTS.md` in future but I figured we could start with
something immediately useful.
It also tweaks the comparison script — rather than nuking results from
existing branches, it keeps them around so that we don't need to re-run
benchmarks (which takes a long time) for branches that haven't changed.
---------
Co-authored-by: vercel[bot] <35613825+vercel[bot]@users.noreply.github.com>
This started out as me implementing
https://github.com/sveltejs/svelte/pull/17998/changes#r3018047965, but
then I realised that I'd also fixed the bug that #17998 addresses. So I
guess it's an alternative to that PR
---------
Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com>
This causes `pnpm bench` and `pnpm bench:compare` to generate
`.cpuprofile` files for each benchmark, which should in theory make it
easier to understand how different branches affect performance (and find
opportunities for optimisation). These files can be opened directly in
VS Code and other editors, or in
[profiler.firefox.com](https://profiler.firefox.com),
[speedscope.app](https://www.speedscope.app), Chrome's performance
devtools and so on.
### 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`
---------
Co-authored-by: Rich Harris <hello@rich-harris.dev>
Co-authored-by: Rich Harris <rich.harris@vercel.com>
Re-submission of #18017. Had a problem with my fork and had to delete
it, which closed the original PR. Apologies for the noise.
## Summary
This PR pins all GitHub Actions to immutable commit SHAs instead of
mutable version tags.
- Pin 9 unpinned actions to full 40-character SHAs
- Add version comments for readability
## Changes by file
| File | Changes |
|------|---------|
| autofix.yml | Pinned actions to SHA |
| ci.yml | Pinned actions to SHA |
| pkg.pr.new.yml | Pinned actions to SHA |
| release.yml | Pinned actions to SHA |
## A note on internal action pinning
This PR pins all actions including org-owned ones. Best practice is to
pin everything — the tj-actions/changed-files attack was an internally
maintained action that was compromised, and every repo referencing it by
tag silently executed attacker code. That said, it's your codebase. If
you'd prefer to leave org-owned actions unpinned, let us know and we'll
adjust the PR.
## How to verify
Review the diff — each change is mechanical and preserves workflow
behavior:
- **SHA pinning**: `action@v3` becomes `action@abc123 # v3` — original
version preserved as comment
- No workflow logic, triggers, or permissions are modified
I wrote a scanner called Runner Guard and open sourced it
[here](https://github.com/Vigilant-LLC/runner-guard).
If you have any questions, reach out. I'll be monitoring comms.
\- Chris Nyhuis (dagecko)
I don't know why exactly, but VS Code suddenly started paying attention
to this line. An indent size of 2 inside an IDE is for masochists and
barbarians. If that's your preference then that's up to you but there's
certainly no reason to impose it on everyone who clones this repo.
## 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 takes advantage of https://github.com/sveltejs/svelte.dev/pull/1879
and https://github.com/sveltejs/svelte.dev/pull/1880 to replace some of
the hardcoded playground links in the docs with ones that are generated
automatically from the code. This is a better user experience, and
easier to maintain.
Not every playground link is suitable for replacement, as in many cases
we gloss over implementation details. For now I'm just starting with
some easy ones
extracting this from #17971 because even if that PR is not merged these
tests show that there's still a lot of cases where we're buggy,
particularly around forking and new branches
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.
Closes#17660
### 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.
- [ ] 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>
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>
### 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`
---------
Co-authored-by: Rich Harris <rich.harris@vercel.com>
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>