Skip to content

Commit 9be0fdd

Browse files
committed
interop for passing slots but rendering them with render tags
1 parent 46dcfca commit 9be0fdd

File tree

8 files changed

+118
-11
lines changed

8 files changed

+118
-11
lines changed

packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1786,6 +1786,10 @@ export const template_visitors = {
17861786
const raw_args = unwrap_optional(node.expression).arguments;
17871787
const is_reactive =
17881788
callee.type !== 'Identifier' || context.state.scope.get(callee.name)?.kind !== 'normal';
1789+
const needs_backwards_compat =
1790+
callee.type === 'Identifier' &&
1791+
raw_args.length < 2 &&
1792+
context.state.scope.get(callee.name)?.kind === 'prop';
17891793

17901794
/** @type {import('estree').Expression[]} */
17911795
const args = [context.state.node];
@@ -1798,7 +1802,19 @@ export const template_visitors = {
17981802
snippet_function = b.call('$.validate_snippet', snippet_function);
17991803
}
18001804

1801-
if (is_reactive) {
1805+
if (needs_backwards_compat) {
1806+
context.state.init.push(
1807+
b.stmt(
1808+
b.call(
1809+
'$.render_snippet_or_slot',
1810+
b.thunk(snippet_function),
1811+
b.id('$$props'),
1812+
b.literal(callee.name),
1813+
...args
1814+
)
1815+
)
1816+
);
1817+
} else if (is_reactive) {
18021818
context.state.init.push(b.stmt(b.call('$.snippet', b.thunk(snippet_function), ...args)));
18031819
} else {
18041820
context.state.init.push(

packages/svelte/src/compiler/phases/3-transform/server/transform-server.js

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1303,6 +1303,10 @@ const template_visitors = {
13031303

13041304
const callee = unwrap_optional(node.expression).callee;
13051305
const raw_args = unwrap_optional(node.expression).arguments;
1306+
const needs_backwards_compat =
1307+
callee.type === 'Identifier' &&
1308+
raw_args.length < 2 &&
1309+
context.state.scope.get(callee.name)?.kind === 'prop';
13061310

13071311
const expression = /** @type {import('estree').Expression} */ (context.visit(callee));
13081312
const snippet_function = state.options.dev
@@ -1313,17 +1317,34 @@ const template_visitors = {
13131317
return /** @type {import('estree').Expression} */ (context.visit(arg));
13141318
});
13151319

1316-
state.template.push(
1317-
t_statement(
1318-
b.stmt(
1319-
(node.expression.type === 'CallExpression' ? b.call : b.maybe_call)(
1320-
snippet_function,
1321-
b.id('$$payload'),
1322-
...snippet_args
1320+
if (needs_backwards_compat) {
1321+
state.template.push(
1322+
t_statement(
1323+
b.stmt(
1324+
b.call(
1325+
'$.render_snippet_or_slot',
1326+
snippet_function,
1327+
b.id('$$props'),
1328+
b.literal(callee.name),
1329+
b.id('$$payload'),
1330+
...snippet_args
1331+
)
13231332
)
13241333
)
1325-
)
1326-
);
1334+
);
1335+
} else {
1336+
state.template.push(
1337+
t_statement(
1338+
b.stmt(
1339+
(node.expression.type === 'CallExpression' ? b.call : b.maybe_call)(
1340+
snippet_function,
1341+
b.id('$$payload'),
1342+
...snippet_args
1343+
)
1344+
)
1345+
)
1346+
);
1347+
}
13271348

13281349
state.template.push(block_close);
13291350
},

packages/svelte/src/internal/client/dom/blocks/snippet.js

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,3 +57,34 @@ export function wrap_snippet(fn) {
5757
}
5858
);
5959
}
60+
61+
/**
62+
* Remove this once slots are gone
63+
* @param {any} snippet_fn
64+
* @param {Record<string, any>} $$props
65+
* @param {string} name
66+
* @param {Element} node
67+
* @param {any} slot_props
68+
*/
69+
export function render_snippet_or_slot(snippet_fn, $$props, name, node, slot_props) {
70+
if ($$props.$$slots) {
71+
const slot = $$props.$$slots[name === 'children' ? 'default' : name];
72+
if (typeof slot === 'function') {
73+
let props = undefined;
74+
if (slot_props) {
75+
props = new Proxy(
76+
{},
77+
{
78+
get(_, key) {
79+
return slot_props()?.[key];
80+
}
81+
}
82+
);
83+
}
84+
slot(node, props);
85+
return;
86+
}
87+
}
88+
89+
snippet(snippet_fn, node, slot_props);
90+
}

packages/svelte/src/internal/client/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ export { key_block as key } from './dom/blocks/key.js';
1515
export { css_props } from './dom/blocks/css-props.js';
1616
export { index, each } from './dom/blocks/each.js';
1717
export { html } from './dom/blocks/html.js';
18-
export { snippet, wrap_snippet } from './dom/blocks/snippet.js';
18+
export { snippet, wrap_snippet, render_snippet_or_slot } from './dom/blocks/snippet.js';
1919
export { component } from './dom/blocks/svelte-component.js';
2020
export { element } from './dom/blocks/svelte-element.js';
2121
export { head } from './dom/blocks/svelte-head.js';

packages/svelte/src/internal/server/index.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -563,6 +563,26 @@ export function sanitize_slots(props) {
563563
return sanitized;
564564
}
565565

566+
/**
567+
* Remove this once slots are gone
568+
* @param {any} snippet_fn
569+
* @param {Record<string, any>} $$props
570+
* @param {string} name
571+
* @param {Payload} payload
572+
* @param {any} slot_props
573+
*/
574+
export function render_snippet_or_slot(snippet_fn, $$props, name, payload, slot_props) {
575+
if ($$props.$$slots) {
576+
const slot = $$props.$$slots[name === 'children' ? 'default' : name];
577+
if (typeof slot === 'function') {
578+
slot(payload, slot_props);
579+
return;
580+
}
581+
}
582+
583+
snippet_fn?.(payload, slot_props);
584+
}
585+
566586
/**
567587
* Legacy mode: If the prop has a fallback and is bound in the
568588
* parent component, propagate the fallback value upwards.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<script>
2+
let { children, named } = $props();
3+
</script>
4+
5+
<p>{@render children()}</p>
6+
{@render named({foo: 'foo'})}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { test } from '../../test';
2+
3+
export default test({
4+
html: `<p>Default</p> <p slot="named">Named foo</p>`
5+
});
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<script>
2+
import Child from './Child.svelte';
3+
</script>
4+
5+
<Child>
6+
Default
7+
<p slot="named" let:foo>Named {foo}</p>
8+
</Child>

0 commit comments

Comments
 (0)