fix: avoid shadowing a variable in dynamic components (#16185)

* fix: avoid shadowing a variable in dynamic components

* split component name and intermediate name

---------

Co-authored-by: 7nik <kifiranet@gmail.com>
pull/16195/head
7nik 3 months ago committed by GitHub
parent 061ab31d23
commit 838f881550
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: avoid shadowing a variable in dynamic components

@ -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);
}

@ -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')))])
)
);

@ -0,0 +1,5 @@
<script>
const { children } = $props()
</script>
{@render children()}

@ -0,0 +1,8 @@
import { test } from '../../test';
import { flushSync } from 'svelte';
export default test({
async test({ assert, target }) {
assert.htmlEqual(target.innerHTML, 'test');
}
});

@ -0,0 +1,9 @@
<script>
import A from './A.svelte';
const B = $derived(A);
</script>
<B>
<B>test</B>
</B>
Loading…
Cancel
Save