From a75866f34d5b64e0c40b2888a5f89a69ec1151f9 Mon Sep 17 00:00:00 2001 From: Artyom Alekseevich <47069814+FrankFMY@users.noreply.github.com> Date: Thu, 5 Feb 2026 23:03:40 +0100 Subject: [PATCH] fix: detect store in each block expression regardless of AST shape (#17636) The store invalidation detection in each blocks only checked for Identifier and MemberExpression AST node types. This caused bind: on iteration variables to silently fail when the expression used logical operators (e.g. `{#each $store.items ?? [] as item}`). Use expression metadata dependencies instead of AST type checking to find store_sub bindings, which correctly handles all expression shapes. Fixes #14625 --- .changeset/fix-each-bind-store-logical.md | 5 +++++ .../3-transform/client/visitors/EachBlock.js | 12 ++++------ .../store-each-binding-logical/_config.js | 22 +++++++++++++++++++ .../store-each-binding-logical/main.svelte | 13 +++++++++++ 4 files changed, 44 insertions(+), 8 deletions(-) create mode 100644 .changeset/fix-each-bind-store-logical.md create mode 100644 packages/svelte/tests/runtime-legacy/samples/store-each-binding-logical/_config.js create mode 100644 packages/svelte/tests/runtime-legacy/samples/store-each-binding-logical/main.svelte diff --git a/.changeset/fix-each-bind-store-logical.md b/.changeset/fix-each-bind-store-logical.md new file mode 100644 index 0000000000..1327015124 --- /dev/null +++ b/.changeset/fix-each-bind-store-logical.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: detect store in each block expression regardless of AST shape diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/EachBlock.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/EachBlock.js index b2724fa90f..a1371b516a 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/EachBlock.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/EachBlock.js @@ -101,15 +101,11 @@ export function EachBlock(node, context) { } // If the array is a store expression, we need to invalidate it when the array is changed. - // This doesn't catch all cases, but all the ones that Svelte 4 catches, too. let store_to_invalidate = ''; - if (node.expression.type === 'Identifier' || node.expression.type === 'MemberExpression') { - const id = object(node.expression); - if (id) { - const binding = context.state.scope.get(id.name); - if (binding?.kind === 'store_sub') { - store_to_invalidate = id.name; - } + for (const binding of node.metadata.expression.dependencies) { + if (binding.kind === 'store_sub') { + store_to_invalidate = binding.node.name; + break; } } diff --git a/packages/svelte/tests/runtime-legacy/samples/store-each-binding-logical/_config.js b/packages/svelte/tests/runtime-legacy/samples/store-each-binding-logical/_config.js new file mode 100644 index 0000000000..1134b20d0c --- /dev/null +++ b/packages/svelte/tests/runtime-legacy/samples/store-each-binding-logical/_config.js @@ -0,0 +1,22 @@ +import { flushSync } from 'svelte'; +import { ok, test } from '../../test'; + +export default test({ + test({ assert, target, window }) { + const input = target.querySelector('input'); + ok(input); + + const event = new window.Event('input'); + input.value = 'changed'; + input.dispatchEvent(event); + flushSync(); + + assert.htmlEqual( + target.innerHTML, + ` + +

changed

+ ` + ); + } +}); diff --git a/packages/svelte/tests/runtime-legacy/samples/store-each-binding-logical/main.svelte b/packages/svelte/tests/runtime-legacy/samples/store-each-binding-logical/main.svelte new file mode 100644 index 0000000000..77731ea5a4 --- /dev/null +++ b/packages/svelte/tests/runtime-legacy/samples/store-each-binding-logical/main.svelte @@ -0,0 +1,13 @@ + + +{#each $items ?? [] as item} + +{/each} + +

{$items[0].text}