diff --git a/.changeset/empty-horses-tell.md b/.changeset/empty-horses-tell.md new file mode 100644 index 0000000000..6633da1967 --- /dev/null +++ b/.changeset/empty-horses-tell.md @@ -0,0 +1,5 @@ +--- +"svelte": patch +--- + +fix: keep default values of props a proxy after reassignment diff --git a/packages/svelte/src/compiler/phases/3-transform/client/utils.js b/packages/svelte/src/compiler/phases/3-transform/client/utils.js index 2c343480af..c6b10bc2d0 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/utils.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/utils.js @@ -292,7 +292,13 @@ export function serialize_set_binding(node, context, fallback, options) { const serialize = () => { if (left === node.left) { - if (binding.kind === 'prop' || binding.kind === 'bindable_prop') { + const is_initial_proxy = + binding.initial !== null && + should_proxy_or_freeze( + /**@type {import("estree").Expression}*/ (binding.initial), + context.state.scope + ); + if ((binding.kind === 'prop' || binding.kind === 'bindable_prop') && !is_initial_proxy) { return b.call(left, value); } else if (is_store) { return b.call('$.store_set', serialize_get_binding(b.id(left_name), state), value); @@ -318,6 +324,18 @@ export function serialize_set_binding(node, context, fallback, options) { ? b.call('$.freeze', value) : value ); + } else if ( + (binding.kind === 'prop' || binding.kind === 'bindable_prop') && + is_initial_proxy + ) { + call = b.call( + left, + context.state.analysis.runes && + !options?.skip_proxy_and_freeze && + should_proxy_or_freeze(value, context.state.scope) + ? serialize_proxy_reassignment(value, left_name, state) + : value + ); } else { call = b.call('$.set', b.id(left_name), value); } diff --git a/packages/svelte/tests/runtime-runes/samples/props-default-reactivity/Counter.svelte b/packages/svelte/tests/runtime-runes/samples/props-default-reactivity/Counter.svelte index 077eda5709..7b0f28b213 100644 --- a/packages/svelte/tests/runtime-runes/samples/props-default-reactivity/Counter.svelte +++ b/packages/svelte/tests/runtime-runes/samples/props-default-reactivity/Counter.svelte @@ -1,12 +1,20 @@ - - + + + + diff --git a/packages/svelte/tests/runtime-runes/samples/props-default-reactivity/_config.js b/packages/svelte/tests/runtime-runes/samples/props-default-reactivity/_config.js index ffcde5c504..4ebb27fc9a 100644 --- a/packages/svelte/tests/runtime-runes/samples/props-default-reactivity/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/props-default-reactivity/_config.js @@ -5,10 +5,12 @@ export default test({ html: ` + + `, async test({ assert, target }) { - const [btn1, btn2] = target.querySelectorAll('button'); + const [btn1, btn2, btn3, btn4] = target.querySelectorAll('button'); flushSync(() => { btn1?.click(); @@ -19,6 +21,8 @@ export default test({ ` + + ` ); @@ -31,6 +35,64 @@ export default test({ ` + + + ` + ); + + flushSync(() => { + btn1?.click(); + }); + + assert.htmlEqual( + target.innerHTML, + ` + + + + + ` + ); + + flushSync(() => { + btn3?.click(); + }); + + assert.htmlEqual( + target.innerHTML, + ` + + + + + ` + ); + + flushSync(() => { + btn4?.click(); + }); + + assert.htmlEqual( + target.innerHTML, + ` + + + + + ` + ); + + flushSync(() => { + btn3?.click(); + }); + + assert.htmlEqual( + target.innerHTML, + ` + + + + ` ); }