From 53b9570e3d36000d9b1cd24504d824eb3d8989c6 Mon Sep 17 00:00:00 2001 From: ComputerGuy <63362464+Ocean-OS@users.noreply.github.com> Date: Sun, 5 Oct 2025 14:55:18 -0700 Subject: [PATCH] fix: don't replace rest props with `$$props` for excluded props --- .changeset/nervous-flies-laugh.md | 5 +++++ .../2-analyze/visitors/VariableDeclarator.js | 15 +++++++++++++++ .../3-transform/client/visitors/Identifier.js | 6 +++++- packages/svelte/src/compiler/phases/scope.js | 2 +- .../rest-props-excludes-properties/_config.js | 7 +++++++ .../component.svelte | 4 ++++ .../rest-props-excludes-properties/main.svelte | 4 ++++ 7 files changed, 41 insertions(+), 2 deletions(-) create mode 100644 .changeset/nervous-flies-laugh.md create mode 100644 packages/svelte/tests/runtime-runes/samples/rest-props-excludes-properties/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/rest-props-excludes-properties/component.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/rest-props-excludes-properties/main.svelte diff --git a/.changeset/nervous-flies-laugh.md b/.changeset/nervous-flies-laugh.md new file mode 100644 index 000000000000..88c7694bcdc4 --- /dev/null +++ b/.changeset/nervous-flies-laugh.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: don't replace rest props with `$$props` for excluded props diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/VariableDeclarator.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/VariableDeclarator.js index f56a665de8b5..7a85b4a93aa1 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/VariableDeclarator.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/VariableDeclarator.js @@ -46,6 +46,21 @@ export function VariableDeclarator(node, context) { : path.is_rest ? 'rest_prop' : 'prop'; + if (rune === '$props' && binding.kind === 'rest_prop' && node.id.type === 'ObjectPattern') { + const { properties } = node.id; + /** @type {string[]} */ + const exclude_props = []; + for (const property of properties) { + if (property.type === 'RestElement') { + continue; + } + const key = /** @type {Identifier | Literal & { value: string | number }} */ ( + property.key + ); + exclude_props.push(key.type === 'Identifier' ? key.name : key.value.toString()); + } + (binding.metadata ??= {}).exclude_props = exclude_props; + } } } diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/Identifier.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/Identifier.js index b01ed01bd706..b43ec7891ea7 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/Identifier.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/Identifier.js @@ -32,7 +32,11 @@ export function Identifier(node, context) { grand_parent?.type !== 'AssignmentExpression' && grand_parent?.type !== 'UpdateExpression' ) { - return b.id('$$props'); + const key = /** @type {Identifier} */ (parent.property); + + if (!binding.metadata?.exclude_props?.includes(key.name)) { + return b.id('$$props'); + } } } diff --git a/packages/svelte/src/compiler/phases/scope.js b/packages/svelte/src/compiler/phases/scope.js index 0c6b64dd044d..ffccaffba393 100644 --- a/packages/svelte/src/compiler/phases/scope.js +++ b/packages/svelte/src/compiler/phases/scope.js @@ -122,7 +122,7 @@ export class Binding { /** * Additional metadata, varies per binding type - * @type {null | { inside_rest?: boolean; is_template_declaration?: boolean }} + * @type {null | { inside_rest?: boolean; is_template_declaration?: boolean; exclude_props?: string[] }} */ metadata = null; diff --git a/packages/svelte/tests/runtime-runes/samples/rest-props-excludes-properties/_config.js b/packages/svelte/tests/runtime-runes/samples/rest-props-excludes-properties/_config.js new file mode 100644 index 000000000000..66a42c08dbc7 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/rest-props-excludes-properties/_config.js @@ -0,0 +1,7 @@ +import { test } from '../../test'; + +export default test({ + async test({ assert, target }) { + assert.equal(target.textContent, ' false'); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/rest-props-excludes-properties/component.svelte b/packages/svelte/tests/runtime-runes/samples/rest-props-excludes-properties/component.svelte new file mode 100644 index 000000000000..40561218db35 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/rest-props-excludes-properties/component.svelte @@ -0,0 +1,4 @@ + +{rest.name} {'name' in rest} \ No newline at end of file diff --git a/packages/svelte/tests/runtime-runes/samples/rest-props-excludes-properties/main.svelte b/packages/svelte/tests/runtime-runes/samples/rest-props-excludes-properties/main.svelte new file mode 100644 index 000000000000..d9f6d8a21c46 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/rest-props-excludes-properties/main.svelte @@ -0,0 +1,4 @@ + + \ No newline at end of file