fix: support bind: in {#each} with $derived($store) expressions #17637
+133
−1
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Closes #13569
Problem
When using
{#each}withbind:on items from a$derivedexpression that wraps a store (e.g.{#each $derived($store) as item}), mutations throughbind:silently fail — neither the input nor any expression reading the derived updates.This happens because the compiler doesn't detect the store subscription hidden behind the derived binding:
store_to_invalidateis empty — the each block's mutation handler never callsinvalidate_store, so the store is never notified of changesEACH_ITEM_IMMUTABLEis set — items use strict===equality instead ofsafe_equals, so same-reference object mutations aren't detected===equality — even if the store were invalidated, the derived returns the same array reference and===says "no change", preventing propagation to subscribers outside the each blockFix
Compiler (EachBlock.js):
find_store_sub_name()helper that walks an AST expression tree to find store subscription bindingsuses_storedetection: when a dependency is aderivedbinding, traces through itsinitial(init expression) to find store subscriptionsstore_to_invalidatedetection: same tracing for derived bindingsEACH_ITEM_IMMUTABLEis correctly not set when the expression depends on a store through a derivedCompiler (VariableDeclaration.js):
$derived(expr)whereexprdepends on a store subscription, the compiler now emits$.derived_safe_equal()instead of$.derived(). This uses object-aware equality (safe_equals) rather than strict reference equality (===), so same-reference mutations are detected and propagated to all subscribers (including expressions outside the each block like<p>{items[0].text}</p>)Test
Added
each-bind-derived-storetest that verifies:bind:valueon items from{#each $derived($store) as item}works<p>tag reading from the derived outside the each block