diff --git a/.changeset/short-mails-know.md b/.changeset/short-mails-know.md
new file mode 100644
index 0000000000..0b8b62d575
--- /dev/null
+++ b/.changeset/short-mails-know.md
@@ -0,0 +1,5 @@
+---
+'svelte': patch
+---
+
+fix: avoid shadowing a variable in dynamic components
diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/Component.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/Component.js
index d58a24b455..9b86557536 100644
--- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/Component.js
+++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/Component.js
@@ -8,12 +8,6 @@ import { build_component } from './shared/component.js';
* @param {ComponentContext} context
*/
export function Component(node, context) {
- const component = build_component(
- node,
- // if it's not dynamic we will just use the node name, if it is dynamic we will use the node name
- // only if it's a valid identifier, otherwise we will use a default name
- !node.metadata.dynamic || regex_is_valid_identifier.test(node.name) ? node.name : '$$component',
- context
- );
+ const component = build_component(node, node.name, context);
context.state.init.push(component);
}
diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/component.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/component.js
index 19ed9cdeb8..cb6e4de478 100644
--- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/component.js
+++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/component.js
@@ -52,6 +52,15 @@ export function build_component(node, component_name, context) {
/** @type {ExpressionStatement[]} */
const binding_initializers = [];
+ const is_component_dynamic =
+ node.type === 'SvelteComponent' || (node.type === 'Component' && node.metadata.dynamic);
+
+ // The variable name used for the component inside $.component()
+ const intermediate_name =
+ node.type === 'Component' && node.metadata.dynamic
+ ? context.state.scope.generate(node.name)
+ : '$$component';
+
/**
* If this component has a slot property, it is a named slot within another component. In this case
* the slot scope applies to the component itself, too, and not just its children.
@@ -199,7 +208,7 @@ export function build_component(node, component_name, context) {
b.call(
'$$ownership_validator.binding',
b.literal(binding.node.name),
- b.id(component_name),
+ b.id(is_component_dynamic ? intermediate_name : component_name),
b.thunk(expression)
)
)
@@ -414,8 +423,8 @@ export function build_component(node, component_name, context) {
// TODO We can remove this ternary once we remove legacy mode, since in runes mode dynamic components
// will be handled separately through the `$.component` function, and then the component name will
// always be referenced through just the identifier here.
- node.type === 'SvelteComponent' || (node.type === 'Component' && node.metadata.dynamic)
- ? component_name
+ is_component_dynamic
+ ? intermediate_name
: /** @type {Expression} */ (context.visit(b.member_id(component_name))),
node_id,
props_expression
@@ -432,7 +441,7 @@ export function build_component(node, component_name, context) {
const statements = [...snippet_declarations];
- if (node.type === 'SvelteComponent' || (node.type === 'Component' && node.metadata.dynamic)) {
+ if (is_component_dynamic) {
const prev = fn;
fn = (node_id) => {
@@ -441,11 +450,11 @@ export function build_component(node, component_name, context) {
node_id,
b.thunk(
/** @type {Expression} */ (
- context.visit(node.type === 'Component' ? b.member_id(node.name) : node.expression)
+ context.visit(node.type === 'Component' ? b.member_id(component_name) : node.expression)
)
),
b.arrow(
- [b.id('$$anchor'), b.id(component_name)],
+ [b.id('$$anchor'), b.id(intermediate_name)],
b.block([...binding_initializers, b.stmt(prev(b.id('$$anchor')))])
)
);
diff --git a/packages/svelte/tests/runtime-runes/samples/dynamic-component-nested/A.svelte b/packages/svelte/tests/runtime-runes/samples/dynamic-component-nested/A.svelte
new file mode 100644
index 0000000000..d37c929273
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/dynamic-component-nested/A.svelte
@@ -0,0 +1,5 @@
+
+
+{@render children()}
diff --git a/packages/svelte/tests/runtime-runes/samples/dynamic-component-nested/_config.js b/packages/svelte/tests/runtime-runes/samples/dynamic-component-nested/_config.js
new file mode 100644
index 0000000000..cd1fa2b1b9
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/dynamic-component-nested/_config.js
@@ -0,0 +1,8 @@
+import { test } from '../../test';
+import { flushSync } from 'svelte';
+
+export default test({
+ async test({ assert, target }) {
+ assert.htmlEqual(target.innerHTML, 'test');
+ }
+});
diff --git a/packages/svelte/tests/runtime-runes/samples/dynamic-component-nested/main.svelte b/packages/svelte/tests/runtime-runes/samples/dynamic-component-nested/main.svelte
new file mode 100644
index 0000000000..d0646b319b
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/dynamic-component-nested/main.svelte
@@ -0,0 +1,9 @@
+
+
+
+ test
+