diff --git a/.changeset/chatty-windows-own.md b/.changeset/chatty-windows-own.md new file mode 100644 index 0000000000..7a5ac00c44 --- /dev/null +++ b/.changeset/chatty-windows-own.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: ensure snippet hoisting works in the correct scope diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/SnippetBlock.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/SnippetBlock.js index cd8af21f98..7b8cd7593f 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/SnippetBlock.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/SnippetBlock.js @@ -99,8 +99,9 @@ function can_hoist_snippet(scope, scopes, visited = new Set()) { if (binding.initial?.type === 'SnippetBlock') { if (visited.has(binding)) continue; visited.add(binding); + const snippet_scope = /** @type {Scope} */ (scopes.get(binding.initial)); - if (can_hoist_snippet(binding.scope, scopes, visited)) { + if (can_hoist_snippet(snippet_scope, scopes, visited)) { continue; } } diff --git a/packages/svelte/src/compiler/phases/scope.js b/packages/svelte/src/compiler/phases/scope.js index 7f22aa7c87..7155d31720 100644 --- a/packages/svelte/src/compiler/phases/scope.js +++ b/packages/svelte/src/compiler/phases/scope.js @@ -628,12 +628,8 @@ export function create_scopes(ast, root, allow_reactive_declarations, parent) { SnippetBlock(node, context) { const state = context.state; - // Special-case for root-level snippets: they become part of the instance scope - const is_top_level = !context.path.at(-2); let scope = state.scope; - if (is_top_level) { - scope = /** @type {Scope} */ (parent); - } + scope.declare(node.expression, 'normal', 'function', node); const child_scope = state.scope.child(); diff --git a/packages/svelte/tests/runtime-runes/samples/snippet-hoisting-2/_config.js b/packages/svelte/tests/runtime-runes/samples/snippet-hoisting-2/_config.js new file mode 100644 index 0000000000..ae00bd9f56 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/snippet-hoisting-2/_config.js @@ -0,0 +1,5 @@ +import { test } from '../../test'; + +export default test({ + html: 'a' +}); diff --git a/packages/svelte/tests/runtime-runes/samples/snippet-hoisting-2/main.svelte b/packages/svelte/tests/runtime-runes/samples/snippet-hoisting-2/main.svelte new file mode 100644 index 0000000000..d090460a09 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/snippet-hoisting-2/main.svelte @@ -0,0 +1,13 @@ + + +{@render b()} + +{#snippet a()} + {abc} +{/snippet} + +{#snippet b()} + {@render a()} +{/snippet} diff --git a/packages/svelte/tests/runtime-runes/samples/snippet-hoisting-3/_config.js b/packages/svelte/tests/runtime-runes/samples/snippet-hoisting-3/_config.js new file mode 100644 index 0000000000..240263603d --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/snippet-hoisting-3/_config.js @@ -0,0 +1,5 @@ +import { test } from '../../test'; + +export default test({ + html: '

Hello world!

' +}); diff --git a/packages/svelte/tests/runtime-runes/samples/snippet-hoisting-3/main.svelte b/packages/svelte/tests/runtime-runes/samples/snippet-hoisting-3/main.svelte new file mode 100644 index 0000000000..b23a69d53d --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/snippet-hoisting-3/main.svelte @@ -0,0 +1,13 @@ + + + + +

Hello {name}!

+ +{#snippet foo()} + oo +{/snippet}