Merge branch 'main' into fix-unmount-memory-leak

pull/12101/head
Rich Harris 2 years ago committed by GitHub
commit 4f84a078bd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
'svelte': patch
---
feat: more accurate `render`/`mount`/`hydrate` options

@ -0,0 +1,5 @@
---
"svelte": patch
---
fix: wait a microtask for await blocks to reduce UI churn

@ -0,0 +1,5 @@
---
"svelte": patch
---
fix: ensure state update expressions are serialised correctly

@ -0,0 +1,5 @@
---
"svelte": patch
---
fix: repair each block length even without an else

@ -0,0 +1,5 @@
---
"svelte": patch
---
breaking: bump dts-buddy

@ -0,0 +1,5 @@
---
"svelte": patch
---
fix: better binding interop between runes/non-runes components

@ -0,0 +1,5 @@
---
"svelte": patch
---
fix: throw compilation error for malformed snippets

@ -141,6 +141,7 @@
"fair-crabs-check",
"fair-pianos-talk",
"fair-spies-repeat",
"famous-chairs-notice",
"famous-falcons-melt",
"famous-kiwis-thank",
"famous-knives-sneeze",
@ -187,6 +188,7 @@
"fuzzy-bags-camp",
"fuzzy-donuts-provide",
"gentle-dolls-juggle",
"gentle-eagles-walk",
"gentle-sheep-hug",
"gentle-spies-happen",
"gentle-ties-fetch",
@ -205,6 +207,7 @@
"good-plums-type",
"good-rivers-yawn",
"good-roses-argue",
"gorgeous-boxes-design",
"gorgeous-hats-wonder",
"gorgeous-monkeys-carry",
"gorgeous-singers-rest",
@ -219,6 +222,7 @@
"grumpy-jars-sparkle",
"happy-beds-scream",
"happy-dogs-jump",
"happy-lobsters-lick",
"happy-suits-film",
"healthy-ants-film",
"healthy-planes-vanish",
@ -322,6 +326,7 @@
"mighty-files-hammer",
"mighty-frogs-obey",
"modern-apricots-promise",
"modern-fishes-double",
"moody-carrots-lay",
"moody-frogs-exist",
"moody-ghosts-buy",
@ -337,6 +342,7 @@
"neat-files-rescue",
"neat-jokes-beam",
"nervous-berries-boil",
"nervous-ducks-repeat",
"nervous-spoons-relax",
"nervous-turkeys-end",
"new-boats-wait",
@ -380,6 +386,7 @@
"polite-dolphins-care",
"polite-pumpkins-guess",
"polite-ravens-study",
"polite-ways-serve",
"poor-eggs-enjoy",
"poor-hats-design",
"poor-seahorses-flash",
@ -522,6 +529,7 @@
"stale-comics-look",
"stale-fans-rest",
"stale-jeans-refuse",
"stale-nails-listen",
"strange-apricots-happen",
"strange-roses-brake",
"strong-apricots-destroy",
@ -537,6 +545,7 @@
"swift-feet-juggle",
"swift-knives-tie",
"swift-poets-carry",
"swift-rats-sing",
"swift-ravens-hunt",
"swift-seahorses-deliver",
"tall-books-grin",
@ -553,6 +562,7 @@
"tasty-steaks-smile",
"ten-eels-move",
"ten-foxes-repeat",
"ten-geese-share",
"ten-jokes-divide",
"ten-peaches-sleep",
"ten-singers-cough",
@ -597,6 +607,7 @@
"twelve-dragons-join",
"twelve-onions-juggle",
"twelve-worms-jog",
"twenty-gifts-develop",
"two-brooms-fail",
"two-candles-move",
"two-dogs-accept",

@ -0,0 +1,5 @@
---
"svelte": patch
---
chore: remove anchor node from each block items

@ -0,0 +1,5 @@
---
"svelte": patch
---
fix: prevent `a11y_label_has_associated_control` false positive for component or render tag in `<label>`

@ -0,0 +1,5 @@
---
"svelte": patch
---
fix: allow multiple optional parameters with defaults in snippets

@ -0,0 +1,5 @@
---
"svelte": patch
---
fix: remove document event listeners on unmount

2
.gitignore vendored

@ -22,3 +22,5 @@ coverage
.DS_Store
tmp
benchmarking/compare/.results

@ -0,0 +1,58 @@
import {
kairo_avoidable_owned,
kairo_avoidable_unowned
} from './benchmarks/kairo/kairo_avoidable.js';
import { kairo_broad_owned, kairo_broad_unowned } from './benchmarks/kairo/kairo_broad.js';
import { kairo_deep_owned, kairo_deep_unowned } from './benchmarks/kairo/kairo_deep.js';
import { kairo_diamond_owned, kairo_diamond_unowned } from './benchmarks/kairo/kairo_diamond.js';
import { kairo_mux_unowned, kairo_mux_owned } from './benchmarks/kairo/kairo_mux.js';
import { kairo_repeated_unowned, kairo_repeated_owned } from './benchmarks/kairo/kairo_repeated.js';
import { kairo_triangle_owned, kairo_triangle_unowned } from './benchmarks/kairo/kairo_triangle.js';
import { kairo_unstable_owned, kairo_unstable_unowned } from './benchmarks/kairo/kairo_unstable.js';
import { mol_bench_owned, mol_bench_unowned } from './benchmarks/mol_bench.js';
import {
sbench_create_0to1,
sbench_create_1000to1,
sbench_create_1to1,
sbench_create_1to1000,
sbench_create_1to2,
sbench_create_1to4,
sbench_create_1to8,
sbench_create_2to1,
sbench_create_4to1,
sbench_create_signals
} from './benchmarks/sbench.js';
// This benchmark has been adapted from the js-reactivity-benchmark (https://github.com/milomg/js-reactivity-benchmark)
// Not all tests are the same, and many parts have been tweaked to capture different data.
export const benchmarks = [
sbench_create_signals,
sbench_create_0to1,
sbench_create_1to1,
sbench_create_2to1,
sbench_create_4to1,
sbench_create_1000to1,
sbench_create_1to2,
sbench_create_1to4,
sbench_create_1to8,
sbench_create_1to1000,
kairo_avoidable_owned,
kairo_avoidable_unowned,
kairo_broad_owned,
kairo_broad_unowned,
kairo_deep_owned,
kairo_deep_unowned,
kairo_diamond_owned,
kairo_diamond_unowned,
kairo_triangle_owned,
kairo_triangle_unowned,
kairo_mux_owned,
kairo_mux_unowned,
kairo_repeated_owned,
kairo_repeated_unowned,
kairo_unstable_owned,
kairo_unstable_unowned,
mol_bench_owned,
mol_bench_unowned
];

