Skip to content

Commit fcf81b1

Browse files
authored
fix: preserve component function context (#12089)
We need to pass the SFC component function to the snippet dev time function or else nested snippets result in the wrong context being set fixes #12040
1 parent 2ca2979 commit fcf81b1

File tree

8 files changed

+66
-7
lines changed

8 files changed

+66
-7
lines changed

.changeset/wet-bats-exercise.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"svelte": patch
3+
---
4+
5+
fix: preserve component function context for nested components

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

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -886,7 +886,12 @@ function serialize_inline_component(node, component_name, context) {
886886

887887
if (slot_name === 'default' && !has_children_prop) {
888888
push_prop(
889-
b.init('children', context.state.options.dev ? b.call('$.wrap_snippet', slot_fn) : slot_fn)
889+
b.init(
890+
'children',
891+
context.state.options.dev
892+
? b.call('$.wrap_snippet', slot_fn, b.id(context.state.analysis.name))
893+
: slot_fn
894+
)
890895
);
891896
// We additionally add the default slot as a boolean, so that the slot render function on the other
892897
// side knows it should get the content to render from $$props.children
@@ -2699,7 +2704,7 @@ export const template_visitors = {
26992704
let snippet = b.arrow(args, body);
27002705

27012706
if (context.state.options.dev) {
2702-
snippet = b.call('$.wrap_snippet', snippet);
2707+
snippet = b.call('$.wrap_snippet', snippet, b.id(context.state.analysis.name));
27032708
}
27042709

27052710
const declaration = b.var(node.expression, snippet);

packages/svelte/src/internal/client/dom/blocks/snippet.js

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,15 +39,13 @@ export function snippet(get_snippet, node, ...args) {
3939
* In development, wrap the snippet function so that it passes validation, and so that the
4040
* correct component context is set for ownership checks
4141
* @param {(node: import('#client').TemplateNode, ...args: any[]) => import('#client').Dom} fn
42-
* @returns
42+
* @param {any} component
4343
*/
44-
export function wrap_snippet(fn) {
45-
let component = /** @type {import('#client').ComponentContext} */ (current_component_context);
46-
44+
export function wrap_snippet(fn, component) {
4745
return add_snippet_symbol(
4846
(/** @type {import('#client').TemplateNode} */ node, /** @type {any[]} */ ...args) => {
4947
var previous_component_function = dev_current_component_function;
50-
set_dev_current_component_function(component.function);
48+
set_dev_current_component_function(component);
5149

5250
try {
5351
return fn(node, ...args);
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<script>
2+
let { children } = $props();
3+
</script>
4+
5+
{@render children()}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<script>
2+
let { children } = $props();
3+
</script>
4+
5+
{@render children()}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<script lang="ts">
2+
let { count = $bindable() } = $props();
3+
</script>
4+
5+
<button onclick={() => count.value++}>{count.value}</button>
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { flushSync } from 'svelte';
2+
import { test } from '../../test';
3+
4+
// Tests that nested snippets preserve correct component function context so we don't get false positive warnings
5+
export default test({
6+
html: `<button>0</button>`,
7+
8+
compileOptions: {
9+
dev: true
10+
},
11+
12+
test({ assert, target, warnings }) {
13+
const button = target.querySelector('button');
14+
15+
button?.click();
16+
flushSync();
17+
18+
assert.htmlEqual(target.innerHTML, `<button>1</button>`);
19+
assert.deepEqual(warnings, []);
20+
},
21+
22+
warnings: []
23+
});
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<script>
2+
import Component1 from './Component1.svelte';
3+
import Component2 from './Component2.svelte';
4+
import Component3 from './Component3.svelte';
5+
6+
let count = $state({ value: 0 });
7+
</script>
8+
9+
<Component1>
10+
<Component2>
11+
<Component3 bind:count></Component3>
12+
</Component2>
13+
</Component1>

0 commit comments

Comments
 (0)