diff --git a/.changeset/clever-news-enjoy.md b/.changeset/clever-news-enjoy.md
new file mode 100644
index 000000000000..2ff3dcbe5668
--- /dev/null
+++ b/.changeset/clever-news-enjoy.md
@@ -0,0 +1,5 @@
+---
+'svelte': patch
+---
+
+fix: allow `$.state` and `$.derived` to be treeshaken
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index b2bcb088480b..cf73a1f6cb02 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -12,6 +12,7 @@ env:
jobs:
Tests:
+ permissions: {}
runs-on: ${{ matrix.os }}
timeout-minutes: 15
strategy:
@@ -41,6 +42,7 @@ jobs:
env:
CI: true
Lint:
+ permissions: {}
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
@@ -61,6 +63,7 @@ jobs:
if: (${{ success() }} || ${{ failure() }}) # ensures this step runs even if previous steps fail
run: pnpm build && { [ "`git status --porcelain=v1`" == "" ] || (echo "Generated types have changed β please regenerate types locally with `cd packages/svelte && pnpm generate:types` and commit the changes after you have reviewed them"; git diff; exit 1); }
Benchmarks:
+ permissions: {}
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
diff --git a/.github/workflows/docs-preview-create-request.yml b/.github/workflows/docs-preview-create-request.yml
deleted file mode 100644
index f57766dc3696..000000000000
--- a/.github/workflows/docs-preview-create-request.yml
+++ /dev/null
@@ -1,26 +0,0 @@
-# https://github.com/sveltejs/svelte.dev/blob/main/apps/svelte.dev/scripts/sync-docs/README.md
-name: Docs preview create request
-
-on:
- pull_request_target:
- branches:
- - main
-
-jobs:
- dispatch:
- runs-on: ubuntu-latest
- steps:
- - name: Repository Dispatch
- uses: peter-evans/repository-dispatch@v3
- with:
- token: ${{ secrets.SYNC_REQUEST_TOKEN }}
- repository: sveltejs/svelte.dev
- event-type: docs-preview-create
- client-payload: |-
- {
- "package": "svelte",
- "repo": "${{ github.repository }}",
- "owner": "${{ github.event.pull_request.head.repo.owner.login }}",
- "branch": "${{ github.event.pull_request.head.ref }}",
- "pr": ${{ github.event.pull_request.number }}
- }
diff --git a/.github/workflows/docs-preview-delete-request.yml b/.github/workflows/docs-preview-delete-request.yml
deleted file mode 100644
index 4eb0e996a68e..000000000000
--- a/.github/workflows/docs-preview-delete-request.yml
+++ /dev/null
@@ -1,27 +0,0 @@
-# https://github.com/sveltejs/svelte.dev/blob/main/apps/svelte.dev/scripts/sync-docs/README.md
-name: Docs preview delete request
-
-on:
- pull_request_target:
- branches:
- - main
- types: [closed]
-
-jobs:
- dispatch:
- runs-on: ubuntu-latest
- steps:
- - name: Repository Dispatch
- uses: peter-evans/repository-dispatch@v3
- with:
- token: ${{ secrets.SYNC_REQUEST_TOKEN }}
- repository: sveltejs/svelte.dev
- event-type: docs-preview-delete
- client-payload: |-
- {
- "package": "svelte",
- "repo": "${{ github.repository }}",
- "owner": "${{ github.event.pull_request.head.repo.owner.login }}",
- "branch": "${{ github.event.pull_request.head.ref }}",
- "pr": ${{ github.event.pull_request.number }}
- }
diff --git a/.github/workflows/ecosystem-ci-trigger.yml b/.github/workflows/ecosystem-ci-trigger.yml
index ce7bf04136ac..71df3242e8f1 100644
--- a/.github/workflows/ecosystem-ci-trigger.yml
+++ b/.github/workflows/ecosystem-ci-trigger.yml
@@ -9,6 +9,7 @@ jobs:
runs-on: ubuntu-latest
if: github.repository == 'sveltejs/svelte' && github.event.issue.pull_request && startsWith(github.event.comment.body, '/ecosystem-ci run')
steps:
+ - uses: GitHubSecurityLab/actions-permissions/monitor@v1
- uses: actions/github-script@v6
with:
script: |
diff --git a/.github/workflows/pkg.pr.new-comment.yml b/.github/workflows/pkg.pr.new-comment.yml
index 1698a456d3df..3f1fca5a0bea 100644
--- a/.github/workflows/pkg.pr.new-comment.yml
+++ b/.github/workflows/pkg.pr.new-comment.yml
@@ -6,11 +6,15 @@ on:
types:
- completed
+permissions:
+ pull-requests: write
+
jobs:
build:
name: 'Update comment'
runs-on: ubuntu-latest
steps:
+ - uses: GitHubSecurityLab/actions-permissions/monitor@v1
- name: Download artifact
uses: actions/download-artifact@v4
with:
diff --git a/.github/workflows/pkg.pr.new.yml b/.github/workflows/pkg.pr.new.yml
index 4292ec900aba..90d219faae6a 100644
--- a/.github/workflows/pkg.pr.new.yml
+++ b/.github/workflows/pkg.pr.new.yml
@@ -3,12 +3,17 @@ on: [push, pull_request]
jobs:
build:
+ permissions: {}
+
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
+ - name: install corepack
+ run: npm i -g corepack@0.31.0
+
- run: corepack enable
- uses: actions/setup-node@v4
with:
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 1daef0b89cc3..6debe5662a88 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -17,6 +17,7 @@ jobs:
name: Release
runs-on: ubuntu-latest
steps:
+ - uses: GitHubSecurityLab/actions-permissions/monitor@v1
- name: Checkout Repo
uses: actions/checkout@v4
with:
diff --git a/.github/workflows/sync-request.yml b/.github/workflows/sync-request.yml
deleted file mode 100644
index de2ce7769238..000000000000
--- a/.github/workflows/sync-request.yml
+++ /dev/null
@@ -1,22 +0,0 @@
-# https://github.com/sveltejs/svelte.dev/blob/main/apps/svelte.dev/scripts/sync-docs/README.md
-name: Sync request
-
-on:
- push:
- branches:
- - main
-
-jobs:
- dispatch:
- runs-on: ubuntu-latest
- steps:
- - name: Repository Dispatch
- uses: peter-evans/repository-dispatch@v3
- with:
- token: ${{ secrets.SYNC_REQUEST_TOKEN }}
- repository: sveltejs/svelte.dev
- event-type: sync-request
- client-payload: |-
- {
- "package": "svelte"
- }
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index dd7bbb476e48..0e2628f84f77 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -51,7 +51,7 @@ We use [GitHub issues](https://github.com/sveltejs/svelte/issues) for our public
If you have questions about using Svelte, contact us on Discord at [svelte.dev/chat](https://svelte.dev/chat), and we will do our best to answer your questions.
-If you see anything you'd like to be implemented, create a [feature request issue](https://github.com/sveltejs/svelte/issues/new?template=feature_request.yml)
+If you see anything you'd like to be implemented, create a [feature request issue](https://github.com/sveltejs/svelte/issues/new?template=feature_request.yml).
### Reporting new issues
@@ -62,8 +62,6 @@ When [opening a new issue](https://github.com/sveltejs/svelte/issues/new/choose)
## Pull requests
-> HEADS UP: Svelte 5 will likely change a lot on the compiler. For that reason, please don't open PRs that are large in scope, touch more than a couple of files etc. In other words, bug fixes are fine, but big feature PRs will likely not be merged.
-
### Proposing a change
If you would like to request a new feature or enhancement but are not yet thinking about opening a pull request, you can also file an issue with [feature template](https://github.com/sveltejs/svelte/issues/new?template=feature_request.yml).
diff --git a/LICENSE.md b/LICENSE.md
index abbace7bfe03..f872adf738de 100644
--- a/LICENSE.md
+++ b/LICENSE.md
@@ -1,4 +1,4 @@
-Copyright (c) 2016-2025 [these people](https://github.com/sveltejs/svelte/graphs/contributors)
+Copyright (c) 2016-2025 [Svelte Contributors](https://github.com/sveltejs/svelte/graphs/contributors)
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
diff --git a/README.md b/README.md
index cfb1328495d0..7ea71647521a 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,11 @@
-[](https://svelte.dev)
-
-[](LICENSE.md) [](https://svelte.dev/chat)
+
+
+
+
+
+
+
+[](LICENSE.md) [](https://svelte.dev/chat)
## What is Svelte?
diff --git a/assets/banner.png b/assets/banner.png
new file mode 100644
index 000000000000..3428b278bfa3
Binary files /dev/null and b/assets/banner.png differ
diff --git a/assets/banner_dark.png b/assets/banner_dark.png
new file mode 100644
index 000000000000..1adba40d8e2f
Binary files /dev/null and b/assets/banner_dark.png differ
diff --git a/benchmarking/benchmarks/reactivity/kairo/kairo_avoidable.js b/benchmarking/benchmarks/reactivity/kairo/kairo_avoidable.js
index 6b058cdc3c1d..9daea6de99cb 100644
--- a/benchmarking/benchmarks/reactivity/kairo/kairo_avoidable.js
+++ b/benchmarking/benchmarks/reactivity/kairo/kairo_avoidable.js
@@ -20,12 +20,12 @@ function setup() {
return {
destroy,
run() {
- $.flush_sync(() => {
+ $.flush(() => {
$.set(head, 1);
});
assert($.get(computed5) === 6);
for (let i = 0; i < 1000; i++) {
- $.flush_sync(() => {
+ $.flush(() => {
$.set(head, i);
});
assert($.get(computed5) === 6);
diff --git a/benchmarking/benchmarks/reactivity/kairo/kairo_broad.js b/benchmarking/benchmarks/reactivity/kairo/kairo_broad.js
index d1cde5958edf..8dc5710c87db 100644
--- a/benchmarking/benchmarks/reactivity/kairo/kairo_broad.js
+++ b/benchmarking/benchmarks/reactivity/kairo/kairo_broad.js
@@ -25,12 +25,12 @@ function setup() {
return {
destroy,
run() {
- $.flush_sync(() => {
+ $.flush(() => {
$.set(head, 1);
});
counter = 0;
for (let i = 0; i < 50; i++) {
- $.flush_sync(() => {
+ $.flush(() => {
$.set(head, i);
});
assert($.get(last) === i + 50);
diff --git a/benchmarking/benchmarks/reactivity/kairo/kairo_deep.js b/benchmarking/benchmarks/reactivity/kairo/kairo_deep.js
index 149457ede156..8690c85f864a 100644
--- a/benchmarking/benchmarks/reactivity/kairo/kairo_deep.js
+++ b/benchmarking/benchmarks/reactivity/kairo/kairo_deep.js
@@ -25,12 +25,12 @@ function setup() {
return {
destroy,
run() {
- $.flush_sync(() => {
+ $.flush(() => {
$.set(head, 1);
});
counter = 0;
for (let i = 0; i < iter; i++) {
- $.flush_sync(() => {
+ $.flush(() => {
$.set(head, i);
});
assert($.get(current) === len + i);
diff --git a/benchmarking/benchmarks/reactivity/kairo/kairo_diamond.js b/benchmarking/benchmarks/reactivity/kairo/kairo_diamond.js
index 958a1bcd7890..bf4e07ee8962 100644
--- a/benchmarking/benchmarks/reactivity/kairo/kairo_diamond.js
+++ b/benchmarking/benchmarks/reactivity/kairo/kairo_diamond.js
@@ -28,13 +28,13 @@ function setup() {
return {
destroy,
run() {
- $.flush_sync(() => {
+ $.flush(() => {
$.set(head, 1);
});
assert($.get(sum) === 2 * width);
counter = 0;
for (let i = 0; i < 500; i++) {
- $.flush_sync(() => {
+ $.flush(() => {
$.set(head, i);
});
assert($.get(sum) === (i + 1) * width);
diff --git a/benchmarking/benchmarks/reactivity/kairo/kairo_mux.js b/benchmarking/benchmarks/reactivity/kairo/kairo_mux.js
index b645051c09bf..fc252a27b5f8 100644
--- a/benchmarking/benchmarks/reactivity/kairo/kairo_mux.js
+++ b/benchmarking/benchmarks/reactivity/kairo/kairo_mux.js
@@ -22,13 +22,13 @@ function setup() {
destroy,
run() {
for (let i = 0; i < 10; i++) {
- $.flush_sync(() => {
+ $.flush(() => {
$.set(heads[i], i);
});
assert($.get(splited[i]) === i + 1);
}
for (let i = 0; i < 10; i++) {
- $.flush_sync(() => {
+ $.flush(() => {
$.set(heads[i], i * 2);
});
assert($.get(splited[i]) === i * 2 + 1);
diff --git a/benchmarking/benchmarks/reactivity/kairo/kairo_repeated.js b/benchmarking/benchmarks/reactivity/kairo/kairo_repeated.js
index 53b85acd3766..3bee06ca0e8f 100644
--- a/benchmarking/benchmarks/reactivity/kairo/kairo_repeated.js
+++ b/benchmarking/benchmarks/reactivity/kairo/kairo_repeated.js
@@ -25,13 +25,13 @@ function setup() {
return {
destroy,
run() {
- $.flush_sync(() => {
+ $.flush(() => {
$.set(head, 1);
});
assert($.get(current) === size);
counter = 0;
for (let i = 0; i < 100; i++) {
- $.flush_sync(() => {
+ $.flush(() => {
$.set(head, i);
});
assert($.get(current) === i * size);
diff --git a/benchmarking/benchmarks/reactivity/kairo/kairo_triangle.js b/benchmarking/benchmarks/reactivity/kairo/kairo_triangle.js
index b9e2ad9fa4a3..11a419a52e7b 100644
--- a/benchmarking/benchmarks/reactivity/kairo/kairo_triangle.js
+++ b/benchmarking/benchmarks/reactivity/kairo/kairo_triangle.js
@@ -38,13 +38,13 @@ function setup() {
destroy,
run() {
const constant = count(width);
- $.flush_sync(() => {
+ $.flush(() => {
$.set(head, 1);
});
assert($.get(sum) === constant);
counter = 0;
for (let i = 0; i < 100; i++) {
- $.flush_sync(() => {
+ $.flush(() => {
$.set(head, i);
});
assert($.get(sum) === constant - width + i * width);
diff --git a/benchmarking/benchmarks/reactivity/kairo/kairo_unstable.js b/benchmarking/benchmarks/reactivity/kairo/kairo_unstable.js
index 0e783732dc67..54eb732cb29d 100644
--- a/benchmarking/benchmarks/reactivity/kairo/kairo_unstable.js
+++ b/benchmarking/benchmarks/reactivity/kairo/kairo_unstable.js
@@ -25,13 +25,13 @@ function setup() {
return {
destroy,
run() {
- $.flush_sync(() => {
+ $.flush(() => {
$.set(head, 1);
});
assert($.get(current) === 40);
counter = 0;
for (let i = 0; i < 100; i++) {
- $.flush_sync(() => {
+ $.flush(() => {
$.set(head, i);
});
}
diff --git a/benchmarking/benchmarks/reactivity/mol_bench.js b/benchmarking/benchmarks/reactivity/mol_bench.js
index c9f492f61967..536b078d74a4 100644
--- a/benchmarking/benchmarks/reactivity/mol_bench.js
+++ b/benchmarking/benchmarks/reactivity/mol_bench.js
@@ -51,11 +51,11 @@ function setup() {
*/
run(i) {
res.length = 0;
- $.flush_sync(() => {
+ $.flush(() => {
$.set(B, 1);
$.set(A, 1 + i * 2);
});
- $.flush_sync(() => {
+ $.flush(() => {
$.set(A, 2 + i * 2);
$.set(B, 2);
});
diff --git a/benchmarking/compare/index.js b/benchmarking/compare/index.js
index a5fc6d10a9a1..9d8d279c353a 100644
--- a/benchmarking/compare/index.js
+++ b/benchmarking/compare/index.js
@@ -2,7 +2,6 @@ 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');
diff --git a/benchmarking/compare/runner.js b/benchmarking/compare/runner.js
index 6fa58e2bacf3..a2e864637969 100644
--- a/benchmarking/compare/runner.js
+++ b/benchmarking/compare/runner.js
@@ -1,7 +1,7 @@
-import { benchmarks } from '../benchmarks.js';
+import { reactivity_benchmarks } from '../benchmarks/reactivity/index.js';
const results = [];
-for (const benchmark of benchmarks) {
+for (const benchmark of reactivity_benchmarks) {
const result = await benchmark();
console.error(result.benchmark);
results.push(result);
diff --git a/documentation/docs/01-introduction/02-getting-started.md b/documentation/docs/01-introduction/02-getting-started.md
index e035e6d6df99..c7351729ff17 100644
--- a/documentation/docs/01-introduction/02-getting-started.md
+++ b/documentation/docs/01-introduction/02-getting-started.md
@@ -2,7 +2,7 @@
title: Getting started
---
-We recommend using [SvelteKit](../kit), the official application framework from the Svelte team powered by [Vite](https://vite.dev/):
+We recommend using [SvelteKit](../kit), which lets you [build almost anything](../kit/project-types). It's the official application framework from the Svelte team and powered by [Vite](https://vite.dev/). Create a new project with:
```bash
npx sv create myapp
@@ -15,7 +15,9 @@ Don't worry if you don't know Svelte yet! You can ignore all the nice features S
## Alternatives to SvelteKit
-You can also use Svelte directly with Vite by running `npm create vite@latest` and selecting the `svelte` option. With this, `npm run build` will generate HTML, JS and CSS files inside the `dist` directory using [vite-plugin-svelte](https://github.com/sveltejs/vite-plugin-svelte). In most cases, you will probably need to [choose a routing library](faq#Is-there-a-router) as well.
+You can also use Svelte directly with Vite by running `npm create vite@latest` and selecting the `svelte` option. With this, `npm run build` will generate HTML, JS, and CSS files inside the `dist` directory using [vite-plugin-svelte](https://github.com/sveltejs/vite-plugin-svelte). In most cases, you will probably need to [choose a routing library](faq#Is-there-a-router) as well.
+
+>[!NOTE] Vite is often used in standalone mode to build [single page apps (SPAs)](../kit/glossary#SPA), which you can also [build with SvelteKit](../kit/single-page-apps).
There are also plugins for [Rollup](https://github.com/sveltejs/rollup-plugin-svelte), [Webpack](https://github.com/sveltejs/svelte-loader) [and a few others](https://sveltesociety.dev/packages?category=build-plugins), but we recommend Vite.
diff --git a/documentation/docs/02-runes/01-what-are-runes.md b/documentation/docs/02-runes/01-what-are-runes.md
index dc163ebdf157..59c371eb4909 100644
--- a/documentation/docs/02-runes/01-what-are-runes.md
+++ b/documentation/docs/02-runes/01-what-are-runes.md
@@ -2,7 +2,7 @@
title: What are runes?
---
-> [!NOTE] **rune** /roΝon/ _noun_
+> [!NOTE] **rune** /ruΛn/ _noun_
>
> A letter or mark used as a mystical or magic symbol.
diff --git a/documentation/docs/02-runes/03-$derived.md b/documentation/docs/02-runes/03-$derived.md
index 24ab643b68f6..2464aa929550 100644
--- a/documentation/docs/02-runes/03-$derived.md
+++ b/documentation/docs/02-runes/03-$derived.md
@@ -52,6 +52,48 @@ Anything read synchronously inside the `$derived` expression (or `$derived.by` f
To exempt a piece of state from being treated as a dependency, use [`untrack`](svelte#untrack).
+## Overriding derived values
+
+Derived expressions are recalculated when their dependencies change, but you can temporarily override their values by reassigning them (unless they are declared with `const`). This can be useful for things like _optimistic UI_, where a value is derived from the 'source of truth' (such as data from your server) but you'd like to show immediate feedback to the user:
+
+```svelte
+
+
+𧑠{likes}
+```
+
+> [!NOTE] Prior to Svelte 5.25, deriveds were read-only.
+
+## Deriveds and reactivity
+
+Unlike `$state`, which converts objects and arrays to [deeply reactive proxies]($state#Deep-state), `$derived` values are left as-is. For example, [in a case like this](/playground/untitled#H4sIAAAAAAAAE4VU22rjMBD9lUHd3aaQi9PdstS1A3t5XvpQ2Ic4D7I1iUUV2UjjNMX431eS7TRdSosxgjMzZ45mjt0yzffIYibvy0ojFJWqDKCQVBk2ZVup0LJ43TJ6rn2aBxw-FP2o67k9oCKP5dziW3hRaUJNjoYltjCyplWmM1JIIAn3FlL4ZIkTTtYez6jtj4w8WwyXv9GiIXiQxLVs9pfTMR7EuoSLIuLFbX7Z4930bZo_nBrD1bs834tlfvsBz9_SyX6PZXu9XaL4gOWn4sXjeyzftv4ZWfyxubpzxzg6LfD4MrooxELEosKCUPigQCMPKCZh0OtQE1iSxcsmdHuBvCiHZXALLXiN08EL3RRkaJ_kDVGle0HcSD5TPEeVtj67O4Nrg9aiSNtBY5oODJkrL5QsHtN2cgXp6nSJMWzpWWGasdlsGEMbzi5jPr5KFr0Ep7pdeM2-TCelCddIhDxAobi1jqF3cMaC1RKp64bAW9iFAmXGIHfd4wNXDabtOLN53w8W53VvJoZLh7xk4Rr3CoL-UNoLhWHrT1JQGcM17u96oES5K-kc2XOzkzqGCKL5De79OUTyyrg1zgwXsrEx3ESfx4Bz0M5UjVMHB24mw9SuXtXFoN13fYKOM1tyUT3FbvbWmSWCZX2Er-41u5xPoml45svRahl9Wb9aasbINJixDZwcPTbyTLZSUsAvrg_cPuCR7s782_WU8343Y72Qtlb8OYatwuOQvuN13M_hJKNfxann1v1U_B1KZ_D_mzhzhz24fw85CSz2irtN9w9HshBK7AQAAA==)...
+
+```svelte
+let items = $state([...]);
+
+let index = $state(0);
+let selected = $derived(items[index]);
+```
+
+...you can change (or `bind:` to) properties of `selected` and it will affect the underlying `items` array. If `items` was _not_ deeply reactive, mutating `selected` would have no effect.
+
## Update propagation
Svelte uses something called _push-pull reactivity_ β when state is updated, everything that depends on the state (whether directly or indirectly) is immediately notified of the change (the 'push'), but derived values are not re-evaluated until they are actually read (the 'pull').
diff --git a/documentation/docs/02-runes/04-$effect.md b/documentation/docs/02-runes/04-$effect.md
index c48a82f9a5b6..ae1a2146c9d4 100644
--- a/documentation/docs/02-runes/04-$effect.md
+++ b/documentation/docs/02-runes/04-$effect.md
@@ -2,15 +2,11 @@
title: $effect
---
-Effects are what make your application _do things_. When Svelte runs an effect function, it tracks which pieces of state (and derived state) are accessed (unless accessed inside [`untrack`](svelte#untrack)), and re-runs the function when that state later changes.
+Effects are functions that run when state updates, and can be used for things like calling third-party libraries, drawing on `` elements, or making network requests. They only run in the browser, not during server-side rendering.
-Most of the effects in a Svelte app are created by Svelte itself β they're the bits that update the text in `hello {name}! ` when `name` changes, for example.
+Generally speaking, you should _not_ update state inside effects, as it will make code more convoluted and will often lead to never-ending update cycles. If you find yourself doing so, see [when not to use `$effect`](#When-not-to-use-$effect) to learn about alternative approaches.
-But you can also create your own effects with the `$effect` rune, which is useful when you need to synchronize an external system (whether that's a library, or a `` element, or something across a network) with state inside your Svelte app.
-
-> [!NOTE] Avoid overusing `$effect`! When you do too much work in effects, code often becomes difficult to understand and maintain. See [when not to use `$effect`](#When-not-to-use-$effect) to learn about alternative approaches.
-
-Your effects run after the component has been mounted to the DOM, and in a [microtask](https://developer.mozilla.org/en-US/docs/Web/API/HTML_DOM_API/Microtask_guide) after state changes ([demo](/playground/untitled#H4sIAAAAAAAAE31S246bMBD9lZF3pSRSAqTVvrCAVPUP2sdSKY4ZwJJjkD0hSVH-vbINuWxXfQH5zMyZc2ZmZLVUaFn6a2R06ZGlHmBrpvnBvb71fWQHVOSwPbf4GS46TajJspRlVhjZU1HqkhQSWPkHIYdXS5xw-Zas3ueI6FRn7qHFS11_xSRZhIxbFtcDtw7SJb1iXaOg5XIFeQGjzyPRaevYNOGZIJ8qogbpe8CWiy_VzEpTXiQUcvPDkSVrSNZz1UlW1N5eLcqmpdXUvaQ4BmqlhZNUCgxuzFHDqUWNAxrYeUM76AzsnOsdiJbrBp_71lKpn3RRbii-4P3f-IMsRxS-wcDV_bL4PmSdBa2wl7pKnbp8DMgVvJm8ZNskKRkEM_OzyOKQFkgqOYBQ3Nq89Ns0nbIl81vMFN-jKoLMTOr-SOBOJS-Z8f5Y6D1wdcR8dFqvEBdetK-PHwj-z-cH8oHPY54wRJ8Ys7iSQ3Bg3VA9azQbmC9k35kKzYa6PoVtfwbbKVnBixBiGn7Pq0rqJoUtHiCZwAM3jdTPWCVtr_glhVrhecIa3vuksJ_b7TqFs4DPyriSjd5IwoNNQaAmNI-ESfR2p8zimzvN1swdCkvJHPH6-_oX8o1SgcIDAAA=)):
+You can create an effect with the `$effect` rune ([demo](/playground/untitled#H4sIAAAAAAAAE31S246bMBD9lZF3pSRSAqTVvrCAVPUP2sdSKY4ZwJJjkD0hSVH-vbINuWxXfQH5zMyZc2ZmZLVUaFn6a2R06ZGlHmBrpvnBvb71fWQHVOSwPbf4GS46TajJspRlVhjZU1HqkhQSWPkHIYdXS5xw-Zas3ueI6FRn7qHFS11_xSRZhIxbFtcDtw7SJb1iXaOg5XIFeQGjzyPRaevYNOGZIJ8qogbpe8CWiy_VzEpTXiQUcvPDkSVrSNZz1UlW1N5eLcqmpdXUvaQ4BmqlhZNUCgxuzFHDqUWNAxrYeUM76AzsnOsdiJbrBp_71lKpn3RRbii-4P3f-IMsRxS-wcDV_bL4PmSdBa2wl7pKnbp8DMgVvJm8ZNskKRkEM_OzyOKQFkgqOYBQ3Nq89Ns0nbIl81vMFN-jKoLMTOr-SOBOJS-Z8f5Y6D1wdcR8dFqvEBdetK-PHwj-z-cH8oHPY54wRJ8Ys7iSQ3Bg3VA9azQbmC9k35kKzYa6PoVtfwbbKVnBixBiGn7Pq0rqJoUtHiCZwAM3jdTPWCVtr_glhVrhecIa3vuksJ_b7TqFs4DPyriSjd5IwoNNQaAmNI-ESfR2p8zimzvN1swdCkvJHPH6-_oX8o1SgcIDAAA=)):
```svelte
-
+ spent, updateSpent} max={total} />
{spent}/{total} spent
-
+ left, updateLeft} max={total} />
{left}/{total} left
```
-If you need to use bindings, for whatever reason (for example when you want some kind of "writable `$derived`"), consider using getters and setters to synchronise state ([demo](/playground/untitled#H4sIAAAAAAAACpWRwW6DMBBEf8WyekikFOihFwcq9TvqHkyyQUjGsfCCQMj_XnvBNKpy6Qn2DTOD1wu_tRocF18Lx9kCFwT4iRvVxenT2syNoDGyWjl4xi93g2AwxPDSXfrW4oc0EjUgwzsqzSr2VhTnxJwNHwf24lAhHIpjVDZNwy1KS5wlNoGMSg9wOCYksQccerMlv65p51X0p_Xpdt_4YEy9yTkmV3z4MJT579-bUqsaNB2kbI0dwlnCgirJe2UakJzVrbkKaqkWivasU1O1ULxnOVk3JU-Uxti0p_-vKO4no_enbQ_yXhnZn0aHs4b1jiJMK7q2zmo1C3bTMG3LaZQVrMjeoSPgaUtkDxePMCEX2Ie6b_8D4WyJJEwCAAA=)):
-
-```svelte
-
-
-
-
- {spent}/{total} spent
-
-
-
-
- {left.value}/{total} left
-
-```
-
If you absolutely have to update `$state` within an effect and run into an infinite loop because you read and write to the same `$state`, use [untrack](svelte#untrack).
diff --git a/documentation/docs/02-runes/05-$props.md b/documentation/docs/02-runes/05-$props.md
index 4b1775bf5a61..f300fb239d77 100644
--- a/documentation/docs/02-runes/05-$props.md
+++ b/documentation/docs/02-runes/05-$props.md
@@ -199,3 +199,24 @@ You can, of course, separate the type declaration from the annotation:
> [!NOTE] Interfaces for native DOM elements are provided in the `svelte/elements` module (see [Typing wrapper components](typescript#Typing-wrapper-components))
Adding types is recommended, as it ensures that people using your component can easily discover which props they should provide.
+
+
+## `$props.id()`
+
+This rune, added in version 5.20.0, generates an ID that is unique to the current component instance. When hydrating a server-rendered component, the value will be consistent between server and client.
+
+This is useful for linking elements via attributes like `for` and `aria-labelledby`.
+
+```svelte
+
+
+
+```
\ No newline at end of file
diff --git a/documentation/docs/02-runes/06-$bindable.md b/documentation/docs/02-runes/06-$bindable.md
index 14bc8ddbec05..c12c2bf4903e 100644
--- a/documentation/docs/02-runes/06-$bindable.md
+++ b/documentation/docs/02-runes/06-$bindable.md
@@ -33,7 +33,7 @@ Now, a component that uses `` can add the [`bind:`](bind) directive
```svelte
-/// App.svelte
+/// file: App.svelte
-
+
```
Components also support `bind:this`, allowing you to interact with component instances programmatically.
diff --git a/documentation/docs/03-template-syntax/18-class.md b/documentation/docs/03-template-syntax/18-class.md
index 880a34e9ec53..1ea4a208dfdc 100644
--- a/documentation/docs/03-template-syntax/18-class.md
+++ b/documentation/docs/03-template-syntax/18-class.md
@@ -71,6 +71,18 @@ The user of this component has the same flexibility to use a mixture of objects,
```
+Svelte also exposes the `ClassValue` type, which is the type of value that the `class` attribute on elements accept. This is useful if you want to use a type-safe class name in component props:
+
+```svelte
+
+
+...
+```
+
## The `class:` directive
Prior to Svelte 5.16, the `class:` directive was the most convenient way to set classes on elements conditionally.
diff --git a/documentation/docs/04-styling/01-scoped-styles.md b/documentation/docs/04-styling/01-scoped-styles.md
index f870d0a5b83c..eae26d0cb1ca 100644
--- a/documentation/docs/04-styling/01-scoped-styles.md
+++ b/documentation/docs/04-styling/01-scoped-styles.md
@@ -33,10 +33,7 @@ If a component defines `@keyframes`, the name is scoped to the component using t
/* these keyframes are only accessible inside this component */
@keyframes bounce {
- /* ... *.
+ /* ... */
}
```
-
-
-
diff --git a/documentation/docs/05-special-elements/01-svelte-boundary.md b/documentation/docs/05-special-elements/01-svelte-boundary.md
index 15f249a7711b..f5439b4b83e7 100644
--- a/documentation/docs/05-special-elements/01-svelte-boundary.md
+++ b/documentation/docs/05-special-elements/01-svelte-boundary.md
@@ -13,7 +13,7 @@ Boundaries allow you to guard against errors in part of your app from breaking t
If an error occurs while rendering or updating the children of a ``, or running any [`$effect`]($effect) functions contained therein, the contents will be removed.
-Errors occurring outside the rendering process (for example, in event handlers) are _not_ caught by error boundaries.
+Errors occurring outside the rendering process (for example, in event handlers or after a `setTimeout` or async work) are _not_ caught by error boundaries.
## Properties
diff --git a/documentation/docs/06-runtime/02-context.md b/documentation/docs/06-runtime/02-context.md
index 30799215b6eb..4204bcfe6d6a 100644
--- a/documentation/docs/06-runtime/02-context.md
+++ b/documentation/docs/06-runtime/02-context.md
@@ -2,129 +2,137 @@
title: Context
---
-
+Context allows components to access values owned by parent components without passing them down as props (potentially through many layers of intermediate components, known as 'prop-drilling'). The parent component sets context with `setContext(key, value)`...
-Most state is component-level state that lives as long as its component lives. There's also section-wide or app-wide state however, which also needs to be handled somehow.
-
-The easiest way to do that is to create global state and just import that.
+```svelte
+
+
```
+...and the child retrieves it with `getContext`:
+
```svelte
-
+
+
+{message}, inside Child.svelte
```
-This has a few drawbacks though:
+This is particularly useful when `Parent.svelte` is not directly aware of `Child.svelte`, but instead renders it as part of a `children` [snippet](snippet) ([demo](/playground/untitled#H4sIAAAAAAAAE42Q3W6DMAyFX8WyJgESK-oto6hTX2D3YxcM3IIUQpR40yqUd58CrCXsp7tL7HNsf2dAWXaEKR56yfTBGOOxFWQwfR6Qz8q1XAHjL-GjUhvzToJd7bU09FO9ctMkG0wxM5VuFeeFLLjtVK8ZnkpNkuGo-w6CTTJ9Z3PwsBAemlbUF934W8iy5DpaZtOUcU02-ZLcaS51jHEkTFm_kY1_wfOO8QnXrb8hBzDEc6pgZ4gFoyz4KgiD7nxfTe8ghqAhIfrJ46cTzVZBbkPlODVJsLCDO6V7ZcJoncyw1yRr0hd1GNn_ZbEM3I9i1bmVxOlWElUvDUNHxpQngt3C4CXzjS1rtvkw22wMrTRtTbC8Lkuabe7jvthPPe3DofYCAAA=)):
+
+```svelte
+
+
+
+```
-- it only safely works when your global state is only used client-side - for example, when you're building a single page application that does not render any of your components on the server. If your state ends up being managed and updated on the server, it could end up being shared between sessions and/or users, causing bugs
-- it may give the false impression that certain state is global when in reality it should only used in a certain part of your app
+The key (`'my-context'`, in the example above) and the context itself can be any JavaScript value.
-To solve these drawbacks, Svelte provides a few `context` primitives which alleviate these problems.
+In addition to [`setContext`](svelte#setContext) and [`getContext`](svelte#getContext), Svelte exposes [`hasContext`](svelte#hasContext) and [`getAllContexts`](svelte#getAllContexts) functions.
-## Setting and getting context
+## Using context with state
-To associate an arbitrary object with the current component, use `setContext`.
+You can store reactive state in context ([demo](/playground/untitled#H4sIAAAAAAAAE41R0W6DMAz8FSuaBNUQdK8MkKZ-wh7HHihzu6hgosRMm1D-fUpSVNq12x4iEvvOx_kmQU2PIhfP3DCCJGgHYvxkkYid7NCI_GUS_KUcxhVEMjOelErNB3bsatvG4LW6n0ZsRC4K02qpuKqpZtmrQTNMYJA3QRAs7PTQQxS40eMCt3mX3duxnWb-lS5h7nTI0A4jMWoo4c44P_Hku-zrOazdy64chWo-ScfRkRgl8wgHKrLTH1OxHZkHgoHaTraHcopXUFYzPPVfuC_hwQaD1GrskdiNCdQwJljJqlvXfyqVsA5CGg0uRUQifHw56xFtciO75QrP07vo_JXf_tf8yK2ezDKY_ZWt_1y2qqYzv7bI1IW1V_sN19m-07wCAAA=))...
```svelte
+
+ counter.count += 1}>
+ increment
+
+
+
+
+
```
-The context is then available to children of the component (including slotted content) with `getContext`.
+...though note that if you _reassign_ `counter` instead of updating it, you will 'break the link' β in other words instead of this...
```svelte
-
+ counter = { count: 0 }}>
+ reset
+
```
-`setContext` and `getContext` solve the above problems:
+...you must do this:
-- the state is not global, it's scoped to the component. That way it's safe to render your components on the server and not leak state
-- it's clear that the state is not global but rather scoped to a specific component tree and therefore can't be used in other parts of your app
+```svelte
+ +++counter.count = 0+++}>
+ reset
+
+```
-> [!NOTE] `setContext`/`getContext` must be called during component initialisation.
+Svelte will warn you if you get it wrong.
-Context is not inherently reactive. If you need reactive values in context then you can pass a `$state` object into context, whose properties _will_ be reactive.
+## Type-safe context
-```svelte
-
-
+```js
+/// file: context.js
+// @filename: ambient.d.ts
+interface User {}
- value.count++}>increment
-```
+// @filename: index.js
+// ---cut---
+import { getContext, setContext } from 'svelte';
-```svelte
-
-
+/** @param {User} user */
+export function setUserContext(user) {
+ setContext(key, user);
+}
-Count is {value.count}
+export function getUserContext() {
+ return /** @type {User} */ (getContext(key));
+}
```
-To check whether a given `key` has been set in the context of a parent component, use `hasContext`.
+## Replacing global state
-```svelte
-
+ // ...
+});
```
-You can also retrieve the whole context map that belongs to the closest parent component using `getAllContexts`. This is useful, for example, if you programmatically create a component and want to pass the existing context to it.
+In many cases this is perfectly fine, but there is a risk: if you mutate the state during server-side rendering (which is discouraged, but entirely possible!)...
```svelte
+
```
-## Encapsulating context interactions
-
-The above methods are very unopinionated about how to use them. When your app grows in scale, it's worthwhile to encapsulate setting and getting the context into functions and properly type them.
-
-```ts
-// @errors: 2304
-import { getContext, setContext } from 'svelte';
-
-let userKey = Symbol('user');
-
-export function setUserContext(user: User) {
- setContext(userKey, user);
-}
-
-export function getUserContext(): User {
- return getContext(userKey) as User;
-}
-```
+...then the data may be accessible by the _next_ user. Context solves this problem because it is not shared between requests.
diff --git a/documentation/docs/06-runtime/03-lifecycle-hooks.md b/documentation/docs/06-runtime/03-lifecycle-hooks.md
index a3dbe04b0029..f051c46d73ba 100644
--- a/documentation/docs/06-runtime/03-lifecycle-hooks.md
+++ b/documentation/docs/06-runtime/03-lifecycle-hooks.md
@@ -45,8 +45,6 @@ If a function is returned from `onMount`, it will be called when the component i
## `onDestroy`
-> EXPORT_SNIPPET: svelte#onDestroy
-
Schedules a callback to run immediately before the component is unmounted.
Out of `onMount`, `beforeUpdate`, `afterUpdate` and `onDestroy`, this is the only one that runs inside a server-side component.
@@ -149,7 +147,7 @@ With runes, we can use `$effect.pre`, which behaves the same as `$effect` but ru
}
function toggle() {
- toggleValue = !toggleValue;
+ theme = theme === 'dark' ? 'light' : 'dark';
}
diff --git a/documentation/docs/07-misc/07-v5-migration-guide.md b/documentation/docs/07-misc/07-v5-migration-guide.md
index 09ea84f26c8a..e502b7921a1e 100644
--- a/documentation/docs/07-misc/07-v5-migration-guide.md
+++ b/documentation/docs/07-misc/07-v5-migration-guide.md
@@ -10,13 +10,13 @@ You don't have to migrate to the new syntax right away - Svelte 5 still supports
At the heart of Svelte 5 is the new runes API. Runes are basically compiler instructions that inform Svelte about reactivity. Syntactically, runes are functions starting with a dollar-sign.
-### let -> $state
+### let β $state
In Svelte 4, a `let` declaration at the top level of a component was implicitly reactive. In Svelte 5, things are more explicit: a variable is reactive when created using the `$state` rune. Let's migrate the counter to runes mode by wrapping the counter in `$state`:
```svelte
```
@@ -25,14 +25,14 @@ Nothing else changes. `count` is still the number itself, and you read and write
> [!DETAILS] Why we did this
> `let` being implicitly reactive at the top level worked great, but it meant that reactivity was constrained - a `let` declaration anywhere else was not reactive. This forced you to resort to using stores when refactoring code out of the top level of components for reuse. This meant you had to learn an entirely separate reactivity model, and the result often wasn't as nice to work with. Because reactivity is more explicit in Svelte 5, you can keep using the same API outside the top level of components. Head to [the tutorial](/tutorial) to learn more.
-### $: -> $derived/$effect
+### $: β $derived/$effect
In Svelte 4, a `$:` statement at the top level of a component could be used to declare a derivation, i.e. state that is entirely defined through a computation of other state. In Svelte 5, this is achieved using the `$derived` rune:
```svelte
```
@@ -42,7 +42,8 @@ A `$:` statement could also be used to create side effects. In Svelte 5, this is
```svelte
```
+Note that [when `$effect` runs is different]($effect#Understanding-dependencies) than when `$:` runs.
+
> [!DETAILS] Why we did this
> `$:` was a great shorthand and easy to get started with: you could slap a `$:` in front of most code and it would somehow work. This intuitiveness was also its drawback the more complicated your code became, because it wasn't as easy to reason about. Was the intent of the code to create a derivation, or a side effect? With `$derived` and `$effect`, you have a bit more up-front decision making to do (spoiler alert: 90% of the time you want `$derived`), but future-you and other developers on your team will have an easier time.
>
@@ -71,14 +74,14 @@ A `$:` statement could also be used to create side effects. In Svelte 5, this is
> - executing dependencies as needed and therefore being immune to ordering problems
> - being TypeScript-friendly
-### export let -> $props
+### export let β $props
In Svelte 4, properties of a component were declared using `export let`. Each property was one declaration. In Svelte 5, all properties are declared through the `$props` rune, through destructuring:
```svelte
```
@@ -103,8 +106,8 @@ In Svelte 5, the `$props` rune makes this straightforward without any additional
```svelte
click me
@@ -190,9 +193,9 @@ This function is deprecated in Svelte 5. Instead, components should accept _call
```svelte
@@ -339,7 +342,31 @@ When spreading props, local event handlers must go _after_ the spread, or they r
In Svelte 4, content can be passed to components using slots. Svelte 5 replaces them with snippets which are more powerful and flexible, and as such slots are deprecated in Svelte 5.
-They continue to work, however, and you can mix and match snippets and slots in your components.
+They continue to work, however, and you can pass snippets to a component that uses slots:
+
+```svelte
+
+
+
+
+```
+
+```svelte
+
+
+
+
+ default child content
+
+ {#snippet foo({ message })}
+ message from child: {message}
+ {/snippet}
+
+```
+
+(The reverse is not true β you cannot pass slotted content to a component that uses [`{@render ...}`](/docs/svelte/@render) tags.)
When using custom elements, you should still use ` ` like before. In a future version, when Svelte removes its internal version of slots, it will leave those slots as-is, i.e. output a regular DOM tag instead of transforming it.
@@ -440,11 +467,11 @@ By now you should have a pretty good understanding of the before/after and how t
We thought the same, which is why we provide a migration script to do most of the migration automatically. You can upgrade your project by using `npx sv migrate svelte-5`. This will do the following things:
- bump core dependencies in your `package.json`
-- migrate to runes (`let` -> `$state` etc)
-- migrate to event attributes for DOM elements (`on:click` -> `onclick`)
-- migrate slot creations to render tags (` ` -> `{@render children()}`)
-- migrate slot usages to snippets (`...
` -> `{#snippet x()}...
{/snippet}`)
-- migrate obvious component creations (`new Component(...)` -> `mount(Component, ...)`)
+- migrate to runes (`let` β `$state` etc)
+- migrate to event attributes for DOM elements (`on:click` β `onclick`)
+- migrate slot creations to render tags (` ` β `{@render children()}`)
+- migrate slot usages to snippets (`...
` β `{#snippet x()}...
{/snippet}`)
+- migrate obvious component creations (`new Component(...)` β `mount(Component, ...)`)
You can also migrate a single component in VS Code through the `Migrate Component to Svelte 5 Syntax` command, or in our Playground through the `Migrate` button.
@@ -597,28 +624,57 @@ export declare const MyComponent: Component<{
To declare that a component of a certain type is required:
+```js
+import { ComponentA, ComponentB } from 'component-library';
+---import type { SvelteComponent } from 'svelte';---
++++import type { Component } from 'svelte';+++
+
+---let C: typeof SvelteComponent<{ foo: string }> = $state(---
++++let C: Component<{ foo: string }> = $state(+++
+ Math.random() ? ComponentA : ComponentB
+);
+```
+
+The two utility types `ComponentEvents` and `ComponentType` are also deprecated. `ComponentEvents` is obsolete because events are defined as callback props now, and `ComponentType` is obsolete because the new `Component` type is the component type already (i.e. `ComponentType>` is equivalent to `Component<{ prop: string }>`).
+
+### bind:this changes
+
+Because components are no longer classes, using `bind:this` no longer returns a class instance with `$set`, `$on` and `$destroy` methods on it. It only returns the instance exports (`export function/const`) and, if you're using the `accessors` option, a getter/setter-pair for each property.
+
+## `` is no longer necessary
+
+In Svelte 4, components are _static_ β if you render ``, and the value of `Thing` changes, [nothing happens](/playground/7f1fa24f0ab44c1089dcbb03568f8dfa?version=4.2.18). To make it dynamic you had to use ``.
+
+This is no longer true in Svelte 5:
+
```svelte
-
-
+
+ A
+ B
+
+
+
+
+
```
+While migrating, keep in mind that your component's name should be capitalized (`Thing`) to distinguish it from elements, unless using dot notation.
-The two utility types `ComponentEvents` and `ComponentType` are also deprecated. `ComponentEvents` is obsolete because events are defined as callback props now, and `ComponentType` is obsolete because the new `Component` type is the component type already (e.g. `ComponentType>` == `Component<{ prop: string }>`).
+### Dot notation indicates a component
-### bind:this changes
+In Svelte 4, `` would create an element with a tag name of `"foo.bar"`. In Svelte 5, `foo.bar` is treated as a component instead. This is particularly useful inside `each` blocks:
-Because components are no longer classes, using `bind:this` no longer returns a class instance with `$set`, `$on` and `$destroy` methods on it. It only returns the instance exports (`export function/const`) and, if you're using the `accessors` option, a getter/setter-pair for each property.
+```svelte
+{#each items as item}
+
+{/each}
+```
## Whitespace handling changed
@@ -653,16 +709,6 @@ The `legacy` compiler option, which generated bulkier but IE-friendly code, no l
Content inside component tags becomes a snippet prop called `children`. You cannot have a separate prop by that name.
-## Dot notation indicates a component
-
-In Svelte 4, `` would create an element with a tag name of `"foo.bar"`. In Svelte 5, `foo.bar` is treated as a component instead. This is particularly useful inside `each` blocks:
-
-```svelte
-{#each items as item}
-
-{/each}
-```
-
## Breaking changes in runes mode
Some breaking changes only apply once your component is in runes mode.
@@ -679,51 +725,59 @@ If a bindable property has a default value (e.g. `let { foo = $bindable('bar') }
### `accessors` option is ignored
-Setting the `accessors` option to `true` makes properties of a component directly accessible on the component instance. In runes mode, properties are never accessible on the component instance. You can use component exports instead if you need to expose them.
+Setting the `accessors` option to `true` makes properties of a component directly accessible on the component instance.
-### `immutable` option is ignored
-
-Setting the `immutable` option has no effect in runes mode. This concept is replaced by how `$state` and its variations work.
+```svelte
+
-### Classes are no longer "auto-reactive"
+
+```
-In Svelte 4, doing the following triggered reactivity:
+In runes mode, properties are never accessible on the component instance. You can use component exports instead if you need to expose them.
```svelte
+```
- (foo.value = 1)}>{foo.value}
+Alternatively, if the place where they are instantiated is under your control, you can also make use of runes inside `.js/.ts` files by adjusting their ending to include `.svelte`, i.e. `.svelte.js` or `.svelte.ts`, and then use `$state`:
+
+```js
++++import { mount } from 'svelte';+++
+import App from './App.svelte'
+
+---const app = new App({ target: document.getElementById("app"), props: { foo: 'bar' } });
+app.foo = 'baz'---
++++const props = $state({ foo: 'bar' });
+const app = mount(App, { target: document.getElementById("app"), props });
+props.foo = 'baz';+++
```
-This is because the Svelte compiler treated the assignment to `foo.value` as an instruction to update anything that referenced `foo`. In Svelte 5, reactivity is determined at runtime rather than compile time, so you should define `value` as a reactive `$state` field on the `Foo` class. Wrapping `new Foo()` with `$state(...)` will have no effect β only vanilla objects and arrays are made deeply reactive.
+### `immutable` option is ignored
-### `` is no longer necessary
+Setting the `immutable` option has no effect in runes mode. This concept is replaced by how `$state` and its variations work.
-In Svelte 4, components are _static_ β if you render ``, and the value of `Thing` changes, [nothing happens](/playground/7f1fa24f0ab44c1089dcbb03568f8dfa?version=4.2.18). To make it dynamic you must use ``.
+### Classes are no longer "auto-reactive"
-This is no longer true in Svelte 5:
+In Svelte 4, doing the following triggered reactivity:
```svelte
-
- A
- B
-
-
-
-
-
+ (foo.value = 1)}>{foo.value}
```
+This is because the Svelte compiler treated the assignment to `foo.value` as an instruction to update anything that referenced `foo`. In Svelte 5, reactivity is determined at runtime rather than compile time, so you should define `value` as a reactive `$state` field on the `Foo` class. Wrapping `new Foo()` with `$state(...)` will have no effect β only vanilla objects and arrays are made deeply reactive.
+
### Touch and wheel events are passive
When using `onwheel`, `onmousewheel`, `ontouchstart` and `ontouchmove` event attributes, the handlers are [passive](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#using_passive_listeners) to align with browser defaults. This greatly improves responsiveness by allowing the browser to scroll the document immediately, rather than waiting to see if the event handler calls `event.preventDefault()`.
diff --git a/documentation/docs/07-misc/99-faq.md b/documentation/docs/07-misc/99-faq.md
index b56c27af86b6..ed5c6277c099 100644
--- a/documentation/docs/07-misc/99-faq.md
+++ b/documentation/docs/07-misc/99-faq.md
@@ -46,7 +46,7 @@ It will show up on hover.
- You can use markdown here.
- You can also use code blocks here.
- Usage:
- ```tsx
+ ```svelte
```
-->
@@ -96,7 +96,7 @@ However, you can use any router library. A lot of people use [page.js](https://g
If you prefer a declarative HTML approach, there's the isomorphic [svelte-routing](https://github.com/EmilTholin/svelte-routing) library and a fork of it called [svelte-navigator](https://github.com/mefechoel/svelte-navigator) containing some additional functionality.
-If you need hash-based routing on the client side, check out [svelte-spa-router](https://github.com/ItalyPaleAle/svelte-spa-router) or [abstract-state-router](https://github.com/TehShrike/abstract-state-router/).
+If you need hash-based routing on the client side, check out the [hash option](https://svelte.dev/docs/kit/configuration#router) in SvelteKit, [svelte-spa-router](https://github.com/ItalyPaleAle/svelte-spa-router), or [abstract-state-router](https://github.com/TehShrike/abstract-state-router/).
[Routify](https://routify.dev) is another filesystem-based router, similar to SvelteKit's router. Version 3 supports Svelte's native SSR.
diff --git a/documentation/docs/98-reference/.generated/client-errors.md b/documentation/docs/98-reference/.generated/client-errors.md
index 2c2e0707ea12..fd9419176d81 100644
--- a/documentation/docs/98-reference/.generated/client-errors.md
+++ b/documentation/docs/98-reference/.generated/client-errors.md
@@ -122,14 +122,39 @@ Property descriptors defined on `$state` objects must contain `value` and always
Cannot set prototype of `$state` object
```
-### state_unsafe_local_read
+### state_unsafe_mutation
```
-Reading state that was created inside the same derived is forbidden. Consider using `untrack` to read locally created state
+Updating state inside a derived or a template expression is forbidden. If the value should not be reactive, declare it without `$state`
```
-### state_unsafe_mutation
+This error occurs when state is updated while evaluating a `$derived`. You might encounter it while trying to 'derive' two pieces of state in one go:
+
+```svelte
+
+ count++}>{count}
+
+{count} is even: {even}
+{count} is odd: {odd}
```
-Updating state inside a derived or a template expression is forbidden. If the value should not be reactive, declare it without `$state`
+
+This is forbidden because it introduces instability: if `{count} is even: {even}
` is updated before `odd` is recalculated, `even` will be stale. In most cases the solution is to make everything derived:
+
+```js
+let count = 0;
+// ---cut---
+let even = $derived(count % 2 === 0);
+let odd = $derived(!even);
```
+
+If side-effects are unavoidable, use [`$effect`]($effect) instead.
diff --git a/documentation/docs/98-reference/.generated/compile-errors.md b/documentation/docs/98-reference/.generated/compile-errors.md
index c4989200075d..a8c39aaf9713 100644
--- a/documentation/docs/98-reference/.generated/compile-errors.md
+++ b/documentation/docs/98-reference/.generated/compile-errors.md
@@ -84,6 +84,12 @@ Attribute values containing `{...}` must be enclosed in quote marks, unless the
`bind:group` can only bind to an Identifier or MemberExpression
```
+### bind_group_invalid_snippet_parameter
+
+```
+Cannot `bind:group` to a snippet parameter
+```
+
### bind_invalid_expression
```
@@ -187,7 +193,7 @@ Cyclical dependency detected: %cycle%
### const_tag_invalid_placement
```
-`{@const}` must be the immediate child of `{#snippet}`, `{#if}`, `{:else if}`, `{:else}`, `{#each}`, `{:then}`, `{:catch}`, `` or ``
+`{@const}` must be the immediate child of `{#snippet}`, `{#if}`, `{:else if}`, `{:else}`, `{#each}`, `{:then}`, `{:catch}`, ``, ``
```
### constant_assignment
@@ -331,7 +337,39 @@ The $ prefix is reserved, and cannot be used for variables and imports
### each_item_invalid_assignment
```
-Cannot reassign or bind to each block argument in runes mode. Use the array and index variables instead (e.g. `array[i] = value` instead of `entry = value`)
+Cannot reassign or bind to each block argument in runes mode. Use the array and index variables instead (e.g. `array[i] = value` instead of `entry = value`, or `bind:value={array[i]}` instead of `bind:value={entry}`)
+```
+
+In legacy mode, it was possible to reassign or bind to the each block argument itself:
+
+```svelte
+
+
+{#each array as entry}
+
+ entry = 4}>change
+
+
+
+{/each}
+```
+
+This turned out to be buggy and unpredictable, particularly when working with derived values (such as `array.map(...)`), and as such is forbidden in runes mode. You can achieve the same outcome by using the index instead:
+
+```svelte
+
+
+{#each array as entry, i}
+
+ array[i] = 4}>change
+
+
+
+{/each}
```
### effect_invalid_placement
@@ -541,7 +579,13 @@ Unrecognised compiler option %keypath%
### props_duplicate
```
-Cannot use `$props()` more than once
+Cannot use `%rune%()` more than once
+```
+
+### props_id_invalid_placement
+
+```
+`$props.id()` can only be used at the top level of components as a variable declaration initializer
```
### props_illegal_name
@@ -616,6 +660,12 @@ Cannot access a computed property of a rune
`%name%` is not a valid rune
```
+### rune_invalid_spread
+
+```
+`%rune%` cannot be called with a spread argument
+```
+
### rune_invalid_usage
```
diff --git a/documentation/docs/99-legacy/30-legacy-svelte-component.md b/documentation/docs/99-legacy/30-legacy-svelte-component.md
index 3da2e3350e3f..a0407e58bfbf 100644
--- a/documentation/docs/99-legacy/30-legacy-svelte-component.md
+++ b/documentation/docs/99-legacy/30-legacy-svelte-component.md
@@ -2,7 +2,7 @@
title:
---
-In runes mode, `` will re-render if the value of `MyComponent` changes. See the [Svelte 5 migration guide](/docs/svelte/v5-migration-guide#Breaking-changes-in-runes-mode-svelte:component-is-no-longer-necessary) for an example.
+In runes mode, `` will re-render if the value of `MyComponent` changes. See the [Svelte 5 migration guide](/docs/svelte/v5-migration-guide#svelte:component-is-no-longer-necessary) for an example.
In legacy mode, it won't β we must use ``, which destroys and recreates the component instance when the value of its `this` expression changes:
diff --git a/package.json b/package.json
index 57dc4cdebedc..ad69bfc9cafb 100644
--- a/package.json
+++ b/package.json
@@ -7,7 +7,7 @@
"license": "MIT",
"packageManager": "pnpm@9.4.0",
"engines": {
- "pnpm": "^9.0.0"
+ "pnpm": ">=9.0.0"
},
"repository": {
"type": "git",
@@ -42,8 +42,8 @@
"prettier-plugin-svelte": "^3.1.2",
"svelte": "workspace:^",
"typescript": "^5.5.4",
- "typescript-eslint": "^8.2.0",
+ "typescript-eslint": "^8.24.0",
"v8-natives": "^1.2.5",
- "vitest": "^2.0.5"
+ "vitest": "^2.1.9"
}
}
diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md
index daf8ed209ac3..361de202b11d 100644
--- a/packages/svelte/CHANGELOG.md
+++ b/packages/svelte/CHANGELOG.md
@@ -1,5 +1,438 @@
# svelte
+## 5.25.8
+
+### Patch Changes
+
+- fix: address untracked_writes memory leak ([#15694](https://github.com/sveltejs/svelte/pull/15694))
+
+## 5.25.7
+
+### Patch Changes
+
+- fix: ensure clearing of old values happens independent of root flushes ([#15664](https://github.com/sveltejs/svelte/pull/15664))
+
+## 5.25.6
+
+### Patch Changes
+
+- fix: ignore generic type arguments while creating AST ([#15659](https://github.com/sveltejs/svelte/pull/15659))
+
+- fix: better consider component and its snippets during css pruning ([#15630](https://github.com/sveltejs/svelte/pull/15630))
+
+## 5.25.5
+
+### Patch Changes
+
+- fix: add setters to `$derived` class properties ([#15628](https://github.com/sveltejs/svelte/pull/15628))
+
+- fix: silence assignment warning on more function bindings ([#15644](https://github.com/sveltejs/svelte/pull/15644))
+
+- fix: make sure CSS is preserved during SSR with bindings ([#15645](https://github.com/sveltejs/svelte/pull/15645))
+
+## 5.25.4
+
+### Patch Changes
+
+- fix: support TS type assertions ([#15642](https://github.com/sveltejs/svelte/pull/15642))
+
+- fix: ensure `undefined` class still applies scoping class, if necessary ([#15643](https://github.com/sveltejs/svelte/pull/15643))
+
+## 5.25.3
+
+### Patch Changes
+
+- fix: prevent state runes from being called with spread ([#15585](https://github.com/sveltejs/svelte/pull/15585))
+
+## 5.25.2
+
+### Patch Changes
+
+- feat: migrate reassigned deriveds to `$derived` ([#15581](https://github.com/sveltejs/svelte/pull/15581))
+
+## 5.25.1
+
+### Patch Changes
+
+- fix: prevent dev server from throwing errors when attempting to retrieve the proxied value of an iframe's contentWindow ([#15577](https://github.com/sveltejs/svelte/pull/15577))
+
+## 5.25.0
+
+### Minor Changes
+
+- feat: make deriveds writable ([#15570](https://github.com/sveltejs/svelte/pull/15570))
+
+## 5.24.1
+
+### Patch Changes
+
+- fix: use `get` in constructor for deriveds ([#15300](https://github.com/sveltejs/svelte/pull/15300))
+
+- fix: ensure toStore root effect is connected to correct parent effect ([#15574](https://github.com/sveltejs/svelte/pull/15574))
+
+## 5.24.0
+
+### Minor Changes
+
+- feat: allow state created in deriveds/effects to be written/read locally without self-invalidation ([#15553](https://github.com/sveltejs/svelte/pull/15553))
+
+### Patch Changes
+
+- fix: check if DOM prototypes are extensible ([#15569](https://github.com/sveltejs/svelte/pull/15569))
+
+- Keep inlined trailing JSDoc comments of properties when running svelte-migrate ([#15567](https://github.com/sveltejs/svelte/pull/15567))
+
+- fix: simplify set calls for proxyable values ([#15548](https://github.com/sveltejs/svelte/pull/15548))
+
+- fix: don't depend on deriveds created inside the current reaction ([#15564](https://github.com/sveltejs/svelte/pull/15564))
+
+## 5.23.2
+
+### Patch Changes
+
+- fix: don't hoist listeners that access non hoistable snippets ([#15534](https://github.com/sveltejs/svelte/pull/15534))
+
+## 5.23.1
+
+### Patch Changes
+
+- fix: invalidate parent effects when child effects update parent dependencies ([#15506](https://github.com/sveltejs/svelte/pull/15506))
+
+- fix: correctly match `:has()` selector during css pruning ([#15277](https://github.com/sveltejs/svelte/pull/15277))
+
+- fix: replace `undefined` with `void 0` to avoid edge case ([#15511](https://github.com/sveltejs/svelte/pull/15511))
+
+- fix: allow global-like pseudo-selectors refinement ([#15313](https://github.com/sveltejs/svelte/pull/15313))
+
+- chore: don't distribute unused types definitions ([#15473](https://github.com/sveltejs/svelte/pull/15473))
+
+- fix: add `files` and `group` to HTMLInputAttributes in elements.d.ts ([#15492](https://github.com/sveltejs/svelte/pull/15492))
+
+- fix: throw rune_invalid_arguments_length when $state.raw() is used with more than 1 arg ([#15516](https://github.com/sveltejs/svelte/pull/15516))
+
+## 5.23.0
+
+### Minor Changes
+
+- fix: make values consistent between effects and their cleanup functions ([#15469](https://github.com/sveltejs/svelte/pull/15469))
+
+## 5.22.6
+
+### Patch Changes
+
+- fix: skip `log_if_contains_state` if only logging literals ([#15468](https://github.com/sveltejs/svelte/pull/15468))
+
+- fix: Add `closedby` property to HTMLDialogAttributes type ([#15458](https://github.com/sveltejs/svelte/pull/15458))
+
+- fix: null and warnings for local handlers ([#15460](https://github.com/sveltejs/svelte/pull/15460))
+
+## 5.22.5
+
+### Patch Changes
+
+- fix: memoize `clsx` calls ([#15456](https://github.com/sveltejs/svelte/pull/15456))
+
+- fix: respect `svelte-ignore hydration_attribute_changed` on elements with spread attributes ([#15443](https://github.com/sveltejs/svelte/pull/15443))
+
+- fix: always use `setAttribute` when setting `style` ([#15323](https://github.com/sveltejs/svelte/pull/15323))
+
+- fix: make `style:` directive and CSS handling more robust ([#15418](https://github.com/sveltejs/svelte/pull/15418))
+
+## 5.22.4
+
+### Patch Changes
+
+- fix: never deduplicate expressions in templates ([#15451](https://github.com/sveltejs/svelte/pull/15451))
+
+## 5.22.3
+
+### Patch Changes
+
+- fix: run effect roots in tree order ([#15446](https://github.com/sveltejs/svelte/pull/15446))
+
+## 5.22.2
+
+### Patch Changes
+
+- fix: correctly set `is_updating` before flushing root effects ([#15442](https://github.com/sveltejs/svelte/pull/15442))
+
+## 5.22.1
+
+### Patch Changes
+
+- chore: switch acorn-typescript plugin ([#15393](https://github.com/sveltejs/svelte/pull/15393))
+
+## 5.22.0
+
+### Minor Changes
+
+- feat: Add `idPrefix` option to `render` ([#15428](https://github.com/sveltejs/svelte/pull/15428))
+
+### Patch Changes
+
+- fix: make dialog element and role interactive ([#15429](https://github.com/sveltejs/svelte/pull/15429))
+
+## 5.21.0
+
+### Minor Changes
+
+- chore: Reduce hydration comment for {:else if} ([#15250](https://github.com/sveltejs/svelte/pull/15250))
+
+### Patch Changes
+
+- fix: disallow `bind:group` to snippet parameters ([#15401](https://github.com/sveltejs/svelte/pull/15401))
+
+## 5.20.5
+
+### Patch Changes
+
+- fix: allow double hyphen css selector names ([#15384](https://github.com/sveltejs/svelte/pull/15384))
+
+- fix: class:directive not working with $restProps #15386 ([#15389](https://github.com/sveltejs/svelte/pull/15389))
+ fix: spread add an useless cssHash on non-scoped element
+
+- fix: catch error on @const tag in svelte:boundary in DEV mode ([#15369](https://github.com/sveltejs/svelte/pull/15369))
+
+- fix: allow for duplicate `var` declarations ([#15382](https://github.com/sveltejs/svelte/pull/15382))
+
+- fix : bug "$0 is not defined" on svelte:element with a function call on class ([#15396](https://github.com/sveltejs/svelte/pull/15396))
+
+## 5.20.4
+
+### Patch Changes
+
+- fix: update types and inline docs for flushSync ([#15348](https://github.com/sveltejs/svelte/pull/15348))
+
+## 5.20.3
+
+### Patch Changes
+
+- fix: allow `@const` inside `#key` ([#15377](https://github.com/sveltejs/svelte/pull/15377))
+
+- fix: remove unnecessary `?? ''` on some expressions ([#15287](https://github.com/sveltejs/svelte/pull/15287))
+
+- fix: correctly override class attributes with class directives ([#15352](https://github.com/sveltejs/svelte/pull/15352))
+
+## 5.20.2
+
+### Patch Changes
+
+- chore: remove unused `options.uid` in `render` ([#15302](https://github.com/sveltejs/svelte/pull/15302))
+
+- fix: do not warn for `binding_property_non_reactive` if binding is a store in an each ([#15318](https://github.com/sveltejs/svelte/pull/15318))
+
+- fix: prevent writable store value from becoming a proxy when reassigning using $-prefix ([#15283](https://github.com/sveltejs/svelte/pull/15283))
+
+- fix: `muted` reactive without `bind` and select/autofocus attributes working with function calls ([#15326](https://github.com/sveltejs/svelte/pull/15326))
+
+- fix: ensure input elements and elements with `dir` attribute are marked as non-static ([#15259](https://github.com/sveltejs/svelte/pull/15259))
+
+- fix: fire delegated events on target even it was disabled in the meantime ([#15319](https://github.com/sveltejs/svelte/pull/15319))
+
+## 5.20.1
+
+### Patch Changes
+
+- fix: ensure AST analysis on `svelte.js` modules succeeds ([#15297](https://github.com/sveltejs/svelte/pull/15297))
+
+- fix: ignore typescript abstract methods ([#15267](https://github.com/sveltejs/svelte/pull/15267))
+
+- fix: correctly ssr component in `svelte:head` with `$props.id()` or `css='injected'` ([#15291](https://github.com/sveltejs/svelte/pull/15291))
+
+## 5.20.0
+
+### Minor Changes
+
+- feat: SSR-safe ID generation with `$props.id()` ([#15185](https://github.com/sveltejs/svelte/pull/15185))
+
+### Patch Changes
+
+- fix: take private and public into account for `constant_assignment` of derived state ([#15276](https://github.com/sveltejs/svelte/pull/15276))
+
+- fix: value/checked not correctly set using spread ([#15239](https://github.com/sveltejs/svelte/pull/15239))
+
+- chore: tweak effect self invalidation logic, run transition dispatches without reactive context ([#15275](https://github.com/sveltejs/svelte/pull/15275))
+
+- fix: use `importNode` to clone templates for Firefox ([#15272](https://github.com/sveltejs/svelte/pull/15272))
+
+- fix: recurse into `$derived` for ownership validation ([#15166](https://github.com/sveltejs/svelte/pull/15166))
+
+## 5.19.10
+
+### Patch Changes
+
+- fix: when re-connecting unowned deriveds, remove their unowned flag ([#15255](https://github.com/sveltejs/svelte/pull/15255))
+
+- fix: allow mutation of private derived state ([#15228](https://github.com/sveltejs/svelte/pull/15228))
+
+## 5.19.9
+
+### Patch Changes
+
+- fix: ensure unowned derived dependencies are not duplicated when reactions are skipped ([#15232](https://github.com/sveltejs/svelte/pull/15232))
+
+- fix: hydrate `href` that is part of spread attributes ([#15226](https://github.com/sveltejs/svelte/pull/15226))
+
+## 5.19.8
+
+### Patch Changes
+
+- fix: properly set `value` property of custom elements ([#15206](https://github.com/sveltejs/svelte/pull/15206))
+
+- fix: ensure custom element updates don't run in hydration mode ([#15217](https://github.com/sveltejs/svelte/pull/15217))
+
+- fix: ensure tracking returns true, even if in unowned ([#15214](https://github.com/sveltejs/svelte/pull/15214))
+
+## 5.19.7
+
+### Patch Changes
+
+- chore: remove unused code from signal logic ([#15195](https://github.com/sveltejs/svelte/pull/15195))
+
+- fix: encounter svelte:element in blocks as sibling during pruning css ([#15165](https://github.com/sveltejs/svelte/pull/15165))
+
+## 5.19.6
+
+### Patch Changes
+
+- fix: do not prune selectors like `:global(.foo):has(.scoped)` ([#15140](https://github.com/sveltejs/svelte/pull/15140))
+
+- fix: don't error on slot prop inside block inside other component ([#15148](https://github.com/sveltejs/svelte/pull/15148))
+
+- fix: ensure reactions are correctly attached for unowned deriveds ([#15158](https://github.com/sveltejs/svelte/pull/15158))
+
+- fix: silence a11y attribute warnings when spread attributes present ([#15150](https://github.com/sveltejs/svelte/pull/15150))
+
+- fix: prevent false-positive ownership validations due to hot reload ([#15154](https://github.com/sveltejs/svelte/pull/15154))
+
+- fix: widen ownership when calling setContext ([#15153](https://github.com/sveltejs/svelte/pull/15153))
+
+## 5.19.5
+
+### Patch Changes
+
+- fix: improve derived connection to ownership graph ([#15137](https://github.com/sveltejs/svelte/pull/15137))
+
+- fix: correctly look for sibling elements inside blocks and components when pruning CSS ([#15106](https://github.com/sveltejs/svelte/pull/15106))
+
+## 5.19.4
+
+### Patch Changes
+
+- fix: Add `bind:focused` property to `HTMLAttributes` type ([#15122](https://github.com/sveltejs/svelte/pull/15122))
+
+- fix: lazily connect derievds (in deriveds) to their parent ([#15129](https://github.com/sveltejs/svelte/pull/15129))
+
+- fix: disallow $state/$derived in const tags ([#15115](https://github.com/sveltejs/svelte/pull/15115))
+
+## 5.19.3
+
+### Patch Changes
+
+- fix: don't throw for `undefined` non delegated event handlers ([#15087](https://github.com/sveltejs/svelte/pull/15087))
+
+- fix: consistently set value to blank string when value attribute is undefined ([#15057](https://github.com/sveltejs/svelte/pull/15057))
+
+- fix: optimise || expressions in template ([#15092](https://github.com/sveltejs/svelte/pull/15092))
+
+- fix: correctly handle `novalidate` attribute casing ([#15083](https://github.com/sveltejs/svelte/pull/15083))
+
+- fix: expand boolean attribute support ([#15095](https://github.com/sveltejs/svelte/pull/15095))
+
+- fix: avoid double deriveds in component props ([#15089](https://github.com/sveltejs/svelte/pull/15089))
+
+- fix: add check for `is` attribute to correctly detect custom elements ([#15086](https://github.com/sveltejs/svelte/pull/15086))
+
+## 5.19.2
+
+### Patch Changes
+
+- fix: address regression with untrack ([#15079](https://github.com/sveltejs/svelte/pull/15079))
+
+## 5.19.1
+
+### Patch Changes
+
+- fix: omit unnecessary nullish coallescing in template expressions ([#15056](https://github.com/sveltejs/svelte/pull/15056))
+
+- fix: more efficient template effect grouping ([#15050](https://github.com/sveltejs/svelte/pull/15050))
+
+- fix: ensure untrack correctly retains the active reaction ([#15065](https://github.com/sveltejs/svelte/pull/15065))
+
+- fix: initialize `files` bind on hydration ([#15059](https://github.com/sveltejs/svelte/pull/15059))
+
+## 5.19.0
+
+### Minor Changes
+
+- feat: Expose `ClassValue` from `svelte/elements` ([#15035](https://github.com/sveltejs/svelte/pull/15035))
+
+### Patch Changes
+
+- fix: create fewer deriveds for concatenated strings ([#15041](https://github.com/sveltejs/svelte/pull/15041))
+
+- fix: correctly parse leading comments in function binding ([#15020](https://github.com/sveltejs/svelte/pull/15020))
+
+## 5.18.0
+
+### Minor Changes
+
+- feat: allow `` elements to contain any child ([#15007](https://github.com/sveltejs/svelte/pull/15007))
+
+### Patch Changes
+
+- fix: ensure resume effects are scheduled in topological order ([#15012](https://github.com/sveltejs/svelte/pull/15012))
+
+- fix: bump esrap ([#15015](https://github.com/sveltejs/svelte/pull/15015))
+
+- fix: remove listener on `bind_current_time` teardown ([#15013](https://github.com/sveltejs/svelte/pull/15013))
+
+## 5.17.5
+
+### Patch Changes
+
+- feat: allow const tag inside `svelte:boundary` ([#14993](https://github.com/sveltejs/svelte/pull/14993))
+
+- fix: ensure signal write invalidation within effects is consistent ([#14989](https://github.com/sveltejs/svelte/pull/14989))
+
+## 5.17.4
+
+### Patch Changes
+
+- fix: never consider inert boundary effects ([#14999](https://github.com/sveltejs/svelte/pull/14999))
+
+- fix: store access on component destroy ([#14968](https://github.com/sveltejs/svelte/pull/14968))
+
+- fix: correctly transform `pre` with no content ([#14973](https://github.com/sveltejs/svelte/pull/14973))
+
+- fix: wrap each block expression in derived to encapsulate effects ([#14967](https://github.com/sveltejs/svelte/pull/14967))
+
+## 5.17.3
+
+### Patch Changes
+
+- fix: reset dependency read versions after reaction execution ([#14964](https://github.com/sveltejs/svelte/pull/14964))
+
+## 5.17.2
+
+### Patch Changes
+
+- fix: account for parent scale when animating elements ([#14957](https://github.com/sveltejs/svelte/pull/14957))
+
+- fix: apply `overflow: hidden` style when transitioning elements, where necessary ([#14930](https://github.com/sveltejs/svelte/pull/14930))
+
+- fix: properly add owners to function bindings ([#14962](https://github.com/sveltejs/svelte/pull/14962))
+
+## 5.17.1
+
+### Patch Changes
+
+- fix: remove bindable prop validation ([#14946](https://github.com/sveltejs/svelte/pull/14946))
+
+- chore: tweak "invalid assignment" compiler error message ([#14955](https://github.com/sveltejs/svelte/pull/14955))
+
+- fix: silence false-positive stale value warning ([#14958](https://github.com/sveltejs/svelte/pull/14958))
+
## 5.17.0
### Minor Changes
diff --git a/packages/svelte/README.md b/packages/svelte/README.md
index 23e35fd4a7a8..c37617da30eb 100644
--- a/packages/svelte/README.md
+++ b/packages/svelte/README.md
@@ -1,4 +1,9 @@
-[](https://svelte.dev)
+
+
+
+
+
+
[](https://www.npmjs.com/package/svelte) [](LICENSE.md) [](https://svelte.dev/chat)
diff --git a/packages/svelte/elements.d.ts b/packages/svelte/elements.d.ts
index 604403f0a261..99d87b4c09a4 100644
--- a/packages/svelte/elements.d.ts
+++ b/packages/svelte/elements.d.ts
@@ -741,7 +741,7 @@ export interface HTMLAttributes extends AriaAttributes, D
accesskey?: string | undefined | null;
autocapitalize?: 'characters' | 'off' | 'on' | 'none' | 'sentences' | 'words' | undefined | null;
autofocus?: boolean | undefined | null;
- class?: string | import('clsx').ClassArray | import('clsx').ClassDictionary | undefined | null;
+ class?: ClassValue | undefined | null;
contenteditable?: Booleanish | 'inherit' | 'plaintext-only' | undefined | null;
contextmenu?: string | undefined | null;
dir?: 'ltr' | 'rtl' | 'auto' | undefined | null;
@@ -839,6 +839,7 @@ export interface HTMLAttributes extends AriaAttributes, D
readonly 'bind:contentBoxSize'?: Array | undefined | null;
readonly 'bind:borderBoxSize'?: Array | undefined | null;
readonly 'bind:devicePixelContentBoxSize'?: Array | undefined | null;
+ readonly 'bind:focused'?: boolean | undefined | null;
// SvelteKit
'data-sveltekit-keepfocus'?: true | '' | 'off' | undefined | null;
@@ -956,6 +957,7 @@ export interface HTMLDelAttributes extends HTMLAttributes {
export interface HTMLDialogAttributes extends HTMLAttributes {
open?: boolean | undefined | null;
+ closedby?: 'any' | 'closerequest' | 'none' | undefined | null;
}
export interface HTMLEmbedAttributes extends HTMLAttributes {
@@ -1074,6 +1076,7 @@ export interface HTMLInputAttributes extends HTMLAttributes {
checked?: boolean | undefined | null;
dirname?: string | undefined | null;
disabled?: boolean | undefined | null;
+ files?: FileList | undefined | null;
form?: string | undefined | null;
formaction?: string | undefined | null;
formenctype?:
@@ -1085,6 +1088,7 @@ export interface HTMLInputAttributes extends HTMLAttributes {
formmethod?: 'dialog' | 'get' | 'post' | 'DIALOG' | 'GET' | 'POST' | undefined | null;
formnovalidate?: boolean | undefined | null;
formtarget?: string | undefined | null;
+ group?: any | undefined | null;
height?: number | string | undefined | null;
indeterminate?: boolean | undefined | null;
list?: string | undefined | null;
@@ -1522,7 +1526,7 @@ export interface SvelteWindowAttributes extends HTMLAttributes {
export interface SVGAttributes extends AriaAttributes, DOMAttributes {
// Attributes which also defined in HTMLAttributes
className?: string | undefined | null;
- class?: string | import('clsx').ClassArray | import('clsx').ClassDictionary | undefined | null;
+ class?: ClassValue | undefined | null;
color?: string | undefined | null;
height?: number | string | undefined | null;
id?: string | undefined | null;
@@ -2059,3 +2063,5 @@ export interface SvelteHTMLElements {
[name: string]: { [name: string]: any };
}
+
+export type ClassValue = string | import('clsx').ClassArray | import('clsx').ClassDictionary;
diff --git a/packages/svelte/messages/client-errors/errors.md b/packages/svelte/messages/client-errors/errors.md
index ce1f222c63ea..ca06122cb581 100644
--- a/packages/svelte/messages/client-errors/errors.md
+++ b/packages/svelte/messages/client-errors/errors.md
@@ -80,10 +80,37 @@ See the [migration guide](/docs/svelte/v5-migration-guide#Components-are-no-long
> Cannot set prototype of `$state` object
-## state_unsafe_local_read
-
-> Reading state that was created inside the same derived is forbidden. Consider using `untrack` to read locally created state
-
## state_unsafe_mutation
> Updating state inside a derived or a template expression is forbidden. If the value should not be reactive, declare it without `$state`
+
+This error occurs when state is updated while evaluating a `$derived`. You might encounter it while trying to 'derive' two pieces of state in one go:
+
+```svelte
+
+
+ count++}>{count}
+
+{count} is even: {even}
+{count} is odd: {odd}
+```
+
+This is forbidden because it introduces instability: if `{count} is even: {even}
` is updated before `odd` is recalculated, `even` will be stale. In most cases the solution is to make everything derived:
+
+```js
+let count = 0;
+// ---cut---
+let even = $derived(count % 2 === 0);
+let odd = $derived(!even);
+```
+
+If side-effects are unavoidable, use [`$effect`]($effect) instead.
diff --git a/packages/svelte/messages/compile-errors/script.md b/packages/svelte/messages/compile-errors/script.md
index fa851cec89ea..aabcbeae4812 100644
--- a/packages/svelte/messages/compile-errors/script.md
+++ b/packages/svelte/messages/compile-errors/script.md
@@ -32,7 +32,39 @@
## each_item_invalid_assignment
-> Cannot reassign or bind to each block argument in runes mode. Use the array and index variables instead (e.g. `array[i] = value` instead of `entry = value`)
+> Cannot reassign or bind to each block argument in runes mode. Use the array and index variables instead (e.g. `array[i] = value` instead of `entry = value`, or `bind:value={array[i]}` instead of `bind:value={entry}`)
+
+In legacy mode, it was possible to reassign or bind to the each block argument itself:
+
+```svelte
+
+
+{#each array as entry}
+
+ entry = 4}>change
+
+
+
+{/each}
+```
+
+This turned out to be buggy and unpredictable, particularly when working with derived values (such as `array.map(...)`), and as such is forbidden in runes mode. You can achieve the same outcome by using the index instead:
+
+```svelte
+
+
+{#each array as entry, i}
+
+ array[i] = 4}>change
+
+
+
+{/each}
+```
## effect_invalid_placement
@@ -88,7 +120,11 @@
## props_duplicate
-> Cannot use `$props()` more than once
+> Cannot use `%rune%()` more than once
+
+## props_id_invalid_placement
+
+> `$props.id()` can only be used at the top level of components as a variable declaration initializer
## props_illegal_name
@@ -126,6 +162,10 @@
> `%name%` is not a valid rune
+## rune_invalid_spread
+
+> `%rune%` cannot be called with a spread argument
+
## rune_invalid_usage
> Cannot use `%rune%` rune in non-runes mode
diff --git a/packages/svelte/messages/compile-errors/template.md b/packages/svelte/messages/compile-errors/template.md
index 287178ef8799..0569f63ad30d 100644
--- a/packages/svelte/messages/compile-errors/template.md
+++ b/packages/svelte/messages/compile-errors/template.md
@@ -54,6 +54,10 @@
> `bind:group` can only bind to an Identifier or MemberExpression
+## bind_group_invalid_snippet_parameter
+
+> Cannot `bind:group` to a snippet parameter
+
## bind_invalid_expression
> Can only bind to an Identifier or MemberExpression or a `{get, set}` pair
@@ -118,7 +122,7 @@
## const_tag_invalid_placement
-> `{@const}` must be the immediate child of `{#snippet}`, `{#if}`, `{:else if}`, `{:else}`, `{#each}`, `{:then}`, `{:catch}`, `` or ``
+> `{@const}` must be the immediate child of `{#snippet}`, `{#if}`, `{:else if}`, `{:else}`, `{#each}`, `{:then}`, `{:catch}`, ``, ``
## debug_tag_invalid_arguments
diff --git a/packages/svelte/package.json b/packages/svelte/package.json
index 5cd078bf77c8..8a225a798d9a 100644
--- a/packages/svelte/package.json
+++ b/packages/svelte/package.json
@@ -2,18 +2,19 @@
"name": "svelte",
"description": "Cybernetically enhanced web apps",
"license": "MIT",
- "version": "5.17.0",
+ "version": "5.25.8",
"type": "module",
"types": "./types/index.d.ts",
"engines": {
"node": ">=18"
},
"files": [
+ "*.d.ts",
"src",
"!src/**/*.test.*",
+ "!src/**/*.d.ts",
"types",
"compiler",
- "*.d.ts",
"README.md"
],
"module": "src/index-client.js",
@@ -137,25 +138,25 @@
"@rollup/plugin-virtual": "^3.0.2",
"@types/aria-query": "^5.0.4",
"@types/node": "^20.11.5",
- "dts-buddy": "^0.5.3",
+ "dts-buddy": "^0.5.5",
"esbuild": "^0.21.5",
"rollup": "^4.22.4",
"source-map": "^0.7.4",
- "tiny-glob": "^0.2.9",
+ "tinyglobby": "^0.2.12",
"typescript": "^5.5.4",
- "vitest": "^2.0.5"
+ "vitest": "^2.1.9"
},
"dependencies": {
"@ampproject/remapping": "^2.3.0",
"@jridgewell/sourcemap-codec": "^1.5.0",
"@types/estree": "^1.0.5",
"acorn": "^8.12.1",
- "acorn-typescript": "^1.4.13",
+ "@sveltejs/acorn-typescript": "^1.0.5",
"aria-query": "^5.3.1",
"axobject-query": "^4.1.0",
"clsx": "^2.1.1",
"esm-env": "^1.2.1",
- "esrap": "^1.3.2",
+ "esrap": "^1.4.6",
"is-reference": "^3.0.3",
"locate-character": "^3.0.0",
"magic-string": "^0.30.11",
diff --git a/packages/svelte/src/ambient.d.ts b/packages/svelte/src/ambient.d.ts
index fbcecba8e47c..a1484718cc77 100644
--- a/packages/svelte/src/ambient.d.ts
+++ b/packages/svelte/src/ambient.d.ts
@@ -339,6 +339,15 @@ declare namespace $effect {
declare function $props(): any;
declare namespace $props {
+ /**
+ * Generates an ID that is unique to the current component instance. When hydrating a server-rendered component,
+ * the value will be consistent between server and client.
+ *
+ * This is useful for linking elements via attributes like `for` and `aria-labelledby`.
+ * @since 5.20.0
+ */
+ export function id(): string;
+
// prevent intellisense from being unhelpful
/** @deprecated */
export const apply: never;
diff --git a/packages/svelte/src/animate/index.js b/packages/svelte/src/animate/index.js
index fbf2ac83e2a7..2ce7708bc7a8 100644
--- a/packages/svelte/src/animate/index.js
+++ b/packages/svelte/src/animate/index.js
@@ -11,18 +11,37 @@ import { cubicOut } from '../easing/index.js';
* @returns {AnimationConfig}
*/
export function flip(node, { from, to }, params = {}) {
+ var { delay = 0, duration = (d) => Math.sqrt(d) * 120, easing = cubicOut } = params;
+
var style = getComputedStyle(node);
- var zoom = get_zoom(node); // https://drafts.csswg.org/css-viewport/#effective-zoom
+ // find the transform origin, expressed as a pair of values between 0 and 1
var transform = style.transform === 'none' ? '' : style.transform;
var [ox, oy] = style.transformOrigin.split(' ').map(parseFloat);
+ ox /= node.clientWidth;
+ oy /= node.clientHeight;
+
+ // calculate effect of parent transforms and zoom
+ var zoom = get_zoom(node); // https://drafts.csswg.org/css-viewport/#effective-zoom
+ var sx = node.clientWidth / to.width / zoom;
+ var sy = node.clientHeight / to.height / zoom;
+
+ // find the starting position of the transform origin
+ var fx = from.left + from.width * ox;
+ var fy = from.top + from.height * oy;
+
+ // find the ending position of the transform origin
+ var tx = to.left + to.width * ox;
+ var ty = to.top + to.height * oy;
+
+ // find the translation at the start of the transform
+ var dx = (fx - tx) * sx;
+ var dy = (fy - ty) * sy;
+
+ // find the relative scale at the start of the transform
var dsx = from.width / to.width;
var dsy = from.height / to.height;
- var dx = (from.left + dsx * ox - (to.left + ox)) / zoom;
- var dy = (from.top + dsy * oy - (to.top + oy)) / zoom;
- var { delay = 0, duration = (d) => Math.sqrt(d) * 120, easing = cubicOut } = params;
-
return {
delay,
duration: typeof duration === 'function' ? duration(Math.sqrt(dx * dx + dy * dy)) : duration,
@@ -32,7 +51,8 @@ export function flip(node, { from, to }, params = {}) {
var y = u * dy;
var sx = t + u * dsx;
var sy = t + u * dsy;
- return `transform: ${transform} scale(${sx}, ${sy}) translate(${x}px, ${y}px);`;
+
+ return `transform: ${transform} translate(${x}px, ${y}px) scale(${sx}, ${sy});`;
}
};
}
diff --git a/packages/svelte/src/compiler/errors.js b/packages/svelte/src/compiler/errors.js
index 870cd9ac093d..6bf973948b92 100644
--- a/packages/svelte/src/compiler/errors.js
+++ b/packages/svelte/src/compiler/errors.js
@@ -52,7 +52,7 @@ function e(node, code, message) {
* @returns {never}
*/
export function options_invalid_value(node, details) {
- e(node, "options_invalid_value", `Invalid compiler option: ${details}\nhttps://svelte.dev/e/options_invalid_value`);
+ e(node, 'options_invalid_value', `Invalid compiler option: ${details}\nhttps://svelte.dev/e/options_invalid_value`);
}
/**
@@ -62,7 +62,7 @@ export function options_invalid_value(node, details) {
* @returns {never}
*/
export function options_removed(node, details) {
- e(node, "options_removed", `Invalid compiler option: ${details}\nhttps://svelte.dev/e/options_removed`);
+ e(node, 'options_removed', `Invalid compiler option: ${details}\nhttps://svelte.dev/e/options_removed`);
}
/**
@@ -72,7 +72,7 @@ export function options_removed(node, details) {
* @returns {never}
*/
export function options_unrecognised(node, keypath) {
- e(node, "options_unrecognised", `Unrecognised compiler option ${keypath}\nhttps://svelte.dev/e/options_unrecognised`);
+ e(node, 'options_unrecognised', `Unrecognised compiler option ${keypath}\nhttps://svelte.dev/e/options_unrecognised`);
}
/**
@@ -81,7 +81,7 @@ export function options_unrecognised(node, keypath) {
* @returns {never}
*/
export function bindable_invalid_location(node) {
- e(node, "bindable_invalid_location", `\`$bindable()\` can only be used inside a \`$props()\` declaration\nhttps://svelte.dev/e/bindable_invalid_location`);
+ e(node, 'bindable_invalid_location', `\`$bindable()\` can only be used inside a \`$props()\` declaration\nhttps://svelte.dev/e/bindable_invalid_location`);
}
/**
@@ -91,7 +91,7 @@ export function bindable_invalid_location(node) {
* @returns {never}
*/
export function constant_assignment(node, thing) {
- e(node, "constant_assignment", `Cannot assign to ${thing}\nhttps://svelte.dev/e/constant_assignment`);
+ e(node, 'constant_assignment', `Cannot assign to ${thing}\nhttps://svelte.dev/e/constant_assignment`);
}
/**
@@ -101,7 +101,7 @@ export function constant_assignment(node, thing) {
* @returns {never}
*/
export function constant_binding(node, thing) {
- e(node, "constant_binding", `Cannot bind to ${thing}\nhttps://svelte.dev/e/constant_binding`);
+ e(node, 'constant_binding', `Cannot bind to ${thing}\nhttps://svelte.dev/e/constant_binding`);
}
/**
@@ -111,7 +111,7 @@ export function constant_binding(node, thing) {
* @returns {never}
*/
export function declaration_duplicate(node, name) {
- e(node, "declaration_duplicate", `\`${name}\` has already been declared\nhttps://svelte.dev/e/declaration_duplicate`);
+ e(node, 'declaration_duplicate', `\`${name}\` has already been declared\nhttps://svelte.dev/e/declaration_duplicate`);
}
/**
@@ -120,7 +120,7 @@ export function declaration_duplicate(node, name) {
* @returns {never}
*/
export function declaration_duplicate_module_import(node) {
- e(node, "declaration_duplicate_module_import", `Cannot declare a variable with the same name as an import inside \`
diff --git a/packages/svelte/tests/compiler-errors/samples/runes-no-derived-binding/_config.js b/packages/svelte/tests/compiler-errors/samples/runes-no-derived-binding/_config.js
deleted file mode 100644
index 87b88d79cc26..000000000000
--- a/packages/svelte/tests/compiler-errors/samples/runes-no-derived-binding/_config.js
+++ /dev/null
@@ -1,8 +0,0 @@
-import { test } from '../../test';
-
-export default test({
- error: {
- code: 'constant_binding',
- message: 'Cannot bind to derived state'
- }
-});
diff --git a/packages/svelte/tests/compiler-errors/samples/runes-no-derived-binding/main.svelte b/packages/svelte/tests/compiler-errors/samples/runes-no-derived-binding/main.svelte
deleted file mode 100644
index 6c198dc068fe..000000000000
--- a/packages/svelte/tests/compiler-errors/samples/runes-no-derived-binding/main.svelte
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
diff --git a/packages/svelte/tests/compiler-errors/samples/runes-no-derived-state-field-assignment/_config.js b/packages/svelte/tests/compiler-errors/samples/runes-no-derived-state-field-assignment/_config.js
deleted file mode 100644
index 94985a99397a..000000000000
--- a/packages/svelte/tests/compiler-errors/samples/runes-no-derived-state-field-assignment/_config.js
+++ /dev/null
@@ -1,8 +0,0 @@
-import { test } from '../../test';
-
-export default test({
- error: {
- code: 'constant_assignment',
- message: 'Cannot assign to derived state'
- }
-});
diff --git a/packages/svelte/tests/compiler-errors/samples/runes-no-derived-state-field-assignment/main.svelte b/packages/svelte/tests/compiler-errors/samples/runes-no-derived-state-field-assignment/main.svelte
deleted file mode 100644
index d44806757e6c..000000000000
--- a/packages/svelte/tests/compiler-errors/samples/runes-no-derived-state-field-assignment/main.svelte
+++ /dev/null
@@ -1,10 +0,0 @@
-
diff --git a/packages/svelte/tests/compiler-errors/samples/runes-no-derived-state-field-update/_config.js b/packages/svelte/tests/compiler-errors/samples/runes-no-derived-state-field-update/_config.js
deleted file mode 100644
index 94985a99397a..000000000000
--- a/packages/svelte/tests/compiler-errors/samples/runes-no-derived-state-field-update/_config.js
+++ /dev/null
@@ -1,8 +0,0 @@
-import { test } from '../../test';
-
-export default test({
- error: {
- code: 'constant_assignment',
- message: 'Cannot assign to derived state'
- }
-});
diff --git a/packages/svelte/tests/compiler-errors/samples/runes-no-derived-state-field-update/main.svelte b/packages/svelte/tests/compiler-errors/samples/runes-no-derived-state-field-update/main.svelte
deleted file mode 100644
index e4ee2e86356d..000000000000
--- a/packages/svelte/tests/compiler-errors/samples/runes-no-derived-state-field-update/main.svelte
+++ /dev/null
@@ -1,10 +0,0 @@
-
diff --git a/packages/svelte/tests/compiler-errors/samples/runes-no-derived-update/_config.js b/packages/svelte/tests/compiler-errors/samples/runes-no-derived-update/_config.js
deleted file mode 100644
index 94985a99397a..000000000000
--- a/packages/svelte/tests/compiler-errors/samples/runes-no-derived-update/_config.js
+++ /dev/null
@@ -1,8 +0,0 @@
-import { test } from '../../test';
-
-export default test({
- error: {
- code: 'constant_assignment',
- message: 'Cannot assign to derived state'
- }
-});
diff --git a/packages/svelte/tests/compiler-errors/samples/runes-no-derived-update/main.svelte b/packages/svelte/tests/compiler-errors/samples/runes-no-derived-update/main.svelte
deleted file mode 100644
index d266c95bb872..000000000000
--- a/packages/svelte/tests/compiler-errors/samples/runes-no-derived-update/main.svelte
+++ /dev/null
@@ -1,5 +0,0 @@
-
diff --git a/packages/svelte/tests/compiler-errors/samples/runes-wrong-state-raw-args/_config.js b/packages/svelte/tests/compiler-errors/samples/runes-wrong-state-raw-args/_config.js
new file mode 100644
index 000000000000..af226559d11b
--- /dev/null
+++ b/packages/svelte/tests/compiler-errors/samples/runes-wrong-state-raw-args/_config.js
@@ -0,0 +1,8 @@
+import { test } from '../../test';
+
+export default test({
+ error: {
+ code: 'rune_invalid_arguments_length',
+ message: '`$state.raw` must be called with zero or one arguments'
+ }
+});
diff --git a/packages/svelte/tests/compiler-errors/samples/runes-wrong-state-raw-args/main.svelte b/packages/svelte/tests/compiler-errors/samples/runes-wrong-state-raw-args/main.svelte
new file mode 100644
index 000000000000..2b50b43b9a2b
--- /dev/null
+++ b/packages/svelte/tests/compiler-errors/samples/runes-wrong-state-raw-args/main.svelte
@@ -0,0 +1,3 @@
+
diff --git a/packages/svelte/tests/compiler-errors/samples/runes-wrong-state-raw-args/main.svelte.js b/packages/svelte/tests/compiler-errors/samples/runes-wrong-state-raw-args/main.svelte.js
new file mode 100644
index 000000000000..442aaad14289
--- /dev/null
+++ b/packages/svelte/tests/compiler-errors/samples/runes-wrong-state-raw-args/main.svelte.js
@@ -0,0 +1 @@
+const foo = $state.raw(1, 2, 3);
diff --git a/packages/svelte/tests/css/samples/double-hyphen/expected.css b/packages/svelte/tests/css/samples/double-hyphen/expected.css
new file mode 100644
index 000000000000..ab4921b657ea
--- /dev/null
+++ b/packages/svelte/tests/css/samples/double-hyphen/expected.css
@@ -0,0 +1,4 @@
+
+ .--foo.svelte-xyz {
+ color: red;
+ }
diff --git a/packages/svelte/tests/css/samples/double-hyphen/input.svelte b/packages/svelte/tests/css/samples/double-hyphen/input.svelte
new file mode 100644
index 000000000000..e2907f9344a4
--- /dev/null
+++ b/packages/svelte/tests/css/samples/double-hyphen/input.svelte
@@ -0,0 +1,7 @@
+
+
+
\ No newline at end of file
diff --git a/packages/svelte/tests/css/samples/general-siblings-combinator-slot/_config.js b/packages/svelte/tests/css/samples/general-siblings-combinator-slot/_config.js
index 97e470d1c325..76448654957d 100644
--- a/packages/svelte/tests/css/samples/general-siblings-combinator-slot/_config.js
+++ b/packages/svelte/tests/css/samples/general-siblings-combinator-slot/_config.js
@@ -5,32 +5,20 @@ export default test({
{
code: 'css_unused_selector',
message: 'Unused CSS selector ".b ~ .c"',
- start: { character: 137, column: 1, line: 11 },
- end: { character: 144, column: 8, line: 11 }
+ start: { character: 191, column: 1, line: 13 },
+ end: { character: 198, column: 8, line: 13 }
},
{
code: 'css_unused_selector',
message: 'Unused CSS selector ".c ~ .f"',
- start: { character: 162, column: 1, line: 12 },
- end: { character: 169, column: 8, line: 12 }
- },
- {
- code: 'css_unused_selector',
- message: 'Unused CSS selector ".f ~ .g"',
- start: { character: 187, column: 1, line: 13 },
- end: { character: 194, column: 8, line: 13 }
+ start: { character: 216, column: 1, line: 14 },
+ end: { character: 223, column: 8, line: 14 }
},
{
code: 'css_unused_selector',
message: 'Unused CSS selector ".b ~ .f"',
- start: { character: 212, column: 1, line: 14 },
- end: { character: 219, column: 8, line: 14 }
- },
- {
- code: 'css_unused_selector',
- message: 'Unused CSS selector ".b ~ .g"',
- start: { character: 237, column: 1, line: 15 },
- end: { character: 244, column: 8, line: 15 }
+ start: { character: 241, column: 1, line: 15 },
+ end: { character: 248, column: 8, line: 15 }
}
]
});
diff --git a/packages/svelte/tests/css/samples/general-siblings-combinator-slot/expected.css b/packages/svelte/tests/css/samples/general-siblings-combinator-slot/expected.css
index 67a19d10c996..53fca3ae9e04 100644
--- a/packages/svelte/tests/css/samples/general-siblings-combinator-slot/expected.css
+++ b/packages/svelte/tests/css/samples/general-siblings-combinator-slot/expected.css
@@ -2,10 +2,10 @@
.d.svelte-xyz ~ .e:where(.svelte-xyz) { color: green; }
.a.svelte-xyz ~ .g:where(.svelte-xyz) { color: green; }
.a.svelte-xyz ~ .b:where(.svelte-xyz) { color: green; }
+ .f.svelte-xyz ~ .g:where(.svelte-xyz) { color: green; }
+ .b.svelte-xyz ~ .g:where(.svelte-xyz) { color: green; }
/* no match */
/* (unused) .b ~ .c { color: red; }*/
/* (unused) .c ~ .f { color: red; }*/
- /* (unused) .f ~ .g { color: red; }*/
/* (unused) .b ~ .f { color: red; }*/
- /* (unused) .b ~ .g { color: red; }*/
diff --git a/packages/svelte/tests/css/samples/general-siblings-combinator-slot/input.svelte b/packages/svelte/tests/css/samples/general-siblings-combinator-slot/input.svelte
index 2e2846fa87a6..52264d3a5a25 100644
--- a/packages/svelte/tests/css/samples/general-siblings-combinator-slot/input.svelte
+++ b/packages/svelte/tests/css/samples/general-siblings-combinator-slot/input.svelte
@@ -6,13 +6,13 @@
.d ~ .e { color: green; }
.a ~ .g { color: green; }
.a ~ .b { color: green; }
+ .f ~ .g { color: green; }
+ .b ~ .g { color: green; }
/* no match */
.b ~ .c { color: red; }
.c ~ .f { color: red; }
- .f ~ .g { color: red; }
.b ~ .f { color: red; }
- .b ~ .g { color: red; }
diff --git a/packages/svelte/tests/css/samples/general-siblings-combinator-svelteelement/_config.js b/packages/svelte/tests/css/samples/general-siblings-combinator-svelteelement/_config.js
index 7c50a76bbbd8..a7d6c3a99d3d 100644
--- a/packages/svelte/tests/css/samples/general-siblings-combinator-svelteelement/_config.js
+++ b/packages/svelte/tests/css/samples/general-siblings-combinator-svelteelement/_config.js
@@ -5,15 +5,15 @@ export default test({
{
code: 'css_unused_selector',
end: {
- character: 496,
+ character: 627,
column: 10,
- line: 26
+ line: 32
},
message: 'Unused CSS selector ".x + .bar"',
start: {
- character: 487,
+ character: 618,
column: 1,
- line: 26
+ line: 32
}
}
]
diff --git a/packages/svelte/tests/css/samples/general-siblings-combinator-svelteelement/expected.css b/packages/svelte/tests/css/samples/general-siblings-combinator-svelteelement/expected.css
index 830d3667024b..d3fa8c97d2e1 100644
--- a/packages/svelte/tests/css/samples/general-siblings-combinator-svelteelement/expected.css
+++ b/packages/svelte/tests/css/samples/general-siblings-combinator-svelteelement/expected.css
@@ -9,5 +9,8 @@
.x.svelte-xyz ~ .foo:where(.svelte-xyz) span:where(.svelte-xyz) { color: green; }
.x.svelte-xyz ~ .bar:where(.svelte-xyz) { color: green; }
+ .z.svelte-xyz + .z:where(.svelte-xyz) { color: green; }
+ .z.svelte-xyz ~ .z:where(.svelte-xyz) { color: green; }
+
/* no match */
/* (unused) .x + .bar { color: green; }*/
diff --git a/packages/svelte/tests/css/samples/general-siblings-combinator-svelteelement/input.svelte b/packages/svelte/tests/css/samples/general-siblings-combinator-svelteelement/input.svelte
index 1c51a2c516a1..655fd861539a 100644
--- a/packages/svelte/tests/css/samples/general-siblings-combinator-svelteelement/input.svelte
+++ b/packages/svelte/tests/css/samples/general-siblings-combinator-svelteelement/input.svelte
@@ -10,6 +10,9 @@
bar
+{#each [1]}
+
+{/each}
diff --git a/packages/svelte/tests/css/samples/has/_config.js b/packages/svelte/tests/css/samples/has/_config.js
index e5dc5f3459b9..5700a09b9627 100644
--- a/packages/svelte/tests/css/samples/has/_config.js
+++ b/packages/svelte/tests/css/samples/has/_config.js
@@ -6,182 +6,238 @@ export default test({
code: 'css_unused_selector',
message: 'Unused CSS selector ".unused:has(y)"',
start: {
- line: 31,
+ line: 41,
column: 1,
- character: 308
+ character: 378
},
end: {
- line: 31,
+ line: 41,
column: 15,
- character: 322
+ character: 392
}
},
{
code: 'css_unused_selector',
message: 'Unused CSS selector ".unused:has(:global(y))"',
start: {
- line: 34,
+ line: 44,
column: 1,
- character: 343
+ character: 413
},
end: {
- line: 34,
+ line: 44,
column: 24,
- character: 366
+ character: 436
}
},
{
code: 'css_unused_selector',
message: 'Unused CSS selector "x:has(.unused)"',
start: {
- line: 37,
+ line: 47,
column: 1,
- character: 387
+ character: 457
},
end: {
- line: 37,
+ line: 47,
column: 15,
- character: 401
+ character: 471
}
},
{
code: 'css_unused_selector',
message: 'Unused CSS selector ":global(.foo):has(.unused)"',
start: {
- line: 40,
+ line: 50,
column: 1,
- character: 422
+ character: 492
},
end: {
- line: 40,
+ line: 50,
column: 27,
- character: 448
+ character: 518
}
},
{
code: 'css_unused_selector',
message: 'Unused CSS selector "x:has(y):has(.unused)"',
start: {
- line: 50,
+ line: 60,
column: 1,
- character: 556
+ character: 626
},
end: {
- line: 50,
+ line: 60,
column: 22,
- character: 577
+ character: 647
}
},
{
code: 'css_unused_selector',
message: 'Unused CSS selector ".unused"',
start: {
- line: 69,
+ line: 79,
column: 2,
- character: 782
+ character: 852
},
end: {
- line: 69,
+ line: 79,
column: 9,
- character: 789
+ character: 859
}
},
{
code: 'css_unused_selector',
message: 'Unused CSS selector ".unused x:has(y)"',
start: {
- line: 85,
+ line: 95,
column: 1,
- character: 936
+ character: 1006
},
end: {
- line: 85,
+ line: 95,
column: 17,
- character: 952
+ character: 1022
}
},
{
code: 'css_unused_selector',
message: 'Unused CSS selector ".unused:has(.unused)"',
start: {
- line: 88,
+ line: 98,
column: 1,
- character: 973
+ character: 1043
},
end: {
- line: 88,
+ line: 98,
column: 21,
- character: 993
+ character: 1063
}
},
{
code: 'css_unused_selector',
message: 'Unused CSS selector "x:has(> z)"',
start: {
- line: 98,
+ line: 108,
column: 1,
- character: 1093
+ character: 1163
},
end: {
- line: 98,
+ line: 108,
column: 11,
- character: 1103
+ character: 1173
}
},
{
code: 'css_unused_selector',
message: 'Unused CSS selector "x:has(> d)"',
start: {
- line: 101,
+ line: 111,
column: 1,
- character: 1124
+ character: 1194
},
end: {
- line: 101,
+ line: 111,
column: 11,
- character: 1134
+ character: 1204
}
},
{
code: 'css_unused_selector',
message: 'Unused CSS selector "x:has(~ y)"',
start: {
- line: 121,
+ line: 131,
+ column: 1,
+ character: 1396
+ },
+ end: {
+ line: 131,
+ column: 11,
+ character: 1406
+ }
+ },
+ {
+ code: 'css_unused_selector',
+ message: 'Unused CSS selector "d:has(+ f)"',
+ start: {
+ line: 141,
column: 1,
- character: 1326
+ character: 1494
},
end: {
- line: 121,
+ line: 141,
column: 11,
- character: 1336
+ character: 1504
+ }
+ },
+ {
+ code: 'css_unused_selector',
+ message: 'Unused CSS selector "f:has(~ d)"',
+ start: {
+ line: 144,
+ column: 1,
+ character: 1525
+ },
+ end: {
+ line: 144,
+ column: 11,
+ character: 1535
}
},
{
code: 'css_unused_selector',
message: 'Unused CSS selector ":has(.unused)"',
start: {
- line: 129,
+ line: 152,
column: 2,
- character: 1409
+ character: 1608
},
end: {
- line: 129,
+ line: 152,
column: 15,
- character: 1422
+ character: 1621
}
},
{
code: 'css_unused_selector',
message: 'Unused CSS selector "&:has(.unused)"',
start: {
- line: 135,
+ line: 158,
column: 2,
- character: 1480
+ character: 1679
},
end: {
- line: 135,
+ line: 158,
column: 16,
- character: 1494
+ character: 1693
+ }
+ },
+ {
+ code: 'css_unused_selector',
+ message: 'Unused CSS selector ":global(.foo):has(.unused)"',
+ start: {
+ line: 166,
+ column: 1,
+ character: 1763
+ },
+ end: {
+ line: 166,
+ column: 27,
+ character: 1789
+ }
+ },
+ {
+ code: 'css_unused_selector',
+ message: 'Unused CSS selector "h:has(> h > i)"',
+ start: {
+ line: 173,
+ column: 1,
+ character: 1848
+ },
+ end: {
+ line: 173,
+ column: 15,
+ character: 1862
}
}
]
diff --git a/packages/svelte/tests/css/samples/has/expected.css b/packages/svelte/tests/css/samples/has/expected.css
index 68d6aad68ad0..2ce4d2bec5cd 100644
--- a/packages/svelte/tests/css/samples/has/expected.css
+++ b/packages/svelte/tests/css/samples/has/expected.css
@@ -112,6 +112,19 @@
color: red;
}*/
+ d.svelte-xyz:has(+ e:where(.svelte-xyz)) {
+ color: green;
+ }
+ d.svelte-xyz:has(~ f:where(.svelte-xyz)) {
+ color: green;
+ }
+ /* (unused) d:has(+ f) {
+ color: red;
+ }*/
+ /* (unused) f:has(~ d) {
+ color: red;
+ }*/
+
.foo {
.svelte-xyz:has(x:where(.svelte-xyz)) {
color: green;
@@ -126,3 +139,20 @@
color: red;
}*/
}
+
+ .foo:has(x.svelte-xyz) {
+ color: green;
+ }
+ /* (unused) :global(.foo):has(.unused) {
+ color: red;
+ }*/
+
+ g.svelte-xyz:has(> h:where(.svelte-xyz) > i:where(.svelte-xyz)) {
+ color: green;
+ }
+ /* (unused) h:has(> h > i) {
+ color: red;
+ }*/
+ g.svelte-xyz:has(+ j:where(.svelte-xyz) > k:where(.svelte-xyz)) {
+ color: green;
+ }
\ No newline at end of file
diff --git a/packages/svelte/tests/css/samples/has/input.svelte b/packages/svelte/tests/css/samples/has/input.svelte
index 3487b64e8c57..033471bc1696 100644
--- a/packages/svelte/tests/css/samples/has/input.svelte
+++ b/packages/svelte/tests/css/samples/has/input.svelte
@@ -3,10 +3,20 @@
{#if foo}
+
+
{/if}
+
+
+
+
+
+
+
+
diff --git a/packages/svelte/tests/css/samples/render-tag-loop/_config.js b/packages/svelte/tests/css/samples/render-tag-loop/_config.js
index f623b92cc38b..292c6c49ac9d 100644
--- a/packages/svelte/tests/css/samples/render-tag-loop/_config.js
+++ b/packages/svelte/tests/css/samples/render-tag-loop/_config.js
@@ -1,20 +1,5 @@
import { test } from '../../test';
export default test({
- warnings: [
- {
- code: 'css_unused_selector',
- message: 'Unused CSS selector "div + div"',
- start: {
- line: 19,
- column: 1,
- character: 185
- },
- end: {
- line: 19,
- column: 10,
- character: 194
- }
- }
- ]
+ warnings: []
});
diff --git a/packages/svelte/tests/css/samples/render-tag-loop/expected.css b/packages/svelte/tests/css/samples/render-tag-loop/expected.css
index 9ced15e96407..3e449286c997 100644
--- a/packages/svelte/tests/css/samples/render-tag-loop/expected.css
+++ b/packages/svelte/tests/css/samples/render-tag-loop/expected.css
@@ -2,9 +2,12 @@
div.svelte-xyz div:where(.svelte-xyz) {
color: green;
}
- /* (unused) div + div {
- color: red; /* this is marked as unused, but only because we've written an infinite loop - worth fixing? *\/
- }*/
+ div.svelte-xyz + div:where(.svelte-xyz) {
+ color: green;
+ }
div.svelte-xyz:has(div:where(.svelte-xyz)) {
color: green;
}
+ span.svelte-xyz:has(~span:where(.svelte-xyz)) {
+ color: green;
+ }
diff --git a/packages/svelte/tests/css/samples/render-tag-loop/input.svelte b/packages/svelte/tests/css/samples/render-tag-loop/input.svelte
index ade8df574489..3c55261f1845 100644
--- a/packages/svelte/tests/css/samples/render-tag-loop/input.svelte
+++ b/packages/svelte/tests/css/samples/render-tag-loop/input.svelte
@@ -12,14 +12,22 @@
{/snippet}
+{#snippet c()}
+
+ {@render c()}
+{/snippet}
+
diff --git a/packages/svelte/tests/css/samples/siblings-combinator-component/Child.svelte b/packages/svelte/tests/css/samples/siblings-combinator-component/Child.svelte
new file mode 100644
index 000000000000..1df9f35e50be
--- /dev/null
+++ b/packages/svelte/tests/css/samples/siblings-combinator-component/Child.svelte
@@ -0,0 +1,5 @@
+
+
+{@render foo()}
diff --git a/packages/svelte/tests/css/samples/siblings-combinator-component/_config.js b/packages/svelte/tests/css/samples/siblings-combinator-component/_config.js
new file mode 100644
index 000000000000..837fa20ae104
--- /dev/null
+++ b/packages/svelte/tests/css/samples/siblings-combinator-component/_config.js
@@ -0,0 +1,20 @@
+import { test } from '../../test';
+
+export default test({
+ warnings: [
+ {
+ code: 'css_unused_selector',
+ message: 'Unused CSS selector "n + m"',
+ end: {
+ character: 468,
+ column: 6,
+ line: 36
+ },
+ start: {
+ character: 463,
+ column: 1,
+ line: 36
+ }
+ }
+ ]
+});
diff --git a/packages/svelte/tests/css/samples/siblings-combinator-component/expected.css b/packages/svelte/tests/css/samples/siblings-combinator-component/expected.css
new file mode 100644
index 000000000000..d2657ccd21df
--- /dev/null
+++ b/packages/svelte/tests/css/samples/siblings-combinator-component/expected.css
@@ -0,0 +1,8 @@
+ x.svelte-xyz + y:where(.svelte-xyz) { color: green; }
+ x.svelte-xyz + v:where(.svelte-xyz) { color: green; }
+ x.svelte-xyz + z:where(.svelte-xyz) { color: green; }
+ y.svelte-xyz + z:where(.svelte-xyz) { color: green; }
+ v.svelte-xyz + z:where(.svelte-xyz) { color: green; }
+ .component + z.svelte-xyz { color: green; }
+
+ /* (unused) n + m { color: red; }*/
diff --git a/packages/svelte/tests/css/samples/siblings-combinator-component/input.svelte b/packages/svelte/tests/css/samples/siblings-combinator-component/input.svelte
new file mode 100644
index 000000000000..8d80acffb364
--- /dev/null
+++ b/packages/svelte/tests/css/samples/siblings-combinator-component/input.svelte
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+ {#snippet foo()}
+
+ {/snippet}
+
+
+
+
+
+
+
+ {#snippet foo()}
+
+
+
+ {/snippet}
+
+
+
+
+
diff --git a/packages/svelte/tests/css/samples/siblings-combinator-slot/_config.js b/packages/svelte/tests/css/samples/siblings-combinator-slot/_config.js
index 2786baeff80b..8a8f561d014b 100644
--- a/packages/svelte/tests/css/samples/siblings-combinator-slot/_config.js
+++ b/packages/svelte/tests/css/samples/siblings-combinator-slot/_config.js
@@ -5,14 +5,8 @@ export default test({
{
code: 'css_unused_selector',
message: 'Unused CSS selector ".b + .c"',
- start: { character: 110, column: 1, line: 10 },
- end: { character: 117, column: 8, line: 10 }
- },
- {
- code: 'css_unused_selector',
- message: 'Unused CSS selector ".c + .f"',
- start: { character: 135, column: 1, line: 11 },
- end: { character: 142, column: 8, line: 11 }
+ start: { character: 137, column: 1, line: 11 },
+ end: { character: 144, column: 8, line: 11 }
}
]
});
diff --git a/packages/svelte/tests/css/samples/siblings-combinator-slot/expected.css b/packages/svelte/tests/css/samples/siblings-combinator-slot/expected.css
index 643f6cf13f77..85cbb77e6514 100644
--- a/packages/svelte/tests/css/samples/siblings-combinator-slot/expected.css
+++ b/packages/svelte/tests/css/samples/siblings-combinator-slot/expected.css
@@ -1,7 +1,7 @@
.d.svelte-xyz + .e:where(.svelte-xyz) { color: green; }
.a.svelte-xyz + .b:where(.svelte-xyz) { color: green; }
+ .c.svelte-xyz + .f:where(.svelte-xyz) { color: green; }
/* no match */
/* (unused) .b + .c { color: red; }*/
- /* (unused) .c + .f { color: red; }*/
diff --git a/packages/svelte/tests/css/samples/siblings-combinator-slot/input.svelte b/packages/svelte/tests/css/samples/siblings-combinator-slot/input.svelte
index 1b543f97b713..57e1df1507be 100644
--- a/packages/svelte/tests/css/samples/siblings-combinator-slot/input.svelte
+++ b/packages/svelte/tests/css/samples/siblings-combinator-slot/input.svelte
@@ -5,10 +5,10 @@
diff --git a/packages/svelte/tests/css/samples/undefined-with-scope/expected.html b/packages/svelte/tests/css/samples/undefined-with-scope/expected.html
index ddb9429bc891..5548be6e5aa5 100644
--- a/packages/svelte/tests/css/samples/undefined-with-scope/expected.html
+++ b/packages/svelte/tests/css/samples/undefined-with-scope/expected.html
@@ -1 +1,2 @@
-Foo
\ No newline at end of file
+Foo
+Bar
\ No newline at end of file
diff --git a/packages/svelte/tests/css/samples/undefined-with-scope/input.svelte b/packages/svelte/tests/css/samples/undefined-with-scope/input.svelte
index c68fb40dea09..20639600d02e 100644
--- a/packages/svelte/tests/css/samples/undefined-with-scope/input.svelte
+++ b/packages/svelte/tests/css/samples/undefined-with-scope/input.svelte
@@ -1,3 +1,4 @@
-Foo
\ No newline at end of file
+Foo
+Bar
diff --git a/packages/svelte/tests/css/samples/view-transition/expected.css b/packages/svelte/tests/css/samples/view-transition/expected.css
index afc84d52ebf5..e216a4d3ad9b 100644
--- a/packages/svelte/tests/css/samples/view-transition/expected.css
+++ b/packages/svelte/tests/css/samples/view-transition/expected.css
@@ -8,9 +8,15 @@
::view-transition-old {
animation-duration: 0.5s;
}
+ ::view-transition-old:only-child {
+ animation-duration: 0.5s;
+ }
::view-transition-new {
animation-duration: 0.5s;
}
+ ::view-transition-new:only-child {
+ animation-duration: 0.5s;
+ }
::view-transition-image-pair {
animation-duration: 0.5s;
}
diff --git a/packages/svelte/tests/css/samples/view-transition/input.svelte b/packages/svelte/tests/css/samples/view-transition/input.svelte
index ebb2b3fd88e0..345213ccd3f5 100644
--- a/packages/svelte/tests/css/samples/view-transition/input.svelte
+++ b/packages/svelte/tests/css/samples/view-transition/input.svelte
@@ -8,9 +8,15 @@
::view-transition-old {
animation-duration: 0.5s;
}
+ ::view-transition-old:only-child {
+ animation-duration: 0.5s;
+ }
::view-transition-new {
animation-duration: 0.5s;
}
+ ::view-transition-new:only-child {
+ animation-duration: 0.5s;
+ }
::view-transition-image-pair {
animation-duration: 0.5s;
}
diff --git a/packages/svelte/tests/helpers.js b/packages/svelte/tests/helpers.js
index 9d7f71c9bd63..87bcb473e7e2 100644
--- a/packages/svelte/tests/helpers.js
+++ b/packages/svelte/tests/helpers.js
@@ -1,7 +1,7 @@
/** @import { CompileOptions } from '#compiler' */
import * as fs from 'node:fs';
import * as path from 'node:path';
-import glob from 'tiny-glob/sync.js';
+import { globSync } from 'tinyglobby';
import { VERSION, compile, compileModule, preprocess } from 'svelte/compiler';
import { vi } from 'vitest';
@@ -70,7 +70,7 @@ export async function compile_directory(
fs.rmSync(output_dir, { recursive: true, force: true });
- for (let file of glob('**', { cwd, filesOnly: true })) {
+ for (let file of globSync('**', { cwd, onlyFiles: true })) {
if (file.startsWith('_')) continue;
let text = fs.readFileSync(`${cwd}/${file}`, 'utf-8').replace(/\r\n/g, '\n');
diff --git a/packages/svelte/tests/html_equal.js b/packages/svelte/tests/html_equal.js
index 0ebf1fa6bd53..4c9e2a725332 100644
--- a/packages/svelte/tests/html_equal.js
+++ b/packages/svelte/tests/html_equal.js
@@ -86,7 +86,7 @@ export function normalize_html(
clean_children(node);
return node.innerHTML;
} catch (err) {
- throw new Error(`Failed to normalize HTML:\n${html}`);
+ throw new Error(`Failed to normalize HTML:\n${html}\nCause: ${err}`);
}
}
diff --git a/packages/svelte/tests/hydration/samples/element-dir-attribute-sibling/_config.js b/packages/svelte/tests/hydration/samples/element-dir-attribute-sibling/_config.js
new file mode 100644
index 000000000000..e3c629aef989
--- /dev/null
+++ b/packages/svelte/tests/hydration/samples/element-dir-attribute-sibling/_config.js
@@ -0,0 +1,9 @@
+import { test } from '../../test';
+
+export default test({
+ test(assert, target) {
+ const p = target.querySelector('p');
+
+ assert.equal(p?.dir, 'rtl');
+ }
+});
diff --git a/packages/svelte/tests/hydration/samples/element-dir-attribute-sibling/main.svelte b/packages/svelte/tests/hydration/samples/element-dir-attribute-sibling/main.svelte
new file mode 100644
index 000000000000..802edc0feeb6
--- /dev/null
+++ b/packages/svelte/tests/hydration/samples/element-dir-attribute-sibling/main.svelte
@@ -0,0 +1 @@
+text
.
diff --git a/packages/svelte/tests/hydration/samples/input-checked-attribute-sibling/_config.js b/packages/svelte/tests/hydration/samples/input-checked-attribute-sibling/_config.js
new file mode 100644
index 000000000000..31ec66fc8857
--- /dev/null
+++ b/packages/svelte/tests/hydration/samples/input-checked-attribute-sibling/_config.js
@@ -0,0 +1,9 @@
+import { test } from '../../test';
+
+export default test({
+ test(assert, target) {
+ const input = target.querySelector('input');
+
+ assert.equal(input?.checked, true);
+ }
+});
diff --git a/packages/svelte/tests/hydration/samples/input-checked-attribute-sibling/_expected.html b/packages/svelte/tests/hydration/samples/input-checked-attribute-sibling/_expected.html
new file mode 100644
index 000000000000..bcd53f878357
--- /dev/null
+++ b/packages/svelte/tests/hydration/samples/input-checked-attribute-sibling/_expected.html
@@ -0,0 +1 @@
+.
diff --git a/packages/svelte/tests/hydration/samples/input-checked-attribute-sibling/main.svelte b/packages/svelte/tests/hydration/samples/input-checked-attribute-sibling/main.svelte
new file mode 100644
index 000000000000..db3eae870f7c
--- /dev/null
+++ b/packages/svelte/tests/hydration/samples/input-checked-attribute-sibling/main.svelte
@@ -0,0 +1 @@
+.
diff --git a/packages/svelte/tests/hydration/samples/repair-mismatched-a-href/_expected.html b/packages/svelte/tests/hydration/samples/repair-mismatched-a-href/_expected.html
index 2f5b652facf3..e1076af2ec8b 100644
--- a/packages/svelte/tests/hydration/samples/repair-mismatched-a-href/_expected.html
+++ b/packages/svelte/tests/hydration/samples/repair-mismatched-a-href/_expected.html
@@ -1 +1 @@
-foo
+foo foo
diff --git a/packages/svelte/tests/hydration/samples/repair-mismatched-a-href/main.svelte b/packages/svelte/tests/hydration/samples/repair-mismatched-a-href/main.svelte
index be01d05f8e7d..3f0c98801637 100644
--- a/packages/svelte/tests/hydration/samples/repair-mismatched-a-href/main.svelte
+++ b/packages/svelte/tests/hydration/samples/repair-mismatched-a-href/main.svelte
@@ -3,3 +3,4 @@
foo
+foo
diff --git a/packages/svelte/tests/hydration/test.ts b/packages/svelte/tests/hydration/test.ts
index 3bf2dd286cd0..266ac07bff39 100644
--- a/packages/svelte/tests/hydration/test.ts
+++ b/packages/svelte/tests/hydration/test.ts
@@ -2,9 +2,9 @@
import * as fs from 'node:fs';
import { assert } from 'vitest';
-import { compile_directory, should_update_expected } from '../helpers.js';
+import { compile_directory } from '../helpers.js';
import { assert_html_equal } from '../html_equal.js';
-import { suite, assert_ok, type BaseTest } from '../suite.js';
+import { assert_ok, suite, type BaseTest } from '../suite.js';
import { createClassComponent } from 'svelte/legacy';
import { render } from 'svelte/server';
import type { CompileOptions } from '#compiler';
@@ -13,6 +13,7 @@ import { flushSync } from 'svelte';
interface HydrationTest extends BaseTest {
load_compiled?: boolean;
server_props?: Record;
+ id_prefix?: string;
props?: Record;
compileOptions?: Partial;
/**
@@ -50,7 +51,8 @@ const { test, run } = suite(async (config, cwd) => {
const head = window.document.head;
const rendered = render((await import(`${cwd}/_output/server/main.svelte.js`)).default, {
- props: config.server_props ?? config.props ?? {}
+ props: config.server_props ?? config.props ?? {},
+ idPrefix: config?.id_prefix
});
const override = read(`${cwd}/_override.html`);
@@ -103,7 +105,8 @@ const { test, run } = suite(async (config, cwd) => {
component: (await import(`${cwd}/_output/client/main.svelte.js`)).default,
target,
hydrate: true,
- props: config.props
+ props: config.props,
+ idPrefix: config?.id_prefix
});
console.warn = warn;
diff --git a/packages/svelte/tests/migrate/samples/jsdoc-with-comments/input.svelte b/packages/svelte/tests/migrate/samples/jsdoc-with-comments/input.svelte
index f2efb1db804c..f138c3a0707d 100644
--- a/packages/svelte/tests/migrate/samples/jsdoc-with-comments/input.svelte
+++ b/packages/svelte/tests/migrate/samples/jsdoc-with-comments/input.svelte
@@ -21,6 +21,9 @@
*/
export let type_no_comment;
+ /** @type {boolean} type_with_comment - One-line declaration with comment */
+ export let type_with_comment;
+
/**
* This is optional
*/
@@ -40,4 +43,10 @@
export let inline_multiline_trailing_comment = 'world'; /*
* this is a same-line trailing multiline comment
**/
+
+ /** @type {number} [default_value=1] */
+ export let default_value = 1;
+
+ /** @type {number} [comment_default_value=1] - This has a comment and an optional value. */
+ export let comment_default_value = 1;
\ No newline at end of file
diff --git a/packages/svelte/tests/migrate/samples/jsdoc-with-comments/output.svelte b/packages/svelte/tests/migrate/samples/jsdoc-with-comments/output.svelte
index 19fbe38b5093..32133ccd4c85 100644
--- a/packages/svelte/tests/migrate/samples/jsdoc-with-comments/output.svelte
+++ b/packages/svelte/tests/migrate/samples/jsdoc-with-comments/output.svelte
@@ -9,12 +9,18 @@
+
+
+
+
+
+
/**
* @typedef {Object} Props
* @property {string} comment - My wonderful comment
@@ -22,11 +28,14 @@
* @property {any} one_line - one line comment
* @property {any} no_comment
* @property {boolean} type_no_comment
+ * @property {boolean} type_with_comment - One-line declaration with comment
* @property {any} [optional] - This is optional
* @property {any} inline_commented - this should stay a comment
* @property {any} inline_commented_merged - This comment should be merged - with this inline comment
* @property {string} [inline_multiline_leading_comment] - this is a same-line leading multiline comment
* @property {string} [inline_multiline_trailing_comment] - this is a same-line trailing multiline comment
+ * @property {number} [default_value]
+ * @property {number} [comment_default_value] - This has a comment and an optional value.
*/
/** @type {Props} */
@@ -36,10 +45,13 @@
one_line,
no_comment,
type_no_comment,
+ type_with_comment,
optional = {stuff: true},
inline_commented,
inline_commented_merged,
inline_multiline_leading_comment = 'world',
- inline_multiline_trailing_comment = 'world'
+ inline_multiline_trailing_comment = 'world',
+ default_value = 1,
+ comment_default_value = 1
} = $props();
\ No newline at end of file
diff --git a/packages/svelte/tests/migrate/samples/reassigned-deriveds/input.svelte b/packages/svelte/tests/migrate/samples/reassigned-deriveds/input.svelte
new file mode 100644
index 000000000000..024f719fb96b
--- /dev/null
+++ b/packages/svelte/tests/migrate/samples/reassigned-deriveds/input.svelte
@@ -0,0 +1,10 @@
+
+
+
+
+
+{upper}
\ No newline at end of file
diff --git a/packages/svelte/tests/migrate/samples/reassigned-deriveds/output.svelte b/packages/svelte/tests/migrate/samples/reassigned-deriveds/output.svelte
new file mode 100644
index 000000000000..0903299d9599
--- /dev/null
+++ b/packages/svelte/tests/migrate/samples/reassigned-deriveds/output.svelte
@@ -0,0 +1,10 @@
+
+
+
+
+
+{upper}
\ No newline at end of file
diff --git a/packages/svelte/tests/parser-modern/samples/comment-before-function-binding/input.svelte b/packages/svelte/tests/parser-modern/samples/comment-before-function-binding/input.svelte
new file mode 100644
index 000000000000..50200a9eac5e
--- /dev/null
+++ b/packages/svelte/tests/parser-modern/samples/comment-before-function-binding/input.svelte
@@ -0,0 +1,10 @@
+
+
+ value,
+ (v) => value = v.toLowerCase()
+}
+/>
diff --git a/packages/svelte/tests/parser-modern/samples/comment-before-function-binding/output.json b/packages/svelte/tests/parser-modern/samples/comment-before-function-binding/output.json
new file mode 100644
index 000000000000..dba258a6b164
--- /dev/null
+++ b/packages/svelte/tests/parser-modern/samples/comment-before-function-binding/output.json
@@ -0,0 +1,326 @@
+{
+ "css": null,
+ "js": [],
+ "start": 37,
+ "end": 117,
+ "type": "Root",
+ "fragment": {
+ "type": "Fragment",
+ "nodes": [
+ {
+ "type": "Text",
+ "start": 35,
+ "end": 37,
+ "raw": "\n\n",
+ "data": "\n\n"
+ },
+ {
+ "type": "RegularElement",
+ "start": 37,
+ "end": 117,
+ "name": "input",
+ "attributes": [
+ {
+ "start": 44,
+ "end": 114,
+ "type": "BindDirective",
+ "name": "value",
+ "expression": {
+ "type": "SequenceExpression",
+ "start": 68,
+ "end": 112,
+ "loc": {
+ "start": {
+ "line": 7,
+ "column": 1
+ },
+ "end": {
+ "line": 8,
+ "column": 31
+ }
+ },
+ "expressions": [
+ {
+ "type": "ArrowFunctionExpression",
+ "start": 68,
+ "end": 79,
+ "loc": {
+ "start": {
+ "line": 7,
+ "column": 1
+ },
+ "end": {
+ "line": 7,
+ "column": 12
+ }
+ },
+ "id": null,
+ "expression": true,
+ "generator": false,
+ "async": false,
+ "params": [],
+ "body": {
+ "type": "Identifier",
+ "start": 74,
+ "end": 79,
+ "loc": {
+ "start": {
+ "line": 7,
+ "column": 7
+ },
+ "end": {
+ "line": 7,
+ "column": 12
+ }
+ },
+ "name": "value"
+ }
+ },
+ {
+ "type": "ArrowFunctionExpression",
+ "start": 82,
+ "end": 112,
+ "loc": {
+ "start": {
+ "line": 8,
+ "column": 1
+ },
+ "end": {
+ "line": 8,
+ "column": 31
+ }
+ },
+ "id": null,
+ "expression": true,
+ "generator": false,
+ "async": false,
+ "params": [
+ {
+ "type": "Identifier",
+ "start": 83,
+ "end": 84,
+ "loc": {
+ "start": {
+ "line": 8,
+ "column": 2
+ },
+ "end": {
+ "line": 8,
+ "column": 3
+ }
+ },
+ "name": "v"
+ }
+ ],
+ "body": {
+ "type": "AssignmentExpression",
+ "start": 89,
+ "end": 112,
+ "loc": {
+ "start": {
+ "line": 8,
+ "column": 8
+ },
+ "end": {
+ "line": 8,
+ "column": 31
+ }
+ },
+ "operator": "=",
+ "left": {
+ "type": "Identifier",
+ "start": 89,
+ "end": 94,
+ "loc": {
+ "start": {
+ "line": 8,
+ "column": 8
+ },
+ "end": {
+ "line": 8,
+ "column": 13
+ }
+ },
+ "name": "value"
+ },
+ "right": {
+ "type": "CallExpression",
+ "start": 97,
+ "end": 112,
+ "loc": {
+ "start": {
+ "line": 8,
+ "column": 16
+ },
+ "end": {
+ "line": 8,
+ "column": 31
+ }
+ },
+ "callee": {
+ "type": "MemberExpression",
+ "start": 97,
+ "end": 110,
+ "loc": {
+ "start": {
+ "line": 8,
+ "column": 16
+ },
+ "end": {
+ "line": 8,
+ "column": 29
+ }
+ },
+ "object": {
+ "type": "Identifier",
+ "start": 97,
+ "end": 98,
+ "loc": {
+ "start": {
+ "line": 8,
+ "column": 16
+ },
+ "end": {
+ "line": 8,
+ "column": 17
+ }
+ },
+ "name": "v"
+ },
+ "property": {
+ "type": "Identifier",
+ "start": 99,
+ "end": 110,
+ "loc": {
+ "start": {
+ "line": 8,
+ "column": 18
+ },
+ "end": {
+ "line": 8,
+ "column": 29
+ }
+ },
+ "name": "toLowerCase"
+ },
+ "computed": false,
+ "optional": false
+ },
+ "arguments": [],
+ "optional": false
+ }
+ }
+ }
+ ],
+ "leadingComments": [
+ {
+ "type": "Block",
+ "value": "* ( ",
+ "start": 58,
+ "end": 66
+ }
+ ]
+ },
+ "modifiers": []
+ }
+ ],
+ "fragment": {
+ "type": "Fragment",
+ "nodes": []
+ }
+ }
+ ]
+ },
+ "options": null,
+ "instance": {
+ "type": "Script",
+ "start": 0,
+ "end": 35,
+ "context": "default",
+ "content": {
+ "type": "Program",
+ "start": 8,
+ "end": 26,
+ "loc": {
+ "start": {
+ "line": 1,
+ "column": 0
+ },
+ "end": {
+ "line": 3,
+ "column": 0
+ }
+ },
+ "body": [
+ {
+ "type": "VariableDeclaration",
+ "start": 10,
+ "end": 25,
+ "loc": {
+ "start": {
+ "line": 2,
+ "column": 1
+ },
+ "end": {
+ "line": 2,
+ "column": 16
+ }
+ },
+ "declarations": [
+ {
+ "type": "VariableDeclarator",
+ "start": 14,
+ "end": 24,
+ "loc": {
+ "start": {
+ "line": 2,
+ "column": 5
+ },
+ "end": {
+ "line": 2,
+ "column": 15
+ }
+ },
+ "id": {
+ "type": "Identifier",
+ "start": 14,
+ "end": 19,
+ "loc": {
+ "start": {
+ "line": 2,
+ "column": 5
+ },
+ "end": {
+ "line": 2,
+ "column": 10
+ }
+ },
+ "name": "value"
+ },
+ "init": {
+ "type": "Literal",
+ "start": 22,
+ "end": 24,
+ "loc": {
+ "start": {
+ "line": 2,
+ "column": 13
+ },
+ "end": {
+ "line": 2,
+ "column": 15
+ }
+ },
+ "value": "",
+ "raw": "''"
+ }
+ }
+ ],
+ "kind": "let"
+ }
+ ],
+ "sourceType": "module"
+ },
+ "attributes": []
+ }
+}
diff --git a/packages/svelte/tests/parser-modern/samples/comment-before-script/output.json b/packages/svelte/tests/parser-modern/samples/comment-before-script/output.json
index 13c099a85757..1aca0ce03607 100644
--- a/packages/svelte/tests/parser-modern/samples/comment-before-script/output.json
+++ b/packages/svelte/tests/parser-modern/samples/comment-before-script/output.json
@@ -75,7 +75,7 @@
"id": {
"type": "Identifier",
"start": 52,
- "end": 57,
+ "end": 65,
"loc": {
"start": {
"line": 3,
diff --git a/packages/svelte/tests/parser-modern/samples/snippets/output.json b/packages/svelte/tests/parser-modern/samples/snippets/output.json
index 2cf2596b18a5..acf484d8ae16 100644
--- a/packages/svelte/tests/parser-modern/samples/snippets/output.json
+++ b/packages/svelte/tests/parser-modern/samples/snippets/output.json
@@ -28,7 +28,7 @@
{
"type": "Identifier",
"start": 43,
- "end": 46,
+ "end": 54,
"loc": {
"start": {
"line": 3,
diff --git a/packages/svelte/tests/parser-modern/samples/typescript-in-event-handler/output.json b/packages/svelte/tests/parser-modern/samples/typescript-in-event-handler/output.json
index 5d72e01b0ab6..9c515ad9058f 100644
--- a/packages/svelte/tests/parser-modern/samples/typescript-in-event-handler/output.json
+++ b/packages/svelte/tests/parser-modern/samples/typescript-in-event-handler/output.json
@@ -25,7 +25,6 @@
"end": 147,
"type": "OnDirective",
"name": "click",
- "modifiers": [],
"expression": {
"type": "ArrowFunctionExpression",
"start": 73,
@@ -48,7 +47,7 @@
{
"type": "Identifier",
"start": 74,
- "end": 75,
+ "end": 87,
"loc": {
"start": {
"line": 6,
@@ -155,7 +154,7 @@
"id": {
"type": "Identifier",
"start": 102,
- "end": 106,
+ "end": 114,
"loc": {
"start": {
"line": 7,
@@ -316,7 +315,8 @@
}
]
}
- }
+ },
+ "modifiers": []
}
],
"fragment": {
diff --git a/packages/svelte/tests/preprocess/samples/script/expected_map.json b/packages/svelte/tests/preprocess/samples/script/expected_map.json
index d5bf98483f78..22275b5338fa 100644
--- a/packages/svelte/tests/preprocess/samples/script/expected_map.json
+++ b/packages/svelte/tests/preprocess/samples/script/expected_map.json
@@ -1,6 +1,6 @@
{
"version": 3,
- "mappings": "AAAA,CAAC,MAAM,CAAC;AACR,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAc,CAAC,CAAC;AAC7B,CAAC,CAAC,MAAM",
+ "mappings": "AAAA,CAAC,MAAM;AACP,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAc,CAAC;AAC5B,CAAC,CAAC,MAAM",
"names": [],
"sources": [
"input.svelte"
diff --git a/packages/svelte/tests/preprocess/test.ts b/packages/svelte/tests/preprocess/test.ts
index 08f237e28667..81030a634633 100644
--- a/packages/svelte/tests/preprocess/test.ts
+++ b/packages/svelte/tests/preprocess/test.ts
@@ -25,7 +25,7 @@ const { test, run } = suite(async (config, cwd) => {
fs.writeFileSync(`${cwd}/_actual.html.map`, JSON.stringify(result.map, null, 2));
}
- expect(result.code).toMatchFileSnapshot(`${cwd}/output.svelte`);
+ await expect(result.code).toMatchFileSnapshot(`${cwd}/output.svelte`);
expect(result.dependencies).toEqual(config.dependencies || []);
diff --git a/packages/svelte/tests/runtime-browser/assert.js b/packages/svelte/tests/runtime-browser/assert.js
index 249d19f80946..fb460c722a0f 100644
--- a/packages/svelte/tests/runtime-browser/assert.js
+++ b/packages/svelte/tests/runtime-browser/assert.js
@@ -119,6 +119,7 @@ function normalize_children(node) {
* skip_mode?: Array<'server' | 'client' | 'hydrate'>;
* html?: string;
* ssrHtml?: string;
+ * id_prefix?: string;
* props?: Props;
* compileOptions?: Partial;
* test?: (args: {
diff --git a/packages/svelte/tests/runtime-browser/custom-elements-samples/camel-case-attribute/_config.js b/packages/svelte/tests/runtime-browser/custom-elements-samples/camel-case-attribute/_config.js
index ba3be3c25b9c..b9bfdd7782cb 100644
--- a/packages/svelte/tests/runtime-browser/custom-elements-samples/camel-case-attribute/_config.js
+++ b/packages/svelte/tests/runtime-browser/custom-elements-samples/camel-case-attribute/_config.js
@@ -1,3 +1,4 @@
+import { flushSync } from 'svelte';
import { test } from '../../assert';
const tick = () => Promise.resolve();
@@ -14,8 +15,7 @@ export default test({
el.setAttribute('camel-case', 'universe');
el.setAttribute('an-array', '[3,4]');
el.setAttribute('camelcase2', 'Hi');
- await tick();
- await tick();
+ flushSync();
assert.htmlEqual(el.shadowRoot.innerHTML, 'Hi universe! 3
4
');
assert.htmlEqual(
target.innerHTML,
@@ -25,8 +25,7 @@ export default test({
el.camelCase = 'galaxy';
el.camelCase2 = 'Hey';
el.anArray = [5, 6];
- await tick();
- await tick();
+ flushSync();
assert.htmlEqual(el.shadowRoot.innerHTML, 'Hey galaxy! 5
6
');
assert.htmlEqual(
target.innerHTML,
diff --git a/packages/svelte/tests/runtime-browser/custom-elements-samples/custom-method/_config.js b/packages/svelte/tests/runtime-browser/custom-elements-samples/custom-method/_config.js
index dbdf006be5cf..4314926a9493 100644
--- a/packages/svelte/tests/runtime-browser/custom-elements-samples/custom-method/_config.js
+++ b/packages/svelte/tests/runtime-browser/custom-elements-samples/custom-method/_config.js
@@ -1,3 +1,4 @@
+import { flushSync } from 'svelte';
import { test } from '../../assert';
const tick = () => Promise.resolve();
@@ -8,7 +9,8 @@ export default test({
/** @type {any} */
const el = target.querySelector('custom-element');
- await el.updateFoo(42);
+ el.updateFoo(42);
+ flushSync();
const p = el.shadowRoot.querySelector('p');
assert.equal(p.textContent, '42');
diff --git a/packages/svelte/tests/runtime-browser/custom-elements-samples/element-effect-context/_config.js b/packages/svelte/tests/runtime-browser/custom-elements-samples/element-effect-context/_config.js
index 3cf7a66df129..7f2ba9f331d5 100644
--- a/packages/svelte/tests/runtime-browser/custom-elements-samples/element-effect-context/_config.js
+++ b/packages/svelte/tests/runtime-browser/custom-elements-samples/element-effect-context/_config.js
@@ -1,4 +1,5 @@
import { test } from '../../assert';
+import { flushSync } from 'svelte';
const tick = () => Promise.resolve();
export default test({
@@ -16,7 +17,7 @@ export default test({
assert.equal(p.innerHTML, 'false');
button.click();
- await tick();
+ flushSync();
assert.equal(button.innerHTML, '1');
assert.equal(p.innerHTML, 'false');
diff --git a/packages/svelte/tests/runtime-browser/driver-ssr.js b/packages/svelte/tests/runtime-browser/driver-ssr.js
index f5f15b64934f..7067e48a1fb9 100644
--- a/packages/svelte/tests/runtime-browser/driver-ssr.js
+++ b/packages/svelte/tests/runtime-browser/driver-ssr.js
@@ -6,5 +6,5 @@ import config from '__CONFIG__';
import { render } from 'svelte/server';
export default function () {
- return render(SvelteComponent, { props: config.props || {} });
+ return render(SvelteComponent, { props: config.props || {}, idPrefix: config?.id_prefix });
}
diff --git a/packages/svelte/tests/runtime-browser/test-ssr.ts b/packages/svelte/tests/runtime-browser/test-ssr.ts
index 2ff1659f802a..6987fac9155a 100644
--- a/packages/svelte/tests/runtime-browser/test-ssr.ts
+++ b/packages/svelte/tests/runtime-browser/test-ssr.ts
@@ -20,7 +20,7 @@ export async function run_ssr_test(
await compile_directory(test_dir, 'server', config.compileOptions);
const Component = (await import(`${test_dir}/_output/server/main.svelte.js`)).default;
- const { body } = render(Component, { props: config.props || {} });
+ const { body } = render(Component, { props: config.props || {}, idPrefix: config.id_prefix });
fs.writeFileSync(`${test_dir}/_output/rendered.html`, body);
diff --git a/packages/svelte/tests/runtime-legacy/samples/action-custom-event-handler-in-each-destructured/_config.js b/packages/svelte/tests/runtime-legacy/samples/action-custom-event-handler-in-each-destructured/_config.js
index 650d0ec360ac..fe13d43bc815 100644
--- a/packages/svelte/tests/runtime-legacy/samples/action-custom-event-handler-in-each-destructured/_config.js
+++ b/packages/svelte/tests/runtime-legacy/samples/action-custom-event-handler-in-each-destructured/_config.js
@@ -1,3 +1,4 @@
+import { flushSync } from 'svelte';
import { test } from '../../test';
export default test({
@@ -10,12 +11,13 @@ export default test({
second:
`,
- async test({ assert, component, target, window }) {
+ test({ assert, component, target, window }) {
const event = new window.MouseEvent('click');
const buttons = target.querySelectorAll('button');
- await buttons[1].dispatchEvent(event);
+ buttons[1].dispatchEvent(event);
+ flushSync();
assert.htmlEqual(
target.innerHTML,
diff --git a/packages/svelte/tests/runtime-legacy/samples/action-custom-event-handler-in-each/_config.js b/packages/svelte/tests/runtime-legacy/samples/action-custom-event-handler-in-each/_config.js
index 21fb1678734e..83b14cb8a3c3 100644
--- a/packages/svelte/tests/runtime-legacy/samples/action-custom-event-handler-in-each/_config.js
+++ b/packages/svelte/tests/runtime-legacy/samples/action-custom-event-handler-in-each/_config.js
@@ -1,3 +1,4 @@
+import { flushSync } from 'svelte';
import { test } from '../../test';
export default test({
@@ -10,12 +11,13 @@ export default test({
fromState:
`,
- async test({ assert, component, target, window }) {
+ test({ assert, component, target, window }) {
const event = new window.MouseEvent('click');
const buttons = target.querySelectorAll('button');
- await buttons[1].dispatchEvent(event);
+ buttons[1].dispatchEvent(event);
+ flushSync();
assert.htmlEqual(
target.innerHTML,
diff --git a/packages/svelte/tests/runtime-legacy/samples/action-custom-event-handler-node-context/_config.js b/packages/svelte/tests/runtime-legacy/samples/action-custom-event-handler-node-context/_config.js
index a2edd90f8b5f..9276ca8a5997 100644
--- a/packages/svelte/tests/runtime-legacy/samples/action-custom-event-handler-node-context/_config.js
+++ b/packages/svelte/tests/runtime-legacy/samples/action-custom-event-handler-node-context/_config.js
@@ -1,3 +1,4 @@
+import { flushSync } from 'svelte';
import { ok, test } from '../../test';
export default test({
@@ -5,13 +6,14 @@ export default test({
html: '10 ',
- async test({ assert, target, window }) {
+ test({ assert, target, window }) {
const event = new window.MouseEvent('click');
const button = target.querySelector('button');
ok(button);
- await button.dispatchEvent(event);
+ button.dispatchEvent(event);
+ flushSync();
assert.htmlEqual(target.innerHTML, '11 ');
}
diff --git a/packages/svelte/tests/runtime-legacy/samples/action-custom-event-handler-with-context/_config.js b/packages/svelte/tests/runtime-legacy/samples/action-custom-event-handler-with-context/_config.js
index c4d84fc2b38e..2e6aa4dfeef3 100644
--- a/packages/svelte/tests/runtime-legacy/samples/action-custom-event-handler-with-context/_config.js
+++ b/packages/svelte/tests/runtime-legacy/samples/action-custom-event-handler-with-context/_config.js
@@ -1,9 +1,10 @@
+import { flushSync } from 'svelte';
import { ok, test } from '../../test';
export default test({
html: '??? ',
- async test({ assert, target, window }) {
+ test({ assert, target, window }) {
const event = new window.MouseEvent('click', {
clientX: 42,
clientY: 42
@@ -12,7 +13,8 @@ export default test({
const button = target.querySelector('button');
ok(button);
- await button.dispatchEvent(event);
+ button.dispatchEvent(event);
+ flushSync();
assert.htmlEqual(target.innerHTML, '42 ');
}
diff --git a/packages/svelte/tests/runtime-legacy/samples/action-custom-event-handler/_config.js b/packages/svelte/tests/runtime-legacy/samples/action-custom-event-handler/_config.js
index 279171692d24..3769a8bd8126 100644
--- a/packages/svelte/tests/runtime-legacy/samples/action-custom-event-handler/_config.js
+++ b/packages/svelte/tests/runtime-legacy/samples/action-custom-event-handler/_config.js
@@ -1,9 +1,10 @@
+import { flushSync } from 'svelte';
import { ok, test } from '../../test';
export default test({
html: '0, 0 ',
- async test({ assert, target, window }) {
+ test({ assert, target, window }) {
const event = new window.MouseEvent('click', {
clientX: 42,
clientY: 42
@@ -12,7 +13,8 @@ export default test({
const button = target.querySelector('button');
ok(button);
- await button.dispatchEvent(event);
+ button.dispatchEvent(event);
+ flushSync();
assert.htmlEqual(target.innerHTML, '42, 42 ');
}
diff --git a/packages/svelte/tests/runtime-legacy/samples/action-this/_config.js b/packages/svelte/tests/runtime-legacy/samples/action-this/_config.js
index d296fc5a28a5..0ab9305ade40 100644
--- a/packages/svelte/tests/runtime-legacy/samples/action-this/_config.js
+++ b/packages/svelte/tests/runtime-legacy/samples/action-this/_config.js
@@ -1,3 +1,4 @@
+import { flushSync } from 'svelte';
import { ok, test } from '../../test';
export default test({
@@ -8,7 +9,8 @@ export default test({
const click = new window.MouseEvent('click');
assert.htmlEqual(target.innerHTML, '1 ');
- await button.dispatchEvent(click);
+ button.dispatchEvent(click);
+ flushSync();
assert.htmlEqual(target.innerHTML, '2 ');
}
});
diff --git a/packages/svelte/tests/runtime-legacy/samples/animation-css/_config.js b/packages/svelte/tests/runtime-legacy/samples/animation-css/_config.js
index b6b601a96b1d..b6bd818e65db 100644
--- a/packages/svelte/tests/runtime-legacy/samples/animation-css/_config.js
+++ b/packages/svelte/tests/runtime-legacy/samples/animation-css/_config.js
@@ -47,6 +47,8 @@ export default test({
{ id: 1, name: 'a' }
];
+ raf.tick(0);
+
divs = target.querySelectorAll('div');
assert.ok(divs[0].getAnimations().length > 0);
assert.equal(divs[1].getAnimations().length, 0);
diff --git a/packages/svelte/tests/runtime-legacy/samples/animation-js-easing/_config.js b/packages/svelte/tests/runtime-legacy/samples/animation-js-easing/_config.js
index 5b7ed1c73209..f4a3554b29f9 100644
--- a/packages/svelte/tests/runtime-legacy/samples/animation-js-easing/_config.js
+++ b/packages/svelte/tests/runtime-legacy/samples/animation-js-easing/_config.js
@@ -46,6 +46,8 @@ export default test({
{ id: 1, name: 'a' }
];
+ raf.tick(0);
+
divs = document.querySelectorAll('div');
assert.equal(divs[0].dy, 120);
assert.equal(divs[4].dy, -120);
diff --git a/packages/svelte/tests/runtime-legacy/samples/animation-js/_config.js b/packages/svelte/tests/runtime-legacy/samples/animation-js/_config.js
index 3606f7d17b77..a2e17b49f869 100644
--- a/packages/svelte/tests/runtime-legacy/samples/animation-js/_config.js
+++ b/packages/svelte/tests/runtime-legacy/samples/animation-js/_config.js
@@ -46,6 +46,8 @@ export default test({
{ id: 1, name: 'a' }
];
+ raf.tick(0);
+
divs = document.querySelectorAll('div');
assert.equal(divs[0].dy, 120);
assert.equal(divs[4].dy, -120);
@@ -66,6 +68,8 @@ export default test({
{ id: 5, name: 'e' }
];
+ raf.tick(100);
+
divs = document.querySelectorAll('div');
assert.equal(divs[0].dy, 120);
diff --git a/packages/svelte/tests/runtime-legacy/samples/attribute-null-classname-with-style/_config.js b/packages/svelte/tests/runtime-legacy/samples/attribute-null-classname-with-style/_config.js
index c8710f9038b9..cbd0456e1335 100644
--- a/packages/svelte/tests/runtime-legacy/samples/attribute-null-classname-with-style/_config.js
+++ b/packages/svelte/tests/runtime-legacy/samples/attribute-null-classname-with-style/_config.js
@@ -1,17 +1,17 @@
import { ok, test } from '../../test';
export default test({
- html: '
',
+ html: '
',
test({ assert, component, target }) {
const div = target.querySelector('div');
ok(div);
component.testName = null;
- assert.equal(div.className, ' svelte-x1o6ra');
+ assert.equal(div.className, 'svelte-x1o6ra');
component.testName = undefined;
- assert.equal(div.className, ' svelte-x1o6ra');
+ assert.equal(div.className, 'svelte-x1o6ra');
component.testName = undefined + '';
assert.equal(div.className, 'undefined svelte-x1o6ra');
@@ -32,10 +32,10 @@ export default test({
assert.equal(div.className, 'true svelte-x1o6ra');
component.testName = {};
- assert.equal(div.className, ' svelte-x1o6ra');
+ assert.equal(div.className, 'svelte-x1o6ra');
component.testName = '';
- assert.equal(div.className, ' svelte-x1o6ra');
+ assert.equal(div.className, 'svelte-x1o6ra');
component.testName = 'testClassName';
assert.equal(div.className, 'testClassName svelte-x1o6ra');
diff --git a/packages/svelte/tests/runtime-legacy/samples/attribute-null-func-classname-with-style/_config.js b/packages/svelte/tests/runtime-legacy/samples/attribute-null-func-classname-with-style/_config.js
index 8d0f411b8fd2..081fceecf279 100644
--- a/packages/svelte/tests/runtime-legacy/samples/attribute-null-func-classname-with-style/_config.js
+++ b/packages/svelte/tests/runtime-legacy/samples/attribute-null-func-classname-with-style/_config.js
@@ -16,10 +16,10 @@ export default test({
assert.equal(div.className, 'testClassName svelte-x1o6ra');
component.testName = null;
- assert.equal(div.className, ' svelte-x1o6ra');
+ assert.equal(div.className, 'svelte-x1o6ra');
component.testName = undefined;
- assert.equal(div.className, ' svelte-x1o6ra');
+ assert.equal(div.className, 'svelte-x1o6ra');
component.testName = undefined + '';
assert.equal(div.className, 'undefined svelte-x1o6ra');
@@ -40,9 +40,9 @@ export default test({
assert.equal(div.className, 'true svelte-x1o6ra');
component.testName = {};
- assert.equal(div.className, ' svelte-x1o6ra');
+ assert.equal(div.className, 'svelte-x1o6ra');
component.testName = '';
- assert.equal(div.className, ' svelte-x1o6ra');
+ assert.equal(div.className, 'svelte-x1o6ra');
}
});
diff --git a/packages/svelte/tests/runtime-legacy/samples/await-mutate-array/_config.js b/packages/svelte/tests/runtime-legacy/samples/await-mutate-array/_config.js
index 67ea2cb0a3c2..70268a0e1d65 100644
--- a/packages/svelte/tests/runtime-legacy/samples/await-mutate-array/_config.js
+++ b/packages/svelte/tests/runtime-legacy/samples/await-mutate-array/_config.js
@@ -1,3 +1,4 @@
+import { flushSync } from 'svelte';
import { test } from '../../test';
export default test({
@@ -11,7 +12,9 @@ export default test({
const [b1] = target.querySelectorAll('button');
b1.click();
- await Promise.resolve();
+ Promise.resolve();
+ flushSync();
+
assert.htmlEqual(
target.innerHTML,
`2 3 4 \n-------\n1 `
diff --git a/packages/svelte/tests/runtime-legacy/samples/await-then-destruct-object-if/_config.js b/packages/svelte/tests/runtime-legacy/samples/await-then-destruct-object-if/_config.js
index e823c21c9a1b..af04467749cb 100644
--- a/packages/svelte/tests/runtime-legacy/samples/await-then-destruct-object-if/_config.js
+++ b/packages/svelte/tests/runtime-legacy/samples/await-then-destruct-object-if/_config.js
@@ -1,3 +1,4 @@
+import { flushSync } from 'svelte';
import { test } from '../../test';
export default test({
@@ -11,6 +12,7 @@ export default test({
async test({ assert, component, target }) {
await (component.thePromise = Promise.resolve({ result: 1 }));
+ flushSync();
assert.htmlEqual(
target.innerHTML,
@@ -21,6 +23,7 @@ export default test({
);
await new Promise((resolve) => setTimeout(resolve, 1));
+ flushSync();
assert.htmlEqual(
target.innerHTML,
diff --git a/packages/svelte/tests/runtime-legacy/samples/before-render-chain/_config.js b/packages/svelte/tests/runtime-legacy/samples/before-render-chain/_config.js
index 9364bd9f9e5d..580df5ca8106 100644
--- a/packages/svelte/tests/runtime-legacy/samples/before-render-chain/_config.js
+++ b/packages/svelte/tests/runtime-legacy/samples/before-render-chain/_config.js
@@ -1,3 +1,4 @@
+import { flushSync } from 'svelte';
import { test } from '../../test';
export default test({
@@ -9,8 +10,9 @@ export default test({
1
`,
- async test({ assert, component, target }) {
- await component.list.update();
+ test({ assert, component, target }) {
+ component.list.update();
+ flushSync();
assert.htmlEqual(
target.innerHTML,
diff --git a/packages/svelte/tests/runtime-legacy/samples/binding-contenteditable-text-initial/_config.js b/packages/svelte/tests/runtime-legacy/samples/binding-contenteditable-text-initial/_config.js
index e2a28e8dd913..83ad9c2558d8 100644
--- a/packages/svelte/tests/runtime-legacy/samples/binding-contenteditable-text-initial/_config.js
+++ b/packages/svelte/tests/runtime-legacy/samples/binding-contenteditable-text-initial/_config.js
@@ -1,3 +1,4 @@
+import { flushSync } from 'svelte';
import { ok, test } from '../../test';
export default test({
@@ -18,7 +19,7 @@ export default test({
hello
`,
- async test({ assert, component, target, window }) {
+ test({ assert, component, target, window }) {
assert.equal(component.name, 'world');
const el = target.querySelector('editor');
@@ -27,7 +28,8 @@ export default test({
const event = new window.Event('input');
el.textContent = 'everybody';
- await el.dispatchEvent(event);
+ el.dispatchEvent(event);
+ flushSync();
assert.htmlEqual(
target.innerHTML,
diff --git a/packages/svelte/tests/runtime-legacy/samples/binding-contenteditable-text/_config.js b/packages/svelte/tests/runtime-legacy/samples/binding-contenteditable-text/_config.js
index afecc5b3cdd5..494c338bfe8b 100644
--- a/packages/svelte/tests/runtime-legacy/samples/binding-contenteditable-text/_config.js
+++ b/packages/svelte/tests/runtime-legacy/samples/binding-contenteditable-text/_config.js
@@ -1,3 +1,4 @@
+import { flushSync } from 'svelte';
import { ok, test } from '../../test';
export default test({
@@ -10,7 +11,7 @@ export default test({
hello world
`,
- async test({ assert, component, target, window }) {
+ test({ assert, component, target, window }) {
const el = target.querySelector('editor');
ok(el);
assert.equal(el.textContent, 'world');
@@ -18,7 +19,8 @@ export default test({
const event = new window.Event('input');
el.textContent = 'everybody';
- await el.dispatchEvent(event);
+ el.dispatchEvent(event);
+ flushSync();
assert.htmlEqual(
target.innerHTML,
diff --git a/packages/svelte/tests/runtime-legacy/samples/binding-input-checkbox-indeterminate/_config.js b/packages/svelte/tests/runtime-legacy/samples/binding-input-checkbox-indeterminate/_config.js
index 631423feaf56..eba77322a1b9 100644
--- a/packages/svelte/tests/runtime-legacy/samples/binding-input-checkbox-indeterminate/_config.js
+++ b/packages/svelte/tests/runtime-legacy/samples/binding-input-checkbox-indeterminate/_config.js
@@ -1,3 +1,4 @@
+import { flushSync } from 'svelte';
import { ok, test } from '../../test';
export default test({
@@ -17,7 +18,7 @@ export default test({
indeterminate? true
`,
- async test({ assert, component, target, window }) {
+ test({ assert, component, target, window }) {
const input = target.querySelector('input');
ok(input);
@@ -28,7 +29,8 @@ export default test({
input.checked = true;
input.indeterminate = false;
- await input.dispatchEvent(event);
+ input.dispatchEvent(event);
+ flushSync();
assert.equal(component.indeterminate, false);
assert.equal(component.checked, true);
diff --git a/packages/svelte/tests/runtime-legacy/samples/binding-select-in-yield/_config.js b/packages/svelte/tests/runtime-legacy/samples/binding-select-in-yield/_config.js
index f7e54450728c..b8f6a9a74a96 100644
--- a/packages/svelte/tests/runtime-legacy/samples/binding-select-in-yield/_config.js
+++ b/packages/svelte/tests/runtime-legacy/samples/binding-select-in-yield/_config.js
@@ -1,3 +1,4 @@
+import { flushSync } from 'svelte';
import { ok, test } from '../../test';
export default test({
@@ -7,8 +8,9 @@ export default test({
return { letter: 'b' };
},
- async test({ assert, component, target, window }) {
- await component.modal.toggle();
+ test({ assert, component, target, window }) {
+ component.modal.toggle();
+ flushSync();
assert.htmlEqual(
target.innerHTML,
@@ -28,7 +30,8 @@ export default test({
const change = new window.MouseEvent('change');
select.options[2].selected = true;
- await select.dispatchEvent(change);
+ select.dispatchEvent(change);
+ flushSync();
assert.equal(component.letter, 'c');
assert.deepEqual(
@@ -49,9 +52,9 @@ export default test({
`
);
- await component.modal.toggle();
- await component.modal.toggle();
- await Promise.resolve();
+ component.modal.toggle();
+ component.modal.toggle();
+ flushSync();
select = target.querySelector('select');
ok(select);
diff --git a/packages/svelte/tests/runtime-legacy/samples/bindings-coalesced/_config.js b/packages/svelte/tests/runtime-legacy/samples/bindings-coalesced/_config.js
index 85e1faeb730a..3bc5d845a1c3 100644
--- a/packages/svelte/tests/runtime-legacy/samples/bindings-coalesced/_config.js
+++ b/packages/svelte/tests/runtime-legacy/samples/bindings-coalesced/_config.js
@@ -1,7 +1,8 @@
+import { flushSync } from 'svelte';
import { test } from '../../test';
export default test({
- async test({ assert, component }) {
+ test({ assert, component }) {
const { foo, p } = component;
/** @type {string[]} */
@@ -13,7 +14,8 @@ export default test({
}
});
- await foo.double();
+ foo.double();
+ flushSync();
assert.deepEqual(values, ['6']);
}
diff --git a/packages/svelte/tests/runtime-legacy/samples/component-props-mutated/_config.js b/packages/svelte/tests/runtime-legacy/samples/component-props-mutated/_config.js
index 91d0aaa0d3c1..4a5c102ba2fe 100644
--- a/packages/svelte/tests/runtime-legacy/samples/component-props-mutated/_config.js
+++ b/packages/svelte/tests/runtime-legacy/samples/component-props-mutated/_config.js
@@ -1,3 +1,4 @@
+import { flushSync } from 'svelte';
import { test } from '../../test';
const data = {
@@ -15,9 +16,10 @@ export default test({
html: 'hello
',
- async test({ assert, component, target }) {
+ test({ assert, component, target }) {
data.message = 'goodbye';
- await component.$set({ data });
+ component.$set({ data });
+ flushSync();
assert.htmlEqual(target.innerHTML, 'goodbye
');
}
diff --git a/packages/svelte/tests/runtime-legacy/samples/component-slot-let-g/_config.js b/packages/svelte/tests/runtime-legacy/samples/component-slot-let-g/_config.js
index db9afae1b085..f3f5e04be1b1 100644
--- a/packages/svelte/tests/runtime-legacy/samples/component-slot-let-g/_config.js
+++ b/packages/svelte/tests/runtime-legacy/samples/component-slot-let-g/_config.js
@@ -1,3 +1,4 @@
+import { flushSync } from 'svelte';
import { test } from '../../test';
export default test({
@@ -5,7 +6,7 @@ export default test({
1
0
`,
- async test({ assert, target, component, window }) {
+ test({ assert, target, component, window }) {
component.x = 2;
assert.htmlEqual(
@@ -17,7 +18,8 @@ export default test({
);
const span = target.querySelector('span');
- await span?.dispatchEvent(new window.MouseEvent('click', { bubbles: true }));
+ span?.dispatchEvent(new window.MouseEvent('click', { bubbles: true }));
+ flushSync();
assert.htmlEqual(
target.innerHTML,
diff --git a/packages/svelte/tests/runtime-legacy/samples/destructured-props-2/_config.js b/packages/svelte/tests/runtime-legacy/samples/destructured-props-2/_config.js
index 950ee4f1ffb1..0f3fcedce529 100644
--- a/packages/svelte/tests/runtime-legacy/samples/destructured-props-2/_config.js
+++ b/packages/svelte/tests/runtime-legacy/samples/destructured-props-2/_config.js
@@ -1,3 +1,4 @@
+import { flushSync } from 'svelte';
import { test } from '../../test';
export default test({
@@ -10,7 +11,7 @@ export default test({
async test({ component, assert, target }) {
await component.update();
- await Promise.resolve();
+ flushSync();
assert.htmlEqual(
target.innerHTML,
diff --git a/packages/svelte/tests/runtime-legacy/samples/destructured-props-3/_config.js b/packages/svelte/tests/runtime-legacy/samples/destructured-props-3/_config.js
index 9ec2810e71eb..c78b84ec8cd9 100644
--- a/packages/svelte/tests/runtime-legacy/samples/destructured-props-3/_config.js
+++ b/packages/svelte/tests/runtime-legacy/samples/destructured-props-3/_config.js
@@ -1,3 +1,4 @@
+import { flushSync } from 'svelte';
import { test } from '../../test';
export default test({
@@ -8,7 +9,8 @@ export default test({
`,
async test({ component, target, assert }) {
await component.update();
- await Promise.resolve();
+ flushSync();
+
assert.htmlEqual(
target.innerHTML,
`
diff --git a/packages/svelte/tests/runtime-legacy/samples/destructured-props-5/_config.js b/packages/svelte/tests/runtime-legacy/samples/destructured-props-5/_config.js
index 8f7a544bfe90..103ce64d7c7b 100644
--- a/packages/svelte/tests/runtime-legacy/samples/destructured-props-5/_config.js
+++ b/packages/svelte/tests/runtime-legacy/samples/destructured-props-5/_config.js
@@ -1,3 +1,4 @@
+import { flushSync } from 'svelte';
import { test } from '../../test';
export default test({
@@ -10,7 +11,7 @@ export default test({
async test({ component, assert, target }) {
await component.update();
- await Promise.resolve();
+ flushSync();
assert.htmlEqual(
target.innerHTML,
diff --git a/packages/svelte/tests/runtime-legacy/samples/destructuring-assignment-array/_config.js b/packages/svelte/tests/runtime-legacy/samples/destructuring-assignment-array/_config.js
index 2df8753114c2..9c09581097c8 100644
--- a/packages/svelte/tests/runtime-legacy/samples/destructuring-assignment-array/_config.js
+++ b/packages/svelte/tests/runtime-legacy/samples/destructuring-assignment-array/_config.js
@@ -1,3 +1,4 @@
+import { flushSync } from 'svelte';
import { test } from '../../test';
export default test({
@@ -10,9 +11,9 @@ export default test({
`,
- async test({ assert, component, target }) {
- await component.swap(0, 1);
- await Promise.resolve();
+ test({ assert, component, target }) {
+ component.swap(0, 1);
+ flushSync();
assert.htmlEqual(
target.innerHTML,
diff --git a/packages/svelte/tests/runtime-legacy/samples/dynamic-element-animation/_config.js b/packages/svelte/tests/runtime-legacy/samples/dynamic-element-animation/_config.js
index 3d127f1375e7..05c2dc73048a 100644
--- a/packages/svelte/tests/runtime-legacy/samples/dynamic-element-animation/_config.js
+++ b/packages/svelte/tests/runtime-legacy/samples/dynamic-element-animation/_config.js
@@ -50,6 +50,8 @@ export default test({
{ id: 1, name: 'a' }
];
+ raf.tick(0);
+
divs = target.querySelectorAll('div');
assert.equal(divs[0].style.transform, 'translate(0px, 120px)');
assert.equal(divs[1].style.transform, '');
diff --git a/packages/svelte/tests/runtime-legacy/samples/inline-style-directive-spread-and-attr-empty/_config.js b/packages/svelte/tests/runtime-legacy/samples/inline-style-directive-spread-and-attr-empty/_config.js
index 9ff0007c3713..04c9868ac378 100644
--- a/packages/svelte/tests/runtime-legacy/samples/inline-style-directive-spread-and-attr-empty/_config.js
+++ b/packages/svelte/tests/runtime-legacy/samples/inline-style-directive-spread-and-attr-empty/_config.js
@@ -2,6 +2,6 @@ import { test } from '../../test';
export default test({
html: `
-
+
`
});
diff --git a/packages/svelte/tests/runtime-legacy/samples/inline-style-optimisation-bailout/_config.js b/packages/svelte/tests/runtime-legacy/samples/inline-style-optimisation-bailout/_config.js
index adcdc4706d88..e9965b2b1e26 100644
--- a/packages/svelte/tests/runtime-legacy/samples/inline-style-optimisation-bailout/_config.js
+++ b/packages/svelte/tests/runtime-legacy/samples/inline-style-optimisation-bailout/_config.js
@@ -2,7 +2,7 @@ import { ok, test } from '../../test';
export default test({
html: `
- color: red
+ color: red;
`,
test({ assert, component, target, window }) {
diff --git a/packages/svelte/tests/runtime-legacy/samples/inline-style-optimisation-bailout/main.svelte b/packages/svelte/tests/runtime-legacy/samples/inline-style-optimisation-bailout/main.svelte
index 35b768547e25..e07adaa1c9d8 100644
--- a/packages/svelte/tests/runtime-legacy/samples/inline-style-optimisation-bailout/main.svelte
+++ b/packages/svelte/tests/runtime-legacy/samples/inline-style-optimisation-bailout/main.svelte
@@ -1,5 +1,5 @@
{styles}
\ No newline at end of file
diff --git a/packages/svelte/tests/runtime-legacy/samples/key-block-expression-2/_config.js b/packages/svelte/tests/runtime-legacy/samples/key-block-expression-2/_config.js
index a661ec25965e..99fa84e2a9b3 100644
--- a/packages/svelte/tests/runtime-legacy/samples/key-block-expression-2/_config.js
+++ b/packages/svelte/tests/runtime-legacy/samples/key-block-expression-2/_config.js
@@ -1,19 +1,26 @@
+import { flushSync } from 'svelte';
import { test } from '../../test';
export default test({
html: '3
',
- async test({ assert, component, target }) {
+ test({ assert, component, target }) {
const div = target.querySelector('div');
- await component.mutate();
+ component.mutate();
+ flushSync();
+
assert.htmlEqual(target.innerHTML, '5
');
assert.strictEqual(div, target.querySelector('div'));
- await component.reassign();
+ component.reassign();
+ flushSync();
+
assert.htmlEqual(target.innerHTML, '7
');
assert.strictEqual(div, target.querySelector('div'));
- await component.changeKey();
+ component.changeKey();
+ flushSync();
+
assert.htmlEqual(target.innerHTML, '7
');
assert.notStrictEqual(div, target.querySelector('div'));
}
diff --git a/packages/svelte/tests/runtime-legacy/samples/lifecycle-render-order-for-children/_config.js b/packages/svelte/tests/runtime-legacy/samples/lifecycle-render-order-for-children/_config.js
index 73bfd09ceb70..99f9681c4ba5 100644
--- a/packages/svelte/tests/runtime-legacy/samples/lifecycle-render-order-for-children/_config.js
+++ b/packages/svelte/tests/runtime-legacy/samples/lifecycle-render-order-for-children/_config.js
@@ -16,13 +16,13 @@ export default test({
test({ assert, compileOptions, component }) {
assert.deepEqual(order, [
'parent: beforeUpdate 0',
- 'parent: render 0',
'1: beforeUpdate 0',
'1: render 0',
'2: beforeUpdate 0',
'2: render 0',
'3: beforeUpdate 0',
'3: render 0',
+ 'parent: render 0',
'1: onMount 0',
'1: afterUpdate 0',
'2: onMount 0',
@@ -39,13 +39,13 @@ export default test({
assert.deepEqual(order, [
'parent: beforeUpdate 1',
- 'parent: render 1',
'1: beforeUpdate 1',
'1: render 1',
'2: beforeUpdate 1',
'2: render 1',
'3: beforeUpdate 1',
'3: render 1',
+ 'parent: render 1',
'1: afterUpdate 1',
'2: afterUpdate 1',
'3: afterUpdate 1',
diff --git a/packages/svelte/tests/runtime-legacy/samples/ondestroy-prop-access-2/Component.svelte b/packages/svelte/tests/runtime-legacy/samples/ondestroy-prop-access-2/Component.svelte
new file mode 100644
index 000000000000..73347c4d7ff1
--- /dev/null
+++ b/packages/svelte/tests/runtime-legacy/samples/ondestroy-prop-access-2/Component.svelte
@@ -0,0 +1,11 @@
+
+
+{my_prop.foo}
diff --git a/packages/svelte/tests/runtime-legacy/samples/ondestroy-prop-access-2/_config.js b/packages/svelte/tests/runtime-legacy/samples/ondestroy-prop-access-2/_config.js
new file mode 100644
index 000000000000..81005cf73760
--- /dev/null
+++ b/packages/svelte/tests/runtime-legacy/samples/ondestroy-prop-access-2/_config.js
@@ -0,0 +1,14 @@
+import { test } from '../../test';
+import { flushSync } from 'svelte';
+
+export default test({
+ async test({ assert, target, logs }) {
+ const [btn1] = target.querySelectorAll('button');
+
+ flushSync(() => {
+ btn1.click();
+ });
+
+ assert.deepEqual(logs, ['bar']);
+ }
+});
diff --git a/packages/svelte/tests/runtime-legacy/samples/ondestroy-prop-access-2/main.svelte b/packages/svelte/tests/runtime-legacy/samples/ondestroy-prop-access-2/main.svelte
new file mode 100644
index 000000000000..f38b37fb7f7c
--- /dev/null
+++ b/packages/svelte/tests/runtime-legacy/samples/ondestroy-prop-access-2/main.svelte
@@ -0,0 +1,15 @@
+
+
+ {
+ value = undefined;
+ }}>Reset value
+
+{#if value !== undefined}
+
+{/if}
diff --git a/packages/svelte/tests/runtime-legacy/samples/ondestroy-prop-access-3/Component.svelte b/packages/svelte/tests/runtime-legacy/samples/ondestroy-prop-access-3/Component.svelte
new file mode 100644
index 000000000000..5bfb7771289d
--- /dev/null
+++ b/packages/svelte/tests/runtime-legacy/samples/ondestroy-prop-access-3/Component.svelte
@@ -0,0 +1,5 @@
+
+
+
diff --git a/packages/svelte/tests/runtime-legacy/samples/ondestroy-prop-access-3/_config.js b/packages/svelte/tests/runtime-legacy/samples/ondestroy-prop-access-3/_config.js
new file mode 100644
index 000000000000..0eb68310cbb6
--- /dev/null
+++ b/packages/svelte/tests/runtime-legacy/samples/ondestroy-prop-access-3/_config.js
@@ -0,0 +1,11 @@
+import { test } from '../../test';
+import { flushSync } from 'svelte';
+
+export default test({
+ async test({ target }) {
+ const [btn1] = target.querySelectorAll('button');
+
+ btn1.click();
+ flushSync();
+ }
+});
diff --git a/packages/svelte/tests/runtime-legacy/samples/ondestroy-prop-access-3/main.svelte b/packages/svelte/tests/runtime-legacy/samples/ondestroy-prop-access-3/main.svelte
new file mode 100644
index 000000000000..9c72d2c48ac1
--- /dev/null
+++ b/packages/svelte/tests/runtime-legacy/samples/ondestroy-prop-access-3/main.svelte
@@ -0,0 +1,16 @@
+
+
+{#if state}
+ {@const attributes = { title: state.title }}
+
+{/if}
+ {
+ state = undefined;
+ }}
+>
+ Del
+
diff --git a/packages/svelte/tests/runtime-legacy/samples/ondestroy-prop-access/Component.svelte b/packages/svelte/tests/runtime-legacy/samples/ondestroy-prop-access/Component.svelte
new file mode 100644
index 000000000000..761f303c2e0c
--- /dev/null
+++ b/packages/svelte/tests/runtime-legacy/samples/ondestroy-prop-access/Component.svelte
@@ -0,0 +1,12 @@
+
+
+{count}
+
+ count-- }>
diff --git a/packages/svelte/tests/runtime-legacy/samples/ondestroy-prop-access/_config.js b/packages/svelte/tests/runtime-legacy/samples/ondestroy-prop-access/_config.js
new file mode 100644
index 000000000000..2ffb7e653f15
--- /dev/null
+++ b/packages/svelte/tests/runtime-legacy/samples/ondestroy-prop-access/_config.js
@@ -0,0 +1,68 @@
+import { test } from '../../test';
+import { flushSync } from 'svelte';
+
+export default test({
+ async test({ assert, target, logs }) {
+ const [btn1, btn2, btn3] = target.querySelectorAll('button');
+ let ps = [...target.querySelectorAll('p')];
+
+ for (const p of ps) {
+ assert.equal(p.innerHTML, '0');
+ }
+
+ flushSync(() => {
+ btn1.click();
+ });
+
+ // prop update normally if we are not unmounting
+ for (const p of ps) {
+ assert.equal(p.innerHTML, '1');
+ }
+
+ flushSync(() => {
+ btn3.click();
+ });
+
+ // binding still works and update the value correctly
+ for (const p of ps) {
+ assert.equal(p.innerHTML, '0');
+ }
+
+ flushSync(() => {
+ btn1.click();
+ });
+
+ flushSync(() => {
+ btn1.click();
+ });
+
+ console.warn(logs);
+
+ // the five components guarded by `count < 2` unmount and log
+ assert.deepEqual(logs, [1, true, 1, true, 1, true, 1, true, 1, true]);
+
+ flushSync(() => {
+ btn2.click();
+ });
+
+ // the three components guarded by `show` unmount and log
+ assert.deepEqual(logs, [
+ 1,
+ true,
+ 1,
+ true,
+ 1,
+ true,
+ 1,
+ true,
+ 1,
+ true,
+ 2,
+ true,
+ 2,
+ true,
+ 2,
+ true
+ ]);
+ }
+});
diff --git a/packages/svelte/tests/runtime-legacy/samples/ondestroy-prop-access/main.svelte b/packages/svelte/tests/runtime-legacy/samples/ondestroy-prop-access/main.svelte
new file mode 100644
index 000000000000..73a7501e9db2
--- /dev/null
+++ b/packages/svelte/tests/runtime-legacy/samples/ondestroy-prop-access/main.svelte
@@ -0,0 +1,41 @@
+
+
+ count++ }>
+ show = !show }>
+
+
+{#if count < 2}
+
+{/if}
+
+
+{#if count < 2}
+
+{/if}
+
+
+{#if count < 2}
+
+{/if}
+
+
+{#if show}
+
+{/if}
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/svelte/tests/runtime-legacy/samples/prop-const/_config.js b/packages/svelte/tests/runtime-legacy/samples/prop-const/_config.js
index 040b911cc68c..804ee53342ec 100644
--- a/packages/svelte/tests/runtime-legacy/samples/prop-const/_config.js
+++ b/packages/svelte/tests/runtime-legacy/samples/prop-const/_config.js
@@ -1,3 +1,4 @@
+import { flushSync } from 'svelte';
import { test } from '../../test';
export default test({
@@ -10,11 +11,12 @@ export default test({
b: 2
`,
- async test({ assert, component, target }) {
- await component.$set({
+ test({ assert, component, target }) {
+ component.$set({
a: 5,
b: 6
});
+ flushSync();
assert.htmlEqual(
target.innerHTML,
diff --git a/packages/svelte/tests/runtime-legacy/samples/props-reactive-b/_config.js b/packages/svelte/tests/runtime-legacy/samples/props-reactive-b/_config.js
index b92d81bd53e7..91133f0bf4b7 100644
--- a/packages/svelte/tests/runtime-legacy/samples/props-reactive-b/_config.js
+++ b/packages/svelte/tests/runtime-legacy/samples/props-reactive-b/_config.js
@@ -1,3 +1,4 @@
+import { flushSync } from 'svelte';
import { test } from '../../test';
export default test({
@@ -11,8 +12,9 @@ export default test({
c: 3
`,
- async test({ assert, component, target }) {
- await component.$set({ a: 4 });
+ test({ assert, component, target }) {
+ component.$set({ a: 4 });
+ flushSync();
assert.htmlEqual(
target.innerHTML,
@@ -23,7 +25,8 @@ export default test({
`
);
- await component.$set({ b: 5 });
+ component.$set({ b: 5 });
+ flushSync();
assert.htmlEqual(
target.innerHTML,
diff --git a/packages/svelte/tests/runtime-legacy/samples/reactive-assignment-in-complex-declaration-with-store-3/_config.js b/packages/svelte/tests/runtime-legacy/samples/reactive-assignment-in-complex-declaration-with-store-3/_config.js
index 2325d17cd5a8..1a67e46b38a2 100644
--- a/packages/svelte/tests/runtime-legacy/samples/reactive-assignment-in-complex-declaration-with-store-3/_config.js
+++ b/packages/svelte/tests/runtime-legacy/samples/reactive-assignment-in-complex-declaration-with-store-3/_config.js
@@ -1,3 +1,4 @@
+import { flushSync } from 'svelte';
import { test } from '../../test';
import { store } from './store.js';
@@ -6,10 +7,9 @@ export default test({
before_test() {
store.reset();
},
- async test({ assert, target }) {
+ test({ assert, target }) {
store.set(42);
-
- await Promise.resolve();
+ flushSync();
assert.htmlEqual(target.innerHTML, '42 ');
diff --git a/packages/svelte/tests/runtime-legacy/samples/reactive-value-dependency-not-referenced/_config.js b/packages/svelte/tests/runtime-legacy/samples/reactive-value-dependency-not-referenced/_config.js
index f2f7fe92b19f..3b31ee766e33 100644
--- a/packages/svelte/tests/runtime-legacy/samples/reactive-value-dependency-not-referenced/_config.js
+++ b/packages/svelte/tests/runtime-legacy/samples/reactive-value-dependency-not-referenced/_config.js
@@ -1,3 +1,4 @@
+import { flushSync } from 'svelte';
import { test } from '../../test';
export default test({
@@ -6,29 +7,35 @@ export default test({
42
`,
- async test({ assert, component, target }) {
- await component.updateStore(undefined);
- await Promise.resolve();
+ test({ assert, component, target }) {
+ component.updateStore(undefined);
+ flushSync();
+
assert.htmlEqual(target.innerHTML, '
42
');
- await component.updateStore(33);
- await Promise.resolve();
+ component.updateStore(33);
+ flushSync();
+
assert.htmlEqual(target.innerHTML, '33
42
');
- await component.updateStore(undefined);
- await Promise.resolve();
+ component.updateStore(undefined);
+ flushSync();
+
assert.htmlEqual(target.innerHTML, '
42
');
- await component.updateVar(undefined);
- await Promise.resolve();
+ component.updateVar(undefined);
+ flushSync();
+
assert.htmlEqual(target.innerHTML, '
');
- await component.updateVar(33);
- await Promise.resolve();
+ component.updateVar(33);
+ flushSync();
+
assert.htmlEqual(target.innerHTML, '
33
');
- await component.updateVar(undefined);
- await Promise.resolve();
+ component.updateVar(undefined);
+ flushSync();
+
assert.htmlEqual(target.innerHTML, '
');
}
});
diff --git a/packages/svelte/tests/runtime-legacy/samples/reactive-value-function/_config.js b/packages/svelte/tests/runtime-legacy/samples/reactive-value-function/_config.js
index cbcb19d95e1d..5317ab496fb5 100644
--- a/packages/svelte/tests/runtime-legacy/samples/reactive-value-function/_config.js
+++ b/packages/svelte/tests/runtime-legacy/samples/reactive-value-function/_config.js
@@ -1,10 +1,12 @@
+import { flushSync } from 'svelte';
import { test } from '../../test';
export default test({
html: '1-2',
- async test({ assert, component, target }) {
- await component.update();
+ test({ assert, component, target }) {
+ component.update();
+ flushSync();
assert.htmlEqual(target.innerHTML, '3-4');
}
diff --git a/packages/svelte/tests/runtime-legacy/samples/reactive-values-text-node/_config.js b/packages/svelte/tests/runtime-legacy/samples/reactive-values-text-node/_config.js
index e05c8c80dd18..e97a046f68c1 100644
--- a/packages/svelte/tests/runtime-legacy/samples/reactive-values-text-node/_config.js
+++ b/packages/svelte/tests/runtime-legacy/samples/reactive-values-text-node/_config.js
@@ -1,5 +1,6 @@
import { test } from '../../test';
import { create_deferred } from '../../../helpers';
+import { flushSync } from 'svelte';
/** @type {ReturnType} */
let deferred;
@@ -17,6 +18,8 @@ export default test({
async test({ assert, target }) {
await deferred.promise;
+ flushSync();
+
assert.htmlEqual(
target.innerHTML,
`
diff --git a/packages/svelte/tests/runtime-legacy/samples/spread-own-props/_config.js b/packages/svelte/tests/runtime-legacy/samples/spread-own-props/_config.js
index 708e3615f1d4..748605544000 100644
--- a/packages/svelte/tests/runtime-legacy/samples/spread-own-props/_config.js
+++ b/packages/svelte/tests/runtime-legacy/samples/spread-own-props/_config.js
@@ -1,3 +1,4 @@
+import { flushSync } from 'svelte';
import { test } from '../../test';
export default test({
@@ -17,13 +18,14 @@ export default test({
quux: core
`,
- async test({ assert, component, target }) {
- await component.$set({
+ test({ assert, component, target }) {
+ component.$set({
foo: 'wut',
baz: 40 + 3,
qux: `this is a ${'rather boring'} string`,
quux: 'heart'
});
+ flushSync();
assert.htmlEqual(
target.innerHTML,
diff --git a/packages/svelte/tests/runtime-legacy/samples/store-auto-subscribe-in-reactive-declaration-2/_config.js b/packages/svelte/tests/runtime-legacy/samples/store-auto-subscribe-in-reactive-declaration-2/_config.js
index 8e0886c66841..d380150e55d8 100644
--- a/packages/svelte/tests/runtime-legacy/samples/store-auto-subscribe-in-reactive-declaration-2/_config.js
+++ b/packages/svelte/tests/runtime-legacy/samples/store-auto-subscribe-in-reactive-declaration-2/_config.js
@@ -1,3 +1,4 @@
+import { flushSync } from 'svelte';
import { test } from '../../test';
export default test({
@@ -6,10 +7,9 @@ export default test({
Hello World
`,
- async test({ assert, component, target }) {
- await component.update_value('Hi Svelte');
- await Promise.resolve();
- await Promise.resolve();
+ test({ assert, component, target }) {
+ component.update_value('Hi Svelte');
+ flushSync();
assert.htmlEqual(
target.innerHTML,
diff --git a/packages/svelte/tests/runtime-legacy/samples/store-increment-updates-reactive/_config.js b/packages/svelte/tests/runtime-legacy/samples/store-increment-updates-reactive/_config.js
index 1c81f7c4e81e..89ff695d9dfb 100644
--- a/packages/svelte/tests/runtime-legacy/samples/store-increment-updates-reactive/_config.js
+++ b/packages/svelte/tests/runtime-legacy/samples/store-increment-updates-reactive/_config.js
@@ -1,10 +1,13 @@
+import { flushSync } from 'svelte';
import { test } from '../../test';
export default test({
html: '0',
- async test({ assert, component, target }) {
- await component.increment();
+ test({ assert, component, target }) {
+ component.increment();
+ flushSync();
+
assert.htmlEqual(target.innerHTML, '1');
}
});
diff --git a/packages/svelte/tests/runtime-legacy/samples/store-unreferenced/_config.js b/packages/svelte/tests/runtime-legacy/samples/store-unreferenced/_config.js
index 808ed899d32f..7096f5d1fefe 100644
--- a/packages/svelte/tests/runtime-legacy/samples/store-unreferenced/_config.js
+++ b/packages/svelte/tests/runtime-legacy/samples/store-unreferenced/_config.js
@@ -1,3 +1,4 @@
+import { flushSync } from 'svelte';
import { test } from '../../test';
import { count } from './store.js';
@@ -8,8 +9,9 @@ export default test({
count.set(0);
},
- async test({ assert, component, target }) {
- await component.increment();
+ test({ assert, component, target }) {
+ component.increment();
+ flushSync();
assert.htmlEqual(target.innerHTML, 'count: 1
');
}
diff --git a/packages/svelte/tests/runtime-legacy/samples/window-event-custom/_config.js b/packages/svelte/tests/runtime-legacy/samples/window-event-custom/_config.js
index 32b1c65154e0..f6b7a8af4228 100644
--- a/packages/svelte/tests/runtime-legacy/samples/window-event-custom/_config.js
+++ b/packages/svelte/tests/runtime-legacy/samples/window-event-custom/_config.js
@@ -1,14 +1,16 @@
+import { flushSync } from 'svelte';
import { test } from '../../test';
export default test({
html: 'escaped: false
',
- async test({ assert, target, window }) {
+ test({ assert, target, window }) {
const event = new window.KeyboardEvent('keydown', {
key: 'Escape'
});
- await window.dispatchEvent(event);
+ window.dispatchEvent(event);
+ flushSync();
assert.htmlEqual(
target.innerHTML,
diff --git a/packages/svelte/tests/runtime-legacy/shared.ts b/packages/svelte/tests/runtime-legacy/shared.ts
index e6dc0f385bf9..fc748ce6b299 100644
--- a/packages/svelte/tests/runtime-legacy/shared.ts
+++ b/packages/svelte/tests/runtime-legacy/shared.ts
@@ -1,6 +1,6 @@
import * as fs from 'node:fs';
import { setImmediate } from 'node:timers/promises';
-import glob from 'tiny-glob/sync.js';
+import { globSync } from 'tinyglobby';
import { createClassComponent } from 'svelte/legacy';
import { proxy } from 'svelte/internal/client';
import { flushSync, hydrate, mount, unmount } from 'svelte';
@@ -36,6 +36,7 @@ export interface RuntimeTest = Record;
props?: Props;
server_props?: Props;
+ id_prefix?: string;
before_test?: () => void;
after_test?: () => void;
test?: (args: {
@@ -256,7 +257,7 @@ async function run_test_variant(
raf.reset();
// Put things we need on window for testing
- const styles = glob('**/*.css', { cwd: `${cwd}/_output/client` })
+ const styles = globSync('**/*.css', { cwd: `${cwd}/_output/client` })
.map((file) => fs.readFileSync(`${cwd}/_output/client/${file}`, 'utf-8'))
.join('\n')
.replace(/\/\*<\/?style>\*\//g, '');
@@ -284,7 +285,8 @@ async function run_test_variant(
// ssr into target
const SsrSvelteComponent = (await import(`${cwd}/_output/server/main.svelte.js`)).default;
const { html, head } = render(SsrSvelteComponent, {
- props: config.server_props ?? config.props ?? {}
+ props: config.server_props ?? config.props ?? {},
+ idPrefix: config.id_prefix
});
fs.writeFileSync(`${cwd}/_output/rendered.html`, html);
@@ -345,6 +347,10 @@ async function run_test_variant(
if (runes) {
props = proxy({ ...(config.props || {}) });
+
+ // @ts-expect-error
+ globalThis.__svelte.uid = 1;
+
if (manual_hydrate) {
hydrate_fn = () => {
instance = hydrate(mod.default, {
diff --git a/packages/svelte/tests/runtime-runes/samples/attribute-boolean-case-insensitivity/_config.js b/packages/svelte/tests/runtime-runes/samples/attribute-boolean-case-insensitivity/_config.js
new file mode 100644
index 000000000000..d0b8a421b369
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/attribute-boolean-case-insensitivity/_config.js
@@ -0,0 +1,60 @@
+import { test } from '../../test';
+
+export default test({
+ // JSDOM lacks support for some of these attributes, so we'll skip it for now.
+ //
+ // See:
+ // - `async`: https://github.com/jsdom/jsdom/issues/1564
+ // - `nomodule`: https://github.com/jsdom/jsdom/issues/2475
+ // - `autofocus`: https://github.com/jsdom/jsdom/issues/3041
+ // - `inert`: https://github.com/jsdom/jsdom/issues/3605
+ // - etc...: https://github.com/jestjs/jest/issues/139#issuecomment-592673550
+ skip_mode: ['client'],
+
+ html: `
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+`
+});
diff --git a/packages/svelte/tests/runtime-runes/samples/attribute-boolean-case-insensitivity/main.svelte b/packages/svelte/tests/runtime-runes/samples/attribute-boolean-case-insensitivity/main.svelte
new file mode 100644
index 000000000000..e9e5a1616803
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/attribute-boolean-case-insensitivity/main.svelte
@@ -0,0 +1,22 @@
+
+
+{#each attributeValues as val}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+{/each}
diff --git a/packages/svelte/tests/runtime-runes/samples/attribute-spread-input/_config.js b/packages/svelte/tests/runtime-runes/samples/attribute-spread-input/_config.js
new file mode 100644
index 000000000000..ab941255037e
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/attribute-spread-input/_config.js
@@ -0,0 +1,33 @@
+import { flushSync } from 'svelte';
+import { test } from '../../test';
+
+export default test({
+ async test({ target, assert }) {
+ // Test for https://github.com/sveltejs/svelte/issues/15237
+ const [setValues, clearValue] = target.querySelectorAll('button');
+ const [text1, text2, check1, check2] = target.querySelectorAll('input');
+
+ assert.equal(text1.value, '');
+ assert.equal(text2.value, '');
+ assert.equal(check1.checked, false);
+ assert.equal(check2.checked, false);
+
+ flushSync(() => {
+ setValues.click();
+ });
+
+ assert.equal(text1.value, 'message');
+ assert.equal(text2.value, 'message');
+ assert.equal(check1.checked, true);
+ assert.equal(check2.checked, true);
+
+ flushSync(() => {
+ clearValue.click();
+ });
+
+ assert.equal(text1.value, '');
+ assert.equal(text2.value, '');
+ assert.equal(check1.checked, false);
+ assert.equal(check2.checked, false);
+ }
+});
diff --git a/packages/svelte/tests/runtime-runes/samples/attribute-spread-input/main.svelte b/packages/svelte/tests/runtime-runes/samples/attribute-spread-input/main.svelte
new file mode 100644
index 000000000000..4bb4365ee270
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/attribute-spread-input/main.svelte
@@ -0,0 +1,22 @@
+
+
+setValues
+clearValues
+
+
+
+
+
+
diff --git a/packages/svelte/tests/runtime-runes/samples/autofocus-with-call/_config.js b/packages/svelte/tests/runtime-runes/samples/autofocus-with-call/_config.js
new file mode 100644
index 000000000000..0597c2fda899
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/autofocus-with-call/_config.js
@@ -0,0 +1,7 @@
+import { test } from '../../test';
+
+export default test({
+ async test({ assert, errors }) {
+ assert.deepEqual(errors, []);
+ }
+});
diff --git a/packages/svelte/tests/runtime-runes/samples/autofocus-with-call/main.svelte b/packages/svelte/tests/runtime-runes/samples/autofocus-with-call/main.svelte
new file mode 100644
index 000000000000..cb3804af34e4
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/autofocus-with-call/main.svelte
@@ -0,0 +1,6 @@
+
+
+
+
\ No newline at end of file
diff --git a/packages/svelte/tests/runtime-runes/samples/bind-current-time-remove-listener/_config.js b/packages/svelte/tests/runtime-runes/samples/bind-current-time-remove-listener/_config.js
new file mode 100644
index 000000000000..29dc5e8d7fbb
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/bind-current-time-remove-listener/_config.js
@@ -0,0 +1,26 @@
+import { flushSync } from 'svelte';
+import { ok, test } from '../../test';
+
+export default test({
+ async test({ assert, target, logs }) {
+ const audio = target.querySelector('audio');
+ const btn = target.querySelector('button');
+
+ ok(audio);
+
+ flushSync(() => {
+ audio.currentTime = 10;
+ audio.dispatchEvent(new Event('timeupdate'));
+ });
+ assert.deepEqual(logs, ['event']);
+
+ flushSync(() => {
+ btn?.click();
+ });
+ flushSync(() => {
+ audio.currentTime = 20;
+ audio.dispatchEvent(new Event('timeupdate'));
+ });
+ assert.deepEqual(logs, ['event']);
+ }
+});
diff --git a/packages/svelte/tests/runtime-runes/samples/bind-current-time-remove-listener/main.svelte b/packages/svelte/tests/runtime-runes/samples/bind-current-time-remove-listener/main.svelte
new file mode 100644
index 000000000000..40c215378e53
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/bind-current-time-remove-listener/main.svelte
@@ -0,0 +1,12 @@
+
+
+ show = false}>
+{#if show}
+ time,(new_time)=>{
+ console.log("event");
+ time = new_time;
+ }}>
+{/if}
\ No newline at end of file
diff --git a/packages/svelte/tests/runtime-runes/samples/class-directive-mutations/_config.js b/packages/svelte/tests/runtime-runes/samples/class-directive-mutations/_config.js
new file mode 100644
index 000000000000..076efee994ec
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/class-directive-mutations/_config.js
@@ -0,0 +1,126 @@
+import { flushSync, tick } from 'svelte';
+import { test } from '../../test';
+
+// This test counts mutations on hydration
+// set_class() should not mutate class on hydration, except if mismatch
+export default test({
+ mode: ['server', 'hydrate'],
+
+ server_props: {
+ browser: false
+ },
+
+ props: {
+ browser: true
+ },
+
+ html: `
+
+
+
+
+
+
+
+
+
+
+
+ `,
+
+ ssrHtml: `
+
+
+
+
+
+
+
+
+
+
+
+ `,
+
+ async test({ target, assert, component, instance }) {
+ flushSync();
+ tick();
+ assert.deepEqual(instance.get_and_clear_mutations(), ['MAIN']);
+
+ component.foo = false;
+ flushSync();
+ tick();
+ assert.deepEqual(
+ instance.get_and_clear_mutations(),
+ ['DIV', 'SPAN', 'B', 'I', 'DIV', 'SPAN', 'B', 'I'],
+ 'first mutation'
+ );
+
+ assert.htmlEqual(
+ target.innerHTML,
+ `
+
+
+
+
+
+
+
+
+
+
+
+ `
+ );
+
+ component.foo = true;
+ flushSync();
+ assert.deepEqual(
+ instance.get_and_clear_mutations(),
+ ['DIV', 'SPAN', 'B', 'I', 'DIV', 'SPAN', 'B', 'I'],
+ 'second mutation'
+ );
+
+ assert.htmlEqual(
+ target.innerHTML,
+ `
+
+
+
+
+
+
+
+
+
+
+
+ `
+ );
+
+ component.classname = 'another';
+ flushSync();
+ assert.deepEqual(
+ instance.get_and_clear_mutations(),
+ ['DIV', 'B', 'DIV', 'B'],
+ 'class mutation'
+ );
+
+ assert.htmlEqual(
+ target.innerHTML,
+ `
+
+
+
+
+
+
+
+
+
+
+
+ `
+ );
+ }
+});
diff --git a/packages/svelte/tests/runtime-runes/samples/class-directive-mutations/main.svelte b/packages/svelte/tests/runtime-runes/samples/class-directive-mutations/main.svelte
new file mode 100644
index 000000000000..d748988e2190
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/class-directive-mutations/main.svelte
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/svelte/tests/runtime-runes/samples/class-directive/_config.js b/packages/svelte/tests/runtime-runes/samples/class-directive/_config.js
new file mode 100644
index 000000000000..2756b40493e2
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/class-directive/_config.js
@@ -0,0 +1,145 @@
+import { flushSync } from 'svelte';
+import { test } from '../../test';
+
+export default test({
+ html: `
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ `,
+ test({ assert, target, component }) {
+ component.foo = true;
+ flushSync();
+
+ assert.htmlEqual(
+ target.innerHTML,
+ `
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ `
+ );
+
+ component.bar = false;
+ flushSync();
+
+ assert.htmlEqual(
+ target.innerHTML,
+ `
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ `
+ );
+
+ component.foo = false;
+ flushSync();
+
+ assert.htmlEqual(
+ target.innerHTML,
+ `
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ `
+ );
+ }
+});
diff --git a/packages/svelte/tests/runtime-runes/samples/class-directive/main.svelte b/packages/svelte/tests/runtime-runes/samples/class-directive/main.svelte
new file mode 100644
index 000000000000..966c07a78e8d
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/class-directive/main.svelte
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/svelte/tests/runtime-runes/samples/const-tag-boundary/FlakyComponent.svelte b/packages/svelte/tests/runtime-runes/samples/const-tag-boundary/FlakyComponent.svelte
new file mode 100644
index 000000000000..8bbec90de4ea
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/const-tag-boundary/FlakyComponent.svelte
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/packages/svelte/tests/runtime-runes/samples/const-tag-boundary/_config.js b/packages/svelte/tests/runtime-runes/samples/const-tag-boundary/_config.js
new file mode 100644
index 000000000000..4338969a48e0
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/const-tag-boundary/_config.js
@@ -0,0 +1,17 @@
+import { flushSync } from 'svelte';
+import { test } from '../../test';
+
+export default test({
+ html: '2
',
+ mode: ['client'],
+ test({ target, assert }) {
+ const btn = target.querySelector('button');
+ const p = target.querySelector('p');
+
+ flushSync(() => {
+ btn?.click();
+ });
+
+ assert.equal(p?.innerHTML, '4');
+ }
+});
diff --git a/packages/svelte/tests/runtime-runes/samples/const-tag-boundary/main.svelte b/packages/svelte/tests/runtime-runes/samples/const-tag-boundary/main.svelte
new file mode 100644
index 000000000000..25ea8a3ffc59
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/const-tag-boundary/main.svelte
@@ -0,0 +1,14 @@
+
+
+test++}>
+
+
+ {@const double = test * 2}
+ {#snippet failed()}
+ {double}
+ {/snippet}
+
+
\ No newline at end of file
diff --git a/packages/svelte/tests/runtime-runes/samples/custom-element-attributes/_config.js b/packages/svelte/tests/runtime-runes/samples/custom-element-attributes/_config.js
new file mode 100644
index 000000000000..7f406d8f0d28
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/custom-element-attributes/_config.js
@@ -0,0 +1,24 @@
+import { test } from '../../test';
+
+export default test({
+ mode: ['client'],
+ async test({ assert, target }) {
+ const my_element = /** @type HTMLElement & { object: { test: true }; } */ (
+ target.querySelector('my-element')
+ );
+ assert.equal(my_element.getAttribute('string'), 'test');
+ assert.equal(my_element.hasAttribute('object'), false);
+ assert.deepEqual(my_element.object, { test: true });
+
+ const my_link = /** @type HTMLAnchorElement & { object: { test: true }; } */ (
+ target.querySelector('a')
+ );
+ assert.equal(my_link.getAttribute('string'), 'test');
+ assert.equal(my_link.hasAttribute('object'), false);
+ assert.deepEqual(my_link.object, { test: true });
+
+ const [value1, value2] = target.querySelectorAll('value-element');
+ assert.equal(value1.shadowRoot?.innerHTML, 'test ');
+ assert.equal(value2.shadowRoot?.innerHTML, 'test ');
+ }
+});
diff --git a/packages/svelte/tests/runtime-runes/samples/custom-element-attributes/main.svelte b/packages/svelte/tests/runtime-runes/samples/custom-element-attributes/main.svelte
new file mode 100644
index 000000000000..4c98245e5b6b
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/custom-element-attributes/main.svelte
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
diff --git a/packages/svelte/tests/runtime-runes/samples/derived-unowned-11/Child.svelte b/packages/svelte/tests/runtime-runes/samples/derived-unowned-11/Child.svelte
new file mode 100644
index 000000000000..cd215304a326
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/derived-unowned-11/Child.svelte
@@ -0,0 +1,5 @@
+
+
+ value = 'a'}>change
diff --git a/packages/svelte/tests/runtime-runes/samples/derived-unowned-11/Child2.svelte b/packages/svelte/tests/runtime-runes/samples/derived-unowned-11/Child2.svelte
new file mode 100644
index 000000000000..a1d9f93bec71
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/derived-unowned-11/Child2.svelte
@@ -0,0 +1,5 @@
+
+
+{disabled}
diff --git a/packages/svelte/tests/runtime-runes/samples/derived-unowned-11/_config.js b/packages/svelte/tests/runtime-runes/samples/derived-unowned-11/_config.js
new file mode 100644
index 000000000000..9948f9196683
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/derived-unowned-11/_config.js
@@ -0,0 +1,16 @@
+import { flushSync } from 'svelte';
+import { test } from '../../test';
+
+export default test({
+ async test({ assert, target }) {
+ let [btn1, btn2] = target.querySelectorAll('button');
+
+ btn1?.click();
+ flushSync();
+
+ btn2?.click();
+ flushSync();
+
+ assert.htmlEqual(target.innerHTML, `change change \nfalse`);
+ }
+});
diff --git a/packages/svelte/tests/runtime-runes/samples/derived-unowned-11/main.svelte b/packages/svelte/tests/runtime-runes/samples/derived-unowned-11/main.svelte
new file mode 100644
index 000000000000..0219acdf7f99
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/derived-unowned-11/main.svelte
@@ -0,0 +1,11 @@
+
+
+
+
+
+
diff --git a/packages/svelte/tests/runtime-runes/samples/derived-unowned-12/_config.js b/packages/svelte/tests/runtime-runes/samples/derived-unowned-12/_config.js
new file mode 100644
index 000000000000..8cd4af05488e
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/derived-unowned-12/_config.js
@@ -0,0 +1,25 @@
+import { flushSync } from 'svelte';
+import { test } from '../../test';
+
+export default test({
+ async test({ assert, target }) {
+ let [btn1, btn2] = target.querySelectorAll('button');
+
+ btn1?.click();
+ flushSync();
+
+ btn2?.click();
+ flushSync();
+
+ btn1?.click();
+ flushSync();
+
+ btn1?.click();
+ flushSync();
+
+ assert.htmlEqual(
+ target.innerHTML,
+ `linked.current \n3\ncount \n1`
+ );
+ }
+});
diff --git a/packages/svelte/tests/runtime-runes/samples/derived-unowned-12/main.svelte b/packages/svelte/tests/runtime-runes/samples/derived-unowned-12/main.svelte
new file mode 100644
index 000000000000..48d4f5fd0b75
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/derived-unowned-12/main.svelte
@@ -0,0 +1,18 @@
+
+
+ linked.current++}>linked.current {linked.current}
+ count++}>count {count}
diff --git a/packages/svelte/tests/runtime-runes/samples/derived-unowned-2/_config.js b/packages/svelte/tests/runtime-runes/samples/derived-unowned-2/_config.js
index 360400054346..3ca98bb0c66e 100644
--- a/packages/svelte/tests/runtime-runes/samples/derived-unowned-2/_config.js
+++ b/packages/svelte/tests/runtime-runes/samples/derived-unowned-2/_config.js
@@ -1,3 +1,4 @@
+import { flushSync } from 'svelte';
import { test } from '../../test';
export default test({
@@ -7,7 +8,7 @@ export default test({
async test({ assert, target }) {
await Promise.resolve();
- await Promise.resolve();
+ flushSync();
assert.htmlEqual(
target.innerHTML,
'd2: 3,4,5
d3: 3,4,5
d4: 3,4,5
'
diff --git a/packages/svelte/tests/runtime-runes/samples/derived-unowned-5/_config.js b/packages/svelte/tests/runtime-runes/samples/derived-unowned-5/_config.js
index 20c3cc112e31..5f38394d06d2 100644
--- a/packages/svelte/tests/runtime-runes/samples/derived-unowned-5/_config.js
+++ b/packages/svelte/tests/runtime-runes/samples/derived-unowned-5/_config.js
@@ -1,3 +1,4 @@
+import { flushSync } from 'svelte';
import { test } from '../../test';
export default test({
@@ -5,7 +6,7 @@ export default test({
// The test has a bunch of queueMicrotasks
await Promise.resolve();
await Promise.resolve();
- await Promise.resolve();
+ flushSync();
assert.htmlEqual(target.innerHTML, `Zeeba Neighba
`);
}
diff --git a/packages/svelte/tests/runtime-runes/samples/deriveds-in-constructor/_config.js b/packages/svelte/tests/runtime-runes/samples/deriveds-in-constructor/_config.js
new file mode 100644
index 000000000000..b364a989f480
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/deriveds-in-constructor/_config.js
@@ -0,0 +1,5 @@
+import { test } from '../../test';
+
+export default test({
+ html: `state,derived state,derived.by derived state
`
+});
diff --git a/packages/svelte/tests/runtime-runes/samples/deriveds-in-constructor/main.svelte b/packages/svelte/tests/runtime-runes/samples/deriveds-in-constructor/main.svelte
new file mode 100644
index 000000000000..bc8efba7e7c2
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/deriveds-in-constructor/main.svelte
@@ -0,0 +1,18 @@
+
+
+{foo.initial}
\ No newline at end of file
diff --git a/packages/svelte/tests/runtime-runes/samples/dynamic-style-attr/_config.js b/packages/svelte/tests/runtime-runes/samples/dynamic-style-attr/_config.js
index f6829721795c..20092ddadf34 100644
--- a/packages/svelte/tests/runtime-runes/samples/dynamic-style-attr/_config.js
+++ b/packages/svelte/tests/runtime-runes/samples/dynamic-style-attr/_config.js
@@ -2,7 +2,7 @@ import { test } from '../../test';
import { flushSync } from 'svelte';
export default test({
- html: `Hello world
Make blue Hello worldMake blue Hello worldMake blue Hello worldMake blue color;
-Hello world
+Hello world
color = 'blue'}>Make blue
diff --git a/packages/svelte/tests/runtime-runes/samples/each-bind-store-no-warning/_config.js b/packages/svelte/tests/runtime-runes/samples/each-bind-store-no-warning/_config.js
new file mode 100644
index 000000000000..dba2e856500c
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/each-bind-store-no-warning/_config.js
@@ -0,0 +1,10 @@
+import { test } from '../../test';
+
+export default test({
+ compileOptions: {
+ dev: true
+ },
+ async test({ assert, warnings }) {
+ assert.deepEqual(warnings, []);
+ }
+});
diff --git a/packages/svelte/tests/runtime-runes/samples/each-bind-store-no-warning/main.svelte b/packages/svelte/tests/runtime-runes/samples/each-bind-store-no-warning/main.svelte
new file mode 100644
index 000000000000..f927bf079a1d
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/each-bind-store-no-warning/main.svelte
@@ -0,0 +1,10 @@
+
+
+
+{#each $array as item}
+
+{/each}
diff --git a/packages/svelte/tests/runtime-runes/samples/each-updates-9/_config.js b/packages/svelte/tests/runtime-runes/samples/each-updates-9/_config.js
new file mode 100644
index 000000000000..ee35058c59bb
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/each-updates-9/_config.js
@@ -0,0 +1,16 @@
+import { flushSync } from 'svelte';
+import { test } from '../../test';
+
+export default test({
+ async test({ assert, target, logs }) {
+ const [btn1] = target.querySelectorAll('button');
+
+ btn1.click();
+ flushSync();
+
+ await Promise.resolve();
+ await Promise.resolve();
+
+ assert.deepEqual(logs, ['cleanup']);
+ }
+});
diff --git a/packages/svelte/tests/runtime-runes/samples/each-updates-9/main.svelte b/packages/svelte/tests/runtime-runes/samples/each-updates-9/main.svelte
new file mode 100644
index 000000000000..f5b2c8eb1282
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/each-updates-9/main.svelte
@@ -0,0 +1,46 @@
+
+
+ {
+ storeOptions.someBoolean = !storeOptions.someBoolean;
+ }}>+
+
+{#each myStore.data as _}{/each}
diff --git a/packages/svelte/tests/runtime-runes/samples/effect-cleanup/_config.js b/packages/svelte/tests/runtime-runes/samples/effect-cleanup/_config.js
index 6a3d9eef7702..e55733c14810 100644
--- a/packages/svelte/tests/runtime-runes/samples/effect-cleanup/_config.js
+++ b/packages/svelte/tests/runtime-runes/samples/effect-cleanup/_config.js
@@ -10,6 +10,6 @@ export default test({
flushSync(() => {
b1.click();
});
- assert.deepEqual(logs, ['init 0', 'cleanup 2', null, 'init 2', 'cleanup 4', null, 'init 4']);
+ assert.deepEqual(logs, ['init 0']);
}
});
diff --git a/packages/svelte/tests/runtime-runes/samples/effect-root-5/_config.js b/packages/svelte/tests/runtime-runes/samples/effect-root-5/_config.js
new file mode 100644
index 000000000000..260c757e3d8e
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/effect-root-5/_config.js
@@ -0,0 +1,17 @@
+import { flushSync } from 'svelte';
+import { test } from '../../test';
+
+export default test({
+ async test({ assert, target, logs }) {
+ const [b1, b2] = target.querySelectorAll('button');
+
+ flushSync(() => b1.click());
+ assert.deepEqual(logs, [0, 1]);
+
+ flushSync(() => b1.click());
+ assert.deepEqual(logs, [0, 1, 2]);
+
+ flushSync(() => b2.click());
+ assert.deepEqual(logs, [0, 1, 2]);
+ }
+});
diff --git a/packages/svelte/tests/runtime-runes/samples/effect-root-5/main.svelte b/packages/svelte/tests/runtime-runes/samples/effect-root-5/main.svelte
new file mode 100644
index 000000000000..06655a53623c
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/effect-root-5/main.svelte
@@ -0,0 +1,23 @@
+
+
+ ((obj ??= { count: 0 }).count += 1)}>+1
+ (obj = null)}>null
diff --git a/packages/svelte/tests/runtime-runes/samples/effect-tracking-unowned/_config.js b/packages/svelte/tests/runtime-runes/samples/effect-tracking-unowned/_config.js
new file mode 100644
index 000000000000..749b9997c2c5
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/effect-tracking-unowned/_config.js
@@ -0,0 +1,16 @@
+import { flushSync } from 'svelte';
+import { test } from '../../test';
+
+export default test({
+ async test({ assert, target, logs }) {
+ const b1 = target.querySelector('button');
+
+ b1?.click();
+ flushSync();
+
+ assert.htmlEqual(
+ target.innerHTML,
+ `Store: new Text: new message
Change Store `
+ );
+ }
+});
diff --git a/packages/svelte/tests/runtime-runes/samples/effect-tracking-unowned/main.svelte b/packages/svelte/tests/runtime-runes/samples/effect-tracking-unowned/main.svelte
new file mode 100644
index 000000000000..3c16e3c0366c
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/effect-tracking-unowned/main.svelte
@@ -0,0 +1,12 @@
+
+
+Store: {$store}
+Text: {text}
+ { store.set("new"); }}>Change Store
diff --git a/packages/svelte/tests/runtime-runes/samples/error-boundary-21/Child.svelte b/packages/svelte/tests/runtime-runes/samples/error-boundary-21/Child.svelte
new file mode 100644
index 000000000000..ea60542af9cc
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/error-boundary-21/Child.svelte
@@ -0,0 +1,3 @@
+
diff --git a/packages/svelte/tests/runtime-runes/samples/error-boundary-21/_config.js b/packages/svelte/tests/runtime-runes/samples/error-boundary-21/_config.js
new file mode 100644
index 000000000000..e301f83e6024
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/error-boundary-21/_config.js
@@ -0,0 +1,17 @@
+import { flushSync } from 'svelte';
+import { test } from '../../test';
+
+export default test({
+ html: '0
',
+ mode: ['client'],
+ test({ assert, target }) {
+ let btn = target.querySelector('button');
+ let div = target.querySelector('div');
+
+ flushSync(() => {
+ btn?.click();
+ });
+
+ assert.equal(div?.innerHTML, `1`);
+ }
+});
diff --git a/packages/svelte/tests/runtime-runes/samples/error-boundary-21/main.svelte b/packages/svelte/tests/runtime-runes/samples/error-boundary-21/main.svelte
new file mode 100644
index 000000000000..ed3140b1efb6
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/error-boundary-21/main.svelte
@@ -0,0 +1,14 @@
+
+
+count++}>
+
+
+
+ {#snippet failed()}
+ {count}
+ {/snippet}
+
diff --git a/packages/svelte/tests/runtime-runes/samples/event-handler-invalid-values/_config.js b/packages/svelte/tests/runtime-runes/samples/event-handler-invalid-values/_config.js
new file mode 100644
index 000000000000..d53812d4c39e
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/event-handler-invalid-values/_config.js
@@ -0,0 +1,48 @@
+import { assertType } from 'vitest';
+import { test } from '../../test';
+
+export default test({
+ mode: ['client'],
+
+ compileOptions: {
+ dev: true
+ },
+
+ test({ assert, target, warnings, logs }) {
+ /** @type {any} */
+ let error = null;
+
+ const handler = (/** @type {any} */ e) => {
+ error = e.error;
+ e.stopImmediatePropagation();
+ };
+
+ window.addEventListener('error', handler, true);
+
+ const [b1, b2, b3] = target.querySelectorAll('button');
+
+ b1.click();
+ assert.deepEqual(logs, []);
+ assert.equal(error, null);
+
+ error = null;
+ logs.length = 0;
+
+ b2.click();
+ assert.deepEqual(logs, ['clicked']);
+ assert.equal(error, null);
+
+ error = null;
+ logs.length = 0;
+
+ b3.click();
+ assert.deepEqual(logs, []);
+ assert.deepEqual(warnings, [
+ '`click` handler at main.svelte:10:17 should be a function. Did you mean to add a leading `() =>`?'
+ ]);
+ assert.isNotNull(error);
+ assert.match(error.message, /is not a function/);
+
+ window.removeEventListener('error', handler, true);
+ }
+});
diff --git a/packages/svelte/tests/runtime-runes/samples/event-handler-invalid-values/main.svelte b/packages/svelte/tests/runtime-runes/samples/event-handler-invalid-values/main.svelte
new file mode 100644
index 000000000000..f6e344ece8cf
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/event-handler-invalid-values/main.svelte
@@ -0,0 +1,10 @@
+
+
+click
+click
+click
diff --git a/packages/svelte/tests/runtime-runes/samples/export-binding/_config.js b/packages/svelte/tests/runtime-runes/samples/export-binding/_config.js
deleted file mode 100644
index d3b9c8e4eb69..000000000000
--- a/packages/svelte/tests/runtime-runes/samples/export-binding/_config.js
+++ /dev/null
@@ -1,10 +0,0 @@
-import { test } from '../../test';
-
-export default test({
- compileOptions: {
- dev: true // to ensure we we catch the error
- },
- error:
- 'bind_invalid_export\n' +
- 'Component counter/index.svelte has an export named `increment` that a consumer component is trying to access using `bind:increment`, which is disallowed. Instead, use `bind:this` (e.g. ` `) and then access the property on the bound component instance (e.g. `component.increment`)'
-});
diff --git a/packages/svelte/tests/runtime-runes/samples/export-binding/counter/index.svelte b/packages/svelte/tests/runtime-runes/samples/export-binding/counter/index.svelte
deleted file mode 100644
index 14e0de961b21..000000000000
--- a/packages/svelte/tests/runtime-runes/samples/export-binding/counter/index.svelte
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-{count}
diff --git a/packages/svelte/tests/runtime-runes/samples/export-binding/main.svelte b/packages/svelte/tests/runtime-runes/samples/export-binding/main.svelte
deleted file mode 100644
index 4ad1684701f9..000000000000
--- a/packages/svelte/tests/runtime-runes/samples/export-binding/main.svelte
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-increment
diff --git a/packages/svelte/tests/runtime-runes/samples/form-novalidate-casing/_config.js b/packages/svelte/tests/runtime-runes/samples/form-novalidate-casing/_config.js
new file mode 100644
index 000000000000..4fdf3632d650
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/form-novalidate-casing/_config.js
@@ -0,0 +1,8 @@
+import { test } from '../../test';
+
+export default test({
+ html: `
+
+
+`
+});
diff --git a/packages/svelte/tests/runtime-runes/samples/form-novalidate-casing/main.svelte b/packages/svelte/tests/runtime-runes/samples/form-novalidate-casing/main.svelte
new file mode 100644
index 000000000000..1e8115ff6263
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/form-novalidate-casing/main.svelte
@@ -0,0 +1,6 @@
+
+
+
+
diff --git a/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children-2/_config.js b/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children-2/_config.js
index 1066d9a2df19..cb8e6486457d 100644
--- a/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children-2/_config.js
+++ b/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children-2/_config.js
@@ -10,7 +10,6 @@ export default test({
assert.deepEqual(logs, [
'parent: $effect.pre 0',
'parent: $effect.pre (2) 0',
- 'parent: render 0',
'1: $effect.pre 0',
'1: $effect.pre (2) 0',
'1: render 0',
@@ -20,6 +19,7 @@ export default test({
'3: $effect.pre 0',
'3: $effect.pre (2) 0',
'3: render 0',
+ 'parent: render 0',
'1: $effect 0',
'2: $effect 0',
'3: $effect 0',
@@ -33,7 +33,6 @@ export default test({
assert.deepEqual(logs, [
'parent: $effect.pre 1',
'parent: $effect.pre (2) 1',
- 'parent: render 1',
'1: $effect.pre 1',
'1: $effect.pre (2) 1',
'1: render 1',
@@ -43,6 +42,7 @@ export default test({
'3: $effect.pre 1',
'3: $effect.pre (2) 1',
'3: render 1',
+ 'parent: render 1',
'1: $effect 1',
'2: $effect 1',
'3: $effect 1',
diff --git a/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children-3/_config.js b/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children-3/_config.js
index 55847c35a2a3..6c063bcb3e45 100644
--- a/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children-3/_config.js
+++ b/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children-3/_config.js
@@ -8,13 +8,13 @@ export default test({
async test({ assert, component, logs }) {
assert.deepEqual(logs, [
- 'parent: render 0',
'1: $effect.pre 0',
'1: render 0',
'2: $effect.pre 0',
'2: render 0',
'3: $effect.pre 0',
'3: render 0',
+ 'parent: render 0',
'1: $effect 0',
'2: $effect 0',
'3: $effect 0',
@@ -26,13 +26,13 @@ export default test({
flushSync(() => (component.n += 1));
assert.deepEqual(logs, [
- 'parent: render 1',
'1: $effect.pre 1',
'1: render 1',
'2: $effect.pre 1',
'2: render 1',
'3: $effect.pre 1',
'3: render 1',
+ 'parent: render 1',
'1: $effect 1',
'2: $effect 1',
'3: $effect 1',
diff --git a/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children-4/_config.js b/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children-4/_config.js
index 0cd7e15a3726..29b0b67a5235 100644
--- a/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children-4/_config.js
+++ b/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children-4/_config.js
@@ -10,7 +10,6 @@ export default test({
assert.deepEqual(logs, [
'parent: $effect.pre 0',
'parent: nested $effect.pre 0',
- 'parent: render 0',
'1: $effect.pre 0',
'1: nested $effect.pre 0',
'1: render 0',
@@ -20,6 +19,7 @@ export default test({
'3: $effect.pre 0',
'3: nested $effect.pre 0',
'3: render 0',
+ 'parent: render 0',
'1: $effect 0',
'2: $effect 0',
'3: $effect 0',
@@ -33,7 +33,6 @@ export default test({
assert.deepEqual(logs, [
'parent: $effect.pre 1',
'parent: nested $effect.pre 1',
- 'parent: render 1',
'1: $effect.pre 1',
'1: nested $effect.pre 1',
'1: render 1',
@@ -43,6 +42,7 @@ export default test({
'3: $effect.pre 1',
'3: nested $effect.pre 1',
'3: render 1',
+ 'parent: render 1',
'1: $effect 1',
'2: $effect 1',
'3: $effect 1',
diff --git a/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children/_config.js b/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children/_config.js
index 19b8fb39383c..3138ec7231d7 100644
--- a/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children/_config.js
+++ b/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children/_config.js
@@ -9,13 +9,13 @@ export default test({
async test({ assert, component, logs }) {
assert.deepEqual(logs, [
'parent: $effect.pre 0',
- 'parent: render 0',
'1: $effect.pre 0',
'1: render 0',
'2: $effect.pre 0',
'2: render 0',
'3: $effect.pre 0',
'3: render 0',
+ 'parent: render 0',
'1: $effect 0',
'2: $effect 0',
'3: $effect 0',
@@ -28,13 +28,13 @@ export default test({
assert.deepEqual(logs, [
'parent: $effect.pre 1',
- 'parent: render 1',
'1: $effect.pre 1',
'1: render 1',
'2: $effect.pre 1',
'2: render 1',
'3: $effect.pre 1',
'3: render 1',
+ 'parent: render 1',
'1: $effect 1',
'2: $effect 1',
'3: $effect 1',
diff --git a/packages/svelte/tests/runtime-runes/samples/muted-without-bind-works/_config.js b/packages/svelte/tests/runtime-runes/samples/muted-without-bind-works/_config.js
new file mode 100644
index 000000000000..cc4dfb37f098
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/muted-without-bind-works/_config.js
@@ -0,0 +1,13 @@
+import { flushSync } from 'svelte';
+import { ok, test } from '../../test';
+
+export default test({
+ async test({ assert, target, logs }) {
+ const btn = target.querySelector('button');
+ ok(btn);
+ flushSync(() => {
+ btn.click();
+ });
+ assert.deepEqual(logs, [true]);
+ }
+});
diff --git a/packages/svelte/tests/runtime-runes/samples/muted-without-bind-works/main.svelte b/packages/svelte/tests/runtime-runes/samples/muted-without-bind-works/main.svelte
new file mode 100644
index 000000000000..646334c1ece3
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/muted-without-bind-works/main.svelte
@@ -0,0 +1,13 @@
+
+
+
+ (muted = !muted)}>
+
diff --git a/packages/svelte/tests/runtime-runes/samples/nested-effect-conflict/_config.js b/packages/svelte/tests/runtime-runes/samples/nested-effect-conflict/_config.js
index a8c16b7008c9..eb631bc9f4bc 100644
--- a/packages/svelte/tests/runtime-runes/samples/nested-effect-conflict/_config.js
+++ b/packages/svelte/tests/runtime-runes/samples/nested-effect-conflict/_config.js
@@ -10,14 +10,6 @@ export default test({
});
await Promise.resolve();
- assert.deepEqual(logs, [
- 'top level',
- 'inner',
- 0,
- 'destroy inner',
- undefined,
- 'destroy outer',
- undefined
- ]);
+ assert.deepEqual(logs, ['top level', 'inner', 0, 'destroy inner', 0, 'destroy outer', 0]);
}
});
diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-8/CounterBinding.svelte b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-8/CounterBinding.svelte
new file mode 100644
index 000000000000..d6da559fb176
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-8/CounterBinding.svelte
@@ -0,0 +1,7 @@
+
+
+Binding
+ linked3.count++}>Increment Linked 1 ({linked3.count})
+ linked4.count++}>Increment Linked 2 ({linked4.count})
diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-8/CounterContext.svelte b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-8/CounterContext.svelte
new file mode 100644
index 000000000000..b935f0a472dc
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-8/CounterContext.svelte
@@ -0,0 +1,13 @@
+
+
+Context
+ linked1.linked.current.count++}
+ >Increment Linked 1 ({linked1.linked.current.count})
+ linked2.linked.current.count++}
+ >Increment Linked 2 ({linked2.linked.current.count})
diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-8/_config.js b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-8/_config.js
new file mode 100644
index 000000000000..d6d12d01cd09
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-8/_config.js
@@ -0,0 +1,34 @@
+import { flushSync } from 'svelte';
+import { test } from '../../test';
+
+// Tests that ownership is widened with $derived (on class or on its own) that contains $state
+export default test({
+ compileOptions: {
+ dev: true
+ },
+
+ test({ assert, target, warnings }) {
+ const [root, counter_context1, counter_context2, counter_binding1, counter_binding2] =
+ target.querySelectorAll('button');
+
+ counter_context1.click();
+ counter_context2.click();
+ counter_binding1.click();
+ counter_binding2.click();
+ flushSync();
+
+ assert.equal(warnings.length, 0);
+
+ root.click();
+ flushSync();
+ counter_context1.click();
+ counter_context2.click();
+ counter_binding1.click();
+ counter_binding2.click();
+ flushSync();
+
+ assert.equal(warnings.length, 0);
+ },
+
+ warnings: []
+});
diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-8/main.svelte b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-8/main.svelte
new file mode 100644
index 000000000000..aaade26e162c
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-8/main.svelte
@@ -0,0 +1,46 @@
+
+
+Parent
+ counter.count++}>
+ Increment Original ({counter.count})
+
+
+
+
diff --git a/packages/svelte/tests/runtime-runes/samples/onmount-prop-access/Component.svelte b/packages/svelte/tests/runtime-runes/samples/onmount-prop-access/Component.svelte
new file mode 100644
index 000000000000..b5da702fa7b2
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/onmount-prop-access/Component.svelte
@@ -0,0 +1,12 @@
+
diff --git a/packages/svelte/tests/runtime-runes/samples/onmount-prop-access/_config.js b/packages/svelte/tests/runtime-runes/samples/onmount-prop-access/_config.js
new file mode 100644
index 000000000000..0d24e265d3ab
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/onmount-prop-access/_config.js
@@ -0,0 +1,7 @@
+import { test } from '../../test';
+
+export default test({
+ async test({ assert, logs }) {
+ assert.deepEqual(logs, [1]);
+ }
+});
diff --git a/packages/svelte/tests/runtime-runes/samples/onmount-prop-access/main.svelte b/packages/svelte/tests/runtime-runes/samples/onmount-prop-access/main.svelte
new file mode 100644
index 000000000000..92746760a484
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/onmount-prop-access/main.svelte
@@ -0,0 +1,14 @@
+
+
+{#key key}
+
+{/key}
diff --git a/packages/svelte/tests/runtime-runes/samples/ownership-function-bindings/Child.svelte b/packages/svelte/tests/runtime-runes/samples/ownership-function-bindings/Child.svelte
new file mode 100644
index 000000000000..ef91b0756dfb
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/ownership-function-bindings/Child.svelte
@@ -0,0 +1,5 @@
+
+
+ arr.push(arr.length)}>
\ No newline at end of file
diff --git a/packages/svelte/tests/runtime-runes/samples/ownership-function-bindings/_config.js b/packages/svelte/tests/runtime-runes/samples/ownership-function-bindings/_config.js
new file mode 100644
index 000000000000..4c77aea20684
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/ownership-function-bindings/_config.js
@@ -0,0 +1,20 @@
+import { flushSync } from 'svelte';
+import { test } from '../../test';
+
+export default test({
+ compileOptions: {
+ dev: true
+ },
+ test({ target, warnings, assert }) {
+ const btn = target.querySelector('button');
+ flushSync(() => {
+ btn?.click();
+ });
+ assert.deepEqual(warnings, []);
+
+ flushSync(() => {
+ btn?.click();
+ });
+ assert.deepEqual(warnings, []);
+ }
+});
diff --git a/packages/svelte/tests/runtime-runes/samples/ownership-function-bindings/main.svelte b/packages/svelte/tests/runtime-runes/samples/ownership-function-bindings/main.svelte
new file mode 100644
index 000000000000..4a3ce82726b6
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/ownership-function-bindings/main.svelte
@@ -0,0 +1,10 @@
+
+
+ len % 2 === 0 ? arr : arr2, (v) => {}} />
\ No newline at end of file
diff --git a/packages/svelte/tests/runtime-runes/samples/pre-no-content/_config.js b/packages/svelte/tests/runtime-runes/samples/pre-no-content/_config.js
new file mode 100644
index 000000000000..cb9d31a69f4f
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/pre-no-content/_config.js
@@ -0,0 +1,5 @@
+import { test } from '../../test';
+
+export default test({
+ html: ` `
+});
diff --git a/packages/svelte/tests/runtime-runes/samples/pre-no-content/main.svelte b/packages/svelte/tests/runtime-runes/samples/pre-no-content/main.svelte
new file mode 100644
index 000000000000..a4357066a55f
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/pre-no-content/main.svelte
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/packages/svelte/tests/runtime-runes/samples/props-id-prefix/Child.svelte b/packages/svelte/tests/runtime-runes/samples/props-id-prefix/Child.svelte
new file mode 100644
index 000000000000..ad8bbd6f01ff
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/props-id-prefix/Child.svelte
@@ -0,0 +1,5 @@
+
+
+{id}
diff --git a/packages/svelte/tests/runtime-runes/samples/props-id-prefix/_config.js b/packages/svelte/tests/runtime-runes/samples/props-id-prefix/_config.js
new file mode 100644
index 000000000000..6d4306c413f1
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/props-id-prefix/_config.js
@@ -0,0 +1,60 @@
+import { flushSync } from 'svelte';
+import { test } from '../../test';
+
+export default test({
+ id_prefix: 'myPrefix',
+ test({ assert, target, variant }) {
+ if (variant === 'dom') {
+ assert.htmlEqual(
+ target.innerHTML,
+ `
+ toggle
+ c1
+ c2
+ c3
+ c4
+ `
+ );
+ } else {
+ assert.htmlEqual(
+ target.innerHTML,
+ `
+ toggle
+ myPrefix-s1
+ myPrefix-s2
+ myPrefix-s3
+ myPrefix-s4
+ `
+ );
+ }
+
+ let button = target.querySelector('button');
+ flushSync(() => button?.click());
+
+ if (variant === 'dom') {
+ assert.htmlEqual(
+ target.innerHTML,
+ `
+ toggle
+ c1
+ c2
+ c3
+ c4
+ c5
+ `
+ );
+ } else {
+ assert.htmlEqual(
+ target.innerHTML,
+ `
+ toggle
+ myPrefix-s1
+ myPrefix-s2
+ myPrefix-s3
+ myPrefix-s4
+ c1
+ `
+ );
+ }
+ }
+});
diff --git a/packages/svelte/tests/runtime-runes/samples/props-id-prefix/main.svelte b/packages/svelte/tests/runtime-runes/samples/props-id-prefix/main.svelte
new file mode 100644
index 000000000000..646bb2ebdefe
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/props-id-prefix/main.svelte
@@ -0,0 +1,19 @@
+
+
+ show = !show}>toggle
+
+{id}
+
+
+
+
+
+{#if show}
+
+{/if}
diff --git a/packages/svelte/tests/runtime-runes/samples/props-id/Child.svelte b/packages/svelte/tests/runtime-runes/samples/props-id/Child.svelte
new file mode 100644
index 000000000000..ad8bbd6f01ff
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/props-id/Child.svelte
@@ -0,0 +1,5 @@
+
+
+{id}
diff --git a/packages/svelte/tests/runtime-runes/samples/props-id/_config.js b/packages/svelte/tests/runtime-runes/samples/props-id/_config.js
new file mode 100644
index 000000000000..416ef6cfbea2
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/props-id/_config.js
@@ -0,0 +1,59 @@
+import { flushSync } from 'svelte';
+import { test } from '../../test';
+
+export default test({
+ test({ assert, target, variant }) {
+ if (variant === 'dom') {
+ assert.htmlEqual(
+ target.innerHTML,
+ `
+ toggle
+ c1
+ c2
+ c3
+ c4
+ `
+ );
+ } else {
+ assert.htmlEqual(
+ target.innerHTML,
+ `
+ toggle
+ s1
+ s2
+ s3
+ s4
+ `
+ );
+ }
+
+ let button = target.querySelector('button');
+ flushSync(() => button?.click());
+
+ if (variant === 'dom') {
+ assert.htmlEqual(
+ target.innerHTML,
+ `
+ toggle
+ c1
+ c2
+ c3
+ c4
+ c5
+ `
+ );
+ } else {
+ assert.htmlEqual(
+ target.innerHTML,
+ `
+ toggle
+ s1
+ s2
+ s3
+ s4
+ c1
+ `
+ );
+ }
+ }
+});
diff --git a/packages/svelte/tests/runtime-runes/samples/props-id/main.svelte b/packages/svelte/tests/runtime-runes/samples/props-id/main.svelte
new file mode 100644
index 000000000000..646bb2ebdefe
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/props-id/main.svelte
@@ -0,0 +1,19 @@
+
+
+ show = !show}>toggle
+
+{id}
+
+
+
+
+
+{#if show}
+
+{/if}
diff --git a/packages/svelte/tests/runtime-runes/samples/props-not-bindable-spread/Counter.svelte b/packages/svelte/tests/runtime-runes/samples/props-not-bindable-spread/Counter.svelte
deleted file mode 100644
index f22fd6e976dd..000000000000
--- a/packages/svelte/tests/runtime-runes/samples/props-not-bindable-spread/Counter.svelte
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-{rest.count}
diff --git a/packages/svelte/tests/runtime-runes/samples/props-not-bindable-spread/_config.js b/packages/svelte/tests/runtime-runes/samples/props-not-bindable-spread/_config.js
deleted file mode 100644
index fa0994c3704a..000000000000
--- a/packages/svelte/tests/runtime-runes/samples/props-not-bindable-spread/_config.js
+++ /dev/null
@@ -1,13 +0,0 @@
-import { test } from '../../test';
-
-export default test({
- compileOptions: {
- dev: true
- },
-
- html: '0',
-
- error:
- 'bind_not_bindable\n' +
- 'A component is attempting to bind to a non-bindable property `count` belonging to Counter.svelte (i.e. ``). To mark a property as bindable: `let { count = $bindable() } = $props()`'
-});
diff --git a/packages/svelte/tests/runtime-runes/samples/props-not-bindable-spread/main.svelte b/packages/svelte/tests/runtime-runes/samples/props-not-bindable-spread/main.svelte
deleted file mode 100644
index 80242b75c6dd..000000000000
--- a/packages/svelte/tests/runtime-runes/samples/props-not-bindable-spread/main.svelte
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
diff --git a/packages/svelte/tests/runtime-runes/samples/props-not-bindable/Counter.svelte b/packages/svelte/tests/runtime-runes/samples/props-not-bindable/Counter.svelte
deleted file mode 100644
index 4bc2db3968e7..000000000000
--- a/packages/svelte/tests/runtime-runes/samples/props-not-bindable/Counter.svelte
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-{count}
diff --git a/packages/svelte/tests/runtime-runes/samples/props-not-bindable/_config.js b/packages/svelte/tests/runtime-runes/samples/props-not-bindable/_config.js
deleted file mode 100644
index fa0994c3704a..000000000000
--- a/packages/svelte/tests/runtime-runes/samples/props-not-bindable/_config.js
+++ /dev/null
@@ -1,13 +0,0 @@
-import { test } from '../../test';
-
-export default test({
- compileOptions: {
- dev: true
- },
-
- html: '0',
-
- error:
- 'bind_not_bindable\n' +
- 'A component is attempting to bind to a non-bindable property `count` belonging to Counter.svelte (i.e. ``). To mark a property as bindable: `let { count = $bindable() } = $props()`'
-});
diff --git a/packages/svelte/tests/runtime-runes/samples/props-not-bindable/main.svelte b/packages/svelte/tests/runtime-runes/samples/props-not-bindable/main.svelte
deleted file mode 100644
index 80242b75c6dd..000000000000
--- a/packages/svelte/tests/runtime-runes/samples/props-not-bindable/main.svelte
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
diff --git a/packages/svelte/tests/runtime-runes/samples/proxy-coercive-assignment-warning/_config.js b/packages/svelte/tests/runtime-runes/samples/proxy-coercive-assignment-warning/_config.js
index ad7bdc654a0b..4462f492fac9 100644
--- a/packages/svelte/tests/runtime-runes/samples/proxy-coercive-assignment-warning/_config.js
+++ b/packages/svelte/tests/runtime-runes/samples/proxy-coercive-assignment-warning/_config.js
@@ -1,24 +1,36 @@
import { flushSync } from 'svelte';
-import { test } from '../../test';
+import { ok, test } from '../../test';
export default test({
compileOptions: {
dev: true
},
- html: `items: null x
`,
+ html: `items: null x
`,
test({ assert, target, warnings }) {
const btn = target.querySelector('button');
+ ok(btn);
- flushSync(() => btn?.click());
- assert.htmlEqual(target.innerHTML, `items: [] x
`);
+ flushSync(() => btn.click());
+ assert.htmlEqual(
+ target.innerHTML,
+ `items: [] x
`
+ );
- flushSync(() => btn?.click());
- assert.htmlEqual(target.innerHTML, `items: [0] x
`);
+ flushSync(() => btn.click());
+ assert.htmlEqual(
+ target.innerHTML,
+ `items: [0] x
`
+ );
+
+ const input = target.querySelector('input');
+ ok(input);
+ input.checked = true;
+ flushSync(() => input.dispatchEvent(new Event('change', { bubbles: true })));
assert.deepEqual(warnings, [
- 'Assignment to `items` property (main.svelte:8:24) will evaluate to the right-hand side, not the value of `items` following the assignment. This may result in unexpected behaviour.'
+ 'Assignment to `items` property (main.svelte:9:24) will evaluate to the right-hand side, not the value of `items` following the assignment. This may result in unexpected behaviour.'
]);
}
});
diff --git a/packages/svelte/tests/runtime-runes/samples/proxy-coercive-assignment-warning/main.svelte b/packages/svelte/tests/runtime-runes/samples/proxy-coercive-assignment-warning/main.svelte
index 40592e08b8ca..a79fe873b7e9 100644
--- a/packages/svelte/tests/runtime-runes/samples/proxy-coercive-assignment-warning/main.svelte
+++ b/packages/svelte/tests/runtime-runes/samples/proxy-coercive-assignment-warning/main.svelte
@@ -2,7 +2,8 @@
import Test from './Test.svelte';
let entries = $state([]);
- let object = $state({ items: null });
+ let object = $state({ items: null, group: [] });
+ let elementFunBind = $state();
(object.items ??= []).push(object.items.length)}>
@@ -11,6 +12,14 @@
x
+
+
+
entries[2], (v) => (entries[2] = v)}>
+
+{#snippet funBind(context)}
+ {}, (e) => (context.element = e)} />
+{/snippet}
+{@render funBind({ set element(e) { elementFunBind = e } })}
diff --git a/packages/svelte/tests/runtime-runes/samples/random/_config.js b/packages/svelte/tests/runtime-runes/samples/random/_config.js
new file mode 100644
index 000000000000..368dd20c6c8f
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/random/_config.js
@@ -0,0 +1,8 @@
+import { test } from '../../test';
+
+export default test({
+ test({ assert, target }) {
+ const [p1, p2] = target.querySelectorAll('p');
+ assert.notEqual(p1.textContent, p2.textContent);
+ }
+});
diff --git a/packages/svelte/tests/runtime-runes/samples/random/main.svelte b/packages/svelte/tests/runtime-runes/samples/random/main.svelte
new file mode 100644
index 000000000000..e1ec0b564903
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/random/main.svelte
@@ -0,0 +1,6 @@
+
+
+{(Math.random() * m).toFixed(10)}
+{(Math.random() * m).toFixed(10)}
diff --git a/packages/svelte/tests/runtime-runes/samples/read-version-previous-reaction/Component.svelte b/packages/svelte/tests/runtime-runes/samples/read-version-previous-reaction/Component.svelte
new file mode 100644
index 000000000000..afb62ced2f4b
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/read-version-previous-reaction/Component.svelte
@@ -0,0 +1,7 @@
+
+
+{label}
diff --git a/packages/svelte/tests/runtime-runes/samples/read-version-previous-reaction/_config.js b/packages/svelte/tests/runtime-runes/samples/read-version-previous-reaction/_config.js
new file mode 100644
index 000000000000..650e48e84ccd
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/read-version-previous-reaction/_config.js
@@ -0,0 +1,19 @@
+import { ok, test } from '../../test';
+import { flushSync } from 'svelte';
+
+export default test({
+ html: `0
`,
+
+ async test({ assert, target }) {
+ const p = target.querySelector('p');
+ const btn = target.querySelector('button');
+ flushSync(() => {
+ btn?.click();
+ });
+ assert.equal(p?.innerHTML, '1');
+ flushSync(() => {
+ btn?.click();
+ });
+ assert.equal(p?.innerHTML, '2');
+ }
+});
diff --git a/packages/svelte/tests/runtime-runes/samples/read-version-previous-reaction/main.svelte b/packages/svelte/tests/runtime-runes/samples/read-version-previous-reaction/main.svelte
new file mode 100644
index 000000000000..773aabeea30b
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/read-version-previous-reaction/main.svelte
@@ -0,0 +1,18 @@
+
+
+props.label++}>
+
+
\ No newline at end of file
diff --git a/packages/svelte/tests/runtime-runes/samples/runes-from-func/_config.js b/packages/svelte/tests/runtime-runes/samples/runes-from-func/_config.js
index 7d3dd9993fb8..5ed7579c629c 100644
--- a/packages/svelte/tests/runtime-runes/samples/runes-from-func/_config.js
+++ b/packages/svelte/tests/runtime-runes/samples/runes-from-func/_config.js
@@ -1,3 +1,4 @@
+import { flushSync } from 'svelte';
import { test } from '../../test';
export default test({
@@ -5,7 +6,7 @@ export default test({
async test({ assert, target }) {
await Promise.resolve();
- await Promise.resolve();
+ flushSync();
assert.htmlEqual(target.innerHTML, `1 `);
}
});
diff --git a/packages/svelte/tests/runtime-runes/samples/select-value-with-call/_config.js b/packages/svelte/tests/runtime-runes/samples/select-value-with-call/_config.js
new file mode 100644
index 000000000000..0597c2fda899
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/select-value-with-call/_config.js
@@ -0,0 +1,7 @@
+import { test } from '../../test';
+
+export default test({
+ async test({ assert, errors }) {
+ assert.deepEqual(errors, []);
+ }
+});
diff --git a/packages/svelte/tests/runtime-runes/samples/select-value-with-call/main.svelte b/packages/svelte/tests/runtime-runes/samples/select-value-with-call/main.svelte
new file mode 100644
index 000000000000..b1d60ecf6d9f
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/select-value-with-call/main.svelte
@@ -0,0 +1,6 @@
+
+
+
+
\ No newline at end of file
diff --git a/packages/svelte/tests/runtime-runes/samples/store-reassign-object/_config.js b/packages/svelte/tests/runtime-runes/samples/store-reassign-object/_config.js
new file mode 100644
index 000000000000..f9a329889d21
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/store-reassign-object/_config.js
@@ -0,0 +1,7 @@
+import { test } from '../../test';
+
+export default test({
+ async test({ target, assert }) {
+ assert.htmlEqual(target.innerHTML, `bar
`);
+ }
+});
diff --git a/packages/svelte/tests/runtime-runes/samples/store-reassign-object/main.svelte b/packages/svelte/tests/runtime-runes/samples/store-reassign-object/main.svelte
new file mode 100644
index 000000000000..ecffbb2d837c
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/store-reassign-object/main.svelte
@@ -0,0 +1,11 @@
+
+
+{clone.name}
diff --git a/packages/svelte/tests/runtime-runes/samples/store-update-on-destroy/Test.svelte b/packages/svelte/tests/runtime-runes/samples/store-update-on-destroy/Test.svelte
new file mode 100644
index 000000000000..364a4a7acac0
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/store-update-on-destroy/Test.svelte
@@ -0,0 +1,12 @@
+
diff --git a/packages/svelte/tests/runtime-runes/samples/store-update-on-destroy/_config.js b/packages/svelte/tests/runtime-runes/samples/store-update-on-destroy/_config.js
new file mode 100644
index 000000000000..bb999887564b
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/store-update-on-destroy/_config.js
@@ -0,0 +1,12 @@
+import { flushSync } from 'svelte';
+import { test } from '../../test';
+
+export default test({
+ async test({ assert, target, logs }) {
+ const input = target.querySelector('input');
+ flushSync(() => {
+ input?.click();
+ });
+ assert.deepEqual(logs, [0, 1]);
+ }
+});
diff --git a/packages/svelte/tests/runtime-runes/samples/store-update-on-destroy/main.svelte b/packages/svelte/tests/runtime-runes/samples/store-update-on-destroy/main.svelte
new file mode 100644
index 000000000000..7ba59b5afc09
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/store-update-on-destroy/main.svelte
@@ -0,0 +1,15 @@
+
+
+
+
+{#if checked}
+
+{/if}
diff --git a/packages/svelte/tests/runtime-runes/samples/style-directive-mutations/_config.js b/packages/svelte/tests/runtime-runes/samples/style-directive-mutations/_config.js
new file mode 100644
index 000000000000..bd76e4e6b929
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/style-directive-mutations/_config.js
@@ -0,0 +1,95 @@
+import { flushSync, tick } from 'svelte';
+import { test } from '../../test';
+
+// This test counts mutations on hydration
+// set_style() should not mutate style on hydration, except if mismatch
+export default test({
+ mode: ['server', 'hydrate'],
+
+ server_props: {
+ browser: false
+ },
+
+ props: {
+ browser: true
+ },
+
+ ssrHtml: `
+
+
+
+
+
+
+
+
+
+
+
+ `,
+
+ html: `
+
+
+
+
+
+
+
+
+
+
+
+ `,
+
+ async test({ target, assert, component, instance }) {
+ flushSync();
+ tick();
+ assert.deepEqual(instance.get_and_clear_mutations(), ['MAIN']);
+
+ let divs = target.querySelectorAll('div');
+
+ // Note : we cannot compare HTML because set_style() use dom.style.cssText
+ // which can alter the format of the attribute...
+
+ divs.forEach((d) => assert.equal(d.style.margin, ''));
+ divs.forEach((d) => assert.equal(d.style.color, 'red'));
+ divs.forEach((d) => assert.equal(d.style.fontSize, '18px'));
+
+ component.margin = '1px';
+ flushSync();
+ assert.deepEqual(
+ instance.get_and_clear_mutations(),
+ ['DIV', 'DIV', 'DIV', 'DIV', 'DIV', 'DIV', 'DIV', 'DIV', 'DIV'],
+ 'margin'
+ );
+ divs.forEach((d) => assert.equal(d.style.margin, '1px'));
+
+ component.color = 'yellow';
+ flushSync();
+ assert.deepEqual(
+ instance.get_and_clear_mutations(),
+ ['DIV', 'DIV', 'DIV', 'DIV', 'DIV', 'DIV', 'DIV', 'DIV', 'DIV'],
+ 'color'
+ );
+ divs.forEach((d) => assert.equal(d.style.color, 'yellow'));
+
+ component.fontSize = '10px';
+ flushSync();
+ assert.deepEqual(
+ instance.get_and_clear_mutations(),
+ ['DIV', 'DIV', 'DIV', 'DIV', 'DIV', 'DIV', 'DIV', 'DIV', 'DIV'],
+ 'fontSize'
+ );
+ divs.forEach((d) => assert.equal(d.style.fontSize, '10px'));
+
+ component.fontSize = null;
+ flushSync();
+ assert.deepEqual(
+ instance.get_and_clear_mutations(),
+ ['DIV', 'DIV', 'DIV', 'DIV', 'DIV', 'DIV', 'DIV', 'DIV', 'DIV'],
+ 'fontSize'
+ );
+ divs.forEach((d) => assert.equal(d.style.fontSize, ''));
+ }
+});
diff --git a/packages/svelte/tests/runtime-runes/samples/style-directive-mutations/main.svelte b/packages/svelte/tests/runtime-runes/samples/style-directive-mutations/main.svelte
new file mode 100644
index 000000000000..ae4da8ae37c7
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/style-directive-mutations/main.svelte
@@ -0,0 +1,54 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/svelte/tests/runtime-runes/samples/style-update/_config.js b/packages/svelte/tests/runtime-runes/samples/style-update/_config.js
new file mode 100644
index 000000000000..52690a431a4d
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/style-update/_config.js
@@ -0,0 +1,65 @@
+import { flushSync } from 'svelte';
+import { test } from '../../test';
+
+const style_1 = 'invalid-key:0; margin:4px;;color: green ;color:blue ';
+const style_2 = ' other-key : 0 ; padding:2px; COLOR:green; color: blue';
+const style_2_normalized = 'padding: 2px; color: blue;';
+
+// https://github.com/sveltejs/svelte/issues/15309
+export default test({
+ props: {
+ style: style_1
+ },
+
+ ssrHtml: `
+
+
+
+
+
+ `,
+
+ async test({ assert, target, component }) {
+ component.style = style_2;
+ flushSync();
+
+ assert.htmlEqual(
+ target.innerHTML,
+ `
+
+
+
+
+
+ `
+ );
+
+ component.style = '';
+ flushSync();
+
+ assert.htmlEqual(
+ target.innerHTML,
+ `
+
+
+
+
+
+ `
+ );
+
+ component.style = null;
+ flushSync();
+
+ assert.htmlEqual(
+ target.innerHTML,
+ `
+
+
+
+
+
+ `
+ );
+ }
+});
diff --git a/packages/svelte/tests/runtime-runes/samples/style-update/main.svelte b/packages/svelte/tests/runtime-runes/samples/style-update/main.svelte
new file mode 100644
index 000000000000..d29590d67035
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/style-update/main.svelte
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
diff --git a/packages/svelte/tests/runtime-runes/samples/svelte-boundary-dev-const/_config.js b/packages/svelte/tests/runtime-runes/samples/svelte-boundary-dev-const/_config.js
new file mode 100644
index 000000000000..3c0195ce3495
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/svelte-boundary-dev-const/_config.js
@@ -0,0 +1,33 @@
+import { flushSync } from 'svelte';
+import { test } from '../../test';
+
+// https://github.com/sveltejs/svelte/issues/15368
+export default test({
+ compileOptions: {
+ dev: true
+ },
+
+ mode: ['client'],
+
+ html: `
+ BOOM
+ BOOM
+ OK
+ OK
+ `,
+
+ async test({ target, assert, component }) {
+ component.ok = false;
+ flushSync();
+
+ assert.htmlEqual(
+ target.innerHTML,
+ `
+ BOOM
+ BOOM
+ BOOM
+ BOOM
+ `
+ );
+ }
+});
diff --git a/packages/svelte/tests/runtime-runes/samples/svelte-boundary-dev-const/main.svelte b/packages/svelte/tests/runtime-runes/samples/svelte-boundary-dev-const/main.svelte
new file mode 100644
index 000000000000..30e074c762f6
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/svelte-boundary-dev-const/main.svelte
@@ -0,0 +1,49 @@
+
+
+
+ {throwError()}
+
+ {#snippet failed()}
+ BOOM
+ {/snippet}
+
+
+
+ {@const result = throwError()}
+ {result}
+
+ {#snippet failed()}
+ BOOM
+ {/snippet}
+
+
+
+ {throwErrorOnUpdate()}
+
+ {#snippet failed()}
+ BOOM
+ {/snippet}
+
+
+
+ {@const result = throwErrorOnUpdate()}
+ {result}
+
+ {#snippet failed()}
+ BOOM
+ {/snippet}
+
diff --git a/packages/svelte/tests/runtime-runes/samples/svelte-element-css-hash/_config.js b/packages/svelte/tests/runtime-runes/samples/svelte-element-css-hash/_config.js
new file mode 100644
index 000000000000..c7a6213d82b8
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/svelte-element-css-hash/_config.js
@@ -0,0 +1,60 @@
+import { flushSync } from 'svelte';
+import { ok, test } from '../../test';
+
+export default test({
+ html: `
+
+
+
+
+
+
+ `,
+
+ async test({ assert, target, component }) {
+ component.active = true;
+ flushSync();
+
+ assert.htmlEqual(
+ target.innerHTML,
+ `
+
+
+
+
+
+
+ `
+ );
+
+ component.tag = 'span';
+ flushSync();
+
+ assert.htmlEqual(
+ target.innerHTML,
+ `
+
+
+
+
+
+
+ `
+ );
+
+ component.active = false;
+ flushSync();
+
+ assert.htmlEqual(
+ target.innerHTML,
+ `
+
+
+
+
+
+
+ `
+ );
+ }
+});
diff --git a/packages/svelte/tests/runtime-runes/samples/svelte-element-css-hash/main.svelte b/packages/svelte/tests/runtime-runes/samples/svelte-element-css-hash/main.svelte
new file mode 100644
index 000000000000..709f7b4fa1d5
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/svelte-element-css-hash/main.svelte
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/svelte/tests/runtime-runes/samples/toStore-subscribe2/_config.js b/packages/svelte/tests/runtime-runes/samples/toStore-subscribe2/_config.js
new file mode 100644
index 000000000000..bc1793e7a461
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/toStore-subscribe2/_config.js
@@ -0,0 +1,16 @@
+import { flushSync } from 'svelte';
+import { test } from '../../test';
+
+export default test({
+ async test({ assert, target }) {
+ let btn = target.querySelector('button');
+
+ btn?.click();
+ flushSync();
+
+ assert.htmlEqual(
+ target.innerHTML,
+ `Count 1!
Count from store 1!
Add 1 `
+ );
+ }
+});
diff --git a/packages/svelte/tests/runtime-runes/samples/toStore-subscribe2/main.svelte b/packages/svelte/tests/runtime-runes/samples/toStore-subscribe2/main.svelte
new file mode 100644
index 000000000000..82d20105b8eb
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/toStore-subscribe2/main.svelte
@@ -0,0 +1,11 @@
+
+
+Count {counter}!
+Count from store {$count}!
+
+ counter+=1}>Add 1
diff --git a/packages/svelte/tests/runtime-runes/samples/toStore-teardown/_config.js b/packages/svelte/tests/runtime-runes/samples/toStore-teardown/_config.js
new file mode 100644
index 000000000000..95904f011fc0
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/toStore-teardown/_config.js
@@ -0,0 +1,13 @@
+import { flushSync } from 'svelte';
+import { test } from '../../test';
+
+export default test({
+ async test({ assert, target }) {
+ let [, btn2] = target.querySelectorAll('button');
+
+ btn2.click();
+ flushSync();
+
+ assert.htmlEqual(target.innerHTML, `Set data Clear data `);
+ }
+});
diff --git a/packages/svelte/tests/runtime-runes/samples/toStore-teardown/child.svelte b/packages/svelte/tests/runtime-runes/samples/toStore-teardown/child.svelte
new file mode 100644
index 000000000000..f1b1b7b49769
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/toStore-teardown/child.svelte
@@ -0,0 +1,11 @@
+
+
+
+ Current value:
+ {$currentValue}
+
diff --git a/packages/svelte/tests/runtime-runes/samples/toStore-teardown/main.svelte b/packages/svelte/tests/runtime-runes/samples/toStore-teardown/main.svelte
new file mode 100644
index 000000000000..7d36dd95cbfd
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/toStore-teardown/main.svelte
@@ -0,0 +1,15 @@
+
+
+Set data
+Clear data
+
+{#if data}
+
+{/if}
diff --git a/packages/svelte/tests/runtime-runes/samples/transition-each-4/_config.js b/packages/svelte/tests/runtime-runes/samples/transition-each-4/_config.js
new file mode 100644
index 000000000000..a0e29ec8ff6d
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/transition-each-4/_config.js
@@ -0,0 +1,35 @@
+import { flushSync } from '../../../../src/index-client.js';
+import { test } from '../../test';
+
+export default test({
+ async test({ assert, raf, target, logs }) {
+ assert.htmlEqual(
+ target.innerHTML,
+ 'Toggle '
+ );
+
+ const btn1 = target.querySelector('button');
+ btn1?.click();
+ flushSync();
+ raf.tick(250);
+
+ assert.htmlEqual(
+ target.innerHTML,
+ 'Toggle '
+ );
+
+ logs.length = 0;
+
+ await Promise.resolve();
+
+ flushSync();
+ raf.tick(500);
+
+ assert.htmlEqual(
+ target.innerHTML,
+ 'Toggle '
+ );
+
+ assert.deepEqual(logs, ['$effect.pre', '$effect.pre', '$effect', '$effect']);
+ }
+});
diff --git a/packages/svelte/tests/runtime-runes/samples/transition-each-4/main.svelte b/packages/svelte/tests/runtime-runes/samples/transition-each-4/main.svelte
new file mode 100644
index 000000000000..d399bee69121
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/transition-each-4/main.svelte
@@ -0,0 +1,39 @@
+
+
+Toggle
+
+{#if toggle}
+
+ {#each items as item (item)}
+ {(() => {
+ $effect(() => {
+ items;
+ console.log('$effect');
+ });
+
+ $effect.pre(() => {
+ items;
+ console.log('$effect.pre');
+ });
+ })()}
+
{item}
+ {/each}
+
+{/if}
diff --git a/packages/svelte/tests/runtime-runes/samples/typescript/main.svelte b/packages/svelte/tests/runtime-runes/samples/typescript/main.svelte
index cd23b31096c3..d1b6452df465 100644
--- a/packages/svelte/tests/runtime-runes/samples/typescript/main.svelte
+++ b/packages/svelte/tests/runtime-runes/samples/typescript/main.svelte
@@ -8,7 +8,7 @@
console.log(this);
}
- function foo(): string {
+ function foo(): string {
return ""!;
}
@@ -22,6 +22,11 @@
class MyClass implements Hello {}
+ abstract class MyAbstractClass {
+ abstract x(): void;
+ y() {}
+ }
+
declare const declared_const: number;
declare function declared_fn(): void;
declare class declared_class {
@@ -40,6 +45,8 @@
export type { Hello };
const TypedFoo = Foo;
+ const typeAssertion = true;
+ const x = foo();
+
+
+
+{#snippet snip()}
+ snippet {x}
+{/snippet}
\ No newline at end of file
diff --git a/packages/svelte/tests/runtime-runes/samples/untrack-own-deriveds/_config.js b/packages/svelte/tests/runtime-runes/samples/untrack-own-deriveds/_config.js
new file mode 100644
index 000000000000..18062b86fb43
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/untrack-own-deriveds/_config.js
@@ -0,0 +1,20 @@
+import { flushSync } from 'svelte';
+import { test } from '../../test';
+
+export default test({
+ test({ assert, target, logs }) {
+ const button = target.querySelector('button');
+
+ flushSync(() => button?.click());
+
+ assert.htmlEqual(
+ target.innerHTML,
+ `
+ increment
+ 1/2
+ class Foo {
+ value = $state(0);
+ double = $derived(this.value * 2);
+
+ constructor() {
+ console.log(this.value, this.double);
+ }
+
+ increment() {
+ this.value++;
+ }
+ }
+
+ let foo = $state();
+
+ $effect(() => {
+ foo = new Foo();
+ });
+
+
+ foo.increment()}>increment
+
+{#if foo}
+ {foo.value}/{foo.double}
+{/if}
diff --git a/packages/svelte/tests/runtime-runes/samples/untracked-derived-local/_config.js b/packages/svelte/tests/runtime-runes/samples/untracked-derived-local/_config.js
new file mode 100644
index 000000000000..1fe92ed2d7c2
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/untracked-derived-local/_config.js
@@ -0,0 +1,5 @@
+import { test } from '../../test';
+
+export default test({
+ html: `3`
+});
diff --git a/packages/svelte/tests/runtime-runes/samples/untracked-derived-local/main.svelte b/packages/svelte/tests/runtime-runes/samples/untracked-derived-local/main.svelte
new file mode 100644
index 000000000000..0873eb741dd1
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/untracked-derived-local/main.svelte
@@ -0,0 +1,47 @@
+
+
+
+
+{test}
diff --git a/packages/svelte/tests/runtime-runes/samples/untracked-write-pre/_config.js b/packages/svelte/tests/runtime-runes/samples/untracked-write-pre/_config.js
new file mode 100644
index 000000000000..0310ec4fbb52
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/untracked-write-pre/_config.js
@@ -0,0 +1,7 @@
+import { test } from '../../test';
+
+export default test({
+ test({ assert, target, logs }) {
+ assert.deepEqual(logs, ['Outer', 'Inner', 'Outer', 'Inner']);
+ }
+});
diff --git a/packages/svelte/tests/runtime-runes/samples/untracked-write-pre/main.svelte b/packages/svelte/tests/runtime-runes/samples/untracked-write-pre/main.svelte
new file mode 100644
index 000000000000..5e95dbfd411a
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/untracked-write-pre/main.svelte
@@ -0,0 +1,13 @@
+
diff --git a/packages/svelte/tests/runtime-runes/samples/value-attribute-null-undefined/_config.js b/packages/svelte/tests/runtime-runes/samples/value-attribute-null-undefined/_config.js
new file mode 100644
index 000000000000..08f0c5aec7ae
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/value-attribute-null-undefined/_config.js
@@ -0,0 +1,49 @@
+import { test, ok } from '../../test';
+import { flushSync } from 'svelte';
+
+export default test({
+ mode: ['client'],
+
+ async test({ assert, target }) {
+ /**
+ * @type {HTMLInputElement | null}
+ */
+ const input = target.querySelector('input[type=text]');
+ /**
+ * @type {HTMLButtonElement | null}
+ */
+ const setString = target.querySelector('#setString');
+ /**
+ * @type {HTMLButtonElement | null}
+ */
+ const setNull = target.querySelector('#setNull');
+ /**
+ * @type {HTMLButtonElement | null}
+ */
+ const setUndefined = target.querySelector('#setUndefined');
+
+ ok(input);
+ ok(setString);
+ ok(setNull);
+ ok(setUndefined);
+
+ // value should always be blank string when value attribute is set to null or undefined
+
+ assert.equal(input.value, '');
+ setString.click();
+ flushSync();
+ assert.equal(input.value, 'foo');
+
+ setNull.click();
+ flushSync();
+ assert.equal(input.value, '');
+
+ setString.click();
+ flushSync();
+ assert.equal(input.value, 'foo');
+
+ setUndefined.click();
+ flushSync();
+ assert.equal(input.value, '');
+ }
+});
diff --git a/packages/svelte/tests/runtime-runes/samples/value-attribute-null-undefined/main.svelte b/packages/svelte/tests/runtime-runes/samples/value-attribute-null-undefined/main.svelte
new file mode 100644
index 000000000000..9f944923c2f7
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/value-attribute-null-undefined/main.svelte
@@ -0,0 +1,9 @@
+
+
+
+
+ {value = "foo";}}>
+ {value = null;}}>
+ {value = undefined;}}>
diff --git a/packages/svelte/tests/runtime-runes/samples/writable-derived/_config.js b/packages/svelte/tests/runtime-runes/samples/writable-derived/_config.js
new file mode 100644
index 000000000000..b48ccbdfd004
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/writable-derived/_config.js
@@ -0,0 +1,46 @@
+import { flushSync } from 'svelte';
+import { test } from '../../test';
+
+export default test({
+ html: `
+ 0 * 2 = 0
+ `,
+
+ ssrHtml: `
+ 0 * 2 = 0
+ `,
+
+ test({ assert, target, window }) {
+ const [input1, input2] = target.querySelectorAll('input');
+
+ flushSync(() => {
+ input1.value = '10';
+ input1.dispatchEvent(new window.Event('input', { bubbles: true }));
+ });
+
+ assert.htmlEqual(
+ target.innerHTML,
+ `10 * 2 = 20
`
+ );
+
+ flushSync(() => {
+ input2.value = '99';
+ input2.dispatchEvent(new window.Event('input', { bubbles: true }));
+ });
+
+ assert.htmlEqual(
+ target.innerHTML,
+ `10 * 2 = 99
`
+ );
+
+ flushSync(() => {
+ input1.value = '20';
+ input1.dispatchEvent(new window.Event('input', { bubbles: true }));
+ });
+
+ assert.htmlEqual(
+ target.innerHTML,
+ `20 * 2 = 40
`
+ );
+ }
+});
diff --git a/packages/svelte/tests/runtime-runes/samples/writable-derived/main.svelte b/packages/svelte/tests/runtime-runes/samples/writable-derived/main.svelte
new file mode 100644
index 000000000000..ab1dde0b9bba
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/writable-derived/main.svelte
@@ -0,0 +1,9 @@
+
+
+
+
+
+{count} * 2 = {double}
diff --git a/packages/svelte/tests/server-side-rendering/samples/head-component-props-id/HeadNested.svelte b/packages/svelte/tests/server-side-rendering/samples/head-component-props-id/HeadNested.svelte
new file mode 100644
index 000000000000..078420879887
--- /dev/null
+++ b/packages/svelte/tests/server-side-rendering/samples/head-component-props-id/HeadNested.svelte
@@ -0,0 +1,5 @@
+
+
+
\ No newline at end of file
diff --git a/packages/svelte/tests/snapshot/_config.js b/packages/svelte/tests/server-side-rendering/samples/head-component-props-id/_config.js
similarity index 100%
rename from packages/svelte/tests/snapshot/_config.js
rename to packages/svelte/tests/server-side-rendering/samples/head-component-props-id/_config.js
diff --git a/packages/svelte/tests/server-side-rendering/samples/head-component-props-id/main.svelte b/packages/svelte/tests/server-side-rendering/samples/head-component-props-id/main.svelte
new file mode 100644
index 000000000000..e32b40e9eda7
--- /dev/null
+++ b/packages/svelte/tests/server-side-rendering/samples/head-component-props-id/main.svelte
@@ -0,0 +1,8 @@
+
+
+
+
+
+
diff --git a/packages/svelte/tests/server-side-rendering/test.ts b/packages/svelte/tests/server-side-rendering/test.ts
index f76c5b539f24..3e5753942745 100644
--- a/packages/svelte/tests/server-side-rendering/test.ts
+++ b/packages/svelte/tests/server-side-rendering/test.ts
@@ -15,6 +15,7 @@ import type { CompileOptions } from '#compiler';
interface SSRTest extends BaseTest {
compileOptions?: Partial;
props?: Record;
+ id_prefix?: string;
withoutNormalizeHtml?: boolean;
errors?: string[];
}
@@ -33,7 +34,7 @@ const { test, run } = suite(async (config, test_dir) => {
const Component = (await import(`${test_dir}/_output/server/main.svelte.js`)).default;
const expected_html = try_read_file(`${test_dir}/_expected.html`);
- const rendered = render(Component, { props: config.props || {} });
+ const rendered = render(Component, { props: config.props || {}, idPrefix: config.id_prefix });
const { body, head } = rendered;
fs.writeFileSync(`${test_dir}/_output/rendered.html`, body);
diff --git a/packages/svelte/tests/signals/test.ts b/packages/svelte/tests/signals/test.ts
index e198a5a89de7..3977caae36ad 100644
--- a/packages/svelte/tests/signals/test.ts
+++ b/packages/svelte/tests/signals/test.ts
@@ -1,18 +1,20 @@
import { describe, assert, it } from 'vitest';
import { flushSync } from '../../src/index-client';
import * as $ from '../../src/internal/client/runtime';
+import { push, pop } from '../../src/internal/client/context';
import {
effect,
effect_root,
render_effect,
user_effect
} from '../../src/internal/client/reactivity/effects';
-import { state, set } from '../../src/internal/client/reactivity/sources';
-import type { Derived, Value } from '../../src/internal/client/types';
+import { state, set, update, update_pre } from '../../src/internal/client/reactivity/sources';
+import type { Derived, Effect, Value } from '../../src/internal/client/types';
import { proxy } from '../../src/internal/client/proxy';
import { derived } from '../../src/internal/client/reactivity/deriveds';
import { snapshot } from '../../src/internal/shared/clone.js';
import { SvelteSet } from '../../src/reactivity/set';
+import { DESTROYED } from '../../src/internal/client/constants';
/**
* @param runes runes mode
@@ -22,13 +24,13 @@ import { SvelteSet } from '../../src/reactivity/set';
function run_test(runes: boolean, fn: (runes: boolean) => () => void) {
return () => {
// Create a component context to test runes vs legacy mode
- $.push({}, runes);
+ push({}, runes);
// Create a render context so that effect validations etc don't fail
let execute: any;
const destroy = effect_root(() => {
execute = fn(runes);
});
- $.pop();
+ pop();
execute();
destroy();
};
@@ -67,7 +69,7 @@ describe('signals', () => {
};
});
- test('multiple effects with state and derived in it#1', () => {
+ test('multiple effects with state and derived in it #1', () => {
const log: string[] = [];
let count = state(0);
@@ -88,7 +90,7 @@ describe('signals', () => {
};
});
- test('multiple effects with state and derived in it#2', () => {
+ test('multiple effects with state and derived in it #2', () => {
const log: string[] = [];
let count = state(0);
@@ -222,20 +224,75 @@ describe('signals', () => {
};
});
- test('effects correctly handle unowned derived values that do not change', () => {
- const log: number[] = [];
+ test('https://perf.js.hyoo.ru/#!bench=9h2as6_u0mfnn #2', () => {
+ let res: number[] = [];
- let count = state(0);
- const read = () => {
- const x = derived(() => ({ count: $.get(count) }));
- return $.get(x);
+ const numbers = Array.from({ length: 2 }, (_, i) => i);
+ const fib = (n: number): number => (n < 2 ? 1 : fib(n - 1) + fib(n - 2));
+ const hard = (n: number, l: string) => n + fib(16);
+
+ const A = state(0);
+ const B = state(0);
+
+ return () => {
+ const C = derived(() => ($.get(A) % 2) + ($.get(B) % 2));
+ const D = derived(() => numbers.map((i) => i + ($.get(A) % 2) - ($.get(B) % 2)));
+ const E = derived(() => hard($.get(C) + $.get(A) + $.get(D)[0]!, 'E'));
+ const F = derived(() => hard($.get(D)[0]! && $.get(B), 'F'));
+ const G = derived(() => $.get(C) + ($.get(C) || $.get(E) % 2) + $.get(D)[0]! + $.get(F));
+
+ const destroy = effect_root(() => {
+ effect(() => {
+ res.push(hard($.get(G), 'H'));
+ });
+ effect(() => {
+ res.push($.get(G));
+ });
+ effect(() => {
+ res.push(hard($.get(F), 'J'));
+ });
+ });
+
+ flushSync();
+
+ let i = 2;
+ while (--i) {
+ res.length = 0;
+ set(B, 1);
+ set(A, 1 + i * 2);
+ flushSync();
+
+ set(A, 2 + i * 2);
+ set(B, 2);
+ flushSync();
+
+ assert.equal(res.length, 4);
+ assert.deepEqual(res, [3198, 1601, 3195, 1598]);
+ }
+
+ destroy();
+ assert(A.reactions === null);
+ assert(B.reactions === null);
};
- const derivedCount = derived(() => read().count);
- user_effect(() => {
- log.push($.get(derivedCount));
- });
+ });
+
+ test('effects correctly handle unowned derived values that do not change', () => {
+ const log: number[] = [];
return () => {
+ let count = state(0);
+ const read = () => {
+ const x = derived(() => ({ count: $.get(count) }));
+ return $.get(x);
+ };
+ const derivedCount = derived(() => read().count);
+
+ const destroy = effect_root(() => {
+ user_effect(() => {
+ log.push($.get(derivedCount));
+ });
+ });
+
flushSync(() => set(count, 1));
// Ensure we're not leaking consumers
assert.deepEqual(count.reactions?.length, 1);
@@ -246,6 +303,8 @@ describe('signals', () => {
// Ensure we're not leaking consumers
assert.deepEqual(count.reactions?.length, 1);
assert.deepEqual(log, [0, 1, 2, 3]);
+
+ destroy();
};
});
@@ -255,12 +314,16 @@ describe('signals', () => {
const a = state(0);
const b = state(0);
- const c = derived(() => {
- const a_2 = derived(() => $.get(a) + '!');
- const b_2 = derived(() => $.get(b) + '?');
- nested.push(a_2, b_2);
+ let c: any;
- return { a: $.get(a_2), b: $.get(b_2) };
+ const destroy = effect_root(() => {
+ c = derived(() => {
+ const a_2 = derived(() => $.get(a) + '!');
+ const b_2 = derived(() => $.get(b) + '?');
+ nested.push(a_2, b_2);
+
+ return { a: $.get(a_2), b: $.get(b_2) };
+ });
});
$.get(c);
@@ -273,11 +336,10 @@ describe('signals', () => {
$.get(c);
- // Ensure we're not leaking dependencies
- assert.deepEqual(
- nested.slice(0, -2).map((s) => s.deps),
- [null, null, null, null]
- );
+ destroy();
+
+ assert.equal(a.reactions, null);
+ assert.equal(b.reactions, null);
};
});
@@ -338,25 +400,69 @@ describe('signals', () => {
};
});
- let some_state = state({});
- let some_deps = derived(() => {
- return [$.get(some_state)];
- });
-
test('two effects with an unowned derived that has some dependencies', () => {
const log: Array> = [];
- render_effect(() => {
- log.push($.get(some_deps));
- });
+ return () => {
+ let some_state = state({});
+ let some_deps = derived(() => {
+ return [$.get(some_state)];
+ });
+ let destroy2: any;
- render_effect(() => {
- log.push($.get(some_deps));
- });
+ const destroy = effect_root(() => {
+ render_effect(() => {
+ $.untrack(() => {
+ log.push($.get(some_deps));
+ });
+ });
- return () => {
+ destroy2 = effect_root(() => {
+ render_effect(() => {
+ log.push($.get(some_deps));
+ });
+
+ render_effect(() => {
+ log.push($.get(some_deps));
+ });
+ });
+ });
+
+ set(some_state, {});
+ flushSync();
+
+ assert.deepEqual(log, [[{}], [{}], [{}], [{}], [{}]]);
+
+ destroy2();
+
+ set(some_state, {});
flushSync();
- assert.deepEqual(log, [[{}], [{}]]);
+
+ assert.deepEqual(log, [[{}], [{}], [{}], [{}], [{}]]);
+
+ log.length = 0;
+
+ const destroy3 = effect_root(() => {
+ render_effect(() => {
+ $.untrack(() => {
+ log.push($.get(some_deps));
+ });
+ log.push($.get(some_deps));
+ });
+ });
+
+ set(some_state, {});
+ flushSync();
+
+ assert.deepEqual(log, [[{}], [{}], [{}], [{}]]);
+
+ destroy3();
+
+ assert(some_state.reactions === null);
+
+ destroy();
+
+ assert(some_state.reactions === null);
};
});
@@ -381,6 +487,26 @@ describe('signals', () => {
};
});
+ test('schedules rerun when updating deeply nested value', (runes) => {
+ if (!runes) return () => {};
+
+ const value = proxy({ a: { b: { c: 0 } } });
+ user_effect(() => {
+ value.a.b.c += 1;
+ });
+
+ return () => {
+ let errored = false;
+ try {
+ flushSync();
+ } catch (e: any) {
+ assert.include(e.message, 'effect_update_depth_exceeded');
+ errored = true;
+ }
+ assert.equal(errored, true);
+ };
+ });
+
test('schedules rerun when writing to signal before reading it', (runes) => {
if (!runes) return () => {};
@@ -402,6 +528,42 @@ describe('signals', () => {
};
});
+ test('schedules rerun when writing to signal before reading it from derived', (runes) => {
+ if (!runes) return () => {};
+ let log: any[] = [];
+
+ const value = state(1);
+ const double = derived(() => $.get(value) * 2);
+
+ user_effect(() => {
+ set(value, 10);
+ log.push($.get(double));
+ });
+
+ return () => {
+ flushSync();
+ assert.deepEqual(log, [20]);
+ };
+ });
+
+ test('schedules rerun when writing to signal after reading it from derived', (runes) => {
+ if (!runes) return () => {};
+ let log: any[] = [];
+
+ const value = state(1);
+ const double = derived(() => $.get(value) * 2);
+
+ user_effect(() => {
+ log.push($.get(double));
+ set(value, 10);
+ });
+
+ return () => {
+ flushSync();
+ assert.deepEqual(log, [2, 20]);
+ };
+ });
+
test('effect teardown is removed on re-run', () => {
const count = state(0);
let first = true;
@@ -440,6 +602,7 @@ describe('signals', () => {
effect(() => {
log.push('inner', $.get(inner));
});
+ return $.get(outer);
});
});
});
@@ -493,6 +656,103 @@ describe('signals', () => {
};
});
+ test('mixed nested deriveds correctly cleanup when no longer connected to graph #1', () => {
+ let a: Derived;
+ let b: Derived;
+ let s = state(0);
+
+ const destroy = effect_root(() => {
+ render_effect(() => {
+ a = derived(() => {
+ b = derived(() => {
+ $.get(s);
+ });
+ $.untrack(() => {
+ $.get(b);
+ });
+ $.get(s);
+ });
+ $.get(a);
+ });
+ });
+
+ return () => {
+ flushSync();
+ assert.equal(a?.deps?.length, 1);
+ assert.equal(s?.reactions?.length, 1);
+ destroy();
+ assert.equal(s?.reactions, null);
+ };
+ });
+
+ test('mixed nested deriveds correctly cleanup when no longer connected to graph #2', () => {
+ let a: Derived;
+ let b: Derived;
+ let s = state(0);
+
+ const destroy = effect_root(() => {
+ render_effect(() => {
+ a = derived(() => {
+ b = derived(() => {
+ $.get(s);
+ });
+ effect_root(() => {
+ $.get(b);
+ });
+ $.get(s);
+ });
+ $.get(a);
+ });
+ });
+
+ return () => {
+ flushSync();
+ assert.equal(a?.deps?.length, 1);
+ assert.equal(s?.reactions?.length, 1);
+ destroy();
+ assert.equal(s?.reactions, null);
+ };
+ });
+
+ test('mixed nested deriveds correctly cleanup when no longer connected to graph #3', () => {
+ let a: Derived;
+ let b: Derived;
+ let s = state(0);
+ let logs: any[] = [];
+
+ const destroy = effect_root(() => {
+ render_effect(() => {
+ a = derived(() => {
+ b = derived(() => {
+ return $.get(s);
+ });
+ effect_root(() => {
+ $.get(b);
+ });
+ render_effect(() => {
+ logs.push($.get(b));
+ });
+ $.get(s);
+ });
+ $.get(a);
+ });
+ });
+
+ return () => {
+ flushSync();
+ assert.equal(a?.deps?.length, 1);
+ assert.equal(s?.reactions?.length, 2);
+
+ set(s, 1);
+ flushSync();
+
+ assert.deepEqual(logs, [0, 1]);
+
+ destroy();
+ assert.equal(s?.reactions, null);
+ };
+ });
+
test('deriveds update upon reconnection #1', () => {
let a = state(false);
let b = state(false);
@@ -679,6 +939,28 @@ describe('signals', () => {
};
});
+ test('unowned deriveds dependencies are correctly de-duped', () => {
+ return () => {
+ let a = state(0);
+ let b = state(true);
+ let c = derived(() => $.get(a));
+ let d = derived(() => ($.get(b) ? 1 : $.get(a) + $.get(c) + $.get(a)));
+
+ $.get(d);
+
+ assert.equal(d.deps?.length, 1);
+
+ $.get(d);
+
+ set(a, 1);
+ set(b, false);
+
+ $.get(d);
+
+ assert.equal(d.deps?.length, 3);
+ };
+ });
+
test('unowned deriveds correctly update', () => {
return () => {
const arr1 = proxy<{ a: number }[]>([]);
@@ -696,14 +978,30 @@ describe('signals', () => {
};
});
- test('deriveds cannot depend on state they own', () => {
+ test('deriveds do not depend on state they own', () => {
return () => {
+ let s;
+
const d = derived(() => {
- const s = state(0);
+ s = state(0);
return $.get(s);
});
- assert.throws(() => $.get(d), 'state_unsafe_local_read');
+ assert.equal($.get(d), 0);
+
+ set(s!, 1);
+ assert.equal($.get(d), 0);
+ };
+ });
+
+ test('effects do not depend on state they own', () => {
+ user_effect(() => {
+ const value = state(0);
+ set(value, $.get(value) + 1);
+ });
+
+ return () => {
+ flushSync();
};
});
@@ -741,29 +1039,87 @@ describe('signals', () => {
};
});
- test('nested deriveds clean up the relationships when used with untrack', () => {
+ test('deriveds containing effects work correctly', () => {
return () => {
let a = render_effect(() => {});
+ let b = state(0);
+ let c;
+ let effects: Effect[] = [];
const destroy = effect_root(() => {
a = render_effect(() => {
- $.untrack(() => {
- const b = derived(() => {
- const c = derived(() => {});
- $.untrack(() => {
- $.get(c);
- });
+ c = derived(() => {
+ effects.push(
+ effect(() => {
+ $.get(b);
+ })
+ );
+ $.get(b);
+ });
+ $.get(c);
+ });
+ });
+
+ assert.equal(c!.effects?.length, 1);
+ assert.equal(a.first, a.last);
+
+ set(b, 1);
+
+ flushSync();
+
+ assert.equal(c!.effects?.length, 1);
+ assert.equal(a.first, a.last);
+
+ destroy();
+
+ assert.equal(a.first, null);
+
+ assert.equal(effects.length, 2);
+ assert.equal((effects[0].f & DESTROYED) !== 0, true);
+ assert.equal((effects[1].f & DESTROYED) !== 0, true);
+ };
+ });
+
+ test('deriveds containing effects work correctly when used with untrack', () => {
+ return () => {
+ let a = render_effect(() => {});
+ let b = state(0);
+ let c;
+ let effects: Effect[] = [];
+
+ const destroy = effect_root(() => {
+ a = render_effect(() => {
+ c = derived(() => {
+ $.untrack(() => {
+ effects.push(
+ effect(() => {
+ $.get(b);
+ })
+ );
});
$.get(b);
});
+ $.get(c);
});
});
- assert.deepEqual(a.deriveds?.length, 1);
+ assert.equal(c!.effects?.length, 1);
+ assert.equal(a.first, a.last);
+
+ set(b, 1);
+
+ flushSync();
+
+ assert.equal(c!.effects?.length, 1);
+ assert.equal(a.first, a.last);
destroy();
- assert.deepEqual(a.deriveds, null);
+ assert.equal(a.first, null);
+
+ assert.equal(effects.length, 2);
+ assert.equal((effects[0].f & DESTROYED) !== 0, true);
+ assert.equal((effects[1].f & DESTROYED) !== 0, true);
};
});
@@ -771,14 +1127,14 @@ describe('signals', () => {
return () => {
const count = state(0n);
- assert.doesNotThrow(() => $.update(count));
+ assert.doesNotThrow(() => update(count));
assert.equal($.get(count), 1n);
- assert.doesNotThrow(() => $.update(count, -1));
+ assert.doesNotThrow(() => update(count, -1));
assert.equal($.get(count), 0n);
- assert.doesNotThrow(() => $.update_pre(count));
+ assert.doesNotThrow(() => update_pre(count));
assert.equal($.get(count), 1n);
- assert.doesNotThrow(() => $.update_pre(count, -1));
+ assert.doesNotThrow(() => update_pre(count, -1));
assert.equal($.get(count), 0n);
};
});
@@ -815,4 +1171,45 @@ describe('signals', () => {
destroy();
};
});
+
+ test('unowned deriveds correctly update', () => {
+ const log: any[] = [];
+
+ return () => {
+ const a = state(0);
+ const b = state(0);
+ const c = derived(() => {
+ return $.get(a);
+ });
+ const d = derived(() => {
+ return $.get(b);
+ });
+
+ const destroy = effect_root(() => {
+ const e = derived(() => {
+ return $.get(c) === 1 && $.get(d) === 1;
+ });
+ render_effect(() => {
+ log.push($.get(e));
+ });
+ });
+
+ assert.deepEqual(log, [false]);
+
+ set(a, 1);
+ set(b, 1);
+
+ flushSync();
+
+ assert.deepEqual(log, [false, true]);
+
+ set(b, 9);
+
+ flushSync();
+
+ assert.deepEqual(log, [false, true, false]);
+
+ destroy();
+ };
+ });
});
diff --git a/packages/svelte/tests/snapshot/samples/await-block-scope/_expected/client/index.svelte.js b/packages/svelte/tests/snapshot/samples/await-block-scope/_expected/client/index.svelte.js
index 87fce120fdc5..3e5a12ed9dec 100644
--- a/packages/svelte/tests/snapshot/samples/await-block-scope/_expected/client/index.svelte.js
+++ b/packages/svelte/tests/snapshot/samples/await-block-scope/_expected/client/index.svelte.js
@@ -1,5 +1,5 @@
-import "svelte/internal/disclose-version";
-import * as $ from "svelte/internal/client";
+import 'svelte/internal/disclose-version';
+import * as $ from 'svelte/internal/client';
function increment(_, counter) {
counter.count += 1;
@@ -26,11 +26,11 @@ export default function Await_block_scope($$anchor) {
var text_1 = $.sibling(node);
$.template_effect(() => {
- $.set_text(text, `clicks: ${counter.count ?? ""}`);
- $.set_text(text_1, ` ${counter.count ?? ""}`);
+ $.set_text(text, `clicks: ${counter.count ?? ''}`);
+ $.set_text(text_1, ` ${counter.count ?? ''}`);
});
$.append($$anchor, fragment);
}
-$.delegate(["click"]);
\ No newline at end of file
+$.delegate(['click']);
\ No newline at end of file
diff --git a/packages/svelte/tests/snapshot/samples/await-block-scope/_expected/server/index.svelte.js b/packages/svelte/tests/snapshot/samples/await-block-scope/_expected/server/index.svelte.js
index 44c68a7c96c0..012789a5509b 100644
--- a/packages/svelte/tests/snapshot/samples/await-block-scope/_expected/server/index.svelte.js
+++ b/packages/svelte/tests/snapshot/samples/await-block-scope/_expected/server/index.svelte.js
@@ -1,4 +1,4 @@
-import * as $ from "svelte/internal/server";
+import * as $ from 'svelte/internal/server';
export default function Await_block_scope($$payload) {
let counter = { count: 0 };
diff --git a/packages/svelte/tests/snapshot/samples/bind-component-snippet/_expected/client/index.svelte.js b/packages/svelte/tests/snapshot/samples/bind-component-snippet/_expected/client/index.svelte.js
index bcddb6f658c8..390e86a3510a 100644
--- a/packages/svelte/tests/snapshot/samples/bind-component-snippet/_expected/client/index.svelte.js
+++ b/packages/svelte/tests/snapshot/samples/bind-component-snippet/_expected/client/index.svelte.js
@@ -1,11 +1,11 @@
-import "svelte/internal/disclose-version";
-import * as $ from "svelte/internal/client";
+import 'svelte/internal/disclose-version';
+import * as $ from 'svelte/internal/client';
import TextInput from './Child.svelte';
const snippet = ($$anchor) => {
$.next();
- var text = $.text("Something");
+ var text = $.text('Something');
$.append($$anchor, text);
};
@@ -23,12 +23,12 @@ export default function Bind_component_snippet($$anchor) {
return $.get(value);
},
set value($$value) {
- $.set(value, $.proxy($$value));
+ $.set(value, $$value, true);
}
});
var text_1 = $.sibling(node);
- $.template_effect(() => $.set_text(text_1, ` value: ${$.get(value) ?? ""}`));
+ $.template_effect(() => $.set_text(text_1, ` value: ${$.get(value) ?? ''}`));
$.append($$anchor, fragment);
}
\ No newline at end of file
diff --git a/packages/svelte/tests/snapshot/samples/bind-component-snippet/_expected/server/index.svelte.js b/packages/svelte/tests/snapshot/samples/bind-component-snippet/_expected/server/index.svelte.js
index d223a3150269..cadae2cf15c0 100644
--- a/packages/svelte/tests/snapshot/samples/bind-component-snippet/_expected/server/index.svelte.js
+++ b/packages/svelte/tests/snapshot/samples/bind-component-snippet/_expected/server/index.svelte.js
@@ -1,4 +1,4 @@
-import * as $ from "svelte/internal/server";
+import * as $ from 'svelte/internal/server';
import TextInput from './Child.svelte';
function snippet($$payload) {
@@ -23,7 +23,7 @@ export default function Bind_component_snippet($$payload) {
});
$$payload.out += ` value: ${$.escape(value)}`;
- };
+ }
do {
$$settled = true;
diff --git a/packages/svelte/tests/snapshot/samples/bind-this/_expected/client/index.svelte.js b/packages/svelte/tests/snapshot/samples/bind-this/_expected/client/index.svelte.js
index fedcc87696cf..dfd32a04e51d 100644
--- a/packages/svelte/tests/snapshot/samples/bind-this/_expected/client/index.svelte.js
+++ b/packages/svelte/tests/snapshot/samples/bind-this/_expected/client/index.svelte.js
@@ -1,6 +1,6 @@
-import "svelte/internal/disclose-version";
-import "svelte/internal/flags/legacy";
-import * as $ from "svelte/internal/client";
+import 'svelte/internal/disclose-version';
+import 'svelte/internal/flags/legacy';
+import * as $ from 'svelte/internal/client';
export default function Bind_this($$anchor) {
$.bind_this(Foo($$anchor, { $$legacy: true }), ($$value) => foo = $$value, () => foo);
diff --git a/packages/svelte/tests/snapshot/samples/bind-this/_expected/server/index.svelte.js b/packages/svelte/tests/snapshot/samples/bind-this/_expected/server/index.svelte.js
index badca8d4a01c..148573766ff2 100644
--- a/packages/svelte/tests/snapshot/samples/bind-this/_expected/server/index.svelte.js
+++ b/packages/svelte/tests/snapshot/samples/bind-this/_expected/server/index.svelte.js
@@ -1,4 +1,4 @@
-import * as $ from "svelte/internal/server";
+import * as $ from 'svelte/internal/server';
export default function Bind_this($$payload) {
Foo($$payload, {});
diff --git a/packages/svelte/tests/snapshot/samples/class-state-field-constructor-assignment/_expected/client/index.svelte.js b/packages/svelte/tests/snapshot/samples/class-state-field-constructor-assignment/_expected/client/index.svelte.js
index 399fa19b621d..21339741761f 100644
--- a/packages/svelte/tests/snapshot/samples/class-state-field-constructor-assignment/_expected/client/index.svelte.js
+++ b/packages/svelte/tests/snapshot/samples/class-state-field-constructor-assignment/_expected/client/index.svelte.js
@@ -1,5 +1,5 @@
-import "svelte/internal/disclose-version";
-import * as $ from "svelte/internal/client";
+import 'svelte/internal/disclose-version';
+import * as $ from 'svelte/internal/client';
export default function Class_state_field_constructor_assignment($$anchor, $$props) {
$.push($$props, true);
@@ -12,14 +12,14 @@ export default function Class_state_field_constructor_assignment($$anchor, $$pro
}
set a(value) {
- $.set(this.#a, $.proxy(value));
+ $.set(this.#a, value, true);
}
#b = $.state();
constructor() {
this.a = 1;
- this.#b.v = 2;
+ $.set(this.#b, 2);
}
}
diff --git a/packages/svelte/tests/snapshot/samples/class-state-field-constructor-assignment/_expected/server/index.svelte.js b/packages/svelte/tests/snapshot/samples/class-state-field-constructor-assignment/_expected/server/index.svelte.js
index bcc7c2e1f20b..2a115a498373 100644
--- a/packages/svelte/tests/snapshot/samples/class-state-field-constructor-assignment/_expected/server/index.svelte.js
+++ b/packages/svelte/tests/snapshot/samples/class-state-field-constructor-assignment/_expected/server/index.svelte.js
@@ -1,4 +1,4 @@
-import * as $ from "svelte/internal/server";
+import * as $ from 'svelte/internal/server';
export default function Class_state_field_constructor_assignment($$payload, $$props) {
$.push();
diff --git a/packages/svelte/tests/snapshot/samples/destructured-assignments/_expected/client/index.svelte.js b/packages/svelte/tests/snapshot/samples/destructured-assignments/_expected/client/index.svelte.js
index dbda825c4d0f..47f297bce9c7 100644
--- a/packages/svelte/tests/snapshot/samples/destructured-assignments/_expected/client/index.svelte.js
+++ b/packages/svelte/tests/snapshot/samples/destructured-assignments/_expected/client/index.svelte.js
@@ -1,5 +1,5 @@
/* index.svelte.js generated by Svelte VERSION */
-import * as $ from "svelte/internal/client";
+import * as $ from 'svelte/internal/client';
let a = $.state(1);
let b = $.state(2);
@@ -8,8 +8,8 @@ let d = 4;
export function update(array) {
(
- $.set(a, $.proxy(array[0])),
- $.set(b, $.proxy(array[1]))
+ $.set(a, array[0], true),
+ $.set(b, array[1], true)
);
[c, d] = array;
diff --git a/packages/svelte/tests/snapshot/samples/destructured-assignments/_expected/server/index.svelte.js b/packages/svelte/tests/snapshot/samples/destructured-assignments/_expected/server/index.svelte.js
index 2797d4312b16..62b655b266f9 100644
--- a/packages/svelte/tests/snapshot/samples/destructured-assignments/_expected/server/index.svelte.js
+++ b/packages/svelte/tests/snapshot/samples/destructured-assignments/_expected/server/index.svelte.js
@@ -1,5 +1,5 @@
/* index.svelte.js generated by Svelte VERSION */
-import * as $ from "svelte/internal/server";
+import * as $ from 'svelte/internal/server';
let a = 1;
let b = 2;
diff --git a/packages/svelte/tests/snapshot/samples/dynamic-attributes-casing/_expected/client/main.svelte.js b/packages/svelte/tests/snapshot/samples/dynamic-attributes-casing/_expected/client/main.svelte.js
index 846acc67a6c3..219db6ffd529 100644
--- a/packages/svelte/tests/snapshot/samples/dynamic-attributes-casing/_expected/client/main.svelte.js
+++ b/packages/svelte/tests/snapshot/samples/dynamic-attributes-casing/_expected/client/main.svelte.js
@@ -1,5 +1,5 @@
-import "svelte/internal/disclose-version";
-import * as $ from "svelte/internal/client";
+import 'svelte/internal/disclose-version';
+import * as $ from 'svelte/internal/client';
var root = $.template(`
`, 3);
@@ -11,23 +11,24 @@ export default function Main($$anchor) {
var div = $.first_child(fragment);
var svg = $.sibling(div, 2);
var custom_element = $.sibling(svg, 2);
- var div_1 = $.sibling(custom_element, 2);
- $.template_effect(() => $.set_attribute(div_1, "foobar", y()));
+ $.template_effect(() => $.set_custom_element_data(custom_element, 'fooBar', x));
+ var div_1 = $.sibling(custom_element, 2);
var svg_1 = $.sibling(div_1, 2);
-
- $.template_effect(() => $.set_attribute(svg_1, "viewBox", y()));
-
var custom_element_1 = $.sibling(svg_1, 2);
- $.template_effect(() => $.set_custom_element_data(custom_element_1, "fooBar", y()));
-
- $.template_effect(() => {
- $.set_attribute(div, "foobar", x);
- $.set_attribute(svg, "viewBox", x);
- $.set_custom_element_data(custom_element, "fooBar", x);
- });
+ $.template_effect(() => $.set_custom_element_data(custom_element_1, 'fooBar', y()));
+
+ $.template_effect(
+ ($0, $1) => {
+ $.set_attribute(div, 'foobar', x);
+ $.set_attribute(svg, 'viewBox', x);
+ $.set_attribute(div_1, 'foobar', $0);
+ $.set_attribute(svg_1, 'viewBox', $1);
+ },
+ [y, y]
+ );
$.append($$anchor, fragment);
}
\ No newline at end of file
diff --git a/packages/svelte/tests/snapshot/samples/dynamic-attributes-casing/_expected/server/main.svelte.js b/packages/svelte/tests/snapshot/samples/dynamic-attributes-casing/_expected/server/main.svelte.js
index 5eaa55aa4970..4ea5edb6a0ac 100644
--- a/packages/svelte/tests/snapshot/samples/dynamic-attributes-casing/_expected/server/main.svelte.js
+++ b/packages/svelte/tests/snapshot/samples/dynamic-attributes-casing/_expected/server/main.svelte.js
@@ -1,9 +1,9 @@
-import * as $ from "svelte/internal/server";
+import * as $ from 'svelte/internal/server';
export default function Main($$payload) {
// needs to be a snapshot test because jsdom does auto-correct the attribute casing
let x = 'test';
let y = () => 'test';
- $$payload.out += `
`;
+ $$payload.out += `
`;
}
\ No newline at end of file
diff --git a/packages/svelte/tests/snapshot/samples/each-string-template/_expected/client/index.svelte.js b/packages/svelte/tests/snapshot/samples/each-string-template/_expected/client/index.svelte.js
index 80f2da11a2a1..c0626bd416c9 100644
--- a/packages/svelte/tests/snapshot/samples/each-string-template/_expected/client/index.svelte.js
+++ b/packages/svelte/tests/snapshot/samples/each-string-template/_expected/client/index.svelte.js
@@ -1,6 +1,6 @@
-import "svelte/internal/disclose-version";
-import "svelte/internal/flags/legacy";
-import * as $ from "svelte/internal/client";
+import 'svelte/internal/disclose-version';
+import 'svelte/internal/flags/legacy';
+import * as $ from 'svelte/internal/client';
export default function Each_string_template($$anchor) {
var fragment = $.comment();
@@ -11,7 +11,7 @@ export default function Each_string_template($$anchor) {
var text = $.text();
- $.template_effect(() => $.set_text(text, `${thing ?? ""}, `));
+ $.template_effect(() => $.set_text(text, `${thing ?? ''}, `));
$.append($$anchor, text);
});
diff --git a/packages/svelte/tests/snapshot/samples/each-string-template/_expected/server/index.svelte.js b/packages/svelte/tests/snapshot/samples/each-string-template/_expected/server/index.svelte.js
index d4debe172745..4386c22ebe0a 100644
--- a/packages/svelte/tests/snapshot/samples/each-string-template/_expected/server/index.svelte.js
+++ b/packages/svelte/tests/snapshot/samples/each-string-template/_expected/server/index.svelte.js
@@ -1,4 +1,4 @@
-import * as $ from "svelte/internal/server";
+import * as $ from 'svelte/internal/server';
export default function Each_string_template($$payload) {
const each_array = $.ensure_array_like(['foo', 'bar', 'baz']);
diff --git a/packages/svelte/tests/snapshot/samples/export-state/_expected/client/index.svelte.js b/packages/svelte/tests/snapshot/samples/export-state/_expected/client/index.svelte.js
index bab47c8c5004..c2a6054bc6f6 100644
--- a/packages/svelte/tests/snapshot/samples/export-state/_expected/client/index.svelte.js
+++ b/packages/svelte/tests/snapshot/samples/export-state/_expected/client/index.svelte.js
@@ -1,4 +1,4 @@
/* index.svelte.js generated by Svelte VERSION */
-import * as $ from "svelte/internal/client";
+import * as $ from 'svelte/internal/client';
export const object = $.proxy({ ok: true });
\ No newline at end of file
diff --git a/packages/svelte/tests/snapshot/samples/export-state/_expected/server/index.svelte.js b/packages/svelte/tests/snapshot/samples/export-state/_expected/server/index.svelte.js
index a3b619df6eef..1f6c24421251 100644
--- a/packages/svelte/tests/snapshot/samples/export-state/_expected/server/index.svelte.js
+++ b/packages/svelte/tests/snapshot/samples/export-state/_expected/server/index.svelte.js
@@ -1,4 +1,4 @@
/* index.svelte.js generated by Svelte VERSION */
-import * as $ from "svelte/internal/server";
+import * as $ from 'svelte/internal/server';
export const object = { ok: true };
\ No newline at end of file
diff --git a/packages/svelte/tests/snapshot/samples/function-prop-no-getter/_expected/client/index.svelte.js b/packages/svelte/tests/snapshot/samples/function-prop-no-getter/_expected/client/index.svelte.js
index 95d1c72017d3..762a23754c9b 100644
--- a/packages/svelte/tests/snapshot/samples/function-prop-no-getter/_expected/client/index.svelte.js
+++ b/packages/svelte/tests/snapshot/samples/function-prop-no-getter/_expected/client/index.svelte.js
@@ -1,5 +1,5 @@
-import "svelte/internal/disclose-version";
-import * as $ from "svelte/internal/client";
+import 'svelte/internal/disclose-version';
+import * as $ from 'svelte/internal/client';
export default function Function_prop_no_getter($$anchor) {
let count = $.state(0);
@@ -13,13 +13,13 @@ export default function Function_prop_no_getter($$anchor) {
Button($$anchor, {
onmousedown: () => $.set(count, $.get(count) + 1),
onmouseup,
- onmouseenter: () => $.set(count, $.proxy(plusOne($.get(count)))),
+ onmouseenter: () => $.set(count, plusOne($.get(count)), true),
children: ($$anchor, $$slotProps) => {
$.next();
var text = $.text();
- $.template_effect(() => $.set_text(text, `clicks: ${$.get(count) ?? ""}`));
+ $.template_effect(() => $.set_text(text, `clicks: ${$.get(count) ?? ''}`));
$.append($$anchor, text);
},
$$slots: { default: true }
diff --git a/packages/svelte/tests/snapshot/samples/function-prop-no-getter/_expected/server/index.svelte.js b/packages/svelte/tests/snapshot/samples/function-prop-no-getter/_expected/server/index.svelte.js
index 05b343d82161..88f6f55ee74a 100644
--- a/packages/svelte/tests/snapshot/samples/function-prop-no-getter/_expected/server/index.svelte.js
+++ b/packages/svelte/tests/snapshot/samples/function-prop-no-getter/_expected/server/index.svelte.js
@@ -1,4 +1,4 @@
-import * as $ from "svelte/internal/server";
+import * as $ from 'svelte/internal/server';
export default function Function_prop_no_getter($$payload) {
let count = 0;
diff --git a/packages/svelte/tests/snapshot/samples/hello-world/_expected/client/index.svelte.js b/packages/svelte/tests/snapshot/samples/hello-world/_expected/client/index.svelte.js
index 9f6f29166975..899c126001f2 100644
--- a/packages/svelte/tests/snapshot/samples/hello-world/_expected/client/index.svelte.js
+++ b/packages/svelte/tests/snapshot/samples/hello-world/_expected/client/index.svelte.js
@@ -1,6 +1,6 @@
-import "svelte/internal/disclose-version";
-import "svelte/internal/flags/legacy";
-import * as $ from "svelte/internal/client";
+import 'svelte/internal/disclose-version';
+import 'svelte/internal/flags/legacy';
+import * as $ from 'svelte/internal/client';
var root = $.template(`hello world `);
diff --git a/packages/svelte/tests/snapshot/samples/hello-world/_expected/server/index.svelte.js b/packages/svelte/tests/snapshot/samples/hello-world/_expected/server/index.svelte.js
index a313799dfd34..8766fb13007a 100644
--- a/packages/svelte/tests/snapshot/samples/hello-world/_expected/server/index.svelte.js
+++ b/packages/svelte/tests/snapshot/samples/hello-world/_expected/server/index.svelte.js
@@ -1,4 +1,4 @@
-import * as $ from "svelte/internal/server";
+import * as $ from 'svelte/internal/server';
export default function Hello_world($$payload) {
$$payload.out += `hello world `;
diff --git a/packages/svelte/tests/snapshot/samples/hmr/_expected/client/index.svelte.js b/packages/svelte/tests/snapshot/samples/hmr/_expected/client/index.svelte.js
index 86c2880abc8f..3c8322500b7c 100644
--- a/packages/svelte/tests/snapshot/samples/hmr/_expected/client/index.svelte.js
+++ b/packages/svelte/tests/snapshot/samples/hmr/_expected/client/index.svelte.js
@@ -1,6 +1,6 @@
-import "svelte/internal/disclose-version";
-import "svelte/internal/flags/legacy";
-import * as $ from "svelte/internal/client";
+import 'svelte/internal/disclose-version';
+import 'svelte/internal/flags/legacy';
+import * as $ from 'svelte/internal/client';
var root = $.template(`hello world `);
diff --git a/packages/svelte/tests/snapshot/samples/hmr/_expected/server/index.svelte.js b/packages/svelte/tests/snapshot/samples/hmr/_expected/server/index.svelte.js
index 33fe307c0984..959e0a403ec8 100644
--- a/packages/svelte/tests/snapshot/samples/hmr/_expected/server/index.svelte.js
+++ b/packages/svelte/tests/snapshot/samples/hmr/_expected/server/index.svelte.js
@@ -1,4 +1,4 @@
-import * as $ from "svelte/internal/server";
+import * as $ from 'svelte/internal/server';
export default function Hmr($$payload) {
$$payload.out += `hello world `;
diff --git a/packages/svelte/tests/snapshot/samples/imports-in-modules/_expected/client/index.svelte.js b/packages/svelte/tests/snapshot/samples/imports-in-modules/_expected/client/index.svelte.js
index 9c7d2f3f23b0..ebbe191dcbe4 100644
--- a/packages/svelte/tests/snapshot/samples/imports-in-modules/_expected/client/index.svelte.js
+++ b/packages/svelte/tests/snapshot/samples/imports-in-modules/_expected/client/index.svelte.js
@@ -1,6 +1,6 @@
-import "svelte/internal/disclose-version";
-import "svelte/internal/flags/legacy";
-import * as $ from "svelte/internal/client";
+import 'svelte/internal/disclose-version';
+import 'svelte/internal/flags/legacy';
+import * as $ from 'svelte/internal/client';
import { random } from './module.svelte';
export default function Imports_in_modules($$anchor) {
diff --git a/packages/svelte/tests/snapshot/samples/imports-in-modules/_expected/client/module.svelte.js b/packages/svelte/tests/snapshot/samples/imports-in-modules/_expected/client/module.svelte.js
index 6edbc8af77a3..0d366e6258ff 100644
--- a/packages/svelte/tests/snapshot/samples/imports-in-modules/_expected/client/module.svelte.js
+++ b/packages/svelte/tests/snapshot/samples/imports-in-modules/_expected/client/module.svelte.js
@@ -1,5 +1,5 @@
/* module.svelte.js generated by Svelte VERSION */
-import * as $ from "svelte/internal/client";
+import * as $ from 'svelte/internal/client';
import { random } from './export';
export { random };
\ No newline at end of file
diff --git a/packages/svelte/tests/snapshot/samples/imports-in-modules/_expected/server/index.svelte.js b/packages/svelte/tests/snapshot/samples/imports-in-modules/_expected/server/index.svelte.js
index 35b64b4186b9..4cd6bc59d782 100644
--- a/packages/svelte/tests/snapshot/samples/imports-in-modules/_expected/server/index.svelte.js
+++ b/packages/svelte/tests/snapshot/samples/imports-in-modules/_expected/server/index.svelte.js
@@ -1,4 +1,4 @@
-import * as $ from "svelte/internal/server";
+import * as $ from 'svelte/internal/server';
import { random } from './module.svelte';
export default function Imports_in_modules($$payload) {
diff --git a/packages/svelte/tests/snapshot/samples/imports-in-modules/_expected/server/module.svelte.js b/packages/svelte/tests/snapshot/samples/imports-in-modules/_expected/server/module.svelte.js
index e51aae5a2555..2e0af8af84d8 100644
--- a/packages/svelte/tests/snapshot/samples/imports-in-modules/_expected/server/module.svelte.js
+++ b/packages/svelte/tests/snapshot/samples/imports-in-modules/_expected/server/module.svelte.js
@@ -1,5 +1,5 @@
/* module.svelte.js generated by Svelte VERSION */
-import * as $ from "svelte/internal/server";
+import * as $ from 'svelte/internal/server';
import { random } from './export';
export { random };
\ No newline at end of file
diff --git a/packages/svelte/tests/snapshot/samples/nullish-coallescence-omittance/_config.js b/packages/svelte/tests/snapshot/samples/nullish-coallescence-omittance/_config.js
new file mode 100644
index 000000000000..f47bee71df87
--- /dev/null
+++ b/packages/svelte/tests/snapshot/samples/nullish-coallescence-omittance/_config.js
@@ -0,0 +1,3 @@
+import { test } from '../../test';
+
+export default test({});
diff --git a/packages/svelte/tests/snapshot/samples/nullish-coallescence-omittance/_expected/client/index.svelte.js b/packages/svelte/tests/snapshot/samples/nullish-coallescence-omittance/_expected/client/index.svelte.js
new file mode 100644
index 000000000000..332c909ebed9
--- /dev/null
+++ b/packages/svelte/tests/snapshot/samples/nullish-coallescence-omittance/_expected/client/index.svelte.js
@@ -0,0 +1,34 @@
+import 'svelte/internal/disclose-version';
+import * as $ from 'svelte/internal/client';
+
+var on_click = (_, count) => $.update(count);
+var root = $.template(` `, 1);
+
+export default function Nullish_coallescence_omittance($$anchor) {
+ let name = 'world';
+ let count = $.state(0);
+ var fragment = root();
+ var h1 = $.first_child(fragment);
+
+ h1.textContent = `Hello, ${name ?? ''}!`;
+
+ var b = $.sibling(h1, 2);
+
+ b.textContent = `${1 ?? 'stuff'}${2 ?? 'more stuff'}${3 ?? 'even more stuff'}`;
+
+ var button = $.sibling(b, 2);
+
+ button.__click = [on_click, count];
+
+ var text = $.child(button);
+
+ $.reset(button);
+
+ var h1_1 = $.sibling(button, 2);
+
+ h1_1.textContent = `Hello, ${name ?? 'earth' ?? ''}`;
+ $.template_effect(() => $.set_text(text, `Count is ${$.get(count) ?? ''}`));
+ $.append($$anchor, fragment);
+}
+
+$.delegate(['click']);
\ No newline at end of file
diff --git a/packages/svelte/tests/snapshot/samples/nullish-coallescence-omittance/_expected/server/index.svelte.js b/packages/svelte/tests/snapshot/samples/nullish-coallescence-omittance/_expected/server/index.svelte.js
new file mode 100644
index 000000000000..8181bfd98eeb
--- /dev/null
+++ b/packages/svelte/tests/snapshot/samples/nullish-coallescence-omittance/_expected/server/index.svelte.js
@@ -0,0 +1,8 @@
+import * as $ from 'svelte/internal/server';
+
+export default function Nullish_coallescence_omittance($$payload) {
+ let name = 'world';
+ let count = 0;
+
+ $$payload.out += `Hello, ${$.escape(name)}! ${$.escape(1 ?? 'stuff')}${$.escape(2 ?? 'more stuff')}${$.escape(3 ?? 'even more stuff')} Count is ${$.escape(count)} Hello, ${$.escape(name ?? 'earth' ?? null)} `;
+}
\ No newline at end of file
diff --git a/packages/svelte/tests/snapshot/samples/nullish-coallescence-omittance/index.svelte b/packages/svelte/tests/snapshot/samples/nullish-coallescence-omittance/index.svelte
new file mode 100644
index 000000000000..a67c574fee26
--- /dev/null
+++ b/packages/svelte/tests/snapshot/samples/nullish-coallescence-omittance/index.svelte
@@ -0,0 +1,8 @@
+
+Hello, {null}{name}!
+{1 ?? 'stuff'}{2 ?? 'more stuff'}{3 ?? 'even more stuff'}
+count++}>Count is {count}
+Hello, {name ?? 'earth' ?? null}
\ No newline at end of file
diff --git a/packages/svelte/tests/snapshot/samples/props-identifier/_expected/client/index.svelte.js b/packages/svelte/tests/snapshot/samples/props-identifier/_expected/client/index.svelte.js
index 2a10dbc1b1c6..5a46b9bbefe1 100644
--- a/packages/svelte/tests/snapshot/samples/props-identifier/_expected/client/index.svelte.js
+++ b/packages/svelte/tests/snapshot/samples/props-identifier/_expected/client/index.svelte.js
@@ -1,10 +1,10 @@
-import "svelte/internal/disclose-version";
-import * as $ from "svelte/internal/client";
+import 'svelte/internal/disclose-version';
+import * as $ from 'svelte/internal/client';
export default function Props_identifier($$anchor, $$props) {
$.push($$props, true);
- let props = $.rest_props($$props, ["$$slots", "$$events", "$$legacy"]);
+ let props = $.rest_props($$props, ['$$slots', '$$events', '$$legacy']);
$$props.a;
props[a];
diff --git a/packages/svelte/tests/snapshot/samples/props-identifier/_expected/server/index.svelte.js b/packages/svelte/tests/snapshot/samples/props-identifier/_expected/server/index.svelte.js
index 6fef87d63b77..33a3633939d5 100644
--- a/packages/svelte/tests/snapshot/samples/props-identifier/_expected/server/index.svelte.js
+++ b/packages/svelte/tests/snapshot/samples/props-identifier/_expected/server/index.svelte.js
@@ -1,4 +1,4 @@
-import * as $ from "svelte/internal/server";
+import * as $ from 'svelte/internal/server';
export default function Props_identifier($$payload, $$props) {
$.push();
diff --git a/packages/svelte/tests/snapshot/samples/purity/_expected/client/index.svelte.js b/packages/svelte/tests/snapshot/samples/purity/_expected/client/index.svelte.js
index 0a627a55ae6c..940ed8f9e8fa 100644
--- a/packages/svelte/tests/snapshot/samples/purity/_expected/client/index.svelte.js
+++ b/packages/svelte/tests/snapshot/samples/purity/_expected/client/index.svelte.js
@@ -1,6 +1,6 @@
-import "svelte/internal/disclose-version";
-import "svelte/internal/flags/legacy";
-import * as $ from "svelte/internal/client";
+import 'svelte/internal/disclose-version';
+import 'svelte/internal/flags/legacy';
+import * as $ from 'svelte/internal/client';
var root = $.template(`
`, 1);
diff --git a/packages/svelte/tests/snapshot/samples/purity/_expected/server/index.svelte.js b/packages/svelte/tests/snapshot/samples/purity/_expected/server/index.svelte.js
index 1eea71e4dd06..588332407a63 100644
--- a/packages/svelte/tests/snapshot/samples/purity/_expected/server/index.svelte.js
+++ b/packages/svelte/tests/snapshot/samples/purity/_expected/server/index.svelte.js
@@ -1,4 +1,4 @@
-import * as $ from "svelte/internal/server";
+import * as $ from 'svelte/internal/server';
export default function Purity($$payload) {
$$payload.out += `${$.escape(Math.max(0, Math.min(0, 100)))}
${$.escape(location.href)}
`;
diff --git a/packages/svelte/tests/snapshot/samples/skip-static-subtree/_expected/client/index.svelte.js b/packages/svelte/tests/snapshot/samples/skip-static-subtree/_expected/client/index.svelte.js
index 8dd7df82cf57..46d376aca2f9 100644
--- a/packages/svelte/tests/snapshot/samples/skip-static-subtree/_expected/client/index.svelte.js
+++ b/packages/svelte/tests/snapshot/samples/skip-static-subtree/_expected/client/index.svelte.js
@@ -1,5 +1,5 @@
-import "svelte/internal/disclose-version";
-import * as $ from "svelte/internal/client";
+import 'svelte/internal/disclose-version';
+import * as $ from 'svelte/internal/client';
var root = $.template(` we don't need to traverse these nodes
or
these
ones
these
trailing
nodes
can
be
completely
ignored
a `, 3);
@@ -20,7 +20,7 @@ export default function Skip_static_subtree($$anchor, $$props) {
var cant_skip = $.sibling(main, 2);
var custom_elements = $.child(cant_skip);
- $.set_custom_element_data(custom_elements, "with", "attributes");
+ $.set_custom_element_data(custom_elements, 'with', 'attributes');
$.reset(cant_skip);
var div = $.sibling(cant_skip, 2);
@@ -38,16 +38,12 @@ export default function Skip_static_subtree($$anchor, $$props) {
var select = $.sibling(div_1, 2);
var option = $.child(select);
- option.value = null == (option.__value = "a") ? "" : "a";
+ option.value = null == (option.__value = 'a') ? '' : 'a';
$.reset(select);
var img = $.sibling(select, 2);
- var div_2 = $.sibling(img, 2);
- var img_1 = $.child(div_2);
- $.reset(div_2);
+ $.next(2);
$.template_effect(() => $.set_text(text, $$props.title));
- $.handle_lazy_img(img);
- $.handle_lazy_img(img_1);
$.append($$anchor, fragment);
}
\ No newline at end of file
diff --git a/packages/svelte/tests/snapshot/samples/skip-static-subtree/_expected/server/index.svelte.js b/packages/svelte/tests/snapshot/samples/skip-static-subtree/_expected/server/index.svelte.js
index 309f5a2b57b3..e694c12647dc 100644
--- a/packages/svelte/tests/snapshot/samples/skip-static-subtree/_expected/server/index.svelte.js
+++ b/packages/svelte/tests/snapshot/samples/skip-static-subtree/_expected/server/index.svelte.js
@@ -1,4 +1,4 @@
-import * as $ from "svelte/internal/server";
+import * as $ from 'svelte/internal/server';
export default function Skip_static_subtree($$payload, $$props) {
let { title, content } = $$props;
diff --git a/packages/svelte/tests/snapshot/samples/state-proxy-literal/_expected/client/index.svelte.js b/packages/svelte/tests/snapshot/samples/state-proxy-literal/_expected/client/index.svelte.js
index c24023c8a438..a67210e5418a 100644
--- a/packages/svelte/tests/snapshot/samples/state-proxy-literal/_expected/client/index.svelte.js
+++ b/packages/svelte/tests/snapshot/samples/state-proxy-literal/_expected/client/index.svelte.js
@@ -1,5 +1,5 @@
-import "svelte/internal/disclose-version";
-import * as $ from "svelte/internal/client";
+import 'svelte/internal/disclose-version';
+import * as $ from 'svelte/internal/client';
function reset(_, str, tpl) {
$.set(str, '');
@@ -30,4 +30,4 @@ export default function State_proxy_literal($$anchor) {
$.append($$anchor, fragment);
}
-$.delegate(["click"]);
\ No newline at end of file
+$.delegate(['click']);
\ No newline at end of file
diff --git a/packages/svelte/tests/snapshot/samples/state-proxy-literal/_expected/server/index.svelte.js b/packages/svelte/tests/snapshot/samples/state-proxy-literal/_expected/server/index.svelte.js
index 3424f807ab01..7b2a884d7044 100644
--- a/packages/svelte/tests/snapshot/samples/state-proxy-literal/_expected/server/index.svelte.js
+++ b/packages/svelte/tests/snapshot/samples/state-proxy-literal/_expected/server/index.svelte.js
@@ -1,4 +1,4 @@
-import * as $ from "svelte/internal/server";
+import * as $ from 'svelte/internal/server';
export default function State_proxy_literal($$payload) {
let str = '';
@@ -11,5 +11,5 @@ export default function State_proxy_literal($$payload) {
tpl = ``;
}
- $$payload.out += ` reset `;
+ $$payload.out += ` reset `;
}
\ No newline at end of file
diff --git a/packages/svelte/tests/snapshot/samples/svelte-element/_expected/client/index.svelte.js b/packages/svelte/tests/snapshot/samples/svelte-element/_expected/client/index.svelte.js
index a4bbea582bf3..2270005ee0dd 100644
--- a/packages/svelte/tests/snapshot/samples/svelte-element/_expected/client/index.svelte.js
+++ b/packages/svelte/tests/snapshot/samples/svelte-element/_expected/client/index.svelte.js
@@ -1,8 +1,8 @@
-import "svelte/internal/disclose-version";
-import * as $ from "svelte/internal/client";
+import 'svelte/internal/disclose-version';
+import * as $ from 'svelte/internal/client';
export default function Svelte_element($$anchor, $$props) {
- let tag = $.prop($$props, "tag", 3, 'hr');
+ let tag = $.prop($$props, 'tag', 3, 'hr');
var fragment = $.comment();
var node = $.first_child(fragment);
diff --git a/packages/svelte/tests/snapshot/samples/svelte-element/_expected/server/index.svelte.js b/packages/svelte/tests/snapshot/samples/svelte-element/_expected/server/index.svelte.js
index 4a766f7de5dd..4426ad1164d3 100644
--- a/packages/svelte/tests/snapshot/samples/svelte-element/_expected/server/index.svelte.js
+++ b/packages/svelte/tests/snapshot/samples/svelte-element/_expected/server/index.svelte.js
@@ -1,4 +1,4 @@
-import * as $ from "svelte/internal/server";
+import * as $ from 'svelte/internal/server';
export default function Svelte_element($$payload, $$props) {
let { tag = 'hr' } = $$props;
diff --git a/packages/svelte/tests/snapshot/samples/text-nodes-deriveds/_expected/client/index.svelte.js b/packages/svelte/tests/snapshot/samples/text-nodes-deriveds/_expected/client/index.svelte.js
index 8ba14526aaf9..d520d1ef2488 100644
--- a/packages/svelte/tests/snapshot/samples/text-nodes-deriveds/_expected/client/index.svelte.js
+++ b/packages/svelte/tests/snapshot/samples/text-nodes-deriveds/_expected/client/index.svelte.js
@@ -1,5 +1,5 @@
-import "svelte/internal/disclose-version";
-import * as $ from "svelte/internal/client";
+import 'svelte/internal/disclose-version';
+import * as $ from 'svelte/internal/client';
var root = $.template(`
`);
@@ -16,11 +16,9 @@ export default function Text_nodes_deriveds($$anchor) {
}
var p = root();
- const stringified_text = $.derived(() => text1() ?? "");
- const stringified_text_1 = $.derived(() => text2() ?? "");
var text = $.child(p);
- $.template_effect(() => $.set_text(text, `${$.get(stringified_text)}${$.get(stringified_text_1)}`));
$.reset(p);
+ $.template_effect(($0, $1) => $.set_text(text, `${$0 ?? ''}${$1 ?? ''}`), [text1, text2]);
$.append($$anchor, p);
}
\ No newline at end of file
diff --git a/packages/svelte/tests/snapshot/samples/text-nodes-deriveds/_expected/server/index.svelte.js b/packages/svelte/tests/snapshot/samples/text-nodes-deriveds/_expected/server/index.svelte.js
index f7dc4176e4af..6f019647f58b 100644
--- a/packages/svelte/tests/snapshot/samples/text-nodes-deriveds/_expected/server/index.svelte.js
+++ b/packages/svelte/tests/snapshot/samples/text-nodes-deriveds/_expected/server/index.svelte.js
@@ -1,4 +1,4 @@
-import * as $ from "svelte/internal/server";
+import * as $ from 'svelte/internal/server';
export default function Text_nodes_deriveds($$payload) {
let count1 = 0;
diff --git a/packages/svelte/tests/snapshot/test.ts b/packages/svelte/tests/snapshot/test.ts
index 88cf9193c3f7..0a591c6e2a71 100644
--- a/packages/svelte/tests/snapshot/test.ts
+++ b/packages/svelte/tests/snapshot/test.ts
@@ -1,6 +1,6 @@
import * as fs from 'node:fs';
import { assert, expect } from 'vitest';
-import glob from 'tiny-glob/sync.js';
+import { globSync } from 'tinyglobby';
import { compile_directory } from '../helpers.js';
import { suite, type BaseTest } from '../suite.js';
import { VERSION } from 'svelte/compiler';
@@ -18,8 +18,8 @@ const { test, run } = suite(async (config, cwd) => {
fs.rmSync(`${cwd}/_expected`, { recursive: true, force: true });
fs.cpSync(`${cwd}/_output`, `${cwd}/_expected`, { recursive: true, force: true });
} else {
- const actual = glob('**', { cwd: `${cwd}/_output`, filesOnly: true });
- const expected = glob('**', { cwd: `${cwd}/_expected`, filesOnly: true });
+ const actual = globSync('**', { cwd: `${cwd}/_output`, onlyFiles: true });
+ const expected = globSync('**', { cwd: `${cwd}/_expected`, onlyFiles: true });
assert.deepEqual(actual, expected);
diff --git a/packages/svelte/tests/sourcemaps/samples/css-injected-map/_config.js b/packages/svelte/tests/sourcemaps/samples/css-injected-map/_config.js
index 74a2185076b1..c2b2fe766a1b 100644
--- a/packages/svelte/tests/sourcemaps/samples/css-injected-map/_config.js
+++ b/packages/svelte/tests/sourcemaps/samples/css-injected-map/_config.js
@@ -30,7 +30,7 @@ export default test({
async test({ assert, code_client }) {
// Check that the css source map embedded in the js is accurate
const match = code_client.match(
- /code: "(.*?)(?:\\n\/\*# sourceMappingURL=data:(.*?);charset=(.*?);base64,(.*?) \*\/)?"/
+ /code: '(.*?)(?:\\n\/\*# sourceMappingURL=data:(.*?);charset=(.*?);base64,(.*?) \*\/)?'/
);
assert.ok(match);
diff --git a/packages/svelte/tests/store/test.ts b/packages/svelte/tests/store/test.ts
index b23ea195d6c9..77cecca7e525 100644
--- a/packages/svelte/tests/store/test.ts
+++ b/packages/svelte/tests/store/test.ts
@@ -602,7 +602,7 @@ describe('toStore', () => {
assert.deepEqual(log, [0]);
set(count, 1);
- $.flush_sync();
+ $.flushSync();
assert.deepEqual(log, [0, 1]);
unsubscribe();
@@ -625,7 +625,7 @@ describe('toStore', () => {
assert.deepEqual(log, [0]);
set(count, 1);
- $.flush_sync();
+ $.flushSync();
assert.deepEqual(log, [0, 1]);
store.set(2);
@@ -654,11 +654,11 @@ describe('fromStore', () => {
assert.deepEqual(log, [0]);
store.set(1);
- $.flush_sync();
+ $.flushSync();
assert.deepEqual(log, [0, 1]);
count.current = 2;
- $.flush_sync();
+ $.flushSync();
assert.deepEqual(log, [0, 1, 2]);
assert.equal(get(store), 2);
diff --git a/packages/svelte/tests/types/bindings.svelte b/packages/svelte/tests/types/bindings.svelte
new file mode 100644
index 000000000000..ce99b2c296c9
--- /dev/null
+++ b/packages/svelte/tests/types/bindings.svelte
@@ -0,0 +1,8 @@
+
+
+
+
+
+
diff --git a/packages/svelte/tests/validator/samples/a11y-label-has-associated-control/input.svelte b/packages/svelte/tests/validator/samples/a11y-label-has-associated-control/input.svelte
index 124888c089ad..f47743b33b26 100644
--- a/packages/svelte/tests/validator/samples/a11y-label-has-associated-control/input.svelte
+++ b/packages/svelte/tests/validator/samples/a11y-label-has-associated-control/input.svelte
@@ -10,3 +10,4 @@
E
F {#if true} {/if}
G
+E
\ No newline at end of file
diff --git a/packages/svelte/tests/validator/samples/a11y-mouse-events-have-key-events/input.svelte b/packages/svelte/tests/validator/samples/a11y-mouse-events-have-key-events/input.svelte
index 613b80e6d940..f9fe4f15c1c3 100644
--- a/packages/svelte/tests/validator/samples/a11y-mouse-events-have-key-events/input.svelte
+++ b/packages/svelte/tests/validator/samples/a11y-mouse-events-have-key-events/input.svelte
@@ -1,21 +1,19 @@
- void 0}>
+ {}}>
- void 0} on:focus={() => void 0}>
+ {}} onfocus={() => {}}>
- void 0} {...otherProps}>
+ {}} {...otherProps}>
- void 0}>
+ {}}>
- void 0} on:blur={() => void 0}>
+ {}} onblur={() => {}}>
- void 0} {...otherProps}>
+ {}} {...otherProps}>
diff --git a/packages/svelte/tests/validator/samples/a11y-mouse-events-have-key-events/warnings.json b/packages/svelte/tests/validator/samples/a11y-mouse-events-have-key-events/warnings.json
index 574b019e0f61..3dee4e967341 100644
--- a/packages/svelte/tests/validator/samples/a11y-mouse-events-have-key-events/warnings.json
+++ b/packages/svelte/tests/validator/samples/a11y-mouse-events-have-key-events/warnings.json
@@ -2,49 +2,25 @@
{
"code": "a11y_mouse_events_have_key_events",
"end": {
- "column": 39,
- "line": 11
+ "column": 34,
+ "line": 9
},
"message": "'mouseover' event must be accompanied by 'focus' event",
"start": {
"column": 0,
- "line": 11
+ "line": 9
}
},
{
"code": "a11y_mouse_events_have_key_events",
"end": {
- "column": 55,
+ "column": 33,
"line": 15
},
- "message": "'mouseover' event must be accompanied by 'focus' event",
- "start": {
- "column": 0,
- "line": 15
- }
- },
- {
- "code": "a11y_mouse_events_have_key_events",
- "end": {
- "column": 38,
- "line": 17
- },
"message": "'mouseout' event must be accompanied by 'blur' event",
"start": {
"column": 0,
- "line": 17
- }
- },
- {
- "code": "a11y_mouse_events_have_key_events",
- "end": {
- "column": 54,
- "line": 21
- },
- "message": "'mouseout' event must be accompanied by 'blur' event",
- "start": {
- "column": 0,
- "line": 21
+ "line": 15
}
}
]
diff --git a/packages/svelte/tests/validator/samples/a11y-no-noninteractive-element-interactions/input.svelte b/packages/svelte/tests/validator/samples/a11y-no-noninteractive-element-interactions/input.svelte
index 1adea90ba39b..759f969d8267 100644
--- a/packages/svelte/tests/validator/samples/a11y-no-noninteractive-element-interactions/input.svelte
+++ b/packages/svelte/tests/validator/samples/a11y-no-noninteractive-element-interactions/input.svelte
@@ -3,6 +3,7 @@
{}} on:keypress={() => {}}>
{}} on:keypress={() => {}}>
{}}>click me
+ {}}>alert
{}}>Heading
Heading
diff --git a/packages/svelte/tests/validator/samples/a11y-no-noninteractive-element-interactions/warnings.json b/packages/svelte/tests/validator/samples/a11y-no-noninteractive-element-interactions/warnings.json
index 760866d13614..e8bcd0cc1a03 100644
--- a/packages/svelte/tests/validator/samples/a11y-no-noninteractive-element-interactions/warnings.json
+++ b/packages/svelte/tests/validator/samples/a11y-no-noninteractive-element-interactions/warnings.json
@@ -3,60 +3,60 @@
"code": "a11y_no_noninteractive_element_interactions",
"end": {
"column": 51,
- "line": 10
+ "line": 11
},
"message": "Non-interactive element `` should not be assigned mouse or keyboard event listeners",
"start": {
"column": 0,
- "line": 10
+ "line": 11
}
},
{
"code": "a11y_no_noninteractive_element_interactions",
"end": {
"column": 58,
- "line": 11
+ "line": 12
},
"message": "Non-interactive element `
` should not be assigned mouse or keyboard event listeners",
"start": {
"column": 0,
- "line": 11
+ "line": 12
}
},
{
"code": "a11y_no_noninteractive_element_interactions",
"end": {
"column": 50,
- "line": 12
+ "line": 13
},
"message": "Non-interactive element `` should not be assigned mouse or keyboard event listeners",
"start": {
"column": 0,
- "line": 12
+ "line": 13
}
},
{
"code": "a11y_no_noninteractive_element_interactions",
"end": {
"column": 30,
- "line": 13
+ "line": 14
},
"message": "Non-interactive element ` ` should not be assigned mouse or keyboard event listeners",
"start": {
"column": 0,
- "line": 13
+ "line": 14
}
},
{
"code": "a11y_no_noninteractive_element_interactions",
"end": {
"column": 50,
- "line": 14
+ "line": 15
},
"message": "Non-interactive element `
` should not be assigned mouse or keyboard event listeners",
"start": {
"column": 0,
- "line": 14
+ "line": 15
}
}
]
diff --git a/packages/svelte/tests/validator/samples/bind-group-snippet-parameter/errors.json b/packages/svelte/tests/validator/samples/bind-group-snippet-parameter/errors.json
new file mode 100644
index 000000000000..15e762419f1d
--- /dev/null
+++ b/packages/svelte/tests/validator/samples/bind-group-snippet-parameter/errors.json
@@ -0,0 +1,14 @@
+[
+ {
+ "code": "bind_group_invalid_snippet_parameter",
+ "end": {
+ "column": 44,
+ "line": 2
+ },
+ "message": "Cannot `bind:group` to a snippet parameter",
+ "start": {
+ "column": 21,
+ "line": 2
+ }
+ }
+]
diff --git a/packages/svelte/tests/validator/samples/bind-group-snippet-parameter/input.svelte b/packages/svelte/tests/validator/samples/bind-group-snippet-parameter/input.svelte
new file mode 100644
index 000000000000..368484788a1f
--- /dev/null
+++ b/packages/svelte/tests/validator/samples/bind-group-snippet-parameter/input.svelte
@@ -0,0 +1,3 @@
+{#snippet test(group)}
+
+{/snippet}
\ No newline at end of file
diff --git a/packages/svelte/tests/validator/samples/comment-before-function-binding/errors.json b/packages/svelte/tests/validator/samples/comment-before-function-binding/errors.json
new file mode 100644
index 000000000000..fe51488c7066
--- /dev/null
+++ b/packages/svelte/tests/validator/samples/comment-before-function-binding/errors.json
@@ -0,0 +1 @@
+[]
diff --git a/packages/svelte/tests/validator/samples/comment-before-function-binding/input.svelte b/packages/svelte/tests/validator/samples/comment-before-function-binding/input.svelte
new file mode 100644
index 000000000000..50200a9eac5e
--- /dev/null
+++ b/packages/svelte/tests/validator/samples/comment-before-function-binding/input.svelte
@@ -0,0 +1,10 @@
+
+
+
value,
+ (v) => value = v.toLowerCase()
+}
+/>
diff --git a/packages/svelte/tests/validator/samples/const-tag-inside-key-block/errors.json b/packages/svelte/tests/validator/samples/const-tag-inside-key-block/errors.json
new file mode 100644
index 000000000000..fe51488c7066
--- /dev/null
+++ b/packages/svelte/tests/validator/samples/const-tag-inside-key-block/errors.json
@@ -0,0 +1 @@
+[]
diff --git a/packages/svelte/tests/validator/samples/const-tag-inside-key-block/input.svelte b/packages/svelte/tests/validator/samples/const-tag-inside-key-block/input.svelte
new file mode 100644
index 000000000000..008072bc4756
--- /dev/null
+++ b/packages/svelte/tests/validator/samples/const-tag-inside-key-block/input.svelte
@@ -0,0 +1,3 @@
+{#key 'key'}
+ {@const foo = 'bar'}
+{/key}
diff --git a/packages/svelte/tests/validator/samples/const-tag-invalid-rune-usage/errors.json b/packages/svelte/tests/validator/samples/const-tag-invalid-rune-usage/errors.json
new file mode 100644
index 000000000000..32594e426846
--- /dev/null
+++ b/packages/svelte/tests/validator/samples/const-tag-invalid-rune-usage/errors.json
@@ -0,0 +1,14 @@
+[
+ {
+ "code": "state_invalid_placement",
+ "message": "`$derived(...)` can only be used as a variable declaration initializer or a class field",
+ "start": {
+ "line": 2,
+ "column": 15
+ },
+ "end": {
+ "line": 2,
+ "column": 26
+ }
+ }
+]
diff --git a/packages/svelte/tests/validator/samples/const-tag-invalid-rune-usage/input.svelte b/packages/svelte/tests/validator/samples/const-tag-invalid-rune-usage/input.svelte
new file mode 100644
index 000000000000..a056058cc59d
--- /dev/null
+++ b/packages/svelte/tests/validator/samples/const-tag-invalid-rune-usage/input.svelte
@@ -0,0 +1,3 @@
+{#snippet test()}
+ {@const der = $derived(0)}
+{/snippet}
\ No newline at end of file
diff --git a/packages/svelte/tests/validator/samples/const-tag-placement-1/errors.json b/packages/svelte/tests/validator/samples/const-tag-placement-1/errors.json
index 144345527ad7..514e5d056195 100644
--- a/packages/svelte/tests/validator/samples/const-tag-placement-1/errors.json
+++ b/packages/svelte/tests/validator/samples/const-tag-placement-1/errors.json
@@ -1,7 +1,7 @@
[
{
"code": "const_tag_invalid_placement",
- "message": "`{@const}` must be the immediate child of `{#snippet}`, `{#if}`, `{:else if}`, `{:else}`, `{#each}`, `{:then}`, `{:catch}`, `
` or ``",
+ "message": "`{@const}` must be the immediate child of `{#snippet}`, `{#if}`, `{:else if}`, `{:else}`, `{#each}`, `{:then}`, `{:catch}`, ``, ``",
"start": {
"line": 5,
"column": 0
diff --git a/packages/svelte/tests/validator/samples/const-tag-placement-2/errors.json b/packages/svelte/tests/validator/samples/const-tag-placement-2/errors.json
index 64a24c5f481a..6b968f7eda01 100644
--- a/packages/svelte/tests/validator/samples/const-tag-placement-2/errors.json
+++ b/packages/svelte/tests/validator/samples/const-tag-placement-2/errors.json
@@ -1,7 +1,7 @@
[
{
"code": "const_tag_invalid_placement",
- "message": "`{@const}` must be the immediate child of `{#snippet}`, `{#if}`, `{:else if}`, `{:else}`, `{#each}`, `{:then}`, `{:catch}`, `` or ``",
+ "message": "`{@const}` must be the immediate child of `{#snippet}`, `{#if}`, `{:else if}`, `{:else}`, `{#each}`, `{:then}`, `{:catch}`, ``, ``",
"start": {
"line": 7,
"column": 4
diff --git a/packages/svelte/tests/validator/samples/const-tag-placement-svelte-boundary/errors.json b/packages/svelte/tests/validator/samples/const-tag-placement-svelte-boundary/errors.json
new file mode 100644
index 000000000000..fe51488c7066
--- /dev/null
+++ b/packages/svelte/tests/validator/samples/const-tag-placement-svelte-boundary/errors.json
@@ -0,0 +1 @@
+[]
diff --git a/packages/svelte/tests/validator/samples/const-tag-placement-svelte-boundary/input.svelte b/packages/svelte/tests/validator/samples/const-tag-placement-svelte-boundary/input.svelte
new file mode 100644
index 000000000000..5708cc36ca00
--- /dev/null
+++ b/packages/svelte/tests/validator/samples/const-tag-placement-svelte-boundary/input.svelte
@@ -0,0 +1,11 @@
+
+
+
+ {@const x = a}
+ {#snippet failed()}
+ {x}
+ {/snippet}
+
+
\ No newline at end of file
diff --git a/packages/svelte/tests/validator/samples/invalid-node-placement-template/errors.json b/packages/svelte/tests/validator/samples/invalid-node-placement-template/errors.json
new file mode 100644
index 000000000000..fe51488c7066
--- /dev/null
+++ b/packages/svelte/tests/validator/samples/invalid-node-placement-template/errors.json
@@ -0,0 +1 @@
+[]
diff --git a/packages/svelte/tests/validator/samples/invalid-node-placement-template/input.svelte b/packages/svelte/tests/validator/samples/invalid-node-placement-template/input.svelte
new file mode 100644
index 000000000000..65aef2a7aacc
--- /dev/null
+++ b/packages/svelte/tests/validator/samples/invalid-node-placement-template/input.svelte
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/packages/svelte/tests/validator/samples/multiple-var-same-name/errors.json b/packages/svelte/tests/validator/samples/multiple-var-same-name/errors.json
new file mode 100644
index 000000000000..fe51488c7066
--- /dev/null
+++ b/packages/svelte/tests/validator/samples/multiple-var-same-name/errors.json
@@ -0,0 +1 @@
+[]
diff --git a/packages/svelte/tests/validator/samples/multiple-var-same-name/input.svelte b/packages/svelte/tests/validator/samples/multiple-var-same-name/input.svelte
new file mode 100644
index 000000000000..19a8ef7722ae
--- /dev/null
+++ b/packages/svelte/tests/validator/samples/multiple-var-same-name/input.svelte
@@ -0,0 +1,6 @@
+
+
+{test}
\ No newline at end of file
diff --git a/packages/svelte/tests/validator/samples/mutate-derived-private-field/errors.json b/packages/svelte/tests/validator/samples/mutate-derived-private-field/errors.json
new file mode 100644
index 000000000000..fe51488c7066
--- /dev/null
+++ b/packages/svelte/tests/validator/samples/mutate-derived-private-field/errors.json
@@ -0,0 +1 @@
+[]
diff --git a/packages/svelte/tests/validator/samples/mutate-derived-private-field/input.svelte b/packages/svelte/tests/validator/samples/mutate-derived-private-field/input.svelte
new file mode 100644
index 000000000000..0a3df4bc1b4a
--- /dev/null
+++ b/packages/svelte/tests/validator/samples/mutate-derived-private-field/input.svelte
@@ -0,0 +1,9 @@
+
\ No newline at end of file
diff --git a/packages/svelte/tests/validator/samples/reassign-derived-private-public-field/errors.json b/packages/svelte/tests/validator/samples/reassign-derived-private-public-field/errors.json
new file mode 100644
index 000000000000..fe51488c7066
--- /dev/null
+++ b/packages/svelte/tests/validator/samples/reassign-derived-private-public-field/errors.json
@@ -0,0 +1 @@
+[]
diff --git a/packages/svelte/tests/validator/samples/reassign-derived-private-public-field/input.svelte b/packages/svelte/tests/validator/samples/reassign-derived-private-public-field/input.svelte
new file mode 100644
index 000000000000..92f356492042
--- /dev/null
+++ b/packages/svelte/tests/validator/samples/reassign-derived-private-public-field/input.svelte
@@ -0,0 +1,13 @@
+
\ No newline at end of file
diff --git a/packages/svelte/tests/validator/samples/rune-invalid-spread-derived-by/errors.json b/packages/svelte/tests/validator/samples/rune-invalid-spread-derived-by/errors.json
new file mode 100644
index 000000000000..be59da95fa6a
--- /dev/null
+++ b/packages/svelte/tests/validator/samples/rune-invalid-spread-derived-by/errors.json
@@ -0,0 +1,14 @@
+[
+ {
+ "code": "rune_invalid_spread",
+ "end": {
+ "column": 35,
+ "line": 3
+ },
+ "message": "`$derived.by` cannot be called with a spread argument",
+ "start": {
+ "column": 15,
+ "line": 3
+ }
+ }
+]
diff --git a/packages/svelte/tests/validator/samples/rune-invalid-spread-derived-by/input.svelte b/packages/svelte/tests/validator/samples/rune-invalid-spread-derived-by/input.svelte
new file mode 100644
index 000000000000..49e8057aa508
--- /dev/null
+++ b/packages/svelte/tests/validator/samples/rune-invalid-spread-derived-by/input.svelte
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/packages/svelte/tests/validator/samples/rune-invalid-spread-derived/errors.json b/packages/svelte/tests/validator/samples/rune-invalid-spread-derived/errors.json
new file mode 100644
index 000000000000..6a333bc36233
--- /dev/null
+++ b/packages/svelte/tests/validator/samples/rune-invalid-spread-derived/errors.json
@@ -0,0 +1,14 @@
+[
+ {
+ "code": "rune_invalid_spread",
+ "end": {
+ "column": 32,
+ "line": 3
+ },
+ "message": "`$derived` cannot be called with a spread argument",
+ "start": {
+ "column": 15,
+ "line": 3
+ }
+ }
+]
diff --git a/packages/svelte/tests/validator/samples/rune-invalid-spread-derived/input.svelte b/packages/svelte/tests/validator/samples/rune-invalid-spread-derived/input.svelte
new file mode 100644
index 000000000000..9155493e1705
--- /dev/null
+++ b/packages/svelte/tests/validator/samples/rune-invalid-spread-derived/input.svelte
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/packages/svelte/tests/validator/samples/rune-invalid-spread-state-raw/errors.json b/packages/svelte/tests/validator/samples/rune-invalid-spread-state-raw/errors.json
new file mode 100644
index 000000000000..e08b498fcbee
--- /dev/null
+++ b/packages/svelte/tests/validator/samples/rune-invalid-spread-state-raw/errors.json
@@ -0,0 +1,14 @@
+[
+ {
+ "code": "rune_invalid_spread",
+ "end": {
+ "column": 34,
+ "line": 3
+ },
+ "message": "`$state.raw` cannot be called with a spread argument",
+ "start": {
+ "column": 15,
+ "line": 3
+ }
+ }
+]
diff --git a/packages/svelte/tests/validator/samples/rune-invalid-spread-state-raw/input.svelte b/packages/svelte/tests/validator/samples/rune-invalid-spread-state-raw/input.svelte
new file mode 100644
index 000000000000..d06fb053b3d9
--- /dev/null
+++ b/packages/svelte/tests/validator/samples/rune-invalid-spread-state-raw/input.svelte
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/packages/svelte/tests/validator/samples/rune-invalid-spread-state/errors.json b/packages/svelte/tests/validator/samples/rune-invalid-spread-state/errors.json
new file mode 100644
index 000000000000..11ae2abce538
--- /dev/null
+++ b/packages/svelte/tests/validator/samples/rune-invalid-spread-state/errors.json
@@ -0,0 +1,14 @@
+[
+ {
+ "code": "rune_invalid_spread",
+ "end": {
+ "column": 30,
+ "line": 3
+ },
+ "message": "`$state` cannot be called with a spread argument",
+ "start": {
+ "column": 15,
+ "line": 3
+ }
+ }
+]
diff --git a/packages/svelte/tests/validator/samples/rune-invalid-spread-state/input.svelte b/packages/svelte/tests/validator/samples/rune-invalid-spread-state/input.svelte
new file mode 100644
index 000000000000..02feac893f6d
--- /dev/null
+++ b/packages/svelte/tests/validator/samples/rune-invalid-spread-state/input.svelte
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/packages/svelte/tests/validator/samples/slot-attribute-component/input.svelte b/packages/svelte/tests/validator/samples/slot-attribute-component/input.svelte
index 5acb14e4095b..5d559e614e8c 100644
--- a/packages/svelte/tests/validator/samples/slot-attribute-component/input.svelte
+++ b/packages/svelte/tests/validator/samples/slot-attribute-component/input.svelte
@@ -1,2 +1,8 @@
valid
valid
+
+ {#if true}
+ valid
+ valid
+ {/if}
+
\ No newline at end of file
diff --git a/packages/svelte/tsconfig.json b/packages/svelte/tsconfig.json
index c9f0fb3b2bba..76005add13be 100644
--- a/packages/svelte/tsconfig.json
+++ b/packages/svelte/tsconfig.json
@@ -15,7 +15,6 @@
"allowJs": true,
"checkJs": true,
"paths": {
- "acorn-typescript": ["./src/compiler/phases/1-parse/ambient.d.ts"],
"svelte": ["./src/index.d.ts"],
"svelte/action": ["./src/action/public.d.ts"],
"svelte/compiler": ["./src/compiler/public.d.ts"],
diff --git a/packages/svelte/types/index.d.ts b/packages/svelte/types/index.d.ts
index d00b2b01ed18..c6000fc4b67f 100644
--- a/packages/svelte/types/index.d.ts
+++ b/packages/svelte/types/index.d.ts
@@ -16,6 +16,7 @@ declare module 'svelte' {
intro?: boolean;
recover?: boolean;
sync?: boolean;
+ idPrefix?: string;
$$inline?: boolean;
}
@@ -348,13 +349,14 @@ declare module 'svelte' {
props: Props;
});
/**
- * The `onMount` function schedules a callback to run as soon as the component has been mounted to the DOM.
- * It must be called during the component's initialisation (but doesn't need to live *inside* the component;
- * it can be called from an external module).
+ * `onMount`, like [`$effect`](https://svelte.dev/docs/svelte/$effect), schedules a function to run as soon as the component has been mounted to the DOM.
+ * Unlike `$effect`, the provided function only runs once.
*
- * If a function is returned _synchronously_ from `onMount`, it will be called when the component is unmounted.
+ * It must be called during the component's initialisation (but doesn't need to live _inside_ the component;
+ * it can be called from an external module). If a function is returned _synchronously_ from `onMount`,
+ * it will be called when the component is unmounted.
*
- * `onMount` does not run inside [server-side components](https://svelte.dev/docs/svelte/svelte-server#render).
+ * `onMount` functions do not run during [server-side rendering](https://svelte.dev/docs/svelte/svelte-server#render).
*
* */
export function onMount(fn: () => NotFunction | Promise> | (() => any)): void;
@@ -408,10 +410,6 @@ declare module 'svelte' {
* @deprecated Use [`$effect`](https://svelte.dev/docs/svelte/$effect) instead
* */
export function afterUpdate(fn: () => void): void;
- /**
- * Synchronously flushes any pending state changes and those that result from it.
- * */
- export function flushSync(fn?: (() => void) | undefined): void;
/**
* Create a snippet programmatically
* */
@@ -422,50 +420,10 @@ declare module 'svelte' {
/** Anything except a function */
type NotFunction = 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.
- * Transitions will play during the initial render unless the `intro` option is set to `false`.
- *
- * */
- export function mount, Exports extends Record>(component: ComponentType> | Component, options: MountOptions): 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, Exports extends Record>(component: ComponentType> | Component, options: {} extends Props ? {
- target: Document | Element | ShadowRoot;
- props?: Props;
- events?: Record any>;
- context?: Map;
- intro?: boolean;
- recover?: boolean;
- } : {
- target: Document | Element | ShadowRoot;
- props: Props;
- events?: Record any>;
- context?: Map;
- intro?: boolean;
- recover?: boolean;
- }): Exports;
- /**
- * Unmounts a component that was previously mounted using `mount` or `hydrate`.
- *
- * Since 5.13.0, if `options.outro` is `true`, [transitions](https://svelte.dev/docs/svelte/transition) will play before the component is removed from the DOM.
- *
- * Returns a `Promise` that resolves after transitions have completed if `options.outro` is true, or immediately otherwise (prior to 5.13.0, returns `void`).
- *
- * ```js
- * import { mount, unmount } from 'svelte';
- * import App from './App.svelte';
- *
- * const app = mount(App, { target: document.body });
- *
- * // later...
- * unmount(app, { outro: true });
- * ```
+ * Synchronously flush any pending updates.
+ * Returns void if no callback is provided, otherwise returns the result of calling the callback.
* */
- export function unmount(component: Record, options?: {
- outro?: boolean;
- } | undefined): Promise;
+ export function flushSync(fn?: (() => T) | undefined): T;
/**
* Returns a promise that resolves once any pending state changes have been applied.
* */
@@ -512,6 +470,51 @@ declare module 'svelte' {
*
* */
export function getAllContexts = Map>(): T;
+ /**
+ * Mounts a component to the given target and returns the exports and potentially the props (if compiled with `accessors: true`) of the component.
+ * Transitions will play during the initial render unless the `intro` option is set to `false`.
+ *
+ * */
+ export function mount, Exports extends Record>(component: ComponentType> | Component, options: MountOptions): 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, Exports extends Record>(component: ComponentType> | Component, options: {} extends Props ? {
+ target: Document | Element | ShadowRoot;
+ props?: Props;
+ events?: Record any>;
+ context?: Map;
+ intro?: boolean;
+ recover?: boolean;
+ } : {
+ target: Document | Element | ShadowRoot;
+ props: Props;
+ events?: Record any>;
+ context?: Map;
+ intro?: boolean;
+ recover?: boolean;
+ }): Exports;
+ /**
+ * Unmounts a component that was previously mounted using `mount` or `hydrate`.
+ *
+ * Since 5.13.0, if `options.outro` is `true`, [transitions](https://svelte.dev/docs/svelte/transition) will play before the component is removed from the DOM.
+ *
+ * Returns a `Promise` that resolves after transitions have completed if `options.outro` is true, or immediately otherwise (prior to 5.13.0, returns `void`).
+ *
+ * ```js
+ * import { mount, unmount } from 'svelte';
+ * import App from './App.svelte';
+ *
+ * const app = mount(App, { target: document.body });
+ *
+ * // later...
+ * unmount(app, { outro: true });
+ * ```
+ * */
+ export function unmount(component: Record, options?: {
+ outro?: boolean;
+ } | undefined): Promise;
type Getters = {
[K in keyof T]: () => T[K];
};
@@ -622,8 +625,8 @@ declare module 'svelte/animate' {
}
declare module 'svelte/compiler' {
- import type { Expression, Identifier, ArrayExpression, ArrowFunctionExpression, VariableDeclaration, VariableDeclarator, MemberExpression, Node, ObjectExpression, Pattern, Program, ChainExpression, SimpleCallExpression, SequenceExpression } from 'estree';
import type { SourceMap } from 'magic-string';
+ import type { ArrayExpression, ArrowFunctionExpression, VariableDeclaration, VariableDeclarator, Expression, Identifier, MemberExpression, Node, ObjectExpression, Pattern, Program, ChainExpression, SimpleCallExpression, SequenceExpression } from 'estree';
import type { Location } from 'locate-character';
/**
* `compile` converts your `.svelte` source code into a JavaScript module that exports a component
@@ -2079,11 +2082,19 @@ declare module 'svelte/server' {
...args: {} extends Props
? [
component: Comp extends SvelteComponent ? ComponentType : Comp,
- options?: { props?: Omit; context?: Map }
+ options?: {
+ props?: Omit;
+ context?: Map;
+ idPrefix?: string;
+ }
]
: [
component: Comp extends SvelteComponent ? ComponentType : Comp,
- options: { props: Omit; context?: Map }
+ options: {
+ props: Omit;
+ context?: Map;
+ idPrefix?: string;
+ }
]
): RenderOutput;
interface RenderOutput {
@@ -2995,6 +3006,15 @@ declare namespace $effect {
declare function $props(): any;
declare namespace $props {
+ /**
+ * Generates an ID that is unique to the current component instance. When hydrating a server-rendered component,
+ * the value will be consistent between server and client.
+ *
+ * This is useful for linking elements via attributes like `for` and `aria-labelledby`.
+ * @since 5.20.0
+ */
+ export function id(): string;
+
// prevent intellisense from being unhelpful
/** @deprecated */
export const apply: never;
diff --git a/playgrounds/sandbox/index.html b/playgrounds/sandbox/index.html
index 512b5426a932..845538abf073 100644
--- a/playgrounds/sandbox/index.html
+++ b/playgrounds/sandbox/index.html
@@ -12,7 +12,7 @@