diff --git a/.changeset/silly-apples-remain.md b/.changeset/silly-apples-remain.md
new file mode 100644
index 0000000000..10d43db550
--- /dev/null
+++ b/.changeset/silly-apples-remain.md
@@ -0,0 +1,5 @@
+---
+'svelte': patch
+---
+
+fix: handle more hydration mismatches
diff --git a/benchmarking/compare/index.js b/benchmarking/compare/index.js
index 9d8d279c35..8f38686a29 100644
--- a/benchmarking/compare/index.js
+++ b/benchmarking/compare/index.js
@@ -67,19 +67,29 @@ for (let i = 0; i < results[0].length; i += 1) {
for (const metric of ['time', 'gc_time']) {
const times = results.map((result) => +result[i][metric]);
let min = Infinity;
+ let max = -Infinity;
let min_index = -1;
for (let b = 0; b < times.length; b += 1) {
- if (times[b] < min) {
- min = times[b];
+ const time = times[b];
+
+ if (time < min) {
+ min = time;
min_index = b;
}
+
+ if (time > max) {
+ max = time;
+ }
}
if (min !== 0) {
- console.group(`${metric}: fastest is ${branches[min_index]}`);
+ console.group(`${metric}: fastest is ${char(min_index)} (${branches[min_index]})`);
times.forEach((time, b) => {
- console.log(`${branches[b]}: ${time.toFixed(2)}ms (${((time / min) * 100).toFixed(2)}%)`);
+ const SIZE = 20;
+ const n = Math.round(SIZE * (time / max));
+
+ console.log(`${char(b)}: ${'◼'.repeat(n)}${' '.repeat(SIZE - n)} ${time.toFixed(2)}ms`);
});
console.groupEnd();
}
@@ -87,3 +97,7 @@ for (let i = 0; i < results[0].length; i += 1) {
console.groupEnd();
}
+
+function char(i) {
+ return String.fromCharCode(97 + i);
+}
diff --git a/documentation/docs/02-runes/02-$state.md b/documentation/docs/02-runes/02-$state.md
index 16630a977b..aac006d2f5 100644
--- a/documentation/docs/02-runes/02-$state.md
+++ b/documentation/docs/02-runes/02-$state.md
@@ -20,7 +20,7 @@ Unlike other frameworks you may have encountered, there is no API for interactin
If `$state` is used with an array or a simple object, the result is a deeply reactive _state proxy_. [Proxies](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy) allow Svelte to run code when you read or write properties, including via methods like `array.push(...)`, triggering granular updates.
-> [!NOTE] Classes like `Set` and `Map` will not be proxied, but Svelte provides reactive implementations for various built-ins like these that can be imported from [`svelte/reactivity`](./svelte-reactivity).
+> [!NOTE] Class instances are not proxied. You can create [reactive state fields](#Classes) on classes that you define. Svelte provides reactive implementations of built-ins like `Set` and `Map` that can be imported from [`svelte/reactivity`](svelte-reactivity).
State is proxified recursively until Svelte finds something other than an array or simple object. In a case like this...
diff --git a/documentation/docs/02-runes/07-$inspect.md b/documentation/docs/02-runes/07-$inspect.md
index ff3d64757b..13ac8b79a3 100644
--- a/documentation/docs/02-runes/07-$inspect.md
+++ b/documentation/docs/02-runes/07-$inspect.md
@@ -52,6 +52,7 @@ This rune, added in 5.14, causes the surrounding function to be _traced_ in deve
import { doSomeWork } from './elsewhere';
$effect(() => {
+ +++// $inspect.trace must be the first statement of a function body+++
+++$inspect.trace();+++
doSomeWork();
});
diff --git a/documentation/docs/03-template-syntax/01-basic-markup.md b/documentation/docs/03-template-syntax/01-basic-markup.md
index fe5f8b02aa..feecfe033e 100644
--- a/documentation/docs/03-template-syntax/01-basic-markup.md
+++ b/documentation/docs/03-template-syntax/01-basic-markup.md
@@ -82,12 +82,14 @@ As with elements, `name={name}` can be replaced with the `{name}` shorthand.
```
+## Spread attributes
+
_Spread attributes_ allow many attributes or properties to be passed to an element or component at once.
-An element or component can have multiple spread attributes, interspersed with regular ones.
+An element or component can have multiple spread attributes, interspersed with regular ones. Order matters — if `things.a` exists it will take precedence over `a="b"`, while `c="d"` would take precedence over `things.c`:
```svelte
-
+
```
## Events
diff --git a/documentation/docs/03-template-syntax/03-each.md b/documentation/docs/03-template-syntax/03-each.md
index 70666f6a57..006cadd152 100644
--- a/documentation/docs/03-template-syntax/03-each.md
+++ b/documentation/docs/03-template-syntax/03-each.md
@@ -43,7 +43,9 @@ An each block can also specify an _index_, equivalent to the second argument in
{#each expression as name, index (key)}...{/each}
```
-If a _key_ expression is provided — which must uniquely identify each list item — Svelte will use it to diff the list when data changes, rather than adding or removing items at the end. The key can be any object, but strings and numbers are recommended since they allow identity to persist when the objects themselves change.
+If a _key_ expression is provided — which must uniquely identify each list item — Svelte will use it to intelligently update the list when data changes by inserting, moving and deleting items, rather than adding or removing items at the end and updating the state in the middle.
+
+The key can be any object, but strings and numbers are recommended since they allow identity to persist when the objects themselves change.
```svelte
{#each items as item (item.id)}
diff --git a/documentation/docs/03-template-syntax/09-@attach.md b/documentation/docs/03-template-syntax/09-@attach.md
new file mode 100644
index 0000000000..2df0882e34
--- /dev/null
+++ b/documentation/docs/03-template-syntax/09-@attach.md
@@ -0,0 +1,131 @@
+---
+title: {@attach ...}
+---
+
+Attachments are functions that run when an element is mounted to the DOM. Optionally, they can return a function that is called when the element is later removed from the DOM.
+
+> [!NOTE]
+> Attachments are available in Svelte 5.29 and newer.
+
+```svelte
+
+
+
+
...
+```
+
+An element can have any number of attachments.
+
+## Attachment factories
+
+A useful pattern is for a function, such as `tooltip` in this example, to _return_ an attachment ([demo](/playground/untitled#H4sIAAAAAAAAE3VT0XLaMBD8lavbDiaNCUlbHhTItG_5h5AH2T5ArdBppDOEMv73SkbGJGnH47F9t3un3TsfMyO3mInsh2SW1Sa7zlZKo8_E0zHjg42pGAjxBPxp7cTvUHOMldLjv-IVGUbDoUw295VTlh-WZslqa8kxsLL2ACtHWxh175NffnQfAAGikSGxYQGfPEvGfPSIWtOH0TiBVo2pWJEBJtKhQp4YYzjG9JIdcuMM5IZqHMPioY8vOSA997zQoevf4a7heO7cdp34olRiTGr07OhwH1IdoO2A7dLMbwahZq6MbRhKZWqxk7rBxTGVbuHmhCgb5qDgmIx_J6XtHHukHTrYYqx_YpzYng8aO4RYayql7hU-1ZJl0akqHBE_D9KLolwL-Dibzc7iSln9XjtqTF1UpMkJ2EmXR-BgQErsN4pxIJKr0RVO1qrxAqaTO4fbc9bKulZm3cfDY3aZDgvFGErWjmzhN7KmfX5rXyDeX8Pt1mU-hXjdBOrtuB97vK4GPUtmJ41XcRMEGDLD8do0nJ73zhUhSlyRw0t3vPqD8cjfLs-axiFgNBrkUd9Ulp50c-GLxlXAVlJX-ffpZyiSn7H0eLCUySZQcQdXlxj4El0Yv_FZvIKElqqGTruVLhzu7VRKCh22_5toOyxsWqLwwzK-cCbYNdg-hy-p9D7sbiZWUnts_wLUOF3CJgQAAA==)):
+
+```svelte
+
+
+
+
+
+
+ Hover me
+
+```
+
+Since the `tooltip(content)` expression runs inside an [effect]($effect), the attachment will be destroyed and recreated whenever `content` changes.
+
+## Inline attachments
+
+Attachments can also be created inline ([demo](/playground/untitled#H4sIAAAAAAAAE71Wf3OaWBT9KoyTTnW3MS-I3dYmnWXVtnRAazRJzbozRSQEApiRhwKO333vuY8m225m_9yZGOT9OPfcc84D943UTfxGr_G7K6Xr3TVeNW7D2M8avT_3DVk-YAoDNF4vNB8e2tnWjyXGlm7mPzfurVPpp5JgGmeZtwkf5PtFupCxLzVvHa832rl2lElX-s2Xm2DZFNqp_hs-rZetd4v07ORpT3qmQHu7MF2td0BZp8k6z_xkvfXP902_pZ2_1_aYWEiqm0kN8I4r79qbdZ6umnq3q_2iNf22F4dE6qt2oimwdpim_uY6XMm7Fuo-IQT_iTD_CeGTHwZ38ieIJUFQRxirR1Xf39Dw0X5z0I72Af4tD61vvPNwWKQnqmfPTbduhsEd2J3vO_oBd3dc6fF2X7umNdWGf0vBRhSS6qoV7cCXfTXWfKmvWG61_si_vfU92Wz-E4RhsLhNIYinsox9QKGVd8-tuACCeKXRX12P-T_eKf7fhTq0Hvt-f3ailtSeoxJHRo1-58NoPe1UiBc1hkL8Yeh45y_vQ3mcuNl9T8s3cXPRWLnS7YWJG_gn2Tb4tUjid8jua-PVl08j_ab8I14mH8Llx0s5Tz5Err4ql52r_GYg0mVy1bEGZuD0ze64b5TWYFiM-16wSuJ4JT5vfVpDcztrcG_YkRU4s6HxufzDWF4XuVeJ1P10IbzBemt3Vp1V2e04ZXfrJd7Wicyd039brRIv_RIVu_nXi7X1cfL2sy66ztToUp1TO7qJ7NlwZ0f30pld5qNSVE5o6PbMojFHjgZB7oSicPpGteyLclQap7SvY0dXtM_LR1NT2JFHey3aaxa0VxCeYJ7RMHemoiCcgPZV9pR7o7kgcOjeGliYk9hjDZx8FAq6enwlTPSZj_vYPw9Il64dXdIY8ZmapzwfEd8-1ZyaxWhqkIZOibXUd-6Upqi1pD4uMicCV1GA_7zi73UN8BaF4sC8peJtMjfmjbHZBFwq5ov50qRaE0l96NZggnW4KqypYRAW-uhSz9ADvklwJF2J-5W0Z5fQPBhDX92R6I_0IFxRgDftge4l4dP-gH1hjD7uqU6fsOEZ9UNrCdPB-nys6uXgY6O3ZMd9sy5T9PghqrWHdjo4jB51CgLiKJaDYYA-7WgYONf1FbjkI-mE3EAfUY_rijfuJ_CVPaR50oe9JF7Q0pI8Dw3osxxYHdYPGbp2CnwHF8KvwJv2wEv0Z3ilQI6U9uwbZxbYJXvEmjjQjjCHkvNLvNg3yhzXQd1olamsT4IRrZmX0MUDpwL7R8zzHj7pSh9hPHFSHjLezKqAST51uC5zmtQ87skDUaneLokT5RbXkPWSYz53Abgjc8_o4KFGUZ-Hgv2Z1l5OTYM9D-HfUD0L-EwxH5wRnIG61gS-khfgY1bq7IAP_DA4l5xRuh9xlm8yGjutc8t-wHtkhWv3hc7aqGwiK5KzgvM5xRkZYn193uEln-su55j1GaIv7oM4iPrsVHiG0Dx7TR9-1lBfqFdwfvSd5LNL5xyZVp5NoHFZ57FkfiF6vKs4k5zvIfrX5xX6MXmt0gM5MTu8DjnhukrHHzTRd3jm0dma0_f_x5cxP9f4jBdqHvmbq2fUjzqcKh2Cp-yWj9ntcHanXmBXxhu7Q--eyjhfNFpaV7zgz4nWEUb7zUOhpevjjf_gu_KZ99pxFlZ-T3sttkmYqrco_26q35v0Ewzv5EZPbnL_8BfduWGMnyyN3q0bZ_7hb_7KG_L4CQAA)):
+
+```svelte
+
+ {
+ const context = canvas.getContext('2d');
+
+ $effect(() => {
+ context.fillStyle = color;
+ context.fillRect(0, 0, canvas.width, canvas.height);
+ });
+ }}
+>
+```
+
+> [!NOTE]
+> The nested effect runs whenever `color` changes, while the outer effect (where `canvas.getContext(...)` is called) only runs once, since it doesn't read any reactive state.
+
+## Passing attachments to components
+
+When used on a component, `{@attach ...}` will create a prop whose key is a [`Symbol`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol). If the component then [spreads](/tutorial/svelte/spread-props) props onto an element, the element will receive those attachments.
+
+This allows you to create _wrapper components_ that augment elements ([demo](/playground/untitled#H4sIAAAAAAAAE3VUS3ObMBD-KxvajnFqsJM2PhA7TXrKob31FjITAbKtRkiMtDhJPfz3LiAMdpxhGJvdb1_fPnaeYjn3Iu-WIbJ04028lZDcetHDzsO3olbVApI74F1RhHbLJdayhFl-Sp5qhVwhufEWNjWiwJtYxSjyQhsEFEXxBiujcxg1_8O_dnQ9APwsEbVyiHDafjrvDZCgkiO4MLCEzxYZcn90z6XUZ6OxA61KlaIgV6i1pFC-sxjDrlbHaDiWRoGvdMbHsLzp5DES0mJnRxGaRBvcBHb7yFUTCQeunEWYcYtGv12TqgFUDbCK1WLaM6IWQhUlQiJUFm2ZLPly51xXMG0Rjoyd69C7UqqG2nu95QZyXvtvLVpri2-SN4hoLXXCZFfhQ8aQBU1VgdEaH_vSgyBZR_BpPp_vi0tY-rw2ulRZkGqpTQRbZvwa2BPgFC8bgbw31CbjJjAsE6WNYBZeGp7vtQXLMqHWnZx-5kM1TR5ycpkZXQR2wzL94l8Ur1C_3-g168SfQf1MyfRi3LW9fs77emJEw5QV9SREoLTq06tcczq7d6xEUcJX2vAhO1b843XK34e5unZEMBr15ekuKEusluWAF8lXhE2ZTP2r2RcIHJ-163FPKerCgYJLOB9i4GvNwviI5-gAQiFFBk3tBTOU3HFXEk0R8o86WvUD64aINhv5K3oRmpJXkw8uxMG6Hh6JY9X7OwGSqfUy9tDG3sHNoEi0d_d_fv9qndxRU0VClFqo3KVo3U655Hnt1PXB3Qra2Y2QGdEwgTAMCxopsoxOe6SD0gD8movDhT0LAnhqlE8gVCpLWnRoV7OJCkFAwEXitrYL1W7p7pbiE_P7XH6E_rihODm5s52XtiH9Ekaw0VgI9exadWL1uoEYjPtg2672k5szsxbKyWB2fdT0w5Y_0hcT8oXOlRetmLS8-g-6TLXXQgYAAA==)):
+
+```svelte
+
+
+
+
+
+ {@render children?.()}
+
+```
+
+```svelte
+
+
+
+
+
+
+ Hover me
+
+```
+
+## Creating attachments programmatically
+
+To add attachments to an object that will be spread onto a component or element, use [`createAttachmentKey`](svelte-attachments#createAttachmentKey).
diff --git a/documentation/docs/03-template-syntax/09-@const.md b/documentation/docs/03-template-syntax/10-@const.md
similarity index 100%
rename from documentation/docs/03-template-syntax/09-@const.md
rename to documentation/docs/03-template-syntax/10-@const.md
diff --git a/documentation/docs/03-template-syntax/10-@debug.md b/documentation/docs/03-template-syntax/11-@debug.md
similarity index 100%
rename from documentation/docs/03-template-syntax/10-@debug.md
rename to documentation/docs/03-template-syntax/11-@debug.md
diff --git a/documentation/docs/03-template-syntax/11-bind.md b/documentation/docs/03-template-syntax/12-bind.md
similarity index 90%
rename from documentation/docs/03-template-syntax/11-bind.md
rename to documentation/docs/03-template-syntax/12-bind.md
index c23f3b5232..0970ee384e 100644
--- a/documentation/docs/03-template-syntax/11-bind.md
+++ b/documentation/docs/03-template-syntax/12-bind.md
@@ -117,6 +117,29 @@ Since 5.6.0, if an ` ` has a `defaultChecked` attribute and is part of a f
```
+## ` `
+
+Checkboxes can be in an [indeterminate](https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/indeterminate) state, independently of whether they are checked or unchecked:
+
+```svelte
+
+
+
+```
+
## ` `
Inputs that work together can use `bind:group`.
@@ -227,6 +250,7 @@ You can give the `` a default value by adding a `selected` attribute to
- [`seeking`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/seeking_event)
- [`ended`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/ended)
- [`readyState`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/readyState)
+- [`played`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/played)
```svelte
@@ -254,6 +278,10 @@ You can give the `` a default value by adding a `selected` attribute to
```
+## `window` and `document`
+
+To bind to properties of `window` and `document`, see [``](svelte-window) and [``](svelte-document).
+
## Contenteditable bindings
Elements with the `contenteditable` attribute support the following bindings:
@@ -278,6 +306,10 @@ All visible elements have the following readonly bindings, measured with a `Resi
- [`clientHeight`](https://developer.mozilla.org/en-US/docs/Web/API/Element/clientHeight)
- [`offsetWidth`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/offsetWidth)
- [`offsetHeight`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/offsetHeight)
+- [`contentRect`](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserverEntry/contentRect)
+- [`contentBoxSize`](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserverEntry/contentBoxSize)
+- [`borderBoxSize`](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserverEntry/borderBoxSize)
+- [`devicePixelContentBoxSize`](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserverEntry/devicePixelContentBoxSize)
```svelte
@@ -285,7 +317,7 @@ All visible elements have the following readonly bindings, measured with a `Resi
```
-> [!NOTE] `display: inline` elements do not have a width or height (except for elements with 'intrinsic' dimensions, like ` ` and ``), and cannot be observed with a `ResizeObserver`. You will need to change the `display` style of these elements to something else, such as `inline-block`.
+> [!NOTE] `display: inline` elements do not have a width or height (except for elements with 'intrinsic' dimensions, like ` ` and ``), and cannot be observed with a `ResizeObserver`. You will need to change the `display` style of these elements to something else, such as `inline-block`. Note that CSS transformations do not trigger `ResizeObserver` callbacks.
## bind:this
diff --git a/documentation/docs/03-template-syntax/12-use.md b/documentation/docs/03-template-syntax/13-use.md
similarity index 93%
rename from documentation/docs/03-template-syntax/12-use.md
rename to documentation/docs/03-template-syntax/13-use.md
index 45de023578..5f5321a1c0 100644
--- a/documentation/docs/03-template-syntax/12-use.md
+++ b/documentation/docs/03-template-syntax/13-use.md
@@ -2,6 +2,9 @@
title: use:
---
+> [!NOTE]
+> In Svelte 5.29 and newer, consider using [attachments](@attach) instead, as they are more flexible and composable.
+
Actions are functions that are called when an element is mounted. They are added with the `use:` directive, and will typically use an `$effect` so that they can reset any state when the element is unmounted:
```svelte
diff --git a/documentation/docs/03-template-syntax/13-transition.md b/documentation/docs/03-template-syntax/14-transition.md
similarity index 100%
rename from documentation/docs/03-template-syntax/13-transition.md
rename to documentation/docs/03-template-syntax/14-transition.md
diff --git a/documentation/docs/03-template-syntax/14-in-and-out.md b/documentation/docs/03-template-syntax/15-in-and-out.md
similarity index 100%
rename from documentation/docs/03-template-syntax/14-in-and-out.md
rename to documentation/docs/03-template-syntax/15-in-and-out.md
diff --git a/documentation/docs/03-template-syntax/15-animate.md b/documentation/docs/03-template-syntax/16-animate.md
similarity index 100%
rename from documentation/docs/03-template-syntax/15-animate.md
rename to documentation/docs/03-template-syntax/16-animate.md
diff --git a/documentation/docs/07-misc/02-testing.md b/documentation/docs/07-misc/02-testing.md
index 0842019039..64bf49d77a 100644
--- a/documentation/docs/07-misc/02-testing.md
+++ b/documentation/docs/07-misc/02-testing.md
@@ -6,9 +6,9 @@ Testing helps you write and maintain your code and guard against regressions. Te
## Unit and integration testing using Vitest
-Unit tests allow you to test small isolated parts of your code. Integration tests allow you to test parts of your application to see if they work together. If you're using Vite (including via SvelteKit), we recommend using [Vitest](https://vitest.dev/).
+Unit tests allow you to test small isolated parts of your code. Integration tests allow you to test parts of your application to see if they work together. If you're using Vite (including via SvelteKit), we recommend using [Vitest](https://vitest.dev/). You can use the Svelte CLI to [setup Vitest](/docs/cli/vitest) either during project creation or later on.
-To get started, install Vitest:
+To setup Vitest manually, first install it:
```bash
npm install -D vitest
@@ -254,9 +254,9 @@ When writing component tests that involve two-way bindings, context or snippet p
E2E (short for 'end to end') tests allow you to test your full application through the eyes of the user. This section uses [Playwright](https://playwright.dev/) as an example, but you can also use other solutions like [Cypress](https://www.cypress.io/) or [NightwatchJS](https://nightwatchjs.org/).
-To get started with Playwright, either install it via [the VS Code extension](https://playwright.dev/docs/getting-started-vscode), or install it from the command line using `npm init playwright`. It is also part of the setup CLI when you run `npx sv create`.
+You can use the Svelte CLI to [setup Playwright](/docs/cli/playwright) either during project creation or later on. You can also [set it up with `npm init playwright`](https://playwright.dev/docs/intro). Additionally, you may also want to install an IDE plugin such as [the VS Code extension](https://playwright.dev/docs/getting-started-vscode) to be able to execute tests from inside your IDE.
-After you've done that, you should have a `tests` folder and a Playwright config. You may need to adjust that config to tell Playwright what to do before running the tests - mainly starting your application at a certain port:
+If you've run `npm init playwright` or are not using Vite, you may need to adjust the Playwright config to tell Playwright what to do before running the tests - mainly starting your application at a certain port. For example:
```js
/// file: playwright.config.js
diff --git a/documentation/docs/07-misc/99-faq.md b/documentation/docs/07-misc/99-faq.md
index ed5c6277c0..cf98cdd3c3 100644
--- a/documentation/docs/07-misc/99-faq.md
+++ b/documentation/docs/07-misc/99-faq.md
@@ -81,9 +81,10 @@ _End-to-End Tests_: To ensure your users are able to interact with your applicat
Some resources for getting started with testing:
+- [Svelte docs on testing](/docs/svelte/testing)
+- [Setup Vitest using the Svelte CLI](/docs/cli/vitest)
- [Svelte Testing Library](https://testing-library.com/docs/svelte-testing-library/example/)
- [Svelte Component Testing in Cypress](https://docs.cypress.io/guides/component-testing/svelte/overview)
-- [Example using vitest](https://github.com/vitest-dev/vitest/tree/main/examples/sveltekit)
- [Example using uvu test runner with JSDOM](https://github.com/lukeed/uvu/tree/master/examples/svelte)
- [Test Svelte components using Vitest & Playwright](https://davipon.hashnode.dev/test-svelte-component-using-vitest-playwright)
- [Component testing with WebdriverIO](https://webdriver.io/docs/component-testing/svelte)
diff --git a/documentation/docs/98-reference/.generated/compile-warnings.md b/documentation/docs/98-reference/.generated/compile-warnings.md
index 0e94cbadb2..7069f90206 100644
--- a/documentation/docs/98-reference/.generated/compile-warnings.md
+++ b/documentation/docs/98-reference/.generated/compile-warnings.md
@@ -586,6 +586,14 @@ Attributes should not contain ':' characters to prevent ambiguity with Svelte di
Quoted attributes on components and custom elements will be stringified in a future version of Svelte. If this isn't what you want, remove the quotes
```
+### bidirectional_control_characters
+
+```
+A bidirectional control character was detected in your code. These characters can be used to alter the visual direction of your code and could have unintended consequences
+```
+
+Bidirectional control characters can alter the direction in which text appears to be in. For example, via control characters, you can make `defabc` look like `abcdef`. As a result, if you were to unknowingly copy and paste some code that has these control characters, they may alter the behavior of your code in ways you did not intend. See [trojansource.codes](https://trojansource.codes/) for more information.
+
### bind_invalid_each_rest
```
diff --git a/documentation/docs/98-reference/21-svelte-attachments.md b/documentation/docs/98-reference/21-svelte-attachments.md
new file mode 100644
index 0000000000..e446bdddc2
--- /dev/null
+++ b/documentation/docs/98-reference/21-svelte-attachments.md
@@ -0,0 +1,5 @@
+---
+title: svelte/attachments
+---
+
+> MODULE: svelte/attachments
diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md
index 72cd00bc6a..2b65a1889f 100644
--- a/packages/svelte/CHANGELOG.md
+++ b/packages/svelte/CHANGELOG.md
@@ -1,5 +1,69 @@
# svelte
+## 5.30.1
+
+### Patch Changes
+
+- fix: add `typeParams` to `SnippetBlock` for legacy parser ([#15921](https://github.com/sveltejs/svelte/pull/15921))
+
+## 5.30.0
+
+### Minor Changes
+
+- feat: allow generics on snippets ([#15915](https://github.com/sveltejs/svelte/pull/15915))
+
+## 5.29.0
+
+### Minor Changes
+
+- feat: attachments ([#15000](https://github.com/sveltejs/svelte/pull/15000))
+
+## 5.28.7
+
+### Patch Changes
+
+- fix: remove unncessary guards that require CSP privilege when removing event attributes ([#15846](https://github.com/sveltejs/svelte/pull/15846))
+
+- fix: rewrite destructuring logic to handle iterators ([#15813](https://github.com/sveltejs/svelte/pull/15813))
+
+## 5.28.6
+
+### Patch Changes
+
+- fix: use `transform.read` for `ownership_validator.mutation` array ([#15848](https://github.com/sveltejs/svelte/pull/15848))
+
+- fix: don't redeclare `$slots` ([#15849](https://github.com/sveltejs/svelte/pull/15849))
+
+## 5.28.5
+
+### Patch Changes
+
+- fix: proxify the value in assignment shorthands to the private field ([#15862](https://github.com/sveltejs/svelte/pull/15862))
+
+- fix: more frequently update `bind:buffered` to actual value ([#15874](https://github.com/sveltejs/svelte/pull/15874))
+
+## 5.28.4
+
+### Patch Changes
+
+- fix: treat nullish expression as empty string ([#15901](https://github.com/sveltejs/svelte/pull/15901))
+
+- fix: prevent invalid BigInt calls from blowing up at compile time ([#15900](https://github.com/sveltejs/svelte/pull/15900))
+
+- fix: warn on bidirectional control characters ([#15893](https://github.com/sveltejs/svelte/pull/15893))
+
+- fix: emit right error for a shadowed invalid rune ([#15892](https://github.com/sveltejs/svelte/pull/15892))
+
+## 5.28.3
+
+### Patch Changes
+
+- chore: avoid microtasks when flushing sync ([#15895](https://github.com/sveltejs/svelte/pull/15895))
+
+- fix: improve error message for migration errors when slot would be renamed ([#15841](https://github.com/sveltejs/svelte/pull/15841))
+
+- fix: allow characters in the supplementary special-purpose plane ([#15823](https://github.com/sveltejs/svelte/pull/15823))
+
## 5.28.2
### Patch Changes
diff --git a/packages/svelte/elements.d.ts b/packages/svelte/elements.d.ts
index 99d87b4c09..c637137365 100644
--- a/packages/svelte/elements.d.ts
+++ b/packages/svelte/elements.d.ts
@@ -31,6 +31,8 @@
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
// TypeScript Version: 2.8
+import type { Attachment } from 'svelte/attachments';
+
// Note: We also allow `null` as a valid value because Svelte treats this the same as `undefined`
type Booleanish = boolean | 'true' | 'false';
@@ -860,6 +862,9 @@ export interface HTMLAttributes extends AriaAttributes, D
// allow any data- attribute
[key: `data-${string}`]: any;
+
+ // allow any attachment
+ [key: symbol]: Attachment;
}
export type HTMLAttributeAnchorTarget = '_self' | '_blank' | '_parent' | '_top' | (string & {});
diff --git a/packages/svelte/messages/compile-warnings/misc.md b/packages/svelte/messages/compile-warnings/misc.md
index 3b977db1be..29343dd28a 100644
--- a/packages/svelte/messages/compile-warnings/misc.md
+++ b/packages/svelte/messages/compile-warnings/misc.md
@@ -1,3 +1,9 @@
+## bidirectional_control_characters
+
+> A bidirectional control character was detected in your code. These characters can be used to alter the visual direction of your code and could have unintended consequences
+
+Bidirectional control characters can alter the direction in which text appears to be in. For example, via control characters, you can make `defabc` look like `abcdef`. As a result, if you were to unknowingly copy and paste some code that has these control characters, they may alter the behavior of your code in ways you did not intend. See [trojansource.codes](https://trojansource.codes/) for more information.
+
## legacy_code
> `%code%` is no longer valid — please use `%suggestion%` instead
diff --git a/packages/svelte/package.json b/packages/svelte/package.json
index ff71429d2f..b6e8d06c63 100644
--- a/packages/svelte/package.json
+++ b/packages/svelte/package.json
@@ -2,7 +2,7 @@
"name": "svelte",
"description": "Cybernetically enhanced web apps",
"license": "MIT",
- "version": "5.28.2",
+ "version": "5.30.1",
"type": "module",
"types": "./types/index.d.ts",
"engines": {
@@ -34,6 +34,10 @@
"types": "./types/index.d.ts",
"default": "./src/animate/index.js"
},
+ "./attachments": {
+ "types": "./types/index.d.ts",
+ "default": "./src/attachments/index.js"
+ },
"./compiler": {
"types": "./types/index.d.ts",
"require": "./compiler/index.js",
diff --git a/packages/svelte/scripts/generate-types.js b/packages/svelte/scripts/generate-types.js
index 377fca4343..c558a2bbf7 100644
--- a/packages/svelte/scripts/generate-types.js
+++ b/packages/svelte/scripts/generate-types.js
@@ -35,6 +35,7 @@ await createBundle({
[pkg.name]: `${dir}/src/index.d.ts`,
[`${pkg.name}/action`]: `${dir}/src/action/public.d.ts`,
[`${pkg.name}/animate`]: `${dir}/src/animate/public.d.ts`,
+ [`${pkg.name}/attachments`]: `${dir}/src/attachments/public.d.ts`,
[`${pkg.name}/compiler`]: `${dir}/src/compiler/public.d.ts`,
[`${pkg.name}/easing`]: `${dir}/src/easing/index.js`,
[`${pkg.name}/legacy`]: `${dir}/src/legacy/legacy-client.js`,
diff --git a/packages/svelte/src/attachments/index.js b/packages/svelte/src/attachments/index.js
new file mode 100644
index 0000000000..948a19f4dd
--- /dev/null
+++ b/packages/svelte/src/attachments/index.js
@@ -0,0 +1,27 @@
+import { ATTACHMENT_KEY } from '../constants.js';
+
+/**
+ * Creates an object key that will be recognised as an attachment when the object is spread onto an element,
+ * as a programmatic alternative to using `{@attach ...}`. This can be useful for library authors, though
+ * is generally not needed when building an app.
+ *
+ * ```svelte
+ *
+ *
+ * click me
+ * ```
+ * @since 5.29
+ */
+export function createAttachmentKey() {
+ return Symbol(ATTACHMENT_KEY);
+}
diff --git a/packages/svelte/src/attachments/public.d.ts b/packages/svelte/src/attachments/public.d.ts
new file mode 100644
index 0000000000..caf1342d0a
--- /dev/null
+++ b/packages/svelte/src/attachments/public.d.ts
@@ -0,0 +1,12 @@
+/**
+ * An [attachment](https://svelte.dev/docs/svelte/@attach) is a function that runs when an element is mounted
+ * to the DOM, and optionally returns a function that is called when the element is later removed.
+ *
+ * It can be attached to an element with an `{@attach ...}` tag, or by spreading an object containing
+ * a property created with [`createAttachmentKey`](https://svelte.dev/docs/svelte/svelte-attachments#createAttachmentKey).
+ */
+export interface Attachment {
+ (element: T): void | (() => void);
+}
+
+export * from './index.js';
diff --git a/packages/svelte/src/compiler/legacy.js b/packages/svelte/src/compiler/legacy.js
index e3f88c8f1d..f6b7e4b054 100644
--- a/packages/svelte/src/compiler/legacy.js
+++ b/packages/svelte/src/compiler/legacy.js
@@ -378,7 +378,8 @@ export function convert(source, ast) {
end: node.end,
expression: node.expression,
parameters: node.parameters,
- children: node.body.nodes.map((child) => visit(child))
+ children: node.body.nodes.map((child) => visit(child)),
+ typeParams: node.typeParams
};
},
// @ts-expect-error
diff --git a/packages/svelte/src/compiler/migrate/index.js b/packages/svelte/src/compiler/migrate/index.js
index 75a9a64905..2d5a4dcd9e 100644
--- a/packages/svelte/src/compiler/migrate/index.js
+++ b/packages/svelte/src/compiler/migrate/index.js
@@ -1307,7 +1307,7 @@ const template = {
name = state.scope.generate(slot_name);
if (name !== slot_name) {
throw new MigrationError(
- 'This migration would change the name of a slot making the component unusable'
+ `This migration would change the name of a slot (${slot_name} to ${name}) making the component unusable`
);
}
}
@@ -1880,7 +1880,7 @@ function handle_identifier(node, state, path) {
let new_name = state.scope.generate(name);
if (new_name !== name) {
throw new MigrationError(
- 'This migration would change the name of a slot making the component unusable'
+ `This migration would change the name of a slot (${name} to ${new_name}) making the component unusable`
);
}
}
diff --git a/packages/svelte/src/compiler/phases/1-parse/read/context.js b/packages/svelte/src/compiler/phases/1-parse/read/context.js
index f4c73dcf40..b118901830 100644
--- a/packages/svelte/src/compiler/phases/1-parse/read/context.js
+++ b/packages/svelte/src/compiler/phases/1-parse/read/context.js
@@ -1,7 +1,7 @@
/** @import { Location } from 'locate-character' */
/** @import { Pattern } from 'estree' */
/** @import { Parser } from '../index.js' */
-import { is_bracket_open, is_bracket_close, get_bracket_close } from '../utils/bracket.js';
+import { match_bracket } from '../utils/bracket.js';
import { parse_expression_at } from '../acorn.js';
import { regex_not_newline_characters } from '../../patterns.js';
import * as e from '../../../errors.js';
@@ -33,7 +33,9 @@ export default function read_pattern(parser) {
};
}
- if (!is_bracket_open(parser.template[i])) {
+ const char = parser.template[i];
+
+ if (char !== '{' && char !== '[') {
e.expected_pattern(i);
}
@@ -71,75 +73,6 @@ export default function read_pattern(parser) {
}
}
-/**
- * @param {Parser} parser
- * @param {number} start
- */
-function match_bracket(parser, start) {
- const bracket_stack = [];
-
- let i = start;
-
- while (i < parser.template.length) {
- let char = parser.template[i++];
-
- if (char === "'" || char === '"' || char === '`') {
- i = match_quote(parser, i, char);
- continue;
- }
-
- if (is_bracket_open(char)) {
- bracket_stack.push(char);
- } else if (is_bracket_close(char)) {
- const popped = /** @type {string} */ (bracket_stack.pop());
- const expected = /** @type {string} */ (get_bracket_close(popped));
-
- if (char !== expected) {
- e.expected_token(i - 1, expected);
- }
-
- if (bracket_stack.length === 0) {
- return i;
- }
- }
- }
-
- e.unexpected_eof(parser.template.length);
-}
-
-/**
- * @param {Parser} parser
- * @param {number} start
- * @param {string} quote
- */
-function match_quote(parser, start, quote) {
- let is_escaped = false;
- let i = start;
-
- while (i < parser.template.length) {
- const char = parser.template[i++];
-
- if (is_escaped) {
- is_escaped = false;
- continue;
- }
-
- if (char === quote) {
- return i;
- }
-
- if (char === '\\') {
- is_escaped = true;
- }
-
- if (quote === '`' && char === '$' && parser.template[i] === '{') {
- i = match_bracket(parser, i);
- }
- }
-
- e.unterminated_string_constant(start);
-}
-
/**
* @param {Parser} parser
* @returns {any}
diff --git a/packages/svelte/src/compiler/phases/1-parse/read/script.js b/packages/svelte/src/compiler/phases/1-parse/read/script.js
index 9d9ed3a1ef..6290127811 100644
--- a/packages/svelte/src/compiler/phases/1-parse/read/script.js
+++ b/packages/svelte/src/compiler/phases/1-parse/read/script.js
@@ -16,7 +16,7 @@ const ALLOWED_ATTRIBUTES = ['context', 'generics', 'lang', 'module'];
/**
* @param {Parser} parser
* @param {number} start
- * @param {Array} attributes
+ * @param {Array} attributes
* @returns {AST.Script}
*/
export function read_script(parser, start, attributes) {
diff --git a/packages/svelte/src/compiler/phases/1-parse/read/style.js b/packages/svelte/src/compiler/phases/1-parse/read/style.js
index 56dbe124b7..e15a47e6d5 100644
--- a/packages/svelte/src/compiler/phases/1-parse/read/style.js
+++ b/packages/svelte/src/compiler/phases/1-parse/read/style.js
@@ -18,7 +18,7 @@ const REGEX_HTML_COMMENT_CLOSE = /-->/;
/**
* @param {Parser} parser
* @param {number} start
- * @param {Array} attributes
+ * @param {Array} attributes
* @returns {AST.CSS.StyleSheet}
*/
export default function read_style(parser, start, attributes) {
diff --git a/packages/svelte/src/compiler/phases/1-parse/state/element.js b/packages/svelte/src/compiler/phases/1-parse/state/element.js
index 66946a8f8d..d20369b0d9 100644
--- a/packages/svelte/src/compiler/phases/1-parse/state/element.js
+++ b/packages/svelte/src/compiler/phases/1-parse/state/element.js
@@ -480,7 +480,7 @@ function read_static_attribute(parser) {
/**
* @param {Parser} parser
- * @returns {AST.Attribute | AST.SpreadAttribute | AST.Directive | null}
+ * @returns {AST.Attribute | AST.SpreadAttribute | AST.Directive | AST.AttachTag | null}
*/
function read_attribute(parser) {
const start = parser.index;
@@ -488,6 +488,27 @@ function read_attribute(parser) {
if (parser.eat('{')) {
parser.allow_whitespace();
+ if (parser.eat('@attach')) {
+ parser.require_whitespace();
+
+ const expression = read_expression(parser);
+ parser.allow_whitespace();
+ parser.eat('}', true);
+
+ /** @type {AST.AttachTag} */
+ const attachment = {
+ type: 'AttachTag',
+ start,
+ end: parser.index,
+ expression,
+ metadata: {
+ expression: create_expression_metadata()
+ }
+ };
+
+ return attachment;
+ }
+
if (parser.eat('...')) {
const expression = read_expression(parser);
diff --git a/packages/svelte/src/compiler/phases/1-parse/state/tag.js b/packages/svelte/src/compiler/phases/1-parse/state/tag.js
index 0eb98c27e8..4153463c83 100644
--- a/packages/svelte/src/compiler/phases/1-parse/state/tag.js
+++ b/packages/svelte/src/compiler/phases/1-parse/state/tag.js
@@ -8,9 +8,12 @@ import { parse_expression_at } from '../acorn.js';
import read_pattern from '../read/context.js';
import read_expression, { get_loose_identifier } from '../read/expression.js';
import { create_fragment } from '../utils/create.js';
+import { match_bracket } from '../utils/bracket.js';
const regex_whitespace_with_closing_curly_brace = /^\s*}/;
+const pointy_bois = { '<': '>' };
+
/** @param {Parser} parser */
export default function tag(parser) {
const start = parser.index;
@@ -351,6 +354,22 @@ function open(parser) {
const params_start = parser.index;
+ // snippets could have a generic signature, e.g. `#snippet foo(...)`
+ /** @type {string | undefined} */
+ let type_params;
+
+ // if we match a generic opening
+ if (parser.ts && parser.match('<')) {
+ const start = parser.index;
+ const end = match_bracket(parser, start, pointy_bois);
+
+ type_params = parser.template.slice(start + 1, end - 1);
+
+ parser.index = end;
+ }
+
+ parser.allow_whitespace();
+
const matched = parser.eat('(', true, false);
if (matched) {
@@ -388,6 +407,7 @@ function open(parser) {
end: name_end,
name
},
+ typeParams: type_params,
parameters: function_expression.params,
body: create_fragment(),
metadata: {
diff --git a/packages/svelte/src/compiler/phases/1-parse/utils/bracket.js b/packages/svelte/src/compiler/phases/1-parse/utils/bracket.js
index b7c8cb43cd..8c69a58c99 100644
--- a/packages/svelte/src/compiler/phases/1-parse/utils/bracket.js
+++ b/packages/svelte/src/compiler/phases/1-parse/utils/bracket.js
@@ -1,34 +1,5 @@
-const SQUARE_BRACKET_OPEN = '[';
-const SQUARE_BRACKET_CLOSE = ']';
-const CURLY_BRACKET_OPEN = '{';
-const CURLY_BRACKET_CLOSE = '}';
-const PARENTHESES_OPEN = '(';
-const PARENTHESES_CLOSE = ')';
-
-/** @param {string} char */
-export function is_bracket_open(char) {
- return char === SQUARE_BRACKET_OPEN || char === CURLY_BRACKET_OPEN;
-}
-
-/** @param {string} char */
-export function is_bracket_close(char) {
- return char === SQUARE_BRACKET_CLOSE || char === CURLY_BRACKET_CLOSE;
-}
-
-/** @param {string} open */
-export function get_bracket_close(open) {
- if (open === SQUARE_BRACKET_OPEN) {
- return SQUARE_BRACKET_CLOSE;
- }
-
- if (open === CURLY_BRACKET_OPEN) {
- return CURLY_BRACKET_CLOSE;
- }
-
- if (open === PARENTHESES_OPEN) {
- return PARENTHESES_CLOSE;
- }
-}
+/** @import { Parser } from '../index.js' */
+import * as e from '../../../errors.js';
/**
* @param {number} num
@@ -121,7 +92,7 @@ function count_leading_backslashes(string, search_start_index) {
* @returns {number | undefined} The index of the closing bracket, or undefined if not found.
*/
export function find_matching_bracket(template, index, open) {
- const close = get_bracket_close(open);
+ const close = default_brackets[open];
let brackets = 1;
let i = index;
while (brackets > 0 && i < template.length) {
@@ -162,3 +133,81 @@ export function find_matching_bracket(template, index, open) {
}
return undefined;
}
+
+/** @type {Record} */
+const default_brackets = {
+ '{': '}',
+ '(': ')',
+ '[': ']'
+};
+
+/**
+ * @param {Parser} parser
+ * @param {number} start
+ * @param {Record} brackets
+ */
+export function match_bracket(parser, start, brackets = default_brackets) {
+ const close = Object.values(brackets);
+ const bracket_stack = [];
+
+ let i = start;
+
+ while (i < parser.template.length) {
+ let char = parser.template[i++];
+
+ if (char === "'" || char === '"' || char === '`') {
+ i = match_quote(parser, i, char);
+ continue;
+ }
+
+ if (char in brackets) {
+ bracket_stack.push(char);
+ } else if (close.includes(char)) {
+ const popped = /** @type {string} */ (bracket_stack.pop());
+ const expected = /** @type {string} */ (brackets[popped]);
+
+ if (char !== expected) {
+ e.expected_token(i - 1, expected);
+ }
+
+ if (bracket_stack.length === 0) {
+ return i;
+ }
+ }
+ }
+
+ e.unexpected_eof(parser.template.length);
+}
+
+/**
+ * @param {Parser} parser
+ * @param {number} start
+ * @param {string} quote
+ */
+function match_quote(parser, start, quote) {
+ let is_escaped = false;
+ let i = start;
+
+ while (i < parser.template.length) {
+ const char = parser.template[i++];
+
+ if (is_escaped) {
+ is_escaped = false;
+ continue;
+ }
+
+ if (char === quote) {
+ return i;
+ }
+
+ if (char === '\\') {
+ is_escaped = true;
+ }
+
+ if (quote === '`' && char === '$' && parser.template[i] === '{') {
+ i = match_bracket(parser, i);
+ }
+ }
+
+ e.unterminated_string_constant(start);
+}
diff --git a/packages/svelte/src/compiler/phases/1-parse/utils/html.js b/packages/svelte/src/compiler/phases/1-parse/utils/html.js
index a68acb996f..a0c2a5b06f 100644
--- a/packages/svelte/src/compiler/phases/1-parse/utils/html.js
+++ b/packages/svelte/src/compiler/phases/1-parse/utils/html.js
@@ -72,6 +72,8 @@ const NUL = 0;
// to replace them ourselves
//
// Source: http://en.wikipedia.org/wiki/Character_encodings_in_HTML#Illegal_characters
+// Also see: https://en.wikipedia.org/wiki/Plane_(Unicode)
+// Also see: https://html.spec.whatwg.org/multipage/parsing.html#preprocessing-the-input-stream
/** @param {number} code */
function validate_code(code) {
@@ -116,5 +118,10 @@ function validate_code(code) {
return code;
}
+ // supplementary special-purpose plane 0xe0000 - 0xe07f and 0xe0100 - 0xe01ef
+ if ((code >= 917504 && code <= 917631) || (code >= 917760 && code <= 917999)) {
+ return code;
+ }
+
return NUL;
}
diff --git a/packages/svelte/src/compiler/phases/2-analyze/index.js b/packages/svelte/src/compiler/phases/2-analyze/index.js
index ac9ab7689b..af3945d435 100644
--- a/packages/svelte/src/compiler/phases/2-analyze/index.js
+++ b/packages/svelte/src/compiler/phases/2-analyze/index.js
@@ -18,6 +18,7 @@ import { extract_svelte_ignore } from '../../utils/extract_svelte_ignore.js';
import { ignore_map, ignore_stack, pop_ignore, push_ignore } from '../../state.js';
import { ArrowFunctionExpression } from './visitors/ArrowFunctionExpression.js';
import { AssignmentExpression } from './visitors/AssignmentExpression.js';
+import { AttachTag } from './visitors/AttachTag.js';
import { Attribute } from './visitors/Attribute.js';
import { AwaitBlock } from './visitors/AwaitBlock.js';
import { BindDirective } from './visitors/BindDirective.js';
@@ -42,6 +43,7 @@ import { ImportDeclaration } from './visitors/ImportDeclaration.js';
import { KeyBlock } from './visitors/KeyBlock.js';
import { LabeledStatement } from './visitors/LabeledStatement.js';
import { LetDirective } from './visitors/LetDirective.js';
+import { Literal } from './visitors/Literal.js';
import { MemberExpression } from './visitors/MemberExpression.js';
import { NewExpression } from './visitors/NewExpression.js';
import { OnDirective } from './visitors/OnDirective.js';
@@ -63,6 +65,7 @@ import { SvelteSelf } from './visitors/SvelteSelf.js';
import { SvelteWindow } from './visitors/SvelteWindow.js';
import { SvelteBoundary } from './visitors/SvelteBoundary.js';
import { TaggedTemplateExpression } from './visitors/TaggedTemplateExpression.js';
+import { TemplateElement } from './visitors/TemplateElement.js';
import { Text } from './visitors/Text.js';
import { TitleElement } from './visitors/TitleElement.js';
import { TransitionDirective } from './visitors/TransitionDirective.js';
@@ -131,6 +134,7 @@ const visitors = {
},
ArrowFunctionExpression,
AssignmentExpression,
+ AttachTag,
Attribute,
AwaitBlock,
BindDirective,
@@ -155,6 +159,7 @@ const visitors = {
KeyBlock,
LabeledStatement,
LetDirective,
+ Literal,
MemberExpression,
NewExpression,
OnDirective,
@@ -176,6 +181,7 @@ const visitors = {
SvelteWindow,
SvelteBoundary,
TaggedTemplateExpression,
+ TemplateElement,
Text,
TransitionDirective,
TitleElement,
diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/AttachTag.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/AttachTag.js
new file mode 100644
index 0000000000..1e318f228d
--- /dev/null
+++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/AttachTag.js
@@ -0,0 +1,13 @@
+/** @import { AST } from '#compiler' */
+/** @import { Context } from '../types' */
+
+import { mark_subtree_dynamic } from './shared/fragment.js';
+
+/**
+ * @param {AST.AttachTag} node
+ * @param {Context} context
+ */
+export function AttachTag(node, context) {
+ mark_subtree_dynamic(context.path);
+ context.next({ ...context.state, expression: node.metadata.expression });
+}
diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/Identifier.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/Identifier.js
index efbbe6cfa2..89a81127c1 100644
--- a/packages/svelte/src/compiler/phases/2-analyze/visitors/Identifier.js
+++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/Identifier.js
@@ -39,7 +39,7 @@ export function Identifier(node, context) {
if (
is_rune(node.name) &&
context.state.scope.get(node.name) === null &&
- context.state.scope.get(node.name.slice(1)) === null
+ context.state.scope.get(node.name.slice(1))?.kind !== 'store_sub'
) {
/** @type {Expression} */
let current = node;
diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/Literal.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/Literal.js
new file mode 100644
index 0000000000..58684ba71c
--- /dev/null
+++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/Literal.js
@@ -0,0 +1,14 @@
+/** @import { Literal } from 'estree' */
+import * as w from '../../../warnings.js';
+import { regex_bidirectional_control_characters } from '../../patterns.js';
+
+/**
+ * @param {Literal} node
+ */
+export function Literal(node) {
+ if (typeof node.value === 'string') {
+ if (regex_bidirectional_control_characters.test(node.value)) {
+ w.bidirectional_control_characters(node);
+ }
+ }
+}
diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/TemplateElement.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/TemplateElement.js
new file mode 100644
index 0000000000..978042bbc5
--- /dev/null
+++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/TemplateElement.js
@@ -0,0 +1,12 @@
+/** @import { TemplateElement } from 'estree' */
+import * as w from '../../../warnings.js';
+import { regex_bidirectional_control_characters } from '../../patterns.js';
+
+/**
+ * @param {TemplateElement} node
+ */
+export function TemplateElement(node) {
+ if (regex_bidirectional_control_characters.test(node.value.cooked ?? '')) {
+ w.bidirectional_control_characters(node);
+ }
+}
diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/Text.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/Text.js
index 363a111b7d..a03421e8dd 100644
--- a/packages/svelte/src/compiler/phases/2-analyze/visitors/Text.js
+++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/Text.js
@@ -1,20 +1,52 @@
/** @import { AST } from '#compiler' */
/** @import { Context } from '../types' */
import { is_tag_valid_with_parent } from '../../../../html-tree-validation.js';
-import { regex_not_whitespace } from '../../patterns.js';
+import { regex_bidirectional_control_characters, regex_not_whitespace } from '../../patterns.js';
import * as e from '../../../errors.js';
+import * as w from '../../../warnings.js';
+import { extract_svelte_ignore } from '../../../utils/extract_svelte_ignore.js';
/**
* @param {AST.Text} node
* @param {Context} context
*/
export function Text(node, context) {
- const in_template = context.path.at(-1)?.type === 'Fragment';
+ const parent = /** @type {AST.SvelteNode} */ (context.path.at(-1));
- if (in_template && context.state.parent_element && regex_not_whitespace.test(node.data)) {
+ if (
+ parent.type === 'Fragment' &&
+ context.state.parent_element &&
+ regex_not_whitespace.test(node.data)
+ ) {
const message = is_tag_valid_with_parent('#text', context.state.parent_element);
if (message) {
e.node_invalid_placement(node, message);
}
}
+
+ regex_bidirectional_control_characters.lastIndex = 0;
+ for (const match of node.data.matchAll(regex_bidirectional_control_characters)) {
+ let is_ignored = false;
+
+ // if we have a svelte-ignore comment earlier in the text, bail
+ // (otherwise we can only use svelte-ignore on parent elements/blocks)
+ if (parent.type === 'Fragment') {
+ for (const child of parent.nodes) {
+ if (child === node) break;
+
+ if (child.type === 'Comment') {
+ is_ignored ||= extract_svelte_ignore(
+ child.start + 4,
+ child.data,
+ context.state.analysis.runes
+ ).includes('bidirectional_control_characters');
+ }
+ }
+ }
+
+ if (!is_ignored) {
+ let start = match.index + node.start;
+ w.bidirectional_control_characters({ start, end: start + match[0].length });
+ }
+ }
}
diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/component.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/component.js
index 04bf3d2ff3..aca87fab81 100644
--- a/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/component.js
+++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/component.js
@@ -1,3 +1,4 @@
+/** @import { Expression } from 'estree' */
/** @import { AST } from '#compiler' */
/** @import { AnalysisState, Context } from '../../types' */
import * as e from '../../../../errors.js';
@@ -74,7 +75,8 @@ export function visit_component(node, context) {
attribute.type !== 'SpreadAttribute' &&
attribute.type !== 'LetDirective' &&
attribute.type !== 'OnDirective' &&
- attribute.type !== 'BindDirective'
+ attribute.type !== 'BindDirective' &&
+ attribute.type !== 'AttachTag'
) {
e.component_invalid_directive(attribute);
}
@@ -91,15 +93,10 @@ export function visit_component(node, context) {
validate_attribute(attribute, node);
if (is_expression_attribute(attribute)) {
- const expression = get_attribute_expression(attribute);
- if (expression.type === 'SequenceExpression') {
- let i = /** @type {number} */ (expression.start);
- while (--i > 0) {
- const char = context.state.analysis.source[i];
- if (char === '(') break; // parenthesized sequence expressions are ok
- if (char === '{') e.attribute_invalid_sequence_expression(expression);
- }
- }
+ disallow_unparenthesized_sequences(
+ get_attribute_expression(attribute),
+ context.state.analysis.source
+ );
}
}
@@ -113,6 +110,10 @@ export function visit_component(node, context) {
if (attribute.type === 'BindDirective' && attribute.name !== 'this') {
context.state.analysis.uses_component_bindings = true;
}
+
+ if (attribute.type === 'AttachTag') {
+ disallow_unparenthesized_sequences(attribute.expression, context.state.analysis.source);
+ }
}
// If the component has a slot attribute — ` ` —
@@ -158,3 +159,18 @@ export function visit_component(node, context) {
context.visit({ ...node.fragment, nodes: nodes[slot_name] }, state);
}
}
+
+/**
+ * @param {Expression} expression
+ * @param {string} source
+ */
+function disallow_unparenthesized_sequences(expression, source) {
+ if (expression.type === 'SequenceExpression') {
+ let i = /** @type {number} */ (expression.start);
+ while (--i > 0) {
+ const char = source[i];
+ if (char === '(') break; // parenthesized sequence expressions are ok
+ if (char === '{') e.attribute_invalid_sequence_expression(expression);
+ }
+ }
+}
diff --git a/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js b/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js
index ff448f31a1..0282a71f6d 100644
--- a/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js
+++ b/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js
@@ -56,6 +56,7 @@ import { TitleElement } from './visitors/TitleElement.js';
import { TransitionDirective } from './visitors/TransitionDirective.js';
import { UpdateExpression } from './visitors/UpdateExpression.js';
import { UseDirective } from './visitors/UseDirective.js';
+import { AttachTag } from './visitors/AttachTag.js';
import { VariableDeclaration } from './visitors/VariableDeclaration.js';
/** @type {Visitors} */
@@ -131,6 +132,7 @@ const visitors = {
TransitionDirective,
UpdateExpression,
UseDirective,
+ AttachTag,
VariableDeclaration
};
diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/AttachTag.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/AttachTag.js
new file mode 100644
index 0000000000..062604cacc
--- /dev/null
+++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/AttachTag.js
@@ -0,0 +1,21 @@
+/** @import { Expression } from 'estree' */
+/** @import { AST } from '#compiler' */
+/** @import { ComponentContext } from '../types' */
+import * as b from '../../../../utils/builders.js';
+
+/**
+ * @param {AST.AttachTag} node
+ * @param {ComponentContext} context
+ */
+export function AttachTag(node, context) {
+ context.state.init.push(
+ b.stmt(
+ b.call(
+ '$.attach',
+ context.state.node,
+ b.thunk(/** @type {Expression} */ (context.visit(node.expression)))
+ )
+ )
+ );
+ context.next();
+}
diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js
index 7468fcbbc7..ab981878ad 100644
--- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js
+++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js
@@ -83,7 +83,7 @@ export function RegularElement(node, context) {
/** @type {AST.StyleDirective[]} */
const style_directives = [];
- /** @type {Array} */
+ /** @type {Array} */
const other_directives = [];
/** @type {ExpressionStatement[]} */
@@ -153,6 +153,10 @@ export function RegularElement(node, context) {
has_use = true;
other_directives.push(attribute);
break;
+
+ case 'AttachTag':
+ other_directives.push(attribute);
+ break;
}
}
diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/VariableDeclaration.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/VariableDeclaration.js
index 84044e4ded..84c205d163 100644
--- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/VariableDeclaration.js
+++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/VariableDeclaration.js
@@ -2,7 +2,7 @@
/** @import { Binding } from '#compiler' */
/** @import { ComponentClientTransformState, ComponentContext } from '../types' */
import { dev } from '../../../../state.js';
-import { extract_paths } from '../../../../utils/ast.js';
+import { build_pattern, extract_paths } from '../../../../utils/ast.js';
import * as b from '#compiler/builders';
import * as assert from '../../../../utils/assert.js';
import { get_rune } from '../../../scope.js';
@@ -141,20 +141,20 @@ export function VariableDeclaration(node, context) {
b.declarator(declarator.id, create_state_declarator(declarator.id, value))
);
} else {
- const tmp = context.state.scope.generate('tmp');
- const paths = extract_paths(declarator.id);
+ const [pattern, replacements] = build_pattern(declarator.id, context.state.scope);
declarations.push(
- b.declarator(b.id(tmp), value),
- ...paths.map((path) => {
- const value = path.expression?.(b.id(tmp));
- const binding = context.state.scope.get(/** @type {Identifier} */ (path.node).name);
- return b.declarator(
- path.node,
- binding?.kind === 'state' || binding?.kind === 'raw_state'
- ? create_state_declarator(binding.node, value)
- : value
- );
- })
+ b.declarator(pattern, value),
+ .../** @type {[Identifier, Identifier][]} */ ([...replacements]).map(
+ ([original, replacement]) => {
+ const binding = context.state.scope.get(original.name);
+ return b.declarator(
+ original,
+ binding?.kind === 'state' || binding?.kind === 'raw_state'
+ ? create_state_declarator(binding.node, replacement)
+ : replacement
+ );
+ }
+ )
);
}
@@ -170,8 +170,7 @@ export function VariableDeclaration(node, context) {
)
);
} else {
- const bindings = extract_paths(declarator.id);
-
+ const [pattern, replacements] = build_pattern(declarator.id, context.state.scope);
const init = /** @type {CallExpression} */ (declarator.init);
/** @type {Identifier} */
@@ -189,10 +188,16 @@ export function VariableDeclaration(node, context) {
);
}
- for (let i = 0; i < bindings.length; i++) {
- const binding = bindings[i];
+ for (let i = 0; i < replacements.size; i++) {
+ const [original, replacement] = [...replacements][i];
declarations.push(
- b.declarator(binding.node, b.call('$.derived', b.thunk(binding.expression(rhs))))
+ b.declarator(
+ original,
+ b.call(
+ '$.derived',
+ b.arrow([], b.block([b.let(pattern, rhs), b.return(replacement)]))
+ )
+ )
);
}
}
@@ -304,19 +309,19 @@ function create_state_declarators(declarator, { scope, analysis }, value) {
];
}
- const tmp = scope.generate('tmp');
- const paths = extract_paths(declarator.id);
+ const [pattern, replacements] = build_pattern(declarator.id, scope);
return [
- b.declarator(b.id(tmp), value),
- ...paths.map((path) => {
- const value = path.expression?.(b.id(tmp));
- const binding = scope.get(/** @type {Identifier} */ (path.node).name);
- return b.declarator(
- path.node,
- binding?.kind === 'state'
- ? b.call('$.mutable_source', value, analysis.immutable ? b.true : undefined)
- : value
- );
- })
+ b.declarator(pattern, value),
+ .../** @type {[Identifier, Identifier][]} */ ([...replacements]).map(
+ ([original, replacement]) => {
+ const binding = scope.get(original.name);
+ return b.declarator(
+ original,
+ binding?.kind === 'state'
+ ? b.call('$.mutable_source', replacement, analysis.immutable ? b.true : undefined)
+ : replacement
+ );
+ }
+ )
];
}
diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/component.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/component.js
index c4071c67fe..6d3d8a68e6 100644
--- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/component.js
+++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/component.js
@@ -257,6 +257,14 @@ export function build_component(node, component_name, context, anchor = context.
);
}
}
+ } else if (attribute.type === 'AttachTag') {
+ let expression = /** @type {Expression} */ (context.visit(attribute.expression));
+
+ if (attribute.metadata.expression.has_state) {
+ expression = b.arrow([b.id('$$node')], b.call(expression, b.id('$$node')));
+ }
+
+ push_prop(b.prop('get', b.call('$.attachment'), expression, true));
}
}
diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js
index bc79b76043..80b472ac37 100644
--- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js
+++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js
@@ -77,7 +77,7 @@ export function build_template_chunk(
// If we have a single expression, then pass that in directly to possibly avoid doing
// extra work in the template_effect (instead we do the work in set_text).
if (evaluated.is_known) {
- value = b.literal(evaluated.value);
+ value = b.literal((evaluated.value ?? '') + '');
}
return { value, has_state };
@@ -96,7 +96,7 @@ export function build_template_chunk(
}
if (evaluated.is_known) {
- quasi.value.cooked += evaluated.value + '';
+ quasi.value.cooked += (evaluated.value ?? '') + '';
} else {
if (!evaluated.is_defined) {
// add `?? ''` where necessary
@@ -324,15 +324,18 @@ export function validate_mutation(node, context, expression) {
const state = /** @type {ComponentClientTransformState} */ (context.state);
state.analysis.needs_mutation_validation = true;
- /** @type {Array} */
+ /** @type {Array} */
const path = [];
while (left.type === 'MemberExpression') {
if (left.property.type === 'Literal') {
path.unshift(left.property);
} else if (left.property.type === 'Identifier') {
+ const transform = Object.hasOwn(context.state.transform, left.property.name)
+ ? context.state.transform[left.property.name]
+ : null;
if (left.computed) {
- path.unshift(left.property);
+ path.unshift(transform?.read ? transform.read(left.property) : left.property);
} else {
path.unshift(b.literal(left.property.name));
}
diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/VariableDeclaration.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/VariableDeclaration.js
index 1f2bd3e2b1..17bf516a22 100644
--- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/VariableDeclaration.js
+++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/VariableDeclaration.js
@@ -1,8 +1,9 @@
/** @import { VariableDeclaration, VariableDeclarator, Expression, CallExpression, Pattern, Identifier } from 'estree' */
/** @import { Binding } from '#compiler' */
/** @import { Context } from '../types.js' */
+/** @import { ComponentAnalysis } from '../../../types.js' */
/** @import { Scope } from '../../../scope.js' */
-import { build_fallback, extract_paths } from '../../../../utils/ast.js';
+import { build_pattern, build_fallback, extract_paths } from '../../../../utils/ast.js';
import * as b from '#compiler/builders';
import { get_rune } from '../../../scope.js';
import { walk } from 'zimmerframe';
@@ -50,20 +51,26 @@ export function VariableDeclaration(node, context) {
}
}
});
+
+ // if `$$slots` is declared separately, deconflict
+ const slots_name = /** @type {ComponentAnalysis} */ (context.state.analysis).uses_slots
+ ? b.id('$$slots_')
+ : b.id('$$slots');
+
if (id.type === 'ObjectPattern' && has_rest) {
// If a rest pattern is used within an object pattern, we need to ensure we don't expose $$slots or $$events
id.properties.splice(
id.properties.length - 1,
0,
// @ts-ignore
- b.prop('init', b.id('$$slots'), b.id('$$slots')),
+ b.prop('init', b.id('$$slots'), slots_name),
b.prop('init', b.id('$$events'), b.id('$$events'))
);
} else if (id.type === 'Identifier') {
// If $props is referenced as an identifier, we need to ensure we don't expose $$slots or $$events as properties
// on the identifier reference
id = b.object_pattern([
- b.prop('init', b.id('$$slots'), b.id('$$slots')),
+ b.prop('init', b.id('$$slots'), slots_name),
b.prop('init', b.id('$$events'), b.id('$$events')),
b.rest(b.id(id.name))
]);
@@ -181,13 +188,10 @@ function create_state_declarators(declarator, scope, value) {
return [b.declarator(declarator.id, value)];
}
- const tmp = scope.generate('tmp');
- const paths = extract_paths(declarator.id);
+ const [pattern, replacements] = build_pattern(declarator.id, scope);
return [
- b.declarator(b.id(tmp), value), // TODO inject declarator for opts, so we can use it below
- ...paths.map((path) => {
- const value = path.expression?.(b.id(tmp));
- return b.declarator(path.node, value);
- })
+ b.declarator(pattern, value),
+ // TODO inject declarator for opts, so we can use it below
+ ...[...replacements].map(([original, replacement]) => b.declarator(original, replacement))
];
}
diff --git a/packages/svelte/src/compiler/phases/3-transform/shared/assignments.js b/packages/svelte/src/compiler/phases/3-transform/shared/assignments.js
index 3e6bb0c4c6..85b0869a15 100644
--- a/packages/svelte/src/compiler/phases/3-transform/shared/assignments.js
+++ b/packages/svelte/src/compiler/phases/3-transform/shared/assignments.js
@@ -1,7 +1,7 @@
-/** @import { AssignmentExpression, AssignmentOperator, Expression, Node, Pattern } from 'estree' */
+/** @import { AssignmentExpression, AssignmentOperator, Expression, Identifier, Node, Pattern } from 'estree' */
/** @import { Context as ClientContext } from '../client/types.js' */
/** @import { Context as ServerContext } from '../server/types.js' */
-import { extract_paths, is_expression_async } from '../../../utils/ast.js';
+import { build_pattern, is_expression_async } from '../../../utils/ast.js';
import * as b from '#compiler/builders';
/**
@@ -23,21 +23,23 @@ export function visit_assignment_expression(node, context, build_assignment) {
let changed = false;
- const assignments = extract_paths(node.left).map((path) => {
- const value = path.expression?.(rhs);
+ const [pattern, replacements] = build_pattern(node.left, context.state.scope);
- let assignment = build_assignment('=', path.node, value, context);
- if (assignment !== null) changed = true;
-
- return (
- assignment ??
- b.assignment(
- '=',
- /** @type {Pattern} */ (context.visit(path.node)),
- /** @type {Expression} */ (context.visit(value))
- )
- );
- });
+ const assignments = [
+ b.let(pattern, rhs),
+ ...[...replacements].map(([original, replacement]) => {
+ let assignment = build_assignment(node.operator, original, replacement, context);
+ if (assignment !== null) changed = true;
+ return b.stmt(
+ assignment ??
+ b.assignment(
+ node.operator,
+ /** @type {Identifier} */ (context.visit(original)),
+ /** @type {Expression} */ (context.visit(replacement))
+ )
+ );
+ })
+ ];
if (!changed) {
// No change to output -> nothing to transform -> we can keep the original assignment
@@ -45,25 +47,36 @@ export function visit_assignment_expression(node, context, build_assignment) {
}
const is_standalone = /** @type {Node} */ (context.path.at(-1)).type.endsWith('Statement');
- const sequence = b.sequence(assignments);
+ const block = b.block(assignments);
if (!is_standalone) {
// this is part of an expression, we need the sequence to end with the value
- sequence.expressions.push(rhs);
+ block.body.push(b.return(rhs));
}
- if (should_cache) {
- // the right hand side is a complex expression, wrap in an IIFE to cache it
- const iife = b.arrow([rhs], sequence);
+ if (is_standalone && !should_cache) {
+ return block;
+ }
- const iife_is_async =
- is_expression_async(value) ||
- assignments.some((assignment) => is_expression_async(assignment));
+ const iife = b.arrow(should_cache ? [rhs] : [], block);
- return iife_is_async ? b.await(b.call(b.async(iife), value)) : b.call(iife, value);
- }
+ const iife_is_async =
+ is_expression_async(value) ||
+ assignments.some(
+ (assignment) =>
+ (assignment.type === 'ExpressionStatement' &&
+ is_expression_async(assignment.expression)) ||
+ (assignment.type === 'VariableDeclaration' &&
+ assignment.declarations.some(
+ (declaration) =>
+ is_expression_async(declaration.id) ||
+ (declaration.init && is_expression_async(declaration.init))
+ ))
+ );
- return sequence;
+ return iife_is_async
+ ? b.await(b.call(b.async(iife), should_cache ? value : undefined))
+ : b.call(iife, should_cache ? value : undefined);
}
if (node.left.type !== 'Identifier' && node.left.type !== 'MemberExpression') {
diff --git a/packages/svelte/src/compiler/phases/patterns.js b/packages/svelte/src/compiler/phases/patterns.js
index bda299de9e..2bee717131 100644
--- a/packages/svelte/src/compiler/phases/patterns.js
+++ b/packages/svelte/src/compiler/phases/patterns.js
@@ -21,3 +21,5 @@ export const regex_invalid_identifier_chars = /(^[^a-zA-Z_$]|[^a-zA-Z0-9_$])/g;
export const regex_starts_with_vowel = /^[aeiou]/;
export const regex_heading_tags = /^h[1-6]$/;
export const regex_illegal_attribute_character = /(^[0-9-.])|[\^$@%?!|()[\]{}^*+~;]/;
+export const regex_bidirectional_control_characters =
+ /[\u202a\u202b\u202c\u202d\u202e\u2066\u2067\u2068\u2069]+/g;
diff --git a/packages/svelte/src/compiler/phases/scope.js b/packages/svelte/src/compiler/phases/scope.js
index 8297f174d3..f37dfab8d1 100644
--- a/packages/svelte/src/compiler/phases/scope.js
+++ b/packages/svelte/src/compiler/phases/scope.js
@@ -23,12 +23,12 @@ export const STRING = Symbol('string');
/** @type {Record} */
const globals = {
- BigInt: [NUMBER, BigInt],
+ BigInt: [NUMBER],
'Math.min': [NUMBER, Math.min],
'Math.max': [NUMBER, Math.max],
'Math.random': [NUMBER],
'Math.floor': [NUMBER, Math.floor],
- // @ts-expect-error
+ // @ts-ignore
'Math.f16round': [NUMBER, Math.f16round],
'Math.round': [NUMBER, Math.round],
'Math.abs': [NUMBER, Math.abs],
@@ -630,9 +630,10 @@ export class Scope {
/**
* @param {string} preferred_name
+ * @param {(name: string, counter: number) => string} [generator]
* @returns {string}
*/
- generate(preferred_name) {
+ generate(preferred_name, generator = (name, counter) => `${name}_${counter}`) {
if (this.#porous) {
return /** @type {Scope} */ (this.parent).generate(preferred_name);
}
@@ -647,7 +648,7 @@ export class Scope {
this.root.conflicts.has(name) ||
is_reserved(name)
) {
- name = `${preferred_name}_${n++}`;
+ name = generator(preferred_name, n++);
}
this.references.set(name, []);
diff --git a/packages/svelte/src/compiler/types/template.d.ts b/packages/svelte/src/compiler/types/template.d.ts
index a544cd1dec..6dec1f2dbe 100644
--- a/packages/svelte/src/compiler/types/template.d.ts
+++ b/packages/svelte/src/compiler/types/template.d.ts
@@ -174,6 +174,16 @@ export namespace AST {
};
}
+ /** A `{@attach foo(...)} tag */
+ export interface AttachTag extends BaseNode {
+ type: 'AttachTag';
+ expression: Expression;
+ /** @internal */
+ metadata: {
+ expression: ExpressionMetadata;
+ };
+ }
+
/** An `animate:` directive */
export interface AnimateDirective extends BaseNode {
type: 'AnimateDirective';
@@ -273,7 +283,7 @@ export namespace AST {
interface BaseElement extends BaseNode {
name: string;
- attributes: Array;
+ attributes: Array;
fragment: Fragment;
}
@@ -458,6 +468,7 @@ export namespace AST {
type: 'SnippetBlock';
expression: Identifier;
parameters: Pattern[];
+ typeParams?: string;
body: Fragment;
/** @internal */
metadata: {
@@ -546,6 +557,7 @@ export namespace AST {
| AST.Attribute
| AST.SpreadAttribute
| Directive
+ | AST.AttachTag
| AST.Comment
| Block;
diff --git a/packages/svelte/src/compiler/utils/ast.js b/packages/svelte/src/compiler/utils/ast.js
index 108f4eff64..32ff5a37b3 100644
--- a/packages/svelte/src/compiler/utils/ast.js
+++ b/packages/svelte/src/compiler/utils/ast.js
@@ -1,7 +1,8 @@
-/** @import { AST } from '#compiler' */
+/** @import { AST, Scope } from '#compiler' */
/** @import * as ESTree from 'estree' */
import { walk } from 'zimmerframe';
import * as b from '#compiler/builders';
+import is_reference from 'is-reference';
/**
* Gets the left-most identifier of a member expression or identifier.
@@ -128,6 +129,49 @@ export function unwrap_pattern(pattern, nodes = []) {
return nodes;
}
+/**
+ * @param {ESTree.Pattern} id
+ * @param {Scope} scope
+ * @returns {[ESTree.Pattern, Map]}
+ */
+export function build_pattern(id, scope) {
+ /** @type {Map} */
+ const map = new Map();
+
+ /** @type {Map} */
+ const names = new Map();
+
+ let counter = 0;
+
+ for (const node of unwrap_pattern(id)) {
+ const name = scope.generate(`$$${++counter}`, (_, counter) => `$$${counter}`);
+
+ map.set(node, b.id(name));
+
+ if (node.type === 'Identifier') {
+ names.set(node.name, name);
+ }
+ }
+
+ const pattern = walk(id, null, {
+ Identifier(node, context) {
+ if (is_reference(node, /** @type {ESTree.Pattern} */ (context.path.at(-1)))) {
+ const name = names.get(node.name);
+ if (name) return b.id(name);
+ }
+ },
+
+ MemberExpression(node, context) {
+ const n = map.get(node);
+ if (n) return n;
+
+ context.next();
+ }
+ });
+
+ return [pattern, map];
+}
+
/**
* Extracts all identifiers from a pattern.
* @param {ESTree.Pattern} pattern
@@ -580,5 +624,7 @@ export function build_assignment_value(operator, left, right) {
return operator === '='
? right
: // turn something like x += 1 into x = x + 1
- b.binary(/** @type {ESTree.BinaryOperator} */ (operator.slice(0, -1)), left, right);
+ ['||=', '&&=', '??='].includes(operator)
+ ? b.logical(/** @type {ESTree.LogicalOperator} */ (operator.slice(0, -1)), left, right)
+ : b.binary(/** @type {ESTree.BinaryOperator} */ (operator.slice(0, -1)), left, right);
}
diff --git a/packages/svelte/src/compiler/warnings.js b/packages/svelte/src/compiler/warnings.js
index e6fc8caba5..c281433213 100644
--- a/packages/svelte/src/compiler/warnings.js
+++ b/packages/svelte/src/compiler/warnings.js
@@ -86,6 +86,7 @@ export const codes = [
'a11y_role_supports_aria_props_implicit',
'a11y_unknown_aria_attribute',
'a11y_unknown_role',
+ 'bidirectional_control_characters',
'legacy_code',
'unknown_code',
'options_deprecated_accessors',
@@ -506,6 +507,14 @@ export function a11y_unknown_role(node, role, suggestion) {
w(node, 'a11y_unknown_role', `${suggestion ? `Unknown role '${role}'. Did you mean '${suggestion}'?` : `Unknown role '${role}'`}\nhttps://svelte.dev/e/a11y_unknown_role`);
}
+/**
+ * A bidirectional control character was detected in your code. These characters can be used to alter the visual direction of your code and could have unintended consequences
+ * @param {null | NodeLike} node
+ */
+export function bidirectional_control_characters(node) {
+ w(node, 'bidirectional_control_characters', `A bidirectional control character was detected in your code. These characters can be used to alter the visual direction of your code and could have unintended consequences\nhttps://svelte.dev/e/bidirectional_control_characters`);
+}
+
/**
* `%code%` is no longer valid — please use `%suggestion%` instead
* @param {null | NodeLike} node
diff --git a/packages/svelte/src/constants.js b/packages/svelte/src/constants.js
index 8861e440fc..2ecd4afee2 100644
--- a/packages/svelte/src/constants.js
+++ b/packages/svelte/src/constants.js
@@ -55,3 +55,5 @@ export const IGNORABLE_RUNTIME_WARNINGS = /** @type {const} */ ([
* TODO this is currently unused
*/
export const ELEMENTS_WITHOUT_TEXT = ['audio', 'datalist', 'dl', 'optgroup', 'select', 'video'];
+
+export const ATTACHMENT_KEY = '@attach';
diff --git a/packages/svelte/src/internal/client/dom/blocks/each.js b/packages/svelte/src/internal/client/dom/blocks/each.js
index 92c953b541..2997664fa2 100644
--- a/packages/svelte/src/internal/client/dom/blocks/each.js
+++ b/packages/svelte/src/internal/client/dom/blocks/each.js
@@ -12,6 +12,7 @@ import {
hydrate_next,
hydrate_node,
hydrating,
+ read_hydration_instruction,
remove_nodes,
set_hydrate_node,
set_hydrating
@@ -160,7 +161,7 @@ export function each(node, flags, get_collection, get_key, render_fn, fallback_f
let mismatch = false;
if (hydrating) {
- var is_else = /** @type {Comment} */ (anchor).data === HYDRATION_START_ELSE;
+ var is_else = read_hydration_instruction(anchor) === HYDRATION_START_ELSE;
if (is_else !== (length === 0)) {
// hydration mismatch — remove the server-rendered DOM and start over
diff --git a/packages/svelte/src/internal/client/dom/blocks/if.js b/packages/svelte/src/internal/client/dom/blocks/if.js
index 925abb9d9d..bf1098c3f4 100644
--- a/packages/svelte/src/internal/client/dom/blocks/if.js
+++ b/packages/svelte/src/internal/client/dom/blocks/if.js
@@ -4,6 +4,7 @@ import {
hydrate_next,
hydrate_node,
hydrating,
+ read_hydration_instruction,
remove_nodes,
set_hydrate_node,
set_hydrating
@@ -56,7 +57,8 @@ export function if_block(node, fn, [root_index, hydrate_index] = [0, 0]) {
if (hydrating && hydrate_index !== -1) {
if (root_index === 0) {
- const data = /** @type {Comment} */ (anchor).data;
+ const data = read_hydration_instruction(anchor);
+
if (data === HYDRATION_START) {
hydrate_index = 0;
} else if (data === HYDRATION_START_ELSE) {
diff --git a/packages/svelte/src/internal/client/dom/elements/attachments.js b/packages/svelte/src/internal/client/dom/elements/attachments.js
new file mode 100644
index 0000000000..6e3089a384
--- /dev/null
+++ b/packages/svelte/src/internal/client/dom/elements/attachments.js
@@ -0,0 +1,15 @@
+import { effect } from '../../reactivity/effects.js';
+
+/**
+ * @param {Element} node
+ * @param {() => (node: Element) => void} get_fn
+ */
+export function attach(node, get_fn) {
+ effect(() => {
+ const fn = get_fn();
+
+ // we use `&&` rather than `?.` so that things like
+ // `{@attach DEV && something_dev_only()}` work
+ return fn && fn(node);
+ });
+}
diff --git a/packages/svelte/src/internal/client/dom/elements/attributes.js b/packages/svelte/src/internal/client/dom/elements/attributes.js
index f63f55cc6e..3d1acbd31c 100644
--- a/packages/svelte/src/internal/client/dom/elements/attributes.js
+++ b/packages/svelte/src/internal/client/dom/elements/attributes.js
@@ -13,10 +13,11 @@ import {
set_active_effect,
set_active_reaction
} from '../../runtime.js';
+import { attach } from './attachments.js';
import { clsx } from '../../../shared/attributes.js';
import { set_class } from './class.js';
import { set_style } from './style.js';
-import { NAMESPACE_HTML } from '../../../../constants.js';
+import { ATTACHMENT_KEY, NAMESPACE_HTML } from '../../../../constants.js';
export const CLASS = Symbol('class');
export const STYLE = Symbol('style');
@@ -446,6 +447,12 @@ export function set_attributes(element, prev, next, css_hash, skip_warning = fal
set_hydrating(true);
}
+ for (let symbol of Object.getOwnPropertySymbols(next)) {
+ if (symbol.description === ATTACHMENT_KEY) {
+ attach(element, () => next[symbol]);
+ }
+ }
+
return current;
}
diff --git a/packages/svelte/src/internal/client/dom/elements/bindings/media.js b/packages/svelte/src/internal/client/dom/elements/bindings/media.js
index 30a8dac1af..f96ee05989 100644
--- a/packages/svelte/src/internal/client/dom/elements/bindings/media.js
+++ b/packages/svelte/src/internal/client/dom/elements/bindings/media.js
@@ -62,7 +62,23 @@ export function bind_current_time(media, get, set = get) {
* @param {(array: Array<{ start: number; end: number }>) => void} set
*/
export function bind_buffered(media, set) {
- listen(media, ['loadedmetadata', 'progress'], () => set(time_ranges_to_array(media.buffered)));
+ /** @type {{ start: number; end: number; }[]} */
+ var current;
+
+ // `buffered` can update without emitting any event, so we check it on various events.
+ // By specs, `buffered` always returns a new object, so we have to compare deeply.
+ listen(media, ['loadedmetadata', 'progress', 'timeupdate', 'seeking'], () => {
+ var ranges = media.buffered;
+
+ if (
+ !current ||
+ current.length !== ranges.length ||
+ current.some((range, i) => ranges.start(i) !== range.start || ranges.end(i) !== range.end)
+ ) {
+ current = time_ranges_to_array(ranges);
+ set(current);
+ }
+ });
}
/**
diff --git a/packages/svelte/src/internal/client/dom/elements/events.js b/packages/svelte/src/internal/client/dom/elements/events.js
index 3374fe713f..c2b7fc7d83 100644
--- a/packages/svelte/src/internal/client/dom/elements/events.js
+++ b/packages/svelte/src/internal/client/dom/elements/events.js
@@ -26,12 +26,8 @@ export const root_event_handles = new Set();
export function replay_events(dom) {
if (!hydrating) return;
- if (dom.onload) {
- dom.removeAttribute('onload');
- }
- if (dom.onerror) {
- dom.removeAttribute('onerror');
- }
+ dom.removeAttribute('onload');
+ dom.removeAttribute('onerror');
// @ts-expect-error
const event = dom.__e;
if (event !== undefined) {
diff --git a/packages/svelte/src/internal/client/dom/hydration.js b/packages/svelte/src/internal/client/dom/hydration.js
index 8523ff97d5..ab3256da82 100644
--- a/packages/svelte/src/internal/client/dom/hydration.js
+++ b/packages/svelte/src/internal/client/dom/hydration.js
@@ -103,3 +103,16 @@ export function remove_nodes() {
node = next;
}
}
+
+/**
+ *
+ * @param {TemplateNode} node
+ */
+export function read_hydration_instruction(node) {
+ if (!node || node.nodeType !== 8) {
+ w.hydration_mismatch();
+ throw HYDRATION_ERROR;
+ }
+
+ return /** @type {Comment} */ (node).data;
+}
diff --git a/packages/svelte/src/internal/client/index.js b/packages/svelte/src/internal/client/index.js
index 14d6e29f5b..b5f746b276 100644
--- a/packages/svelte/src/internal/client/index.js
+++ b/packages/svelte/src/internal/client/index.js
@@ -1,3 +1,4 @@
+export { createAttachmentKey as attachment } from '../../attachments/index.js';
export { FILENAME, HMR, NAMESPACE_SVG } from '../../constants.js';
export { push, pop } from './context.js';
export { assign, assign_and, assign_or, assign_nullish } from './dev/assign.js';
@@ -22,6 +23,7 @@ export { element } from './dom/blocks/svelte-element.js';
export { head } from './dom/blocks/svelte-head.js';
export { append_styles } from './dom/css.js';
export { action } from './dom/elements/actions.js';
+export { attach } from './dom/elements/attachments.js';
export {
remove_input_defaults,
set_attribute,
diff --git a/packages/svelte/src/internal/client/reactivity/props.js b/packages/svelte/src/internal/client/reactivity/props.js
index 8bfd8f9e25..77c58720e1 100644
--- a/packages/svelte/src/internal/client/reactivity/props.js
+++ b/packages/svelte/src/internal/client/reactivity/props.js
@@ -218,9 +218,15 @@ const spread_props_handler = {
for (let p of target.props) {
if (is_function(p)) p = p();
+ if (!p) continue;
+
for (const key in p) {
if (!keys.includes(key)) keys.push(key);
}
+
+ for (const key of Object.getOwnPropertySymbols(p)) {
+ if (!keys.includes(key)) keys.push(key);
+ }
}
return keys;
diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js
index 7a926bf624..d790c0ad14 100644
--- a/packages/svelte/src/internal/client/runtime.js
+++ b/packages/svelte/src/internal/client/runtime.js
@@ -823,18 +823,21 @@ export function flushSync(fn) {
if (fn) {
is_flushing = true;
flush_queued_root_effects();
+
+ is_flushing = true;
result = fn();
}
- flush_tasks();
+ while (true) {
+ flush_tasks();
+
+ if (queued_root_effects.length === 0) {
+ return /** @type {T} */ (result);
+ }
- while (queued_root_effects.length > 0) {
is_flushing = true;
flush_queued_root_effects();
- flush_tasks();
}
-
- return /** @type {T} */ (result);
}
/**
diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js
index a3a9979d65..b849318036 100644
--- a/packages/svelte/src/version.js
+++ b/packages/svelte/src/version.js
@@ -4,5 +4,5 @@
* The current version, as set in package.json.
* @type {string}
*/
-export const VERSION = '5.28.2';
+export const VERSION = '5.30.1';
export const PUBLIC_VERSION = '5';
diff --git a/packages/svelte/tests/compiler-errors/samples/invalid-rune-name-shadowed/_config.js b/packages/svelte/tests/compiler-errors/samples/invalid-rune-name-shadowed/_config.js
new file mode 100644
index 0000000000..a24996677c
--- /dev/null
+++ b/packages/svelte/tests/compiler-errors/samples/invalid-rune-name-shadowed/_config.js
@@ -0,0 +1,8 @@
+import { test } from '../../test';
+
+export default test({
+ error: {
+ code: 'rune_invalid_name',
+ message: '`$state.foo` is not a valid rune'
+ }
+});
diff --git a/packages/svelte/tests/compiler-errors/samples/invalid-rune-name-shadowed/main.svelte.js b/packages/svelte/tests/compiler-errors/samples/invalid-rune-name-shadowed/main.svelte.js
new file mode 100644
index 0000000000..966f7bc63b
--- /dev/null
+++ b/packages/svelte/tests/compiler-errors/samples/invalid-rune-name-shadowed/main.svelte.js
@@ -0,0 +1,5 @@
+class State {
+ value = $state.foo();
+}
+
+export const state = new State();
diff --git a/packages/svelte/tests/hydration/samples/cloudflare-mirage-borking-2/_config.js b/packages/svelte/tests/hydration/samples/cloudflare-mirage-borking-2/_config.js
new file mode 100644
index 0000000000..56ba73b064
--- /dev/null
+++ b/packages/svelte/tests/hydration/samples/cloudflare-mirage-borking-2/_config.js
@@ -0,0 +1,6 @@
+import { test } from '../../test';
+
+// https://github.com/sveltejs/svelte/issues/15819
+export default test({
+ expect_hydration_error: true
+});
diff --git a/packages/svelte/tests/hydration/samples/cloudflare-mirage-borking-2/_expected.html b/packages/svelte/tests/hydration/samples/cloudflare-mirage-borking-2/_expected.html
new file mode 100644
index 0000000000..5179fb04a5
--- /dev/null
+++ b/packages/svelte/tests/hydration/samples/cloudflare-mirage-borking-2/_expected.html
@@ -0,0 +1 @@
+start
cond
diff --git a/packages/svelte/tests/hydration/samples/cloudflare-mirage-borking-2/_override.html b/packages/svelte/tests/hydration/samples/cloudflare-mirage-borking-2/_override.html
new file mode 100644
index 0000000000..2a1c323288
--- /dev/null
+++ b/packages/svelte/tests/hydration/samples/cloudflare-mirage-borking-2/_override.html
@@ -0,0 +1 @@
+ start
cond
diff --git a/packages/svelte/tests/hydration/samples/cloudflare-mirage-borking-2/main.svelte b/packages/svelte/tests/hydration/samples/cloudflare-mirage-borking-2/main.svelte
new file mode 100644
index 0000000000..bfb4f2cdb8
--- /dev/null
+++ b/packages/svelte/tests/hydration/samples/cloudflare-mirage-borking-2/main.svelte
@@ -0,0 +1,5 @@
+
+
+start
{#if cond}cond
{/if}
diff --git a/packages/svelte/tests/hydration/samples/cloudflare-mirage-borking/_config.js b/packages/svelte/tests/hydration/samples/cloudflare-mirage-borking/_config.js
new file mode 100644
index 0000000000..56ba73b064
--- /dev/null
+++ b/packages/svelte/tests/hydration/samples/cloudflare-mirage-borking/_config.js
@@ -0,0 +1,6 @@
+import { test } from '../../test';
+
+// https://github.com/sveltejs/svelte/issues/15819
+export default test({
+ expect_hydration_error: true
+});
diff --git a/packages/svelte/tests/hydration/samples/cloudflare-mirage-borking/_expected.html b/packages/svelte/tests/hydration/samples/cloudflare-mirage-borking/_expected.html
new file mode 100644
index 0000000000..f6c03b87c1
--- /dev/null
+++ b/packages/svelte/tests/hydration/samples/cloudflare-mirage-borking/_expected.html
@@ -0,0 +1 @@
+start
pre123 mid
diff --git a/packages/svelte/tests/hydration/samples/cloudflare-mirage-borking/_override.html b/packages/svelte/tests/hydration/samples/cloudflare-mirage-borking/_override.html
new file mode 100644
index 0000000000..c84efbb00b
--- /dev/null
+++ b/packages/svelte/tests/hydration/samples/cloudflare-mirage-borking/_override.html
@@ -0,0 +1 @@
+start
pre123 mid
diff --git a/packages/svelte/tests/hydration/samples/cloudflare-mirage-borking/main.svelte b/packages/svelte/tests/hydration/samples/cloudflare-mirage-borking/main.svelte
new file mode 100644
index 0000000000..2c9a94686e
--- /dev/null
+++ b/packages/svelte/tests/hydration/samples/cloudflare-mirage-borking/main.svelte
@@ -0,0 +1,9 @@
+
+
+start
+pre123
+{#if cond}
+mid
+{/if}
diff --git a/packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-3/output.svelte b/packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-3/output.svelte
index 9e4f086aed..26012e1115 100644
--- a/packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-3/output.svelte
+++ b/packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-3/output.svelte
@@ -1,7 +1,7 @@
-
+
-
\ No newline at end of file
+
diff --git a/packages/svelte/tests/migrate/samples/impossible-migrate-slot-change-name/output.svelte b/packages/svelte/tests/migrate/samples/impossible-migrate-slot-change-name/output.svelte
index 2b6838a1d6..328966b63b 100644
--- a/packages/svelte/tests/migrate/samples/impossible-migrate-slot-change-name/output.svelte
+++ b/packages/svelte/tests/migrate/samples/impossible-migrate-slot-change-name/output.svelte
@@ -1,6 +1,6 @@
-
+
-
\ No newline at end of file
+
diff --git a/packages/svelte/tests/migrate/samples/impossible-migrate-slot-non-identifier/output.svelte b/packages/svelte/tests/migrate/samples/impossible-migrate-slot-non-identifier/output.svelte
index 6e5ab10310..1e763577df 100644
--- a/packages/svelte/tests/migrate/samples/impossible-migrate-slot-non-identifier/output.svelte
+++ b/packages/svelte/tests/migrate/samples/impossible-migrate-slot-non-identifier/output.svelte
@@ -1,2 +1,2 @@
-
-
\ No newline at end of file
+
+
diff --git a/packages/svelte/tests/parser-legacy/samples/generic-snippets/input.svelte b/packages/svelte/tests/parser-legacy/samples/generic-snippets/input.svelte
new file mode 100644
index 0000000000..4ee619728d
--- /dev/null
+++ b/packages/svelte/tests/parser-legacy/samples/generic-snippets/input.svelte
@@ -0,0 +1,10 @@
+
+
+{#snippet generic(val: T)}
+ {val}
+{/snippet}
+
+{#snippet complex_generic">>(val: T)}
+ {val}
+{/snippet}
\ No newline at end of file
diff --git a/packages/svelte/tests/parser-legacy/samples/generic-snippets/output.json b/packages/svelte/tests/parser-legacy/samples/generic-snippets/output.json
new file mode 100644
index 0000000000..37fb499e7b
--- /dev/null
+++ b/packages/svelte/tests/parser-legacy/samples/generic-snippets/output.json
@@ -0,0 +1,244 @@
+{
+ "html": {
+ "type": "Fragment",
+ "start": 30,
+ "end": 192,
+ "children": [
+ {
+ "type": "Text",
+ "start": 28,
+ "end": 30,
+ "raw": "\n\n",
+ "data": "\n\n"
+ },
+ {
+ "type": "SnippetBlock",
+ "start": 30,
+ "end": 92,
+ "expression": {
+ "type": "Identifier",
+ "start": 40,
+ "end": 47,
+ "name": "generic"
+ },
+ "parameters": [
+ {
+ "type": "Identifier",
+ "start": 66,
+ "end": 72,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 36
+ },
+ "end": {
+ "line": 4,
+ "column": 42
+ }
+ },
+ "name": "val",
+ "typeAnnotation": {
+ "type": "TSTypeAnnotation",
+ "start": 69,
+ "end": 72,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 39
+ },
+ "end": {
+ "line": 4,
+ "column": 42
+ }
+ },
+ "typeAnnotation": {
+ "type": "TSTypeReference",
+ "start": 71,
+ "end": 72,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 41
+ },
+ "end": {
+ "line": 4,
+ "column": 42
+ }
+ },
+ "typeName": {
+ "type": "Identifier",
+ "start": 71,
+ "end": 72,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 41
+ },
+ "end": {
+ "line": 4,
+ "column": 42
+ }
+ },
+ "name": "T"
+ }
+ }
+ }
+ }
+ ],
+ "children": [
+ {
+ "type": "MustacheTag",
+ "start": 76,
+ "end": 81,
+ "expression": {
+ "type": "Identifier",
+ "start": 77,
+ "end": 80,
+ "loc": {
+ "start": {
+ "line": 5,
+ "column": 2
+ },
+ "end": {
+ "line": 5,
+ "column": 5
+ }
+ },
+ "name": "val"
+ }
+ }
+ ],
+ "typeParams": "T extends string"
+ },
+ {
+ "type": "Text",
+ "start": 92,
+ "end": 94,
+ "raw": "\n\n",
+ "data": "\n\n"
+ },
+ {
+ "type": "SnippetBlock",
+ "start": 94,
+ "end": 192,
+ "expression": {
+ "type": "Identifier",
+ "start": 104,
+ "end": 119,
+ "name": "complex_generic"
+ },
+ "parameters": [
+ {
+ "type": "Identifier",
+ "start": 166,
+ "end": 172,
+ "loc": {
+ "start": {
+ "line": 8,
+ "column": 72
+ },
+ "end": {
+ "line": 8,
+ "column": 78
+ }
+ },
+ "name": "val",
+ "typeAnnotation": {
+ "type": "TSTypeAnnotation",
+ "start": 169,
+ "end": 172,
+ "loc": {
+ "start": {
+ "line": 8,
+ "column": 75
+ },
+ "end": {
+ "line": 8,
+ "column": 78
+ }
+ },
+ "typeAnnotation": {
+ "type": "TSTypeReference",
+ "start": 171,
+ "end": 172,
+ "loc": {
+ "start": {
+ "line": 8,
+ "column": 77
+ },
+ "end": {
+ "line": 8,
+ "column": 78
+ }
+ },
+ "typeName": {
+ "type": "Identifier",
+ "start": 171,
+ "end": 172,
+ "loc": {
+ "start": {
+ "line": 8,
+ "column": 77
+ },
+ "end": {
+ "line": 8,
+ "column": 78
+ }
+ },
+ "name": "T"
+ }
+ }
+ }
+ }
+ ],
+ "children": [
+ {
+ "type": "MustacheTag",
+ "start": 176,
+ "end": 181,
+ "expression": {
+ "type": "Identifier",
+ "start": 177,
+ "end": 180,
+ "loc": {
+ "start": {
+ "line": 9,
+ "column": 2
+ },
+ "end": {
+ "line": 9,
+ "column": 5
+ }
+ },
+ "name": "val"
+ }
+ }
+ ],
+ "typeParams": "T extends { bracket: \"<\" } | \"<\" | Set<\"<>\">"
+ }
+ ]
+ },
+ "instance": {
+ "type": "Script",
+ "start": 0,
+ "end": 28,
+ "context": "default",
+ "content": {
+ "type": "Program",
+ "start": 18,
+ "end": 19,
+ "loc": {
+ "start": {
+ "line": 1,
+ "column": 0
+ },
+ "end": {
+ "line": 2,
+ "column": 0
+ }
+ },
+ "body": [],
+ "sourceType": "module"
+ }
+ }
+}
diff --git a/packages/svelte/tests/parser-modern/samples/attachments/input.svelte b/packages/svelte/tests/parser-modern/samples/attachments/input.svelte
new file mode 100644
index 0000000000..9faae8d1bf
--- /dev/null
+++ b/packages/svelte/tests/parser-modern/samples/attachments/input.svelte
@@ -0,0 +1 @@
+ {}} {@attach (node) => {}}>
diff --git a/packages/svelte/tests/parser-modern/samples/attachments/output.json b/packages/svelte/tests/parser-modern/samples/attachments/output.json
new file mode 100644
index 0000000000..42e9880fcc
--- /dev/null
+++ b/packages/svelte/tests/parser-modern/samples/attachments/output.json
@@ -0,0 +1,141 @@
+{
+ "css": null,
+ "js": [],
+ "start": 0,
+ "end": 57,
+ "type": "Root",
+ "fragment": {
+ "type": "Fragment",
+ "nodes": [
+ {
+ "type": "RegularElement",
+ "start": 0,
+ "end": 57,
+ "name": "div",
+ "attributes": [
+ {
+ "type": "AttachTag",
+ "start": 5,
+ "end": 27,
+ "expression": {
+ "type": "ArrowFunctionExpression",
+ "start": 14,
+ "end": 26,
+ "loc": {
+ "start": {
+ "line": 1,
+ "column": 14
+ },
+ "end": {
+ "line": 1,
+ "column": 26
+ }
+ },
+ "id": null,
+ "expression": false,
+ "generator": false,
+ "async": false,
+ "params": [
+ {
+ "type": "Identifier",
+ "start": 15,
+ "end": 19,
+ "loc": {
+ "start": {
+ "line": 1,
+ "column": 15
+ },
+ "end": {
+ "line": 1,
+ "column": 19
+ }
+ },
+ "name": "node"
+ }
+ ],
+ "body": {
+ "type": "BlockStatement",
+ "start": 24,
+ "end": 26,
+ "loc": {
+ "start": {
+ "line": 1,
+ "column": 24
+ },
+ "end": {
+ "line": 1,
+ "column": 26
+ }
+ },
+ "body": []
+ }
+ }
+ },
+ {
+ "type": "AttachTag",
+ "start": 28,
+ "end": 50,
+ "expression": {
+ "type": "ArrowFunctionExpression",
+ "start": 37,
+ "end": 49,
+ "loc": {
+ "start": {
+ "line": 1,
+ "column": 37
+ },
+ "end": {
+ "line": 1,
+ "column": 49
+ }
+ },
+ "id": null,
+ "expression": false,
+ "generator": false,
+ "async": false,
+ "params": [
+ {
+ "type": "Identifier",
+ "start": 38,
+ "end": 42,
+ "loc": {
+ "start": {
+ "line": 1,
+ "column": 38
+ },
+ "end": {
+ "line": 1,
+ "column": 42
+ }
+ },
+ "name": "node"
+ }
+ ],
+ "body": {
+ "type": "BlockStatement",
+ "start": 47,
+ "end": 49,
+ "loc": {
+ "start": {
+ "line": 1,
+ "column": 47
+ },
+ "end": {
+ "line": 1,
+ "column": 49
+ }
+ },
+ "body": []
+ }
+ }
+ }
+ ],
+ "fragment": {
+ "type": "Fragment",
+ "nodes": []
+ }
+ }
+ ]
+ },
+ "options": null
+}
diff --git a/packages/svelte/tests/parser-modern/samples/generic-snippets/input.svelte b/packages/svelte/tests/parser-modern/samples/generic-snippets/input.svelte
new file mode 100644
index 0000000000..4ee619728d
--- /dev/null
+++ b/packages/svelte/tests/parser-modern/samples/generic-snippets/input.svelte
@@ -0,0 +1,10 @@
+
+
+{#snippet generic(val: T)}
+ {val}
+{/snippet}
+
+{#snippet complex_generic">>(val: T)}
+ {val}
+{/snippet}
\ No newline at end of file
diff --git a/packages/svelte/tests/parser-modern/samples/generic-snippets/output.json b/packages/svelte/tests/parser-modern/samples/generic-snippets/output.json
new file mode 100644
index 0000000000..b66ee7288f
--- /dev/null
+++ b/packages/svelte/tests/parser-modern/samples/generic-snippets/output.json
@@ -0,0 +1,299 @@
+{
+ "css": null,
+ "js": [],
+ "start": 30,
+ "end": 192,
+ "type": "Root",
+ "fragment": {
+ "type": "Fragment",
+ "nodes": [
+ {
+ "type": "Text",
+ "start": 28,
+ "end": 30,
+ "raw": "\n\n",
+ "data": "\n\n"
+ },
+ {
+ "type": "SnippetBlock",
+ "start": 30,
+ "end": 92,
+ "expression": {
+ "type": "Identifier",
+ "start": 40,
+ "end": 47,
+ "name": "generic"
+ },
+ "typeParams": "T extends string",
+ "parameters": [
+ {
+ "type": "Identifier",
+ "start": 66,
+ "end": 72,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 36
+ },
+ "end": {
+ "line": 4,
+ "column": 42
+ }
+ },
+ "name": "val",
+ "typeAnnotation": {
+ "type": "TSTypeAnnotation",
+ "start": 69,
+ "end": 72,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 39
+ },
+ "end": {
+ "line": 4,
+ "column": 42
+ }
+ },
+ "typeAnnotation": {
+ "type": "TSTypeReference",
+ "start": 71,
+ "end": 72,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 41
+ },
+ "end": {
+ "line": 4,
+ "column": 42
+ }
+ },
+ "typeName": {
+ "type": "Identifier",
+ "start": 71,
+ "end": 72,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 41
+ },
+ "end": {
+ "line": 4,
+ "column": 42
+ }
+ },
+ "name": "T"
+ }
+ }
+ }
+ }
+ ],
+ "body": {
+ "type": "Fragment",
+ "nodes": [
+ {
+ "type": "Text",
+ "start": 74,
+ "end": 76,
+ "raw": "\n\t",
+ "data": "\n\t"
+ },
+ {
+ "type": "ExpressionTag",
+ "start": 76,
+ "end": 81,
+ "expression": {
+ "type": "Identifier",
+ "start": 77,
+ "end": 80,
+ "loc": {
+ "start": {
+ "line": 5,
+ "column": 2
+ },
+ "end": {
+ "line": 5,
+ "column": 5
+ }
+ },
+ "name": "val"
+ }
+ },
+ {
+ "type": "Text",
+ "start": 81,
+ "end": 82,
+ "raw": "\n",
+ "data": "\n"
+ }
+ ]
+ }
+ },
+ {
+ "type": "Text",
+ "start": 92,
+ "end": 94,
+ "raw": "\n\n",
+ "data": "\n\n"
+ },
+ {
+ "type": "SnippetBlock",
+ "start": 94,
+ "end": 192,
+ "expression": {
+ "type": "Identifier",
+ "start": 104,
+ "end": 119,
+ "name": "complex_generic"
+ },
+ "typeParams": "T extends { bracket: \"<\" } | \"<\" | Set<\"<>\">",
+ "parameters": [
+ {
+ "type": "Identifier",
+ "start": 166,
+ "end": 172,
+ "loc": {
+ "start": {
+ "line": 8,
+ "column": 72
+ },
+ "end": {
+ "line": 8,
+ "column": 78
+ }
+ },
+ "name": "val",
+ "typeAnnotation": {
+ "type": "TSTypeAnnotation",
+ "start": 169,
+ "end": 172,
+ "loc": {
+ "start": {
+ "line": 8,
+ "column": 75
+ },
+ "end": {
+ "line": 8,
+ "column": 78
+ }
+ },
+ "typeAnnotation": {
+ "type": "TSTypeReference",
+ "start": 171,
+ "end": 172,
+ "loc": {
+ "start": {
+ "line": 8,
+ "column": 77
+ },
+ "end": {
+ "line": 8,
+ "column": 78
+ }
+ },
+ "typeName": {
+ "type": "Identifier",
+ "start": 171,
+ "end": 172,
+ "loc": {
+ "start": {
+ "line": 8,
+ "column": 77
+ },
+ "end": {
+ "line": 8,
+ "column": 78
+ }
+ },
+ "name": "T"
+ }
+ }
+ }
+ }
+ ],
+ "body": {
+ "type": "Fragment",
+ "nodes": [
+ {
+ "type": "Text",
+ "start": 174,
+ "end": 176,
+ "raw": "\n\t",
+ "data": "\n\t"
+ },
+ {
+ "type": "ExpressionTag",
+ "start": 176,
+ "end": 181,
+ "expression": {
+ "type": "Identifier",
+ "start": 177,
+ "end": 180,
+ "loc": {
+ "start": {
+ "line": 9,
+ "column": 2
+ },
+ "end": {
+ "line": 9,
+ "column": 5
+ }
+ },
+ "name": "val"
+ }
+ },
+ {
+ "type": "Text",
+ "start": 181,
+ "end": 182,
+ "raw": "\n",
+ "data": "\n"
+ }
+ ]
+ }
+ }
+ ]
+ },
+ "options": null,
+ "instance": {
+ "type": "Script",
+ "start": 0,
+ "end": 28,
+ "context": "default",
+ "content": {
+ "type": "Program",
+ "start": 18,
+ "end": 19,
+ "loc": {
+ "start": {
+ "line": 1,
+ "column": 0
+ },
+ "end": {
+ "line": 2,
+ "column": 0
+ }
+ },
+ "body": [],
+ "sourceType": "module"
+ },
+ "attributes": [
+ {
+ "type": "Attribute",
+ "start": 8,
+ "end": 17,
+ "name": "lang",
+ "value": [
+ {
+ "start": 14,
+ "end": 16,
+ "type": "Text",
+ "raw": "ts",
+ "data": "ts"
+ }
+ ]
+ }
+ ]
+ }
+}
diff --git a/packages/svelte/tests/runtime-runes/samples/attachment-basic/_config.js b/packages/svelte/tests/runtime-runes/samples/attachment-basic/_config.js
new file mode 100644
index 0000000000..1be4737069
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/attachment-basic/_config.js
@@ -0,0 +1,6 @@
+import { test } from '../../test';
+
+export default test({
+ ssrHtml: `
`,
+ html: `DIV
`
+});
diff --git a/packages/svelte/tests/runtime-runes/samples/attachment-basic/main.svelte b/packages/svelte/tests/runtime-runes/samples/attachment-basic/main.svelte
new file mode 100644
index 0000000000..1a1f74e4a9
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/attachment-basic/main.svelte
@@ -0,0 +1 @@
+ node.textContent = node.nodeName}>
diff --git a/packages/svelte/tests/runtime-runes/samples/attachment-component-spread/Child.svelte b/packages/svelte/tests/runtime-runes/samples/attachment-component-spread/Child.svelte
new file mode 100644
index 0000000000..6760da61fa
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/attachment-component-spread/Child.svelte
@@ -0,0 +1,5 @@
+
+
+
diff --git a/packages/svelte/tests/runtime-runes/samples/attachment-component-spread/_config.js b/packages/svelte/tests/runtime-runes/samples/attachment-component-spread/_config.js
new file mode 100644
index 0000000000..23907c62d2
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/attachment-component-spread/_config.js
@@ -0,0 +1,15 @@
+import { flushSync } from 'svelte';
+import { test } from '../../test';
+
+export default test({
+ html: `update
`,
+
+ test({ target, assert, logs }) {
+ const button = target.querySelector('button');
+
+ assert.deepEqual(logs, ['one DIV']);
+
+ flushSync(() => button?.click());
+ assert.deepEqual(logs, ['one DIV', 'cleanup one', 'two DIV']);
+ }
+});
diff --git a/packages/svelte/tests/runtime-runes/samples/attachment-component-spread/main.svelte b/packages/svelte/tests/runtime-runes/samples/attachment-component-spread/main.svelte
new file mode 100644
index 0000000000..d1d7e65126
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/attachment-component-spread/main.svelte
@@ -0,0 +1,30 @@
+
+
+update
+
+
diff --git a/packages/svelte/tests/runtime-runes/samples/attachment-component/Child.svelte b/packages/svelte/tests/runtime-runes/samples/attachment-component/Child.svelte
new file mode 100644
index 0000000000..6760da61fa
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/attachment-component/Child.svelte
@@ -0,0 +1,5 @@
+
+
+
diff --git a/packages/svelte/tests/runtime-runes/samples/attachment-component/_config.js b/packages/svelte/tests/runtime-runes/samples/attachment-component/_config.js
new file mode 100644
index 0000000000..b6ef016be5
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/attachment-component/_config.js
@@ -0,0 +1,14 @@
+import { flushSync } from 'svelte';
+import { test } from '../../test';
+
+export default test({
+ ssrHtml: `update
`,
+ html: `update one
`,
+
+ test({ target, assert }) {
+ const button = target.querySelector('button');
+
+ flushSync(() => button?.click());
+ assert.htmlEqual(target.innerHTML, 'update two
');
+ }
+});
diff --git a/packages/svelte/tests/runtime-runes/samples/attachment-component/main.svelte b/packages/svelte/tests/runtime-runes/samples/attachment-component/main.svelte
new file mode 100644
index 0000000000..29e26689db
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/attachment-component/main.svelte
@@ -0,0 +1,15 @@
+
+
+ message = 'two'}>update
+
+
diff --git a/packages/svelte/tests/runtime-runes/samples/attachment-reactive/_config.js b/packages/svelte/tests/runtime-runes/samples/attachment-reactive/_config.js
new file mode 100644
index 0000000000..7d0502590b
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/attachment-reactive/_config.js
@@ -0,0 +1,14 @@
+import { flushSync } from 'svelte';
+import { test } from '../../test';
+
+export default test({
+ ssrHtml: `
increment `,
+ html: `1
increment `,
+
+ test: ({ assert, target }) => {
+ const btn = target.querySelector('button');
+
+ flushSync(() => btn?.click());
+ assert.htmlEqual(target.innerHTML, `2
increment `);
+ }
+});
diff --git a/packages/svelte/tests/runtime-runes/samples/attachment-reactive/main.svelte b/packages/svelte/tests/runtime-runes/samples/attachment-reactive/main.svelte
new file mode 100644
index 0000000000..9fa3cfdb67
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/attachment-reactive/main.svelte
@@ -0,0 +1,6 @@
+
+
+ node.textContent = value}>
+ value += 1}>increment
diff --git a/packages/svelte/tests/runtime-runes/samples/attachment-spread/_config.js b/packages/svelte/tests/runtime-runes/samples/attachment-spread/_config.js
new file mode 100644
index 0000000000..96fc207450
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/attachment-spread/_config.js
@@ -0,0 +1,8 @@
+import { flushSync } from 'svelte';
+import { test } from '../../test';
+
+export default test({
+ test({ assert, logs, target }) {
+ assert.deepEqual(logs, ['hello']);
+ }
+});
diff --git a/packages/svelte/tests/runtime-runes/samples/attachment-spread/main.svelte b/packages/svelte/tests/runtime-runes/samples/attachment-spread/main.svelte
new file mode 100644
index 0000000000..dbd8c47ada
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/attachment-spread/main.svelte
@@ -0,0 +1,9 @@
+
+
+
diff --git a/packages/svelte/tests/runtime-runes/samples/attachment-svelte-element/_config.js b/packages/svelte/tests/runtime-runes/samples/attachment-svelte-element/_config.js
new file mode 100644
index 0000000000..1be4737069
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/attachment-svelte-element/_config.js
@@ -0,0 +1,6 @@
+import { test } from '../../test';
+
+export default test({
+ ssrHtml: `
`,
+ html: `DIV
`
+});
diff --git a/packages/svelte/tests/runtime-runes/samples/attachment-svelte-element/main.svelte b/packages/svelte/tests/runtime-runes/samples/attachment-svelte-element/main.svelte
new file mode 100644
index 0000000000..bd4b52342f
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/attachment-svelte-element/main.svelte
@@ -0,0 +1 @@
+ node.textContent = node.nodeName}>
diff --git a/packages/svelte/tests/runtime-runes/samples/bigint-invalid/_config.js b/packages/svelte/tests/runtime-runes/samples/bigint-invalid/_config.js
new file mode 100644
index 0000000000..810ac338a5
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/bigint-invalid/_config.js
@@ -0,0 +1,7 @@
+import { test } from '../../test';
+
+export default test({
+ // check that this is a runtime error, not a compile time error
+ // caused by over-eager partial-evaluation
+ error: 'Cannot convert invalid to a BigInt'
+});
diff --git a/packages/svelte/tests/runtime-runes/samples/bigint-invalid/main.svelte b/packages/svelte/tests/runtime-runes/samples/bigint-invalid/main.svelte
new file mode 100644
index 0000000000..126528bad2
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/bigint-invalid/main.svelte
@@ -0,0 +1,5 @@
+
+
+{invalid}
diff --git a/packages/svelte/tests/runtime-runes/samples/class-private-fields-assignment-shorthand/_config.js b/packages/svelte/tests/runtime-runes/samples/class-private-fields-assignment-shorthand/_config.js
new file mode 100644
index 0000000000..0fdeabfe0b
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/class-private-fields-assignment-shorthand/_config.js
@@ -0,0 +1,20 @@
+import { flushSync } from 'svelte';
+import { test } from '../../test';
+
+export default test({
+ async test({ assert, target }) {
+ const btn = target.querySelector('button');
+
+ btn?.click();
+ flushSync();
+ assert.htmlEqual(
+ target.innerHTML,
+ `
+ inc
+ a:1
+ b:2
+ c:3
+ `
+ );
+ }
+});
diff --git a/packages/svelte/tests/runtime-runes/samples/class-private-fields-assignment-shorthand/main.svelte b/packages/svelte/tests/runtime-runes/samples/class-private-fields-assignment-shorthand/main.svelte
new file mode 100644
index 0000000000..746f22b1e6
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/class-private-fields-assignment-shorthand/main.svelte
@@ -0,0 +1,28 @@
+
+
+ counter.inc()}>inc
+
+{#key 1}a:{counter.a}
{/key}
+{#key 2}b:{counter.b}
{/key}
+{#key 3}c:{counter.c}
{/key}
diff --git a/packages/svelte/tests/runtime-runes/samples/nullish-empty-string/_config.js b/packages/svelte/tests/runtime-runes/samples/nullish-empty-string/_config.js
new file mode 100644
index 0000000000..84e97e9735
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/nullish-empty-string/_config.js
@@ -0,0 +1,5 @@
+import { test } from '../../test';
+
+export default test({
+ html: '[]'
+});
diff --git a/packages/svelte/tests/runtime-runes/samples/nullish-empty-string/main.svelte b/packages/svelte/tests/runtime-runes/samples/nullish-empty-string/main.svelte
new file mode 100644
index 0000000000..efe39b91fe
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/nullish-empty-string/main.svelte
@@ -0,0 +1 @@
+[{undefined ?? null}]
diff --git a/packages/svelte/tests/runtime-runes/samples/ownership-invalid-mutation-use-transform/_config.js b/packages/svelte/tests/runtime-runes/samples/ownership-invalid-mutation-use-transform/_config.js
new file mode 100644
index 0000000000..95556f4737
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/ownership-invalid-mutation-use-transform/_config.js
@@ -0,0 +1,10 @@
+import { test } from '../../test';
+
+export default test({
+ compileOptions: {
+ dev: true
+ },
+ async test({ assert, errors }) {
+ assert.deepEqual(errors, []);
+ }
+});
diff --git a/packages/svelte/tests/runtime-runes/samples/ownership-invalid-mutation-use-transform/main.svelte b/packages/svelte/tests/runtime-runes/samples/ownership-invalid-mutation-use-transform/main.svelte
new file mode 100644
index 0000000000..d834551221
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/ownership-invalid-mutation-use-transform/main.svelte
@@ -0,0 +1,5 @@
+
+
diff --git a/packages/svelte/tests/runtime-runes/samples/props-and-slots/Child.svelte b/packages/svelte/tests/runtime-runes/samples/props-and-slots/Child.svelte
new file mode 100644
index 0000000000..a2e7d6d8a4
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/props-and-slots/Child.svelte
@@ -0,0 +1,9 @@
+
+
+{Object.keys(props)}
+
+{#if $$slots.foo}
+ foo exists
+{/if}
diff --git a/packages/svelte/tests/runtime-runes/samples/props-and-slots/_config.js b/packages/svelte/tests/runtime-runes/samples/props-and-slots/_config.js
new file mode 100644
index 0000000000..3f6fb4143c
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/props-and-slots/_config.js
@@ -0,0 +1,8 @@
+import { test } from '../../test';
+
+export default test({
+ html: `
+ a
+ foo exists
+ `
+});
diff --git a/packages/svelte/tests/runtime-runes/samples/props-and-slots/main.svelte b/packages/svelte/tests/runtime-runes/samples/props-and-slots/main.svelte
new file mode 100644
index 0000000000..3535da7132
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/props-and-slots/main.svelte
@@ -0,0 +1,7 @@
+
+
+
+ foo
+
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 47f297bce9..b2ef29ccaf 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
@@ -7,10 +7,12 @@ let c = 3;
let d = 4;
export function update(array) {
- (
- $.set(a, array[0], true),
- $.set(b, array[1], true)
- );
+ {
+ let [$$1, $$2] = array;
+
+ $.set(a, $$1, true);
+ $.set(b, $$2, true);
+ };
[c, d] = array;
}
\ No newline at end of file
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 5bc9766acf..f661dbc01d 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
@@ -8,7 +8,7 @@ export default function Purity($$anchor) {
var fragment = root();
var p = $.first_child(fragment);
- p.textContent = 0;
+ p.textContent = '0';
var p_1 = $.sibling(p, 2);
diff --git a/packages/svelte/tests/validator/samples/bidirectional-control-characters/input.svelte b/packages/svelte/tests/validator/samples/bidirectional-control-characters/input.svelte
new file mode 100644
index 0000000000..21587e5f4f
--- /dev/null
+++ b/packages/svelte/tests/validator/samples/bidirectional-control-characters/input.svelte
@@ -0,0 +1,8 @@
+
+defabc
+Hello, {name}!
+
+
+defabc
diff --git a/packages/svelte/tests/validator/samples/bidirectional-control-characters/warnings.json b/packages/svelte/tests/validator/samples/bidirectional-control-characters/warnings.json
new file mode 100644
index 0000000000..6e70193c6c
--- /dev/null
+++ b/packages/svelte/tests/validator/samples/bidirectional-control-characters/warnings.json
@@ -0,0 +1,50 @@
+[
+ {
+ "code": "bidirectional_control_characters",
+ "message": "A bidirectional control character was detected in your code. These characters can be used to alter the visual direction of your code and could have unintended consequences",
+ "start": {
+ "line": 2,
+ "column": 15
+ },
+ "end": {
+ "line": 2,
+ "column": 58
+ }
+ },
+ {
+ "code": "bidirectional_control_characters",
+ "message": "A bidirectional control character was detected in your code. These characters can be used to alter the visual direction of your code and could have unintended consequences",
+ "start": {
+ "line": 4,
+ "column": 0
+ },
+ "end": {
+ "line": 4,
+ "column": 2
+ }
+ },
+ {
+ "code": "bidirectional_control_characters",
+ "message": "A bidirectional control character was detected in your code. These characters can be used to alter the visual direction of your code and could have unintended consequences",
+ "start": {
+ "line": 4,
+ "column": 5
+ },
+ "end": {
+ "line": 4,
+ "column": 7
+ }
+ },
+ {
+ "code": "bidirectional_control_characters",
+ "message": "A bidirectional control character was detected in your code. These characters can be used to alter the visual direction of your code and could have unintended consequences",
+ "start": {
+ "line": 4,
+ "column": 10
+ },
+ "end": {
+ "line": 4,
+ "column": 12
+ }
+ }
+]
diff --git a/packages/svelte/types/index.d.ts b/packages/svelte/types/index.d.ts
index b233cfcc0b..bb958c5108 100644
--- a/packages/svelte/types/index.d.ts
+++ b/packages/svelte/types/index.d.ts
@@ -624,6 +624,44 @@ declare module 'svelte/animate' {
export {};
}
+declare module 'svelte/attachments' {
+ /**
+ * An [attachment](https://svelte.dev/docs/svelte/@attach) is a function that runs when an element is mounted
+ * to the DOM, and optionally returns a function that is called when the element is later removed.
+ *
+ * It can be attached to an element with an `{@attach ...}` tag, or by spreading an object containing
+ * a property created with [`createAttachmentKey`](https://svelte.dev/docs/svelte/svelte-attachments#createAttachmentKey).
+ */
+ export interface Attachment {
+ (element: T): void | (() => void);
+ }
+ /**
+ * Creates an object key that will be recognised as an attachment when the object is spread onto an element,
+ * as a programmatic alternative to using `{@attach ...}`. This can be useful for library authors, though
+ * is generally not needed when building an app.
+ *
+ * ```svelte
+ *
+ *
+ * click me
+ * ```
+ * @since 5.29
+ */
+ export function createAttachmentKey(): symbol;
+
+ export {};
+}
+
declare module 'svelte/compiler' {
import type { SourceMap } from 'magic-string';
import type { ArrayExpression, ArrowFunctionExpression, VariableDeclaration, VariableDeclarator, Expression, Identifier, MemberExpression, Node, ObjectExpression, Pattern, Program, ChainExpression, SimpleCallExpression, SequenceExpression } from 'estree';
@@ -1055,6 +1093,12 @@ declare module 'svelte/compiler' {
expression: SimpleCallExpression | (ChainExpression & { expression: SimpleCallExpression });
}
+ /** A `{@attach foo(...)} tag */
+ export interface AttachTag extends BaseNode {
+ type: 'AttachTag';
+ expression: Expression;
+ }
+
/** An `animate:` directive */
export interface AnimateDirective extends BaseNode {
type: 'AnimateDirective';
@@ -1137,7 +1181,7 @@ declare module 'svelte/compiler' {
interface BaseElement extends BaseNode {
name: string;
- attributes: Array;
+ attributes: Array;
fragment: Fragment;
}
@@ -1257,6 +1301,7 @@ declare module 'svelte/compiler' {
type: 'SnippetBlock';
expression: Identifier;
parameters: Pattern[];
+ typeParams?: string;
body: Fragment;
}
@@ -1327,6 +1372,7 @@ declare module 'svelte/compiler' {
| AST.Attribute
| AST.SpreadAttribute
| Directive
+ | AST.AttachTag
| AST.Comment
| Block;
diff --git a/playgrounds/sandbox/package.json b/playgrounds/sandbox/package.json
index 5aee92ab17..3ab65ac4b5 100644
--- a/playgrounds/sandbox/package.json
+++ b/playgrounds/sandbox/package.json
@@ -18,7 +18,7 @@
"polka": "^1.0.0-next.25",
"svelte": "workspace:*",
"tinyglobby": "^0.2.12",
- "vite": "^5.4.18",
+ "vite": "^5.4.19",
"vite-plugin-inspect": "^0.8.4"
}
}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 3518b0e57e..3d5db0fd8e 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -152,7 +152,7 @@ importers:
devDependencies:
'@sveltejs/vite-plugin-svelte':
specifier: ^4.0.0-next.6
- version: 4.0.0-next.6(svelte@packages+svelte)(vite@5.4.18(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0))
+ version: 4.0.0-next.6(svelte@packages+svelte)(vite@5.4.19(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0))
polka:
specifier: ^1.0.0-next.25
version: 1.0.0-next.25
@@ -163,11 +163,11 @@ importers:
specifier: ^0.2.12
version: 0.2.12
vite:
- specifier: ^5.4.18
- version: 5.4.18(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)
+ specifier: ^5.4.19
+ version: 5.4.19(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)
vite-plugin-inspect:
specifier: ^0.8.4
- version: 0.8.4(rollup@4.39.0)(vite@5.4.18(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0))
+ version: 0.8.4(rollup@4.40.2)(vite@5.4.19(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0))
packages:
@@ -408,8 +408,8 @@ packages:
peerDependencies:
eslint: ^6.0.0 || ^7.0.0 || >=8.0.0
- '@eslint-community/eslint-utils@4.5.1':
- resolution: {integrity: sha512-soEIOALTfTK6EjmKMMoLugwaP0rzkad90iIWd1hMO9ARkSAyjfMfkRRhLvD5qH7vvM0Cg72pieUfR6yh6XxC4w==}
+ '@eslint-community/eslint-utils@4.7.0':
+ resolution: {integrity: sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
eslint: ^6.0.0 || ^7.0.0 || >=8.0.0
@@ -551,8 +551,8 @@ packages:
cpu: [arm]
os: [android]
- '@rollup/rollup-android-arm-eabi@4.39.0':
- resolution: {integrity: sha512-lGVys55Qb00Wvh8DMAocp5kIcaNzEFTmGhfFd88LfaogYTRKrdxgtlO5H6S49v2Nd8R2C6wLOal0qv6/kCkOwA==}
+ '@rollup/rollup-android-arm-eabi@4.40.2':
+ resolution: {integrity: sha512-JkdNEq+DFxZfUwxvB58tHMHBHVgX23ew41g1OQinthJ+ryhdRk67O31S7sYw8u2lTjHUPFxwar07BBt1KHp/hg==}
cpu: [arm]
os: [android]
@@ -561,8 +561,8 @@ packages:
cpu: [arm64]
os: [android]
- '@rollup/rollup-android-arm64@4.39.0':
- resolution: {integrity: sha512-It9+M1zE31KWfqh/0cJLrrsCPiF72PoJjIChLX+rEcujVRCb4NLQ5QzFkzIZW8Kn8FTbvGQBY5TkKBau3S8cCQ==}
+ '@rollup/rollup-android-arm64@4.40.2':
+ resolution: {integrity: sha512-13unNoZ8NzUmnndhPTkWPWbX3vtHodYmy+I9kuLxN+F+l+x3LdVF7UCu8TWVMt1POHLh6oDHhnOA04n8oJZhBw==}
cpu: [arm64]
os: [android]
@@ -571,8 +571,8 @@ packages:
cpu: [arm64]
os: [darwin]
- '@rollup/rollup-darwin-arm64@4.39.0':
- resolution: {integrity: sha512-lXQnhpFDOKDXiGxsU9/l8UEGGM65comrQuZ+lDcGUx+9YQ9dKpF3rSEGepyeR5AHZ0b5RgiligsBhWZfSSQh8Q==}
+ '@rollup/rollup-darwin-arm64@4.40.2':
+ resolution: {integrity: sha512-Gzf1Hn2Aoe8VZzevHostPX23U7N5+4D36WJNHK88NZHCJr7aVMG4fadqkIf72eqVPGjGc0HJHNuUaUcxiR+N/w==}
cpu: [arm64]
os: [darwin]
@@ -581,18 +581,18 @@ packages:
cpu: [x64]
os: [darwin]
- '@rollup/rollup-darwin-x64@4.39.0':
- resolution: {integrity: sha512-mKXpNZLvtEbgu6WCkNij7CGycdw9cJi2k9v0noMb++Vab12GZjFgUXD69ilAbBh034Zwn95c2PNSz9xM7KYEAQ==}
+ '@rollup/rollup-darwin-x64@4.40.2':
+ resolution: {integrity: sha512-47N4hxa01a4x6XnJoskMKTS8XZ0CZMd8YTbINbi+w03A2w4j1RTlnGHOz/P0+Bg1LaVL6ufZyNprSg+fW5nYQQ==}
cpu: [x64]
os: [darwin]
- '@rollup/rollup-freebsd-arm64@4.39.0':
- resolution: {integrity: sha512-jivRRlh2Lod/KvDZx2zUR+I4iBfHcu2V/BA2vasUtdtTN2Uk3jfcZczLa81ESHZHPHy4ih3T/W5rPFZ/hX7RtQ==}
+ '@rollup/rollup-freebsd-arm64@4.40.2':
+ resolution: {integrity: sha512-8t6aL4MD+rXSHHZUR1z19+9OFJ2rl1wGKvckN47XFRVO+QL/dUSpKA2SLRo4vMg7ELA8pzGpC+W9OEd1Z/ZqoQ==}
cpu: [arm64]
os: [freebsd]
- '@rollup/rollup-freebsd-x64@4.39.0':
- resolution: {integrity: sha512-8RXIWvYIRK9nO+bhVz8DwLBepcptw633gv/QT4015CpJ0Ht8punmoHU/DuEd3iw9Hr8UwUV+t+VNNuZIWYeY7Q==}
+ '@rollup/rollup-freebsd-x64@4.40.2':
+ resolution: {integrity: sha512-C+AyHBzfpsOEYRFjztcYUFsH4S7UsE9cDtHCtma5BK8+ydOZYgMmWg1d/4KBytQspJCld8ZIujFMAdKG1xyr4Q==}
cpu: [x64]
os: [freebsd]
@@ -601,8 +601,8 @@ packages:
cpu: [arm]
os: [linux]
- '@rollup/rollup-linux-arm-gnueabihf@4.39.0':
- resolution: {integrity: sha512-mz5POx5Zu58f2xAG5RaRRhp3IZDK7zXGk5sdEDj4o96HeaXhlUwmLFzNlc4hCQi5sGdR12VDgEUqVSHer0lI9g==}
+ '@rollup/rollup-linux-arm-gnueabihf@4.40.2':
+ resolution: {integrity: sha512-de6TFZYIvJwRNjmW3+gaXiZ2DaWL5D5yGmSYzkdzjBDS3W+B9JQ48oZEsmMvemqjtAFzE16DIBLqd6IQQRuG9Q==}
cpu: [arm]
os: [linux]
@@ -611,8 +611,8 @@ packages:
cpu: [arm]
os: [linux]
- '@rollup/rollup-linux-arm-musleabihf@4.39.0':
- resolution: {integrity: sha512-+YDwhM6gUAyakl0CD+bMFpdmwIoRDzZYaTWV3SDRBGkMU/VpIBYXXEvkEcTagw/7VVkL2vA29zU4UVy1mP0/Yw==}
+ '@rollup/rollup-linux-arm-musleabihf@4.40.2':
+ resolution: {integrity: sha512-urjaEZubdIkacKc930hUDOfQPysezKla/O9qV+O89enqsqUmQm8Xj8O/vh0gHg4LYfv7Y7UsE3QjzLQzDYN1qg==}
cpu: [arm]
os: [linux]
@@ -621,8 +621,8 @@ packages:
cpu: [arm64]
os: [linux]
- '@rollup/rollup-linux-arm64-gnu@4.39.0':
- resolution: {integrity: sha512-EKf7iF7aK36eEChvlgxGnk7pdJfzfQbNvGV/+l98iiMwU23MwvmV0Ty3pJ0p5WQfm3JRHOytSIqD9LB7Bq7xdQ==}
+ '@rollup/rollup-linux-arm64-gnu@4.40.2':
+ resolution: {integrity: sha512-KlE8IC0HFOC33taNt1zR8qNlBYHj31qGT1UqWqtvR/+NuCVhfufAq9fxO8BMFC22Wu0rxOwGVWxtCMvZVLmhQg==}
cpu: [arm64]
os: [linux]
@@ -631,13 +631,13 @@ packages:
cpu: [arm64]
os: [linux]
- '@rollup/rollup-linux-arm64-musl@4.39.0':
- resolution: {integrity: sha512-vYanR6MtqC7Z2SNr8gzVnzUul09Wi1kZqJaek3KcIlI/wq5Xtq4ZPIZ0Mr/st/sv/NnaPwy/D4yXg5x0B3aUUA==}
+ '@rollup/rollup-linux-arm64-musl@4.40.2':
+ resolution: {integrity: sha512-j8CgxvfM0kbnhu4XgjnCWJQyyBOeBI1Zq91Z850aUddUmPeQvuAy6OiMdPS46gNFgy8gN1xkYyLgwLYZG3rBOg==}
cpu: [arm64]
os: [linux]
- '@rollup/rollup-linux-loongarch64-gnu@4.39.0':
- resolution: {integrity: sha512-NMRUT40+h0FBa5fb+cpxtZoGAggRem16ocVKIv5gDB5uLDgBIwrIsXlGqYbLwW8YyO3WVTk1FkFDjMETYlDqiw==}
+ '@rollup/rollup-linux-loongarch64-gnu@4.40.2':
+ resolution: {integrity: sha512-Ybc/1qUampKuRF4tQXc7G7QY9YRyeVSykfK36Y5Qc5dmrIxwFhrOzqaVTNoZygqZ1ZieSWTibfFhQ5qK8jpWxw==}
cpu: [loong64]
os: [linux]
@@ -646,8 +646,8 @@ packages:
cpu: [ppc64]
os: [linux]
- '@rollup/rollup-linux-powerpc64le-gnu@4.39.0':
- resolution: {integrity: sha512-0pCNnmxgduJ3YRt+D+kJ6Ai/r+TaePu9ZLENl+ZDV/CdVczXl95CbIiwwswu4L+K7uOIGf6tMo2vm8uadRaICQ==}
+ '@rollup/rollup-linux-powerpc64le-gnu@4.40.2':
+ resolution: {integrity: sha512-3FCIrnrt03CCsZqSYAOW/k9n625pjpuMzVfeI+ZBUSDT3MVIFDSPfSUgIl9FqUftxcUXInvFah79hE1c9abD+Q==}
cpu: [ppc64]
os: [linux]
@@ -656,13 +656,13 @@ packages:
cpu: [riscv64]
os: [linux]
- '@rollup/rollup-linux-riscv64-gnu@4.39.0':
- resolution: {integrity: sha512-t7j5Zhr7S4bBtksT73bO6c3Qa2AV/HqiGlj9+KB3gNF5upcVkx+HLgxTm8DK4OkzsOYqbdqbLKwvGMhylJCPhQ==}
+ '@rollup/rollup-linux-riscv64-gnu@4.40.2':
+ resolution: {integrity: sha512-QNU7BFHEvHMp2ESSY3SozIkBPaPBDTsfVNGx3Xhv+TdvWXFGOSH2NJvhD1zKAT6AyuuErJgbdvaJhYVhVqrWTg==}
cpu: [riscv64]
os: [linux]
- '@rollup/rollup-linux-riscv64-musl@4.39.0':
- resolution: {integrity: sha512-m6cwI86IvQ7M93MQ2RF5SP8tUjD39Y7rjb1qjHgYh28uAPVU8+k/xYWvxRO3/tBN2pZkSMa5RjnPuUIbrwVxeA==}
+ '@rollup/rollup-linux-riscv64-musl@4.40.2':
+ resolution: {integrity: sha512-5W6vNYkhgfh7URiXTO1E9a0cy4fSgfE4+Hl5agb/U1sa0kjOLMLC1wObxwKxecE17j0URxuTrYZZME4/VH57Hg==}
cpu: [riscv64]
os: [linux]
@@ -671,8 +671,8 @@ packages:
cpu: [s390x]
os: [linux]
- '@rollup/rollup-linux-s390x-gnu@4.39.0':
- resolution: {integrity: sha512-iRDJd2ebMunnk2rsSBYlsptCyuINvxUfGwOUldjv5M4tpa93K8tFMeYGpNk2+Nxl+OBJnBzy2/JCscGeO507kA==}
+ '@rollup/rollup-linux-s390x-gnu@4.40.2':
+ resolution: {integrity: sha512-B7LKIz+0+p348JoAL4X/YxGx9zOx3sR+o6Hj15Y3aaApNfAshK8+mWZEf759DXfRLeL2vg5LYJBB7DdcleYCoQ==}
cpu: [s390x]
os: [linux]
@@ -681,8 +681,8 @@ packages:
cpu: [x64]
os: [linux]
- '@rollup/rollup-linux-x64-gnu@4.39.0':
- resolution: {integrity: sha512-t9jqYw27R6Lx0XKfEFe5vUeEJ5pF3SGIM6gTfONSMb7DuG6z6wfj2yjcoZxHg129veTqU7+wOhY6GX8wmf90dA==}
+ '@rollup/rollup-linux-x64-gnu@4.40.2':
+ resolution: {integrity: sha512-lG7Xa+BmBNwpjmVUbmyKxdQJ3Q6whHjMjzQplOs5Z+Gj7mxPtWakGHqzMqNER68G67kmCX9qX57aRsW5V0VOng==}
cpu: [x64]
os: [linux]
@@ -691,8 +691,8 @@ packages:
cpu: [x64]
os: [linux]
- '@rollup/rollup-linux-x64-musl@4.39.0':
- resolution: {integrity: sha512-ThFdkrFDP55AIsIZDKSBWEt/JcWlCzydbZHinZ0F/r1h83qbGeenCt/G/wG2O0reuENDD2tawfAj2s8VK7Bugg==}
+ '@rollup/rollup-linux-x64-musl@4.40.2':
+ resolution: {integrity: sha512-tD46wKHd+KJvsmije4bUskNuvWKFcTOIM9tZ/RrmIvcXnbi0YK/cKS9FzFtAm7Oxi2EhV5N2OpfFB348vSQRXA==}
cpu: [x64]
os: [linux]
@@ -701,8 +701,8 @@ packages:
cpu: [arm64]
os: [win32]
- '@rollup/rollup-win32-arm64-msvc@4.39.0':
- resolution: {integrity: sha512-jDrLm6yUtbOg2TYB3sBF3acUnAwsIksEYjLeHL+TJv9jg+TmTwdyjnDex27jqEMakNKf3RwwPahDIt7QXCSqRQ==}
+ '@rollup/rollup-win32-arm64-msvc@4.40.2':
+ resolution: {integrity: sha512-Bjv/HG8RRWLNkXwQQemdsWw4Mg+IJ29LK+bJPW2SCzPKOUaMmPEppQlu/Fqk1d7+DX3V7JbFdbkh/NMmurT6Pg==}
cpu: [arm64]
os: [win32]
@@ -711,8 +711,8 @@ packages:
cpu: [ia32]
os: [win32]
- '@rollup/rollup-win32-ia32-msvc@4.39.0':
- resolution: {integrity: sha512-6w9uMuza+LbLCVoNKL5FSLE7yvYkq9laSd09bwS0tMjkwXrmib/4KmoJcrKhLWHvw19mwU+33ndC69T7weNNjQ==}
+ '@rollup/rollup-win32-ia32-msvc@4.40.2':
+ resolution: {integrity: sha512-dt1llVSGEsGKvzeIO76HToiYPNPYPkmjhMHhP00T9S4rDern8P2ZWvWAQUEJ+R1UdMWJ/42i/QqJ2WV765GZcA==}
cpu: [ia32]
os: [win32]
@@ -721,8 +721,8 @@ packages:
cpu: [x64]
os: [win32]
- '@rollup/rollup-win32-x64-msvc@4.39.0':
- resolution: {integrity: sha512-yAkUOkIKZlK5dl7u6dg897doBgLXmUHhIINM2c+sND3DZwnrdQkkSiDh7N75Ll4mM4dxSkYfXqU9fW3lLkMFug==}
+ '@rollup/rollup-win32-x64-msvc@4.40.2':
+ resolution: {integrity: sha512-bwspbWB04XJpeElvsp+DCylKfF4trJDa2Y9Go8O6A7YLX2LIKGcNK/CYImJN6ZP4DcuOHB4Utl3iCbnR62DudA==}
cpu: [x64]
os: [win32]
@@ -816,8 +816,8 @@ packages:
resolution: {integrity: sha512-E0ntLvsfPqnPwng8b8y4OGuzh/iIOm2z8U3S9zic2TeMLW61u5IH2Q1wu0oSTkfrSzwbDJIB/Lm8O3//8BWMPA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
- '@typescript-eslint/scope-manager@8.29.1':
- resolution: {integrity: sha512-2nggXGX5F3YrsGN08pw4XpMLO1Rgtnn4AzTegC2MDesv6q3QaTU5yU7IbS1tf1IwCR0Hv/1EFygLn9ms6LIpDA==}
+ '@typescript-eslint/scope-manager@8.32.1':
+ resolution: {integrity: sha512-7IsIaIDeZn7kffk7qXC3o6Z4UblZJKV3UBpkvRNpr5NSyLji7tvTcvmnMNYuYLyh26mN8W723xpo3i4MlD33vA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@typescript-eslint/type-utils@8.26.0':
@@ -831,8 +831,8 @@ packages:
resolution: {integrity: sha512-89B1eP3tnpr9A8L6PZlSjBvnJhWXtYfZhECqlBl1D9Lme9mHO6iWlsprBtVenQvY1HMhax1mWOjhtL3fh/u+pA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
- '@typescript-eslint/types@8.29.1':
- resolution: {integrity: sha512-VT7T1PuJF1hpYC3AGm2rCgJBjHL3nc+A/bhOp9sGMKfi5v0WufsX/sHCFBfNTx2F+zA6qBc/PD0/kLRLjdt8mQ==}
+ '@typescript-eslint/types@8.32.1':
+ resolution: {integrity: sha512-YmybwXUJcgGqgAp6bEsgpPXEg6dcCyPyCSr0CAAueacR/CCBi25G3V8gGQ2kRzQRBNol7VQknxMs9HvVa9Rvfg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@typescript-eslint/typescript-estree@8.26.0':
@@ -841,8 +841,8 @@ packages:
peerDependencies:
typescript: '>=4.8.4 <5.9.0'
- '@typescript-eslint/typescript-estree@8.29.1':
- resolution: {integrity: sha512-l1enRoSaUkQxOQnbi0KPUtqeZkSiFlqrx9/3ns2rEDhGKfTa+88RmXqedC1zmVTOWrLc2e6DEJrTA51C9iLH5g==}
+ '@typescript-eslint/typescript-estree@8.32.1':
+ resolution: {integrity: sha512-Y3AP9EIfYwBb4kWGb+simvPaqQoT5oJuzzj9m0i6FCY6SPvlomY2Ei4UEMm7+FXtlNJbor80ximyslzaQF6xhg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
typescript: '>=4.8.4 <5.9.0'
@@ -854,8 +854,8 @@ packages:
eslint: ^8.57.0 || ^9.0.0
typescript: '>=4.8.4 <5.9.0'
- '@typescript-eslint/utils@8.29.1':
- resolution: {integrity: sha512-QAkFEbytSaB8wnmB+DflhUPz6CLbFWE2SnSCrRMEa+KnXIzDYbpsn++1HGvnfAsUY44doDXmvRkO5shlM/3UfA==}
+ '@typescript-eslint/utils@8.32.1':
+ resolution: {integrity: sha512-DsSFNIgLSrc89gpq1LJB7Hm1YpuhK086DRDJSNrewcGvYloWW1vZLHBTIvarKZDcAORIy/uWNx8Gad+4oMpkSA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
eslint: ^8.57.0 || ^9.0.0
@@ -865,8 +865,8 @@ packages:
resolution: {integrity: sha512-2z8JQJWAzPdDd51dRQ/oqIJxe99/hoLIqmf8RMCAJQtYDc535W/Jt2+RTP4bP0aKeBG1F65yjIZuczOXCmbWwg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
- '@typescript-eslint/visitor-keys@8.29.1':
- resolution: {integrity: sha512-RGLh5CRaUEf02viP5c1Vh1cMGffQscyHe7HPAzGpfmfflFg1wUz2rYxd+OZqwpeypYvZ8UxSxuIpF++fmOzEcg==}
+ '@typescript-eslint/visitor-keys@8.32.1':
+ resolution: {integrity: sha512-ar0tjQfObzhSaW3C3QNmTc5ofj0hDoNQ5XWrCy6zDyabdr0TWhCkClp+rywGNj/odAFBVzzJrK4tEq5M4Hmu4w==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@vitest/coverage-v8@2.0.5':
@@ -1974,8 +1974,8 @@ packages:
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
hasBin: true
- rollup@4.39.0:
- resolution: {integrity: sha512-thI8kNc02yNvnmJp8dr3fNWJ9tCONDhp6TV35X6HkKGGs9E6q7YWCHbe5vKiTa7TAiNcFEmXKj3X/pG2b3ci0g==}
+ rollup@4.40.2:
+ resolution: {integrity: sha512-tfUOg6DTP4rhQ3VjOO6B4wyrJnGOX85requAXvqYTHsOgb2TFJdZ3aWpT8W2kPoypSGP7dZUyzxJ9ee4buM5Fg==}
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
hasBin: true
@@ -2013,6 +2013,11 @@ packages:
engines: {node: '>=10'}
hasBin: true
+ semver@7.7.2:
+ resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==}
+ engines: {node: '>=10'}
+ hasBin: true
+
serialize-javascript@6.0.2:
resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==}
@@ -2310,8 +2315,8 @@ packages:
terser:
optional: true
- vite@5.4.18:
- resolution: {integrity: sha512-1oDcnEp3lVyHCuQ2YFelM4Alm2o91xNoMncRm1U7S+JdYfYOvbiGZ3/CxGttrOu2M/KcGz7cRC2DoNUA6urmMA==}
+ vite@5.4.19:
+ resolution: {integrity: sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA==}
engines: {node: ^18.0.0 || >=20.0.0}
hasBin: true
peerDependencies:
@@ -2711,7 +2716,7 @@ snapshots:
eslint: 9.9.1
eslint-visitor-keys: 3.4.3
- '@eslint-community/eslint-utils@4.5.1(eslint@9.9.1)':
+ '@eslint-community/eslint-utils@4.7.0(eslint@9.9.1)':
dependencies:
eslint: 9.9.1
eslint-visitor-keys: 3.4.3
@@ -2860,120 +2865,120 @@ snapshots:
optionalDependencies:
rollup: 4.22.4
- '@rollup/pluginutils@5.1.0(rollup@4.39.0)':
+ '@rollup/pluginutils@5.1.0(rollup@4.40.2)':
dependencies:
'@types/estree': 1.0.6
estree-walker: 2.0.2
picomatch: 2.3.1
optionalDependencies:
- rollup: 4.39.0
+ rollup: 4.40.2
'@rollup/rollup-android-arm-eabi@4.22.4':
optional: true
- '@rollup/rollup-android-arm-eabi@4.39.0':
+ '@rollup/rollup-android-arm-eabi@4.40.2':
optional: true
'@rollup/rollup-android-arm64@4.22.4':
optional: true
- '@rollup/rollup-android-arm64@4.39.0':
+ '@rollup/rollup-android-arm64@4.40.2':
optional: true
'@rollup/rollup-darwin-arm64@4.22.4':
optional: true
- '@rollup/rollup-darwin-arm64@4.39.0':
+ '@rollup/rollup-darwin-arm64@4.40.2':
optional: true
'@rollup/rollup-darwin-x64@4.22.4':
optional: true
- '@rollup/rollup-darwin-x64@4.39.0':
+ '@rollup/rollup-darwin-x64@4.40.2':
optional: true
- '@rollup/rollup-freebsd-arm64@4.39.0':
+ '@rollup/rollup-freebsd-arm64@4.40.2':
optional: true
- '@rollup/rollup-freebsd-x64@4.39.0':
+ '@rollup/rollup-freebsd-x64@4.40.2':
optional: true
'@rollup/rollup-linux-arm-gnueabihf@4.22.4':
optional: true
- '@rollup/rollup-linux-arm-gnueabihf@4.39.0':
+ '@rollup/rollup-linux-arm-gnueabihf@4.40.2':
optional: true
'@rollup/rollup-linux-arm-musleabihf@4.22.4':
optional: true
- '@rollup/rollup-linux-arm-musleabihf@4.39.0':
+ '@rollup/rollup-linux-arm-musleabihf@4.40.2':
optional: true
'@rollup/rollup-linux-arm64-gnu@4.22.4':
optional: true
- '@rollup/rollup-linux-arm64-gnu@4.39.0':
+ '@rollup/rollup-linux-arm64-gnu@4.40.2':
optional: true
'@rollup/rollup-linux-arm64-musl@4.22.4':
optional: true
- '@rollup/rollup-linux-arm64-musl@4.39.0':
+ '@rollup/rollup-linux-arm64-musl@4.40.2':
optional: true
- '@rollup/rollup-linux-loongarch64-gnu@4.39.0':
+ '@rollup/rollup-linux-loongarch64-gnu@4.40.2':
optional: true
'@rollup/rollup-linux-powerpc64le-gnu@4.22.4':
optional: true
- '@rollup/rollup-linux-powerpc64le-gnu@4.39.0':
+ '@rollup/rollup-linux-powerpc64le-gnu@4.40.2':
optional: true
'@rollup/rollup-linux-riscv64-gnu@4.22.4':
optional: true
- '@rollup/rollup-linux-riscv64-gnu@4.39.0':
+ '@rollup/rollup-linux-riscv64-gnu@4.40.2':
optional: true
- '@rollup/rollup-linux-riscv64-musl@4.39.0':
+ '@rollup/rollup-linux-riscv64-musl@4.40.2':
optional: true
'@rollup/rollup-linux-s390x-gnu@4.22.4':
optional: true
- '@rollup/rollup-linux-s390x-gnu@4.39.0':
+ '@rollup/rollup-linux-s390x-gnu@4.40.2':
optional: true
'@rollup/rollup-linux-x64-gnu@4.22.4':
optional: true
- '@rollup/rollup-linux-x64-gnu@4.39.0':
+ '@rollup/rollup-linux-x64-gnu@4.40.2':
optional: true
'@rollup/rollup-linux-x64-musl@4.22.4':
optional: true
- '@rollup/rollup-linux-x64-musl@4.39.0':
+ '@rollup/rollup-linux-x64-musl@4.40.2':
optional: true
'@rollup/rollup-win32-arm64-msvc@4.22.4':
optional: true
- '@rollup/rollup-win32-arm64-msvc@4.39.0':
+ '@rollup/rollup-win32-arm64-msvc@4.40.2':
optional: true
'@rollup/rollup-win32-ia32-msvc@4.22.4':
optional: true
- '@rollup/rollup-win32-ia32-msvc@4.39.0':
+ '@rollup/rollup-win32-ia32-msvc@4.40.2':
optional: true
'@rollup/rollup-win32-x64-msvc@4.22.4':
optional: true
- '@rollup/rollup-win32-x64-msvc@4.39.0':
+ '@rollup/rollup-win32-x64-msvc@4.40.2':
optional: true
'@stylistic/eslint-plugin-js@1.8.0(eslint@9.9.1)':
@@ -3000,25 +3005,25 @@ snapshots:
typescript: 5.5.4
typescript-eslint: 8.26.0(eslint@9.9.1)(typescript@5.5.4)
- '@sveltejs/vite-plugin-svelte-inspector@3.0.0-next.2(@sveltejs/vite-plugin-svelte@4.0.0-next.6(svelte@packages+svelte)(vite@5.4.18(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)))(svelte@packages+svelte)(vite@5.4.18(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0))':
+ '@sveltejs/vite-plugin-svelte-inspector@3.0.0-next.2(@sveltejs/vite-plugin-svelte@4.0.0-next.6(svelte@packages+svelte)(vite@5.4.19(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)))(svelte@packages+svelte)(vite@5.4.19(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0))':
dependencies:
- '@sveltejs/vite-plugin-svelte': 4.0.0-next.6(svelte@packages+svelte)(vite@5.4.18(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0))
+ '@sveltejs/vite-plugin-svelte': 4.0.0-next.6(svelte@packages+svelte)(vite@5.4.19(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0))
debug: 4.4.0
svelte: link:packages/svelte
- vite: 5.4.18(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)
+ vite: 5.4.19(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)
transitivePeerDependencies:
- supports-color
- '@sveltejs/vite-plugin-svelte@4.0.0-next.6(svelte@packages+svelte)(vite@5.4.18(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0))':
+ '@sveltejs/vite-plugin-svelte@4.0.0-next.6(svelte@packages+svelte)(vite@5.4.19(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0))':
dependencies:
- '@sveltejs/vite-plugin-svelte-inspector': 3.0.0-next.2(@sveltejs/vite-plugin-svelte@4.0.0-next.6(svelte@packages+svelte)(vite@5.4.18(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)))(svelte@packages+svelte)(vite@5.4.18(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0))
+ '@sveltejs/vite-plugin-svelte-inspector': 3.0.0-next.2(@sveltejs/vite-plugin-svelte@4.0.0-next.6(svelte@packages+svelte)(vite@5.4.19(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)))(svelte@packages+svelte)(vite@5.4.19(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0))
debug: 4.4.0
deepmerge: 4.3.1
kleur: 4.1.5
magic-string: 0.30.17
svelte: link:packages/svelte
- vite: 5.4.18(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)
- vitefu: 0.2.5(vite@5.4.18(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0))
+ vite: 5.4.19(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)
+ vitefu: 0.2.5(vite@5.4.19(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0))
transitivePeerDependencies:
- supports-color
@@ -3088,10 +3093,10 @@ snapshots:
'@typescript-eslint/types': 8.26.0
'@typescript-eslint/visitor-keys': 8.26.0
- '@typescript-eslint/scope-manager@8.29.1':
+ '@typescript-eslint/scope-manager@8.32.1':
dependencies:
- '@typescript-eslint/types': 8.29.1
- '@typescript-eslint/visitor-keys': 8.29.1
+ '@typescript-eslint/types': 8.32.1
+ '@typescript-eslint/visitor-keys': 8.32.1
'@typescript-eslint/type-utils@8.26.0(eslint@9.9.1)(typescript@5.5.4)':
dependencies:
@@ -3106,7 +3111,7 @@ snapshots:
'@typescript-eslint/types@8.26.0': {}
- '@typescript-eslint/types@8.29.1': {}
+ '@typescript-eslint/types@8.32.1': {}
'@typescript-eslint/typescript-estree@8.26.0(typescript@5.5.4)':
dependencies:
@@ -3122,15 +3127,15 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/typescript-estree@8.29.1(typescript@5.5.4)':
+ '@typescript-eslint/typescript-estree@8.32.1(typescript@5.5.4)':
dependencies:
- '@typescript-eslint/types': 8.29.1
- '@typescript-eslint/visitor-keys': 8.29.1
+ '@typescript-eslint/types': 8.32.1
+ '@typescript-eslint/visitor-keys': 8.32.1
debug: 4.4.0
fast-glob: 3.3.3
is-glob: 4.0.3
minimatch: 9.0.5
- semver: 7.7.1
+ semver: 7.7.2
ts-api-utils: 2.1.0(typescript@5.5.4)
typescript: 5.5.4
transitivePeerDependencies:
@@ -3147,12 +3152,12 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/utils@8.29.1(eslint@9.9.1)(typescript@5.5.4)':
+ '@typescript-eslint/utils@8.32.1(eslint@9.9.1)(typescript@5.5.4)':
dependencies:
- '@eslint-community/eslint-utils': 4.5.1(eslint@9.9.1)
- '@typescript-eslint/scope-manager': 8.29.1
- '@typescript-eslint/types': 8.29.1
- '@typescript-eslint/typescript-estree': 8.29.1(typescript@5.5.4)
+ '@eslint-community/eslint-utils': 4.7.0(eslint@9.9.1)
+ '@typescript-eslint/scope-manager': 8.32.1
+ '@typescript-eslint/types': 8.32.1
+ '@typescript-eslint/typescript-estree': 8.32.1(typescript@5.5.4)
eslint: 9.9.1
typescript: 5.5.4
transitivePeerDependencies:
@@ -3163,9 +3168,9 @@ snapshots:
'@typescript-eslint/types': 8.26.0
eslint-visitor-keys: 4.2.0
- '@typescript-eslint/visitor-keys@8.29.1':
+ '@typescript-eslint/visitor-keys@8.32.1':
dependencies:
- '@typescript-eslint/types': 8.29.1
+ '@typescript-eslint/types': 8.32.1
eslint-visitor-keys: 4.2.0
'@vitest/coverage-v8@2.0.5(vitest@2.1.9(@types/node@20.12.7)(jsdom@25.0.1)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0))':
@@ -3491,7 +3496,7 @@ snapshots:
eslint-compat-utils@0.5.1(eslint@9.9.1):
dependencies:
eslint: 9.9.1
- semver: 7.7.1
+ semver: 7.7.2
eslint-config-prettier@9.1.0(eslint@9.9.1):
dependencies:
@@ -3499,7 +3504,7 @@ snapshots:
eslint-plugin-es-x@7.8.0(eslint@9.9.1):
dependencies:
- '@eslint-community/eslint-utils': 4.5.1(eslint@9.9.1)
+ '@eslint-community/eslint-utils': 4.7.0(eslint@9.9.1)
'@eslint-community/regexpp': 4.12.1
eslint: 9.9.1
eslint-compat-utils: 0.5.1(eslint@9.9.1)
@@ -3508,8 +3513,8 @@ snapshots:
eslint-plugin-n@17.16.1(eslint@9.9.1)(typescript@5.5.4):
dependencies:
- '@eslint-community/eslint-utils': 4.5.1(eslint@9.9.1)
- '@typescript-eslint/utils': 8.29.1(eslint@9.9.1)(typescript@5.5.4)
+ '@eslint-community/eslint-utils': 4.7.0(eslint@9.9.1)
+ '@typescript-eslint/utils': 8.32.1(eslint@9.9.1)(typescript@5.5.4)
enhanced-resolve: 5.18.1
eslint: 9.9.1
eslint-plugin-es-x: 7.8.0(eslint@9.9.1)
@@ -3517,7 +3522,7 @@ snapshots:
globals: 15.15.0
ignore: 5.3.2
minimatch: 9.0.5
- semver: 7.7.1
+ semver: 7.7.2
ts-declaration-location: 1.0.7(typescript@5.5.4)
transitivePeerDependencies:
- supports-color
@@ -3525,7 +3530,7 @@ snapshots:
eslint-plugin-svelte@2.38.0(eslint@9.9.1)(svelte@packages+svelte):
dependencies:
- '@eslint-community/eslint-utils': 4.5.1(eslint@9.9.1)
+ '@eslint-community/eslint-utils': 4.7.0(eslint@9.9.1)
'@jridgewell/sourcemap-codec': 1.5.0
debug: 4.4.0
eslint: 9.9.1
@@ -3536,7 +3541,7 @@ snapshots:
postcss-load-config: 3.1.4(postcss@8.5.3)
postcss-safe-parser: 6.0.0(postcss@8.5.3)
postcss-selector-parser: 6.1.2
- semver: 7.7.1
+ semver: 7.7.2
svelte-eslint-parser: 0.43.0(svelte@packages+svelte)
optionalDependencies:
svelte: link:packages/svelte
@@ -4292,30 +4297,30 @@ snapshots:
'@rollup/rollup-win32-x64-msvc': 4.22.4
fsevents: 2.3.3
- rollup@4.39.0:
+ rollup@4.40.2:
dependencies:
'@types/estree': 1.0.7
optionalDependencies:
- '@rollup/rollup-android-arm-eabi': 4.39.0
- '@rollup/rollup-android-arm64': 4.39.0
- '@rollup/rollup-darwin-arm64': 4.39.0
- '@rollup/rollup-darwin-x64': 4.39.0
- '@rollup/rollup-freebsd-arm64': 4.39.0
- '@rollup/rollup-freebsd-x64': 4.39.0
- '@rollup/rollup-linux-arm-gnueabihf': 4.39.0
- '@rollup/rollup-linux-arm-musleabihf': 4.39.0
- '@rollup/rollup-linux-arm64-gnu': 4.39.0
- '@rollup/rollup-linux-arm64-musl': 4.39.0
- '@rollup/rollup-linux-loongarch64-gnu': 4.39.0
- '@rollup/rollup-linux-powerpc64le-gnu': 4.39.0
- '@rollup/rollup-linux-riscv64-gnu': 4.39.0
- '@rollup/rollup-linux-riscv64-musl': 4.39.0
- '@rollup/rollup-linux-s390x-gnu': 4.39.0
- '@rollup/rollup-linux-x64-gnu': 4.39.0
- '@rollup/rollup-linux-x64-musl': 4.39.0
- '@rollup/rollup-win32-arm64-msvc': 4.39.0
- '@rollup/rollup-win32-ia32-msvc': 4.39.0
- '@rollup/rollup-win32-x64-msvc': 4.39.0
+ '@rollup/rollup-android-arm-eabi': 4.40.2
+ '@rollup/rollup-android-arm64': 4.40.2
+ '@rollup/rollup-darwin-arm64': 4.40.2
+ '@rollup/rollup-darwin-x64': 4.40.2
+ '@rollup/rollup-freebsd-arm64': 4.40.2
+ '@rollup/rollup-freebsd-x64': 4.40.2
+ '@rollup/rollup-linux-arm-gnueabihf': 4.40.2
+ '@rollup/rollup-linux-arm-musleabihf': 4.40.2
+ '@rollup/rollup-linux-arm64-gnu': 4.40.2
+ '@rollup/rollup-linux-arm64-musl': 4.40.2
+ '@rollup/rollup-linux-loongarch64-gnu': 4.40.2
+ '@rollup/rollup-linux-powerpc64le-gnu': 4.40.2
+ '@rollup/rollup-linux-riscv64-gnu': 4.40.2
+ '@rollup/rollup-linux-riscv64-musl': 4.40.2
+ '@rollup/rollup-linux-s390x-gnu': 4.40.2
+ '@rollup/rollup-linux-x64-gnu': 4.40.2
+ '@rollup/rollup-linux-x64-musl': 4.40.2
+ '@rollup/rollup-win32-arm64-msvc': 4.40.2
+ '@rollup/rollup-win32-ia32-msvc': 4.40.2
+ '@rollup/rollup-win32-x64-msvc': 4.40.2
fsevents: 2.3.3
rrweb-cssom@0.7.1: {}
@@ -4347,6 +4352,8 @@ snapshots:
semver@7.7.1: {}
+ semver@7.7.2: {}
+
serialize-javascript@6.0.2:
dependencies:
randombytes: 2.1.0
@@ -4562,7 +4569,7 @@ snapshots:
debug: 4.4.0
es-module-lexer: 1.6.0
pathe: 1.1.2
- vite: 5.4.18(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)
+ vite: 5.4.19(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)
transitivePeerDependencies:
- '@types/node'
- less
@@ -4574,10 +4581,10 @@ snapshots:
- supports-color
- terser
- vite-plugin-inspect@0.8.4(rollup@4.39.0)(vite@5.4.18(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)):
+ vite-plugin-inspect@0.8.4(rollup@4.40.2)(vite@5.4.19(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)):
dependencies:
'@antfu/utils': 0.7.8
- '@rollup/pluginutils': 5.1.0(rollup@4.39.0)
+ '@rollup/pluginutils': 5.1.0(rollup@4.40.2)
debug: 4.4.0
error-stack-parser-es: 0.1.1
fs-extra: 11.2.0
@@ -4585,7 +4592,7 @@ snapshots:
perfect-debounce: 1.0.0
picocolors: 1.1.1
sirv: 2.0.4
- vite: 5.4.18(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)
+ vite: 5.4.19(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)
transitivePeerDependencies:
- rollup
- supports-color
@@ -4602,11 +4609,11 @@ snapshots:
sass: 1.70.0
terser: 5.27.0
- vite@5.4.18(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0):
+ vite@5.4.19(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0):
dependencies:
esbuild: 0.21.5
postcss: 8.5.3
- rollup: 4.39.0
+ rollup: 4.40.2
optionalDependencies:
'@types/node': 20.12.7
fsevents: 2.3.3
@@ -4614,9 +4621,9 @@ snapshots:
sass: 1.70.0
terser: 5.27.0
- vitefu@0.2.5(vite@5.4.18(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)):
+ vitefu@0.2.5(vite@5.4.19(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)):
optionalDependencies:
- vite: 5.4.18(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)
+ vite: 5.4.19(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)
vitest@2.1.9(@types/node@20.12.7)(jsdom@25.0.1)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0):
dependencies: