Skip to content

Commit a784607

Browse files
author
Fedor Nezhivoi
committed
fix: lift unsafe_state_mutation constrained for SvelteDate, SvelteURL and SvelteURLSearchParams created inside the derived
1 parent 3c3255f commit a784607

File tree

10 files changed

+164
-14
lines changed

10 files changed

+164
-14
lines changed

.changeset/fair-bats-visit.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22
'svelte': patch
33
---
44

5-
lift unsafe_state_mutation constrained for SvelteSet and SvelteMap created inside the derived
5+
lift unsafe_state_mutation constraints for SvelteSet, SvelteMap, SvelteDate, SvelteURL and SvelteURLSearchParams created inside the derived

packages/svelte/src/reactivity/date.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/** @import { Source } from '#client' */
22
import { derived } from '../internal/client/index.js';
3-
import { source, set } from '../internal/client/reactivity/sources.js';
3+
import { set, state } from '../internal/client/reactivity/sources.js';
44
import { tag } from '../internal/client/dev/tracing.js';
55
import { active_reaction, get, set_active_reaction } from '../internal/client/runtime.js';
66
import { DEV } from 'esm-env';
@@ -40,7 +40,7 @@ var inited = false;
4040
* ```
4141
*/
4242
export class SvelteDate extends Date {
43-
#time = source(super.getTime());
43+
#time = state(super.getTime());
4444

4545
/** @type {Map<keyof Date, Source<unknown>>} */
4646
#deriveds = new Map();

packages/svelte/src/reactivity/url-search-params.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { DEV } from 'esm-env';
2-
import { source } from '../internal/client/reactivity/sources.js';
2+
import { state } from '../internal/client/reactivity/sources.js';
33
import { tag } from '../internal/client/dev/tracing.js';
44
import { get } from '../internal/client/runtime.js';
55
import { get_current_url } from './url.js';
@@ -34,7 +34,7 @@ export const REPLACE = Symbol();
3434
* ```
3535
*/
3636
export class SvelteURLSearchParams extends URLSearchParams {
37-
#version = DEV ? tag(source(0), 'SvelteURLSearchParams version') : source(0);
37+
#version = DEV ? tag(state(0), 'SvelteURLSearchParams version') : state(0);
3838
#url = get_current_url();
3939

4040
#updating = false;

packages/svelte/src/reactivity/url.js

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { DEV } from 'esm-env';
2-
import { source, set } from '../internal/client/reactivity/sources.js';
2+
import { set, state } from '../internal/client/reactivity/sources.js';
33
import { tag } from '../internal/client/dev/tracing.js';
44
import { get } from '../internal/client/runtime.js';
55
import { REPLACE, SvelteURLSearchParams } from './url-search-params.js';
@@ -40,14 +40,14 @@ export function get_current_url() {
4040
* ```
4141
*/
4242
export class SvelteURL extends URL {
43-
#protocol = source(super.protocol);
44-
#username = source(super.username);
45-
#password = source(super.password);
46-
#hostname = source(super.hostname);
47-
#port = source(super.port);
48-
#pathname = source(super.pathname);
49-
#hash = source(super.hash);
50-
#search = source(super.search);
43+
#protocol = state(super.protocol);
44+
#username = state(super.username);
45+
#password = state(super.password);
46+
#hostname = state(super.hostname);
47+
#port = state(super.port);
48+
#pathname = state(super.pathname);
49+
#hash = state(super.hash);
50+
#search = state(super.search);
5151
#searchParams;
5252

5353
/**
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { flushSync } from 'svelte';
2+
import { test } from '../../test';
3+
4+
export default test({
5+
compileOptions: {
6+
dev: true,
7+
runes: true
8+
},
9+
10+
test({ assert, target }) {
11+
const [button1, button2] = target.querySelectorAll('button');
12+
13+
assert.throws(() => {
14+
button1?.click();
15+
flushSync();
16+
}, /state_unsafe_mutation/);
17+
18+
assert.doesNotThrow(() => {
19+
button2?.click();
20+
flushSync();
21+
});
22+
}
23+
});
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<script>
2+
import { SvelteDate } from 'svelte/reactivity';
3+
4+
let visibleExternal = $state(false);
5+
let external = new SvelteDate();
6+
const throws = $derived.by(() => {
7+
external.setTime(12345);
8+
return external;
9+
})
10+
11+
let visibleInternal = $state(false);
12+
const works = $derived.by(() => {
13+
let internal = new SvelteDate();
14+
internal.setTime(12345);
15+
return internal;
16+
})
17+
</script>
18+
19+
<button onclick={() => (visibleExternal = true)}>external</button>
20+
{#if visibleExternal}
21+
{throws}
22+
{/if}
23+
<button onclick={() => (visibleInternal = true)}>internal</button>
24+
{#if visibleInternal}
25+
{works}
26+
{/if}
27+
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { flushSync } from 'svelte';
2+
import { test } from '../../test';
3+
4+
export default test({
5+
compileOptions: {
6+
dev: true,
7+
runes: true
8+
},
9+
10+
test({ assert, target }) {
11+
const [button1, button2] = target.querySelectorAll('button');
12+
13+
assert.throws(() => {
14+
button1?.click();
15+
flushSync();
16+
}, /state_unsafe_mutation/);
17+
18+
assert.doesNotThrow(() => {
19+
button2?.click();
20+
flushSync();
21+
});
22+
}
23+
});
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<script>
2+
import { SvelteURLSearchParams } from 'svelte/reactivity';
3+
4+
let visibleExternal = $state(false);
5+
let external = new SvelteURLSearchParams();
6+
const throws = $derived.by(() => {
7+
external.append('foo', 'bar')
8+
return external;
9+
})
10+
11+
let visibleInternal = $state(false);
12+
const works = $derived.by(() => {
13+
let internal = new SvelteURLSearchParams();
14+
internal.append('foo', 'bar')
15+
return internal;
16+
})
17+
</script>
18+
19+
<button onclick={() => (visibleExternal = true)}>external</button>
20+
{#if visibleExternal}
21+
{throws}
22+
{/if}
23+
<button onclick={() => (visibleInternal = true)}>internal</button>
24+
{#if visibleInternal}
25+
{works}
26+
{/if}
27+
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { flushSync } from 'svelte';
2+
import { test } from '../../test';
3+
4+
export default test({
5+
compileOptions: {
6+
dev: true,
7+
runes: true
8+
},
9+
10+
test({ assert, target }) {
11+
const [button1, button2] = target.querySelectorAll('button');
12+
13+
assert.throws(() => {
14+
button1?.click();
15+
flushSync();
16+
}, /state_unsafe_mutation/);
17+
18+
assert.doesNotThrow(() => {
19+
button2?.click();
20+
flushSync();
21+
});
22+
}
23+
});
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<script>
2+
import { SvelteURL } from 'svelte/reactivity';
3+
4+
let visibleExternal = $state(false);
5+
let external = new SvelteURL('https://svelte.dev');
6+
const throws = $derived.by(() => {
7+
external.host = 'kit.svelte.dev'
8+
return external;
9+
})
10+
11+
let visibleInternal = $state(false);
12+
const works = $derived.by(() => {
13+
let internal = new SvelteURL('https://svelte.dev');
14+
internal.host = 'kit.svelte.dev'
15+
return internal;
16+
})
17+
</script>
18+
19+
<button onclick={() => (visibleExternal = true)}>external</button>
20+
{#if visibleExternal}
21+
{throws}
22+
{/if}
23+
<button onclick={() => (visibleInternal = true)}>internal</button>
24+
{#if visibleInternal}
25+
{works}
26+
{/if}
27+

0 commit comments

Comments
 (0)