Skip to content

Commit fa3e4d0

Browse files
committed
fix: treat all spread attributes as reactive
The objects could contain getters with reactive values, so we play it safe and assume they're always reactive fixes #10065
1 parent 2133d7d commit fa3e4d0

File tree

4 files changed

+33
-4
lines changed

4 files changed

+33
-4
lines changed

.changeset/sharp-kids-happen.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: always treat spread attributes as reactive

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

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -314,7 +314,8 @@ function serialize_element_spread_attributes(attributes, context, element, eleme
314314

315315
is_reactive ||=
316316
attribute.metadata.dynamic ||
317-
(attribute.type === 'SpreadAttribute' && attribute.metadata.contains_call_expression);
317+
// objects could contain reactive getters -> play it safe and always assume spread attributes are reactive
318+
attribute.type === 'SpreadAttribute';
318319
}
319320

320321
const lowercase_attributes =
@@ -388,7 +389,10 @@ function serialize_dynamic_element_spread_attributes(attributes, context, elemen
388389
values.push(/** @type {import('estree').Expression} */ (context.visit(attribute)));
389390
}
390391

391-
is_reactive ||= attribute.metadata.dynamic;
392+
is_reactive ||=
393+
attribute.metadata.dynamic ||
394+
// objects could contain reactive getters -> play it safe and always assume spread attributes are reactive
395+
attribute.type === 'SpreadAttribute';
392396
}
393397

394398
if (is_reactive) {
Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
import { test } from '../../test';
22

33
export default test({
4-
html: `<div style="color: red;"></div><div class="red"></div><button>toggle</button`,
4+
html: `
5+
<div style="color: red;"></div><div class="red"></div><div class="red"></div>
6+
<div style="color: red;"></div><div class="red"></div><div class="red"></div>
7+
<button>toggle</button
8+
`,
59

610
async test({ assert, target }) {
711
const [b1] = target.querySelectorAll('button');
@@ -10,7 +14,11 @@ export default test({
1014
await Promise.resolve();
1115
assert.htmlEqual(
1216
target.innerHTML,
13-
'<div class="blue" style="color: blue;"></div><div class="blue"></div><button>toggle</button>'
17+
`
18+
<div class="blue" style="color: blue;"></div><div class="blue"></div><div class="blue"></div>
19+
<div class="blue" style="color: blue;"></div><div class="blue"></div><div class="blue"></div>
20+
<button>toggle</button
21+
`
1422
);
1523
}
1624
});

packages/svelte/tests/runtime-runes/samples/event-attribute-call-expressions/main.svelte

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
<script>
22
let value = $state('red');
3+
let tag = $state('div');
34
45
const getValue = () => {
56
return value;
@@ -10,9 +11,20 @@
1011
const getSpread = () => {
1112
return { class: value };
1213
}
14+
const props = {
15+
get class() {
16+
return value;
17+
}
18+
}
1319
</script>
1420

1521
<div class:blue={getClass()} style:color={getValue()}></div>
1622
<div {...getSpread()}></div>
23+
<div {...props}></div>
24+
25+
<svelte:element this={tag} class:blue={getClass()} style:color={getValue()}></svelte:element>
26+
<svelte:element this={tag} {...getSpread()}></svelte:element>
27+
<svelte:element this={tag} {...props}></svelte:element>
28+
1729
<button on:click={() => value = 'blue'}>toggle</button>
1830

0 commit comments

Comments
 (0)