Skip to content

Commit bc6f857

Browse files
committed
fix issue + add test
1 parent eaff971 commit bc6f857

File tree

7 files changed

+51
-5
lines changed

7 files changed

+51
-5
lines changed

packages/svelte/src/compiler/phases/3-transform/client/visitors/SnippetBlock.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ export function SnippetBlock(node, context) {
8282

8383
const declaration = b.const(node.expression, snippet);
8484
const local_scope = context.state.scope;
85-
const can_hoist = can_hoist_snippet(node, local_scope);
85+
const can_hoist = can_hoist_snippet(node, local_scope, context.state.scopes);
8686

8787
// Top-level snippets are hoisted so they can be referenced in the `<script>`
8888
if (context.path.length === 1 && context.path[0].type === 'Fragment') {

packages/svelte/src/compiler/phases/3-transform/server/visitors/SnippetBlock.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export function SnippetBlock(node, context) {
1818
// @ts-expect-error - TODO remove this hack once $$render_inner for legacy bindings is gone
1919
fn.___snippet = true;
2020

21-
const can_hoist = can_hoist_snippet(node, context.state.scope);
21+
const can_hoist = can_hoist_snippet(node, context.state.scope, context.state.scopes);
2222

2323
if (context.path.length === 1 && context.path[0].type === 'Fragment' && can_hoist) {
2424
context.state.hoisted.push(fn);

packages/svelte/src/compiler/phases/3-transform/utils.js

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -456,16 +456,17 @@ export function transform_inspect_rune(node, context) {
456456

457457
/**
458458
* @param {AST.SnippetBlock} node
459+
* @param {Map<SvelteNode, Scope>} scopes
459460
* @param {Scope} scope
460461
*/
461-
export function can_hoist_snippet(node, scope) {
462+
export function can_hoist_snippet(node, scope, scopes, visited = new Set()) {
462463
let can_hoist = true;
463464

464465
ref_loop: for (const [reference] of scope.references) {
465466
const local_binding = scope.get(reference);
466467

467468
if (local_binding) {
468-
if (local_binding.node === node.expression) {
469+
if (local_binding.node === node.expression || local_binding.scope.function_depth === 0) {
469470
continue;
470471
}
471472
/** @type {Scope | null} */
@@ -477,6 +478,24 @@ export function can_hoist_snippet(node, scope) {
477478
}
478479
current_scope = current_scope.parent;
479480
}
481+
482+
// Recursively check if another snippet can be hoisted
483+
if (local_binding.kind === 'normal') {
484+
for (const ref of local_binding.references) {
485+
const parent = ref.path.at(-1);
486+
if (ref.node === local_binding.node && parent?.type === 'SnippetBlock') {
487+
const ref_scope = scopes.get(parent);
488+
if (visited.has(ref)) {
489+
break;
490+
}
491+
visited.add(ref);
492+
if (ref_scope && can_hoist_snippet(parent, ref_scope, scopes, visited)) {
493+
continue ref_loop;
494+
}
495+
break;
496+
}
497+
}
498+
}
480499
can_hoist = false;
481500
break;
482501
}

packages/svelte/src/compiler/types/index.d.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,8 @@ export interface Binding {
279279
| 'snippet'
280280
| 'store_sub'
281281
| 'legacy_reactive'
282-
| 'template';
282+
| 'template'
283+
| 'snippet';
283284
declaration_kind: DeclarationKind;
284285
/**
285286
* What the value was initialized with.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<script module>
2+
const message = 'hello';
3+
4+
export { one };
5+
</script>
6+
7+
{#snippet one()}
8+
{@render two()}
9+
{/snippet}
10+
11+
{#snippet two()}
12+
{message}
13+
{/snippet}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { test } from '../../test';
2+
3+
export default test({
4+
compileOptions: {
5+
dev: true // Render in dev mode to check that the validation error is not thrown
6+
},
7+
html: `hello`
8+
});
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<script>
2+
import { one } from './Child.svelte';
3+
</script>
4+
5+
{@render one()}

0 commit comments

Comments
 (0)