From 6c97a78049ccc706076e8b962d482c7a1fa27650 Mon Sep 17 00:00:00 2001 From: Paolo Ricciuti Date: Thu, 10 Apr 2025 13:29:40 +0200 Subject: [PATCH] fix: prevent ownership warnings if the fallback of a bindable is used (#15720) * fix: prevent ownership warnings if the fallback of a bindable is used * fix: filter out symbol from own keys * fix: don't create sources for `BINDABLE_FALLBACK_SYMBOL` * fix: use strategy suggested by actually competent person aka @dummdidumm * chore: rename function --- .changeset/wise-turkeys-yell.md | 5 ++++ .../src/internal/client/dev/ownership.js | 12 ++++++---- .../src/internal/client/reactivity/props.js | 2 +- .../Child.svelte | 5 ++++ .../Parent.svelte | 7 ++++++ .../_config.js | 11 +++++++++ .../main.svelte | 5 ++++ .../Parent.svelte | 8 +++++++ .../_config.js | 23 +++++++++++++++++++ .../main.svelte | 5 ++++ 10 files changed, 78 insertions(+), 5 deletions(-) create mode 100644 .changeset/wise-turkeys-yell.md create mode 100644 packages/svelte/tests/runtime-runes/samples/ownership-invalid-binding-bindable-fallback/Child.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/ownership-invalid-binding-bindable-fallback/Parent.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/ownership-invalid-binding-bindable-fallback/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/ownership-invalid-binding-bindable-fallback/main.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/ownership-invalid-mutation-bindable-fallback/Parent.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/ownership-invalid-mutation-bindable-fallback/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/ownership-invalid-mutation-bindable-fallback/main.svelte diff --git a/.changeset/wise-turkeys-yell.md b/.changeset/wise-turkeys-yell.md new file mode 100644 index 0000000000..cd5e103de3 --- /dev/null +++ b/.changeset/wise-turkeys-yell.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: prevent ownership warnings if the fallback of a bindable is used diff --git a/packages/svelte/src/internal/client/dev/ownership.js b/packages/svelte/src/internal/client/dev/ownership.js index e28a40dd77..108c7adf92 100644 --- a/packages/svelte/src/internal/client/dev/ownership.js +++ b/packages/svelte/src/internal/client/dev/ownership.js @@ -27,7 +27,7 @@ export function create_ownership_validator(props) { */ mutation: (prop, path, result, line, column) => { const name = path[0]; - if (is_bound(props, name) || !parent) { + if (is_bound_or_unset(props, name) || !parent) { return result; } @@ -52,7 +52,7 @@ export function create_ownership_validator(props) { * @param {() => any} value */ binding: (key, child_component, value) => { - if (!is_bound(props, key) && parent && value()?.[STATE_SYMBOL]) { + if (!is_bound_or_unset(props, key) && parent && value()?.[STATE_SYMBOL]) { w.ownership_invalid_binding( component[FILENAME], key, @@ -68,9 +68,13 @@ export function create_ownership_validator(props) { * @param {Record} props * @param {string} prop_name */ -function is_bound(props, prop_name) { +function is_bound_or_unset(props, prop_name) { // Can be the case when someone does `mount(Component, props)` with `let props = $state({...})` // or `createClassComponent(Component, props)` const is_entry_props = STATE_SYMBOL in props || LEGACY_PROPS in props; - return !!get_descriptor(props, prop_name)?.set || (is_entry_props && prop_name in props); + return ( + !!get_descriptor(props, prop_name)?.set || + (is_entry_props && prop_name in props) || + !(prop_name in props) + ); } diff --git a/packages/svelte/src/internal/client/reactivity/props.js b/packages/svelte/src/internal/client/reactivity/props.js index bd85b14df0..341d7c768a 100644 --- a/packages/svelte/src/internal/client/reactivity/props.js +++ b/packages/svelte/src/internal/client/reactivity/props.js @@ -7,7 +7,7 @@ import { PROPS_IS_RUNES, PROPS_IS_UPDATED } from '../../../constants.js'; -import { get_descriptor, is_function } from '../../shared/utils.js'; +import { define_property, get_descriptor, is_function } from '../../shared/utils.js'; import { mutable_source, set, source, update } from './sources.js'; import { derived, derived_safe_equal } from './deriveds.js'; import { get, captured_signals, untrack } from '../runtime.js'; diff --git a/packages/svelte/tests/runtime-runes/samples/ownership-invalid-binding-bindable-fallback/Child.svelte b/packages/svelte/tests/runtime-runes/samples/ownership-invalid-binding-bindable-fallback/Child.svelte new file mode 100644 index 0000000000..78b82caed9 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/ownership-invalid-binding-bindable-fallback/Child.svelte @@ -0,0 +1,5 @@ + + +{test} diff --git a/packages/svelte/tests/runtime-runes/samples/ownership-invalid-binding-bindable-fallback/Parent.svelte b/packages/svelte/tests/runtime-runes/samples/ownership-invalid-binding-bindable-fallback/Parent.svelte new file mode 100644 index 0000000000..7bfb17aa64 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/ownership-invalid-binding-bindable-fallback/Parent.svelte @@ -0,0 +1,7 @@ + + + diff --git a/packages/svelte/tests/runtime-runes/samples/ownership-invalid-binding-bindable-fallback/_config.js b/packages/svelte/tests/runtime-runes/samples/ownership-invalid-binding-bindable-fallback/_config.js new file mode 100644 index 0000000000..e93067eb9d --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/ownership-invalid-binding-bindable-fallback/_config.js @@ -0,0 +1,11 @@ +import { test } from '../../test'; + +export default test({ + mode: ['client'], + compileOptions: { + dev: true + }, + async test({ warnings, assert }) { + assert.deepEqual(warnings, []); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/ownership-invalid-binding-bindable-fallback/main.svelte b/packages/svelte/tests/runtime-runes/samples/ownership-invalid-binding-bindable-fallback/main.svelte new file mode 100644 index 0000000000..282afb1771 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/ownership-invalid-binding-bindable-fallback/main.svelte @@ -0,0 +1,5 @@ + + + diff --git a/packages/svelte/tests/runtime-runes/samples/ownership-invalid-mutation-bindable-fallback/Parent.svelte b/packages/svelte/tests/runtime-runes/samples/ownership-invalid-mutation-bindable-fallback/Parent.svelte new file mode 100644 index 0000000000..7d6b248da7 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/ownership-invalid-mutation-bindable-fallback/Parent.svelte @@ -0,0 +1,8 @@ + + + + + +{test} \ No newline at end of file diff --git a/packages/svelte/tests/runtime-runes/samples/ownership-invalid-mutation-bindable-fallback/_config.js b/packages/svelte/tests/runtime-runes/samples/ownership-invalid-mutation-bindable-fallback/_config.js new file mode 100644 index 0000000000..9b4e3479ea --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/ownership-invalid-mutation-bindable-fallback/_config.js @@ -0,0 +1,23 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +export default test({ + mode: ['client'], + compileOptions: { + dev: true + }, + async test({ warnings, assert, target }) { + const [btn, btn2] = target.querySelectorAll('button'); + flushSync(() => { + btn2.click(); + }); + assert.deepEqual(warnings, []); + flushSync(() => { + btn.click(); + }); + flushSync(() => { + btn2.click(); + }); + assert.deepEqual(warnings, []); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/ownership-invalid-mutation-bindable-fallback/main.svelte b/packages/svelte/tests/runtime-runes/samples/ownership-invalid-mutation-bindable-fallback/main.svelte new file mode 100644 index 0000000000..282afb1771 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/ownership-invalid-mutation-bindable-fallback/main.svelte @@ -0,0 +1,5 @@ + + +