@ -34,7 +34,7 @@ function setup() {
};
}
export async function kairo_avoidable() {
export async function kairo_avoidable_unowned() {
// Do 10 loops to warm up JIT
for (let i = 0; i < 10; i++) {
const { run, destroy } = setup();
@ -53,7 +53,38 @@ export async function kairo_avoidable() {
destroy();
return {
benchmark: 'kairo_avoidable',
benchmark: 'kairo_avoidable_unowned',
time: timing.time.toFixed(2),
gc_time: timing.gc_time.toFixed(2)
};
}
export async function kairo_avoidable_owned() {
let run, destroy;
const destroy_owned = $.effect_root(() => {
// Do 10 loops to warm up JIT
for (let i = 0; i < 10; i++) {
const { run, destroy } = setup();
run();
destroy();
}
({ run, destroy } = setup());
});
const { timing } = await fastest_test(10, () => {
for (let i = 0; i < 100; i++) {
run();
}
});
// @ts-ignore
destroy();
destroy_owned();
return {
benchmark: 'kairo_avoidable_owned',
time: timing.time.toFixed(2),
gc_time: timing.gc_time.toFixed(2)
};

@ -28,7 +28,7 @@ function setup() {
$.flush_sync(() => {
$.set(head, 1);
});
counter = 0
counter = 0;
for (let i = 0; i < 50; i++) {
$.flush_sync(() => {
$.set(head, i);
@ -40,7 +40,7 @@ function setup() {
};
}
export async function kairo_broad() {
export async function kairo_broad_unowned() {
// Do 10 loops to warm up JIT
for (let i = 0; i < 10; i++) {
const { run, destroy } = setup();
@ -59,7 +59,38 @@ export async function kairo_broad() {
destroy();
return {
benchmark: 'kairo_broad',
benchmark: 'kairo_broad_unowned',
time: timing.time.toFixed(2),
gc_time: timing.gc_time.toFixed(2)
};
}
export async function kairo_broad_owned() {
let run, destroy;
const destroy_owned = $.effect_root(() => {
// Do 10 loops to warm up JIT
for (let i = 0; i < 10; i++) {
const { run, destroy } = setup();
run();
destroy();
}
({ run, destroy } = setup());
});
const { timing } = await fastest_test(10, () => {
for (let i = 0; i < 100; i++) {
run();
}
});
// @ts-ignore
destroy();
destroy_owned();
return {
benchmark: 'kairo_broad_owned',
time: timing.time.toFixed(2),
gc_time: timing.gc_time.toFixed(2)
};

@ -28,7 +28,7 @@ function setup() {
$.flush_sync(() => {
$.set(head, 1);
});
counter = 0
counter = 0;
for (let i = 0; i < iter; i++) {
$.flush_sync(() => {
$.set(head, i);
@ -40,7 +40,7 @@ function setup() {
};
}
export async function kairo_deep() {
export async function kairo_deep_unowned() {
// Do 10 loops to warm up JIT
for (let i = 0; i < 10; i++) {
const { run, destroy } = setup();
@ -59,7 +59,38 @@ export async function kairo_deep() {
destroy();
return {
benchmark: 'kairo_deep',
benchmark: 'kairo_deep_unowned',
time: timing.time.toFixed(2),
gc_time: timing.gc_time.toFixed(2)
};
}
export async function kairo_deep_owned() {
let run, destroy;
const destroy_owned = $.effect_root(() => {
// Do 10 loops to warm up JIT
for (let i = 0; i < 10; i++) {
const { run, destroy } = setup();
run();
destroy();
}
({ run, destroy } = setup());
});
const { timing } = await fastest_test(10, () => {
for (let i = 0; i < 100; i++) {
run();
}
});
// @ts-ignore
destroy();
destroy_owned();
return {
benchmark: 'kairo_deep_owned',
time: timing.time.toFixed(2),
gc_time: timing.gc_time.toFixed(2)
};

@ -44,7 +44,7 @@ function setup() {
};
}
export async function kairo_diamond() {
export async function kairo_diamond_unowned() {
// Do 10 loops to warm up JIT
for (let i = 0; i < 10; i++) {
const { run, destroy } = setup();
@ -63,7 +63,38 @@ export async function kairo_diamond() {
destroy();
return {
benchmark: 'kairo_diamond',
benchmark: 'kairo_diamond_unowned',
time: timing.time.toFixed(2),
gc_time: timing.gc_time.toFixed(2)
};
}
export async function kairo_diamond_owned() {
let run, destroy;
const destroy_owned = $.effect_root(() => {
// Do 10 loops to warm up JIT
for (let i = 0; i < 10; i++) {
const { run, destroy } = setup();
run();
destroy();
}
({ run, destroy } = setup());
});
const { timing } = await fastest_test(10, () => {
for (let i = 0; i < 100; i++) {
run();
}
});
// @ts-ignore
destroy();
destroy_owned();
return {
benchmark: 'kairo_diamond_owned',
time: timing.time.toFixed(2),
gc_time: timing.gc_time.toFixed(2)
};

@ -37,7 +37,7 @@ function setup() {
};
}
export async function kairo_mux() {
export async function kairo_mux_unowned() {
// Do 10 loops to warm up JIT
for (let i = 0; i < 10; i++) {
const { run, destroy } = setup();
@ -56,7 +56,38 @@ export async function kairo_mux() {
destroy();
return {
benchmark: 'kairo_mux',
benchmark: 'kairo_mux_unowned',
time: timing.time.toFixed(2),
gc_time: timing.gc_time.toFixed(2)
};
}
export async function kairo_mux_owned() {
let run, destroy;
const destroy_owned = $.effect_root(() => {
// Do 10 loops to warm up JIT
for (let i = 0; i < 10; i++) {
const { run, destroy } = setup();
run();
destroy();
}
({ run, destroy } = setup());
});
const { timing } = await fastest_test(10, () => {
for (let i = 0; i < 100; i++) {
run();
}
});
// @ts-ignore
destroy();
destroy_owned();
return {
benchmark: 'kairo_mux_owned',
time: timing.time.toFixed(2),
gc_time: timing.gc_time.toFixed(2)
};

@ -41,7 +41,7 @@ function setup() {
};
}
export async function kairo_repeated() {
export async function kairo_repeated_unowned() {
// Do 10 loops to warm up JIT
for (let i = 0; i < 10; i++) {
const { run, destroy } = setup();
@ -60,7 +60,38 @@ export async function kairo_repeated() {
destroy();
return {
benchmark: 'kairo_repeated',
benchmark: 'kairo_repeated_unowned',
time: timing.time.toFixed(2),
gc_time: timing.gc_time.toFixed(2)
};
}
export async function kairo_repeated_owned() {
let run, destroy;
const destroy_owned = $.effect_root(() => {
// Do 10 loops to warm up JIT
for (let i = 0; i < 10; i++) {
const { run, destroy } = setup();
run();
destroy();
}
({ run, destroy } = setup());
});
const { timing } = await fastest_test(10, () => {
for (let i = 0; i < 100; i++) {
run();
}
});
// @ts-ignore
destroy();
destroy_owned();
return {
benchmark: 'kairo_repeated_owned',
time: timing.time.toFixed(2),
gc_time: timing.gc_time.toFixed(2)
};

@ -54,7 +54,7 @@ function setup() {
};
}
export async function kairo_triangle() {
export async function kairo_triangle_unowned() {
// Do 10 loops to warm up JIT
for (let i = 0; i < 10; i++) {
const { run, destroy } = setup();
@ -73,7 +73,38 @@ export async function kairo_triangle() {
destroy();
return {
benchmark: 'kairo_triangle',
benchmark: 'kairo_triangle_unowned',
time: timing.time.toFixed(2),
gc_time: timing.gc_time.toFixed(2)
};
}
export async function kairo_triangle_owned() {
let run, destroy;
const destroy_owned = $.effect_root(() => {
// Do 10 loops to warm up JIT
for (let i = 0; i < 10; i++) {
const { run, destroy } = setup();
run();
destroy();
}
({ run, destroy } = setup());
});
const { timing } = await fastest_test(10, () => {
for (let i = 0; i < 100; i++) {
run();
}
});
// @ts-ignore
destroy();
destroy_owned();
return {
benchmark: 'kairo_triangle_owned',
time: timing.time.toFixed(2),
gc_time: timing.gc_time.toFixed(2)
};

@ -40,7 +40,7 @@ function setup() {
};
}
export async function kairo_unstable() {
export async function kairo_unstable_unowned() {
// Do 10 loops to warm up JIT
for (let i = 0; i < 10; i++) {
const { run, destroy } = setup();
@ -59,7 +59,38 @@ export async function kairo_unstable() {
destroy();
return {
benchmark: 'kairo_unstable',
benchmark: 'kairo_unstable_unowned',
time: timing.time.toFixed(2),
gc_time: timing.gc_time.toFixed(2)
};
}
export async function kairo_unstable_owned() {
let run, destroy;
const destroy_owned = $.effect_root(() => {
// Do 10 loops to warm up JIT
for (let i = 0; i < 10; i++) {
const { run, destroy } = setup();
run();
destroy();
}
({ run, destroy } = setup());
});
const { timing } = await fastest_test(10, () => {
for (let i = 0; i < 100; i++) {
run();
}
});
// @ts-ignore
destroy();
destroy_owned();
return {
benchmark: 'kairo_unstable_owned',
time: timing.time.toFixed(2),
gc_time: timing.gc_time.toFixed(2)
};

@ -64,7 +64,38 @@ function setup() {
};
}
export async function mol_bench() {
export async function mol_bench_owned() {
let run, destroy;
const destroy_owned = $.effect_root(() => {
// Do 10 loops to warm up JIT
for (let i = 0; i < 10; i++) {
const { run, destroy } = setup();
run(0);
destroy();
}
({ run, destroy } = setup());
});
const { timing } = await fastest_test(10, () => {
for (let i = 0; i < 1e4; i++) {
run(i);
}
});
// @ts-ignore
destroy();
destroy_owned();
return {
benchmark: 'mol_bench_owned',
time: timing.time.toFixed(2),
gc_time: timing.gc_time.toFixed(2)
};
}
export async function mol_bench_unowned() {
// Do 10 loops to warm up JIT
for (let i = 0; i < 10; i++) {
const { run, destroy } = setup();
@ -83,7 +114,7 @@ export async function mol_bench() {
destroy();
return {
benchmark: 'mol_bench',
benchmark: 'mol_bench_unowned',
time: timing.time.toFixed(2),
gc_time: timing.gc_time.toFixed(2)
};

@ -0,0 +1,339 @@
import { fastest_test } from '../utils.js';
import * as $ from '../../packages/svelte/src/internal/client/index.js';
const COUNT = 1e5;
/**
* @param {number} n
* @param {any[]} sources
*/
function create_data_signals(n, sources) {
for (let i = 0; i < n; i++) {
sources[i] = $.source(i);
}
return sources;
}
/**
* @param {number} i
*/
function create_computation_0(i) {
$.derived(() => i);
}
/**
* @param {any} s1
*/
function create_computation_1(s1) {
$.derived(() => $.get(s1));
}
/**
* @param {any} s1
* @param {any} s2
*/
function create_computation_2(s1, s2) {
$.derived(() => $.get(s1) + $.get(s2));
}
function create_computation_1000(ss, offset) {
$.derived(() => {
let sum = 0;
for (let i = 0; i < 1000; i++) {
sum += $.get(ss[offset + i]);
}
return sum;
});
}
/**
* @param {number} n
*/
function create_computations_0to1(n) {
for (let i = 0; i < n; i++) {
create_computation_0(i);
}
}
/**
* @param {number} n
* @param {any[]} sources
*/
function create_computations_1to1(n, sources) {
for (let i = 0; i < n; i++) {
const source = sources[i];
create_computation_1(source);
}
}
/**
* @param {number} n
* @param {any[]} sources
*/
function create_computations_2to1(n, sources) {
for (let i = 0; i < n; i++) {
create_computation_2(sources[i * 2], sources[i * 2 + 1]);
}
}
function create_computation_4(s1, s2, s3, s4) {
$.derived(() => $.get(s1) + $.get(s2) + $.get(s3) + $.get(s4));
}
function create_computations_1000to1(n, sources) {
for (let i = 0; i < n; i++) {
create_computation_1000(sources, i * 1000);
}
}
function create_computations_1to2(n, sources) {
for (let i = 0; i < n / 2; i++) {
const source = sources[i];
create_computation_1(source);
create_computation_1(source);
}
}
function create_computations_1to4(n, sources) {
for (let i = 0; i < n / 4; i++) {
const source = sources[i];
create_computation_1(source);
create_computation_1(source);
create_computation_1(source);
create_computation_1(source);
}
}
function create_computations_1to8(n, sources) {
for (let i = 0; i < n / 8; i++) {
const source = sources[i];
create_computation_1(source);
create_computation_1(source);
create_computation_1(source);
create_computation_1(source);
create_computation_1(source);
create_computation_1(source);
create_computation_1(source);
create_computation_1(source);
}
}
function create_computations_1to1000(n, sources) {
for (let i = 0; i < n / 1000; i++) {
const source = sources[i];
for (let j = 0; j < 1000; j++) {
create_computation_1(source);
}
}
}
function create_computations_4to1(n, sources) {
for (let i = 0; i < n; i++) {
create_computation_4(
sources[i * 4],
sources[i * 4 + 1],
sources[i * 4 + 2],
sources[i * 4 + 3]
);
}
}
/**
* @param {any} fn
* @param {number} count
* @param {number} scount
*/
function bench(fn, count, scount) {
let sources = create_data_signals(scount, []);
fn(count, sources);
}
export async function sbench_create_signals() {
// Do 3 loops to warm up JIT
for (let i = 0; i < 3; i++) {
bench(create_data_signals, COUNT, COUNT);
}
const { timing } = await fastest_test(10, () => {
for (let i = 0; i < 100; i++) {
bench(create_data_signals, COUNT, COUNT);
}
});
return {
benchmark: 'sbench_create_signals',
time: timing.time.toFixed(2),
gc_time: timing.gc_time.toFixed(2)
};
}
export async function sbench_create_0to1() {
// Do 3 loops to warm up JIT
for (let i = 0; i < 3; i++) {
bench(create_computations_0to1, COUNT, 0);
}
const { timing } = await fastest_test(10, () => {
for (let i = 0; i < 100; i++) {
bench(create_computations_0to1, COUNT, 0);
}
});
return {
benchmark: 'sbench_create_0to1',
time: timing.time.toFixed(2),
gc_time: timing.gc_time.toFixed(2)
};
}
export async function sbench_create_1to1() {
// Do 3 loops to warm up JIT
for (let i = 0; i < 3; i++) {
bench(create_computations_1to1, COUNT, COUNT);
}
const { timing } = await fastest_test(10, () => {
for (let i = 0; i < 100; i++) {
bench(create_computations_1to1, COUNT, COUNT);
}
});
return {
benchmark: 'sbench_create_1to1',
time: timing.time.toFixed(2),
gc_time: timing.gc_time.toFixed(2)
};
}
export async function sbench_create_2to1() {
// Do 3 loops to warm up JIT
for (let i = 0; i < 3; i++) {
bench(create_computations_2to1, COUNT / 2, COUNT);
}
const { timing } = await fastest_test(10, () => {
for (let i = 0; i < 100; i++) {
bench(create_computations_2to1, COUNT / 2, COUNT);
}
});
return {
benchmark: 'sbench_create_2to1',
time: timing.time.toFixed(2),
gc_time: timing.gc_time.toFixed(2)
};
}
export async function sbench_create_4to1() {
// Do 3 loops to warm up JIT
for (let i = 0; i < 3; i++) {
bench(create_computations_4to1, COUNT / 4, COUNT);
}
const { timing } = await fastest_test(10, () => {
for (let i = 0; i < 100; i++) {
bench(create_computations_4to1, COUNT / 4, COUNT);
}
});
return {
benchmark: 'sbench_create_4to1',
time: timing.time.toFixed(2),
gc_time: timing.gc_time.toFixed(2)
};
}
export async function sbench_create_1000to1() {
// Do 3 loops to warm up JIT
for (let i = 0; i < 3; i++) {
bench(create_computations_1000to1, COUNT / 1000, COUNT);
}
const { timing } = await fastest_test(10, () => {
for (let i = 0; i < 100; i++) {
bench(create_computations_1000to1, COUNT / 1000, COUNT);
}
});
return {
benchmark: 'sbench_create_1000to1',
time: timing.time.toFixed(2),
gc_time: timing.gc_time.toFixed(2)
};
}
export async function sbench_create_1to2() {
// Do 3 loops to warm up JIT
for (let i = 0; i < 3; i++) {
bench(create_computations_1to2, COUNT, COUNT / 2);
}
const { timing } = await fastest_test(10, () => {
for (let i = 0; i < 100; i++) {
bench(create_computations_1to2, COUNT, COUNT / 2);
}
});
return {
benchmark: 'sbench_create_1to2',
time: timing.time.toFixed(2),
gc_time: timing.gc_time.toFixed(2)
};
}
export async function sbench_create_1to4() {
// Do 3 loops to warm up JIT
for (let i = 0; i < 3; i++) {
bench(create_computations_1to4, COUNT, COUNT / 4);
}
const { timing } = await fastest_test(10, () => {
for (let i = 0; i < 100; i++) {
bench(create_computations_1to4, COUNT, COUNT / 4);
}
});
return {
benchmark: 'sbench_create_1to4',
time: timing.time.toFixed(2),
gc_time: timing.gc_time.toFixed(2)
};
}
export async function sbench_create_1to8() {
// Do 3 loops to warm up JIT
for (let i = 0; i < 3; i++) {
bench(create_computations_1to8, COUNT, COUNT / 8);
}
const { timing } = await fastest_test(10, () => {
for (let i = 0; i < 100; i++) {
bench(create_computations_1to8, COUNT, COUNT / 8);
}
});
return {
benchmark: 'sbench_create_1to8',
time: timing.time.toFixed(2),
gc_time: timing.gc_time.toFixed(2)
};
}
export async function sbench_create_1to1000() {
// Do 3 loops to warm up JIT
for (let i = 0; i < 3; i++) {
bench(create_computations_1to1000, COUNT, COUNT / 1000);
}
const { timing } = await fastest_test(10, () => {
for (let i = 0; i < 100; i++) {
bench(create_computations_1to1000, COUNT, COUNT / 1000);
}
});
return {
benchmark: 'sbench_create_1to1000',
time: timing.time.toFixed(2),
gc_time: timing.gc_time.toFixed(2)
};
}

@ -0,0 +1,90 @@
import fs from 'node:fs';
import path from 'node:path';
import { execSync, fork } from 'node:child_process';
import { fileURLToPath } from 'node:url';
import { benchmarks } from '../benchmarks.js';
// if (execSync('git status --porcelain').toString().trim()) {
// console.error('Working directory is not clean');
// process.exit(1);
// }
const filename = fileURLToPath(import.meta.url);
const runner = path.resolve(filename, '../runner.js');
const outdir = path.resolve(filename, '../.results');
if (fs.existsSync(outdir)) fs.rmSync(outdir, { recursive: true });
fs.mkdirSync(outdir);
const branches = [];
for (const arg of process.argv.slice(2)) {
if (arg.startsWith('--')) continue;
if (arg === filename) continue;
branches.push(arg);
}
if (branches.length === 0) {
branches.push(
execSync('git symbolic-ref --short -q HEAD || git rev-parse --short HEAD').toString().trim()
);
}
if (branches.length === 1) {
branches.push('main');
}
process.on('exit', () => {
execSync(`git checkout ${branches[0]}`);
});
for (const branch of branches) {
console.group(`Benchmarking ${branch}`);
execSync(`git checkout ${branch}`);
await new Promise((fulfil, reject) => {
const child = fork(runner);
child.on('message', (results) => {
fs.writeFileSync(`${outdir}/${branch}.json`, JSON.stringify(results, null, ' '));
fulfil();
});
child.on('error', reject);
});
console.groupEnd();
}
const results = branches.map((branch) => {
return JSON.parse(fs.readFileSync(`${outdir}/${branch}.json`, 'utf-8'));
});
for (let i = 0; i < results[0].length; i += 1) {
console.group(`${results[0][i].benchmark}`);
for (const metric of ['time', 'gc_time']) {
const times = results.map((result) => +result[i][metric]);
let min = Infinity;
let min_index = -1;
for (let b = 0; b < times.length; b += 1) {
if (times[b] < min) {
min = times[b];
min_index = b;
}
}
if (min !== 0) {
console.group(`${metric}: fastest is ${branches[min_index]}`);
times.forEach((time, b) => {
console.log(`${branches[b]}: ${time.toFixed(2)}ms (${((time / min) * 100).toFixed(2)}%)`);
});
console.groupEnd();
}
}
console.groupEnd();
}

@ -0,0 +1,10 @@
import { benchmarks } from '../benchmarks.js';
const results = [];
for (const benchmark of benchmarks) {
const result = await benchmark();
console.error(result.benchmark);
results.push(result);
}
process.send(results);

@ -1,48 +1,32 @@
import * as $ from '../packages/svelte/src/internal/client/index.js';
import { kairo_avoidable } from './benchmarks/kairo/kairo_avoidable.js';
import { kairo_broad } from './benchmarks/kairo/kairo_broad.js';
import { kairo_deep } from './benchmarks/kairo/kairo_deep.js';
import { kairo_diamond } from './benchmarks/kairo/kairo_diamond.js';
import { kairo_mux } from './benchmarks/kairo/kairo_mux.js';
import { kairo_repeated } from './benchmarks/kairo/kairo_repeated.js';
import { kairo_triangle } from './benchmarks/kairo/kairo_triangle.js';
import { kairo_unstable } from './benchmarks/kairo/kairo_unstable.js';
import { mol_bench } from './benchmarks/mol_bench.js';
import { benchmarks } from './benchmarks.js';
// This benchmark has been adapted from the js-reactivity-benchmark (https://github.com/milomg/js-reactivity-benchmark)
// Not all tests are the same, and many parts have been tweaked to capture different data.
let total_time = 0;
let total_gc_time = 0;
const benchmarks = [
kairo_avoidable,
kairo_broad,
kairo_deep,
kairo_diamond,
kairo_triangle,
kairo_mux,
kairo_repeated,
kairo_unstable,
mol_bench
];
async function run_benchmarks() {
// eslint-disable-next-line no-console
console.log('-- Benchmarking Started --');
$.push({}, true);
try {
// eslint-disable-next-line no-console
console.log('-- Benchmarking Started --');
$.push({}, true);
try {
for (const benchmark of benchmarks) {
const results = await benchmark();
// eslint-disable-next-line no-console
console.log(await benchmark());
console.log(results);
total_time += Number(results.time);
total_gc_time += Number(results.gc_time);
}
} catch (e) {
} catch (e) {
// eslint-disable-next-line no-console
console.error('-- Benchmarking Failed --');
// eslint-disable-next-line no-console
console.error(e);
process.exit(1);
}
$.pop();
// eslint-disable-next-line no-console
console.log('-- Benchmarking Complete --');
}
run_benchmarks();
$.pop();
// eslint-disable-next-line no-console
console.log('-- Benchmarking Complete --');
// eslint-disable-next-line no-console
console.log({
total_time: total_time.toFixed(2),
total_gc_time: total_gc_time.toFixed(2)
});

@ -25,23 +25,22 @@
"changeset:version": "changeset version && pnpm -r generate:version && git add --all",
"changeset:publish": "changeset publish",
"bench": "node --allow-natives-syntax ./benchmarking/run.js",
"bench:compare": "node --allow-natives-syntax ./benchmarking/compare/index.js",
"bench:debug": "node --allow-natives-syntax --inspect-brk ./benchmarking/run.js"
},
"devDependencies": {
"@changesets/cli": "^2.27.1",
"@changesets/cli": "^2.27.6",
"@sveltejs/eslint-config": "^7.0.1",
"@svitejs/changesets-changelog-github-compact": "^1.1.0",
"@types/node": "^20.11.5",
"@vitest/coverage-v8": "^1.2.1",
"concurrently": "^8.2.2",
"cross-env": "^7.0.3",
"eslint": "^9.0.0",
"eslint-plugin-lube": "^0.4.3",
"jsdom": "22.0.0",
"playwright": "^1.41.1",
"prettier": "^3.2.4",
"prettier-plugin-svelte": "^3.1.2",
"typescript": "^5.3.3",
"typescript": "^5.5.2",
"typescript-eslint": "^8.0.0-alpha.20",
"v8-natives": "^1.2.5",
"vitest": "^1.2.1"

@ -1,5 +1,47 @@
# svelte
## 5.0.0-next.165
### Patch Changes
- breaking: bump dts-buddy ([#12134](https://github.com/sveltejs/svelte/pull/12134))
- fix: throw compilation error for malformed snippets ([#12144](https://github.com/sveltejs/svelte/pull/12144))
## 5.0.0-next.164
### Patch Changes
- fix: prevent `a11y_label_has_associated_control` false positive for component or render tag in `<label>` ([#12119](https://github.com/sveltejs/svelte/pull/12119))
- fix: allow multiple optional parameters with defaults in snippets ([#12070](https://github.com/sveltejs/svelte/pull/12070))
## 5.0.0-next.163
### Patch Changes
- feat: more accurate `render`/`mount`/`hydrate` options ([#12111](https://github.com/sveltejs/svelte/pull/12111))
- fix: better binding interop between runes/non-runes components ([#12123](https://github.com/sveltejs/svelte/pull/12123))
## 5.0.0-next.162
### Patch Changes
- chore: remove anchor node from each block items ([#11836](https://github.com/sveltejs/svelte/pull/11836))
## 5.0.0-next.161
### Patch Changes
- fix: wait a microtask for await blocks to reduce UI churn ([#11989](https://github.com/sveltejs/svelte/pull/11989))
- fix: ensure state update expressions are serialised correctly ([#12109](https://github.com/sveltejs/svelte/pull/12109))
- fix: repair each block length even without an else ([#12098](https://github.com/sveltejs/svelte/pull/12098))
- fix: remove document event listeners on unmount ([#12105](https://github.com/sveltejs/svelte/pull/12105))
## 5.0.0-next.160
### Patch Changes

@ -2,7 +2,7 @@
"name": "svelte",
"description": "Cybernetically enhanced web apps",
"license": "MIT",
"version": "5.0.0-next.160",
"version": "5.0.0-next.165",
"type": "module",
"types": "./types/index.d.ts",
"engines": {
@ -112,7 +112,7 @@
"prepublishOnly": "pnpm build",
"format": "prettier --check --write .",
"lint": "prettier --check . && eslint",
"knip": "knip"
"knip": "pnpm dlx knip"
},
"devDependencies": {
"@jridgewell/trace-mapping": "^0.3.22",
@ -122,9 +122,8 @@
"@rollup/plugin-terser": "^0.4.4",
"@rollup/plugin-virtual": "^3.0.2",
"@types/aria-query": "^5.0.4",
"dts-buddy": "^0.4.7",
"dts-buddy": "^0.5.0",
"esbuild": "^0.19.11",
"knip": "^4.2.1",
"rollup": "^4.9.5",
"source-map": "^0.7.4",
"tiny-glob": "^0.2.9"

@ -30,7 +30,7 @@ await createBundle({
[`${pkg.name}/legacy`]: `${dir}/src/legacy/legacy-client.js`,
[`${pkg.name}/motion`]: `${dir}/src/motion/public.d.ts`,
[`${pkg.name}/reactivity`]: `${dir}/src/reactivity/index-client.js`,
[`${pkg.name}/server`]: `${dir}/src/server/index.js`,
[`${pkg.name}/server`]: `${dir}/src/server/index.d.ts`,
[`${pkg.name}/store`]: `${dir}/src/store/public.d.ts`,
[`${pkg.name}/transition`]: `${dir}/src/transition/public.d.ts`,
[`${pkg.name}/events`]: `${dir}/src/events/index.js`,

@ -1,6 +1,7 @@
import * as acorn from 'acorn';
import { walk } from 'zimmerframe';
import { tsPlugin } from 'acorn-typescript';
import { locator } from '../../state.js';
const ParserWithTS = acorn.Parser.extend(tsPlugin({ allowSatisfies: true }));
@ -127,6 +128,16 @@ function amend(source, node) {
// @ts-expect-error
delete node.loc.end.index;
if (typeof node.loc?.end === 'number') {
const loc = locator(node.loc.end);
if (loc) {
node.loc.end = {
line: loc.line,
column: loc.column
};
}
}
if (/** @type {any} */ (node).typeAnnotation && node.end === undefined) {
// i think there might be a bug in acorn-typescript that prevents
// `end` from being assigned when there's a type annotation

@ -14,17 +14,16 @@ import { locator } from '../../../state.js';
/**
* @param {import('../index.js').Parser} parser
* @param {boolean} [optional_allowed]
* @returns {import('estree').Pattern}
*/
export default function read_pattern(parser, optional_allowed = false) {
export default function read_pattern(parser) {
const start = parser.index;
let i = parser.index;
const code = full_char_code_at(parser.template, i);
if (isIdentifierStart(code, true)) {
const name = /** @type {string} */ (parser.read_identifier());
const annotation = read_type_annotation(parser, optional_allowed);
const annotation = read_type_annotation(parser);
return {
type: 'Identifier',
@ -84,7 +83,7 @@ export default function read_pattern(parser, optional_allowed = false) {
parse_expression_at(`${space_with_newline}(${pattern_string} = 1)`, parser.ts, start - 1)
).left;
expression.typeAnnotation = read_type_annotation(parser, optional_allowed);
expression.typeAnnotation = read_type_annotation(parser);
if (expression.typeAnnotation) {
expression.end = expression.typeAnnotation.end;
}
@ -97,19 +96,12 @@ export default function read_pattern(parser, optional_allowed = false) {
/**
* @param {import('../index.js').Parser} parser
* @param {boolean} [optional_allowed]
* @returns {any}
*/
function read_type_annotation(parser, optional_allowed = false) {
function read_type_annotation(parser) {
const start = parser.index;
parser.allow_whitespace();
if (optional_allowed && parser.eat('?')) {
// Acorn-TS puts the optional info as a property on the surrounding node.
// We spare the work here because it doesn't matter for us anywhere else.
parser.allow_whitespace();
}
if (!parser.eat(':')) {
parser.index = start;
return undefined;

@ -3,6 +3,7 @@ import read_expression from '../read/expression.js';
import * as e from '../../../errors.js';
import { create_fragment } from '../utils/create.js';
import { walk } from 'zimmerframe';
import { parse_expression_at } from '../acorn.js';
const regex_whitespace_with_closing_curly_brace = /^\s*}/;
@ -268,36 +269,27 @@ function open(parser) {
e.expected_identifier(parser.index);
}
parser.eat('(', true);
parser.allow_whitespace();
/** @type {import('estree').Pattern[]} */
const parameters = [];
const params_start = parser.index;
while (!parser.match(')')) {
let pattern = read_pattern(parser, true);
parser.eat('(', true);
let parentheses = 1;
parser.allow_whitespace();
if (parser.eat('=')) {
parser.allow_whitespace();
const right = read_expression(parser);
pattern = {
type: 'AssignmentPattern',
left: pattern,
right: right,
start: pattern.start,
end: right.end
};
while (parser.index < parser.template.length && (!parser.match(')') || parentheses !== 1)) {
if (parser.match('(')) parentheses++;
if (parser.match(')')) parentheses--;
parser.index += 1;
}
parameters.push(pattern);
parser.eat(')', true);
if (!parser.eat(',')) break;
parser.allow_whitespace();
}
const prelude = parser.template.slice(0, params_start).replace(/\S/g, ' ');
const params = parser.template.slice(params_start, parser.index);
parser.eat(')', true);
let function_expression = /** @type {import('estree').ArrowFunctionExpression} */ (
parse_expression_at(prelude + `${params} => {}`, parser.ts, params_start)
);
parser.allow_whitespace();
parser.eat('}', true);
@ -313,10 +305,9 @@ function open(parser) {
end: name_end,
name
},
parameters,
parameters: function_expression.params,
body: create_fragment()
});
parser.stack.push(block);
parser.fragments.push(block.body);

@ -1086,6 +1086,8 @@ function check_element(node, state) {
if (
node.type === 'SvelteElement' ||
node.type === 'SlotElement' ||
node.type === 'Component' ||
node.type === 'RenderTag' ||
(node.type === 'RegularElement' &&
(a11y_labelable.includes(node.name) || node.name === 'slot'))
) {

@ -363,7 +363,12 @@ export function client_component(source, analysis, options) {
}
if (analysis.uses_props || analysis.uses_rest_props) {
const to_remove = [b.literal('children'), b.literal('$$slots'), b.literal('$$events')];
const to_remove = [
b.literal('children'),
b.literal('$$slots'),
b.literal('$$events'),
b.literal('$$legacy')
];
if (analysis.custom_element) {
to_remove.push(b.literal('$$host'));
}

@ -1,5 +1,6 @@
import * as b from '../../../utils/builders.js';
import {
extract_identifiers,
extract_paths,
is_expression_async,
is_simple_expression,
@ -88,7 +89,7 @@ export function serialize_get_binding(node, state) {
}
if (binding.kind === 'prop' || binding.kind === 'bindable_prop') {
if (!state.analysis.runes || binding.reassigned || binding.initial) {
if (is_prop_source(binding, state)) {
return b.call(node);
}
@ -119,10 +120,11 @@ export function serialize_get_binding(node, state) {
* @param {import('estree').AssignmentExpression} node
* @param {import('zimmerframe').Context<import('#compiler').SvelteNode, State>} context
* @param {() => any} fallback
* @param {boolean} prefix
* @param {{skip_proxy_and_freeze?: boolean}} [options]
* @returns {import('estree').Expression}
*/
export function serialize_set_binding(node, context, fallback, options) {
export function serialize_set_binding(node, context, fallback, prefix, options) {
const { state, visit } = context;
const assignee = node.left;
@ -146,7 +148,9 @@ export function serialize_set_binding(node, context, fallback, options) {
const value = path.expression?.(b.id(tmp_id));
const assignment = b.assignment('=', path.node, value);
original_assignments.push(assignment);
assignments.push(serialize_set_binding(assignment, context, () => assignment, options));
assignments.push(
serialize_set_binding(assignment, context, () => assignment, prefix, options)
);
}
if (assignments.every((assignment, i) => assignment === original_assignments[i])) {
@ -387,18 +391,20 @@ export function serialize_set_binding(node, context, fallback, options) {
),
b.call('$.untrack', b.id('$' + left_name))
);
} else if (!state.analysis.runes) {
} else if (
!state.analysis.runes ||
// this condition can go away once legacy mode is gone; only necessary for interop with legacy parent bindings
(binding.mutated && binding.kind === 'bindable_prop')
) {
if (binding.kind === 'bindable_prop') {
return b.call(
left,
b.sequence([
b.assignment(
node.operator,
/** @type {import('estree').Pattern} */ (visit(node.left)),
value
),
b.call(left)
])
b.true
);
} else {
return b.call(
@ -411,6 +417,15 @@ export function serialize_set_binding(node, context, fallback, options) {
)
);
}
} else if (
node.right.type === 'Literal' &&
(node.operator === '+=' || node.operator === '-=')
) {
return b.update(
node.operator === '+=' ? '++' : '--',
/** @type {import('estree').Expression} */ (visit(node.left)),
prefix
);
} else {
return b.assignment(
node.operator,
@ -525,9 +540,7 @@ function get_hoistable_params(node, context) {
} else if (
// If we are referencing a simple $$props value, then we need to reference the object property instead
(binding.kind === 'prop' || binding.kind === 'bindable_prop') &&
!binding.reassigned &&
binding.initial === null &&
!context.state.analysis.accessors
!is_prop_source(binding, context.state)
) {
push_unique(b.id('$$props'));
} else {
@ -589,7 +602,9 @@ export function get_prop_source(binding, state, name, initial) {
if (
state.analysis.accessors ||
(state.analysis.immutable ? binding.reassigned : binding.mutated)
(state.analysis.immutable
? binding.reassigned || (state.analysis.runes && binding.mutated)
: binding.mutated)
) {
flags |= PROPS_IS_UPDATED;
}
@ -624,6 +639,25 @@ export function get_prop_source(binding, state, name, initial) {
return b.call('$.prop', ...args);
}
/**
*
* @param {import('#compiler').Binding} binding
* @param {import('./types').ClientTransformState} state
* @returns
*/
export function is_prop_source(binding, state) {
return (
(binding.kind === 'prop' || binding.kind === 'bindable_prop') &&
(!state.analysis.runes ||
state.analysis.accessors ||
binding.reassigned ||
binding.initial ||
// Until legacy mode is gone, we also need to use the prop source when only mutated is true,
// because the parent could be a legacy component which needs coarse-grained reactivity
binding.mutated)
);
}
/**
* @param {import('estree').Expression} node
* @param {import("../../scope.js").Scope | null} scope
@ -672,3 +706,44 @@ export function with_loc(target, source) {
}
return target;
}
/**
* @param {import("estree").Pattern} node
* @param {import("zimmerframe").Context<import("#compiler").SvelteNode, import("./types").ComponentClientTransformState>} context
* @returns {{ id: import("estree").Pattern, declarations: null | import("estree").Statement[] }}
*/
export function create_derived_block_argument(node, context) {
if (node.type === 'Identifier') {
return { id: node, declarations: null };
}
const pattern = /** @type {import('estree').Pattern} */ (context.visit(node));
const identifiers = extract_identifiers(node);
const id = b.id('$$source');
const value = b.id('$$value');
const block = b.block([
b.var(pattern, b.call('$.get', id)),
b.return(b.object(identifiers.map((identifier) => b.prop('init', identifier, identifier))))
]);
const declarations = [b.var(value, create_derived(context.state, b.thunk(block)))];
for (const id of identifiers) {
declarations.push(
b.var(id, create_derived(context.state, b.thunk(b.member(b.call('$.get', value), id))))
);
}
return { id, declarations };
}
/**
* Svelte legacy mode should use safe equals in most places, runes mode shouldn't
* @param {import('./types.js').ComponentClientTransformState} state
* @param {import('estree').Expression} arg
*/
export function create_derived(state, arg) {
return b.call(state.analysis.runes ? '$.derived' : '$.derived_safe_equal', arg);
}

@ -32,7 +32,7 @@ export const global_visitors = {
next();
},
AssignmentExpression(node, context) {
return serialize_set_binding(node, context, context.next);
return serialize_set_binding(node, context, context.next, false);
},
UpdateExpression(node, context) {
const { state, next, visit } = context;
@ -98,7 +98,12 @@ export const global_visitors = {
/** @type {import('estree').Pattern} */ (argument),
b.literal(1)
);
const serialized_assignment = serialize_set_binding(assignment, context, () => assignment);
const serialized_assignment = serialize_set_binding(
assignment,
context,
() => assignment,
node.prefix
);
const value = /** @type {import('estree').Expression} */ (visit(argument));
if (serialized_assignment === assignment) {
// No change to output -> nothing to transform -> we can keep the original update expression

@ -4,6 +4,7 @@ import * as b from '../../../../utils/builders.js';
import * as assert from '../../../../utils/assert.js';
import {
get_prop_source,
is_prop_source,
is_state_source,
serialize_proxy_reassignment,
should_proxy_or_freeze
@ -240,7 +241,11 @@ export const javascript_visitors_runes = {
assert.equal(declarator.id.type, 'ObjectPattern');
/** @type {string[]} */
const seen = [];
const seen = ['$$slots', '$$events', '$$legacy'];
if (state.analysis.custom_element) {
seen.push('$$host');
}
for (const property of declarator.id.properties) {
if (property.type === 'Property') {
@ -264,7 +269,7 @@ export const javascript_visitors_runes = {
initial = b.call('$.proxy', initial);
}
if (binding.reassigned || state.analysis.accessors || initial) {
if (is_prop_source(binding, state)) {
declarations.push(b.declarator(id, get_prop_source(binding, state, name, initial)));
}
} else {

@ -21,7 +21,9 @@ import {
function_visitor,
get_assignment_value,
serialize_get_binding,
serialize_set_binding
serialize_set_binding,
create_derived,
create_derived_block_argument
} from '../utils.js';
import {
AttributeAliases,
@ -646,15 +648,6 @@ function collect_parent_each_blocks(context) {
);
}
/**
* Svelte legacy mode should use safe equals in most places, runes mode shouldn't
* @param {import('../types.js').ComponentClientTransformState} state
* @param {import('estree').Expression} arg
*/
function create_derived(state, arg) {
return b.call(state.analysis.runes ? '$.derived' : '$.derived_safe_equal', arg);
}
/**
* @param {import('#compiler').Component | import('#compiler').SvelteComponent | import('#compiler').SvelteSelf} node
* @param {string} component_name
@ -798,7 +791,9 @@ function serialize_inline_component(node, component_name, context) {
const assignment = b.assignment('=', attribute.expression, b.id('$$value'));
push_prop(
b.set(attribute.name, [
b.stmt(serialize_set_binding(assignment, context, () => context.visit(assignment)))
b.stmt(
serialize_set_binding(assignment, context, () => context.visit(assignment), false)
)
])
);
}
@ -905,6 +900,10 @@ function serialize_inline_component(node, component_name, context) {
push_prop(b.init('$$slots', b.object(serialized_slots)));
}
if (!context.state.analysis.runes) {
push_prop(b.init('$$legacy', b.true));
}
const props_expression =
props_and_spreads.length === 0 ||
(props_and_spreads.length === 1 && Array.isArray(props_and_spreads[0]))
@ -1026,7 +1025,7 @@ function serialize_bind_this(bind_this, context, node) {
const bind_this_id = /** @type {import('estree').Expression} */ (context.visit(bind_this));
const ids = Array.from(each_ids.values()).map((id) => b.id('$$value_' + id[0]));
const assignment = b.assignment('=', bind_this, b.id('$$value'));
const update = serialize_set_binding(assignment, context, () => context.visit(assignment));
const update = serialize_set_binding(assignment, context, () => context.visit(assignment), false);
for (const [binding, [, , expression]] of each_ids) {
// reset expressions to what they were before
@ -2400,7 +2399,7 @@ export const template_visitors = {
if (assignment.left.type !== 'Identifier' && assignment.left.type !== 'MemberExpression') {
// serialize_set_binding turns other patterns into IIFEs and separates the assignments
// into separate expressions, at which point this is called again with an identifier or member expression
return serialize_set_binding(assignment, context, () => assignment);
return serialize_set_binding(assignment, context, () => assignment, false);
}
const left = object(assignment.left);
const value = get_assignment_value(assignment, context);
@ -2438,7 +2437,7 @@ export const template_visitors = {
: b.id(node.index);
const item = each_node_meta.item;
const binding = /** @type {import('#compiler').Binding} */ (context.state.scope.get(item.name));
binding.expression = (id) => {
binding.expression = (/** @type {import("estree").Identifier} */ id) => {
const item_with_loc = with_loc(item, id);
return b.call('$.unwrap', item_with_loc);
};
@ -2592,6 +2591,45 @@ export const template_visitors = {
AwaitBlock(node, context) {
context.state.template.push('<!>');
let then_block;
let catch_block;
if (node.then) {
/** @type {import('estree').Pattern[]} */
const args = [b.id('$$anchor')];
const block = /** @type {import('estree').BlockStatement} */ (context.visit(node.then));
if (node.value) {
const argument = create_derived_block_argument(node.value, context);
args.push(argument.id);
if (argument.declarations !== null) {
block.body.unshift(...argument.declarations);
}
}
then_block = b.arrow(args, block);
}
if (node.catch) {
/** @type {import('estree').Pattern[]} */
const args = [b.id('$$anchor')];
const block = /** @type {import('estree').BlockStatement} */ (context.visit(node.catch));
if (node.error) {
const argument = create_derived_block_argument(node.error, context);
args.push(argument.id);
if (argument.declarations !== null) {
block.body.unshift(...argument.declarations);
}
}
catch_block = b.arrow(args, block);
}
context.state.init.push(
b.stmt(
b.call(
@ -2604,28 +2642,8 @@ export const template_visitors = {
/** @type {import('estree').BlockStatement} */ (context.visit(node.pending))
)
: b.literal(null),
node.then
? b.arrow(
node.value
? [
b.id('$$anchor'),
/** @type {import('estree').Pattern} */ (context.visit(node.value))
]
: [b.id('$$anchor')],
/** @type {import('estree').BlockStatement} */ (context.visit(node.then))
)
: b.literal(null),
node.catch
? b.arrow(
node.error
? [
b.id('$$anchor'),
/** @type {import('estree').Pattern} */ (context.visit(node.error))
]
: [b.id('$$anchor')],
/** @type {import('estree').BlockStatement} */ (context.visit(node.catch))
)
: b.literal(null)
then_block,
catch_block
)
)
);
@ -2762,6 +2780,7 @@ export const template_visitors = {
assignment,
context,
() => /** @type {import('estree').Expression} */ (visit(assignment)),
false,
{
skip_proxy_and_freeze: true
}

@ -613,7 +613,7 @@ export function create_scopes(ast, root, allow_reactive_declarations, parent) {
scopes.set(node.value, value_scope);
context.visit(node.value, { scope: value_scope });
for (const id of extract_identifiers(node.value)) {
then_scope.declare(id, 'normal', 'const');
then_scope.declare(id, 'derived', 'const');
value_scope.declare(id, 'normal', 'const');
}
}
@ -627,7 +627,7 @@ export function create_scopes(ast, root, allow_reactive_declarations, parent) {
scopes.set(node.error, error_scope);
context.visit(node.error, { scope: error_scope });
for (const id of extract_identifiers(node.error)) {
catch_scope.declare(id, 'normal', 'const');
catch_scope.declare(id, 'derived', 'const');
error_scope.declare(id, 'normal', 'const');
}
}

@ -189,14 +189,31 @@ export type ComponentEvents<Comp extends SvelteComponent> =
Comp extends SvelteComponent<any, infer Events> ? Events : never;
/**
* Convenience type to get the props the given component expects. Example:
* ```html
* <script lang="ts">
* Convenience type to get the props the given component expects.
*
* Example: Ensure a variable contains the props expected by `MyComponent`:
*
* ```ts
* import type { ComponentProps } from 'svelte';
* import Component from './Component.svelte';
* import MyComponent from './MyComponent.svelte';
*
* const props: ComponentProps<Component> = { foo: 'bar' }; // Errors if these aren't the correct props
* </script>
* // Errors if these aren't the correct props expected by MyComponent.
* const props: ComponentProps<MyComponent> = { foo: 'bar' };
* ```
*
* Example: A generic function that accepts some component and infers the type of its props:
*
* ```ts
* import type { Component, ComponentProps } from 'svelte';
* import MyComponent from './MyComponent.svelte';
*
* function withProps<TComponent extends Component<any>>(
* component: TComponent,
* props: ComponentProps<TComponent>
* ) {};
*
* // Errors if the second argument is not the correct props expected by the component in the first argument.
* withProps(MyComponent, { foo: 'bar' });
* ```
*/
export type ComponentProps<Comp extends SvelteComponent | Component<any>> =

@ -7,111 +7,135 @@ import {
set_current_reaction,
set_dev_current_component_function
} from '../../runtime.js';
import { block, branch, destroy_effect, pause_effect } from '../../reactivity/effects.js';
import { INERT } from '../../constants.js';
import { block, branch, pause_effect, resume_effect } from '../../reactivity/effects.js';
import { DEV } from 'esm-env';
import { queue_micro_task } from '../task.js';
import { hydrating } from '../hydration.js';
import { set, source } from '../../reactivity/sources.js';
const PENDING = 0;
const THEN = 1;
const CATCH = 2;
/**
* @template V
* @param {Comment} anchor
* @param {(() => Promise<V>)} get_input
* @param {null | ((anchor: Node) => void)} pending_fn
* @param {null | ((anchor: Node, value: V) => void)} then_fn
* @param {null | ((anchor: Node, value: import('#client').Source<V>) => void)} then_fn
* @param {null | ((anchor: Node, error: unknown) => void)} catch_fn
* @returns {void}
*/
export function await_block(anchor, get_input, pending_fn, then_fn, catch_fn) {
const component_context = current_component_context;
/** @type {any} */
let component_function;
if (DEV) {
component_function = component_context?.function ?? null;
}
var component_context = current_component_context;
/** @type {any} */
let input;
var component_function = DEV ? component_context?.function : null;
/** @type {V | Promise<V>} */
var input;
/** @type {import('#client').Effect | null} */
let pending_effect;
var pending_effect;
/** @type {import('#client').Effect | null} */
let then_effect;
var then_effect;
/** @type {import('#client').Effect | null} */
let catch_effect;
var catch_effect;
var input_source = source(/** @type {V} */ (undefined));
var error_source = source(undefined);
var resolved = false;
/**
* @param {(anchor: Comment, value: any) => void} fn
* @param {any} value
* @param {PENDING | THEN | CATCH} state
* @param {boolean} restore
*/
function create_effect(fn, value) {
function update(state, restore) {
resolved = true;
if (restore) {
set_current_effect(effect);
set_current_reaction(effect); // TODO do we need both?
set_current_component_context(component_context);
if (DEV) {
set_dev_current_component_function(component_function);
if (DEV) set_dev_current_component_function(component_function);
}
var e = branch(() => fn(anchor, value));
if (DEV) {
set_dev_current_component_function(null);
if (state === PENDING && pending_fn) {
if (pending_effect) resume_effect(pending_effect);
else pending_effect = branch(() => pending_fn(anchor));
}
if (state === THEN && then_fn) {
if (then_effect) resume_effect(then_effect);
else then_effect = branch(() => then_fn(anchor, input_source));
}
if (state === CATCH && catch_fn) {
if (catch_effect) resume_effect(catch_effect);
else catch_effect = branch(() => catch_fn(anchor, error_source));
}
if (state !== PENDING && pending_effect) {
pause_effect(pending_effect, () => (pending_effect = null));
}
if (state !== THEN && then_effect) {
pause_effect(then_effect, () => (then_effect = null));
}
if (state !== CATCH && catch_effect) {
pause_effect(catch_effect, () => (catch_effect = null));
}
if (restore) {
if (DEV) set_dev_current_component_function(null);
set_current_component_context(null);
set_current_reaction(null);
set_current_effect(null);
// without this, the DOM does not update until two ticks after the promise,
// resolves which is unexpected behaviour (and somewhat irksome to test)
// without this, the DOM does not update until two ticks after the promise
// resolves, which is unexpected behaviour (and somewhat irksome to test)
flush_sync();
return e;
}
}
const effect = block(() => {
var effect = block(() => {
if (input === (input = get_input())) return;
if (is_promise(input)) {
const promise = /** @type {Promise<any>} */ (input);
var promise = input;
if (pending_fn) {
if (pending_effect && (pending_effect.f & INERT) === 0) {
destroy_effect(pending_effect);
}
pending_effect = branch(() => pending_fn(anchor));
}
if (then_effect) pause_effect(then_effect);
if (catch_effect) pause_effect(catch_effect);
resolved = false;
promise.then(
(value) => {
if (promise !== input) return;
if (pending_effect) pause_effect(pending_effect);
if (then_fn) {
then_effect = create_effect(then_fn, value);
}
set(input_source, value);
update(THEN, true);
},
(error) => {
if (promise !== input) return;
if (pending_effect) pause_effect(pending_effect);
if (catch_fn) {
catch_effect = create_effect(catch_fn, error);
}
set(error_source, error);
update(CATCH, true);
}
);
} else {
if (pending_effect) pause_effect(pending_effect);
if (catch_effect) pause_effect(catch_effect);
if (then_fn) {
if (then_effect) {
destroy_effect(then_effect);
if (hydrating) {
if (pending_fn) {
pending_effect = branch(() => pending_fn(anchor));
}
then_effect = branch(() => then_fn(anchor, input));
} else {
// Wait a microtask before checking if we should show the pending state as
// the promise might have resolved by the next microtask.
queue_micro_task(() => {
if (!resolved) update(PENDING, true);
});
}
} else {
set(input_source, input);
update(THEN, false);
}
// Inert effects are proactively detached from the effect tree. Returning a noop

@ -85,7 +85,6 @@ function pause_effects(items, controlled_anchor, items_map) {
var item = items[i];
if (!is_controlled) {
items_map.delete(item.k);
item.o.remove();
link(item.prev, item.next);
}
destroy_effect(item.e, !is_controlled);
@ -151,7 +150,7 @@ export function each(anchor, flags, get_collection, get_key, render_fn, fallback
if (hydrating) {
var is_else = /** @type {Comment} */ (anchor).data === HYDRATION_END_ELSE;
if (is_else !== (length === 0)) {
if (is_else !== (length === 0) || hydrate_start === undefined) {
// hydration mismatch — remove the server-rendered DOM and start over
remove(hydrate_nodes);
set_hydrating(false);
@ -182,11 +181,10 @@ export function each(anchor, flags, get_collection, get_key, render_fn, fallback
break;
}
var child_open = /** @type {Comment} */ (child_anchor);
child_anchor = hydrate_anchor(child_anchor);
var value = array[i];
var key = get_key(value, i);
item = create_item(child_open, child_anchor, prev, null, value, key, i, render_fn, flags);
item = create_item(child_anchor, prev, null, value, key, i, render_fn, flags);
state.items.set(key, item);
child_anchor = /** @type {Comment} */ (child_anchor.nextSibling);
@ -293,22 +291,9 @@ function reconcile(array, state, anchor, render_fn, flags, get_key) {
item = items.get(key);
if (item === undefined) {
var child_open = empty();
var child_anchor = current ? current.o : anchor;
var child_anchor = current ? get_first_node(current.e) : anchor;
child_anchor.before(child_open);
prev = create_item(
child_open,
child_anchor,
prev,
prev.next,
value,
key,
i,
render_fn,
flags
);
prev = create_item(child_anchor, prev, prev.next, value, key, i, render_fn, flags);
items.set(key, prev);
@ -451,7 +436,6 @@ function update_item(item, value, index, type) {
/**
* @template V
* @param {Comment | Text} open
* @param {Node} anchor
* @param {import('#client').EachItem | import('#client').EachState} prev
* @param {import('#client').EachItem | null} next
@ -462,7 +446,7 @@ function update_item(item, value, index, type) {
* @param {number} flags
* @returns {import('#client').EachItem}
*/
function create_item(open, anchor, prev, next, value, key, index, render_fn, flags) {
function create_item(anchor, prev, next, value, key, index, render_fn, flags) {
var previous_each_item = current_each_item;
try {
@ -480,7 +464,6 @@ function create_item(open, anchor, prev, next, value, key, index, render_fn, fla
a: null,
// @ts-expect-error
e: null,
o: open,
prev,
next
};
@ -497,16 +480,52 @@ function create_item(open, anchor, prev, next, value, key, index, render_fn, fla
}
}
/**
* @param {import('#client').TemplateNode} dom
* @param {import("#client").Effect} effect
* @returns {import('#client').TemplateNode}
*/
function get_adjusted_first_node(dom, effect) {
if ((dom.nodeType === 3 && /** @type {Text} */ (dom).data === '') || dom.nodeType === 8) {
var adjusted = effect.first;
var next;
while (adjusted !== null) {
next = adjusted.first;
if (adjusted.dom !== null) {
break;
} else if (next === null) {
return /** @type {import('#client').TemplateNode} */ (dom.previousSibling);
}
adjusted = next;
}
return get_first_node(/** @type {import("#client").Effect} */ (adjusted));
}
return dom;
}
/**
*
* @param {import('#client').Effect} effect
* @returns {import('#client').TemplateNode}
*/
function get_first_node(effect) {
var dom = effect.dom;
if (is_array(dom)) {
return get_adjusted_first_node(dom[0], effect);
}
return get_adjusted_first_node(/** @type {import('#client').TemplateNode} **/ (dom), effect);
}
/**
* @param {import('#client').EachItem} item
* @param {import('#client').EachItem | null} next
* @param {Text | Element | Comment} anchor
*/
function move(item, next, anchor) {
var end = item.next ? item.next.o : anchor;
var dest = next ? next.o : anchor;
var end = item.next ? get_first_node(item.next.e) : anchor;
var dest = next ? get_first_node(next.e) : anchor;
var node = /** @type {import('#client').TemplateNode} */ (item.o);
var node = get_first_node(item.e);
while (node !== end) {
var next_node = /** @type {import('#client').TemplateNode} */ (node.nextSibling);

@ -47,7 +47,6 @@ function swap_block_dom(effect, from, to) {
* @returns {void}
*/
export function element(node, get_tag, is_svg, render_fn, get_namespace, location) {
const parent_effect = /** @type {import('#client').Effect} */ (current_effect);
const filename = DEV && location && current_component_context?.function.filename;
/** @type {string | null} */
@ -64,6 +63,18 @@ export function element(node, get_tag, is_svg, render_fn, get_namespace, locatio
/** @type {import('#client').Effect | null} */
let effect;
const parent_effect = /** @type {import('#client').Effect} */ (current_effect);
// Remove the the hydrated effect dom entry for our dynamic element
if (hydrating && is_array(parent_effect.dom)) {
var remove_index = parent_effect.dom.indexOf(
/** @type {import('#client').TemplateNode} */ (element)
);
if (remove_index !== -1) {
parent_effect.dom.splice(remove_index, 1);
}
}
/**
* The keyed `{#each ...}` item block, if any, that this element is inside.
* We track this so we can set it when changing the element, allowing any
@ -72,6 +83,7 @@ export function element(node, get_tag, is_svg, render_fn, get_namespace, locatio
let each_item_block = current_each_item;
block(() => {
const element_effect = /** @type {import('#client').Effect} */ (current_effect);
const next_tag = get_tag() || null;
const ns = get_namespace
? get_namespace()
@ -124,6 +136,13 @@ export function element(node, get_tag, is_svg, render_fn, get_namespace, locatio
};
}
if (prev_element && !hydrating) {
swap_block_dom(element_effect, prev_element, element);
prev_element.remove();
} else {
push_template_node(element, element_effect);
}
if (render_fn) {
// If hydrating, use the existing ssr comment as the anchor so that the
// inner open and close methods can pick up the existing nodes correctly
@ -144,15 +163,6 @@ export function element(node, get_tag, is_svg, render_fn, get_namespace, locatio
anchor.before(element);
if (!hydrating) {
if (prev_element) {
swap_block_dom(parent_effect, prev_element, element);
prev_element.remove();
} else {
push_template_node(element, parent_effect);
}
}
// See below
return noop;
});

@ -267,9 +267,16 @@ export function prop(props, key, flags, fallback) {
// intermediate mode — prop is written to, but the parent component had
// `bind:foo` which means we can just call `$$props.foo = value` directly
if (setter) {
return function (/** @type {V} */ value) {
if (arguments.length === 1) {
/** @type {Function} */ (setter)(value);
var legacy_parent = props.$$legacy;
return function (/** @type {any} */ value, /** @type {boolean} */ mutation) {
if (arguments.length > 0) {
// We don't want to notify if the value was mutated and the parent is in runes mode.
// In that case the state proxy (if it exists) should take care of the notification.
// If the parent is not in runes mode, we need to notify on mutation, too, that the prop
// has changed because the parent will not be able to detect the change otherwise.
if (!runes || !mutation || legacy_parent) {
/** @type {Function} */ (setter)(mutation ? getter() : value);
}
return value;
} else {
return getter();
@ -302,7 +309,7 @@ export function prop(props, key, flags, fallback) {
if (!immutable) current_value.equals = safe_equals;
return function (/** @type {V} */ value) {
return function (/** @type {any} */ value, /** @type {boolean} */ mutation) {
var current = get(current_value);
// legacy nonsense — need to ensure the source is invalidated when necessary
@ -318,9 +325,11 @@ export function prop(props, key, flags, fallback) {
}
if (arguments.length > 0) {
if (!current_value.equals(value)) {
const new_value = mutation ? get(current_value) : value;
if (!current_value.equals(new_value)) {
from_child = true;
set(inner_current_value, value);
set(inner_current_value, new_value);
get(current_value); // force a synchronisation immediately
}

@ -72,13 +72,20 @@ export function slot(anchor, slot_fn, slot_props, fallback_fn) {
* @template {Record<string, any>} Props
* @template {Record<string, any>} Exports
* @param {import('../../index.js').ComponentType<import('../../index.js').SvelteComponent<Props>> | import('../../index.js').Component<Props, Exports, any>} component
* @param {{
* @param {{} extends Props ? {
* target: Document | Element | ShadowRoot;
* anchor?: Node;
* props?: Props;
* events?: Record<string, (e: any) => any>;
* context?: Map<any, any>;
* intro?: boolean;
* }: {
* target: Document | Element | ShadowRoot;
* props: Props;
* anchor?: Node;
* events?: Record<string, (e: any) => any>;
* context?: Map<any, any>;
* intro?: boolean;
* }} options
* @returns {Exports}
*/
@ -98,13 +105,20 @@ export function mount(component, options) {
* @template {Record<string, any>} Props
* @template {Record<string, any>} Exports
* @param {import('../../index.js').ComponentType<import('../../index.js').SvelteComponent<Props>> | import('../../index.js').Component<Props, Exports, any>} component
* @param {{
* @param {{} extends Props ? {
* target: Document | Element | ShadowRoot;
* props?: Props;
* events?: Record<string, (e: any) => any>;
* context?: Map<any, any>;
* intro?: boolean;
* recover?: boolean;
* } : {
* target: Document | Element | ShadowRoot;
* props: Props;
* events?: Record<string, (e: any) => any>;
* context?: Map<any, any>;
* intro?: boolean;
* recover?: boolean;
* }} options
* @returns {Exports}
*/

@ -85,8 +85,6 @@ export type EachItem = {
i: number | Source<number>;
/** key */
k: unknown;
/** anchor for items inserted before this */
o: Comment | Text;
prev: EachItem | EachState;
next: EachItem | null;
};

@ -97,10 +97,10 @@ export let on_destroy = [];
* Takes a component and returns an object with `body` and `head` properties on it, which you can use to populate the HTML when server-rendering your app.
* @template {Record<string, any>} Props
* @param {import('svelte').Component<Props> | import('svelte').ComponentType<import('svelte').SvelteComponent<Props>>} component
* @param {{ props: Omit<Props, '$$slots' | '$$events'>; context?: Map<any, any> }} options
* @param {{ props?: Omit<Props, '$$slots' | '$$events'>; context?: Map<any, any> }} [options]
* @returns {import('#server').RenderOutput}
*/
export function render(component, options) {
export function render(component, options = {}) {
const payload = create_payload();
const prev_on_destroy = on_destroy;

@ -0,0 +1,21 @@
import type { RenderOutput } from '#server';
import type { ComponentProps, Component, SvelteComponent, ComponentType } from 'svelte';
/**
* Only available on the server and when compiling with the `server` option.
* Takes a component and returns an object with `body` and `head` properties on it, which you can use to populate the HTML when server-rendering your app.
*/
export function render<
Comp extends SvelteComponent<any> | Component<any>,
Props extends ComponentProps<Comp> = ComponentProps<Comp>
>(
...args: {} extends Props
? [
component: Comp extends SvelteComponent<any> ? ComponentType<Comp> : Comp,
options?: { props?: Omit<Props, '$$slots' | '$$events'>; context?: Map<any, any> }
]
: [
component: Comp extends SvelteComponent<any> ? ComponentType<Comp> : Comp,
options: { props: Omit<Props, '$$slots' | '$$events'>; context?: Map<any, any> }
]
): RenderOutput;

@ -1,4 +1,5 @@
import { noop, run_all } from '../internal/shared/utils.js';
import { safe_not_equal } from '../internal/client/reactivity/equality.js';
import { subscribe_to_store } from './utils.js';
/**
@ -21,15 +22,6 @@ export function readable(value, start) {
};
}
/**
* @param {any} a
* @param {any} b
* @returns {boolean}
*/
export function safe_not_equal(a, b) {
return a != a ? b == b : a !== b || (a && typeof a === 'object') || typeof a === 'function';
}
/**
* Create a `Writable` store that allows both updating and reading by subscription.
*
@ -213,11 +205,9 @@ export function readonly(store) {
* @param {import('../store/public').Readable<T>} store
* @returns {T}
*/
export function get_store_value(store) {
export function get(store) {
let value;
subscribe_to_store(store, (_) => (value = _))();
// @ts-expect-error
return value;
}
export { get_store_value as get };

@ -6,5 +6,5 @@
* https://svelte.dev/docs/svelte-compiler#svelte-version
* @type {string}
*/
export const VERSION = '5.0.0-next.160';
export const VERSION = '5.0.0-next.165';
export const PUBLIC_VERSION = '5';

@ -0,0 +1,9 @@
import { test } from '../../test';
export default test({
error: {
code: 'expected_token',
message: 'Expected token )',
position: [31, 31]
}
});

@ -0,0 +1,9 @@
import { test } from '../../test';
export default test({
error: {
code: 'expected_token',
message: 'Expected token }',
position: [20, 20]
}
});

@ -0,0 +1,20 @@
import { assert_ok, test } from '../../test';
export default test({
server_props: {
items: []
},
props: {
items: [{ name: 'a' }]
},
snapshot(target) {
const ul = target.querySelector('ul');
assert_ok(ul);
return {
ul
};
}
});

@ -0,0 +1,9 @@
<!--ssr:0--><ul><!--ssr:1--><!--ssr:7--><li>a</li><!--ssr:7--><!--ssr:1--></ul>
<ul><!--ssr:2--><!--ssr:9--><li>a</li><!--ssr:9--><!--ssr:2--></ul>
<ul><!--ssr:3--><!--ssr:11--><li>a</li><!--ssr:11--><!--ssr:3--></ul>
<!--ssr:4--><!--ssr:13--><li>a</li>
<li>a</li><!--ssr:13--><!--ssr:4-->
<!--ssr:5--><!--ssr:15--><li>a</li>
<li>a</li><!--ssr:15--><!--ssr:5-->
<!--ssr:6--><!--ssr:17--><li>a</li>
<li>a</li><!--ssr:17--><!--ssr:6--><!--ssr:0--></div>

@ -0,0 +1,32 @@
<script>
let { items } = $props();
</script>
<ul>
{#each items as item}
<li>{item.name}</li>
{/each}
</ul>
<ul>
{#each items as item (item)}
<li>{item.name}</li>
{/each}
</ul>
<ul>
{#each items as item (item.name)}
<li>{item.name}</li>
{/each}
</ul>
{#each items as item}
<li>{item.name}</li>
<li>{item.name}</li>
{/each}
{#each items as item (item)}
<li>{item.name}</li>
<li>{item.name}</li>
{/each}
{#each items as item (item.name)}
<li>{item.name}</li>
<li>{item.name}</li>
{/each}

@ -32,20 +32,28 @@
"loc": {
"start": {
"line": 3,
"column": 14,
"character": 43
"column": 14
},
"end": {
"line": 3,
"column": 25,
"character": 54
"column": 25
}
},
"end": 54,
"end": 46,
"typeAnnotation": {
"type": "TSTypeAnnotation",
"start": 46,
"end": 54,
"loc": {
"start": {
"line": 3,
"column": 17
},
"end": {
"line": 3,
"column": 25
}
},
"typeAnnotation": {
"type": "TSStringKeyword",
"start": 48,

@ -54,7 +54,10 @@
"line": 6,
"column": 12
},
"end": 87
"end": {
"line": 6,
"column": 25
}
},
"name": "e",
"typeAnnotation": {

@ -22,6 +22,7 @@ export default test({
prop3: { prop7: 'seven' },
prop4: { prop10: 'ten' }
}));
await Promise.resolve();
assert.htmlEqual(
target.innerHTML,
`

@ -0,0 +1,8 @@
import { test } from '../../test';
export default test({
mode: ['client'],
test({ assert, logs }) {
assert.deepEqual(logs, [true]);
}
});

@ -0,0 +1,4 @@
<script>
export let a = {};
console.log((a.b = true));
</script>

@ -118,9 +118,8 @@ export default test({
assert.htmlEqual(
target.innerHTML,
`
<p class="then" foo="0.5">43</p>
<p class="then" foo="0.5">44</p>
<p class="pending" foo="0.5">loading...</p>
<p class="then" foo="0.0">44</p>
`
);
@ -159,9 +158,8 @@ export default test({
assert.htmlEqual(
target.innerHTML,
`
<p class="then" foo="0.6">44</p>
<p class="then" foo="0.6">45</p>
<p class="pending" foo="0.4">loading...</p>
<p class="then" foo="0.0">45</p>
`
);
@ -169,9 +167,8 @@ export default test({
assert.htmlEqual(
target.innerHTML,
`
<p class="then" foo="0.4">44</p>
<p class="then" foo="0.8">45</p>
<p class="pending" foo="0.2">loading...</p>
<p class="then" foo="0.2">45</p>
`
);
@ -183,10 +180,8 @@ export default test({
assert.htmlEqual(
target.innerHTML,
`
<p class="then" foo="0.4">44</p>
<p class="then" foo="0.8">45</p>
<p class="pending" foo="0.2">loading...</p>
<p class="then" foo="0.2">45</p>
<p class="pending" foo="0.0">loading...</p>
`
);
@ -195,10 +190,8 @@ export default test({
assert.htmlEqual(
target.innerHTML,
`
<p class="then" foo="0.3">44</p>
<p class="pending" foo="0.1">loading...</p>
<p class="then" foo="0.1">45</p>
<p class="pending" foo="0.1">loading...</p>
<p class="then" foo="0.7">45</p>
<p class="pending" foo="0.3">loading...</p>
`
);
@ -207,11 +200,8 @@ export default test({
assert.htmlEqual(
target.innerHTML,
`
<p class="then" foo="0.3">44</p>
<p class="pending" foo="0.1">loading...</p>
<p class="then" foo="0.1">45</p>
<p class="pending" foo="0.1">loading...</p>
<p class="then" foo="0.0">46</p>
<p class="then" foo="0.7">46</p>
<p class="pending" foo="0.3">loading...</p>
`
);
@ -219,20 +209,12 @@ export default test({
assert.htmlEqual(
target.innerHTML,
`
<p class="then" foo="0.2">44</p>
<p class="then" foo="0.1">46</p>
<p class="then" foo="0.8">46</p>
<p class="pending" foo="0.2">loading...</p>
`
);
raf.tick((time += 20));
assert.htmlEqual(
target.innerHTML,
`
<p class="then" foo="0.3">46</p>
`
);
raf.tick((time += 70));
assert.htmlEqual(
target.innerHTML,
`

@ -183,7 +183,9 @@ async function run_test_variant(
if (str.slice(0, i).includes('logs')) {
// eslint-disable-next-line no-console
console.log = (...args) => logs.push(...args);
console.log = (...args) => {
logs.push(...args);
};
}
if (str.slice(0, i).includes('hydrate')) {

@ -0,0 +1,24 @@
import { test } from '../../test';
export default test({
async test({ assert, target, logs }) {
const [b1, b2] = target.querySelectorAll('button');
b1.click();
await Promise.resolve();
assert.htmlEqual(
target.innerHTML,
`<p>pending</p><button>Show Promise A</button><button>Show Promise B</button>`
);
b2.click();
await Promise.resolve();
await Promise.resolve();
assert.htmlEqual(
target.innerHTML,
`<p>pending</p><button>Show Promise A</button><button>Show Promise B</button>`
);
assert.deepEqual(logs, ['rendering pending block']);
}
});

@ -0,0 +1,17 @@
<script>
const a = new Promise(() => {});
const b = new Promise(() => {});
let promise = $state(a);
</script>
{#await promise}
{console.log('rendering pending block')}
<p>pending</p>
{:then value}
{console.log('rendering then block')}
<p>then {value}</p>
{/await}
<button onclick={() => (promise = a)}>Show Promise A</button>
<button onclick={() => (promise = b)}>Show Promise B</button>

@ -0,0 +1,21 @@
import { test } from '../../test';
export default test({
async test({ assert, target, logs }) {
const [b1, b2, b3, b4] = target.querySelectorAll('button');
b1.click();
await Promise.resolve();
b2.click();
await Promise.resolve();
await Promise.resolve();
await Promise.resolve();
b3.click();
await Promise.resolve();
await Promise.resolve();
await Promise.resolve();
b4.click();
await Promise.resolve();
await Promise.resolve();
assert.deepEqual(logs, ['pending', 'a', 'b', 'c', 'pending']);
}
});

@ -0,0 +1,21 @@
<script>
const promise_a = Promise.resolve('a');
const promise_b = Promise.resolve('b');
const promise_c = Promise.resolve('c');
const promise_d = new Promise(() => {});
let current_promise = $state(promise_a);
</script>
{#await current_promise}
{console.log('pending')}
{:then value}
{console.log(value)}
{:catch}
{console.log('error')}
{/await}
<button onclick={()=>{current_promise = promise_a}}>Show Promise A</button>
<button onclick={()=>{current_promise = promise_b}}>Show Promise B</button>
<button onclick={()=>{current_promise = promise_c}}>Show Promise C</button>
<button onclick={()=>{current_promise = promise_d}}>Show Promise D</button>

@ -0,0 +1,27 @@
import { test } from '../../test';
export default test({
async test({ assert, target, logs }) {
const [b1, b2] = target.querySelectorAll('button');
b1.click();
await Promise.resolve();
assert.htmlEqual(
target.innerHTML,
`<p>then a</p><button>Show Promise A</button><button>Show Promise B</button>`
);
b2.click();
await Promise.resolve();
assert.htmlEqual(
target.innerHTML,
`<p>then a</p><button>Show Promise A</button><button>Show Promise B</button>`
);
await Promise.resolve();
await Promise.resolve();
assert.htmlEqual(
target.innerHTML,
`<p>then b</p><button>Show Promise A</button><button>Show Promise B</button>`
);
assert.deepEqual(logs, ['rendering pending block', 'rendering then block']);
}
});

@ -0,0 +1,17 @@
<script>
const a = Promise.resolve('a');
const b = Promise.resolve('b');
let promise = $state(a);
</script>
{#await promise}
{console.log('rendering pending block')}
<p>pending</p>
{:then value}
{console.log('rendering then block')}
<p>then {value}</p>
{/await}
<button onclick={() => (promise = a)}>Show Promise A</button>
<button onclick={() => (promise = b)}>Show Promise B</button>

@ -0,0 +1,9 @@
<script>
let { object = $bindable(), primitive = $bindable() } = $props();
</script>
{#if primitive}
<button onclick={() => (primitive = 'bar')}>{primitive}</button>
{:else}
<button onclick={() => (object.value = 'bar')}>{object.value}</button>
{/if}

@ -0,0 +1,9 @@
<script>
let { object = $bindable({}), primitive = $bindable('') } = $props();
</script>
{#if primitive}
<button onclick={() => (primitive = 'bar')}>{primitive}</button>
{:else}
<button onclick={() => (object.value = 'bar')}>{object.value}</button>
{/if}

@ -0,0 +1,24 @@
<svelte:options runes={false} />
<script>
import Component1 from './Component1.svelte';
import Component2 from './Component2.svelte';
let object1 = { value: 'foo' };
let object2 = { value: 'foo' };
let primitive1 = 'foo';
let primitive2 = 'foo';
</script>
{object1.value}
<Component1 bind:object={object1} />
{object2.value}
<Component2 bind:object={object2} />
{primitive1}
<Component1 bind:primitive={primitive1} />
{primitive2}
<Component2 bind:primitive={primitive2} />

@ -0,0 +1,39 @@
<script>
import Component1 from './Component1.svelte';
import Component2 from './Component2.svelte';
let object1 = $state({ value: 'foo' });
let object2 = $state({ value: 'foo' });
class Frozen {
constructor(value) {
this.value = value;
}
}
let object3 = $state(new Frozen('foo'));
let object4 = $state(new Frozen('foo'));
let primitive1 = $state('foo');
let primitive2 = $state('foo');
</script>
{object1.value}
<Component1 bind:object={object1} />
{object2.value}
<Component2 bind:object={object2} />
<!-- force them into a different render effect so they don't coincidently update with the others -->
{#if true}
{object3.value}
<Component1 bind:object={object3} />
{object4.value}
<Component2 bind:object={object4} />
{/if}
{primitive1}
<Component1 bind:primitive={primitive1} />
{primitive2}
<Component2 bind:primitive={primitive2} />

@ -0,0 +1,24 @@
import { flushSync } from 'svelte';
import { test } from '../../test';
export default test({
mode: ['client'],
async test({ assert, target }) {
const buttons = target.querySelectorAll('button');
for (const button of buttons) {
await button.click();
flushSync();
}
flushSync();
assert.htmlEqual(
target.innerHTML,
`
bar <button>bar</button> bar <button>bar</button> bar <button>bar</button> bar <button>bar</button>
<hr>
bar <button>bar</button> bar <button>bar</button> foo <button>foo</button> foo <button>foo</button> bar <button>bar</button> bar <button>bar</button>
`
);
}
});

@ -0,0 +1,10 @@
<script>
import Legacy from './Legacy.svelte';
import Runes from './Runes.svelte';
</script>
<Legacy />
<hr />
<Runes />

@ -0,0 +1,5 @@
import { test } from '../../test';
export default test({
html: '013/023'
});

@ -0,0 +1,9 @@
{#snippet one(a, b = 1, c = (2, 3))}
{a}{b}{c}
{/snippet}
{#snippet two(a, b = (1, 2), c = 3)}
{a}{b}{c}
{/snippet}
{@render one(0)}/{@render two(0)}

@ -0,0 +1,7 @@
import { test } from '../../test';
export default test({
test({ assert, logs }) {
assert.deepEqual(logs, [1, 1, 1, 1]);
}
});

@ -0,0 +1,9 @@
<script>
let x = $state(0);
let o = $state({ x: 0 });
console.log(++x);
console.log(x++);
console.log(++o.x);
console.log(o.x++);
</script>

@ -5,6 +5,6 @@ export default function Bind_this($$anchor) {
var fragment = $.comment();
var node = $.first_child(fragment);
$.bind_this(Foo(node, {}), ($$value) => foo = $$value, () => foo);
$.bind_this(Foo(node, { $$legacy: true }), ($$value) => foo = $$value, () => foo);
$.append($$anchor, fragment);
}

@ -131,6 +131,13 @@ mount(NewComponent, {
intro: false,
recover: false
});
mount(
NewComponent,
// @ts-expect-error props missing
{ target: null as any }
);
// if component receives no args, props can be omitted
mount(null as any as typeof SvelteComponent<{}>, { target: null as any });
hydrate(NewComponent, {
target: null as any as Document | Element | ShadowRoot,
@ -148,6 +155,13 @@ hydrate(NewComponent, {
intro: false,
recover: false
});
hydrate(
NewComponent,
// @ts-expect-error props missing
{ target: null as any }
);
// if component receives no args, props can be omitted
hydrate(null as any as typeof SvelteComponent<{}>, { target: null as any });
render(NewComponent, {
props: {
@ -156,6 +170,14 @@ render(NewComponent, {
x: ''
}
});
// @ts-expect-error
render(NewComponent);
render(NewComponent, {
props: {
// @ts-expect-error
prop: 1
}
});
// --------------------------------------------------------------------------- interop
@ -255,6 +277,13 @@ mount(functionComponent, {
readonly: 1
}
});
mount(
functionComponent,
// @ts-expect-error props missing
{ target: null as any }
);
// if component receives no args, props can be omitted
mount(null as any as Component<{}>, { target: null as any });
hydrate(functionComponent, {
target: null as any as Document | Element | ShadowRoot,
@ -272,13 +301,33 @@ hydrate(functionComponent, {
binding: true
}
});
hydrate(
functionComponent,
// @ts-expect-error props missing
{ target: null as any }
);
// if component receives no args, props can be omitted
hydrate(null as any as Component<{}>, { target: null as any });
render(functionComponent, {
props: {
binding: true,
readonly: 'foo',
readonly: 'foo'
}
});
// @ts-expect-error
render(functionComponent);
render(functionComponent, {
// @ts-expect-error
x: ''
props: {
binding: true
}
});
render(functionComponent, {
props: {
binding: true,
// @ts-expect-error
readonly: 1
}
});

@ -1,3 +1,11 @@
<script>
import Component from './component.svelte'
</script>
<label>
<slot />
</label>
<label>
<Component />
</label>

@ -0,0 +1,7 @@
<script>
import Component from './component.svelte'
</script>
<label>
{@render x()}
</label>

@ -0,0 +1,4 @@
<!-- prettier-ignore -->
{#snippet ok () }
asd
{/snippet}

@ -23,7 +23,7 @@
"svelte/internal/client": ["./src/internal/client/index.js"],
"svelte/legacy": ["./src/legacy/legacy-client.js"],
"svelte/motion": ["./src/motion/public.d.ts"],
"svelte/server": ["./src/server/index.js"],
"svelte/server": ["./src/server/index.d.ts"],
"svelte/store": ["./src/store/public.d.ts"],
"#compiler": ["./src/compiler/types/index.d.ts"],
"#client": ["./src/internal/client/types.d.ts"],

@ -5,7 +5,7 @@ declare module 'svelte' {
* See [breaking changes](https://svelte-5-preview.vercel.app/docs/breaking-changes#components-are-no-longer-classes)
* for more info.
*/
export interface ComponentConstructorOptions<
interface ComponentConstructorOptions<
Props extends Record<string, any> = Record<string, any>
> {
target: Element | Document | ShadowRoot;
@ -36,7 +36,7 @@ declare module 'svelte' {
* To instantiate components, use `mount` instead`.
* See [breaking changes documentation](https://svelte-5-preview.vercel.app/docs/breaking-changes#components-are-no-longer-classes) for more info.
*/
export class SvelteComponent<
class SvelteComponent<
Props extends Record<string, any> = Record<string, any>,
Events extends Record<string, any> = any,
Slots extends Record<string, any> = any
@ -123,7 +123,7 @@ declare module 'svelte' {
* <MyComponent foo={'bar'} />
* ```
*/
export interface Component<
interface Component<
Props extends Record<string, any> = {},
Exports extends Record<string, any> = {},
Bindings extends keyof Props | '' = string
@ -158,7 +158,7 @@ declare module 'svelte' {
/**
* @deprecated Use `Component` instead. See [breaking changes documentation](https://svelte-5-preview.vercel.app/docs/breaking-changes#components-are-no-longer-classes) for more information.
*/
export class SvelteComponentTyped<
class SvelteComponentTyped<
Props extends Record<string, any> = Record<string, any>,
Events extends Record<string, any> = any,
Slots extends Record<string, any> = any
@ -182,21 +182,38 @@ declare module 'svelte' {
* <Component on:close={handleCloseEvent} />
* ```
*/
export type ComponentEvents<Comp extends SvelteComponent> =
type ComponentEvents<Comp extends SvelteComponent> =
Comp extends SvelteComponent<any, infer Events> ? Events : never;
/**
* Convenience type to get the props the given component expects. Example:
* ```html
* <script lang="ts">
* Convenience type to get the props the given component expects.
*
* Example: Ensure a variable contains the props expected by `MyComponent`:
*
* ```ts
* import type { ComponentProps } from 'svelte';
* import Component from './Component.svelte';
* import MyComponent from './MyComponent.svelte';
*
* const props: ComponentProps<Component> = { foo: 'bar' }; // Errors if these aren't the correct props
* </script>
* // Errors if these aren't the correct props expected by MyComponent.
* const props: ComponentProps<MyComponent> = { foo: 'bar' };
* ```
*
* Example: A generic function that accepts some component and infers the type of its props:
*
* ```ts
* import type { Component, ComponentProps } from 'svelte';
* import MyComponent from './MyComponent.svelte';
*
* function withProps<TComponent extends Component<any>>(
* component: TComponent,
* props: ComponentProps<TComponent>
* ) {};
*
* // Errors if the second argument is not the correct props expected by the component in the first argument.
* withProps(MyComponent, { foo: 'bar' });
* ```
*/
export type ComponentProps<Comp extends SvelteComponent | Component<any>> =
type ComponentProps<Comp extends SvelteComponent | Component<any>> =
Comp extends SvelteComponent<infer Props>
? Props
: Comp extends Component<infer Props>
@ -225,7 +242,7 @@ declare module 'svelte' {
* <svelte:component this={componentOfCertainSubType} needsThisProp="hello" />
* ```
*/
export type ComponentType<Comp extends SvelteComponent = SvelteComponent> = (new (
type ComponentType<Comp extends SvelteComponent = SvelteComponent> = (new (
options: ComponentConstructorOptions<
Comp extends SvelteComponent<infer Props> ? Props : Record<string, any>
>
@ -243,7 +260,7 @@ declare module 'svelte' {
* ```
* You can only call a snippet through the `{@render ...}` tag.
*/
export type Snippet<T extends unknown[] = []> =
type Snippet<T extends unknown[] = []> =
// this conditional allows tuples but not arrays. Arrays would indicate a
// rest parameter type, which is not supported. If rest parameters are added
// in the future, the condition can be removed.
@ -262,7 +279,7 @@ declare module 'svelte' {
cancelable?: boolean;
}
export interface EventDispatcher<EventMap extends Record<string, any>> {
interface EventDispatcher<EventMap extends Record<string, any>> {
// Implementation notes:
// - undefined extends X instead of X extends undefined makes this work better with both strict and nonstrict mode
// - | null | undefined is added for convenience, as they are equivalent for the custom event constructor (both result in a null detail)
@ -285,7 +302,7 @@ declare module 'svelte' {
*
* https://svelte.dev/docs/svelte#onmount
* */
export function onMount<T>(fn: () => NotFunction<T> | Promise<NotFunction<T>> | (() => any)): void;
function onMount<T>(fn: () => NotFunction<T> | Promise<NotFunction<T>> | (() => any)): void;
/**
* Schedules a callback to run immediately before the component is unmounted.
*
@ -294,7 +311,7 @@ declare module 'svelte' {
*
* https://svelte.dev/docs/svelte#ondestroy
* */
export function onDestroy(fn: () => any): void;
function onDestroy(fn: () => any): void;
/**
* Creates an event dispatcher that can be used to dispatch [component events](https://svelte.dev/docs#template-syntax-component-directives-on-eventname).
* Event dispatchers are functions that can take two arguments: `name` and `detail`.
@ -317,7 +334,7 @@ declare module 'svelte' {
* https://svelte.dev/docs/svelte#createeventdispatcher
* @deprecated Use callback props and/or the `$host()` rune instead see https://svelte-5-preview.vercel.app/docs/deprecations#createeventdispatcher
* */
export function createEventDispatcher<EventMap extends Record<string, any> = any>(): EventDispatcher<EventMap>;
function createEventDispatcher<EventMap extends Record<string, any> = any>(): EventDispatcher<EventMap>;
/**
* Schedules a callback to run immediately before the component is updated after any state change.
*
@ -328,7 +345,7 @@ declare module 'svelte' {
* https://svelte.dev/docs/svelte#beforeupdate
* @deprecated Use `$effect.pre` instead see https://svelte-5-preview.vercel.app/docs/deprecations#beforeupdate-and-afterupdate
* */
export function beforeUpdate(fn: () => void): void;
function beforeUpdate(fn: () => void): void;
/**
* Schedules a callback to run immediately after the component has been updated.
*
@ -339,58 +356,72 @@ declare module 'svelte' {
* https://svelte.dev/docs/svelte#afterupdate
* @deprecated Use `$effect` instead see https://svelte-5-preview.vercel.app/docs/deprecations#beforeupdate-and-afterupdate
* */
export function afterUpdate(fn: () => void): void;
function afterUpdate(fn: () => void): void;
/**
* Synchronously flushes any pending state changes and those that result from it.
* */
export function flushSync(fn?: (() => void) | undefined): void;
function flushSync(fn?: (() => void) | undefined): void;
/** Anything except a function */
type NotFunction<T> = T extends Function ? never : T;
/**
* Mounts a component to the given target and returns the exports and potentially the props (if compiled with `accessors: true`) of the component
*
* */
export function mount<Props extends Record<string, any>, Exports extends Record<string, any>>(component: ComponentType<SvelteComponent<Props, any, any>> | Component<Props, Exports, any>, options: {
function mount<Props extends Record<string, any>, Exports extends Record<string, any>>(component: ComponentType<SvelteComponent<Props>> | Component<Props, Exports, any>, options: {} extends Props ? {
target: Document | Element | ShadowRoot;
anchor?: Node;
props?: Props;
events?: Record<string, (e: any) => any>;
context?: Map<any, any>;
intro?: boolean;
} : {
target: Document | Element | ShadowRoot;
anchor?: Node | undefined;
props?: Props | undefined;
events?: Record<string, (e: any) => any> | undefined;
context?: Map<any, any> | undefined;
intro?: boolean | undefined;
props: Props;
anchor?: Node;
events?: Record<string, (e: any) => any>;
context?: Map<any, any>;
intro?: boolean;
}): Exports;
/**
* Hydrates a component on the given target and returns the exports and potentially the props (if compiled with `accessors: true`) of the component
*
* */
export function hydrate<Props extends Record<string, any>, Exports extends Record<string, any>>(component: ComponentType<SvelteComponent<Props, any, any>> | Component<Props, Exports, any>, options: {
function hydrate<Props extends Record<string, any>, Exports extends Record<string, any>>(component: ComponentType<SvelteComponent<Props>> | Component<Props, Exports, any>, options: {} extends Props ? {
target: Document | Element | ShadowRoot;
props?: Props;
events?: Record<string, (e: any) => any>;
context?: Map<any, any>;
intro?: boolean;
recover?: boolean;
} : {
target: Document | Element | ShadowRoot;
props?: Props | undefined;
events?: Record<string, (e: any) => any> | undefined;
context?: Map<any, any> | undefined;
intro?: boolean | undefined;
recover?: boolean | undefined;
props: Props;
events?: Record<string, (e: any) => any>;
context?: Map<any, any>;
intro?: boolean;
recover?: boolean;
}): Exports;
/**
* Unmounts a component that was previously mounted using `mount` or `hydrate`.
* */
export function unmount(component: Record<string, any>): void;
function unmount(component: Record<string, any>): void;
/**
* Returns a promise that resolves once any pending state changes have been applied.
* */
export function tick(): Promise<void>;
function tick(): Promise<void>;
/**
* Use `untrack` to prevent something from being treated as an `$effect`/`$derived` dependency.
*
* https://svelte-5-preview.vercel.app/docs/functions#untrack
* */
export function untrack<T>(fn: () => T): T;
function untrack<T>(fn: () => T): T;
/**
* Retrieves the context that belongs to the closest parent component with the specified `key`.
* Must be called during component initialisation.
*
* https://svelte.dev/docs/svelte#getcontext
* */
export function getContext<T>(key: any): T;
function getContext<T>(key: any): T;
/**
* Associates an arbitrary `context` object with the current component and the specified `key`
* and returns that object. The context is then available to children of the component
@ -400,14 +431,14 @@ declare module 'svelte' {
*
* https://svelte.dev/docs/svelte#setcontext
* */
export function setContext<T>(key: any, context: T): T;
function setContext<T>(key: any, context: T): T;
/**
* Checks whether a given `key` has been set in the context of a parent component.
* Must be called during component initialisation.
*
* https://svelte.dev/docs/svelte#hascontext
* */
export function hasContext(key: any): boolean;
function hasContext(key: any): boolean;
/**
* Retrieves the whole context map that belongs to the closest parent component.
* Must be called during component initialisation. Useful, for example, if you
@ -415,7 +446,9 @@ declare module 'svelte' {
*
* https://svelte.dev/docs/svelte#getallcontexts
* */
export function getAllContexts<T extends Map<any, any> = Map<any, any>>(): T;
function getAllContexts<T extends Map<any, any> = Map<any, any>>(): T;
export { ComponentConstructorOptions, SvelteComponent, Component, SvelteComponentTyped, ComponentEvents, ComponentProps, ComponentType, Snippet, EventDispatcher, onMount, onDestroy, createEventDispatcher, beforeUpdate, afterUpdate, flushSync, hydrate, mount, unmount, getContext, getAllContexts, hasContext, setContext, tick, untrack };
}
declare module 'svelte/action' {
@ -447,7 +480,7 @@ declare module 'svelte/action' {
*
* Docs: https://svelte.dev/docs/svelte-action
*/
export interface ActionReturn<
interface ActionReturn<
Parameter = undefined,
Attributes extends Record<string, any> = Record<never, any>
> {
@ -478,7 +511,7 @@ declare module 'svelte/action' {
*
* Docs: https://svelte.dev/docs/svelte-action
*/
export interface Action<
interface Action<
Element = HTMLElement,
Parameter = undefined,
Attributes extends Record<string, any> = Record<never, any>
@ -492,11 +525,13 @@ declare module 'svelte/action' {
// Implementation notes:
// - undefined extends X instead of X extends undefined makes this work better with both strict and nonstrict mode
export { ActionReturn, Action };
}
declare module 'svelte/animate' {
// todo: same as Transition, should it be shared?
export interface AnimationConfig {
interface AnimationConfig {
delay?: number;
duration?: number;
easing?: (t: number) => number;
@ -504,7 +539,7 @@ declare module 'svelte/animate' {
tick?: (t: number, u: number) => void;
}
export interface FlipParams {
interface FlipParams {
delay?: number;
duration?: number | ((len: number) => number);
easing?: (t: number) => number;
@ -515,10 +550,12 @@ declare module 'svelte/animate' {
*
* https://svelte.dev/docs/svelte-animate#flip
* */
export function flip(node: Element, { from, to }: {
function flip(node: Element, { from, to }: {
from: DOMRect;
to: DOMRect;
}, params?: FlipParams): AnimationConfig;
export { AnimationConfig, FlipParams, flip };
}
declare module 'svelte/compiler' {
@ -533,14 +570,14 @@ declare module 'svelte/compiler' {
* @param source The component source code
* @param options The compiler options
* */
export function compile(source: string, options: CompileOptions): CompileResult;
function compile(source: string, options: CompileOptions): CompileResult;
/**
* `compileModule` takes your JavaScript source code containing runes, and turns it into a JavaScript module.
*
* https://svelte.dev/docs/svelte-compiler#svelte-compile
* @param source The component source code
* */
export function compileModule(source: string, options: ModuleCompileOptions): CompileResult;
function compileModule(source: string, options: ModuleCompileOptions): CompileResult;
/**
* The parse function parses a component, returning only its abstract syntax tree.
*
@ -549,7 +586,7 @@ declare module 'svelte/compiler' {
*
* https://svelte.dev/docs/svelte-compiler#svelte-parse
* */
export function parse(source: string, options: {
function parse(source: string, options: {
filename?: string;
modern: true;
}): Root;
@ -561,14 +598,14 @@ declare module 'svelte/compiler' {
*
* https://svelte.dev/docs/svelte-compiler#svelte-parse
* */
export function parse(source: string, options?: {
filename?: string | undefined;
modern?: false | undefined;
function parse(source: string, options?: {
filename?: string;
modern?: false;
} | undefined): LegacyRoot;
/**
* @deprecated Replace this with `import { walk } from 'estree-walker'`
* */
export function walk(): never;
function walk(): never;
/** The return value of `compile` from `svelte/compiler` */
interface CompileResult {
/** The compiled JavaScript */
@ -1093,19 +1130,19 @@ declare module 'svelte/compiler' {
*
* https://svelte.dev/docs/svelte-compiler#svelte-preprocess
* */
export function preprocess(source: string, preprocessor: PreprocessorGroup | PreprocessorGroup[], options?: {
filename?: string | undefined;
function preprocess(source: string, preprocessor: PreprocessorGroup | PreprocessorGroup[], options?: {
filename?: string;
} | undefined): Promise<Processed>;
export class CompileError extends Error {
class CompileError extends Error {
constructor(code: string, message: string, position: [number, number] | undefined);
filename: string | undefined;
position: CompileError_1['position'];
position: CompileError_1["position"];
start: CompileError_1['start'];
start: CompileError_1["start"];
end: CompileError_1['end'];
end: CompileError_1["end"];
code: string;
}
/**
@ -1113,13 +1150,13 @@ declare module 'svelte/compiler' {
*
* https://svelte.dev/docs/svelte-compiler#svelte-version
* */
export const VERSION: string;
const VERSION: string;
/**
* Does a best-effort migration of Svelte code towards using runes, event attributes and render tags.
* May throw an error if the code is too complex to migrate automatically.
*
* */
export function migrate(source: string): {
function migrate(source: string): {
code: string;
};
class Scope {
@ -1139,13 +1176,13 @@ declare module 'svelte/compiler' {
/**
* A map of declarators to the bindings they declare
* */
declarators: Map<import('estree').VariableDeclarator | LetDirective, Binding[]>;
declarators: Map<import("estree").VariableDeclarator | LetDirective, Binding[]>;
/**
* A set of all the names referenced with this scope
* useful for generating unique names
* */
references: Map<string, {
node: import('estree').Identifier;
node: import("estree").Identifier;
path: SvelteNode[];
}[]>;
/**
@ -1154,18 +1191,18 @@ declare module 'svelte/compiler' {
*/
function_depth: number;
declare(node: import('estree').Identifier, kind: Binding['kind'], declaration_kind: DeclarationKind, initial?: null | import('estree').Expression | import('estree').FunctionDeclaration | import('estree').ClassDeclaration | import('estree').ImportDeclaration | EachBlock): Binding;
declare(node: import("estree").Identifier, kind: Binding["kind"], declaration_kind: DeclarationKind, initial?: null | import("estree").Expression | import("estree").FunctionDeclaration | import("estree").ClassDeclaration | import("estree").ImportDeclaration | EachBlock): Binding;
child(porous?: boolean): Scope;
generate(preferred_name: string): string;
get(name: string): Binding | null;
get_bindings(node: import('estree').VariableDeclarator | LetDirective): Binding[];
get_bindings(node: import("estree").VariableDeclarator | LetDirective): Binding[];
owner(name: string): Scope | null;
reference(node: import('estree').Identifier, path: SvelteNode[]): void;
reference(node: import("estree").Identifier, path: SvelteNode[]): void;
#private;
}
class ScopeRoot {
@ -1855,133 +1892,137 @@ declare module 'svelte/compiler' {
style?: Preprocessor;
script?: Preprocessor;
}
export { compile, compileModule, parse, walk, preprocess, CompileError, VERSION, migrate };
}
declare module 'svelte/easing' {
/**
* https://svelte.dev/docs/svelte-easing
* */
export function linear(t: number): number;
function linear(t: number): number;
/**
* https://svelte.dev/docs/svelte-easing
* */
export function backInOut(t: number): number;
function backInOut(t: number): number;
/**
* https://svelte.dev/docs/svelte-easing
* */
export function backIn(t: number): number;
function backIn(t: number): number;
/**
* https://svelte.dev/docs/svelte-easing
* */
export function backOut(t: number): number;
function backOut(t: number): number;
/**
* https://svelte.dev/docs/svelte-easing
* */
export function bounceOut(t: number): number;
function bounceOut(t: number): number;
/**
* https://svelte.dev/docs/svelte-easing
* */
export function bounceInOut(t: number): number;
function bounceInOut(t: number): number;
/**
* https://svelte.dev/docs/svelte-easing
* */
export function bounceIn(t: number): number;
function bounceIn(t: number): number;
/**
* https://svelte.dev/docs/svelte-easing
* */
export function circInOut(t: number): number;
function circInOut(t: number): number;
/**
* https://svelte.dev/docs/svelte-easing
* */
export function circIn(t: number): number;
function circIn(t: number): number;
/**
* https://svelte.dev/docs/svelte-easing
* */
export function circOut(t: number): number;
function circOut(t: number): number;
/**
* https://svelte.dev/docs/svelte-easing
* */
export function cubicInOut(t: number): number;
function cubicInOut(t: number): number;
/**
* https://svelte.dev/docs/svelte-easing
* */
export function cubicIn(t: number): number;
function cubicIn(t: number): number;
/**
* https://svelte.dev/docs/svelte-easing
* */
export function cubicOut(t: number): number;
function cubicOut(t: number): number;
/**
* https://svelte.dev/docs/svelte-easing
* */
export function elasticInOut(t: number): number;
function elasticInOut(t: number): number;
/**
* https://svelte.dev/docs/svelte-easing
* */
export function elasticIn(t: number): number;
function elasticIn(t: number): number;
/**
* https://svelte.dev/docs/svelte-easing
* */
export function elasticOut(t: number): number;
function elasticOut(t: number): number;
/**
* https://svelte.dev/docs/svelte-easing
* */
export function expoInOut(t: number): number;
function expoInOut(t: number): number;
/**
* https://svelte.dev/docs/svelte-easing
* */
export function expoIn(t: number): number;
function expoIn(t: number): number;
/**
* https://svelte.dev/docs/svelte-easing
* */
export function expoOut(t: number): number;
function expoOut(t: number): number;
/**
* https://svelte.dev/docs/svelte-easing
* */
export function quadInOut(t: number): number;
function quadInOut(t: number): number;
/**
* https://svelte.dev/docs/svelte-easing
* */
export function quadIn(t: number): number;
function quadIn(t: number): number;
/**
* https://svelte.dev/docs/svelte-easing
* */
export function quadOut(t: number): number;
function quadOut(t: number): number;
/**
* https://svelte.dev/docs/svelte-easing
* */
export function quartInOut(t: number): number;
function quartInOut(t: number): number;
/**
* https://svelte.dev/docs/svelte-easing
* */
export function quartIn(t: number): number;
function quartIn(t: number): number;
/**
* https://svelte.dev/docs/svelte-easing
* */
export function quartOut(t: number): number;
function quartOut(t: number): number;
/**
* https://svelte.dev/docs/svelte-easing
* */
export function quintInOut(t: number): number;
function quintInOut(t: number): number;
/**
* https://svelte.dev/docs/svelte-easing
* */
export function quintIn(t: number): number;
function quintIn(t: number): number;
/**
* https://svelte.dev/docs/svelte-easing
* */
export function quintOut(t: number): number;
function quintOut(t: number): number;
/**
* https://svelte.dev/docs/svelte-easing
* */
export function sineInOut(t: number): number;
function sineInOut(t: number): number;
/**
* https://svelte.dev/docs/svelte-easing
* */
export function sineIn(t: number): number;
function sineIn(t: number): number;
/**
* https://svelte.dev/docs/svelte-easing
* */
export function sineOut(t: number): number;
function sineOut(t: number): number;
export { linear, backInOut, backIn, backOut, bounceOut, bounceInOut, bounceIn, circInOut, circIn, circOut, cubicInOut, cubicIn, cubicOut, elasticInOut, elasticIn, elasticOut, expoInOut, expoIn, expoOut, quadInOut, quadIn, quadOut, quartInOut, quartIn, quartOut, quintInOut, quintIn, quintOut, sineInOut, sineIn, sineOut };
}
declare module 'svelte/legacy' {
@ -1991,11 +2032,11 @@ declare module 'svelte/legacy' {
* @deprecated Use this only as a temporary solution to migrate your imperative component code to Svelte 5.
*
* */
export function createClassComponent<Props extends Record<string, any>, Exports extends Record<string, any>, Events extends Record<string, any>, Slots extends Record<string, any>>(options: import("svelte").ComponentConstructorOptions<Props> & {
component: import("svelte").ComponentType<import("svelte").SvelteComponent<Props, Events, Slots>> | import("svelte").Component<Props, any, string>;
immutable?: boolean | undefined;
hydrate?: boolean | undefined;
recover?: boolean | undefined;
function createClassComponent<Props extends Record<string, any>, Exports extends Record<string, any>, Events extends Record<string, any>, Slots extends Record<string, any>>(options: import("svelte").ComponentConstructorOptions<Props> & {
component: import("svelte").ComponentType<import("svelte").SvelteComponent<Props, Events, Slots>> | import("svelte").Component<Props>;
immutable?: boolean;
hydrate?: boolean;
recover?: boolean;
}): import("svelte").SvelteComponent<Props, Events, Slots> & Exports;
/**
* Takes the component function and returns a Svelte 4 compatible component constructor.
@ -2003,17 +2044,19 @@ declare module 'svelte/legacy' {
* @deprecated Use this only as a temporary solution to migrate your imperative component code to Svelte 5.
*
* */
export function asClassComponent<Props extends Record<string, any>, Exports extends Record<string, any>, Events extends Record<string, any>, Slots extends Record<string, any>>(component: import("svelte").SvelteComponent<Props, Events, Slots> | import("svelte").Component<Props, any, string>): import("svelte").ComponentType<import("svelte").SvelteComponent<Props, Events, Slots> & Exports>;
function asClassComponent<Props extends Record<string, any>, Exports extends Record<string, any>, Events extends Record<string, any>, Slots extends Record<string, any>>(component: import("svelte").SvelteComponent<Props, Events, Slots> | import("svelte").Component<Props>): import("svelte").ComponentType<import("svelte").SvelteComponent<Props, Events, Slots> & Exports>;
/**
* Runs the given function once immediately on the server, and works like `$effect.pre` on the client.
*
* @deprecated Use this only as a temporary solution to migrate your component code to Svelte 5.
* */
export function run(fn: () => void | (() => void)): void;
function run(fn: () => void | (() => void)): void;
export { createClassComponent, asClassComponent, run };
}
declare module 'svelte/motion' {
export interface Spring<T> extends Readable<T> {
interface Spring<T> extends Readable<T> {
set: (new_value: T, opts?: SpringUpdateOpts) => Promise<void>;
update: (fn: Updater<T>, opts?: SpringUpdateOpts) => Promise<void>;
precision: number;
@ -2021,7 +2064,7 @@ declare module 'svelte/motion' {
stiffness: number;
}
export interface Tweened<T> extends Readable<T> {
interface Tweened<T> extends Readable<T> {
set(value: T, opts?: TweenedOptions<T>): Promise<void>;
update(updater: Updater<T>, opts?: TweenedOptions<T>): Promise<void>;
}
@ -2066,13 +2109,15 @@ declare module 'svelte/motion' {
*
* https://svelte.dev/docs/svelte-motion#spring
* */
export function spring<T = any>(value?: T | undefined, opts?: SpringOpts | undefined): Spring<T>;
function spring<T = any>(value?: T | undefined, opts?: SpringOpts | undefined): Spring<T>;
/**
* A tweened store in Svelte is a special type of store that provides smooth transitions between state values over time.
*
* https://svelte.dev/docs/svelte-motion#tweened
* */
export function tweened<T>(value?: T | undefined, defaults?: TweenedOptions<T> | undefined): Tweened<T>;
function tweened<T>(value?: T | undefined, defaults?: TweenedOptions<T> | undefined): Tweened<T>;
export { Spring, Tweened, spring, tweened };
}
declare module 'svelte/reactivity' {
@ -2110,14 +2155,25 @@ declare module 'svelte/reactivity' {
}
declare module 'svelte/server' {
import type { ComponentProps, Component, SvelteComponent, ComponentType } from 'svelte';
/**
* Only available on the server and when compiling with the `server` option.
* Takes a component and returns an object with `body` and `head` properties on it, which you can use to populate the HTML when server-rendering your app.
* */
export function render<Props extends Record<string, any>>(component: import("svelte").Component<Props, any, string> | import("svelte").ComponentType<import("svelte").SvelteComponent<Props, any, any>>, options: {
props: Omit<Props, "$$slots" | "$$events">;
context?: Map<any, any> | undefined;
}): RenderOutput;
*/
function render<
Comp extends SvelteComponent<any> | Component<any>,
Props extends ComponentProps<Comp> = ComponentProps<Comp>
>(
...args: {} extends Props
? [
component: Comp extends SvelteComponent<any> ? ComponentType<Comp> : Comp,
options?: { props?: Omit<Props, '$$slots' | '$$events'>; context?: Map<any, any> }
]
: [
component: Comp extends SvelteComponent<any> ? ComponentType<Comp> : Comp,
options: { props: Omit<Props, '$$slots' | '$$events'>; context?: Map<any, any> }
]
): RenderOutput;
interface RenderOutput {
/** HTML that goes into the `<head>` */
head: string;
@ -2126,17 +2182,19 @@ declare module 'svelte/server' {
/** HTML that goes somewhere into the `<body>` */
body: string;
}
export { render };
}
declare module 'svelte/store' {
/** Callback to inform of a value updates. */
export type Subscriber<T> = (value: T) => void;
type Subscriber<T> = (value: T) => void;
/** Unsubscribes from value updates. */
export type Unsubscriber = () => void;
type Unsubscriber = () => void;
/** Callback to update a value. */
export type Updater<T> = (value: T) => T;
type Updater<T> = (value: T) => T;
/**
* Start and stop notification callbacks.
@ -2147,13 +2205,13 @@ declare module 'svelte/store' {
* @returns Optionally, a cleanup function that is called when the last remaining
* subscriber unsubscribes.
*/
export type StartStopNotifier<T> = (
type StartStopNotifier<T> = (
set: (value: T) => void,
update: (fn: Updater<T>) => void
) => void | (() => void);
/** Readable interface for subscribing. */
export interface Readable<T> {
interface Readable<T> {
/**
* Subscribe on value changes.
* @param run subscription callback
@ -2163,7 +2221,7 @@ declare module 'svelte/store' {
}
/** Writable interface for both updating and subscribing. */
export interface Writable<T> extends Readable<T> {
interface Writable<T> extends Readable<T> {
/**
* Set value and inform subscribers.
* @param value to set
@ -2194,49 +2252,49 @@ declare module 'svelte/store' {
* https://svelte.dev/docs/svelte-store#readable
* @param value initial value
* */
export function readable<T>(value?: T | undefined, start?: StartStopNotifier<T> | undefined): Readable<T>;
export function safe_not_equal(a: any, b: any): boolean;
function readable<T>(value?: T | undefined, start?: StartStopNotifier<T> | undefined): Readable<T>;
/**
* Create a `Writable` store that allows both updating and reading by subscription.
*
* https://svelte.dev/docs/svelte-store#writable
* @param value initial value
* */
export function writable<T>(value?: T | undefined, start?: StartStopNotifier<T> | undefined): Writable<T>;
function writable<T>(value?: T | undefined, start?: StartStopNotifier<T> | undefined): Writable<T>;
/**
* Derived value store by synchronizing one or more readable stores and
* applying an aggregation function over its input values.
*
* https://svelte.dev/docs/svelte-store#derived
* */
export function derived<S extends Stores, T>(stores: S, fn: (values: StoresValues<S>, set: (value: T) => void, update: (fn: Updater<T>) => void) => Unsubscriber | void, initial_value?: T | undefined): Readable<T>;
function derived<S extends Stores, T>(stores: S, fn: (values: StoresValues<S>, set: (value: T) => void, update: (fn: Updater<T>) => void) => Unsubscriber | void, initial_value?: T | undefined): Readable<T>;
/**
* Derived value store by synchronizing one or more readable stores and
* applying an aggregation function over its input values.
*
* https://svelte.dev/docs/svelte-store#derived
* */
export function derived<S extends Stores, T>(stores: S, fn: (values: StoresValues<S>) => T, initial_value?: T | undefined): Readable<T>;
function derived<S extends Stores, T>(stores: S, fn: (values: StoresValues<S>) => T, initial_value?: T | undefined): Readable<T>;
/**
* Takes a store and returns a new one derived from the old one that is readable.
*
* https://svelte.dev/docs/svelte-store#readonly
* @param store - store to make readonly
* */
export function readonly<T>(store: Readable<T>): Readable<T>;
function readonly<T>(store: Readable<T>): Readable<T>;
/**
* Get the current value from a store by subscribing and immediately unsubscribing.
*
* https://svelte.dev/docs/svelte-store#get
* */
export function get<T>(store: Readable<T>): T;
function get<T>(store: Readable<T>): T;
export { Subscriber, Unsubscriber, Updater, StartStopNotifier, Readable, Writable, readable, writable, derived, readonly, get };
}
declare module 'svelte/transition' {
export type EasingFunction = (t: number) => number;
type EasingFunction = (t: number) => number;
export interface TransitionConfig {
interface TransitionConfig {
delay?: number;
duration?: number;
easing?: EasingFunction;
@ -2244,7 +2302,7 @@ declare module 'svelte/transition' {
tick?: (t: number, u: number) => void;
}
export interface BlurParams {
interface BlurParams {
delay?: number;
duration?: number;
easing?: EasingFunction;
@ -2252,13 +2310,13 @@ declare module 'svelte/transition' {
opacity?: number;
}
export interface FadeParams {
interface FadeParams {
delay?: number;
duration?: number;
easing?: EasingFunction;
}
export interface FlyParams {
interface FlyParams {
delay?: number;
duration?: number;
easing?: EasingFunction;
@ -2267,14 +2325,14 @@ declare module 'svelte/transition' {
opacity?: number;
}
export interface SlideParams {
interface SlideParams {
delay?: number;
duration?: number;
easing?: EasingFunction;
axis?: 'x' | 'y';
}
export interface ScaleParams {
interface ScaleParams {
delay?: number;
duration?: number;
easing?: EasingFunction;
@ -2282,14 +2340,14 @@ declare module 'svelte/transition' {
opacity?: number;
}
export interface DrawParams {
interface DrawParams {
delay?: number;
speed?: number;
duration?: number | ((len: number) => number);
easing?: EasingFunction;
}
export interface CrossfadeParams {
interface CrossfadeParams {
delay?: number;
duration?: number | ((len: number) => number);
easing?: EasingFunction;
@ -2299,37 +2357,37 @@ declare module 'svelte/transition' {
*
* https://svelte.dev/docs/svelte-transition#blur
* */
export function blur(node: Element, { delay, duration, easing, amount, opacity }?: BlurParams | undefined): TransitionConfig;
function blur(node: Element, { delay, duration, easing, amount, opacity }?: BlurParams | undefined): TransitionConfig;
/**
* Animates the opacity of an element from 0 to the current opacity for `in` transitions and from the current opacity to 0 for `out` transitions.
*
* https://svelte.dev/docs/svelte-transition#fade
* */
export function fade(node: Element, { delay, duration, easing }?: FadeParams | undefined): TransitionConfig;
function fade(node: Element, { delay, duration, easing }?: FadeParams | undefined): TransitionConfig;
/**
* Animates the x and y positions and the opacity of an element. `in` transitions animate from the provided values, passed as parameters to the element's default values. `out` transitions animate from the element's default values to the provided values.
*
* https://svelte.dev/docs/svelte-transition#fly
* */
export function fly(node: Element, { delay, duration, easing, x, y, opacity }?: FlyParams | undefined): TransitionConfig;
function fly(node: Element, { delay, duration, easing, x, y, opacity }?: FlyParams | undefined): TransitionConfig;
/**
* Slides an element in and out.
*
* https://svelte.dev/docs/svelte-transition#slide
* */
export function slide(node: Element, { delay, duration, easing, axis }?: SlideParams | undefined): TransitionConfig;
function slide(node: Element, { delay, duration, easing, axis }?: SlideParams | undefined): TransitionConfig;
/**
* Animates the opacity and scale of an element. `in` transitions animate from an element's current (default) values to the provided values, passed as parameters. `out` transitions animate from the provided values to an element's default values.
*
* https://svelte.dev/docs/svelte-transition#scale
* */
export function scale(node: Element, { delay, duration, easing, start, opacity }?: ScaleParams | undefined): TransitionConfig;
function scale(node: Element, { delay, duration, easing, start, opacity }?: ScaleParams | undefined): TransitionConfig;
/**
* Animates the stroke of an SVG element, like a snake in a tube. `in` transitions begin with the path invisible and draw the path to the screen over time. `out` transitions start in a visible state and gradually erase the path. `draw` only works with elements that have a `getTotalLength` method, like `<path>` and `<polyline>`.
*
* https://svelte.dev/docs/svelte-transition#draw
* */
export function draw(node: SVGElement & {
function draw(node: SVGElement & {
getTotalLength(): number;
}, { delay, speed, duration, easing }?: DrawParams | undefined): TransitionConfig;
/**
@ -2337,13 +2395,15 @@ declare module 'svelte/transition' {
*
* https://svelte.dev/docs/svelte-transition#crossfade
* */
export function crossfade({ fallback, ...defaults }: CrossfadeParams & {
function crossfade({ fallback, ...defaults }: CrossfadeParams & {
fallback?: (node: Element, params: CrossfadeParams, intro: boolean) => TransitionConfig;
}): [(node: any, params: CrossfadeParams & {
key: any;
}) => () => TransitionConfig, (node: any, params: CrossfadeParams & {
key: any;
}) => () => TransitionConfig];
export { EasingFunction, TransitionConfig, BlurParams, FadeParams, FlyParams, SlideParams, ScaleParams, DrawParams, CrossfadeParams, blur, fade, fly, slide, scale, draw, crossfade };
}
declare module 'svelte/events' {
@ -2353,27 +2413,29 @@ declare module 'svelte/events' {
* (with attributes like `onclick`), which use event delegation for performance reasons
*
* */
export function on<Element extends HTMLElement, Type extends keyof HTMLElementEventMap>(element: Element, type: Type, handler: (this: Element, event: HTMLElementEventMap[Type]) => any, options?: AddEventListenerOptions | undefined): () => void;
function on<Element extends HTMLElement, Type extends keyof HTMLElementEventMap>(element: Element, type: Type, handler: (this: Element, event: HTMLElementEventMap[Type]) => any, options?: AddEventListenerOptions | undefined): () => void;
/**
* Attaches an event handler to an element and returns a function that removes the handler. Using this
* rather than `addEventListener` will preserve the correct order relative to handlers added declaratively
* (with attributes like `onclick`), which use event delegation for performance reasons
*
* */
export function on(element: EventTarget, type: string, handler: EventListener, options?: AddEventListenerOptions | undefined): () => void;
function on(element: EventTarget, type: string, handler: EventListener, options?: AddEventListenerOptions | undefined): () => void;
export { on };
}
declare module 'svelte/types/compiler/preprocess' {
/** @deprecated import this from 'svelte/preprocess' instead */
export type MarkupPreprocessor = MarkupPreprocessor_1;
type MarkupPreprocessor = MarkupPreprocessor_1;
/** @deprecated import this from 'svelte/preprocess' instead */
export type Preprocessor = Preprocessor_1;
type Preprocessor = Preprocessor_1;
/** @deprecated import this from 'svelte/preprocess' instead */
export type PreprocessorGroup = PreprocessorGroup_1;
type PreprocessorGroup = PreprocessorGroup_1;
/** @deprecated import this from 'svelte/preprocess' instead */
export type Processed = Processed_1;
type Processed = Processed_1;
/** @deprecated import this from 'svelte/preprocess' instead */
export type SveltePreprocessor<PreprocessorType extends keyof PreprocessorGroup_1, Options = any> = SveltePreprocessor_1<
type SveltePreprocessor<PreprocessorType extends keyof PreprocessorGroup_1, Options = any> = SveltePreprocessor_1<
PreprocessorType,
Options
>;
@ -2457,14 +2519,16 @@ declare module 'svelte/types/compiler/preprocess' {
> {
(options?: Options): Required<Pick<PreprocessorGroup_1, PreprocessorType>>;
}
export { MarkupPreprocessor, Preprocessor, PreprocessorGroup, Processed, SveltePreprocessor };
}
declare module 'svelte/types/compiler/interfaces' {
import type { Location } from 'locate-character';
/** @deprecated import this from 'svelte' instead */
export type CompileOptions = CompileOptions_1;
type CompileOptions = CompileOptions_1;
/** @deprecated import this from 'svelte' instead */
export type Warning = Warning_1;
type Warning = Warning_1;
interface Warning_1 {
start?: Location;
end?: Location;
@ -2635,6 +2699,8 @@ declare module 'svelte/types/compiler/interfaces' {
* (also see https://github.com/sveltejs/svelte/pull/5652)
*/
type Namespace = 'html' | 'svg' | 'mathml' | 'foreign';
export { CompileOptions, Warning };
}declare module '*.svelte' {
// use prettier-ignore for a while because of https://github.com/sveltejs/language-tools/commit/026111228b5814a9109cc4d779d37fb02955fb8b
// prettier-ignore

@ -13,8 +13,8 @@
},
"devDependencies": {
"@sveltejs/vite-plugin-svelte": "^3.1.0",
"express": "^4.19.2",
"nodemon": "^3.0.3",
"polka": "^1.0.0-next.25",
"svelte": "workspace:*",
"vite": "^5.0.13",
"vite-plugin-inspect": "^0.8.4"

@ -1,7 +1,7 @@
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import express from 'express';
import polka from 'polka';
import { createServer as createViteServer } from 'vite';
const PORT = process.env.PORT || '5173';
@ -11,7 +11,7 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url));
process.env.NODE_ENV = 'development';
async function createServer() {
const app = express();
const app = polka();
const vite = await createViteServer({
server: { middlewareMode: true },

@ -1,7 +1,5 @@
// @ts-ignore
import { render } from 'svelte/server';
// @ts-ignore you need to create this file
import App from './App.svelte';
// @ts-ignore
export const { head, body, css } = render(App, { props: { initialCount: 0 } });
export const { head, body } = render(App);

File diff suppressed because it is too large Load Diff

@ -21,7 +21,6 @@
"@sveltejs/kit": "^2.5.0",
"@sveltejs/site-kit": "6.0.0-next.64",
"@sveltejs/vite-plugin-svelte": "^3.1.0",
"@types/marked": "^6.0.0",
"@vercel/speed-insights": "^1.0.0",
"esrap": "^1.2.2",
"marked": "^9.0.0",
@ -31,7 +30,7 @@
"svelte": "workspace:^",
"svelte-check": "^3.6.3",
"tslib": "^2.6.2",
"typescript": "^5.3.3",
"typescript": "^5.5.2",
"vite": "^5.0.13"
},
"dependencies": {

@ -54,7 +54,7 @@
"svelte-check": "^3.6.3",
"svelte-preprocess": "^5.1.3",
"tiny-glob": "^0.2.9",
"typescript": "^5.3.3",
"typescript": "^5.5.2",
"vite": "^5.0.13",
"vite-imagetools": "^6.2.9"
}

@ -3,12 +3,14 @@ import { browserslistToTargets } from 'lightningcss';
import { readFile } from 'node:fs/promises';
import browserslist from 'browserslist';
/** @type {import('vite').PluginOption[]} */
const plugins = [raw(['.ttf']), sveltekit()];
// Only enable sharp if we're not in a webcontainer env
if (!process.versions.webcontainer) {
plugins.push(
(await import('vite-imagetools')).imagetools({
const { imagetools } = await import('vite-imagetools');
const plugin = imagetools({
defaultDirectives: (url) => {
if (url.searchParams.has('big-image')) {
return new URLSearchParams('w=640;1280;2560;3840&format=avif;webp;png&as=picture');
@ -16,8 +18,9 @@ if (!process.versions.webcontainer) {
return new URLSearchParams();
}
})
);
});
plugins.push(/** @type {import('vite').PluginOption} */ (/** @type {unknown} */ (plugin)));
}
/**

Loading…
Cancel
Save