From 3494f4cc6b1dbeed12836bf53076705b8b1c7050 Mon Sep 17 00:00:00 2001 From: Simon Holthausen Date: Tue, 14 May 2024 15:12:57 +0200 Subject: [PATCH] allow passing snippets rendered as slots --- .../3-transform/client/visitors/template.js | 19 +++++++++++------- .../3-transform/server/transform-server.js | 20 +++++++++++++------ .../src/internal/client/dom/legacy/misc.js | 12 ----------- packages/svelte/src/internal/client/index.js | 3 +-- packages/svelte/src/internal/client/render.js | 14 +++++++++++-- packages/svelte/src/internal/server/index.js | 16 +++++++++++---- .../samples/snippet-slot-interop/Child.svelte | 2 ++ .../samples/snippet-slot-interop/_config.js | 5 +++++ .../samples/snippet-slot-interop/main.svelte | 10 ++++++++++ 9 files changed, 68 insertions(+), 33 deletions(-) create mode 100644 packages/svelte/tests/runtime-runes/samples/snippet-slot-interop/Child.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/snippet-slot-interop/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/snippet-slot-interop/main.svelte diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js index 85fcf7afe3..fcf8d00d4f 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js @@ -789,6 +789,8 @@ function serialize_inline_component(node, component_name, context) { /** @type {import('estree').Statement[]} */ const snippet_declarations = []; + /** @type {import('estree').Property[]} */ + const serialized_slots = []; // Group children by slot for (const child of node.fragment.nodes) { @@ -802,6 +804,8 @@ function serialize_inline_component(node, component_name, context) { }); push_prop(b.prop('init', child.expression, child.expression)); + // Back/forward compatibility: allows people to pass snippets when component still uses slots + serialized_slots.push(b.init(child.expression.name, b.true)); continue; } @@ -825,8 +829,6 @@ function serialize_inline_component(node, component_name, context) { } // Serialize each slot - /** @type {import('estree').Property[]} */ - const serialized_slots = []; for (const slot_name of Object.keys(children)) { const body = create_block(node, `${node.name}_${slot_name}`, children[slot_name], context); if (body.length === 0) continue; @@ -3070,11 +3072,14 @@ export const template_visitors = { b.block(create_block(node, 'fallback', node.fragment.nodes, context)) ); - const expression = is_default - ? b.call('$.default_slot', b.id('$$props')) - : b.member(b.member(b.id('$$props'), b.id('$$slots')), name, true, true); - - const slot = b.call('$.slot', context.state.node, expression, props_expression, fallback); + const slot = b.call( + '$.slot', + context.state.node, + b.id('$$props'), + name, + props_expression, + fallback + ); context.state.init.push(b.stmt(slot)); }, SvelteHead(node, context) { diff --git a/packages/svelte/src/compiler/phases/3-transform/server/transform-server.js b/packages/svelte/src/compiler/phases/3-transform/server/transform-server.js index 660ba7dbf1..f5d7e2c3ee 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/transform-server.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/transform-server.js @@ -1037,6 +1037,8 @@ function serialize_inline_component(node, component_name, context) { /** @type {import('estree').Statement[]} */ const snippet_declarations = []; + /** @type {import('estree').Property[]} */ + const serialized_slots = []; // Group children by slot for (const child of node.fragment.nodes) { @@ -1050,6 +1052,8 @@ function serialize_inline_component(node, component_name, context) { }); push_prop(b.prop('init', child.expression, child.expression)); + // Back/forward compatibility: allows people to pass snippets when component still uses slots + serialized_slots.push(b.init(child.expression.name, b.true)); continue; } @@ -1071,9 +1075,6 @@ function serialize_inline_component(node, component_name, context) { } // Serialize each slot - /** @type {import('estree').Property[]} */ - const serialized_slots = []; - for (const slot_name of Object.keys(children)) { const body = create_block(node, children[slot_name], context); if (body.length === 0) continue; @@ -1750,7 +1751,7 @@ const template_visitors = { const lets = []; /** @type {import('estree').Expression} */ - let expression = b.call('$.default_slot', b.id('$$props')); + let slot_name = b.literal('default'); for (const attribute of node.attributes) { if (attribute.type === 'SpreadAttribute') { @@ -1758,7 +1759,7 @@ const template_visitors = { } else if (attribute.type === 'Attribute') { const value = serialize_attribute_value(attribute.value, context, false, true); if (attribute.name === 'name') { - expression = b.member(b.member_id('$$props.$$slots'), value, true, true); + slot_name = value; } else if (attribute.name !== 'slot') { if (attribute.metadata.dynamic) { props.push(b.get(attribute.name, [b.return(value)])); @@ -1782,7 +1783,14 @@ const template_visitors = { node.fragment.nodes.length === 0 ? b.literal(null) : b.thunk(b.block(create_block(node, node.fragment.nodes, context))); - const slot = b.call('$.slot', b.id('$$payload'), expression, props_expression, fallback); + const slot = b.call( + '$.slot', + b.id('$$payload'), + b.id('$$props'), + slot_name, + props_expression, + fallback + ); state.template.push(t_statement(b.stmt(slot))); state.template.push(block_close); diff --git a/packages/svelte/src/internal/client/dom/legacy/misc.js b/packages/svelte/src/internal/client/dom/legacy/misc.js index b9ef9fc53a..a4b52efe28 100644 --- a/packages/svelte/src/internal/client/dom/legacy/misc.js +++ b/packages/svelte/src/internal/client/dom/legacy/misc.js @@ -66,15 +66,3 @@ export function update_legacy_props($$new_props) { } } } - -/** - * @param {Record} $$props - */ -export function default_slot($$props) { - var children = $$props.$$slots?.default; - if (children === true) { - return $$props.children; - } else { - return children; - } -} diff --git a/packages/svelte/src/internal/client/index.js b/packages/svelte/src/internal/client/index.js index 9c65c98963..c54de90fe5 100644 --- a/packages/svelte/src/internal/client/index.js +++ b/packages/svelte/src/internal/client/index.js @@ -72,8 +72,7 @@ export { add_legacy_event_listener, bubble_event, reactive_import, - update_legacy_props, - default_slot + update_legacy_props } from './dom/legacy/misc.js'; export { append, diff --git a/packages/svelte/src/internal/client/render.js b/packages/svelte/src/internal/client/render.js index ee9cfbfc30..534d386ef1 100644 --- a/packages/svelte/src/internal/client/render.js +++ b/packages/svelte/src/internal/client/render.js @@ -63,11 +63,21 @@ export function set_text(dom, value) { /** * @param {Comment} anchor - * @param {void | ((anchor: Comment, slot_props: Record) => void)} slot_fn + * @param {Record} $$props + * @param {string} slot_name * @param {Record} slot_props * @param {null | ((anchor: Comment) => void)} fallback_fn */ -export function slot(anchor, slot_fn, slot_props, fallback_fn) { +export function slot(anchor, $$props, slot_name, slot_props, fallback_fn) { + var slot_fn = $$props.$$slots?.[slot_name]; + if (slot_fn === true) { + if (slot_name === 'default') { + slot_fn = $$props.children; + } else { + slot_fn = $$props[slot_name]; + } + } + if (slot_fn === undefined) { if (fallback_fn !== null) { fallback_fn(anchor); diff --git a/packages/svelte/src/internal/server/index.js b/packages/svelte/src/internal/server/index.js index 3928b897b7..cd5d563545 100644 --- a/packages/svelte/src/internal/server/index.js +++ b/packages/svelte/src/internal/server/index.js @@ -499,12 +499,22 @@ export async function value_or_fallback_async(value, fallback) { /** * @param {Payload} payload - * @param {void | ((payload: Payload, props: Record) => void)} slot_fn + * @param {Record} $$props + * @param {string} slot_name * @param {Record} slot_props * @param {null | (() => void)} fallback_fn * @returns {void} */ -export function slot(payload, slot_fn, slot_props, fallback_fn) { +export function slot(payload, $$props, slot_name, slot_props, fallback_fn) { + var slot_fn = $$props.$$slots?.[slot_name]; + if (slot_fn === true) { + if (slot_name === 'default') { + slot_fn = $$props.children; + } else { + slot_fn = $$props[slot_name]; + } + } + if (slot_fn === undefined) { if (fallback_fn !== null) { fallback_fn(); @@ -631,5 +641,3 @@ export { } from '../shared/validate.js'; export { escape_html as escape }; - -export { default_slot } from '../client/dom/legacy/misc.js'; diff --git a/packages/svelte/tests/runtime-runes/samples/snippet-slot-interop/Child.svelte b/packages/svelte/tests/runtime-runes/samples/snippet-slot-interop/Child.svelte new file mode 100644 index 0000000000..c14bed8d55 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/snippet-slot-interop/Child.svelte @@ -0,0 +1,2 @@ +

+

diff --git a/packages/svelte/tests/runtime-runes/samples/snippet-slot-interop/_config.js b/packages/svelte/tests/runtime-runes/samples/snippet-slot-interop/_config.js new file mode 100644 index 0000000000..99e1719bf3 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/snippet-slot-interop/_config.js @@ -0,0 +1,5 @@ +import { test } from '../../test'; + +export default test({ + html: `

Default

Named

` +}); diff --git a/packages/svelte/tests/runtime-runes/samples/snippet-slot-interop/main.svelte b/packages/svelte/tests/runtime-runes/samples/snippet-slot-interop/main.svelte new file mode 100644 index 0000000000..d439d5bd69 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/snippet-slot-interop/main.svelte @@ -0,0 +1,10 @@ + + + + Default + {#snippet named()} + Named + {/snippet} +