Skip to content

Commit a4e994b

Browse files
authored
fix: allow slot attribute inside snippets (#12188)
Someone could render a snippet into a custom element, and therefore elements with slot attributes should be allowed within them and be treated as regular elements closes #12158
1 parent 48d1ef9 commit a4e994b

File tree

5 files changed

+64
-1
lines changed

5 files changed

+64
-1
lines changed

.changeset/seven-bees-tell.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"svelte": patch
3+
---
4+
5+
fix: allow slot attribute inside snippets

packages/svelte/src/compiler/phases/2-analyze/validation.js

+9-1
Original file line numberDiff line numberDiff line change
@@ -256,8 +256,16 @@ function validate_attribute_name(attribute) {
256256
* @param {boolean} is_component
257257
*/
258258
function validate_slot_attribute(context, attribute, is_component = false) {
259+
const parent = context.path.at(-2);
259260
let owner = undefined;
260261

262+
if (parent?.type === 'SnippetBlock') {
263+
if (!is_text_attribute(attribute)) {
264+
e.slot_attribute_invalid(attribute);
265+
}
266+
return;
267+
}
268+
261269
let i = context.path.length;
262270
while (i--) {
263271
const ancestor = context.path[i];
@@ -283,7 +291,7 @@ function validate_slot_attribute(context, attribute, is_component = false) {
283291
owner.type === 'SvelteComponent' ||
284292
owner.type === 'SvelteSelf'
285293
) {
286-
if (owner !== context.path.at(-2)) {
294+
if (owner !== parent) {
287295
e.slot_attribute_invalid_placement(attribute);
288296
}
289297

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<script context="module">
2+
if (!customElements.get('my-custom-element')) {
3+
customElements.define('my-custom-element', class extends HTMLElement {
4+
connectedCallback() {
5+
this.attachShadow({ mode: 'open' });
6+
this.shadowRoot.innerHTML = '|<slot></slot>|<slot name="slot"></slot>|';
7+
}
8+
});
9+
}
10+
</script>
11+
12+
<script>
13+
const { children } = $props();
14+
</script>
15+
16+
<my-custom-element>
17+
{@render children()}
18+
</my-custom-element>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { test } from '../../test';
2+
3+
export default test({
4+
mode: ['client', 'server'],
5+
html: `<my-custom-element>Default <span slot="slot">Slotted</span></my-custom-element>`,
6+
test({ target, assert }) {
7+
const shadowRoot = /** @type {ShadowRoot} */ (
8+
target.querySelector('my-custom-element')?.shadowRoot
9+
);
10+
const [defaultSlot, namedSlot] = shadowRoot.querySelectorAll('slot');
11+
const assignedDefaultNodes = defaultSlot.assignedNodes();
12+
const assignedNamedNodes = namedSlot.assignedNodes();
13+
14+
assert.equal(assignedDefaultNodes.length, 1);
15+
assert.equal(assignedNamedNodes.length, 1);
16+
assert.htmlEqual(assignedDefaultNodes[0].textContent || '', `Default`);
17+
assert.htmlEqual(
18+
/** @type {HTMLElement} */ (assignedNamedNodes[0]).outerHTML,
19+
`<span slot="slot">Slotted</span>`
20+
);
21+
}
22+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<script>
2+
import Component from './Component.svelte';
3+
</script>
4+
5+
<Component>
6+
{#snippet children()}
7+
Default
8+
<span slot="slot">Slotted</span>
9+
{/snippet}
10+
</Component>

0 commit comments

Comments
 (0)