From 9414eaa41b4a14109594589f275495aafff2968a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gautier=20Ben=20A=C3=AFm?= <48261497+GauBen@users.noreply.github.com> Date: Tue, 18 Aug 2026 16:29:10 +0200 Subject: [PATCH] fix: prevent crash on async custom element attributes (#18661) attributes setter logic wasn't async-aware --------- Co-authored-by: Simon Holthausen --- .changeset/spotty-files-trade.md | 5 +++ .../client/visitors/RegularElement.js | 15 ++++++- .../async-custom-element-attribute/_config.js | 11 +++++ .../main.svelte | 41 +++++++++++++++++++ .../_expected/client/main.svelte.js | 2 +- 5 files changed, 71 insertions(+), 3 deletions(-) create mode 100644 .changeset/spotty-files-trade.md create mode 100644 packages/svelte/tests/runtime-runes/samples/async-custom-element-attribute/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/async-custom-element-attribute/main.svelte diff --git a/.changeset/spotty-files-trade.md b/.changeset/spotty-files-trade.md new file mode 100644 index 0000000000..39975b7a9a --- /dev/null +++ b/.changeset/spotty-files-trade.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: allow custom elements to receive async values as props 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 8dcd3c7d6f..1cf5abeb69 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 @@ -664,14 +664,25 @@ function build_element_attribute_update(element, node_id, name, value, attribute * @param {ComponentContext} context */ function build_custom_element_attribute_update_assignment(node_id, attribute, context) { - const { value, has_state } = build_attribute_value(attribute.value, context); + const memoizer = new Memoizer(); + const { value, has_state } = build_attribute_value(attribute.value, context, (value, metadata) => + memoizer.add(value, metadata) + ); // don't lowercase name, as we set the element's property, which might be case sensitive const call = b.call('$.set_custom_element_data', node_id, b.literal(attribute.name), value); // this is different from other updates — it doesn't get grouped, // because set_custom_element_data may not be idempotent - const update = has_state ? b.call('$.template_effect', b.thunk(call)) : call; + const update = has_state + ? b.call( + '$.template_effect', + b.arrow(memoizer.apply(), call), + memoizer.sync_values(), + memoizer.async_values(), + memoizer.blockers() + ) + : call; context.state.init.push(b.stmt(update)); } diff --git a/packages/svelte/tests/runtime-runes/samples/async-custom-element-attribute/_config.js b/packages/svelte/tests/runtime-runes/samples/async-custom-element-attribute/_config.js new file mode 100644 index 0000000000..788573d7cf --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/async-custom-element-attribute/_config.js @@ -0,0 +1,11 @@ +import { tick } from 'svelte'; +import { test } from '../../test'; + +export default test({ + async test({ target, assert }) { + await tick(); + const [element] = target.querySelectorAll('async-custom-element'); + + assert.htmlEqual(element.innerHTML, `Hello foobar!`); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/async-custom-element-attribute/main.svelte b/packages/svelte/tests/runtime-runes/samples/async-custom-element-attribute/main.svelte new file mode 100644 index 0000000000..a047054069 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/async-custom-element-attribute/main.svelte @@ -0,0 +1,41 @@ + + + diff --git a/packages/svelte/tests/snapshot/samples/dynamic-attributes-casing/_expected/client/main.svelte.js b/packages/svelte/tests/snapshot/samples/dynamic-attributes-casing/_expected/client/main.svelte.js index eafadfc2ea..799c9f2139 100644 --- a/packages/svelte/tests/snapshot/samples/dynamic-attributes-casing/_expected/client/main.svelte.js +++ b/packages/svelte/tests/snapshot/samples/dynamic-attributes-casing/_expected/client/main.svelte.js @@ -25,7 +25,7 @@ export default function Main($$anchor) { var svg_1 = $.sibling(div_1, 2); var custom_element_1 = $.sibling(svg_1, 2); - $.template_effect(() => $.set_custom_element_data(custom_element_1, 'fooBar', y())); + $.template_effect(($0) => $.set_custom_element_data(custom_element_1, 'fooBar', $0), [() => y()]); $.template_effect( ($0, $1) => {