diff --git a/.changeset/wet-bats-exercise.md b/.changeset/wet-bats-exercise.md new file mode 100644 index 000000000..550ad8337 --- /dev/null +++ b/.changeset/wet-bats-exercise.md @@ -0,0 +1,5 @@ +--- +"svelte": patch +--- + +fix: preserve component function context for nested components diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js index 2ddafb24d..d8ae6749f 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js @@ -886,7 +886,12 @@ function serialize_inline_component(node, component_name, context) { if (slot_name === 'default' && !has_children_prop) { push_prop( - b.init('children', context.state.options.dev ? b.call('$.wrap_snippet', slot_fn) : slot_fn) + b.init( + 'children', + context.state.options.dev + ? b.call('$.wrap_snippet', slot_fn, b.id(context.state.analysis.name)) + : slot_fn + ) ); // We additionally add the default slot as a boolean, so that the slot render function on the other // side knows it should get the content to render from $$props.children @@ -2699,7 +2704,7 @@ export const template_visitors = { let snippet = b.arrow(args, body); if (context.state.options.dev) { - snippet = b.call('$.wrap_snippet', snippet); + snippet = b.call('$.wrap_snippet', snippet, b.id(context.state.analysis.name)); } const declaration = b.var(node.expression, snippet); diff --git a/packages/svelte/src/internal/client/dom/blocks/snippet.js b/packages/svelte/src/internal/client/dom/blocks/snippet.js index 5bf13a45d..611733feb 100644 --- a/packages/svelte/src/internal/client/dom/blocks/snippet.js +++ b/packages/svelte/src/internal/client/dom/blocks/snippet.js @@ -39,15 +39,13 @@ export function snippet(get_snippet, node, ...args) { * In development, wrap the snippet function so that it passes validation, and so that the * correct component context is set for ownership checks * @param {(node: import('#client').TemplateNode, ...args: any[]) => import('#client').Dom} fn - * @returns + * @param {any} component */ -export function wrap_snippet(fn) { - let component = /** @type {import('#client').ComponentContext} */ (current_component_context); - +export function wrap_snippet(fn, component) { return add_snippet_symbol( (/** @type {import('#client').TemplateNode} */ node, /** @type {any[]} */ ...args) => { var previous_component_function = dev_current_component_function; - set_dev_current_component_function(component.function); + set_dev_current_component_function(component); try { return fn(node, ...args); diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-6/Component1.svelte b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-6/Component1.svelte new file mode 100644 index 000000000..17d34e7cf --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-6/Component1.svelte @@ -0,0 +1,5 @@ + + +{@render children()} diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-6/Component2.svelte b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-6/Component2.svelte new file mode 100644 index 000000000..17d34e7cf --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-6/Component2.svelte @@ -0,0 +1,5 @@ + + +{@render children()} diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-6/Component3.svelte b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-6/Component3.svelte new file mode 100644 index 000000000..57ef96fed --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-6/Component3.svelte @@ -0,0 +1,5 @@ + + + diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-6/_config.js b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-6/_config.js new file mode 100644 index 000000000..216f4c138 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-6/_config.js @@ -0,0 +1,23 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +// Tests that nested snippets preserve correct component function context so we don't get false positive warnings +export default test({ + html: ``, + + compileOptions: { + dev: true + }, + + test({ assert, target, warnings }) { + const button = target.querySelector('button'); + + button?.click(); + flushSync(); + + assert.htmlEqual(target.innerHTML, ``); + assert.deepEqual(warnings, []); + }, + + warnings: [] +}); diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-6/main.svelte b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-6/main.svelte new file mode 100644 index 000000000..d2f53f536 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-6/main.svelte @@ -0,0 +1,13 @@ + + + + + + +