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
pull/15726/head
Paolo Ricciuti 5 months ago committed by GitHub
parent 475b5dbe83
commit 6c97a78049
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: prevent ownership warnings if the fallback of a bindable is used

@ -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<string, any>} 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)
);
}

@ -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';

@ -0,0 +1,5 @@
<script>
const { test = $bindable() } = $props();
</script>
{test}

@ -0,0 +1,7 @@
<script>
import Child from './Child.svelte';
let { test = $bindable({}) } = $props();
</script>
<Child bind:test />

@ -0,0 +1,11 @@
import { test } from '../../test';
export default test({
mode: ['client'],
compileOptions: {
dev: true
},
async test({ warnings, assert }) {
assert.deepEqual(warnings, []);
}
});

@ -0,0 +1,5 @@
<script>
import Parent from './Parent.svelte';
</script>
<Parent />

@ -0,0 +1,8 @@
<script>
let { test = $bindable({}) } = $props();
</script>
<button onclick={()=>test = {}}></button>
<button onclick={()=>test.test = {}}></button>
{test}

@ -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, []);
}
});

@ -0,0 +1,5 @@
<script>
import Parent from './Parent.svelte';
</script>
<Parent />
Loading…
Cancel
Save