Skip to content

Commit a48df4a

Browse files
committed
Merge remote-tracking branch 'origin/main' into templateless-template-generation
2 parents d8afd8e + f498a21 commit a48df4a

File tree

67 files changed

+682
-358
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

67 files changed

+682
-358
lines changed

.changeset/nine-laws-rush.md

Lines changed: 0 additions & 5 deletions
This file was deleted.

benchmarking/compare/index.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import fs from 'node:fs';
22
import path from 'node:path';
33
import { execSync, fork } from 'node:child_process';
44
import { fileURLToPath } from 'node:url';
5-
import { benchmarks } from '../benchmarks.js';
65

76
// if (execSync('git status --porcelain').toString().trim()) {
87
// console.error('Working directory is not clean');

benchmarking/compare/runner.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
import { benchmarks } from '../benchmarks.js';
1+
import { reactivity_benchmarks } from '../benchmarks/reactivity/index.js';
22

33
const results = [];
4-
for (const benchmark of benchmarks) {
4+
for (const benchmark of reactivity_benchmarks) {
55
const result = await benchmark();
66
console.error(result.benchmark);
77
results.push(result);

documentation/docs/02-runes/03-$derived.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,48 @@ Anything read synchronously inside the `$derived` expression (or `$derived.by` f
5252

5353
To exempt a piece of state from being treated as a dependency, use [`untrack`](svelte#untrack).
5454

55+
## Overriding derived values
56+
57+
Derived expressions are recalculated when their dependencies change, but you can temporarily override their values by reassigning them (unless they are declared with `const`). This can be useful for things like _optimistic UI_, where a value is derived from the 'source of truth' (such as data from your server) but you'd like to show immediate feedback to the user:
58+
59+
```svelte
60+
<script>
61+
let { post, like } = $props();
62+
63+
let likes = $derived(post.likes);
64+
65+
async function onclick() {
66+
// increment the `likes` count immediately...
67+
likes += 1;
68+
69+
// and tell the server, which will eventually update `post`
70+
try {
71+
await like();
72+
} catch {
73+
// failed! roll back the change
74+
likes -= 1;
75+
}
76+
}
77+
</script>
78+
79+
<button {onclick}>🧡 {likes}</button>
80+
```
81+
82+
> [!NOTE] Prior to Svelte 5.25, deriveds were read-only.
83+
84+
## Deriveds and reactivity
85+
86+
Unlike `$state`, which converts objects and arrays to [deeply reactive proxies]($state#Deep-state), `$derived` values are left as-is. For example, [in a case like this](/playground/untitled#H4sIAAAAAAAAE4VU22rjMBD9lUHd3aaQi9PdstS1A3t5XvpQ2Ic4D7I1iUUV2UjjNMX431eS7TRdSosxgjMzZ45mjt0yzffIYibvy0ojFJWqDKCQVBk2ZVup0LJ43TJ6rn2aBxw-FP2o67k9oCKP5dziW3hRaUJNjoYltjCyplWmM1JIIAn3FlL4ZIkTTtYez6jtj4w8WwyXv9GiIXiQxLVs9pfTMR7EuoSLIuLFbX7Z4930bZo_nBrD1bs834tlfvsBz9_SyX6PZXu9XaL4gOWn4sXjeyzftv4ZWfyxubpzxzg6LfD4MrooxELEosKCUPigQCMPKCZh0OtQE1iSxcsmdHuBvCiHZXALLXiN08EL3RRkaJ_kDVGle0HcSD5TPEeVtj67O4Nrg9aiSNtBY5oODJkrL5QsHtN2cgXp6nSJMWzpWWGasdlsGEMbzi5jPr5KFr0Ep7pdeM2-TCelCddIhDxAobi1jqF3cMaC1RKp64bAW9iFAmXGIHfd4wNXDabtOLN53w8W53VvJoZLh7xk4Rr3CoL-UNoLhWHrT1JQGcM17u96oES5K-kc2XOzkzqGCKL5De79OUTyyrg1zgwXsrEx3ESfx4Bz0M5UjVMHB24mw9SuXtXFoN13fYKOM1tyUT3FbvbWmSWCZX2Er-41u5xPoml45svRahl9Wb9aasbINJixDZwcPTbyTLZSUsAvrg_cPuCR7s782_WU8343Y72Qtlb8OYatwuOQvuN13M_hJKNfxann1v1U_B1KZ_D_mzhzhz24fw85CSz2irtN9w9HshBK7AQAAA==)...
87+
88+
```svelte
89+
let items = $state([...]);
90+
91+
let index = $state(0);
92+
let selected = $derived(items[index]);
93+
```
94+
95+
...you can change (or `bind:` to) properties of `selected` and it will affect the underlying `items` array. If `items` was _not_ deeply reactive, mutating `selected` would have no effect.
96+
5597
## Update propagation
5698

5799
Svelte uses something called _push-pull reactivity_ — when state is updated, everything that depends on the state (whether directly or indirectly) is immediately notified of the change (the 'push'), but derived values are not re-evaluated until they are actually read (the 'pull').

documentation/docs/02-runes/04-$effect.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ Teardown functions also run when the effect is destroyed, which happens when its
7474

7575
`$effect` automatically picks up any reactive values (`$state`, `$derived`, `$props`) that are _synchronously_ read inside its function body (including indirectly, via function calls) and registers them as dependencies. When those dependencies change, the `$effect` schedules a re-run.
7676

77+
If `$state` and `$derived` are used directly inside the `$effect` (for example, during creation of a [reactive class](https://svelte.dev/docs/svelte/$state#Classes)), those values will _not_ be treated as dependencies.
78+
7779
Values that are read _asynchronously_ — after an `await` or inside a `setTimeout`, for example — will not be tracked. Here, the canvas will be repainted when `color` changes, but not when `size` changes ([demo](/playground/untitled#H4sIAAAAAAAAE31T246bMBD9lZF3pWSlBEirfaEQqdo_2PatVIpjBrDkGGQPJGnEv1e2IZfVal-wfHzmzJyZ4cIqqdCy9M-F0blDlnqArZjmB3f72XWRHVCRw_bc4me4aDWhJstSlllhZEfbQhekkMDKfwg5PFvihMvX5OXH_CJa1Zrb0-Kpqr5jkiwC48rieuDWQbqgZ6wqFLRcvkC-hYvnkWi1dWqa8ESQTxFRjfQWsOXiWzmr0sSLhEJu3p1YsoJkNUcdZUnN9dagrBu6FVRQHAM10sJRKgUG16bXcGxQ44AGdt7SDkTDdY02iqLHnJVU6hedlWuIp94JW6Tf8oBt_8GdTxlF0b4n0C35ZLBzXb3mmYn3ae6cOW74zj0YVzDNYXRHFt9mprNgHfZSl6mzml8CMoLvTV6wTZIUDEJv5us2iwMtiJRyAKG4tXnhl8O0yhbML0Wm-B7VNlSSSd31BG7z8oIZZ6dgIffAVY_5xdU9Qrz1Bnx8fCfwtZ7v8Qc9j3nB8PqgmMWlHIID6-bkVaPZwDySfWtKNGtquxQ23Qlsq2QJT0KIqb8dL0up6xQ2eIBkAg_c1FI_YqW0neLnFCqFpwmreedJYT7XX8FVOBfwWRhXstZrSXiwKQjUhOZeMIleb5JZfHWn2Yq5pWEpmR7Hv-N_wEqT8hEEAAA=)):
7880

7981
```ts
@@ -252,6 +254,8 @@ In general, `$effect` is best considered something of an escape hatch — useful
252254

253255
> [!NOTE] For things that are more complicated than a simple expression like `count * 2`, you can also use `$derived.by`.
254256
257+
If you're using an effect because you want to be able to reassign the derived value (to build an optimistic UI, for example) note that [deriveds can be directly overridden]($derived#Overriding-derived-values) as of Svelte 5.25.
258+
255259
You might be tempted to do something convoluted with effects to link one value to another. The following example shows two inputs for "money spent" and "money left" that are connected to each other. If you update one, the other should update accordingly. Don't use effects for this ([demo](/playground/untitled#H4sIAAAAAAAACpVRy26DMBD8FcvKgUhtoIdeHBwp31F6MGSJkBbHwksEQvx77aWQqooq9bgzOzP7mGTdIHipPiZJowOpGJAv0po2VmfnDv4OSBErjYdneHWzBJaCjcx91TWOToUtCIEE3cig0OIty44r5l1oDtjOkyFIsv3GINQ_CNYyGegd1DVUlCR7oU9iilDUcP8S8roYs9n8p2wdYNVFm4csTx872BxNCcjr5I11fdgonEkXsjP2CoUUZWMv6m6wBz2x7yxaM-iJvWeRsvSbSVeUy5i0uf8vKA78NIeJLSZWv1I8jQjLdyK4XuTSeIdmVKJGGI4LdjVOiezwDu1yG74My8PLCQaSiroe5s_5C2PHrkVGAgAA)):
256260

257261
```svelte

documentation/docs/98-reference/.generated/client-errors.md

Lines changed: 20 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -122,38 +122,37 @@ Property descriptors defined on `$state` objects must contain `value` and always
122122
Cannot set prototype of `$state` object
123123
```
124124

125-
### state_unsafe_local_read
126-
127-
```
128-
Reading state that was created inside the same derived is forbidden. Consider using `untrack` to read locally created state
129-
```
130-
131125
### state_unsafe_mutation
132126

133127
```
134128
Updating state inside a derived or a template expression is forbidden. If the value should not be reactive, declare it without `$state`
135129
```
136130

137-
This error is thrown in a situation like this:
131+
This error occurs when state is updated while evaluating a `$derived`. You might encounter it while trying to 'derive' two pieces of state in one go:
138132

139133
```svelte
140134
<script>
141-
let count = $state(0);
142-
let multiple = $derived.by(() => {
143-
const result = count * 2;
144-
if (result > 10) {
145-
count = 0;
146-
}
147-
return result;
148-
});
135+
let count = $state(0);
136+
137+
let even = $state(true);
138+
139+
let odd = $derived.by(() => {
140+
even = count % 2 === 0;
141+
return !even;
142+
});
149143
</script>
150144
151-
<button onclick={() => count++}>{count} / {multiple}</button>
145+
<button onclick={() => count++}>{count}</button>
146+
147+
<p>{count} is even: {even}</p>
148+
<p>{count} is odd: {odd}</p>
152149
```
153150

154-
Here, the `$derived` updates `count`, which is `$state` and therefore forbidden to do. It is forbidden because the reactive graph could become unstable as a result, leading to subtle bugs, like values being stale or effects firing in the wrong order. To prevent this, Svelte errors when detecting an update to a `$state` variable.
151+
This is forbidden because it introduces instability: if `<p>{count} is even: {even}</p>` is updated before `odd` is recalculated, `even` will be stale. In most cases the solution is to make everything derived:
152+
153+
```js
154+
let even = $derived(count % 2 === 0);
155+
let odd = $derived(!even);
156+
```
155157

156-
To fix this:
157-
- See if it's possible to refactor your `$derived` such that the update becomes unnecessary
158-
- Think about why you need to update `$state` inside a `$derived` in the first place. Maybe it's because you're using `bind:`, which leads you down a bad code path, and separating input and output path (by splitting it up to an attribute and an event, or by using [Function bindings](bind#Function-bindings)) makes it possible avoid the update
159-
- If it's unavoidable, you may need to use an [`$effect`]($effect) instead. This could include splitting parts of the `$derived` into an [`$effect`]($effect) which does the updates
158+
If side-effects are unavoidable, use [`$effect`]($effect) instead.

documentation/docs/98-reference/.generated/compile-errors.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -660,6 +660,12 @@ Cannot access a computed property of a rune
660660
`%name%` is not a valid rune
661661
```
662662

663+
### rune_invalid_spread
664+
665+
```
666+
`%rune%` cannot be called with a spread argument
667+
```
668+
663669
### rune_invalid_usage
664670

665671
```

packages/svelte/CHANGELOG.md

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,53 @@
11
# svelte
22

3+
## 5.25.3
4+
5+
### Patch Changes
6+
7+
- fix: prevent state runes from being called with spread ([#15585](https://github.com/sveltejs/svelte/pull/15585))
8+
9+
## 5.25.2
10+
11+
### Patch Changes
12+
13+
- feat: migrate reassigned deriveds to `$derived` ([#15581](https://github.com/sveltejs/svelte/pull/15581))
14+
15+
## 5.25.1
16+
17+
### Patch Changes
18+
19+
- fix: prevent dev server from throwing errors when attempting to retrieve the proxied value of an iframe's contentWindow ([#15577](https://github.com/sveltejs/svelte/pull/15577))
20+
21+
## 5.25.0
22+
23+
### Minor Changes
24+
25+
- feat: make deriveds writable ([#15570](https://github.com/sveltejs/svelte/pull/15570))
26+
27+
## 5.24.1
28+
29+
### Patch Changes
30+
31+
- fix: use `get` in constructor for deriveds ([#15300](https://github.com/sveltejs/svelte/pull/15300))
32+
33+
- fix: ensure toStore root effect is connected to correct parent effect ([#15574](https://github.com/sveltejs/svelte/pull/15574))
34+
35+
## 5.24.0
36+
37+
### Minor Changes
38+
39+
- feat: allow state created in deriveds/effects to be written/read locally without self-invalidation ([#15553](https://github.com/sveltejs/svelte/pull/15553))
40+
41+
### Patch Changes
42+
43+
- fix: check if DOM prototypes are extensible ([#15569](https://github.com/sveltejs/svelte/pull/15569))
44+
45+
- Keep inlined trailing JSDoc comments of properties when running svelte-migrate ([#15567](https://github.com/sveltejs/svelte/pull/15567))
46+
47+
- fix: simplify set calls for proxyable values ([#15548](https://github.com/sveltejs/svelte/pull/15548))
48+
49+
- fix: don't depend on deriveds created inside the current reaction ([#15564](https://github.com/sveltejs/svelte/pull/15564))
50+
351
## 5.23.2
452

553
### Patch Changes

packages/svelte/messages/client-errors/errors.md

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -80,34 +80,35 @@ See the [migration guide](/docs/svelte/v5-migration-guide#Components-are-no-long
8080

8181
> Cannot set prototype of `$state` object
8282
83-
## state_unsafe_local_read
84-
85-
> Reading state that was created inside the same derived is forbidden. Consider using `untrack` to read locally created state
86-
8783
## state_unsafe_mutation
8884

8985
> Updating state inside a derived or a template expression is forbidden. If the value should not be reactive, declare it without `$state`
9086
91-
This error is thrown in a situation like this:
87+
This error occurs when state is updated while evaluating a `$derived`. You might encounter it while trying to 'derive' two pieces of state in one go:
9288

9389
```svelte
9490
<script>
95-
let count = $state(0);
96-
let multiple = $derived.by(() => {
97-
const result = count * 2;
98-
if (result > 10) {
99-
count = 0;
100-
}
101-
return result;
102-
});
91+
let count = $state(0);
92+
93+
let even = $state(true);
94+
95+
let odd = $derived.by(() => {
96+
even = count % 2 === 0;
97+
return !even;
98+
});
10399
</script>
104100
105-
<button onclick={() => count++}>{count} / {multiple}</button>
101+
<button onclick={() => count++}>{count}</button>
102+
103+
<p>{count} is even: {even}</p>
104+
<p>{count} is odd: {odd}</p>
106105
```
107106

108-
Here, the `$derived` updates `count`, which is `$state` and therefore forbidden to do. It is forbidden because the reactive graph could become unstable as a result, leading to subtle bugs, like values being stale or effects firing in the wrong order. To prevent this, Svelte errors when detecting an update to a `$state` variable.
107+
This is forbidden because it introduces instability: if `<p>{count} is even: {even}</p>` is updated before `odd` is recalculated, `even` will be stale. In most cases the solution is to make everything derived:
108+
109+
```js
110+
let even = $derived(count % 2 === 0);
111+
let odd = $derived(!even);
112+
```
109113

110-
To fix this:
111-
- See if it's possible to refactor your `$derived` such that the update becomes unnecessary
112-
- Think about why you need to update `$state` inside a `$derived` in the first place. Maybe it's because you're using `bind:`, which leads you down a bad code path, and separating input and output path (by splitting it up to an attribute and an event, or by using [Function bindings](bind#Function-bindings)) makes it possible avoid the update
113-
- If it's unavoidable, you may need to use an [`$effect`]($effect) instead. This could include splitting parts of the `$derived` into an [`$effect`]($effect) which does the updates
114+
If side-effects are unavoidable, use [`$effect`]($effect) instead.

packages/svelte/messages/compile-errors/script.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,10 @@ This turned out to be buggy and unpredictable, particularly when working with de
162162

163163
> `%name%` is not a valid rune
164164
165+
## rune_invalid_spread
166+
167+
> `%rune%` cannot be called with a spread argument
168+
165169
## rune_invalid_usage
166170

167171
> Cannot use `%rune%` rune in non-runes mode

0 commit comments

Comments
 (0)