From 6b23a7c4777a123dc1ea4db6cb87e03268fbb45b Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Fri, 21 Mar 2025 09:52:30 -0400 Subject: [PATCH 1/6] chore: camelCase -> snake_case (#15573) --- packages/svelte/src/compiler/migrate/index.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/svelte/src/compiler/migrate/index.js b/packages/svelte/src/compiler/migrate/index.js index 02bb5b144385..7f26d0d0103a 100644 --- a/packages/svelte/src/compiler/migrate/index.js +++ b/packages/svelte/src/compiler/migrate/index.js @@ -1673,9 +1673,9 @@ function extract_type_and_comment(declarator, state, path) { const match = /@type {(.+)}/.exec(comment_node.value); if (match) { // try to find JSDoc comments after a hyphen `-` - const jsdocComment = /@type {.+} (?:\w+|\[.*?\]) - (.+)/.exec(comment_node.value); - if (jsdocComment) { - cleaned_comment += jsdocComment[1]?.trim(); + const jsdoc_comment = /@type {.+} (?:\w+|\[.*?\]) - (.+)/.exec(comment_node.value); + if (jsdoc_comment) { + cleaned_comment += jsdoc_comment[1]?.trim(); } return { type: match[1], From 83d0c5894dc26c92274f162c9f9495038cabe37d Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Fri, 21 Mar 2025 09:52:51 -0400 Subject: [PATCH 2/6] docs: add note on effect-local state (#15572) --- documentation/docs/02-runes/04-$effect.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/documentation/docs/02-runes/04-$effect.md b/documentation/docs/02-runes/04-$effect.md index 6a2b565aeaa2..75f59102f96b 100644 --- a/documentation/docs/02-runes/04-$effect.md +++ b/documentation/docs/02-runes/04-$effect.md @@ -74,6 +74,8 @@ Teardown functions also run when the effect is destroyed, which happens when its `$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. +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. + 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=)): ```ts From ade66c6feade92cfd932dcb4be2812305e518d2b Mon Sep 17 00:00:00 2001 From: Paolo Ricciuti Date: Fri, 21 Mar 2025 15:21:40 +0100 Subject: [PATCH 3/6] fix: use `get` in constructor for deriveds (#15300) Co-authored-by: Rich Harris --- .changeset/new-cherries-leave.md | 5 +++++ .../client/visitors/MemberExpression.js | 4 +++- .../samples/deriveds-in-constructor/_config.js | 5 +++++ .../deriveds-in-constructor/main.svelte | 18 ++++++++++++++++++ 4 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 .changeset/new-cherries-leave.md create mode 100644 packages/svelte/tests/runtime-runes/samples/deriveds-in-constructor/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/deriveds-in-constructor/main.svelte diff --git a/.changeset/new-cherries-leave.md b/.changeset/new-cherries-leave.md new file mode 100644 index 000000000000..738a78b4a34a --- /dev/null +++ b/.changeset/new-cherries-leave.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: use `get` in constructor for deriveds diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/MemberExpression.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/MemberExpression.js index 501ecda5557d..3f2aada1f575 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/MemberExpression.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/MemberExpression.js @@ -11,7 +11,9 @@ export function MemberExpression(node, context) { if (node.property.type === 'PrivateIdentifier') { const field = context.state.private_state.get(node.property.name); if (field) { - return context.state.in_constructor ? b.member(node, 'v') : b.call('$.get', node); + return context.state.in_constructor && (field.kind === 'raw_state' || field.kind === 'state') + ? b.member(node, 'v') + : b.call('$.get', node); } } diff --git a/packages/svelte/tests/runtime-runes/samples/deriveds-in-constructor/_config.js b/packages/svelte/tests/runtime-runes/samples/deriveds-in-constructor/_config.js new file mode 100644 index 000000000000..b364a989f480 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/deriveds-in-constructor/_config.js @@ -0,0 +1,5 @@ +import { test } from '../../test'; + +export default test({ + html: `

state,derived state,derived.by derived state

` +}); diff --git a/packages/svelte/tests/runtime-runes/samples/deriveds-in-constructor/main.svelte b/packages/svelte/tests/runtime-runes/samples/deriveds-in-constructor/main.svelte new file mode 100644 index 000000000000..bc8efba7e7c2 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/deriveds-in-constructor/main.svelte @@ -0,0 +1,18 @@ + + +

{foo.initial}

\ No newline at end of file From 1f37c02f918d6fa4d8a14de5d6868228e61dd05a Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Fri, 21 Mar 2025 14:25:46 +0000 Subject: [PATCH 4/6] fix: ensure toStore root effect is connected to correct parent effect (#15574) * fix: ensure toStore root effect is connected to correct parent effect * prettier --------- Co-authored-by: Rich Harris --- .changeset/twelve-bananas-destroy.md | 5 +++ packages/svelte/src/store/index-client.js | 35 +++++++++++++++---- .../samples/toStore-subscribe2/_config.js | 16 +++++++++ .../samples/toStore-subscribe2/main.svelte | 11 ++++++ 4 files changed, 60 insertions(+), 7 deletions(-) create mode 100644 .changeset/twelve-bananas-destroy.md create mode 100644 packages/svelte/tests/runtime-runes/samples/toStore-subscribe2/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/toStore-subscribe2/main.svelte diff --git a/.changeset/twelve-bananas-destroy.md b/.changeset/twelve-bananas-destroy.md new file mode 100644 index 000000000000..873ee21877b7 --- /dev/null +++ b/.changeset/twelve-bananas-destroy.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: ensure toStore root effect is connected to correct parent effect diff --git a/packages/svelte/src/store/index-client.js b/packages/svelte/src/store/index-client.js index ae6806ec763f..2f0a1a831a0c 100644 --- a/packages/svelte/src/store/index-client.js +++ b/packages/svelte/src/store/index-client.js @@ -6,6 +6,12 @@ import { } from '../internal/client/reactivity/effects.js'; import { get, writable } from './shared/index.js'; import { createSubscriber } from '../reactivity/create-subscriber.js'; +import { + active_effect, + active_reaction, + set_active_effect, + set_active_reaction +} from '../internal/client/runtime.js'; export { derived, get, readable, readonly, writable } from './shared/index.js'; @@ -39,19 +45,34 @@ export { derived, get, readable, readonly, writable } from './shared/index.js'; * @returns {Writable | Readable} */ export function toStore(get, set) { - let init_value = get(); + var effect = active_effect; + var reaction = active_reaction; + var init_value = get(); + const store = writable(init_value, (set) => { // If the value has changed before we call subscribe, then // we need to treat the value as already having run - let ran = init_value !== get(); + var ran = init_value !== get(); // TODO do we need a different implementation on the server? - const teardown = effect_root(() => { - render_effect(() => { - const value = get(); - if (ran) set(value); + var teardown; + // Apply the reaction and effect at the time of toStore being called + var previous_reaction = active_reaction; + var previous_effect = active_effect; + set_active_reaction(reaction); + set_active_effect(effect); + + try { + teardown = effect_root(() => { + render_effect(() => { + const value = get(); + if (ran) set(value); + }); }); - }); + } finally { + set_active_reaction(previous_reaction); + set_active_effect(previous_effect); + } ran = true; diff --git a/packages/svelte/tests/runtime-runes/samples/toStore-subscribe2/_config.js b/packages/svelte/tests/runtime-runes/samples/toStore-subscribe2/_config.js new file mode 100644 index 000000000000..bc1793e7a461 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/toStore-subscribe2/_config.js @@ -0,0 +1,16 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +export default test({ + async test({ assert, target }) { + let btn = target.querySelector('button'); + + btn?.click(); + flushSync(); + + assert.htmlEqual( + target.innerHTML, + `
Count 1!
Count from store 1!
` + ); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/toStore-subscribe2/main.svelte b/packages/svelte/tests/runtime-runes/samples/toStore-subscribe2/main.svelte new file mode 100644 index 000000000000..82d20105b8eb --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/toStore-subscribe2/main.svelte @@ -0,0 +1,11 @@ + + +
Count {counter}!
+
Count from store {$count}!
+ + From 842a7c6995f94f46b1839fcac91042fd541e52ca Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Fri, 21 Mar 2025 10:26:15 -0400 Subject: [PATCH 5/6] docs: update state_unsafe_mutation message (#15539) * docs: update state_unsafe_mutation message * regenerate * fix example --- .../98-reference/.generated/client-errors.md | 35 +++++++++++-------- .../svelte/messages/client-errors/errors.md | 35 +++++++++++-------- 2 files changed, 40 insertions(+), 30 deletions(-) diff --git a/documentation/docs/98-reference/.generated/client-errors.md b/documentation/docs/98-reference/.generated/client-errors.md index 62d9c3302a3c..901c49822c00 100644 --- a/documentation/docs/98-reference/.generated/client-errors.md +++ b/documentation/docs/98-reference/.generated/client-errors.md @@ -128,26 +128,31 @@ Cannot set prototype of `$state` object Updating state inside a derived or a template expression is forbidden. If the value should not be reactive, declare it without `$state` ``` -This error is thrown in a situation like this: +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: ```svelte - + + +

{count} is even: {even}

+

{count} is odd: {odd}

``` -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. +This is forbidden because it introduces instability: if `

{count} is even: {even}

` is updated before `odd` is recalculated, `even` will be stale. In most cases the solution is to make everything derived: + +```js +let even = $derived(count % 2 === 0); +let odd = $derived(!even); +``` -To fix this: -- See if it's possible to refactor your `$derived` such that the update becomes unnecessary -- 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 -- 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 +If side-effects are unavoidable, use [`$effect`]($effect) instead. diff --git a/packages/svelte/messages/client-errors/errors.md b/packages/svelte/messages/client-errors/errors.md index bc8ec3625605..572930843e78 100644 --- a/packages/svelte/messages/client-errors/errors.md +++ b/packages/svelte/messages/client-errors/errors.md @@ -84,26 +84,31 @@ See the [migration guide](/docs/svelte/v5-migration-guide#Components-are-no-long > Updating state inside a derived or a template expression is forbidden. If the value should not be reactive, declare it without `$state` -This error is thrown in a situation like this: +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: ```svelte - + + +

{count} is even: {even}

+

{count} is odd: {odd}

``` -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. +This is forbidden because it introduces instability: if `

{count} is even: {even}

` is updated before `odd` is recalculated, `even` will be stale. In most cases the solution is to make everything derived: + +```js +let even = $derived(count % 2 === 0); +let odd = $derived(!even); +``` -To fix this: -- See if it's possible to refactor your `$derived` such that the update becomes unnecessary -- 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 -- 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 +If side-effects are unavoidable, use [`$effect`]($effect) instead. From 2d3b65dfbd589416d94661bd34ed7a99896bcde2 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 21 Mar 2025 10:28:23 -0400 Subject: [PATCH 6/6] Version Packages (#15575) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .changeset/new-cherries-leave.md | 5 ----- .changeset/twelve-bananas-destroy.md | 5 ----- packages/svelte/CHANGELOG.md | 8 ++++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 5 files changed, 10 insertions(+), 12 deletions(-) delete mode 100644 .changeset/new-cherries-leave.md delete mode 100644 .changeset/twelve-bananas-destroy.md diff --git a/.changeset/new-cherries-leave.md b/.changeset/new-cherries-leave.md deleted file mode 100644 index 738a78b4a34a..000000000000 --- a/.changeset/new-cherries-leave.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: use `get` in constructor for deriveds diff --git a/.changeset/twelve-bananas-destroy.md b/.changeset/twelve-bananas-destroy.md deleted file mode 100644 index 873ee21877b7..000000000000 --- a/.changeset/twelve-bananas-destroy.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: ensure toStore root effect is connected to correct parent effect diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 8cb7efd4ef14..04ddfcadbd30 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,13 @@ # svelte +## 5.24.1 + +### Patch Changes + +- fix: use `get` in constructor for deriveds ([#15300](https://github.com/sveltejs/svelte/pull/15300)) + +- fix: ensure toStore root effect is connected to correct parent effect ([#15574](https://github.com/sveltejs/svelte/pull/15574)) + ## 5.24.0 ### Minor Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 0aa6b2984123..f321571e7abb 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.24.0", + "version": "5.24.1", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index 7cd43e74cb7d..565c19071396 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -4,5 +4,5 @@ * The current version, as set in package.json. * @type {string} */ -export const VERSION = '5.24.0'; +export const VERSION = '5.24.1'; export const PUBLIC_VERSION = '5'; pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy