From 303750a1241ac0ded3115ff879fa417e23fe7c89 Mon Sep 17 00:00:00 2001 From: ComputerGuy <63362464+Ocean-OS@users.noreply.github.com> Date: Tue, 7 Oct 2025 07:21:43 -0700 Subject: [PATCH] fix: don't replace rest props with `$$props` for excluded props (#16898) Closes #16895 --- .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 0000000000..88c7694bcd --- /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 f56a665de8..7a85b4a93a 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 b01ed01bd7..b43ec7891e 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 0c6b64dd04..ffccaffba3 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 0000000000..66a42c08db --- /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 0000000000..40561218db --- /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 0000000000..d9f6d8a21c --- /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