Skip to content

Commit e75e987

Browse files
committed
fix: use safe-equals comparison for @const tags in legacy mode
fixes #10600
1 parent 6afaf75 commit e75e987

File tree

4 files changed

+80
-13
lines changed

4 files changed

+80
-13
lines changed

.changeset/chatty-beans-divide.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"svelte": patch
3+
---
4+
5+
fix: use safe-equals comparison for `@const` tags in legacy mode

packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1789,15 +1789,17 @@ export const template_visitors = {
17891789
const declaration = node.declaration.declarations[0];
17901790
// TODO we can almost certainly share some code with $derived(...)
17911791
if (declaration.id.type === 'Identifier') {
1792-
state.init.push(
1793-
b.const(
1794-
declaration.id,
1795-
b.call(
1796-
'$.derived',
1797-
b.thunk(/** @type {import('estree').Expression} */ (visit(declaration.init)))
1798-
)
1799-
)
1800-
);
1792+
const fn = b.thunk(/** @type {import('estree').Expression} */ (visit(declaration.init)));
1793+
// In runes mode, we want things to be fine-grained - but not in legacy mode
1794+
if (state.options.runes) {
1795+
state.init.push(b.const(declaration.id, b.call('$.derived', fn)));
1796+
} else {
1797+
state.init.push(b.const(declaration.id, fn));
1798+
const binding = /** @type {import('#compiler').Binding} */ (
1799+
state.scope.get(declaration.id.name)
1800+
);
1801+
binding.expression = b.call(declaration.id);
1802+
}
18011803
} else {
18021804
const identifiers = extract_identifiers(declaration.id);
18031805
const tmp = b.id(state.scope.generate('computed_const'));
@@ -1822,11 +1824,21 @@ export const template_visitors = {
18221824
])
18231825
);
18241826

1825-
state.init.push(b.const(tmp, b.call('$.derived', fn)));
1827+
// In runes mode, we want things to be fine-grained - but not in legacy mode
1828+
if (state.options.runes) {
1829+
state.init.push(b.const(tmp, b.call('$.derived', fn)));
18261830

1827-
for (const node of identifiers) {
1828-
const binding = /** @type {import('#compiler').Binding} */ (state.scope.get(node.name));
1829-
binding.expression = b.member(b.call('$.get', tmp), node);
1831+
for (const node of identifiers) {
1832+
const binding = /** @type {import('#compiler').Binding} */ (state.scope.get(node.name));
1833+
binding.expression = b.member(b.call('$.get', tmp), node);
1834+
}
1835+
} else {
1836+
state.init.push(b.const(tmp, fn));
1837+
1838+
for (const node of identifiers) {
1839+
const binding = /** @type {import('#compiler').Binding} */ (state.scope.get(node.name));
1840+
binding.expression = b.member(b.call(tmp), node);
1841+
}
18301842
}
18311843
}
18321844
},
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { tick } from 'svelte';
2+
import { test } from '../../test';
3+
4+
// Test ensures that the `const` tag is coarse-grained in legacy mode (i.e. always fires an update when the array changes)
5+
export default test({
6+
html: `
7+
<button>Show</button>
8+
<p>0</p>
9+
<p>1</p>
10+
<p>2</p>
11+
<p>3</p>
12+
`,
13+
async test({ target, assert }) {
14+
const btn = target.querySelector('button');
15+
16+
btn?.click();
17+
await tick();
18+
assert.htmlEqual(
19+
target.innerHTML,
20+
`
21+
<button>Show</button>
22+
<p>0 show (v_item) show (item)</p>
23+
<p>1</p>
24+
<p>2 show (v_item) show (item)</p>
25+
<p>3</p>
26+
`
27+
);
28+
}
29+
});
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<script>
2+
export let items = {0:{clicked:false},length:4};
3+
</script>
4+
5+
<button on:click={()=>{
6+
items[0].clicked=true;
7+
items[2]={clicked:true};
8+
}}>Show</button>
9+
10+
{#each items as item, i}
11+
{@const v_item=item}
12+
<p>
13+
{i}
14+
{#if v_item?.clicked}
15+
show (v_item)
16+
{/if}
17+
{#if item?.clicked}
18+
show (item)
19+
{/if}
20+
</p>
21+
{/each}

0 commit comments

Comments
 (0)