mirror of https://github.com/sveltejs/svelte
fix: reflect SvelteURLSearchParams changes to SvelteURL (#12285)
* separated url-search params and url classes and added more tests * making URL aware of SvelteURLSearchParams changes so they are in sync * added changeset * generated types * bail out if url.sp and SvelteSp are already in sync * sync search on searchParams change because of how node18 handles it * use ts-expect-error instead of ts-ignore * remove a bit of indirection * tweak * fix * regenerate types * short-circuit in both directions --------- Co-authored-by: Rich Harris <rich.harris@vercel.com>pull/12387/head
parent
d9860941eb
commit
b2d106b67d
@ -0,0 +1,5 @@
|
||||
---
|
||||
"svelte": patch
|
||||
---
|
||||
|
||||
fix: reflect SvelteURLSearchParams changes to SvelteURL
|
@ -0,0 +1,146 @@
|
||||
import { source } from '../internal/client/reactivity/sources.js';
|
||||
import { get } from '../internal/client/runtime.js';
|
||||
import { get_current_url } from './url.js';
|
||||
import { increment } from './utils.js';
|
||||
|
||||
export const REPLACE = Symbol();
|
||||
|
||||
export class SvelteURLSearchParams extends URLSearchParams {
|
||||
#version = source(0);
|
||||
#url = get_current_url();
|
||||
|
||||
#updating = false;
|
||||
|
||||
#update_url() {
|
||||
if (!this.#url || this.#updating) return;
|
||||
this.#updating = true;
|
||||
|
||||
const search = this.toString();
|
||||
this.#url.search = search && `?${search}`;
|
||||
|
||||
this.#updating = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {URLSearchParams} params
|
||||
*/
|
||||
[REPLACE](params) {
|
||||
if (this.#updating) return;
|
||||
this.#updating = true;
|
||||
|
||||
for (const key of [...super.keys()]) {
|
||||
super.delete(key);
|
||||
}
|
||||
|
||||
for (const [key, value] of params) {
|
||||
super.append(key, value);
|
||||
}
|
||||
|
||||
increment(this.#version);
|
||||
this.#updating = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} name
|
||||
* @param {string} value
|
||||
* @returns {void}
|
||||
*/
|
||||
append(name, value) {
|
||||
super.append(name, value);
|
||||
this.#update_url();
|
||||
increment(this.#version);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} name
|
||||
* @param {string=} value
|
||||
* @returns {void}
|
||||
*/
|
||||
delete(name, value) {
|
||||
var has_value = super.has(name, value);
|
||||
super.delete(name, value);
|
||||
if (has_value) {
|
||||
this.#update_url();
|
||||
increment(this.#version);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} name
|
||||
* @returns {string|null}
|
||||
*/
|
||||
get(name) {
|
||||
get(this.#version);
|
||||
return super.get(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} name
|
||||
* @returns {string[]}
|
||||
*/
|
||||
getAll(name) {
|
||||
get(this.#version);
|
||||
return super.getAll(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} name
|
||||
* @param {string=} value
|
||||
* @returns {boolean}
|
||||
*/
|
||||
has(name, value) {
|
||||
get(this.#version);
|
||||
return super.has(name, value);
|
||||
}
|
||||
|
||||
keys() {
|
||||
get(this.#version);
|
||||
return super.keys();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} name
|
||||
* @param {string} value
|
||||
* @returns {void}
|
||||
*/
|
||||
set(name, value) {
|
||||
var previous = super.getAll(name).join('');
|
||||
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('')) {
|
||||
this.#update_url();
|
||||
increment(this.#version);
|
||||
}
|
||||
}
|
||||
|
||||
sort() {
|
||||
super.sort();
|
||||
this.#update_url();
|
||||
increment(this.#version);
|
||||
}
|
||||
|
||||
toString() {
|
||||
get(this.#version);
|
||||
return super.toString();
|
||||
}
|
||||
|
||||
values() {
|
||||
get(this.#version);
|
||||
return super.values();
|
||||
}
|
||||
|
||||
entries() {
|
||||
get(this.#version);
|
||||
return super.entries();
|
||||
}
|
||||
|
||||
[Symbol.iterator]() {
|
||||
return this.entries();
|
||||
}
|
||||
|
||||
get size() {
|
||||
get(this.#version);
|
||||
return super.size;
|
||||
}
|
||||
}
|
@ -0,0 +1,208 @@
|
||||
import { render_effect, effect_root } from '../internal/client/reactivity/effects.js';
|
||||
import { flushSync } from '../index-client.js';
|
||||
import { assert, test } from 'vitest';
|
||||
import { SvelteURLSearchParams } from './url-search-params';
|
||||
|
||||
test('new URLSearchParams', () => {
|
||||
const params = new SvelteURLSearchParams('a=b');
|
||||
const log: any = [];
|
||||
|
||||
const cleanup = effect_root(() => {
|
||||
render_effect(() => {
|
||||
log.push(params.toString());
|
||||
});
|
||||
});
|
||||
|
||||
flushSync(() => {
|
||||
params.set('a', 'c');
|
||||
});
|
||||
|
||||
flushSync(() => {
|
||||
// nothing should happen here
|
||||
params.set('a', 'c');
|
||||
});
|
||||
|
||||
assert.deepEqual(log, ['a=b', 'a=c']);
|
||||
|
||||
cleanup();
|
||||
});
|
||||
|
||||
test('URLSearchParams.set', () => {
|
||||
const params = new SvelteURLSearchParams();
|
||||
const log: any = [];
|
||||
|
||||
const cleanup = effect_root(() => {
|
||||
render_effect(() => {
|
||||
log.push(params.toString());
|
||||
});
|
||||
});
|
||||
|
||||
flushSync(() => {
|
||||
params.set('a', 'b');
|
||||
});
|
||||
|
||||
flushSync(() => {
|
||||
params.set('a', 'c');
|
||||
});
|
||||
|
||||
flushSync(() => {
|
||||
// nothing should happen here
|
||||
params.set('a', 'c');
|
||||
});
|
||||
|
||||
assert.deepEqual(log, ['', 'a=b', 'a=c']);
|
||||
|
||||
cleanup();
|
||||
});
|
||||
|
||||
test('URLSearchParams.append', () => {
|
||||
const params = new SvelteURLSearchParams();
|
||||
const log: any = [];
|
||||
|
||||
const cleanup = effect_root(() => {
|
||||
render_effect(() => {
|
||||
log.push(params.toString());
|
||||
});
|
||||
});
|
||||
|
||||
flushSync(() => {
|
||||
params.append('a', 'b');
|
||||
});
|
||||
|
||||
flushSync(() => {
|
||||
// nothing should happen here
|
||||
params.set('a', 'b');
|
||||
});
|
||||
|
||||
flushSync(() => {
|
||||
params.append('a', 'c');
|
||||
});
|
||||
|
||||
assert.deepEqual(log, ['', 'a=b', 'a=b&a=c']);
|
||||
|
||||
cleanup();
|
||||
});
|
||||
|
||||
test('URLSearchParams.delete', () => {
|
||||
const params = new SvelteURLSearchParams('a=b&c=d');
|
||||
const log: any = [];
|
||||
|
||||
const cleanup = effect_root(() => {
|
||||
render_effect(() => {
|
||||
log.push(params.toString());
|
||||
});
|
||||
});
|
||||
|
||||
flushSync(() => {
|
||||
params.delete('a');
|
||||
});
|
||||
|
||||
flushSync(() => {
|
||||
// nothing should happen here
|
||||
params.delete('a');
|
||||
});
|
||||
|
||||
flushSync(() => {
|
||||
params.set('a', 'b');
|
||||
});
|
||||
|
||||
assert.deepEqual(log, ['a=b&c=d', 'c=d', 'c=d&a=b']);
|
||||
|
||||
cleanup();
|
||||
});
|
||||
|
||||
test('URLSearchParams.get', () => {
|
||||
const params = new SvelteURLSearchParams('a=b&c=d');
|
||||
const log: any = [];
|
||||
|
||||
const cleanup = effect_root(() => {
|
||||
render_effect(() => {
|
||||
log.push(params.get('a'));
|
||||
});
|
||||
render_effect(() => {
|
||||
log.push(params.get('c'));
|
||||
});
|
||||
render_effect(() => {
|
||||
log.push(params.get('e'));
|
||||
});
|
||||
});
|
||||
|
||||
flushSync(() => {
|
||||
params.set('a', 'b');
|
||||
});
|
||||
|
||||
flushSync(() => {
|
||||
params.set('a', 'new-b');
|
||||
});
|
||||
|
||||
flushSync(() => {
|
||||
params.delete('a');
|
||||
});
|
||||
|
||||
assert.deepEqual(log, ['b', 'd', null, 'new-b', 'd', null, null, 'd', null]);
|
||||
|
||||
cleanup();
|
||||
});
|
||||
|
||||
test('URLSearchParams.getAll', () => {
|
||||
const params = new SvelteURLSearchParams('a=b&c=d');
|
||||
const log: any = [];
|
||||
|
||||
const cleanup = effect_root(() => {
|
||||
render_effect(() => {
|
||||
log.push(params.getAll('a'));
|
||||
});
|
||||
render_effect(() => {
|
||||
log.push(params.getAll('q'));
|
||||
});
|
||||
});
|
||||
|
||||
flushSync(() => {
|
||||
params.append('a', 'b1');
|
||||
});
|
||||
|
||||
flushSync(() => {
|
||||
params.append('q', 'z');
|
||||
});
|
||||
|
||||
assert.deepEqual(log, [
|
||||
// initial
|
||||
['b'],
|
||||
[],
|
||||
// first flush
|
||||
['b', 'b1'],
|
||||
[],
|
||||
// second flush
|
||||
['b', 'b1'],
|
||||
['z']
|
||||
]);
|
||||
|
||||
cleanup();
|
||||
});
|
||||
|
||||
test('URLSearchParams.toString', () => {
|
||||
const params = new SvelteURLSearchParams();
|
||||
const log: any = [];
|
||||
|
||||
const cleanup = effect_root(() => {
|
||||
render_effect(() => {
|
||||
log.push(params.toString());
|
||||
});
|
||||
});
|
||||
|
||||
flushSync(() => {
|
||||
params.set('a', 'b');
|
||||
});
|
||||
|
||||
flushSync(() => {
|
||||
params.append('a', 'c');
|
||||
});
|
||||
|
||||
assert.deepEqual(log, ['', 'a=b', 'a=b&a=c']);
|
||||
|
||||
cleanup();
|
||||
});
|
||||
|
||||
test('SvelteURLSearchParams instanceof URLSearchParams', () => {
|
||||
assert.ok(new SvelteURLSearchParams() instanceof URLSearchParams);
|
||||
});
|
Loading…
Reference in new issue