From 5b9f0df8ee97ba43a3d3af18f99f2dd44bd86965 Mon Sep 17 00:00:00 2001 From: Paolo Ricciuti Date: Tue, 18 Mar 2025 11:51:26 +0100 Subject: [PATCH] fix: don't hoist listeners that access non hoistable snippets (#15534) * fix: don't hoist listeners that access non hoistable snippets * chore: add comment * chore: fix auto import fumble --- .changeset/thick-pans-fold.md | 5 +++++ .../compiler/phases/2-analyze/visitors/Attribute.js | 9 +++++++++ .../unhoist-function-accessing-snippet/_config.js | 12 ++++++++++++ .../unhoist-function-accessing-snippet/main.svelte | 12 ++++++++++++ 4 files changed, 38 insertions(+) create mode 100644 .changeset/thick-pans-fold.md create mode 100644 packages/svelte/tests/runtime-runes/samples/unhoist-function-accessing-snippet/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/unhoist-function-accessing-snippet/main.svelte diff --git a/.changeset/thick-pans-fold.md b/.changeset/thick-pans-fold.md new file mode 100644 index 0000000000..b5b5cee53e --- /dev/null +++ b/.changeset/thick-pans-fold.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: don't hoist listeners that access non hoistable snippets diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/Attribute.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/Attribute.js index 9124a8822f..3ba81767cc 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/Attribute.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/Attribute.js @@ -183,6 +183,15 @@ function get_delegated_event(event_name, handler, context) { const binding = scope.get(reference); const local_binding = context.state.scope.get(reference); + // if the function access a snippet that can't be hoisted we bail out + if ( + local_binding !== null && + local_binding.initial?.type === 'SnippetBlock' && + !local_binding.initial.metadata.can_hoist + ) { + return unhoisted; + } + // If we are referencing a binding that is shadowed in another scope then bail out. if (local_binding !== null && binding !== null && local_binding.node !== binding.node) { return unhoisted; diff --git a/packages/svelte/tests/runtime-runes/samples/unhoist-function-accessing-snippet/_config.js b/packages/svelte/tests/runtime-runes/samples/unhoist-function-accessing-snippet/_config.js new file mode 100644 index 0000000000..b1229f5a8a --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/unhoist-function-accessing-snippet/_config.js @@ -0,0 +1,12 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +export default test({ + async test({ assert, target, errors }) { + const button = target.querySelector('button'); + flushSync(() => { + button?.click(); + }); + assert.deepEqual(errors, []); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/unhoist-function-accessing-snippet/main.svelte b/packages/svelte/tests/runtime-runes/samples/unhoist-function-accessing-snippet/main.svelte new file mode 100644 index 0000000000..e909d77fd6 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/unhoist-function-accessing-snippet/main.svelte @@ -0,0 +1,12 @@ + + + + +{#snippet snip()} + snippet {x} +{/snippet} \ No newline at end of file