diff --git a/.changeset/chatty-beans-divide.md b/.changeset/chatty-beans-divide.md new file mode 100644 index 0000000000..bcf47bfbbf --- /dev/null +++ b/.changeset/chatty-beans-divide.md @@ -0,0 +1,5 @@ +--- +"svelte": patch +--- + +fix: use safe-equals comparison for `@const` tags in legacy mode diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js index 46906b3e98..0bd8d9235d 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js @@ -1793,7 +1793,8 @@ export const template_visitors = { b.const( declaration.id, b.call( - '$.derived', + // In runes mode, we want things to be fine-grained - but not in legacy mode + state.options.runes ? '$.derived' : '$.derived_safe_equal', b.thunk(/** @type {import('estree').Expression} */ (visit(declaration.init))) ) ) @@ -1822,7 +1823,10 @@ export const template_visitors = { ]) ); - state.init.push(b.const(tmp, b.call('$.derived', fn))); + state.init.push( + // In runes mode, we want things to be fine-grained - but not in legacy mode + b.const(tmp, b.call(state.options.runes ? '$.derived' : '$.derived_safe_equal', fn)) + ); for (const node of identifiers) { const binding = /** @type {import('#compiler').Binding} */ (state.scope.get(node.name)); diff --git a/packages/svelte/tests/runtime-legacy/samples/const-tag-each-update/_config.js b/packages/svelte/tests/runtime-legacy/samples/const-tag-each-update/_config.js new file mode 100644 index 0000000000..90363bd88e --- /dev/null +++ b/packages/svelte/tests/runtime-legacy/samples/const-tag-each-update/_config.js @@ -0,0 +1,29 @@ +import { tick } from 'svelte'; +import { test } from '../../test'; + +// Test ensures that the `const` tag is coarse-grained in legacy mode (i.e. always fires an update when the array changes) +export default test({ + html: ` + +
0
+1
+2
+3
+ `, + async test({ target, assert }) { + const btn = target.querySelector('button'); + + btn?.click(); + await tick(); + assert.htmlEqual( + target.innerHTML, + ` + +0 show (v_item) show (item)
+1
+2 show (v_item) show (item)
+3
+ ` + ); + } +}); diff --git a/packages/svelte/tests/runtime-legacy/samples/const-tag-each-update/main.svelte b/packages/svelte/tests/runtime-legacy/samples/const-tag-each-update/main.svelte new file mode 100644 index 0000000000..66d0d4d0a2 --- /dev/null +++ b/packages/svelte/tests/runtime-legacy/samples/const-tag-each-update/main.svelte @@ -0,0 +1,21 @@ + + + + +{#each items as item, i} + {@const v_item=item} ++ {i} + {#if v_item?.clicked} + show (v_item) + {/if} + {#if item?.clicked} + show (item) + {/if} +
+{/each}