fix: use coarse-grained updates for derived expressions in legacy mode (#11652)

fixes #11648
Also deduplicates the code a bit
pull/11659/head
Simon H 1 year ago committed by GitHub
parent 54083fb9cc
commit c2f75dc7ba
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
"svelte": patch
---
fix: use coarse-grained updates for derived expressions passed to props in legacy mode

@ -632,6 +632,15 @@ function collect_parent_each_blocks(context) {
);
}
/**
* Svelte legacy mode should use safe equals in most places, runes mode shouldn't
* @param {import('../types.js').ComponentClientTransformState} state
* @param {import('estree').Expression} arg
*/
function create_derived(state, arg) {
return b.call(state.analysis.runes ? '$.derived' : '$.derived_safe_equal', arg);
}
/**
* @param {import('#compiler').Component | import('#compiler').SvelteComponent | import('#compiler').SvelteSelf} node
* @param {string} component_name
@ -745,7 +754,7 @@ function serialize_inline_component(node, component_name, context) {
if (should_wrap_in_derived) {
const id = b.id(context.state.scope.generate(attribute.name));
context.state.init.push(b.var(id, b.call('$.derived', b.thunk(value))));
context.state.init.push(b.var(id, create_derived(context.state, b.thunk(value))));
arg = b.call('$.get', id);
}
@ -1649,9 +1658,8 @@ function serialize_template_literal(values, visit, state) {
state.init.push(
b.const(
id,
b.call(
// In runes mode, we want things to be fine-grained - but not in legacy mode
state.analysis.runes ? '$.derived' : '$.derived_safe_equal',
create_derived(
state,
b.thunk(/** @type {import('estree').Expression} */ (visit(node.expression)))
)
)
@ -1701,9 +1709,8 @@ export const template_visitors = {
state.init.push(
b.const(
declaration.id,
b.call(
// In runes mode, we want things to be fine-grained - but not in legacy mode
state.analysis.runes ? '$.derived' : '$.derived_safe_equal',
create_derived(
state,
b.thunk(/** @type {import('estree').Expression} */ (visit(declaration.init)))
)
)
@ -1738,10 +1745,7 @@ export const template_visitors = {
])
);
state.init.push(
// In runes mode, we want things to be fine-grained - but not in legacy mode
b.const(tmp, b.call(state.analysis.runes ? '$.derived' : '$.derived_safe_equal', fn))
);
state.init.push(b.const(tmp, create_derived(state, fn)));
// we need to eagerly evaluate the expression in order to hit any
// 'Cannot access x before initialization' errors
@ -2995,12 +2999,7 @@ export const template_visitors = {
const name = node.expression === null ? node.name : node.expression.name;
return b.const(
name,
b.call(
// in legacy mode, sources can be mutated but they're not fine-grained.
// Using the safe-equal derived version ensures the slot is still updated
state.analysis.runes ? '$.derived' : '$.derived_safe_equal',
b.thunk(b.member(b.id('$$slotProps'), b.id(node.name)))
)
create_derived(state, b.thunk(b.member(b.id('$$slotProps'), b.id(node.name))))
);
}
},

@ -0,0 +1,10 @@
import { test } from '../../test';
export default test({
html: `child: 0 parent: 0 <button>inc x</button>`,
async test({ assert, target }) {
await target.querySelector('button')?.click();
assert.htmlEqual(target.innerHTML, `child: 1 parent: 1 <button>inc x</button>`);
}
});

@ -0,0 +1,11 @@
<script>
import Child from "./Child.svelte";
let x;
$: if (!x) x = {y:0};
</script>
<Child x={x ?? {}} />
parent: {x.y}
<button on:click={() => x.y++}>inc x</button>
Loading…
Cancel
Save