Skip to content

Commit e1c79ba

Browse files
committed
don't accidentally narrow global ownership, fix has_owner method
1 parent ed78fed commit e1c79ba

File tree

6 files changed

+65
-5
lines changed

6 files changed

+65
-5
lines changed

packages/svelte/src/internal/client/dev/ownership.js

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -133,8 +133,10 @@ function add_owner_to_object(object, owner) {
133133
const metadata = /** @type {import('#client').ProxyMetadata} */ (object?.[STATE_SYMBOL]);
134134

135135
if (metadata) {
136-
// this is a state proxy, add owner directly
137-
(metadata.owners ??= new Set()).add(owner);
136+
// this is a state proxy, add owner directly, if not globally shared
137+
if (metadata.owners !== null) {
138+
metadata.owners.add(owner);
139+
}
138140
} else if (object && typeof object === 'object') {
139141
if (object[ADD_OWNER]) {
140142
// this is a class with state fields. we put this in a render effect
@@ -159,10 +161,13 @@ function add_owner_to_object(object, owner) {
159161
*/
160162
function has_owner(metadata, component) {
161163
if (metadata.owners === null) {
162-
return metadata.parent === null ? true : has_owner(metadata.parent, component);
164+
return true;
163165
}
164166

165-
return metadata.owners.has(component);
167+
return (
168+
metadata.owners.has(component) ||
169+
(metadata.parent !== null && has_owner(metadata.parent, component))
170+
);
166171
}
167172

168173
/**

packages/svelte/src/internal/client/types.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ export interface ProxyMetadata<T = Record<string | symbol, any>> {
165165
p: ProxyStateObject<T>;
166166
/** The original target this proxy was created for */
167167
t: T;
168-
/** Dev-only — the components that 'own' this state, if any */
168+
/** Dev-only — the components that 'own' this state, if any. `null` means no owners, i.e. everyone can mutate this state. */
169169
owners: null | Set<Function>;
170170
/** Dev-only — the parent metadata object */
171171
parent: null | ProxyMetadata;
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { tick } from 'svelte';
2+
import { test } from '../../test';
3+
4+
/** @type {typeof console.warn} */
5+
let warn;
6+
7+
/** @type {any[]} */
8+
let warnings = [];
9+
10+
export default test({
11+
html: `<button>clicks: 0</button>`,
12+
13+
compileOptions: {
14+
dev: true
15+
},
16+
17+
before_test: () => {
18+
warn = console.warn;
19+
20+
console.warn = (...args) => {
21+
warnings.push(...args);
22+
};
23+
},
24+
25+
after_test: () => {
26+
console.warn = warn;
27+
warnings = [];
28+
},
29+
30+
async test({ assert, target }) {
31+
const btn = target.querySelector('button');
32+
btn?.click();
33+
await tick();
34+
35+
assert.htmlEqual(target.innerHTML, `<button>clicks: 1</button>`);
36+
37+
assert.deepEqual(warnings, []);
38+
}
39+
});
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<script>
2+
let { a = $bindable() } = $props();
3+
</script>
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<script>
2+
import Child from './child.svelte';
3+
import { global } from './state.svelte.js';
4+
global.value.count = 0;
5+
</script>
6+
7+
<!-- binding shouldn't accidentally narrow ownership when it's already global -->
8+
<Child bind:a={global.value} />
9+
10+
<button onclick={() => global.value.count++}>
11+
clicks: {global.value.count}
12+
</button>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const global = $state({ value: { count: 0 } });

0 commit comments

Comments
 (0)