From e26d8ff6682a84f36a39cc21efb4cbed6df81b14 Mon Sep 17 00:00:00 2001 From: paoloricciuti Date: Mon, 17 Feb 2025 22:07:51 +0100 Subject: [PATCH] fix: `muted` reactive without `bind` and select/autofocus attributes working with function calls Co-authored-by: Joel Howse --- .changeset/silver-ties-itch.md | 5 +++ .../client/visitors/RegularElement.js | 35 +++++++++++++------ .../samples/autofocus-with-call/_config.js | 7 ++++ .../samples/autofocus-with-call/main.svelte | 6 ++++ .../muted-without-bind-works/_config.js | 13 +++++++ .../muted-without-bind-works/main.svelte | 13 +++++++ .../samples/select-value-with-call/_config.js | 7 ++++ .../select-value-with-call/main.svelte | 6 ++++ 8 files changed, 82 insertions(+), 10 deletions(-) create mode 100644 .changeset/silver-ties-itch.md create mode 100644 packages/svelte/tests/runtime-runes/samples/autofocus-with-call/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/autofocus-with-call/main.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/muted-without-bind-works/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/muted-without-bind-works/main.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/select-value-with-call/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/select-value-with-call/main.svelte diff --git a/.changeset/silver-ties-itch.md b/.changeset/silver-ties-itch.md new file mode 100644 index 000000000000..67e33dadfbff --- /dev/null +++ b/.changeset/silver-ties-itch.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: `muted` reactive without `bind` and select/autofocus attributes working with function calls diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js index 98036aa9b609..01bbda19641f 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js @@ -29,7 +29,8 @@ import { build_render_statement, build_template_chunk, build_update_assignment, - get_expression_id + get_expression_id, + memoize_expression } from './shared/utils.js'; import { visit_event_attribute } from './shared/events.js'; @@ -532,18 +533,28 @@ function build_element_attribute_update_assignment( const is_svg = context.state.metadata.namespace === 'svg' || element.name === 'svg'; const is_mathml = context.state.metadata.namespace === 'mathml'; + const is_autofocus = name === 'autofocus'; + let { value, has_state } = build_attribute_value(attribute.value, context, (value, metadata) => - metadata.has_call ? get_expression_id(state, value) : value + metadata.has_call + ? is_autofocus + ? memoize_expression(state, value) + : get_expression_id(state, value) + : value ); - if (name === 'autofocus') { + if (is_autofocus) { state.init.push(b.stmt(b.call('$.autofocus', node_id, value))); return false; } // Special case for Firefox who needs it set as a property in order to work if (name === 'muted') { - state.init.push(b.stmt(b.assignment('=', b.member(node_id, b.id('muted')), value))); + if (!has_state) { + state.init.push(b.stmt(b.assignment('=', b.member(node_id, b.id('muted')), value))); + return false; + } + state.update.push(b.stmt(b.assignment('=', b.member(node_id, b.id('muted')), value))); return false; } @@ -660,8 +671,17 @@ function build_custom_element_attribute_update_assignment(node_id, attribute, co */ function build_element_special_value_attribute(element, node_id, attribute, context) { const state = context.state; + const is_select_with_value = + // attribute.metadata.dynamic would give false negatives because even if the value does not change, + // the inner options could still change, so we need to always treat it as reactive + element === 'select' && attribute.value !== true && !is_text_attribute(attribute); + const { value, has_state } = build_attribute_value(attribute.value, context, (value, metadata) => - metadata.has_call ? get_expression_id(state, value) : value + metadata.has_call + ? is_select_with_value + ? memoize_expression(context.state, value) + : get_expression_id(state, value) + : value ); const inner_assignment = b.assignment( @@ -674,11 +694,6 @@ function build_element_special_value_attribute(element, node_id, attribute, cont ) ); - const is_select_with_value = - // attribute.metadata.dynamic would give false negatives because even if the value does not change, - // the inner options could still change, so we need to always treat it as reactive - element === 'select' && attribute.value !== true && !is_text_attribute(attribute); - const update = b.stmt( is_select_with_value ? b.sequence([ diff --git a/packages/svelte/tests/runtime-runes/samples/autofocus-with-call/_config.js b/packages/svelte/tests/runtime-runes/samples/autofocus-with-call/_config.js new file mode 100644 index 000000000000..0597c2fda899 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/autofocus-with-call/_config.js @@ -0,0 +1,7 @@ +import { test } from '../../test'; + +export default test({ + async test({ assert, errors }) { + assert.deepEqual(errors, []); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/autofocus-with-call/main.svelte b/packages/svelte/tests/runtime-runes/samples/autofocus-with-call/main.svelte new file mode 100644 index 000000000000..cb3804af34e4 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/autofocus-with-call/main.svelte @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/packages/svelte/tests/runtime-runes/samples/muted-without-bind-works/_config.js b/packages/svelte/tests/runtime-runes/samples/muted-without-bind-works/_config.js new file mode 100644 index 000000000000..cc4dfb37f098 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/muted-without-bind-works/_config.js @@ -0,0 +1,13 @@ +import { flushSync } from 'svelte'; +import { ok, test } from '../../test'; + +export default test({ + async test({ assert, target, logs }) { + const btn = target.querySelector('button'); + ok(btn); + flushSync(() => { + btn.click(); + }); + assert.deepEqual(logs, [true]); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/muted-without-bind-works/main.svelte b/packages/svelte/tests/runtime-runes/samples/muted-without-bind-works/main.svelte new file mode 100644 index 000000000000..646334c1ece3 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/muted-without-bind-works/main.svelte @@ -0,0 +1,13 @@ + + + + + diff --git a/packages/svelte/tests/runtime-runes/samples/select-value-with-call/_config.js b/packages/svelte/tests/runtime-runes/samples/select-value-with-call/_config.js new file mode 100644 index 000000000000..0597c2fda899 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/select-value-with-call/_config.js @@ -0,0 +1,7 @@ +import { test } from '../../test'; + +export default test({ + async test({ assert, errors }) { + assert.deepEqual(errors, []); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/select-value-with-call/main.svelte b/packages/svelte/tests/runtime-runes/samples/select-value-with-call/main.svelte new file mode 100644 index 000000000000..b1d60ecf6d9f --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/select-value-with-call/main.svelte @@ -0,0 +1,6 @@ + + + + \ No newline at end of file