diff --git a/.changeset/search-params-duplicate-set.md b/.changeset/search-params-duplicate-set.md new file mode 100644 index 0000000000..9fcaa8b289 --- /dev/null +++ b/.changeset/search-params-duplicate-set.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: update `SvelteURLSearchParams` when setting duplicate keys to the same joined value diff --git a/packages/svelte/src/reactivity/url-search-params.js b/packages/svelte/src/reactivity/url-search-params.js index 2e70bf518d..38cf3ebe4f 100644 --- a/packages/svelte/src/reactivity/url-search-params.js +++ b/packages/svelte/src/reactivity/url-search-params.js @@ -132,11 +132,12 @@ export class SvelteURLSearchParams extends URLSearchParams { * @returns {void} */ set(name, value) { - var previous = super.getAll(name).join(''); + var previous = super.getAll(name); super.set(name, value); // can't use has(name, value), because for something like https://svelte.dev?foo=1&bar=2&foo=3 // if you set `foo` to 1, then foo=3 gets deleted whilst `has("foo", "1")` returns true - if (previous !== super.getAll(name).join('')) { + var current = super.getAll(name); + if (previous.length !== current.length || previous.some((value, i) => value !== current[i])) { this.#update_url(); increment(this.#version); } diff --git a/packages/svelte/src/reactivity/url-search-params.test.ts b/packages/svelte/src/reactivity/url-search-params.test.ts index 27a413a553..b0c84872b0 100644 --- a/packages/svelte/src/reactivity/url-search-params.test.ts +++ b/packages/svelte/src/reactivity/url-search-params.test.ts @@ -55,6 +55,44 @@ test('URLSearchParams.set', () => { cleanup(); }); +test('URLSearchParams.set updates when duplicate values collapse to the same joined string', () => { + const params = new SvelteURLSearchParams('a=ab&a=c'); + const log: any = []; + + const cleanup = effect_root(() => { + render_effect(() => { + log.push(params.toString()); + }); + }); + + flushSync(() => { + params.set('a', 'abc'); + }); + + assert.deepEqual(log, ['a=ab&a=c', 'a=abc']); + + cleanup(); +}); + +test('URLSearchParams.set updates when duplicate values collapse to the same comma-joined string', () => { + const params = new SvelteURLSearchParams('a=a&a=b'); + const log: any = []; + + const cleanup = effect_root(() => { + render_effect(() => { + log.push(params.toString()); + }); + }); + + flushSync(() => { + params.set('a', 'a,b'); + }); + + assert.deepEqual(log, ['a=a&a=b', 'a=a%2Cb']); + + cleanup(); +}); + test('URLSearchParams.append', () => { const params = new SvelteURLSearchParams(); const log: any = []; diff --git a/packages/svelte/src/reactivity/url.test.ts b/packages/svelte/src/reactivity/url.test.ts index a76efeb2b2..a79aa32315 100644 --- a/packages/svelte/src/reactivity/url.test.ts +++ b/packages/svelte/src/reactivity/url.test.ts @@ -115,6 +115,25 @@ test('url.searchParams', () => { cleanup(); }); +test('url.searchParams.set updates url when duplicate values collapse to the same joined string', () => { + const url = new SvelteURL('https://svelte.dev?a=ab&a=c'); + const log: any = []; + + const cleanup = effect_root(() => { + render_effect(() => { + log.push(url.href); + }); + }); + + flushSync(() => { + url.searchParams.set('a', 'abc'); + }); + + assert.deepEqual(log, ['https://svelte.dev/?a=ab&a=c', 'https://svelte.dev/?a=abc']); + + cleanup(); +}); + test('url.search normalizes value', () => { const url = new SvelteURL('https://svelte.dev'); const log: any = [];