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
pull/17640/head
Artyom Alekseevich 8 months ago committed by GitHub
parent 660c4c12b1
commit a75866f34d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: detect store in each block expression regardless of AST shape

@ -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;
}
}

@ -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,
`
<input>
<p>changed</p>
`
);
}
});

@ -0,0 +1,13 @@
<script>
import { writable } from 'svelte/store';
const items = writable([
{ id: 0, text: 'initial' }
]);
</script>
{#each $items ?? [] as item}
<input bind:value={item.text}>
{/each}
<p>{$items[0].text}</p>
Loading…
Cancel
Save