diff --git a/.changeset/seven-bees-tell.md b/.changeset/seven-bees-tell.md new file mode 100644 index 000000000000..753a93add2d1 --- /dev/null +++ b/.changeset/seven-bees-tell.md @@ -0,0 +1,5 @@ +--- +"svelte": patch +--- + +fix: allow slot attribute inside snippets diff --git a/packages/svelte/src/compiler/phases/2-analyze/validation.js b/packages/svelte/src/compiler/phases/2-analyze/validation.js index 8ae8c264bdf4..f4778e432202 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/validation.js +++ b/packages/svelte/src/compiler/phases/2-analyze/validation.js @@ -256,8 +256,16 @@ function validate_attribute_name(attribute) { * @param {boolean} is_component */ function validate_slot_attribute(context, attribute, is_component = false) { + const parent = context.path.at(-2); let owner = undefined; + if (parent?.type === 'SnippetBlock') { + if (!is_text_attribute(attribute)) { + e.slot_attribute_invalid(attribute); + } + return; + } + let i = context.path.length; while (i--) { const ancestor = context.path[i]; @@ -283,7 +291,7 @@ function validate_slot_attribute(context, attribute, is_component = false) { owner.type === 'SvelteComponent' || owner.type === 'SvelteSelf' ) { - if (owner !== context.path.at(-2)) { + if (owner !== parent) { e.slot_attribute_invalid_placement(attribute); } diff --git a/packages/svelte/tests/runtime-runes/samples/custom-element-slot-in-snippet/Component.svelte b/packages/svelte/tests/runtime-runes/samples/custom-element-slot-in-snippet/Component.svelte new file mode 100644 index 000000000000..568baa0be92a --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/custom-element-slot-in-snippet/Component.svelte @@ -0,0 +1,18 @@ + + + + + + {@render children()} + diff --git a/packages/svelte/tests/runtime-runes/samples/custom-element-slot-in-snippet/_config.js b/packages/svelte/tests/runtime-runes/samples/custom-element-slot-in-snippet/_config.js new file mode 100644 index 000000000000..d40d8f6c7245 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/custom-element-slot-in-snippet/_config.js @@ -0,0 +1,22 @@ +import { test } from '../../test'; + +export default test({ + mode: ['client', 'server'], + html: `Default Slotted`, + test({ target, assert }) { + const shadowRoot = /** @type {ShadowRoot} */ ( + target.querySelector('my-custom-element')?.shadowRoot + ); + const [defaultSlot, namedSlot] = shadowRoot.querySelectorAll('slot'); + const assignedDefaultNodes = defaultSlot.assignedNodes(); + const assignedNamedNodes = namedSlot.assignedNodes(); + + assert.equal(assignedDefaultNodes.length, 1); + assert.equal(assignedNamedNodes.length, 1); + assert.htmlEqual(assignedDefaultNodes[0].textContent || '', `Default`); + assert.htmlEqual( + /** @type {HTMLElement} */ (assignedNamedNodes[0]).outerHTML, + `Slotted` + ); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/custom-element-slot-in-snippet/main.svelte b/packages/svelte/tests/runtime-runes/samples/custom-element-slot-in-snippet/main.svelte new file mode 100644 index 000000000000..6902623f7511 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/custom-element-slot-in-snippet/main.svelte @@ -0,0 +1,10 @@ + + + + {#snippet children()} + Default + Slotted + {/snippet} +