Skip to content

Commit 18c5a5b

Browse files
fix: bail out if slot name changes and $$slots assigned to variable (#13678)
1 parent ab9eeb4 commit 18c5a5b

File tree

18 files changed

+128
-56
lines changed

18 files changed

+128
-56
lines changed

.changeset/early-rockets-join.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: bail out if slot name changes and $$slots assigned to variable

packages/svelte/src/compiler/migrate/index.js

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -418,7 +418,7 @@ const instance_script = {
418418
state.str.remove(/** @type {number} */ (node.start), /** @type {number} */ (node.end));
419419
}
420420
},
421-
VariableDeclaration(node, { state, path, visit }) {
421+
VariableDeclaration(node, { state, path, visit, next }) {
422422
if (state.scope !== state.analysis.instance.scope) {
423423
return;
424424
}
@@ -439,12 +439,14 @@ const instance_script = {
439439
bindings = state.scope.get_bindings(declarator);
440440
} catch (e) {
441441
// no bindings, so we can skip this
442+
next();
442443
continue;
443444
}
444445
const has_state = bindings.some((binding) => binding.kind === 'state');
445446
const has_props = bindings.some((binding) => binding.kind === 'bindable_prop');
446447

447448
if (!has_state && !has_props) {
449+
next();
448450
continue;
449451
}
450452

@@ -491,25 +493,31 @@ const instance_script = {
491493

492494
const prop = state.props.find((prop) => prop.exported === (binding.prop_alias || name));
493495
if (prop) {
496+
next();
494497
// $$Props type was used
495498
prop.init = declarator.init
496-
? state.str.original.substring(
497-
/** @type {number} */ (declarator.init.start),
498-
/** @type {number} */ (declarator.init.end)
499-
)
499+
? state.str
500+
.snip(
501+
/** @type {number} */ (declarator.init.start),
502+
/** @type {number} */ (declarator.init.end)
503+
)
504+
.toString()
500505
: '';
501506
prop.bindable = binding.updated;
502507
prop.exported = binding.prop_alias || name;
503508
prop.type_only = false;
504509
} else {
510+
next();
505511
state.props.push({
506512
local: name,
507513
exported: binding.prop_alias ? binding.prop_alias : name,
508514
init: declarator.init
509-
? state.str.original.substring(
510-
/** @type {number} */ (declarator.init.start),
511-
/** @type {number} */ (declarator.init.end)
512-
)
515+
? state.str
516+
.snip(
517+
/** @type {number} */ (declarator.init.start),
518+
/** @type {number} */ (declarator.init.end)
519+
)
520+
.toString()
513521
: '',
514522
optional: !!declarator.init,
515523
bindable: binding.updated,
@@ -1036,6 +1044,11 @@ const template = {
10361044
name = existing_prop.local;
10371045
} else if (slot_name !== 'default') {
10381046
name = state.scope.generate(slot_name);
1047+
if (name !== slot_name) {
1048+
throw new Error(
1049+
'This migration would change the name of a slot making the component unusable'
1050+
);
1051+
}
10391052
}
10401053

10411054
if (!existing_prop) {
@@ -1462,7 +1475,12 @@ function handle_identifier(node, state, path) {
14621475
if (existing_prop) {
14631476
name = existing_prop.local;
14641477
} else if (name !== 'default') {
1465-
name = state.scope.generate(name);
1478+
let new_name = state.scope.generate(name);
1479+
if (new_name !== name) {
1480+
throw new Error(
1481+
'This migration would change the name of a slot making the component unusable'
1482+
);
1483+
}
14661484
}
14671485

14681486
name = name === 'default' ? 'children' : name;
@@ -1479,7 +1497,7 @@ function handle_identifier(node, state, path) {
14791497
// we start with any and delegate to when the slot
14801498
// is actually rendered (it might not happen in that case)
14811499
// any is still a safe bet
1482-
type: `import('svelte').Snippet<[any]>}`,
1500+
type: `import('svelte').Snippet<[any]>`,
14831501
needs_refine_type: true
14841502
});
14851503
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<script>
2+
let showMessage = $$slots.message;
3+
$: extraTitle = $$slots.extra;
4+
</script>
5+
6+
{#if showMessage}
7+
<slot name="message" />
8+
{/if}
9+
10+
{$$props}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<script>
2+
/** @type {{message?: import('svelte').Snippet, extra?: import('svelte').Snippet<[any]>, [key: string]: any}} */
3+
let { ...props } = $props();
4+
let showMessage = props.message;
5+
let extraTitle = $derived(props.extra);
6+
</script>
7+
8+
{#if showMessage}
9+
{@render props.message?.()}
10+
{/if}
11+
12+
{props}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<script>
2+
export let showMessage = $$slots.message;
3+
4+
let showTitle = $$slots.title;
5+
6+
$: extraTitle = $$slots.extra;
7+
</script>
8+
9+
{#if showMessage}
10+
<slot name="message" />
11+
{/if}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<script>
2+
/** @type {{message?: import('svelte').Snippet, showMessage?: any, title?: import('svelte').Snippet<[any]>, extra?: import('svelte').Snippet<[any]>}} */
3+
let {
4+
message,
5+
showMessage = message,
6+
title,
7+
extra
8+
} = $props();
9+
10+
let showTitle = title;
11+
12+
let extraTitle = $derived(extra);
13+
</script>
14+
15+
{#if showMessage}
16+
{@render message?.()}
17+
{/if}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { test } from '../../test';
2+
3+
export default test({
4+
logs: [
5+
'One or more `@migration-task` comments were added to `output.svelte`, please check them and complete the migration manually.'
6+
]
7+
});
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<script>
2+
let body;
3+
</script>
4+
5+
<slot name="body"></slot>
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<!-- @migration-task Error while migrating Svelte code: This migration would change the name of a slot making the component unusable -->
2+
<script>
3+
let body;
4+
</script>
5+
6+
<slot name="body"></slot>
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { test } from '../../test';
2+
3+
export default test({
4+
logs: [
5+
'One or more `@migration-task` comments were added to `output.svelte`, please check them and complete the migration manually.'
6+
]
7+
});
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<slot name="dashed-name"></slot>
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
<!-- @migration-task Error while migrating Svelte code: This migration would change the name of a slot making the component unusable -->
2+
<slot name="dashed-name"></slot>
Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,2 @@
11
<button><slot /></button>
2-
<button><slot /></button>
3-
4-
<slot name="dashed-name" />
5-
<slot name="dashed-name" />
2+
<button><slot /></button>
Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
11
<script>
2-
/** @type {{children?: import('svelte').Snippet, dashed_name?: import('svelte').Snippet}} */
3-
let { children, dashed_name } = $props();
2+
/** @type {{children?: import('svelte').Snippet}} */
3+
let { children } = $props();
44
</script>
55

66
<button>{@render children?.()}</button>
7-
<button>{@render children?.()}</button>
8-
9-
{@render dashed_name?.()}
10-
{@render dashed_name?.()}
7+
<button>{@render children?.()}</button>
Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<button><slot /></button>
22

3-
{#if foo}
4-
<slot name="foo" {foo} />
3+
{#if foos}
4+
<slot name="foo" foo={foos} />
55
{/if}
66

77
{#if $$slots.bar}
@@ -13,8 +13,4 @@
1313

1414
{#if $$slots['default']}foo{/if}
1515

16-
{#if $$slots['dashed-name']}foo{/if}
17-
18-
<slot name="dashed-name" />
19-
2016
<slot header="something" title={$$props.cool} {id} />
Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
<script>
2-
/** @type {{children?: import('svelte').Snippet, foo_1?: import('svelte').Snippet<[any]>, bar?: import('svelte').Snippet, dashed_name?: import('svelte').Snippet, [key: string]: any}} */
3-
let {
4-
...props
5-
} = $props();
2+
/** @type {{children?: import('svelte').Snippet, foo?: import('svelte').Snippet<[any]>, bar?: import('svelte').Snippet, [key: string]: any}} */
3+
let { ...props } = $props();
64
</script>
75

86
<button>{@render props.children?.()}</button>
97

10-
{#if foo}
11-
{@render props.foo_1?.({ foo, })}
8+
{#if foos}
9+
{@render props.foo?.({ foo: foos, })}
1210
{/if}
1311

1412
{#if props.bar}
@@ -20,8 +18,4 @@
2018

2119
{#if props.children}foo{/if}
2220

23-
{#if props.dashed_name}foo{/if}
24-
25-
{@render props.dashed_name?.()}
26-
2721
{@render props.children?.({ header: "something", title: props.cool, id, })}
Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<button><slot /></button>
22

3-
{#if foo}
4-
<slot name="foo" {foo} />
3+
{#if foos}
4+
<slot name="foo" foo={foos} />
55
{/if}
66

77
{#if $$slots.bar}
@@ -13,8 +13,4 @@
1313

1414
{#if $$slots['default']}foo{/if}
1515

16-
{#if $$slots['dashed-name']}foo{/if}
17-
18-
<slot name="dashed-name" />
19-
2016
<slot header="something" title={my_title} {id} />
Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,12 @@
11
<script>
2-
/** @type {{children?: import('svelte').Snippet, foo_1?: import('svelte').Snippet<[any]>, bar?: import('svelte').Snippet, dashed_name?: import('svelte').Snippet}} */
3-
let {
4-
children,
5-
foo_1,
6-
bar,
7-
dashed_name
8-
} = $props();
2+
/** @type {{children?: import('svelte').Snippet, foo?: import('svelte').Snippet<[any]>, bar?: import('svelte').Snippet}} */
3+
let { children, foo, bar } = $props();
94
</script>
105

116
<button>{@render children?.()}</button>
127

13-
{#if foo}
14-
{@render foo_1?.({ foo, })}
8+
{#if foos}
9+
{@render foo?.({ foo: foos, })}
1510
{/if}
1611

1712
{#if bar}
@@ -23,8 +18,4 @@
2318

2419
{#if children}foo{/if}
2520

26-
{#if dashed_name}foo{/if}
27-
28-
{@render dashed_name?.()}
29-
3021
{@render children?.({ header: "something", title: my_title, id, })}

0 commit comments

Comments
 (0)