diff --git a/.changeset/sour-poems-trade.md b/.changeset/sour-poems-trade.md new file mode 100644 index 0000000000..d6ab60a446 --- /dev/null +++ b/.changeset/sour-poems-trade.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: strip internal properties from rest props during SSR diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/VariableDeclaration.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/VariableDeclaration.js index 5e0b34a557..31de811ac7 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/VariableDeclaration.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/VariableDeclaration.js @@ -25,8 +25,14 @@ export function VariableDeclaration(node, context) { } if (rune === '$props') { + let has_rest = false; // remove $bindable() from props declaration - const id = walk(declarator.id, null, { + let id = walk(declarator.id, null, { + RestElement(node, context) { + if (context.path.at(-1) === declarator.id) { + has_rest = true; + } + }, AssignmentPattern(node) { if ( node.right.type === 'CallExpression' && @@ -39,6 +45,24 @@ export function VariableDeclaration(node, context) { } } }); + if (id.type === 'ObjectPattern' && has_rest) { + // If a rest pattern is used within an object pattern, we need to ensure we don't expose $$slots or $$events + id.properties.splice( + id.properties.length - 1, + 0, + // @ts-ignore + b.prop('init', b.id('$$slots'), b.id('$$slots')), + b.prop('init', b.id('$$events'), b.id('$$events')) + ); + } else if (id.type === 'Identifier') { + // If $props is referenced as an identifier, we need to ensure we don't expose $$slots or $$events as properties + // on the identifier reference + id = b.object_pattern([ + b.prop('init', b.id('$$slots'), b.id('$$slots')), + b.prop('init', b.id('$$events'), b.id('$$events')), + b.rest(b.id(id.name)) + ]); + } declarations.push( b.declarator(/** @type {Pattern} */ (context.visit(id)), b.id('$$props')) ); diff --git a/packages/svelte/tests/migrate/samples/derivations/output.svelte b/packages/svelte/tests/migrate/samples/derivations/output.svelte index e797a4da1e..ed6e72dfab 100644 --- a/packages/svelte/tests/migrate/samples/derivations/output.svelte +++ b/packages/svelte/tests/migrate/samples/derivations/output.svelte @@ -8,4 +8,4 @@ let { time_16 } = $derived({ time_16: count * 16 }) -{count} / {doubled} / {quadrupled} / {time_8} / {time_16} +{count} / {doubled} / {quadrupled} / {time_8} / {time_16} \ No newline at end of file diff --git a/packages/svelte/tests/migrate/samples/state-and-derivations-sequence/output.svelte b/packages/svelte/tests/migrate/samples/state-and-derivations-sequence/output.svelte index bd9fe82142..19e6ea1a62 100644 --- a/packages/svelte/tests/migrate/samples/state-and-derivations-sequence/output.svelte +++ b/packages/svelte/tests/migrate/samples/state-and-derivations-sequence/output.svelte @@ -11,4 +11,4 @@ -{count} / {doubled} / {quadrupled} / {time_8} / {time_16} +{count} / {doubled} / {quadrupled} / {time_8} / {time_16} \ No newline at end of file diff --git a/packages/svelte/tests/runtime-runes/samples/props-rest-2/Component.svelte b/packages/svelte/tests/runtime-runes/samples/props-rest-2/Component.svelte new file mode 100644 index 0000000000..d2abcacd64 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/props-rest-2/Component.svelte @@ -0,0 +1,8 @@ + +