From f498a21063894e6e515e62d753396410624b2e0f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 23 Mar 2025 09:59:21 -0400 Subject: [PATCH 01/48] Version Packages (#15587) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .changeset/nice-pianos-punch.md | 5 ----- packages/svelte/CHANGELOG.md | 6 ++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) delete mode 100644 .changeset/nice-pianos-punch.md diff --git a/.changeset/nice-pianos-punch.md b/.changeset/nice-pianos-punch.md deleted file mode 100644 index f70af9eb04..0000000000 --- a/.changeset/nice-pianos-punch.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: prevent state runes from being called with spread diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 86e1179029..e6b5de7e5f 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,11 @@ # svelte +## 5.25.3 + +### Patch Changes + +- fix: prevent state runes from being called with spread ([#15585](https://github.com/sveltejs/svelte/pull/15585)) + ## 5.25.2 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 56fa949391..0410520634 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.25.2", + "version": "5.25.3", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index e2dc6c2c09..e4ae33a9b1 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.25.2'; +export const VERSION = '5.25.3'; export const PUBLIC_VERSION = '5'; From f49856449dbabb0815066485b23bf404c9c789ff Mon Sep 17 00:00:00 2001 From: Scott Wu Date: Fri, 28 Mar 2025 23:53:10 +0800 Subject: [PATCH 02/48] docs: add a reference to the official hash router (#15611) --- documentation/docs/07-misc/99-faq.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/docs/07-misc/99-faq.md b/documentation/docs/07-misc/99-faq.md index 7e25cdab55..ed5c6277c0 100644 --- a/documentation/docs/07-misc/99-faq.md +++ b/documentation/docs/07-misc/99-faq.md @@ -96,7 +96,7 @@ However, you can use any router library. A lot of people use [page.js](https://g If you prefer a declarative HTML approach, there's the isomorphic [svelte-routing](https://github.com/EmilTholin/svelte-routing) library and a fork of it called [svelte-navigator](https://github.com/mefechoel/svelte-navigator) containing some additional functionality. -If you need hash-based routing on the client side, check out [svelte-spa-router](https://github.com/ItalyPaleAle/svelte-spa-router) or [abstract-state-router](https://github.com/TehShrike/abstract-state-router/). +If you need hash-based routing on the client side, check out the [hash option](https://svelte.dev/docs/kit/configuration#router) in SvelteKit, [svelte-spa-router](https://github.com/ItalyPaleAle/svelte-spa-router), or [abstract-state-router](https://github.com/TehShrike/abstract-state-router/). [Routify](https://routify.dev) is another filesystem-based router, similar to SvelteKit's router. Version 3 supports Svelte's native SSR. From 04257925d22d8ecef37f50330ac258c7f97dca0c Mon Sep 17 00:00:00 2001 From: Ben McCann <322311+benmccann@users.noreply.github.com> Date: Sat, 29 Mar 2025 01:42:18 -0700 Subject: [PATCH 03/48] docs: clarify what you can build with SvelteKit (#15461) * docs: clarify what you can build with SvelteKit * try relative URLs * Update documentation/docs/01-introduction/02-getting-started.md Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com> --------- Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com> --- documentation/docs/01-introduction/02-getting-started.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/documentation/docs/01-introduction/02-getting-started.md b/documentation/docs/01-introduction/02-getting-started.md index e035e6d6df..c7351729ff 100644 --- a/documentation/docs/01-introduction/02-getting-started.md +++ b/documentation/docs/01-introduction/02-getting-started.md @@ -2,7 +2,7 @@ title: Getting started --- -We recommend using [SvelteKit](../kit), the official application framework from the Svelte team powered by [Vite](https://vite.dev/): +We recommend using [SvelteKit](../kit), which lets you [build almost anything](../kit/project-types). It's the official application framework from the Svelte team and powered by [Vite](https://vite.dev/). Create a new project with: ```bash npx sv create myapp @@ -15,7 +15,9 @@ Don't worry if you don't know Svelte yet! You can ignore all the nice features S ## Alternatives to SvelteKit -You can also use Svelte directly with Vite by running `npm create vite@latest` and selecting the `svelte` option. With this, `npm run build` will generate HTML, JS and CSS files inside the `dist` directory using [vite-plugin-svelte](https://github.com/sveltejs/vite-plugin-svelte). In most cases, you will probably need to [choose a routing library](faq#Is-there-a-router) as well. +You can also use Svelte directly with Vite by running `npm create vite@latest` and selecting the `svelte` option. With this, `npm run build` will generate HTML, JS, and CSS files inside the `dist` directory using [vite-plugin-svelte](https://github.com/sveltejs/vite-plugin-svelte). In most cases, you will probably need to [choose a routing library](faq#Is-there-a-router) as well. + +>[!NOTE] Vite is often used in standalone mode to build [single page apps (SPAs)](../kit/glossary#SPA), which you can also [build with SvelteKit](../kit/single-page-apps). There are also plugins for [Rollup](https://github.com/sveltejs/rollup-plugin-svelte), [Webpack](https://github.com/sveltejs/svelte-loader) [and a few others](https://sveltesociety.dev/packages?category=build-plugins), but we recommend Vite. From 6f8068637c6f18649d17687c588b06381318d578 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Mon, 31 Mar 2025 16:48:42 +0200 Subject: [PATCH 04/48] fix: support TS type assertions (#15642) fixes #15565 --- .changeset/lovely-windows-hang.md | 5 +++++ .../src/compiler/phases/1-parse/remove_typescript_nodes.js | 3 +++ .../tests/runtime-runes/samples/typescript/main.svelte | 1 + 3 files changed, 9 insertions(+) create mode 100644 .changeset/lovely-windows-hang.md diff --git a/.changeset/lovely-windows-hang.md b/.changeset/lovely-windows-hang.md new file mode 100644 index 0000000000..406e6c4961 --- /dev/null +++ b/.changeset/lovely-windows-hang.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: support TS type assertions diff --git a/packages/svelte/src/compiler/phases/1-parse/remove_typescript_nodes.js b/packages/svelte/src/compiler/phases/1-parse/remove_typescript_nodes.js index 09eb0bfa68..37dc0e17a1 100644 --- a/packages/svelte/src/compiler/phases/1-parse/remove_typescript_nodes.js +++ b/packages/svelte/src/compiler/phases/1-parse/remove_typescript_nodes.js @@ -94,6 +94,9 @@ const visitors = { TSTypeAliasDeclaration() { return b.empty; }, + TSTypeAssertion(node, context) { + return context.visit(node.expression); + }, TSEnumDeclaration(node) { e.typescript_invalid_feature(node, 'enums'); }, diff --git a/packages/svelte/tests/runtime-runes/samples/typescript/main.svelte b/packages/svelte/tests/runtime-runes/samples/typescript/main.svelte index e2942b21f3..d2a9da5439 100644 --- a/packages/svelte/tests/runtime-runes/samples/typescript/main.svelte +++ b/packages/svelte/tests/runtime-runes/samples/typescript/main.svelte @@ -45,6 +45,7 @@ export type { Hello }; const TypedFoo = Foo; + const typeAssertion = true; `, + html: ``, compileOptions: { dev: true @@ -34,8 +34,8 @@ export default test({ btn?.click(); }); - assert.htmlEqual(target.innerHTML, ``); + assert.htmlEqual(target.innerHTML, ``); - assert.deepEqual(warnings, []); + assert.deepEqual(warnings, [], 'expected getContext to have widened ownership'); } }); diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-1/main.svelte b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-1/main.svelte new file mode 100644 index 0000000000..2dd7cab141 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-1/main.svelte @@ -0,0 +1,9 @@ + + + diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-3/sub.svelte b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-1/sub.svelte similarity index 100% rename from packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-3/sub.svelte rename to packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-1/sub.svelte diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-2/_config.js b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-2/_config.js index c07b9ce129..66f1726a2a 100644 --- a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-2/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-2/_config.js @@ -1,41 +1,24 @@ import { flushSync } from 'svelte'; import { test } from '../../test'; -/** @type {typeof console.warn} */ -let warn; - -/** @type {any[]} */ -let warnings = []; - export default test({ - html: ``, - compileOptions: { dev: true }, - before_test: () => { - warn = console.warn; - - console.warn = (...args) => { - warnings.push(...args); - }; - }, + test({ assert, target, warnings }) { + const [btn1, btn2] = target.querySelectorAll('button'); - after_test: () => { - console.warn = warn; - warnings = []; - }, + flushSync(() => { + btn1.click(); + }); - test({ assert, target }) { - const btn = target.querySelector('button'); + assert.deepEqual(warnings.length, 0); flushSync(() => { - btn?.click(); + btn2.click(); }); - assert.htmlEqual(target.innerHTML, ``); - - assert.deepEqual(warnings, []); + assert.deepEqual(warnings.length, 1); } }); diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-2/main.svelte b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-2/main.svelte index ad450a937e..0be7e434e4 100644 --- a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-2/main.svelte +++ b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-2/main.svelte @@ -1,9 +1,8 @@ - - + diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-2/state.svelte.js b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-2/state.svelte.js index 3e7a68cf97..2906b9bce5 100644 --- a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-2/state.svelte.js +++ b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-2/state.svelte.js @@ -1 +1,14 @@ -export let global = $state({}); +export function create_my_state() { + const my_state = $state({ + a: 0 + }); + + function inc() { + my_state.a++; + } + + return { + my_state, + inc + }; +} diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-5/sub.svelte b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-2/sub.svelte similarity index 100% rename from packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-5/sub.svelte rename to packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-2/sub.svelte diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-7/Child.svelte b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-3/Child.svelte similarity index 100% rename from packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-7/Child.svelte rename to packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-3/Child.svelte diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-3/_config.js b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-3/_config.js index 96b18d1854..ab7327ab8b 100644 --- a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-3/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-3/_config.js @@ -1,41 +1,24 @@ import { flushSync } from 'svelte'; import { test } from '../../test'; -/** @type {typeof console.warn} */ -let warn; - -/** @type {any[]} */ -let warnings = []; - export default test({ - html: ``, - compileOptions: { dev: true }, - before_test: () => { - warn = console.warn; - - console.warn = (...args) => { - warnings.push(...args); - }; - }, + async test({ assert, target, warnings }) { + const [btn1, btn2] = target.querySelectorAll('button'); - after_test: () => { - console.warn = warn; - warnings = []; - }, + flushSync(() => { + btn1.click(); + }); - test({ assert, target }) { - const btn = target.querySelector('button'); + assert.deepEqual(warnings.length, 0); flushSync(() => { - btn?.click(); + btn2.click(); }); - assert.htmlEqual(target.innerHTML, ``); - - assert.deepEqual(warnings, [], 'expected getContext to have widened ownership'); + assert.deepEqual(warnings.length, 1); } }); diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-3/main.svelte b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-3/main.svelte index 2dd7cab141..8e8343790b 100644 --- a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-3/main.svelte +++ b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-3/main.svelte @@ -1,9 +1,13 @@ - + + diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-4/_config.js b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-4/_config.js deleted file mode 100644 index aeb3740dfe..0000000000 --- a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-4/_config.js +++ /dev/null @@ -1,37 +0,0 @@ -import { flushSync } from 'svelte'; -import { test } from '../../test'; - -/** @type {typeof console.warn} */ -let warn; - -/** @type {any[]} */ -let warnings = []; - -export default test({ - compileOptions: { - dev: true - }, - - before_test: () => { - warn = console.warn; - - console.warn = (...args) => { - warnings.push(...args); - }; - }, - - after_test: () => { - console.warn = warn; - warnings = []; - }, - - test({ assert, target }) { - const btn = target.querySelector('button'); - - flushSync(() => { - btn?.click(); - }); - - assert.deepEqual(warnings, []); - } -}); diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-4/main.svelte b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-4/main.svelte deleted file mode 100644 index 2d40c13949..0000000000 --- a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-4/main.svelte +++ /dev/null @@ -1,11 +0,0 @@ - - - - - diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-4/state.svelte.js b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-4/state.svelte.js deleted file mode 100644 index 4079059171..0000000000 --- a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-4/state.svelte.js +++ /dev/null @@ -1,13 +0,0 @@ -class Global { - state = $state({}); - - add_a(a) { - this.state.a = a; - } - - increment_a_b() { - this.state.a.b++; - } -} - -export const global = new Global(); diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-4/sub.svelte b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-4/sub.svelte deleted file mode 100644 index 044904aa18..0000000000 --- a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-4/sub.svelte +++ /dev/null @@ -1,8 +0,0 @@ - diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-5/_config.js b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-5/_config.js deleted file mode 100644 index 66f1726a2a..0000000000 --- a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-5/_config.js +++ /dev/null @@ -1,24 +0,0 @@ -import { flushSync } from 'svelte'; -import { test } from '../../test'; - -export default test({ - compileOptions: { - dev: true - }, - - test({ assert, target, warnings }) { - const [btn1, btn2] = target.querySelectorAll('button'); - - flushSync(() => { - btn1.click(); - }); - - assert.deepEqual(warnings.length, 0); - - flushSync(() => { - btn2.click(); - }); - - assert.deepEqual(warnings.length, 1); - } -}); diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-5/main.svelte b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-5/main.svelte deleted file mode 100644 index 0be7e434e4..0000000000 --- a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-5/main.svelte +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-5/state.svelte.js b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-5/state.svelte.js deleted file mode 100644 index 2906b9bce5..0000000000 --- a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-5/state.svelte.js +++ /dev/null @@ -1,14 +0,0 @@ -export function create_my_state() { - const my_state = $state({ - a: 0 - }); - - function inc() { - my_state.a++; - } - - return { - my_state, - inc - }; -} diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-6/Child.svelte b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-6/Child.svelte deleted file mode 100644 index aa31fd7606..0000000000 --- a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-6/Child.svelte +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-6/_config.js b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-6/_config.js deleted file mode 100644 index cc9ea715f0..0000000000 --- a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-6/_config.js +++ /dev/null @@ -1,37 +0,0 @@ -import { flushSync } from 'svelte'; -import { test } from '../../test'; - -/** @type {typeof console.warn} */ -let warn; - -/** @type {any[]} */ -let warnings = []; - -export default test({ - compileOptions: { - dev: true - }, - - before_test: () => { - warn = console.warn; - - console.warn = (...args) => { - warnings.push(...args); - }; - }, - - after_test: () => { - console.warn = warn; - warnings = []; - }, - - async test({ assert, target }) { - const btn = target.querySelector('button'); - - flushSync(() => { - btn?.click(); - }); - - assert.deepEqual(warnings.length, 0); - } -}); diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-6/main.svelte b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-6/main.svelte deleted file mode 100644 index 92d7dbd2db..0000000000 --- a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-6/main.svelte +++ /dev/null @@ -1,17 +0,0 @@ - - - diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-7/_config.js b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-7/_config.js deleted file mode 100644 index ab7327ab8b..0000000000 --- a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-7/_config.js +++ /dev/null @@ -1,24 +0,0 @@ -import { flushSync } from 'svelte'; -import { test } from '../../test'; - -export default test({ - compileOptions: { - dev: true - }, - - async test({ assert, target, warnings }) { - const [btn1, btn2] = target.querySelectorAll('button'); - - flushSync(() => { - btn1.click(); - }); - - assert.deepEqual(warnings.length, 0); - - flushSync(() => { - btn2.click(); - }); - - assert.deepEqual(warnings.length, 1); - } -}); diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-7/main.svelte b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-7/main.svelte deleted file mode 100644 index 8e8343790b..0000000000 --- a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-7/main.svelte +++ /dev/null @@ -1,13 +0,0 @@ - - - - diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner/Counter.svelte b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner/Counter.svelte deleted file mode 100644 index ffe6ef75c4..0000000000 --- a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner/Counter.svelte +++ /dev/null @@ -1,7 +0,0 @@ - - - diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner/main.svelte b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner/main.svelte deleted file mode 100644 index 5f1c7461f6..0000000000 --- a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner/main.svelte +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner/state.svelte.js b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner/state.svelte.js deleted file mode 100644 index 6881c2faf6..0000000000 --- a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner/state.svelte.js +++ /dev/null @@ -1,3 +0,0 @@ -export let global = $state({ - object: { count: -1 } -}); diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-2/_config.js b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-2/_config.js index 87474a05cc..39fa80c55a 100644 --- a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-2/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-2/_config.js @@ -8,6 +8,6 @@ export default test({ }, warnings: [ - 'Intermediate.svelte passed a value to Counter.svelte with `bind:`, but the value is owned by main.svelte. Consider creating a binding between main.svelte and Intermediate.svelte' + 'Intermediate.svelte passed property `object` to Counter.svelte with `bind:`, but its parent component main.svelte did not declare `object` as a binding. Consider creating a binding between main.svelte and Intermediate.svelte (e.g. `bind:object={...}` instead of `object={...}`)' ] }); diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-3/_config.js b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-3/_config.js index 66e5184380..7b8cc676d5 100644 --- a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-3/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-3/_config.js @@ -33,7 +33,7 @@ export default test({ assert.htmlEqual(target.innerHTML, ``); assert.deepEqual(warnings, [ - 'Counter.svelte mutated a value owned by main.svelte. This is strongly discouraged. Consider passing values to child components with `bind:`, or use a callback instead' + 'Mutating unbound props (`notshared`, at Counter.svelte:10:23) is strongly discouraged. Consider using `bind:notshared={...}` in main.svelte (or using a callback) instead' ]); } }); diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-7/_config.js b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-7/_config.js index e766a946d0..bd2ecc28b6 100644 --- a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-7/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-7/_config.js @@ -1,7 +1,6 @@ import { flushSync } from 'svelte'; import { ok, test } from '../../test'; -// Tests that proxies widen ownership correctly even if not directly connected to each other export default test({ compileOptions: { dev: true diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-8/CounterBinding.svelte b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-8/CounterBinding.svelte deleted file mode 100644 index d6da559fb1..0000000000 --- a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-8/CounterBinding.svelte +++ /dev/null @@ -1,7 +0,0 @@ - - -

Binding

- - diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-8/CounterContext.svelte b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-8/CounterContext.svelte deleted file mode 100644 index b935f0a472..0000000000 --- a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-8/CounterContext.svelte +++ /dev/null @@ -1,13 +0,0 @@ - - -

Context

- - diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-8/_config.js b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-8/_config.js deleted file mode 100644 index d6d12d01cd..0000000000 --- a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-8/_config.js +++ /dev/null @@ -1,34 +0,0 @@ -import { flushSync } from 'svelte'; -import { test } from '../../test'; - -// Tests that ownership is widened with $derived (on class or on its own) that contains $state -export default test({ - compileOptions: { - dev: true - }, - - test({ assert, target, warnings }) { - const [root, counter_context1, counter_context2, counter_binding1, counter_binding2] = - target.querySelectorAll('button'); - - counter_context1.click(); - counter_context2.click(); - counter_binding1.click(); - counter_binding2.click(); - flushSync(); - - assert.equal(warnings.length, 0); - - root.click(); - flushSync(); - counter_context1.click(); - counter_context2.click(); - counter_binding1.click(); - counter_binding2.click(); - flushSync(); - - assert.equal(warnings.length, 0); - }, - - warnings: [] -}); diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-8/main.svelte b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-8/main.svelte deleted file mode 100644 index aaade26e16..0000000000 --- a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-8/main.svelte +++ /dev/null @@ -1,46 +0,0 @@ - - -

Parent

- - - - From 7694818f9cf70cf802058c2a49571aaa69956d8a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 8 Apr 2025 17:09:22 -0400 Subject: [PATCH 25/48] Version Packages (#15705) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .changeset/clever-news-enjoy.md | 5 ----- .changeset/sweet-ants-care.md | 5 ----- packages/svelte/CHANGELOG.md | 8 ++++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 5 files changed, 10 insertions(+), 12 deletions(-) delete mode 100644 .changeset/clever-news-enjoy.md delete mode 100644 .changeset/sweet-ants-care.md diff --git a/.changeset/clever-news-enjoy.md b/.changeset/clever-news-enjoy.md deleted file mode 100644 index 2ff3dcbe56..0000000000 --- a/.changeset/clever-news-enjoy.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: allow `$.state` and `$.derived` to be treeshaken diff --git a/.changeset/sweet-ants-care.md b/.changeset/sweet-ants-care.md deleted file mode 100644 index b4805626ab..0000000000 --- a/.changeset/sweet-ants-care.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: rework binding ownership validation diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 361de202b1..eb76e9a9e9 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,13 @@ # svelte +## 5.25.9 + +### Patch Changes + +- fix: allow `$.state` and `$.derived` to be treeshaken ([#15702](https://github.com/sveltejs/svelte/pull/15702)) + +- fix: rework binding ownership validation ([#15678](https://github.com/sveltejs/svelte/pull/15678)) + ## 5.25.8 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 8a225a798d..3fb843b3a2 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.25.8", + "version": "5.25.9", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index 2c8140e365..13a69857a7 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.25.8'; +export const VERSION = '5.25.9'; export const PUBLIC_VERSION = '5'; From 708f541ad8ed2e9426cedb03e0bc1ad5d84cb787 Mon Sep 17 00:00:00 2001 From: 7nik Date: Wed, 9 Apr 2025 00:39:11 +0300 Subject: [PATCH 26/48] fix: better scope `:global()` with nesting selector `&` (#15671) Co-authored-by: 7nik --- .changeset/stupid-vans-draw.md | 5 +++++ .../phases/2-analyze/css/css-prune.js | 20 +++++++++++++++---- .../samples/global-with-nesting/expected.css | 7 ++++++- .../samples/global-with-nesting/input.svelte | 7 ++++++- 4 files changed, 33 insertions(+), 6 deletions(-) create mode 100644 .changeset/stupid-vans-draw.md diff --git a/.changeset/stupid-vans-draw.md b/.changeset/stupid-vans-draw.md new file mode 100644 index 0000000000..24892f1e8f --- /dev/null +++ b/.changeset/stupid-vans-draw.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: better scope `:global()` with nesting selector `&` diff --git a/packages/svelte/src/compiler/phases/2-analyze/css/css-prune.js b/packages/svelte/src/compiler/phases/2-analyze/css/css-prune.js index 0646c6341a..fbe6ca1cd3 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/css/css-prune.js +++ b/packages/svelte/src/compiler/phases/2-analyze/css/css-prune.js @@ -1,6 +1,11 @@ /** @import * as Compiler from '#compiler' */ import { walk } from 'zimmerframe'; -import { get_parent_rules, get_possible_values, is_outer_global } from './utils.js'; +import { + get_parent_rules, + get_possible_values, + is_outer_global, + is_unscoped_pseudo_class +} from './utils.js'; import { regex_ends_with_whitespace, regex_starts_with_whitespace } from '../../patterns.js'; import { get_attribute_chunks, is_text_attribute } from '../../../utils/ast.js'; @@ -286,20 +291,26 @@ function apply_combinator(relative_selector, rest_selectors, rule, node, directi * a global selector * @param {Compiler.AST.CSS.RelativeSelector} selector * @param {Compiler.AST.CSS.Rule} rule + * @returns {boolean} */ function is_global(selector, rule) { if (selector.metadata.is_global || selector.metadata.is_global_like) { return true; } + let explicitly_global = false; + for (const s of selector.selectors) { /** @type {Compiler.AST.CSS.SelectorList | null} */ let selector_list = null; + let can_be_global = false; let owner = rule; if (s.type === 'PseudoClassSelector') { if ((s.name === 'is' || s.name === 'where') && s.args) { selector_list = s.args; + } else { + can_be_global = is_unscoped_pseudo_class(s); } } @@ -308,18 +319,19 @@ function is_global(selector, rule) { selector_list = owner.prelude; } - const has_global_selectors = selector_list?.children.some((complex_selector) => { + const has_global_selectors = !!selector_list?.children.some((complex_selector) => { return complex_selector.children.every((relative_selector) => is_global(relative_selector, owner) ); }); + explicitly_global ||= has_global_selectors; - if (!has_global_selectors) { + if (!has_global_selectors && !can_be_global) { return false; } } - return true; + return explicitly_global || selector.selectors.length === 0; } const regex_backslash_and_following_character = /\\(.)/g; diff --git a/packages/svelte/tests/css/samples/global-with-nesting/expected.css b/packages/svelte/tests/css/samples/global-with-nesting/expected.css index dcb8a0e481..1863c57d85 100644 --- a/packages/svelte/tests/css/samples/global-with-nesting/expected.css +++ b/packages/svelte/tests/css/samples/global-with-nesting/expected.css @@ -1,5 +1,10 @@ div.svelte-xyz { &.class{ - color: red; + color: green; + } + } + * { + &:hover .class.svelte-xyz { + color: green; } } \ No newline at end of file diff --git a/packages/svelte/tests/css/samples/global-with-nesting/input.svelte b/packages/svelte/tests/css/samples/global-with-nesting/input.svelte index 0c73ed7a78..2c1d2b5ebd 100644 --- a/packages/svelte/tests/css/samples/global-with-nesting/input.svelte +++ b/packages/svelte/tests/css/samples/global-with-nesting/input.svelte @@ -1,7 +1,12 @@ From 966ccfbe7451b7c00fc63ee43ea65bd7f286b5cf Mon Sep 17 00:00:00 2001 From: Paolo Ricciuti Date: Tue, 8 Apr 2025 23:41:53 +0200 Subject: [PATCH 27/48] fix: set deriveds as `CLEAN` if they are assigned to (#15592) * fix: set deriveds as `CLEAN` if they are assigned to * chore: remove only * chore: remove unnecessary typecast * fix: set unowned as `MAYBE_DIRTY` instead of `CLEAN` * fix: visit the derived function when to update the dependencies even when it's reassigned * fix: use `execute_derived` instead of `update_reaction` * fix: execute deriveds eagerly when they are set if DIRTY --- .changeset/nervous-kids-shake.md | 5 +++ .../internal/client/reactivity/deriveds.js | 2 +- .../src/internal/client/reactivity/sources.js | 12 +++++-- .../svelte/src/internal/client/runtime.js | 2 +- packages/svelte/tests/signals/test.ts | 32 +++++++++++++++++++ 5 files changed, 49 insertions(+), 4 deletions(-) create mode 100644 .changeset/nervous-kids-shake.md diff --git a/.changeset/nervous-kids-shake.md b/.changeset/nervous-kids-shake.md new file mode 100644 index 0000000000..3fc6429797 --- /dev/null +++ b/.changeset/nervous-kids-shake.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: set deriveds as `CLEAN` if they are assigned to diff --git a/packages/svelte/src/internal/client/reactivity/deriveds.js b/packages/svelte/src/internal/client/reactivity/deriveds.js index 86171c2b2d..c9a8f7674a 100644 --- a/packages/svelte/src/internal/client/reactivity/deriveds.js +++ b/packages/svelte/src/internal/client/reactivity/deriveds.js @@ -131,7 +131,7 @@ function get_derived_parent_effect(derived) { * @param {Derived} derived * @returns {T} */ -function execute_derived(derived) { +export function execute_derived(derived) { var value; var prev_active_effect = active_effect; diff --git a/packages/svelte/src/internal/client/reactivity/sources.js b/packages/svelte/src/internal/client/reactivity/sources.js index 2361762519..cae49c1832 100644 --- a/packages/svelte/src/internal/client/reactivity/sources.js +++ b/packages/svelte/src/internal/client/reactivity/sources.js @@ -28,14 +28,14 @@ import { UNOWNED, MAYBE_DIRTY, BLOCK_EFFECT, - ROOT_EFFECT, - EFFECT_IS_UPDATING + ROOT_EFFECT } from '../constants.js'; import * as e from '../errors.js'; import { legacy_mode_flag, tracing_mode_flag } from '../../flags/index.js'; import { get_stack } from '../dev/tracing.js'; import { component_context, is_runes } from '../context.js'; import { proxy } from '../proxy.js'; +import { execute_derived } from './deriveds.js'; export let inspect_effects = new Set(); export const old_values = new Map(); @@ -172,6 +172,14 @@ export function internal_set(source, value) { } } + if ((source.f & DERIVED) !== 0) { + // if we are assigning to a dirty derived we set it to clean/maybe dirty but we also eagerly execute it to track the dependencies + if ((source.f & DIRTY) !== 0) { + execute_derived(/** @type {Derived} */ (source)); + } + set_signal_status(source, (source.f & UNOWNED) === 0 ? CLEAN : MAYBE_DIRTY); + } + mark_reactions(source, DIRTY); // It's possible that the current reaction might not have up-to-date dependencies diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index c1c91f3551..a7662be617 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -27,7 +27,7 @@ import { } from './constants.js'; import { flush_tasks } from './dom/task.js'; import { internal_set, old_values } from './reactivity/sources.js'; -import { destroy_derived_effects, update_derived } from './reactivity/deriveds.js'; +import { destroy_derived_effects, execute_derived, update_derived } from './reactivity/deriveds.js'; import * as e from './errors.js'; import { FILENAME } from '../../constants.js'; import { tracing_mode_flag } from '../flags/index.js'; diff --git a/packages/svelte/tests/signals/test.ts b/packages/svelte/tests/signals/test.ts index 3977caae36..3a427e9392 100644 --- a/packages/svelte/tests/signals/test.ts +++ b/packages/svelte/tests/signals/test.ts @@ -1080,6 +1080,38 @@ describe('signals', () => { }; }); + test("deriveds set after they are DIRTY doesn't get updated on get", () => { + return () => { + const a = state(0); + const b = derived(() => $.get(a)); + + set(b, 1); + assert.equal($.get(b), 1); + + set(a, 2); + assert.equal($.get(b), 2); + set(b, 3); + + assert.equal($.get(b), 3); + }; + }); + + test("unowned deriveds set after they are DIRTY doesn't get updated on get", () => { + return () => { + const a = state(0); + const b = derived(() => $.get(a)); + const c = derived(() => $.get(b)); + + set(b, 1); + assert.equal($.get(c), 1); + + set(a, 2); + + assert.equal($.get(b), 2); + assert.equal($.get(c), 2); + }; + }); + test('deriveds containing effects work correctly when used with untrack', () => { return () => { let a = render_effect(() => {}); From e9a16c4b4209d104e0bef7cf28df51f830de1254 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 8 Apr 2025 18:53:18 -0400 Subject: [PATCH 28/48] chore: revert corepack override (#15197) * Revert "try this (#15196)" This reverts commit f878736f3825e1832fa7344306358e877e20bd7f. * upgrade node * upgrade pnpm --------- Co-authored-by: Ben McCann <322311+benmccann@users.noreply.github.com> --- .github/workflows/pkg.pr.new.yml | 5 +---- package.json | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/.github/workflows/pkg.pr.new.yml b/.github/workflows/pkg.pr.new.yml index 90d219faae..b2b521dc6f 100644 --- a/.github/workflows/pkg.pr.new.yml +++ b/.github/workflows/pkg.pr.new.yml @@ -11,13 +11,10 @@ jobs: - name: Checkout code uses: actions/checkout@v4 - - name: install corepack - run: npm i -g corepack@0.31.0 - - run: corepack enable - uses: actions/setup-node@v4 with: - node-version: 18.x + node-version: 22.x cache: pnpm - name: Install dependencies diff --git a/package.json b/package.json index ad69bfc9ca..70e85438f0 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "private": true, "type": "module", "license": "MIT", - "packageManager": "pnpm@9.4.0", + "packageManager": "pnpm@10.4.0", "engines": { "pnpm": ">=9.0.0" }, From 0ca1f4a37ec008092fc1798e374c6108308addef Mon Sep 17 00:00:00 2001 From: Min Idzelis Date: Tue, 8 Apr 2025 21:26:54 -0400 Subject: [PATCH 29/48] docs: raise importance of global vs local transitions (#15479) * Doc: Raise importance of global vs local transitions * switch order --------- Co-authored-by: Rich Harris --- documentation/docs/03-template-syntax/13-transition.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/documentation/docs/03-template-syntax/13-transition.md b/documentation/docs/03-template-syntax/13-transition.md index 51c11e8b34..c51175c272 100644 --- a/documentation/docs/03-template-syntax/13-transition.md +++ b/documentation/docs/03-template-syntax/13-transition.md @@ -22,10 +22,6 @@ The `transition:` directive indicates a _bidirectional_ transition, which means {/if} ``` -## Built-in transitions - -A selection of built-in transitions can be imported from the [`svelte/transition`](svelte-transition) module. - ## Local vs global Transitions are local by default. Local transitions only play when the block they belong to is created or destroyed, _not_ when parent blocks are created or destroyed. @@ -40,6 +36,10 @@ Transitions are local by default. Local transitions only play when the block the {/if} ``` +## Built-in transitions + +A selection of built-in transitions can be imported from the [`svelte/transition`](svelte-transition) module. + ## Transition parameters Transitions can have parameters. From 0ff3d7452092511a55a63a7639a8e8798fea9fed Mon Sep 17 00:00:00 2001 From: Ben McCann <322311+benmccann@users.noreply.github.com> Date: Tue, 8 Apr 2025 19:40:05 -0700 Subject: [PATCH 30/48] docs: update `$effect` examples (#15463) * docs: update effect examples * revert * Update documentation/docs/02-runes/04-$effect.md * update example * revert * update effect root example --------- Co-authored-by: Rich Harris --- documentation/docs/02-runes/04-$effect.md | 55 ++++++++++++++--------- 1 file changed, 34 insertions(+), 21 deletions(-) diff --git a/documentation/docs/02-runes/04-$effect.md b/documentation/docs/02-runes/04-$effect.md index ae1a2146c9..46ea9b81e9 100644 --- a/documentation/docs/02-runes/04-$effect.md +++ b/documentation/docs/02-runes/04-$effect.md @@ -25,7 +25,7 @@ You can create an effect with the `$effect` rune ([demo](/playground/untitled#H4 }); - + ``` When Svelte runs an effect function, it tracks which pieces of state (and derived state) are accessed (unless accessed inside [`untrack`](svelte#untrack)), and re-runs the function when that state later changes. @@ -135,19 +135,33 @@ An effect only reruns when the object it reads changes, not when a property insi An effect only depends on the values that it read the last time it ran. This has interesting implications for effects that have conditional code. -For instance, if `a` is `true` in the code snippet below, the code inside the `if` block will run and `b` will be evaluated. As such, changes to either `a` or `b` [will cause the effect to re-run](/playground/untitled#H4sIAAAAAAAAE3VQzWrDMAx-FdUU4kBp71li6EPstOxge0ox8-QQK2PD-N1nLy2F0Z2Evj9_chKkP1B04pnYscc3cRCT8xhF95IEf8-Vq0DBr8rzPB_jJ3qumNERH-E2ECNxiRF9tIubWY00lgcYNAywj6wZJS8rtk83wjwgCrXHaULLUrYwKEgVGrnkx-Dx6MNFNstK5OjSbFGbwE0gdXuT_zGYrjmAuco515Hr1p_uXak3K3MgCGS9s-9D2grU-judlQYXIencnzad-tdR79qZrMyvw9wd5Z8Yv1h09dz8mn8AkM7Pfo0BAAA=). +For instance, if `condition` is `true` in the code snippet below, the code inside the `if` block will run and `color` will be evaluated. As such, changes to either `condition` or `color` [will cause the effect to re-run](/playground/untitled#H4sIAAAAAAAAE21RQW6DMBD8ytaNBJHaJFLViwNIVZ8RcnBgXVk1xsILTYT4e20TQg89IOPZ2fHM7siMaJBx9tmaWpFqjQNlAKXEihx7YVJpdIyfRkY3G4gB8Pi97cPanRtQU8AuwuF_eNUaQuPlOMtc1SlLRWlKUo1tOwJflUikQHZtA0klzCDc64Imx0ANn8bInV1CDhtHgjClrsftcSXotluLybOUb3g4JJHhOZs5WZpuIS9gjNqkJKQP5e2ClrR4SMdZ13E4xZ8zTPOTJU2A2uE_PQ9COCI926_hTVarIU4hu_REPlBrKq2q73ycrf1N-vS4TMUsulaVg3EtR8H9rFgsg8uUsT1B2F9eshigZHBRpuaD0D3mY8Qm2BfB5N2YyRzdNEYVDy0Ja-WsFjcOUuP1HvFLWA6H3XuHTUSmmDV2--0TXonxsKbp7G9C6R__NONS-MFNvxj_d6mBAgAA). -Conversely, if `a` is `false`, `b` will not be evaluated, and the effect will _only_ re-run when `a` changes. +Conversely, if `condition` is `false`, `color` will not be evaluated, and the effect will _only_ re-run again when `condition` changes. ```ts -let a = false; -let b = false; +// @filename: ambient.d.ts +declare module 'canvas-confetti' { + interface ConfettiOptions { + colors: string[]; + } + + function confetti(opts?: ConfettiOptions): void; + export default confetti; +} + +// @filename: index.js // ---cut--- -$effect(() => { - console.log('running'); +import confetti from 'canvas-confetti'; - if (a) { - console.log('b:', b); +let condition = $state(true); +let color = $state('#ff3e00'); + +$effect(() => { + if (condition) { + confetti({ colors: [color] }); + } else { + confetti(); } }); ``` @@ -211,20 +225,19 @@ It is used to implement abstractions like [`createSubscriber`](/docs/svelte/svel The `$effect.root` rune is an advanced feature that creates a non-tracked scope that doesn't auto-cleanup. This is useful for nested effects that you want to manually control. This rune also allows for the creation of effects outside of the component initialisation phase. -```svelte - +// later... +destroy(); ``` ## When not to use `$effect` From 93110a32469779794d2c022a7605e958ce75cb34 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 9 Apr 2025 06:30:53 -0400 Subject: [PATCH 31/48] docs: explain restriction on exporting reassigned state (#15713) --- .../01-introduction/04-svelte-js-files.md | 2 +- documentation/docs/02-runes/02-$state.md | 80 +++++++++++++++++++ 2 files changed, 81 insertions(+), 1 deletion(-) diff --git a/documentation/docs/01-introduction/04-svelte-js-files.md b/documentation/docs/01-introduction/04-svelte-js-files.md index 0e05484299..1d3e3dd61a 100644 --- a/documentation/docs/01-introduction/04-svelte-js-files.md +++ b/documentation/docs/01-introduction/04-svelte-js-files.md @@ -4,7 +4,7 @@ title: .svelte.js and .svelte.ts files Besides `.svelte` files, Svelte also operates on `.svelte.js` and `.svelte.ts` files. -These behave like any other `.js` or `.ts` module, except that you can use runes. This is useful for creating reusable reactive logic, or sharing reactive state across your app. +These behave like any other `.js` or `.ts` module, except that you can use runes. This is useful for creating reusable reactive logic, or sharing reactive state across your app (though note that you [cannot export reassigned state]($state#Passing-state-across-modules)). > [!LEGACY] > This is a concept that didn't exist prior to Svelte 5 diff --git a/documentation/docs/02-runes/02-$state.md b/documentation/docs/02-runes/02-$state.md index 49e17cd08f..16630a977b 100644 --- a/documentation/docs/02-runes/02-$state.md +++ b/documentation/docs/02-runes/02-$state.md @@ -250,3 +250,83 @@ console.log(total.value); // 7 ``` ...though if you find yourself writing code like that, consider using [classes](#Classes) instead. + +## Passing state across modules + +You can declare state in `.svelte.js` and `.svelte.ts` files, but you can only _export_ that state if it's not directly reassigned. In other words you can't do this: + +```js +/// file: state.svelte.js +export let count = $state(0); + +export function increment() { + count += 1; +} +``` + +That's because every reference to `count` is transformed by the Svelte compiler — the code above is roughly equivalent to this: + +```js +/// file: state.svelte.js (compiler output) +// @filename: index.ts +interface Signal { + value: T; +} + +interface Svelte { + state(value?: T): Signal; + get(source: Signal): T; + set(source: Signal, value: T): void; +} +declare const $: Svelte; +// ---cut--- +export let count = $.state(0); + +export function increment() { + $.set(count, $.get(count) + 1); +} +``` + +> [!NOTE] You can see the code Svelte generates by clicking the 'JS Output' tab in the [playground](/playground). + +Since the compiler only operates on one file at a time, if another file imports `count` Svelte doesn't know that it needs to wrap each reference in `$.get` and `$.set`: + +```js +// @filename: state.svelte.js +export let count = 0; + +// @filename: index.js +// ---cut--- +import { count } from './state.svelte.js'; + +console.log(typeof count); // 'object', not 'number' +``` + +This leaves you with two options for sharing state between modules — either don't reassign it... + +```js +// This is allowed — since we're updating +// `counter.count` rather than `counter`, +// Svelte doesn't wrap it in `$.state` +export const counter = $state({ + count: 0 +}); + +export function increment() { + counter.count += 1; +} +``` + +...or don't directly export it: + +```js +let count = $state(0); + +export function getCount() { + return count; +} + +export function increment() { + count += 1; +} +``` From c23f15134e85c17e85ad326fcb50bbc4a1cdffa4 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 9 Apr 2025 12:51:21 -0400 Subject: [PATCH 32/48] chore: remove stack-based module boundaries (#15711) --- .../98-reference/.generated/client-errors.md | 2 +- .../svelte/messages/client-errors/errors.md | 2 +- .../3-transform/client/transform-client.js | 3 - .../svelte/src/internal/client/dev/legacy.js | 5 +- .../src/internal/client/dev/ownership.js | 97 ------------------- packages/svelte/src/internal/client/errors.js | 7 +- packages/svelte/src/internal/client/index.js | 2 +- 7 files changed, 7 insertions(+), 111 deletions(-) diff --git a/documentation/docs/98-reference/.generated/client-errors.md b/documentation/docs/98-reference/.generated/client-errors.md index fd9419176d..32348bb781 100644 --- a/documentation/docs/98-reference/.generated/client-errors.md +++ b/documentation/docs/98-reference/.generated/client-errors.md @@ -21,7 +21,7 @@ A component is attempting to bind to a non-bindable property `%key%` belonging t ### component_api_changed ``` -%parent% called `%method%` on an instance of %component%, which is no longer valid in Svelte 5 +Calling `%method%` on a component instance (of %component%) is no longer valid in Svelte 5 ``` See the [migration guide](/docs/svelte/v5-migration-guide#Components-are-no-longer-classes) for more information. diff --git a/packages/svelte/messages/client-errors/errors.md b/packages/svelte/messages/client-errors/errors.md index ca06122cb5..c4e68f8fee 100644 --- a/packages/svelte/messages/client-errors/errors.md +++ b/packages/svelte/messages/client-errors/errors.md @@ -12,7 +12,7 @@ ## component_api_changed -> %parent% called `%method%` on an instance of %component%, which is no longer valid in Svelte 5 +> Calling `%method%` on a component instance (of %component%) is no longer valid in Svelte 5 See the [migration guide](/docs/svelte/v5-migration-guide#Components-are-no-longer-classes) for more information. 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 38fcee8d6f..098b3ecae8 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 @@ -536,9 +536,6 @@ export function client_component(analysis, options) { b.assignment('=', b.member(b.id(analysis.name), '$.FILENAME', true), b.literal(filename)) ) ); - - body.unshift(b.stmt(b.call(b.id('$.mark_module_start')))); - body.push(b.stmt(b.call(b.id('$.mark_module_end'), b.id(analysis.name)))); } if (!analysis.runes) { diff --git a/packages/svelte/src/internal/client/dev/legacy.js b/packages/svelte/src/internal/client/dev/legacy.js index 138213c551..02428dc824 100644 --- a/packages/svelte/src/internal/client/dev/legacy.js +++ b/packages/svelte/src/internal/client/dev/legacy.js @@ -1,7 +1,6 @@ import * as e from '../errors.js'; import { component_context } from '../context.js'; import { FILENAME } from '../../../constants.js'; -import { get_component } from './ownership.js'; /** @param {Function & { [FILENAME]: string }} target */ export function check_target(target) { @@ -15,9 +14,7 @@ export function legacy_api() { /** @param {string} method */ function error(method) { - // @ts-expect-error - const parent = get_component()?.[FILENAME] ?? 'Something'; - e.component_api_changed(parent, method, component[FILENAME]); + e.component_api_changed(method, component[FILENAME]); } return { diff --git a/packages/svelte/src/internal/client/dev/ownership.js b/packages/svelte/src/internal/client/dev/ownership.js index 6c40a744df..e28a40dd77 100644 --- a/packages/svelte/src/internal/client/dev/ownership.js +++ b/packages/svelte/src/internal/client/dev/ownership.js @@ -7,103 +7,6 @@ import { component_context } from '../context.js'; import * as w from '../warnings.js'; import { sanitize_location } from '../../../utils.js'; -/** @type {Record>} */ -const boundaries = {}; - -const chrome_pattern = /at (?:.+ \()?(.+):(\d+):(\d+)\)?$/; -const firefox_pattern = /@(.+):(\d+):(\d+)$/; - -function get_stack() { - const stack = new Error().stack; - if (!stack) return null; - - const entries = []; - - for (const line of stack.split('\n')) { - let match = chrome_pattern.exec(line) ?? firefox_pattern.exec(line); - - if (match) { - entries.push({ - file: match[1], - line: +match[2], - column: +match[3] - }); - } - } - - return entries; -} - -/** - * Determines which `.svelte` component is responsible for a given state change - * @returns {Function | null} - */ -export function get_component() { - // first 4 lines are svelte internals; adjust this number if we change the internal call stack - const stack = get_stack()?.slice(4); - if (!stack) return null; - - for (let i = 0; i < stack.length; i++) { - const entry = stack[i]; - const modules = boundaries[entry.file]; - if (!modules) { - // If the first entry is not a component, that means the modification very likely happened - // within a .svelte.js file, possibly triggered by a component. Since these files are not part - // of the bondaries/component context heuristic, we need to bail in this case, else we would - // have false positives when the .svelte.ts file provides a state creator function, encapsulating - // the state and its mutations, and is being called from a component other than the one who - // called the state creator function. - if (i === 0) return null; - continue; - } - - for (const module of modules) { - if (module.end == null) { - return null; - } - if (module.start.line < entry.line && module.end.line > entry.line) { - return module.component; - } - } - } - - return null; -} - -/** - * Together with `mark_module_end`, this function establishes the boundaries of a `.svelte` file, - * such that subsequent calls to `get_component` can tell us which component is responsible - * for a given state change - */ -export function mark_module_start() { - const start = get_stack()?.[2]; - - if (start) { - (boundaries[start.file] ??= []).push({ - start, - // @ts-expect-error - end: null, - // @ts-expect-error we add the component at the end, since HMR will overwrite the function - component: null - }); - } -} - -/** - * @param {Function} component - */ -export function mark_module_end(component) { - const end = get_stack()?.[2]; - - if (end) { - const boundaries_file = boundaries[end.file]; - const boundary = boundaries_file[boundaries_file.length - 1]; - - boundary.end = end; - boundary.component = component; - } -} - /** * Sets up a validator that * - traverses the path of a prop to find out if it is allowed to be mutated diff --git a/packages/svelte/src/internal/client/errors.js b/packages/svelte/src/internal/client/errors.js index 8a5b5033a7..429dd99da9 100644 --- a/packages/svelte/src/internal/client/errors.js +++ b/packages/svelte/src/internal/client/errors.js @@ -54,15 +54,14 @@ export function bind_not_bindable(key, component, name) { } /** - * %parent% called `%method%` on an instance of %component%, which is no longer valid in Svelte 5 - * @param {string} parent + * Calling `%method%` on a component instance (of %component%) is no longer valid in Svelte 5 * @param {string} method * @param {string} component * @returns {never} */ -export function component_api_changed(parent, method, component) { +export function component_api_changed(method, component) { if (DEV) { - const error = new Error(`component_api_changed\n${parent} called \`${method}\` on an instance of ${component}, which is no longer valid in Svelte 5\nhttps://svelte.dev/e/component_api_changed`); + const error = new Error(`component_api_changed\nCalling \`${method}\` on a component instance (of ${component}) is no longer valid in Svelte 5\nhttps://svelte.dev/e/component_api_changed`); error.name = 'Svelte error'; throw error; diff --git a/packages/svelte/src/internal/client/index.js b/packages/svelte/src/internal/client/index.js index 7eed8a744a..a865419c5f 100644 --- a/packages/svelte/src/internal/client/index.js +++ b/packages/svelte/src/internal/client/index.js @@ -4,7 +4,7 @@ export { assign, assign_and, assign_or, assign_nullish } from './dev/assign.js'; export { cleanup_styles } from './dev/css.js'; export { add_locations } from './dev/elements.js'; export { hmr } from './dev/hmr.js'; -export { mark_module_start, mark_module_end, create_ownership_validator } from './dev/ownership.js'; +export { create_ownership_validator } from './dev/ownership.js'; export { check_target, legacy_api } from './dev/legacy.js'; export { trace } from './dev/tracing.js'; export { inspect } from './dev/inspect.js'; From 475b5dbe83732fd031fa4f97aac712550385c700 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 9 Apr 2025 14:09:51 -0400 Subject: [PATCH 33/48] Version Packages (#15712) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .changeset/nervous-kids-shake.md | 5 ----- .changeset/stupid-vans-draw.md | 5 ----- packages/svelte/CHANGELOG.md | 8 ++++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 5 files changed, 10 insertions(+), 12 deletions(-) delete mode 100644 .changeset/nervous-kids-shake.md delete mode 100644 .changeset/stupid-vans-draw.md diff --git a/.changeset/nervous-kids-shake.md b/.changeset/nervous-kids-shake.md deleted file mode 100644 index 3fc6429797..0000000000 --- a/.changeset/nervous-kids-shake.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: set deriveds as `CLEAN` if they are assigned to diff --git a/.changeset/stupid-vans-draw.md b/.changeset/stupid-vans-draw.md deleted file mode 100644 index 24892f1e8f..0000000000 --- a/.changeset/stupid-vans-draw.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: better scope `:global()` with nesting selector `&` diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index eb76e9a9e9..6f999f381e 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,13 @@ # svelte +## 5.25.10 + +### Patch Changes + +- fix: set deriveds as `CLEAN` if they are assigned to ([#15592](https://github.com/sveltejs/svelte/pull/15592)) + +- fix: better scope `:global()` with nesting selector `&` ([#15671](https://github.com/sveltejs/svelte/pull/15671)) + ## 5.25.9 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 3fb843b3a2..b9f434e688 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.25.9", + "version": "5.25.10", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index 13a69857a7..2ea9890df9 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.25.9'; +export const VERSION = '5.25.10'; export const PUBLIC_VERSION = '5'; From 6c97a78049ccc706076e8b962d482c7a1fa27650 Mon Sep 17 00:00:00 2001 From: Paolo Ricciuti Date: Thu, 10 Apr 2025 13:29:40 +0200 Subject: [PATCH 34/48] fix: prevent ownership warnings if the fallback of a bindable is used (#15720) * fix: prevent ownership warnings if the fallback of a bindable is used * fix: filter out symbol from own keys * fix: don't create sources for `BINDABLE_FALLBACK_SYMBOL` * fix: use strategy suggested by actually competent person aka @dummdidumm * chore: rename function --- .changeset/wise-turkeys-yell.md | 5 ++++ .../src/internal/client/dev/ownership.js | 12 ++++++---- .../src/internal/client/reactivity/props.js | 2 +- .../Child.svelte | 5 ++++ .../Parent.svelte | 7 ++++++ .../_config.js | 11 +++++++++ .../main.svelte | 5 ++++ .../Parent.svelte | 8 +++++++ .../_config.js | 23 +++++++++++++++++++ .../main.svelte | 5 ++++ 10 files changed, 78 insertions(+), 5 deletions(-) create mode 100644 .changeset/wise-turkeys-yell.md create mode 100644 packages/svelte/tests/runtime-runes/samples/ownership-invalid-binding-bindable-fallback/Child.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/ownership-invalid-binding-bindable-fallback/Parent.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/ownership-invalid-binding-bindable-fallback/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/ownership-invalid-binding-bindable-fallback/main.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/ownership-invalid-mutation-bindable-fallback/Parent.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/ownership-invalid-mutation-bindable-fallback/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/ownership-invalid-mutation-bindable-fallback/main.svelte diff --git a/.changeset/wise-turkeys-yell.md b/.changeset/wise-turkeys-yell.md new file mode 100644 index 0000000000..cd5e103de3 --- /dev/null +++ b/.changeset/wise-turkeys-yell.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: prevent ownership warnings if the fallback of a bindable is used diff --git a/packages/svelte/src/internal/client/dev/ownership.js b/packages/svelte/src/internal/client/dev/ownership.js index e28a40dd77..108c7adf92 100644 --- a/packages/svelte/src/internal/client/dev/ownership.js +++ b/packages/svelte/src/internal/client/dev/ownership.js @@ -27,7 +27,7 @@ export function create_ownership_validator(props) { */ mutation: (prop, path, result, line, column) => { const name = path[0]; - if (is_bound(props, name) || !parent) { + if (is_bound_or_unset(props, name) || !parent) { return result; } @@ -52,7 +52,7 @@ export function create_ownership_validator(props) { * @param {() => any} value */ binding: (key, child_component, value) => { - if (!is_bound(props, key) && parent && value()?.[STATE_SYMBOL]) { + if (!is_bound_or_unset(props, key) && parent && value()?.[STATE_SYMBOL]) { w.ownership_invalid_binding( component[FILENAME], key, @@ -68,9 +68,13 @@ export function create_ownership_validator(props) { * @param {Record} props * @param {string} prop_name */ -function is_bound(props, prop_name) { +function is_bound_or_unset(props, prop_name) { // Can be the case when someone does `mount(Component, props)` with `let props = $state({...})` // or `createClassComponent(Component, props)` const is_entry_props = STATE_SYMBOL in props || LEGACY_PROPS in props; - return !!get_descriptor(props, prop_name)?.set || (is_entry_props && prop_name in props); + return ( + !!get_descriptor(props, prop_name)?.set || + (is_entry_props && prop_name in props) || + !(prop_name in props) + ); } diff --git a/packages/svelte/src/internal/client/reactivity/props.js b/packages/svelte/src/internal/client/reactivity/props.js index bd85b14df0..341d7c768a 100644 --- a/packages/svelte/src/internal/client/reactivity/props.js +++ b/packages/svelte/src/internal/client/reactivity/props.js @@ -7,7 +7,7 @@ import { PROPS_IS_RUNES, PROPS_IS_UPDATED } from '../../../constants.js'; -import { get_descriptor, is_function } from '../../shared/utils.js'; +import { define_property, get_descriptor, is_function } from '../../shared/utils.js'; import { mutable_source, set, source, update } from './sources.js'; import { derived, derived_safe_equal } from './deriveds.js'; import { get, captured_signals, untrack } from '../runtime.js'; diff --git a/packages/svelte/tests/runtime-runes/samples/ownership-invalid-binding-bindable-fallback/Child.svelte b/packages/svelte/tests/runtime-runes/samples/ownership-invalid-binding-bindable-fallback/Child.svelte new file mode 100644 index 0000000000..78b82caed9 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/ownership-invalid-binding-bindable-fallback/Child.svelte @@ -0,0 +1,5 @@ + + +{test} diff --git a/packages/svelte/tests/runtime-runes/samples/ownership-invalid-binding-bindable-fallback/Parent.svelte b/packages/svelte/tests/runtime-runes/samples/ownership-invalid-binding-bindable-fallback/Parent.svelte new file mode 100644 index 0000000000..7bfb17aa64 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/ownership-invalid-binding-bindable-fallback/Parent.svelte @@ -0,0 +1,7 @@ + + + diff --git a/packages/svelte/tests/runtime-runes/samples/ownership-invalid-binding-bindable-fallback/_config.js b/packages/svelte/tests/runtime-runes/samples/ownership-invalid-binding-bindable-fallback/_config.js new file mode 100644 index 0000000000..e93067eb9d --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/ownership-invalid-binding-bindable-fallback/_config.js @@ -0,0 +1,11 @@ +import { test } from '../../test'; + +export default test({ + mode: ['client'], + compileOptions: { + dev: true + }, + async test({ warnings, assert }) { + assert.deepEqual(warnings, []); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/ownership-invalid-binding-bindable-fallback/main.svelte b/packages/svelte/tests/runtime-runes/samples/ownership-invalid-binding-bindable-fallback/main.svelte new file mode 100644 index 0000000000..282afb1771 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/ownership-invalid-binding-bindable-fallback/main.svelte @@ -0,0 +1,5 @@ + + + diff --git a/packages/svelte/tests/runtime-runes/samples/ownership-invalid-mutation-bindable-fallback/Parent.svelte b/packages/svelte/tests/runtime-runes/samples/ownership-invalid-mutation-bindable-fallback/Parent.svelte new file mode 100644 index 0000000000..7d6b248da7 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/ownership-invalid-mutation-bindable-fallback/Parent.svelte @@ -0,0 +1,8 @@ + + + + + +{test} \ No newline at end of file diff --git a/packages/svelte/tests/runtime-runes/samples/ownership-invalid-mutation-bindable-fallback/_config.js b/packages/svelte/tests/runtime-runes/samples/ownership-invalid-mutation-bindable-fallback/_config.js new file mode 100644 index 0000000000..9b4e3479ea --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/ownership-invalid-mutation-bindable-fallback/_config.js @@ -0,0 +1,23 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +export default test({ + mode: ['client'], + compileOptions: { + dev: true + }, + async test({ warnings, assert, target }) { + const [btn, btn2] = target.querySelectorAll('button'); + flushSync(() => { + btn2.click(); + }); + assert.deepEqual(warnings, []); + flushSync(() => { + btn.click(); + }); + flushSync(() => { + btn2.click(); + }); + assert.deepEqual(warnings, []); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/ownership-invalid-mutation-bindable-fallback/main.svelte b/packages/svelte/tests/runtime-runes/samples/ownership-invalid-mutation-bindable-fallback/main.svelte new file mode 100644 index 0000000000..282afb1771 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/ownership-invalid-mutation-bindable-fallback/main.svelte @@ -0,0 +1,5 @@ + + + From 6d195f0350c5e46da0012525f92654c62257402e Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 10 Apr 2025 08:19:07 -0400 Subject: [PATCH 35/48] fix: handle hydration mismatches in await blocks (#15708) * failing test for #15704 * handle hydration mismatches in await blocks * DRY out * changeset * update test --- .changeset/wild-carrots-eat.md | 5 ++++ .../3-transform/server/visitors/AwaitBlock.js | 10 ++----- packages/svelte/src/constants.js | 1 + .../src/internal/client/dom/blocks/await.js | 29 +++++++++++++++++-- packages/svelte/src/internal/server/index.js | 7 +++-- .../await-hydrate-maybe-promise/_config.js | 23 +++++++++++++++ .../await-hydrate-maybe-promise/main.svelte | 25 ++++++++++++++++ .../_expected/server/index.svelte.js | 6 ++-- 8 files changed, 92 insertions(+), 14 deletions(-) create mode 100644 .changeset/wild-carrots-eat.md create mode 100644 packages/svelte/tests/runtime-runes/samples/await-hydrate-maybe-promise/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/await-hydrate-maybe-promise/main.svelte diff --git a/.changeset/wild-carrots-eat.md b/.changeset/wild-carrots-eat.md new file mode 100644 index 0000000000..23b55f945c --- /dev/null +++ b/.changeset/wild-carrots-eat.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: handle hydration mismatches in await blocks diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/AwaitBlock.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/AwaitBlock.js index 8fc82b8905..2aa534d257 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/AwaitBlock.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/AwaitBlock.js @@ -2,7 +2,7 @@ /** @import { AST } from '#compiler' */ /** @import { ComponentContext } from '../types.js' */ import * as b from '../../../../utils/builders.js'; -import { empty_comment } from './shared/utils.js'; +import { block_close } from './shared/utils.js'; /** * @param {AST.AwaitBlock} node @@ -10,10 +10,10 @@ import { empty_comment } from './shared/utils.js'; */ export function AwaitBlock(node, context) { context.state.template.push( - empty_comment, b.stmt( b.call( '$.await', + b.id('$$payload'), /** @type {Expression} */ (context.visit(node.expression)), b.thunk( node.pending ? /** @type {BlockStatement} */ (context.visit(node.pending)) : b.block([]) @@ -21,13 +21,9 @@ export function AwaitBlock(node, context) { b.arrow( node.value ? [/** @type {Pattern} */ (context.visit(node.value))] : [], node.then ? /** @type {BlockStatement} */ (context.visit(node.then)) : b.block([]) - ), - b.arrow( - node.error ? [/** @type {Pattern} */ (context.visit(node.error))] : [], - node.catch ? /** @type {BlockStatement} */ (context.visit(node.catch)) : b.block([]) ) ) ), - empty_comment + block_close ); } diff --git a/packages/svelte/src/constants.js b/packages/svelte/src/constants.js index 8861e440fc..6ea407d448 100644 --- a/packages/svelte/src/constants.js +++ b/packages/svelte/src/constants.js @@ -22,6 +22,7 @@ export const HYDRATION_START = '['; /** used to indicate that an `{:else}...` block was rendered */ export const HYDRATION_START_ELSE = '[!'; export const HYDRATION_END = ']'; +export const HYDRATION_AWAIT_THEN = '!'; export const HYDRATION_ERROR = {}; export const ELEMENT_IS_NAMESPACED = 1; diff --git a/packages/svelte/src/internal/client/dom/blocks/await.js b/packages/svelte/src/internal/client/dom/blocks/await.js index 2e3d229779..99bdc0000c 100644 --- a/packages/svelte/src/internal/client/dom/blocks/await.js +++ b/packages/svelte/src/internal/client/dom/blocks/await.js @@ -4,9 +4,16 @@ import { is_promise } from '../../../shared/utils.js'; import { block, branch, pause_effect, resume_effect } from '../../reactivity/effects.js'; import { internal_set, mutable_source, source } from '../../reactivity/sources.js'; import { flushSync, set_active_effect, set_active_reaction } from '../../runtime.js'; -import { hydrate_next, hydrate_node, hydrating } from '../hydration.js'; +import { + hydrate_next, + hydrate_node, + hydrating, + remove_nodes, + set_hydrate_node, + set_hydrating +} from '../hydration.js'; import { queue_micro_task } from '../task.js'; -import { UNINITIALIZED } from '../../../../constants.js'; +import { HYDRATION_START_ELSE, UNINITIALIZED } from '../../../../constants.js'; import { component_context, is_runes, @@ -113,6 +120,19 @@ export function await_block(node, get_input, pending_fn, then_fn, catch_fn) { var effect = block(() => { if (input === (input = get_input())) return; + /** Whether or not there was a hydration mismatch. Needs to be a `let` or else it isn't treeshaken out */ + // @ts-ignore coercing `anchor` to a `Comment` causes TypeScript and Prettier to fight + let mismatch = hydrating && is_promise(input) === (anchor.data === HYDRATION_START_ELSE); + + if (mismatch) { + // Hydration mismatch: remove everything inside the anchor and start fresh + anchor = remove_nodes(); + + set_hydrate_node(anchor); + set_hydrating(false); + mismatch = true; + } + if (is_promise(input)) { var promise = input; @@ -155,6 +175,11 @@ export function await_block(node, get_input, pending_fn, then_fn, catch_fn) { update(THEN, false); } + if (mismatch) { + // continue in hydration mode + set_hydrating(true); + } + // Set the input to something else, in order to disable the promise callbacks return () => (input = UNINITIALIZED); }); diff --git a/packages/svelte/src/internal/server/index.js b/packages/svelte/src/internal/server/index.js index bf36a595d8..ff34c07132 100644 --- a/packages/svelte/src/internal/server/index.js +++ b/packages/svelte/src/internal/server/index.js @@ -13,7 +13,7 @@ import { import { escape_html } from '../../escaping.js'; import { DEV } from 'esm-env'; import { current_component, pop, push } from './context.js'; -import { EMPTY_COMMENT, BLOCK_CLOSE, BLOCK_OPEN } from './hydration.js'; +import { EMPTY_COMMENT, BLOCK_CLOSE, BLOCK_OPEN, BLOCK_OPEN_ELSE } from './hydration.js'; import { validate_store } from '../shared/validate.js'; import { is_boolean_attribute, is_raw_text_element, is_void } from '../../utils.js'; import { reset_elements } from './dev.js'; @@ -474,18 +474,21 @@ export function bind_props(props_parent, props_now) { /** * @template V + * @param {Payload} payload * @param {Promise} promise * @param {null | (() => void)} pending_fn * @param {(value: V) => void} then_fn * @returns {void} */ -function await_block(promise, pending_fn, then_fn) { +function await_block(payload, promise, pending_fn, then_fn) { if (is_promise(promise)) { + payload.out += BLOCK_OPEN; promise.then(null, noop); if (pending_fn !== null) { pending_fn(); } } else if (then_fn !== null) { + payload.out += BLOCK_OPEN_ELSE; then_fn(promise); } } diff --git a/packages/svelte/tests/runtime-runes/samples/await-hydrate-maybe-promise/_config.js b/packages/svelte/tests/runtime-runes/samples/await-hydrate-maybe-promise/_config.js new file mode 100644 index 0000000000..f81b41d41a --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/await-hydrate-maybe-promise/_config.js @@ -0,0 +1,23 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +export default test({ + ssrHtml: '

42


loading...

', + html: '

loading...


42

', + + props: { + browser: true + }, + + server_props: { + browser: false + }, + + async test({ assert, target }) { + const button = target.querySelector('button'); + + flushSync(() => button?.click()); + await Promise.resolve(); + assert.htmlEqual(target.innerHTML, '

42


42

'); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/await-hydrate-maybe-promise/main.svelte b/packages/svelte/tests/runtime-runes/samples/await-hydrate-maybe-promise/main.svelte new file mode 100644 index 0000000000..d8d0cd4027 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/await-hydrate-maybe-promise/main.svelte @@ -0,0 +1,25 @@ + + + + +{#await a} + {#if true}

loading...

{/if} +{:then a} +

{a}

+{/await} + +
+ +{#await b} + {#if true}

loading...

{/if} +{:then b} +

{b}

+{/await} diff --git a/packages/svelte/tests/snapshot/samples/await-block-scope/_expected/server/index.svelte.js b/packages/svelte/tests/snapshot/samples/await-block-scope/_expected/server/index.svelte.js index 012789a550..4b6e32d58e 100644 --- a/packages/svelte/tests/snapshot/samples/await-block-scope/_expected/server/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/await-block-scope/_expected/server/index.svelte.js @@ -8,7 +8,7 @@ export default function Await_block_scope($$payload) { counter.count += 1; } - $$payload.out += ` `; - $.await(promise, () => {}, (counter) => {}, () => {}); - $$payload.out += ` ${$.escape(counter.count)}`; + $$payload.out += ` `; + $.await($$payload, promise, () => {}, (counter) => {}); + $$payload.out += ` ${$.escape(counter.count)}`; } \ No newline at end of file From c2c83b67f3470fa27afe998e079a48de47b7d9c1 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 10 Apr 2025 15:39:50 -0400 Subject: [PATCH 36/48] Version Packages (#15726) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .changeset/wild-carrots-eat.md | 5 ----- .changeset/wise-turkeys-yell.md | 5 ----- packages/svelte/CHANGELOG.md | 8 ++++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 5 files changed, 10 insertions(+), 12 deletions(-) delete mode 100644 .changeset/wild-carrots-eat.md delete mode 100644 .changeset/wise-turkeys-yell.md diff --git a/.changeset/wild-carrots-eat.md b/.changeset/wild-carrots-eat.md deleted file mode 100644 index 23b55f945c..0000000000 --- a/.changeset/wild-carrots-eat.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: handle hydration mismatches in await blocks diff --git a/.changeset/wise-turkeys-yell.md b/.changeset/wise-turkeys-yell.md deleted file mode 100644 index cd5e103de3..0000000000 --- a/.changeset/wise-turkeys-yell.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: prevent ownership warnings if the fallback of a bindable is used diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 6f999f381e..3066c19bce 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,13 @@ # svelte +## 5.25.11 + +### Patch Changes + +- fix: handle hydration mismatches in await blocks ([#15708](https://github.com/sveltejs/svelte/pull/15708)) + +- fix: prevent ownership warnings if the fallback of a bindable is used ([#15720](https://github.com/sveltejs/svelte/pull/15720)) + ## 5.25.10 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index b9f434e688..187d3aaa2c 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.25.10", + "version": "5.25.11", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index 2ea9890df9..cc5f8f775c 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.25.10'; +export const VERSION = '5.25.11'; export const PUBLIC_VERSION = '5'; From 9cafdd89d0e8f2fb0ce02e5b5112498218d4f2b5 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Thu, 10 Apr 2025 22:18:38 +0100 Subject: [PATCH 37/48] fix: improve internal_set versioning mechanic (#15724) --- .changeset/pretty-planes-visit.md | 5 +++++ .../src/internal/client/reactivity/sources.js | 3 ++- .../samples/writable-derived-2/_config.js | 7 +++++++ .../samples/writable-derived-2/main.svelte | 9 +++++++++ .../samples/writable-derived-2/util.svelte.js | 17 +++++++++++++++++ 5 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 .changeset/pretty-planes-visit.md create mode 100644 packages/svelte/tests/runtime-runes/samples/writable-derived-2/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/writable-derived-2/main.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/writable-derived-2/util.svelte.js diff --git a/.changeset/pretty-planes-visit.md b/.changeset/pretty-planes-visit.md new file mode 100644 index 0000000000..d2ee0cae2e --- /dev/null +++ b/.changeset/pretty-planes-visit.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: improve internal_set versioning mechanic diff --git a/packages/svelte/src/internal/client/reactivity/sources.js b/packages/svelte/src/internal/client/reactivity/sources.js index cae49c1832..27e8fd824d 100644 --- a/packages/svelte/src/internal/client/reactivity/sources.js +++ b/packages/svelte/src/internal/client/reactivity/sources.js @@ -162,7 +162,6 @@ export function internal_set(source, value) { } source.v = value; - source.wv = increment_write_version(); if (DEV && tracing_mode_flag) { source.updated = get_stack('UpdatedAt'); @@ -180,6 +179,8 @@ export function internal_set(source, value) { set_signal_status(source, (source.f & UNOWNED) === 0 ? CLEAN : MAYBE_DIRTY); } + source.wv = increment_write_version(); + mark_reactions(source, DIRTY); // It's possible that the current reaction might not have up-to-date dependencies diff --git a/packages/svelte/tests/runtime-runes/samples/writable-derived-2/_config.js b/packages/svelte/tests/runtime-runes/samples/writable-derived-2/_config.js new file mode 100644 index 0000000000..fde3e7b1ea --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/writable-derived-2/_config.js @@ -0,0 +1,7 @@ +import { test } from '../../test'; + +export default test({ + html: `true true`, + + test({ assert, target, window }) {} +}); diff --git a/packages/svelte/tests/runtime-runes/samples/writable-derived-2/main.svelte b/packages/svelte/tests/runtime-runes/samples/writable-derived-2/main.svelte new file mode 100644 index 0000000000..741aa69125 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/writable-derived-2/main.svelte @@ -0,0 +1,9 @@ + + +{expect1} {expect2} diff --git a/packages/svelte/tests/runtime-runes/samples/writable-derived-2/util.svelte.js b/packages/svelte/tests/runtime-runes/samples/writable-derived-2/util.svelte.js new file mode 100644 index 0000000000..8e862753ab --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/writable-derived-2/util.svelte.js @@ -0,0 +1,17 @@ +export const createAppState = (options) => { + const source = $derived(options.source()); + let value = $derived(source); + + return { + get value() { + return value; + }, + onChange(nextValue) { + value = nextValue; + } + }; +}; + +const result = createAppState({ source: () => 'wrong' }); +result.onChange('right'); +export const expect2 = result.value === 'right'; From 73acf6e7f48957d39d9e261083d652e89bbc94c3 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 10 Apr 2025 17:26:25 -0400 Subject: [PATCH 38/48] nicer sandbox output (#15730) --- playgrounds/sandbox/run.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/playgrounds/sandbox/run.js b/playgrounds/sandbox/run.js index 8fa2c2a2da..3a62286b24 100644 --- a/playgrounds/sandbox/run.js +++ b/playgrounds/sandbox/run.js @@ -56,7 +56,7 @@ for (const generate of /** @type {const} */ (['client', 'server'])) { }); write( - `${cwd}/output/${file}.json`, + `${cwd}/output/ast/${file}.json`, JSON.stringify( ast, (key, value) => (typeof value === 'bigint' ? ['BigInt', value.toString()] : value), @@ -66,7 +66,7 @@ for (const generate of /** @type {const} */ (['client', 'server'])) { try { const migrated = migrate(source); - write(`${cwd}/output/${file}.migrated.svelte`, migrated.code); + write(`${cwd}/output/migrated/${file}`, migrated.code); } catch (e) { console.warn(`Error migrating ${file}`, e); } From 0d233e58cf4472cb922118ad76509500273f0d60 Mon Sep 17 00:00:00 2001 From: Paolo Ricciuti Date: Fri, 11 Apr 2025 00:17:22 +0200 Subject: [PATCH 39/48] fix: don't transform reassigned state in labeled statement in `$derived` (#15725) * fix: don't transform reassigned state in labeled statement in `$derived` * fix type so optional chaining is unnecessary * drive-by tidy up * drive-by tidy up --------- Co-authored-by: Rich Harris --- .changeset/weak-doors-yell.md | 5 ++ packages/svelte/src/compiler/migrate/index.js | 72 +++++++++---------- .../input.svelte | 6 ++ .../output.svelte | 10 +++ 4 files changed, 57 insertions(+), 36 deletions(-) create mode 100644 .changeset/weak-doors-yell.md create mode 100644 packages/svelte/tests/migrate/samples/labeled-statement-reassign-state/input.svelte create mode 100644 packages/svelte/tests/migrate/samples/labeled-statement-reassign-state/output.svelte diff --git a/.changeset/weak-doors-yell.md b/.changeset/weak-doors-yell.md new file mode 100644 index 0000000000..1b21783435 --- /dev/null +++ b/.changeset/weak-doors-yell.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: don't transform reassigned state in labeled statement in `$derived` diff --git a/packages/svelte/src/compiler/migrate/index.js b/packages/svelte/src/compiler/migrate/index.js index 9d79d88b23..523389a25a 100644 --- a/packages/svelte/src/compiler/migrate/index.js +++ b/packages/svelte/src/compiler/migrate/index.js @@ -944,54 +944,53 @@ const instance_script = { node.body.type === 'ExpressionStatement' && node.body.expression.type === 'AssignmentExpression' ) { - const ids = extract_identifiers(node.body.expression.left); - const [, expression_ids] = extract_all_identifiers_from_expression( - node.body.expression.right - ); - const bindings = ids.map((id) => state.scope.get(id.name)); - const reassigned_bindings = bindings.filter((b) => b?.reassigned); + const { left, right } = node.body.expression; - if ( - node.body.expression.right.type !== 'Literal' && - !bindings.some((b) => b?.kind === 'store_sub') && - node.body.expression.left.type !== 'MemberExpression' - ) { - let { start, end } = /** @type {{ start: number, end: number }} */ ( - node.body.expression.right - ); + const ids = extract_identifiers(left); + const [, expression_ids] = extract_all_identifiers_from_expression(right); + const bindings = ids.map((id) => /** @type {Binding} */ (state.scope.get(id.name))); - check_rune_binding('derived'); + if (bindings.every((b) => b.kind === 'legacy_reactive')) { + if ( + right.type !== 'Literal' && + bindings.every((b) => b.kind !== 'store_sub') && + left.type !== 'MemberExpression' + ) { + let { start, end } = /** @type {{ start: number, end: number }} */ (right); - // $derived - state.str.update( - /** @type {number} */ (node.start), - /** @type {number} */ (node.body.expression.start), - 'let ' - ); + check_rune_binding('derived'); - if (node.body.expression.right.type === 'SequenceExpression') { - while (state.str.original[start] !== '(') start -= 1; - while (state.str.original[end - 1] !== ')') end += 1; - } + // $derived + state.str.update( + /** @type {number} */ (node.start), + /** @type {number} */ (node.body.expression.start), + 'let ' + ); + + if (right.type === 'SequenceExpression') { + while (state.str.original[start] !== '(') start -= 1; + while (state.str.original[end - 1] !== ')') end += 1; + } + + state.str.prependRight(start, `$derived(`); - state.str.prependRight(start, `$derived(`); + // in a case like `$: ({ a } = b())`, there's already a trailing parenthesis. + // otherwise, we need to add one + if (state.str.original[/** @type {number} */ (node.body.start)] !== '(') { + state.str.appendLeft(end, `)`); + } - // in a case like `$: ({ a } = b())`, there's already a trailing parenthesis. - // otherwise, we need to add one - if (state.str.original[/** @type {number} */ (node.body.start)] !== '(') { - state.str.appendLeft(end, `)`); + return; } - return; - } else { - for (const binding of reassigned_bindings) { - if (binding && (ids.includes(binding.node) || expression_ids.length === 0)) { + for (const binding of bindings) { + if (binding.reassigned && (ids.includes(binding.node) || expression_ids.length === 0)) { check_rune_binding('state'); const init = binding.kind === 'state' ? ' = $state()' : expression_ids.length === 0 - ? ` = $state(${state.str.original.substring(/** @type {number} */ (node.body.expression.right.start), node.body.expression.right.end)})` + ? ` = $state(${state.str.original.substring(/** @type {number} */ (right.start), right.end)})` : ''; // implicitly-declared variable which we need to make explicit state.str.prependLeft( @@ -1000,7 +999,8 @@ const instance_script = { ); } } - if (expression_ids.length === 0 && !bindings.some((b) => b?.kind === 'store_sub')) { + + if (expression_ids.length === 0 && bindings.every((b) => b.kind !== 'store_sub')) { state.str.remove(/** @type {number} */ (node.start), /** @type {number} */ (node.end)); return; } diff --git a/packages/svelte/tests/migrate/samples/labeled-statement-reassign-state/input.svelte b/packages/svelte/tests/migrate/samples/labeled-statement-reassign-state/input.svelte new file mode 100644 index 0000000000..0b5c13d889 --- /dev/null +++ b/packages/svelte/tests/migrate/samples/labeled-statement-reassign-state/input.svelte @@ -0,0 +1,6 @@ + \ No newline at end of file diff --git a/packages/svelte/tests/migrate/samples/labeled-statement-reassign-state/output.svelte b/packages/svelte/tests/migrate/samples/labeled-statement-reassign-state/output.svelte new file mode 100644 index 0000000000..c2b36a6e30 --- /dev/null +++ b/packages/svelte/tests/migrate/samples/labeled-statement-reassign-state/output.svelte @@ -0,0 +1,10 @@ + \ No newline at end of file From 0020e597e2137cdc8d6c913885978f0ccb966e3f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 10 Apr 2025 18:39:23 -0400 Subject: [PATCH 40/48] Version Packages (#15729) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .changeset/pretty-planes-visit.md | 5 ----- .changeset/weak-doors-yell.md | 5 ----- packages/svelte/CHANGELOG.md | 8 ++++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 5 files changed, 10 insertions(+), 12 deletions(-) delete mode 100644 .changeset/pretty-planes-visit.md delete mode 100644 .changeset/weak-doors-yell.md diff --git a/.changeset/pretty-planes-visit.md b/.changeset/pretty-planes-visit.md deleted file mode 100644 index d2ee0cae2e..0000000000 --- a/.changeset/pretty-planes-visit.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: improve internal_set versioning mechanic diff --git a/.changeset/weak-doors-yell.md b/.changeset/weak-doors-yell.md deleted file mode 100644 index 1b21783435..0000000000 --- a/.changeset/weak-doors-yell.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: don't transform reassigned state in labeled statement in `$derived` diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 3066c19bce..f27a6640bd 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,13 @@ # svelte +## 5.25.12 + +### Patch Changes + +- fix: improve internal_set versioning mechanic ([#15724](https://github.com/sveltejs/svelte/pull/15724)) + +- fix: don't transform reassigned state in labeled statement in `$derived` ([#15725](https://github.com/sveltejs/svelte/pull/15725)) + ## 5.25.11 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 187d3aaa2c..67f6186fc7 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.25.11", + "version": "5.25.12", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index cc5f8f775c..c554e9018c 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.25.11'; +export const VERSION = '5.25.12'; export const PUBLIC_VERSION = '5'; From ec1d85c89e345529b9108b770c68cf89b808f3b0 Mon Sep 17 00:00:00 2001 From: ComputerGuy <63362464+Ocean-OS@users.noreply.github.com> Date: Thu, 10 Apr 2025 16:50:05 -0700 Subject: [PATCH 41/48] fix: add snippet argument validation in dev (#15521) * init * fix * make `Payload` a class * doh * lint * tweak changeset * fix * only export things that should be available on $ * tweak message * fix --------- Co-authored-by: Rich Harris --- .changeset/bright-jeans-compare.md | 5 ++ .../98-reference/.generated/shared-errors.md | 6 ++ .../svelte/messages/shared-errors/errors.md | 4 ++ .../client/visitors/SnippetBlock.js | 17 ++++- .../server/visitors/SnippetBlock.js | 5 +- .../src/internal/client/dev/validation.js | 15 +++++ packages/svelte/src/internal/client/index.js | 1 + .../src/internal/server/blocks/snippet.js | 2 +- packages/svelte/src/internal/server/dev.js | 13 +++- packages/svelte/src/internal/server/index.js | 57 ++--------------- .../svelte/src/internal/server/payload.js | 64 +++++++++++++++++++ .../svelte/src/internal/server/types.d.ts | 13 ---- packages/svelte/src/internal/shared/errors.js | 15 +++++ 13 files changed, 147 insertions(+), 70 deletions(-) create mode 100644 .changeset/bright-jeans-compare.md create mode 100644 packages/svelte/src/internal/client/dev/validation.js create mode 100644 packages/svelte/src/internal/server/payload.js diff --git a/.changeset/bright-jeans-compare.md b/.changeset/bright-jeans-compare.md new file mode 100644 index 0000000000..eaec658e03 --- /dev/null +++ b/.changeset/bright-jeans-compare.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: add snippet argument validation in dev diff --git a/documentation/docs/98-reference/.generated/shared-errors.md b/documentation/docs/98-reference/.generated/shared-errors.md index 0102aafcbc..4c81d7b894 100644 --- a/documentation/docs/98-reference/.generated/shared-errors.md +++ b/documentation/docs/98-reference/.generated/shared-errors.md @@ -30,6 +30,12 @@ This error would be thrown in a setup like this: Here, `List.svelte` is using `{@render children(item)` which means it expects `Parent.svelte` to use snippets. Instead, `Parent.svelte` uses the deprecated `let:` directive. This combination of APIs is incompatible, hence the error. +### invalid_snippet_arguments + +``` +A snippet function was passed invalid arguments. Snippets should only be instantiated via `{@render ...}` +``` + ### lifecycle_outside_component ``` diff --git a/packages/svelte/messages/shared-errors/errors.md b/packages/svelte/messages/shared-errors/errors.md index 8b4c61303a..20f3d193d9 100644 --- a/packages/svelte/messages/shared-errors/errors.md +++ b/packages/svelte/messages/shared-errors/errors.md @@ -26,6 +26,10 @@ This error would be thrown in a setup like this: Here, `List.svelte` is using `{@render children(item)` which means it expects `Parent.svelte` to use snippets. Instead, `Parent.svelte` uses the deprecated `let:` directive. This combination of APIs is incompatible, hence the error. +## invalid_snippet_arguments + +> A snippet function was passed invalid arguments. Snippets should only be instantiated via `{@render ...}` + ## lifecycle_outside_component > `%name%(...)` can only be used during component initialisation diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/SnippetBlock.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/SnippetBlock.js index 7a0d6981b5..7eb043aa5d 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/SnippetBlock.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/SnippetBlock.js @@ -1,4 +1,4 @@ -/** @import { BlockStatement, Expression, Identifier, Pattern, Statement } from 'estree' */ +/** @import { AssignmentPattern, BlockStatement, Expression, Identifier, Statement } from 'estree' */ /** @import { AST } from '#compiler' */ /** @import { ComponentContext } from '../types' */ import { dev } from '../../../../state.js'; @@ -12,7 +12,7 @@ import { get_value } from './shared/declarations.js'; */ export function SnippetBlock(node, context) { // TODO hoist where possible - /** @type {Pattern[]} */ + /** @type {(Identifier | AssignmentPattern)[]} */ const args = [b.id('$$anchor')]; /** @type {BlockStatement} */ @@ -66,7 +66,18 @@ export function SnippetBlock(node, context) { } } } - + if (dev) { + declarations.unshift( + b.stmt( + b.call( + '$.validate_snippet_args', + .../** @type {Identifier[]} */ ( + args.map((arg) => (arg?.type === 'Identifier' ? arg : arg?.left)) + ) + ) + ) + ); + } body = b.block([ ...declarations, .../** @type {BlockStatement} */ (context.visit(node.body, child_state)).body diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/SnippetBlock.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/SnippetBlock.js index eb83917927..cae3e7d79c 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/SnippetBlock.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/SnippetBlock.js @@ -1,6 +1,7 @@ /** @import { BlockStatement } from 'estree' */ /** @import { AST } from '#compiler' */ /** @import { ComponentContext } from '../types.js' */ +import { dev } from '../../../../state.js'; import * as b from '../../../../utils/builders.js'; /** @@ -13,7 +14,9 @@ export function SnippetBlock(node, context) { [b.id('$$payload'), ...node.parameters], /** @type {BlockStatement} */ (context.visit(node.body)) ); - + if (dev) { + fn.body.body.unshift(b.stmt(b.call('$.validate_snippet_args', b.id('$$payload')))); + } // @ts-expect-error - TODO remove this hack once $$render_inner for legacy bindings is gone fn.___snippet = true; diff --git a/packages/svelte/src/internal/client/dev/validation.js b/packages/svelte/src/internal/client/dev/validation.js new file mode 100644 index 0000000000..e41e4c4628 --- /dev/null +++ b/packages/svelte/src/internal/client/dev/validation.js @@ -0,0 +1,15 @@ +import { invalid_snippet_arguments } from '../../shared/errors.js'; +/** + * @param {Node} anchor + * @param {...(()=>any)[]} args + */ +export function validate_snippet_args(anchor, ...args) { + if (typeof anchor !== 'object' || !(anchor instanceof Node)) { + invalid_snippet_arguments(); + } + for (let arg of args) { + if (typeof arg !== 'function') { + invalid_snippet_arguments(); + } + } +} diff --git a/packages/svelte/src/internal/client/index.js b/packages/svelte/src/internal/client/index.js index a865419c5f..e977bf3b0f 100644 --- a/packages/svelte/src/internal/client/index.js +++ b/packages/svelte/src/internal/client/index.js @@ -8,6 +8,7 @@ export { create_ownership_validator } from './dev/ownership.js'; export { check_target, legacy_api } from './dev/legacy.js'; export { trace } from './dev/tracing.js'; export { inspect } from './dev/inspect.js'; +export { validate_snippet_args } from './dev/validation.js'; export { await_block as await } from './dom/blocks/await.js'; export { if_block as if } from './dom/blocks/if.js'; export { key_block as key } from './dom/blocks/key.js'; diff --git a/packages/svelte/src/internal/server/blocks/snippet.js b/packages/svelte/src/internal/server/blocks/snippet.js index 3c5e860790..9e96ae3430 100644 --- a/packages/svelte/src/internal/server/blocks/snippet.js +++ b/packages/svelte/src/internal/server/blocks/snippet.js @@ -1,5 +1,5 @@ /** @import { Snippet } from 'svelte' */ -/** @import { Payload } from '#server' */ +/** @import { Payload } from '../payload' */ /** @import { Getters } from '#shared' */ /** diff --git a/packages/svelte/src/internal/server/dev.js b/packages/svelte/src/internal/server/dev.js index ecf4e67429..34849196b7 100644 --- a/packages/svelte/src/internal/server/dev.js +++ b/packages/svelte/src/internal/server/dev.js @@ -1,10 +1,12 @@ -/** @import { Component, Payload } from '#server' */ +/** @import { Component } from '#server' */ import { FILENAME } from '../../constants.js'; import { is_tag_valid_with_ancestor, is_tag_valid_with_parent } from '../../html-tree-validation.js'; import { current_component } from './context.js'; +import { invalid_snippet_arguments } from '../shared/errors.js'; +import { Payload } from './payload.js'; /** * @typedef {{ @@ -98,3 +100,12 @@ export function push_element(payload, tag, line, column) { export function pop_element() { parent = /** @type {Element} */ (parent).parent; } + +/** + * @param {Payload} payload + */ +export function validate_snippet_args(payload) { + if (typeof payload !== 'object' || !(payload instanceof Payload)) { + invalid_snippet_arguments(); + } +} diff --git a/packages/svelte/src/internal/server/index.js b/packages/svelte/src/internal/server/index.js index ff34c07132..d711778a44 100644 --- a/packages/svelte/src/internal/server/index.js +++ b/packages/svelte/src/internal/server/index.js @@ -1,5 +1,5 @@ /** @import { ComponentType, SvelteComponent } from 'svelte' */ -/** @import { Component, Payload, RenderOutput } from '#server' */ +/** @import { Component, RenderOutput } from '#server' */ /** @import { Store } from '#shared' */ export { FILENAME, HMR } from '../../constants.js'; import { attr, clsx, to_class, to_style } from '../shared/attributes.js'; @@ -17,43 +17,13 @@ import { EMPTY_COMMENT, BLOCK_CLOSE, BLOCK_OPEN, BLOCK_OPEN_ELSE } from './hydra import { validate_store } from '../shared/validate.js'; import { is_boolean_attribute, is_raw_text_element, is_void } from '../../utils.js'; import { reset_elements } from './dev.js'; +import { Payload } from './payload.js'; // https://html.spec.whatwg.org/multipage/syntax.html#attributes-2 // https://infra.spec.whatwg.org/#noncharacter const INVALID_ATTR_NAME_CHAR_REGEX = /[\s'">/=\u{FDD0}-\u{FDEF}\u{FFFE}\u{FFFF}\u{1FFFE}\u{1FFFF}\u{2FFFE}\u{2FFFF}\u{3FFFE}\u{3FFFF}\u{4FFFE}\u{4FFFF}\u{5FFFE}\u{5FFFF}\u{6FFFE}\u{6FFFF}\u{7FFFE}\u{7FFFF}\u{8FFFE}\u{8FFFF}\u{9FFFE}\u{9FFFF}\u{AFFFE}\u{AFFFF}\u{BFFFE}\u{BFFFF}\u{CFFFE}\u{CFFFF}\u{DFFFE}\u{DFFFF}\u{EFFFE}\u{EFFFF}\u{FFFFE}\u{FFFFF}\u{10FFFE}\u{10FFFF}]/u; -/** - * @param {Payload} to_copy - * @returns {Payload} - */ -export function copy_payload({ out, css, head, uid }) { - return { - out, - css: new Set(css), - head: { - title: head.title, - out: head.out, - css: new Set(head.css), - uid: head.uid - }, - uid - }; -} - -/** - * Assigns second payload to first - * @param {Payload} p1 - * @param {Payload} p2 - * @returns {void} - */ -export function assign_payload(p1, p2) { - p1.out = p2.out; - p1.css = p2.css; - p1.head = p2.head; - p1.uid = p2.uid; -} - /** * @param {Payload} payload * @param {string} tag @@ -87,16 +57,6 @@ export function element(payload, tag, attributes_fn = noop, children_fn = noop) */ export let on_destroy = []; -/** - * Creates an ID generator - * @param {string} prefix - * @returns {() => string} - */ -function props_id_generator(prefix) { - let uid = 1; - return () => `${prefix}s${uid++}`; -} - /** * Only available on the server and when compiling with the `server` option. * Takes a component and returns an object with `body` and `head` properties on it, which you can use to populate the HTML when server-rendering your app. @@ -106,14 +66,7 @@ function props_id_generator(prefix) { * @returns {RenderOutput} */ export function render(component, options = {}) { - const uid = props_id_generator(options.idPrefix ? options.idPrefix + '-' : ''); - /** @type {Payload} */ - const payload = { - out: '', - css: new Set(), - head: { title: '', out: '', css: new Set(), uid }, - uid - }; + const payload = new Payload(options.idPrefix ? options.idPrefix + '-' : ''); const prev_on_destroy = on_destroy; on_destroy = []; @@ -545,7 +498,9 @@ export { html } from './blocks/html.js'; export { push, pop } from './context.js'; -export { push_element, pop_element } from './dev.js'; +export { push_element, pop_element, validate_snippet_args } from './dev.js'; + +export { assign_payload, copy_payload } from './payload.js'; export { snapshot } from '../shared/clone.js'; diff --git a/packages/svelte/src/internal/server/payload.js b/packages/svelte/src/internal/server/payload.js new file mode 100644 index 0000000000..03bcaf492e --- /dev/null +++ b/packages/svelte/src/internal/server/payload.js @@ -0,0 +1,64 @@ +export class Payload { + /** @type {Set<{ hash: string; code: string }>} */ + css = new Set(); + out = ''; + uid = () => ''; + + head = { + /** @type {Set<{ hash: string; code: string }>} */ + css: new Set(), + title: '', + out: '', + uid: () => '' + }; + + constructor(id_prefix = '') { + this.uid = props_id_generator(id_prefix); + this.head.uid = this.uid; + } +} + +/** + * Used in legacy mode to handle bindings + * @param {Payload} to_copy + * @returns {Payload} + */ +export function copy_payload({ out, css, head, uid }) { + const payload = new Payload(); + + payload.out = out; + payload.css = new Set(css); + payload.uid = uid; + + payload.head = { + title: head.title, + out: head.out, + css: new Set(head.css), + uid: head.uid + }; + + return payload; +} + +/** + * Assigns second payload to first + * @param {Payload} p1 + * @param {Payload} p2 + * @returns {void} + */ +export function assign_payload(p1, p2) { + p1.out = p2.out; + p1.css = p2.css; + p1.head = p2.head; + p1.uid = p2.uid; +} + +/** + * Creates an ID generator + * @param {string} prefix + * @returns {() => string} + */ +function props_id_generator(prefix) { + let uid = 1; + return () => `${prefix}s${uid++}`; +} diff --git a/packages/svelte/src/internal/server/types.d.ts b/packages/svelte/src/internal/server/types.d.ts index 2fffdbbdf0..6b0fc146c4 100644 --- a/packages/svelte/src/internal/server/types.d.ts +++ b/packages/svelte/src/internal/server/types.d.ts @@ -11,19 +11,6 @@ export interface Component { function?: any; } -export interface Payload { - out: string; - css: Set<{ hash: string; code: string }>; - head: { - title: string; - out: string; - uid: () => string; - css: Set<{ hash: string; code: string }>; - }; - /** Function that generates a unique ID */ - uid: () => string; -} - export interface RenderOutput { /** HTML that goes into the `` */ head: string; diff --git a/packages/svelte/src/internal/shared/errors.js b/packages/svelte/src/internal/shared/errors.js index 26d6822cdb..2e89dc1ad1 100644 --- a/packages/svelte/src/internal/shared/errors.js +++ b/packages/svelte/src/internal/shared/errors.js @@ -17,6 +17,21 @@ export function invalid_default_snippet() { } } +/** + * A snippet function was passed invalid arguments. Snippets should only be instantiated via `{@render ...}` + * @returns {never} + */ +export function invalid_snippet_arguments() { + if (DEV) { + const error = new Error(`invalid_snippet_arguments\nA snippet function was passed invalid arguments. Snippets should only be instantiated via \`{@render ...}\`\nhttps://svelte.dev/e/invalid_snippet_arguments`); + + error.name = 'Svelte error'; + throw error; + } else { + throw new Error(`https://svelte.dev/e/invalid_snippet_arguments`); + } +} + /** * `%name%(...)` can only be used during component initialisation * @param {string} name From 01171096cef8652704b92053e57156ae4ed9e5d9 Mon Sep 17 00:00:00 2001 From: Paolo Ricciuti Date: Fri, 11 Apr 2025 04:42:44 +0200 Subject: [PATCH 42/48] feat: add `css.hasGlobal` to `compile` output (#15450) * feat: add `hasUnscopedGlobalCss` to `compile` metadata * chore: rename to `has_unscoped_global` * fix: handle `-global` keyframes * chore: guard the check if the value is already true * update types * add tests * tweak * tweak * regenerate types * Update .changeset/plenty-hotels-mix.md * fix test, add failing test * fix * fix * fix jsdoc * unused * fix * lint * rename * rename * reduce indirection * tidy up * revert * tweak * lint --------- Co-authored-by: Rich Harris --- .changeset/plenty-hotels-mix.md | 5 ++ .../src/compiler/phases/1-parse/read/style.js | 2 + .../phases/2-analyze/css/css-analyze.js | 70 ++++++++++++++----- .../src/compiler/phases/2-analyze/index.js | 3 +- .../compiler/phases/3-transform/css/index.js | 3 +- .../svelte/src/compiler/phases/types.d.ts | 1 + packages/svelte/src/compiler/types/css.d.ts | 5 ++ packages/svelte/src/compiler/types/index.d.ts | 2 + .../css/samples/global-keyframes/_config.js | 5 ++ .../samples/global-local-nested/_config.js | 5 ++ .../samples/global-local-nested/expected.css | 12 ++++ .../samples/global-local-nested/input.svelte | 15 ++++ .../tests/css/samples/global-local/_config.js | 5 ++ .../css/samples/global-local/expected.css | 8 +++ .../css/samples/global-local/input.svelte | 11 +++ .../samples/global-with-nesting/_config.js | 4 +- .../tests/css/samples/global/_config.js | 5 ++ packages/svelte/tests/css/test.ts | 9 +++ packages/svelte/tests/helpers.js | 4 ++ packages/svelte/types/index.d.ts | 2 + 20 files changed, 155 insertions(+), 21 deletions(-) create mode 100644 .changeset/plenty-hotels-mix.md create mode 100644 packages/svelte/tests/css/samples/global-keyframes/_config.js create mode 100644 packages/svelte/tests/css/samples/global-local-nested/_config.js create mode 100644 packages/svelte/tests/css/samples/global-local-nested/expected.css create mode 100644 packages/svelte/tests/css/samples/global-local-nested/input.svelte create mode 100644 packages/svelte/tests/css/samples/global-local/_config.js create mode 100644 packages/svelte/tests/css/samples/global-local/expected.css create mode 100644 packages/svelte/tests/css/samples/global-local/input.svelte create mode 100644 packages/svelte/tests/css/samples/global/_config.js diff --git a/.changeset/plenty-hotels-mix.md b/.changeset/plenty-hotels-mix.md new file mode 100644 index 0000000000..5e7aa834da --- /dev/null +++ b/.changeset/plenty-hotels-mix.md @@ -0,0 +1,5 @@ +--- +'svelte': minor +--- + +feat: add `css.hasGlobal` to `compile` output 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 f8c39f1b1d..56dbe124b7 100644 --- a/packages/svelte/src/compiler/phases/1-parse/read/style.js +++ b/packages/svelte/src/compiler/phases/1-parse/read/style.js @@ -118,6 +118,7 @@ function read_rule(parser) { metadata: { parent_rule: null, has_local_selectors: false, + has_global_selectors: false, is_global_block: false } }; @@ -342,6 +343,7 @@ function read_selector(parser, inside_pseudo_class = false) { children, metadata: { rule: null, + is_global: false, used: false } }; diff --git a/packages/svelte/src/compiler/phases/2-analyze/css/css-analyze.js b/packages/svelte/src/compiler/phases/2-analyze/css/css-analyze.js index 362ac9dcad..76cb2f56e9 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/css/css-analyze.js +++ b/packages/svelte/src/compiler/phases/2-analyze/css/css-analyze.js @@ -7,13 +7,15 @@ import { is_keyframes_node } from '../../css.js'; import { is_global, is_unscoped_pseudo_class } from './utils.js'; /** - * @typedef {Visitors< - * AST.CSS.Node, - * { - * keyframes: string[]; - * rule: AST.CSS.Rule | null; - * } - * >} CssVisitors + * @typedef {{ + * keyframes: string[]; + * rule: AST.CSS.Rule | null; + * analysis: ComponentAnalysis; + * }} CssState + */ + +/** + * @typedef {Visitors} CssVisitors */ /** @@ -28,6 +30,15 @@ function is_global_block_selector(simple_selector) { ); } +/** + * @param {AST.SvelteNode[]} path + */ +function is_unscoped(path) { + return path + .filter((node) => node.type === 'Rule') + .every((node) => node.metadata.has_global_selectors); +} + /** * * @param {Array} path @@ -42,6 +53,9 @@ const css_visitors = { if (is_keyframes_node(node)) { if (!node.prelude.startsWith('-global-') && !is_in_global_block(context.path)) { context.state.keyframes.push(node.prelude); + } else if (node.prelude.startsWith('-global-')) { + // we don't check if the block.children.length because the keyframe is still added even if empty + context.state.analysis.css.has_global ||= is_unscoped(context.path); } } @@ -99,10 +113,12 @@ const css_visitors = { node.metadata.rule = context.state.rule; - node.metadata.used ||= node.children.every( + node.metadata.is_global = node.children.every( ({ metadata }) => metadata.is_global || metadata.is_global_like ); + node.metadata.used ||= node.metadata.is_global; + if ( node.metadata.rule?.metadata.parent_rule && node.children[0]?.selectors[0]?.type === 'NestingSelector' @@ -190,6 +206,7 @@ const css_visitors = { if (idx !== -1) { is_global_block = true; + for (let i = idx + 1; i < child.selectors.length; i++) { walk(/** @type {AST.CSS.Node} */ (child.selectors[i]), null, { ComplexSelector(node) { @@ -242,16 +259,26 @@ const css_visitors = { } } - context.next({ - ...context.state, - rule: node - }); + const state = { ...context.state, rule: node }; - node.metadata.has_local_selectors = node.prelude.children.some((selector) => { - return selector.children.some( - ({ metadata }) => !metadata.is_global && !metadata.is_global_like - ); - }); + // visit selector list first, to populate child selector metadata + context.visit(node.prelude, state); + + for (const selector of node.prelude.children) { + node.metadata.has_global_selectors ||= selector.metadata.is_global; + node.metadata.has_local_selectors ||= !selector.metadata.is_global; + } + + // if this rule has a ComplexSelector whose RelativeSelector children are all + // `:global(...)`, and the rule contains declarations (rather than just + // nested rules) then the component as a whole includes global CSS + context.state.analysis.css.has_global ||= + node.metadata.has_global_selectors && + node.block.children.filter((child) => child.type === 'Declaration').length > 0 && + is_unscoped(context.path); + + // visit block list, so parent rule metadata is populated + context.visit(node.block, state); }, NestingSelector(node, context) { const rule = /** @type {AST.CSS.Rule} */ (context.state.rule); @@ -289,5 +316,12 @@ const css_visitors = { * @param {ComponentAnalysis} analysis */ export function analyze_css(stylesheet, analysis) { - walk(stylesheet, { keyframes: analysis.css.keyframes, rule: null }, css_visitors); + /** @type {CssState} */ + const css_state = { + keyframes: analysis.css.keyframes, + rule: null, + analysis + }; + + walk(stylesheet, css_state, css_visitors); } diff --git a/packages/svelte/src/compiler/phases/2-analyze/index.js b/packages/svelte/src/compiler/phases/2-analyze/index.js index c62fb03e8f..a6eb9565cb 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/index.js +++ b/packages/svelte/src/compiler/phases/2-analyze/index.js @@ -456,7 +456,8 @@ export function analyze_component(root, source, options) { hash }) : '', - keyframes: [] + keyframes: [], + has_global: false }, source, undefined_exports: new Map(), diff --git a/packages/svelte/src/compiler/phases/3-transform/css/index.js b/packages/svelte/src/compiler/phases/3-transform/css/index.js index 5b0dcd5588..dff034f8aa 100644 --- a/packages/svelte/src/compiler/phases/3-transform/css/index.js +++ b/packages/svelte/src/compiler/phases/3-transform/css/index.js @@ -59,7 +59,8 @@ export function render_stylesheet(source, analysis, options) { // generateMap takes care of calculating source relative to file source: options.filename, file: options.cssOutputFilename || options.filename - }) + }), + hasGlobal: analysis.css.has_global }; merge_with_preprocessor_map(css, options, css.map.sources[0]); diff --git a/packages/svelte/src/compiler/phases/types.d.ts b/packages/svelte/src/compiler/phases/types.d.ts index f09b881303..f98cbe1415 100644 --- a/packages/svelte/src/compiler/phases/types.d.ts +++ b/packages/svelte/src/compiler/phases/types.d.ts @@ -74,6 +74,7 @@ export interface ComponentAnalysis extends Analysis { ast: AST.CSS.StyleSheet | null; hash: string; keyframes: string[]; + has_global: boolean; }; source: string; undefined_exports: Map; diff --git a/packages/svelte/src/compiler/types/css.d.ts b/packages/svelte/src/compiler/types/css.d.ts index 7b2e6ae5f7..154a06ffb1 100644 --- a/packages/svelte/src/compiler/types/css.d.ts +++ b/packages/svelte/src/compiler/types/css.d.ts @@ -34,6 +34,10 @@ export namespace _CSS { metadata: { parent_rule: null | Rule; has_local_selectors: boolean; + /** + * `true` if the rule contains a ComplexSelector whose RelativeSelectors are all global or global-like + */ + has_global_selectors: boolean; /** * `true` if the rule contains a `:global` selector, and therefore everything inside should be unscoped */ @@ -64,6 +68,7 @@ export namespace _CSS { /** @internal */ metadata: { rule: null | Rule; + is_global: boolean; /** True if this selector applies to an element. For global selectors, this is defined in css-analyze, for others in css-prune while scoping */ used: boolean; }; diff --git a/packages/svelte/src/compiler/types/index.d.ts b/packages/svelte/src/compiler/types/index.d.ts index eec41bad9d..616c346ad3 100644 --- a/packages/svelte/src/compiler/types/index.d.ts +++ b/packages/svelte/src/compiler/types/index.d.ts @@ -18,6 +18,8 @@ export interface CompileResult { code: string; /** A source map */ map: SourceMap; + /** Whether or not the CSS includes global rules */ + hasGlobal: boolean; }; /** * An array of warning objects that were generated during compilation. Each warning has several properties: diff --git a/packages/svelte/tests/css/samples/global-keyframes/_config.js b/packages/svelte/tests/css/samples/global-keyframes/_config.js new file mode 100644 index 0000000000..30953854ad --- /dev/null +++ b/packages/svelte/tests/css/samples/global-keyframes/_config.js @@ -0,0 +1,5 @@ +import { test } from '../../test'; + +export default test({ + hasGlobal: true +}); diff --git a/packages/svelte/tests/css/samples/global-local-nested/_config.js b/packages/svelte/tests/css/samples/global-local-nested/_config.js new file mode 100644 index 0000000000..5a7796ebac --- /dev/null +++ b/packages/svelte/tests/css/samples/global-local-nested/_config.js @@ -0,0 +1,5 @@ +import { test } from '../../test'; + +export default test({ + hasGlobal: false +}); diff --git a/packages/svelte/tests/css/samples/global-local-nested/expected.css b/packages/svelte/tests/css/samples/global-local-nested/expected.css new file mode 100644 index 0000000000..8eadf2b948 --- /dev/null +++ b/packages/svelte/tests/css/samples/global-local-nested/expected.css @@ -0,0 +1,12 @@ + + div.svelte-xyz { + .whatever { + color: green; + } + } + + .whatever { + div.svelte-xyz { + color: green; + } + } diff --git a/packages/svelte/tests/css/samples/global-local-nested/input.svelte b/packages/svelte/tests/css/samples/global-local-nested/input.svelte new file mode 100644 index 0000000000..60210be753 --- /dev/null +++ b/packages/svelte/tests/css/samples/global-local-nested/input.svelte @@ -0,0 +1,15 @@ +
{@html whatever}
+ + diff --git a/packages/svelte/tests/css/samples/global-local/_config.js b/packages/svelte/tests/css/samples/global-local/_config.js new file mode 100644 index 0000000000..5a7796ebac --- /dev/null +++ b/packages/svelte/tests/css/samples/global-local/_config.js @@ -0,0 +1,5 @@ +import { test } from '../../test'; + +export default test({ + hasGlobal: false +}); diff --git a/packages/svelte/tests/css/samples/global-local/expected.css b/packages/svelte/tests/css/samples/global-local/expected.css new file mode 100644 index 0000000000..c4fc74fb1a --- /dev/null +++ b/packages/svelte/tests/css/samples/global-local/expected.css @@ -0,0 +1,8 @@ + + div.svelte-xyz .whatever { + color: green; + } + + .whatever div.svelte-xyz { + color: green; + } diff --git a/packages/svelte/tests/css/samples/global-local/input.svelte b/packages/svelte/tests/css/samples/global-local/input.svelte new file mode 100644 index 0000000000..bff97ab485 --- /dev/null +++ b/packages/svelte/tests/css/samples/global-local/input.svelte @@ -0,0 +1,11 @@ +
{@html whatever}
+ + diff --git a/packages/svelte/tests/css/samples/global-with-nesting/_config.js b/packages/svelte/tests/css/samples/global-with-nesting/_config.js index 292c6c49ac..6cec7c2360 100644 --- a/packages/svelte/tests/css/samples/global-with-nesting/_config.js +++ b/packages/svelte/tests/css/samples/global-with-nesting/_config.js @@ -1,5 +1,7 @@ import { test } from '../../test'; export default test({ - warnings: [] + warnings: [], + + hasGlobal: false }); diff --git a/packages/svelte/tests/css/samples/global/_config.js b/packages/svelte/tests/css/samples/global/_config.js new file mode 100644 index 0000000000..30953854ad --- /dev/null +++ b/packages/svelte/tests/css/samples/global/_config.js @@ -0,0 +1,5 @@ +import { test } from '../../test'; + +export default test({ + hasGlobal: true +}); diff --git a/packages/svelte/tests/css/test.ts b/packages/svelte/tests/css/test.ts index dd51f52eab..8846b1d986 100644 --- a/packages/svelte/tests/css/test.ts +++ b/packages/svelte/tests/css/test.ts @@ -34,6 +34,7 @@ interface CssTest extends BaseTest { compileOptions?: Partial; warnings?: Warning[]; props?: Record; + hasGlobal?: boolean; } /** @@ -78,6 +79,14 @@ const { test, run } = suite(async (config, cwd) => { // assert_html_equal(actual_ssr, expected.html); } + if (config.hasGlobal !== undefined) { + const metadata = JSON.parse( + fs.readFileSync(`${cwd}/_output/client/input.svelte.css.json`, 'utf-8') + ); + + assert.equal(metadata.hasGlobal, config.hasGlobal); + } + const dom_css = fs.readFileSync(`${cwd}/_output/client/input.svelte.css`, 'utf-8').trim(); const ssr_css = fs.readFileSync(`${cwd}/_output/server/input.svelte.css`, 'utf-8').trim(); diff --git a/packages/svelte/tests/helpers.js b/packages/svelte/tests/helpers.js index 87bcb473e7..f853d5873c 100644 --- a/packages/svelte/tests/helpers.js +++ b/packages/svelte/tests/helpers.js @@ -146,6 +146,10 @@ export async function compile_directory( if (compiled.css) { write(`${output_dir}/${file}.css`, compiled.css.code); + write( + `${output_dir}/${file}.css.json`, + JSON.stringify({ hasGlobal: compiled.css.hasGlobal }) + ); if (output_map) { write(`${output_dir}/${file}.css.map`, JSON.stringify(compiled.css.map, null, '\t')); } diff --git a/packages/svelte/types/index.d.ts b/packages/svelte/types/index.d.ts index c6000fc4b6..6f12daf187 100644 --- a/packages/svelte/types/index.d.ts +++ b/packages/svelte/types/index.d.ts @@ -753,6 +753,8 @@ declare module 'svelte/compiler' { code: string; /** A source map */ map: SourceMap; + /** Whether or not the CSS includes global rules */ + hasGlobal: boolean; }; /** * An array of warning objects that were generated during compilation. Each warning has several properties: From 02448a9acdf781ea44b696d4a60f387b8856e82c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 10 Apr 2025 23:19:23 -0400 Subject: [PATCH 43/48] Version Packages (#15731) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .changeset/bright-jeans-compare.md | 5 ----- .changeset/plenty-hotels-mix.md | 5 ----- packages/svelte/CHANGELOG.md | 10 ++++++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 5 files changed, 12 insertions(+), 12 deletions(-) delete mode 100644 .changeset/bright-jeans-compare.md delete mode 100644 .changeset/plenty-hotels-mix.md diff --git a/.changeset/bright-jeans-compare.md b/.changeset/bright-jeans-compare.md deleted file mode 100644 index eaec658e03..0000000000 --- a/.changeset/bright-jeans-compare.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: add snippet argument validation in dev diff --git a/.changeset/plenty-hotels-mix.md b/.changeset/plenty-hotels-mix.md deleted file mode 100644 index 5e7aa834da..0000000000 --- a/.changeset/plenty-hotels-mix.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': minor ---- - -feat: add `css.hasGlobal` to `compile` output diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index f27a6640bd..68a8803663 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,15 @@ # svelte +## 5.26.0 + +### Minor Changes + +- feat: add `css.hasGlobal` to `compile` output ([#15450](https://github.com/sveltejs/svelte/pull/15450)) + +### Patch Changes + +- fix: add snippet argument validation in dev ([#15521](https://github.com/sveltejs/svelte/pull/15521)) + ## 5.25.12 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 67f6186fc7..8267cb5218 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.25.12", + "version": "5.26.0", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index c554e9018c..ade51aaf17 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.25.12'; +export const VERSION = '5.26.0'; export const PUBLIC_VERSION = '5'; From 95a020acead1a403ae2d4320bea57227e52143ec Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Fri, 11 Apr 2025 10:01:51 -0400 Subject: [PATCH 44/48] fix: update `state_referenced_locally` message (#15733) * fix: update state_referenced_locally message * changeset * update message --- .changeset/stale-gorillas-judge.md | 5 ++++ .../.generated/compile-warnings.md | 9 +++--- .../messages/compile-warnings/script.md | 9 +++--- .../phases/2-analyze/visitors/Identifier.js | 30 ++++++++++++++++++- packages/svelte/src/compiler/warnings.js | 8 +++-- .../static-state-reference/input.svelte | 1 + .../static-state-reference/warnings.json | 24 +++++++++++---- 7 files changed, 68 insertions(+), 18 deletions(-) create mode 100644 .changeset/stale-gorillas-judge.md diff --git a/.changeset/stale-gorillas-judge.md b/.changeset/stale-gorillas-judge.md new file mode 100644 index 0000000000..3d91f401dd --- /dev/null +++ b/.changeset/stale-gorillas-judge.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: update `state_referenced_locally` message diff --git a/documentation/docs/98-reference/.generated/compile-warnings.md b/documentation/docs/98-reference/.generated/compile-warnings.md index 57396bd7fd..0e94cbadb2 100644 --- a/documentation/docs/98-reference/.generated/compile-warnings.md +++ b/documentation/docs/98-reference/.generated/compile-warnings.md @@ -823,15 +823,16 @@ See [the migration guide](v5-migration-guide#Snippets-instead-of-slots) for more ### state_referenced_locally ``` -State referenced in its own scope will never update. Did you mean to reference it inside a closure? +This reference only captures the initial value of `%name%`. Did you mean to reference it inside a %type% instead? ``` This warning is thrown when the compiler detects the following: + - A reactive variable is declared -- the variable is reassigned -- the variable is referenced inside the same scope it is declared and it is a non-reactive context +- ...and later reassigned... +- ...and referenced in the same scope -In this case, the state reassignment will not be noticed by whatever you passed it to. For example, if you pass the state to a function, that function will not notice the updates: +This 'breaks the link' to the original state declaration. For example, if you pass the state to a function, the function loses access to the state once it is reassigned: ```svelte diff --git a/packages/svelte/messages/compile-warnings/script.md b/packages/svelte/messages/compile-warnings/script.md index 8c32fb7082..6603759156 100644 --- a/packages/svelte/messages/compile-warnings/script.md +++ b/packages/svelte/messages/compile-warnings/script.md @@ -54,14 +54,15 @@ To fix this, wrap your variable declaration with `$state`. ## state_referenced_locally -> State referenced in its own scope will never update. Did you mean to reference it inside a closure? +> This reference only captures the initial value of `%name%`. Did you mean to reference it inside a %type% instead? This warning is thrown when the compiler detects the following: + - A reactive variable is declared -- the variable is reassigned -- the variable is referenced inside the same scope it is declared and it is a non-reactive context +- ...and later reassigned... +- ...and referenced in the same scope -In this case, the state reassignment will not be noticed by whatever you passed it to. For example, if you pass the state to a function, that function will not notice the updates: +This 'breaks the link' to the original state declaration. For example, if you pass the state to a function, the function loses access to the state once it is reassigned: ```svelte 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 79dccd5a7c..dcbe564543 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/Identifier.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/Identifier.js @@ -7,6 +7,7 @@ import * as e from '../../../errors.js'; import * as w from '../../../warnings.js'; import { is_rune } from '../../../../utils.js'; import { mark_subtree_dynamic } from './shared/fragment.js'; +import { get_rune } from '../../scope.js'; /** * @param {Identifier} node @@ -111,7 +112,34 @@ export function Identifier(node, context) { (parent.type !== 'AssignmentExpression' || parent.left !== node) && parent.type !== 'UpdateExpression' ) { - w.state_referenced_locally(node); + let type = 'closure'; + + let i = context.path.length; + while (i--) { + const parent = context.path[i]; + + if ( + parent.type === 'ArrowFunctionExpression' || + parent.type === 'FunctionDeclaration' || + parent.type === 'FunctionExpression' + ) { + break; + } + + if ( + parent.type === 'CallExpression' && + parent.arguments.includes(/** @type {any} */ (context.path[i + 1])) + ) { + const rune = get_rune(parent, context.state.scope); + + if (rune === '$state' || rune === '$state.raw') { + type = 'derived'; + break; + } + } + } + + w.state_referenced_locally(node, node.name, type); } if ( diff --git a/packages/svelte/src/compiler/warnings.js b/packages/svelte/src/compiler/warnings.js index a9ea617d3f..e6fc8caba5 100644 --- a/packages/svelte/src/compiler/warnings.js +++ b/packages/svelte/src/compiler/warnings.js @@ -641,11 +641,13 @@ export function reactive_declaration_module_script_dependency(node) { } /** - * State referenced in its own scope will never update. Did you mean to reference it inside a closure? + * This reference only captures the initial value of `%name%`. Did you mean to reference it inside a %type% instead? * @param {null | NodeLike} node + * @param {string} name + * @param {string} type */ -export function state_referenced_locally(node) { - w(node, 'state_referenced_locally', `State referenced in its own scope will never update. Did you mean to reference it inside a closure?\nhttps://svelte.dev/e/state_referenced_locally`); +export function state_referenced_locally(node, name, type) { + w(node, 'state_referenced_locally', `This reference only captures the initial value of \`${name}\`. Did you mean to reference it inside a ${type} instead?\nhttps://svelte.dev/e/state_referenced_locally`); } /** diff --git a/packages/svelte/tests/validator/samples/static-state-reference/input.svelte b/packages/svelte/tests/validator/samples/static-state-reference/input.svelte index cd0c7b7349..577527ee60 100644 --- a/packages/svelte/tests/validator/samples/static-state-reference/input.svelte +++ b/packages/svelte/tests/validator/samples/static-state-reference/input.svelte @@ -2,6 +2,7 @@ let obj = $state({ a: 0 }); let count = $state(0); let doubled = $derived(count * 2); + let tripled = $state(count * 3); console.log(obj); console.log(count); diff --git a/packages/svelte/tests/validator/samples/static-state-reference/warnings.json b/packages/svelte/tests/validator/samples/static-state-reference/warnings.json index 9ba0941519..a118d5e4a0 100644 --- a/packages/svelte/tests/validator/samples/static-state-reference/warnings.json +++ b/packages/svelte/tests/validator/samples/static-state-reference/warnings.json @@ -1,26 +1,38 @@ [ { "code": "state_referenced_locally", - "message": "State referenced in its own scope will never update. Did you mean to reference it inside a closure?", + "message": "This reference only captures the initial value of `count`. Did you mean to reference it inside a derived instead?", + "start": { + "column": 22, + "line": 5 + }, + "end": { + "column": 27, + "line": 5 + } + }, + { + "code": "state_referenced_locally", + "message": "This reference only captures the initial value of `count`. Did you mean to reference it inside a closure instead?", "start": { "column": 13, - "line": 7 + "line": 8 }, "end": { "column": 18, - "line": 7 + "line": 8 } }, { "code": "state_referenced_locally", - "message": "State referenced in its own scope will never update. Did you mean to reference it inside a closure?", + "message": "This reference only captures the initial value of `doubled`. Did you mean to reference it inside a closure instead?", "start": { "column": 13, - "line": 8 + "line": 9 }, "end": { "column": 20, - "line": 8 + "line": 9 } } ] From 9b2507131ca0134b59cef7da1fdd59603c519f0a Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Fri, 11 Apr 2025 10:50:11 -0400 Subject: [PATCH 45/48] chore: remove unused documentation markdown files (#15738) --- .../docs/01-introduction/xx-props.md | 139 ----------------- .../xx-reactivity-fundamentals.md | 144 ------------------ .../03-template-syntax/xx-control-flow.md | 111 -------------- .../03-template-syntax/xx-data-fetching.md | 20 --- .../docs/07-misc/xx-reactivity-indepth.md | 6 - 5 files changed, 420 deletions(-) delete mode 100644 documentation/docs/01-introduction/xx-props.md delete mode 100644 documentation/docs/01-introduction/xx-reactivity-fundamentals.md delete mode 100644 documentation/docs/03-template-syntax/xx-control-flow.md delete mode 100644 documentation/docs/03-template-syntax/xx-data-fetching.md delete mode 100644 documentation/docs/07-misc/xx-reactivity-indepth.md diff --git a/documentation/docs/01-introduction/xx-props.md b/documentation/docs/01-introduction/xx-props.md deleted file mode 100644 index cad854d878..0000000000 --- a/documentation/docs/01-introduction/xx-props.md +++ /dev/null @@ -1,139 +0,0 @@ ---- -title: Public API of a component ---- - -### Public API of a component - -Svelte uses the `$props` rune to declare _properties_ or _props_, which means describing the public interface of the component which becomes accessible to consumers of the component. - -> [!NOTE] `$props` is one of several runes, which are special hints for Svelte's compiler to make things reactive. - -```svelte - -``` - -You can specify a fallback value for a prop. It will be used if the component's consumer doesn't specify the prop on the component when instantiating the component, or if the passed value is `undefined` at some point. - -```svelte - -``` - -To get all properties, use rest syntax: - -```svelte - -``` - -You can use reserved words as prop names. - -```svelte - -``` - -If you're using TypeScript, you can declare the prop types: - -```svelte - -``` - -If you're using JavaScript, you can declare the prop types using JSDoc: - -```svelte - -``` - -If you export a `const`, `class` or `function`, it is readonly from outside the component. - -```svelte - -``` - -Readonly props can be accessed as properties on the element, tied to the component using [`bind:this` syntax](bindings#bind:this). - -### Reactive variables - -To change component state and trigger a re-render, just assign to a locally declared variable that was declared using the `$state` rune. - -Update expressions (`count += 1`) and property assignments (`obj.x = y`) have the same effect. - -```svelte - -``` - -Svelte's ` -``` - -If you'd like to react to changes to a prop, use the `$derived` or `$effect` runes instead. - -```svelte - -``` - -For more information on reactivity, read the documentation around runes. diff --git a/documentation/docs/01-introduction/xx-reactivity-fundamentals.md b/documentation/docs/01-introduction/xx-reactivity-fundamentals.md deleted file mode 100644 index d5e67ada71..0000000000 --- a/documentation/docs/01-introduction/xx-reactivity-fundamentals.md +++ /dev/null @@ -1,144 +0,0 @@ ---- -title: Reactivity fundamentals ---- - -Reactivity is at the heart of interactive UIs. When you click a button, you expect some kind of response. It's your job as a developer to make this happen. It's Svelte's job to make your job as intuitive as possible, by providing a good API to express reactive systems. - -## Runes - -Svelte 5 uses _runes_, a powerful set of primitives for controlling reactivity inside your Svelte components and inside `.svelte.js` and `.svelte.ts` modules. - -Runes are function-like symbols that provide instructions to the Svelte compiler. You don't need to import them from anywhere — when you use Svelte, they're part of the language. - -The following sections introduce the most important runes for declare state, derived state and side effects at a high level. For more details refer to the later sections on [state](state) and [side effects](side-effects). - -## `$state` - -Reactive state is declared with the `$state` rune: - -```svelte - - - -``` - -You can also use `$state` in class fields (whether public or private): - -```js -// @errors: 7006 2554 -class Todo { - done = $state(false); - text = $state(); - - constructor(text) { - this.text = text; - } -} -``` - -> [!LEGACY] -> In Svelte 4, state was implicitly reactive if the variable was declared at the top level -> -> ```svelte -> -> -> -> ``` - -## `$derived` - -Derived state is declared with the `$derived` rune: - -```svelte - - - - -

{count} doubled is {doubled}

-``` - -The expression inside `$derived(...)` should be free of side-effects. Svelte will disallow state changes (e.g. `count++`) inside derived expressions. - -As with `$state`, you can mark class fields as `$derived`. - -> [!LEGACY] -> In Svelte 4, you could use reactive statements for this. -> -> ```svelte -> -> -> -> ->

{count} doubled is {doubled}

-> ``` -> -> This only worked at the top level of a component. - -## `$effect` - -To run _side-effects_ when the component is mounted to the DOM, and when values change, we can use the `$effect` rune ([demo](/playground/untitled#H4sIAAAAAAAAE31T24rbMBD9lUG7kAQ2sbdlX7xOYNk_aB_rQhRpbAsU2UiTW0P-vbrYubSlYGzmzMzROTPymdVKo2PFjzMzfIusYB99z14YnfoQuD1qQh-7bmdFQEonrOppVZmKNBI49QthCc-OOOH0LZ-9jxnR6c7eUpOnuv6KeT5JFdcqbvbcBcgDz1jXKGg6ncFyBedYR6IzLrAZwiN5vtSxaJA-EzadfJEjKw11C6GR22-BLH8B_wxdByWpvUYtqqal2XB6RVkG1CoHB6U1WJzbnYFDiwb3aGEdDa3Bm1oH12sQLTcNPp7r56m_00mHocSG97_zd7ICUXonA5fwKbPbkE2ZtMJGGVkEdctzQi4QzSwr9prnFYNk5hpmqVuqPQjNnfOJoMF22lUsrq_UfIN6lfSVyvQ7grB3X2mjMZYO3XO9w-U5iLx42qg29md3BP_ni5P4gy9ikTBlHxjLzAtPDlyYZmRdjAbGq7HprEQ7p64v4LU_guu0kvAkhBim3nMplWl8FreQD-CW20aZR0wq12t-KqDWeBywhvexKC3memmDwlHAv9q4Vo2ZK8KtK0CgX7u9J8wXbzdKv-nRnfF_2baTqlYoWUF2h5efl9-n0O6koAMAAA==)): - -```svelte - - - -``` - -The function passed to `$effect` will run when the component mounts, and will re-run after any changes to the values it reads that were declared with `$state` or `$derived` (including those passed in with `$props`). Re-runs are batched (i.e. changing `color` and `size` in the same moment won't cause two separate runs), and happen after any DOM updates have been applied. - -> [!LEGACY] -> In Svelte 4, you could use reactive statements for this. -> -> ```svelte -> -> -> -> ``` -> -> This only worked at the top level of a component. diff --git a/documentation/docs/03-template-syntax/xx-control-flow.md b/documentation/docs/03-template-syntax/xx-control-flow.md deleted file mode 100644 index b73917997b..0000000000 --- a/documentation/docs/03-template-syntax/xx-control-flow.md +++ /dev/null @@ -1,111 +0,0 @@ ---- -title: Control flow ---- - -- if -- each -- await (or move that into some kind of data loading section?) -- NOT: key (move into transition section, because that's the common use case) - -Svelte augments HTML with control flow blocks to be able to express conditionally rendered content or lists. - -The syntax between these blocks is the same: - -- `{#` denotes the start of a block -- `{:` denotes a different branch part of the block. Depending on the block, there can be multiple of these -- `{/` denotes the end of a block - -## {#if ...} - -## {#each ...} - -```svelte - -{#each expression as name}...{/each} -``` - -```svelte - -{#each expression as name, index}...{/each} -``` - -```svelte - -{#each expression as name (key)}...{/each} -``` - -```svelte - -{#each expression as name, index (key)}...{/each} -``` - -```svelte - -{#each expression as name}...{:else}...{/each} -``` - -Iterating over lists of values can be done with an each block. - -```svelte -

Shopping list

-
    - {#each items as item} -
  • {item.name} x {item.qty}
  • - {/each} -
-``` - -You can use each blocks to iterate over any array or array-like value — that is, any object with a `length` property. - -An each block can also specify an _index_, equivalent to the second argument in an `array.map(...)` callback: - -```svelte -{#each items as item, i} -
  • {i + 1}: {item.name} x {item.qty}
  • -{/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. - -```svelte -{#each items as item (item.id)} -
  • {item.name} x {item.qty}
  • -{/each} - - -{#each items as item, i (item.id)} -
  • {i + 1}: {item.name} x {item.qty}
  • -{/each} -``` - -You can freely use destructuring and rest patterns in each blocks. - -```svelte -{#each items as { id, name, qty }, i (id)} -
  • {i + 1}: {name} x {qty}
  • -{/each} - -{#each objects as { id, ...rest }} -
  • {id}
  • -{/each} - -{#each items as [id, ...rest]} -
  • {id}
  • -{/each} -``` - -An each block can also have an `{:else}` clause, which is rendered if the list is empty. - -```svelte -{#each todos as todo} -

    {todo.text}

    -{:else} -

    No tasks today!

    -{/each} -``` - -It is possible to iterate over iterables like `Map` or `Set`. Iterables need to be finite and static (they shouldn't change while being iterated over). Under the hood, they are transformed to an array using `Array.from` before being passed off to rendering. If you're writing performance-sensitive code, try to avoid iterables and use regular arrays as they are more performant. - -## Other block types - -Svelte also provides [`#snippet`](snippets), [`#key`](transitions-and-animations) and [`#await`](data-fetching) blocks. You can find out more about them in their respective sections. diff --git a/documentation/docs/03-template-syntax/xx-data-fetching.md b/documentation/docs/03-template-syntax/xx-data-fetching.md deleted file mode 100644 index 4526d51335..0000000000 --- a/documentation/docs/03-template-syntax/xx-data-fetching.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -title: Data fetching ---- - -Fetching data is a fundamental part of apps interacting with the outside world. Svelte is unopinionated with how you fetch your data. The simplest way would be using the built-in `fetch` method: - -```svelte - -``` - -While this works, it makes working with promises somewhat unergonomic. Svelte alleviates this problem using the `#await` block. - -## {#await ...} - -## SvelteKit loaders - -Fetching inside your components is great for simple use cases, but it's prone to data loading waterfalls and makes code harder to work with because of the promise handling. SvelteKit solves this problem by providing a opinionated data loading story that is coupled to its router. Learn more about it [in the docs](../kit). diff --git a/documentation/docs/07-misc/xx-reactivity-indepth.md b/documentation/docs/07-misc/xx-reactivity-indepth.md deleted file mode 100644 index b40072552f..0000000000 --- a/documentation/docs/07-misc/xx-reactivity-indepth.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: Reactivity in depth ---- - -- how to think about Runes ("just JavaScript" with added reactivity, what this means for keeping reactivity alive across boundaries) -- signals From fd57eb362ddf6e9e43936a7d098087f5e4720181 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 11 Apr 2025 17:16:49 -0400 Subject: [PATCH 46/48] Version Packages (#15737) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .changeset/stale-gorillas-judge.md | 5 ----- packages/svelte/CHANGELOG.md | 6 ++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) delete mode 100644 .changeset/stale-gorillas-judge.md diff --git a/.changeset/stale-gorillas-judge.md b/.changeset/stale-gorillas-judge.md deleted file mode 100644 index 3d91f401dd..0000000000 --- a/.changeset/stale-gorillas-judge.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: update `state_referenced_locally` message diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 68a8803663..8b46efc94c 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,11 @@ # svelte +## 5.26.1 + +### Patch Changes + +- fix: update `state_referenced_locally` message ([#15733](https://github.com/sveltejs/svelte/pull/15733)) + ## 5.26.0 ### Minor Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 8267cb5218..a06c73429a 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.26.0", + "version": "5.26.1", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index ade51aaf17..e5cb34ecd5 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.26.0'; +export const VERSION = '5.26.1'; export const PUBLIC_VERSION = '5'; From d38504d073e9a6704660a4b98718b577975ce5d7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 11 Apr 2025 17:56:33 -0400 Subject: [PATCH 47/48] chore(deps-dev): bump vite from 5.4.17 to 5.4.18 (#15743) Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 5.4.17 to 5.4.18. - [Release notes](https://github.com/vitejs/vite/releases) - [Changelog](https://github.com/vitejs/vite/blob/v5.4.18/packages/vite/CHANGELOG.md) - [Commits](https://github.com/vitejs/vite/commits/v5.4.18/packages/vite) --- updated-dependencies: - dependency-name: vite dependency-version: 5.4.18 dependency-type: direct:development ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- playgrounds/sandbox/package.json | 2 +- pnpm-lock.yaml | 86 ++++++++++++++++---------------- 2 files changed, 44 insertions(+), 44 deletions(-) diff --git a/playgrounds/sandbox/package.json b/playgrounds/sandbox/package.json index d392349b60..5aee92ab17 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.17", + "vite": "^5.4.18", "vite-plugin-inspect": "^0.8.4" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f0e66d1681..3518b0e57e 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.17(@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.18(@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.17 - version: 5.4.17(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) + 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) vite-plugin-inspect: specifier: ^0.8.4 - version: 0.8.4(rollup@4.39.0)(vite@5.4.17(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)) + 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)) packages: @@ -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.0': - resolution: {integrity: sha512-aO1PVsq7Gm+tcghabUpzEnVSFMCU4/nYIgC2GOatJcllvWfnhrgW0ZEbnTxm36QsikmCN1K/6ZgM7fok2I7xNw==} + '@typescript-eslint/scope-manager@8.29.1': + resolution: {integrity: sha512-2nggXGX5F3YrsGN08pw4XpMLO1Rgtnn4AzTegC2MDesv6q3QaTU5yU7IbS1tf1IwCR0Hv/1EFygLn9ms6LIpDA==} 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.0': - resolution: {integrity: sha512-wcJL/+cOXV+RE3gjCyl/V2G877+2faqvlgtso/ZRbTCnZazh0gXhe+7gbAnfubzN2bNsBtZjDvlh7ero8uIbzg==} + '@typescript-eslint/types@8.29.1': + resolution: {integrity: sha512-VT7T1PuJF1hpYC3AGm2rCgJBjHL3nc+A/bhOp9sGMKfi5v0WufsX/sHCFBfNTx2F+zA6qBc/PD0/kLRLjdt8mQ==} 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.0': - resolution: {integrity: sha512-yOfen3jE9ISZR/hHpU/bmNvTtBW1NjRbkSFdZOksL1N+ybPEE7UVGMwqvS6CP022Rp00Sb0tdiIkhSCe6NI8ow==} + '@typescript-eslint/typescript-estree@8.29.1': + resolution: {integrity: sha512-l1enRoSaUkQxOQnbi0KPUtqeZkSiFlqrx9/3ns2rEDhGKfTa+88RmXqedC1zmVTOWrLc2e6DEJrTA51C9iLH5g==} 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.0': - resolution: {integrity: sha512-gX/A0Mz9Bskm8avSWFcK0gP7cZpbY4AIo6B0hWYFCaIsz750oaiWR4Jr2CI+PQhfW1CpcQr9OlfPS+kMFegjXA==} + '@typescript-eslint/utils@8.29.1': + resolution: {integrity: sha512-QAkFEbytSaB8wnmB+DflhUPz6CLbFWE2SnSCrRMEa+KnXIzDYbpsn++1HGvnfAsUY44doDXmvRkO5shlM/3UfA==} 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.0': - resolution: {integrity: sha512-Sne/pVz8ryR03NFK21VpN88dZ2FdQXOlq3VIklbrTYEt8yXtRFr9tvUhqvCeKjqYk5FSim37sHbooT6vzBTZcg==} + '@typescript-eslint/visitor-keys@8.29.1': + resolution: {integrity: sha512-RGLh5CRaUEf02viP5c1Vh1cMGffQscyHe7HPAzGpfmfflFg1wUz2rYxd+OZqwpeypYvZ8UxSxuIpF++fmOzEcg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@vitest/coverage-v8@2.0.5': @@ -2310,8 +2310,8 @@ packages: terser: optional: true - vite@5.4.17: - resolution: {integrity: sha512-5+VqZryDj4wgCs55o9Lp+p8GE78TLVg0lasCH5xFZ4jacZjtqZa6JUw9/p0WeAojaOfncSM6v77InkFPGnvPvg==} + vite@5.4.18: + resolution: {integrity: sha512-1oDcnEp3lVyHCuQ2YFelM4Alm2o91xNoMncRm1U7S+JdYfYOvbiGZ3/CxGttrOu2M/KcGz7cRC2DoNUA6urmMA==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: @@ -3000,25 +3000,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.17(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)))(svelte@packages+svelte)(vite@5.4.17(@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.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))': dependencies: - '@sveltejs/vite-plugin-svelte': 4.0.0-next.6(svelte@packages+svelte)(vite@5.4.17(@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.18(@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.17(@types/node@20.12.7)(lightningcss@1.23.0)(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) transitivePeerDependencies: - supports-color - '@sveltejs/vite-plugin-svelte@4.0.0-next.6(svelte@packages+svelte)(vite@5.4.17(@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.18(@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.17(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)))(svelte@packages+svelte)(vite@5.4.17(@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.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)) debug: 4.4.0 deepmerge: 4.3.1 kleur: 4.1.5 magic-string: 0.30.17 svelte: link:packages/svelte - vite: 5.4.17(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) - vitefu: 0.2.5(vite@5.4.17(@types/node@20.12.7)(lightningcss@1.23.0)(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) + 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)) transitivePeerDependencies: - supports-color @@ -3088,10 +3088,10 @@ snapshots: '@typescript-eslint/types': 8.26.0 '@typescript-eslint/visitor-keys': 8.26.0 - '@typescript-eslint/scope-manager@8.29.0': + '@typescript-eslint/scope-manager@8.29.1': dependencies: - '@typescript-eslint/types': 8.29.0 - '@typescript-eslint/visitor-keys': 8.29.0 + '@typescript-eslint/types': 8.29.1 + '@typescript-eslint/visitor-keys': 8.29.1 '@typescript-eslint/type-utils@8.26.0(eslint@9.9.1)(typescript@5.5.4)': dependencies: @@ -3106,7 +3106,7 @@ snapshots: '@typescript-eslint/types@8.26.0': {} - '@typescript-eslint/types@8.29.0': {} + '@typescript-eslint/types@8.29.1': {} '@typescript-eslint/typescript-estree@8.26.0(typescript@5.5.4)': dependencies: @@ -3122,10 +3122,10 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/typescript-estree@8.29.0(typescript@5.5.4)': + '@typescript-eslint/typescript-estree@8.29.1(typescript@5.5.4)': dependencies: - '@typescript-eslint/types': 8.29.0 - '@typescript-eslint/visitor-keys': 8.29.0 + '@typescript-eslint/types': 8.29.1 + '@typescript-eslint/visitor-keys': 8.29.1 debug: 4.4.0 fast-glob: 3.3.3 is-glob: 4.0.3 @@ -3147,12 +3147,12 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.29.0(eslint@9.9.1)(typescript@5.5.4)': + '@typescript-eslint/utils@8.29.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.0 - '@typescript-eslint/types': 8.29.0 - '@typescript-eslint/typescript-estree': 8.29.0(typescript@5.5.4) + '@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: 9.9.1 typescript: 5.5.4 transitivePeerDependencies: @@ -3163,9 +3163,9 @@ snapshots: '@typescript-eslint/types': 8.26.0 eslint-visitor-keys: 4.2.0 - '@typescript-eslint/visitor-keys@8.29.0': + '@typescript-eslint/visitor-keys@8.29.1': dependencies: - '@typescript-eslint/types': 8.29.0 + '@typescript-eslint/types': 8.29.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))': @@ -3509,7 +3509,7 @@ 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.0(eslint@9.9.1)(typescript@5.5.4) + '@typescript-eslint/utils': 8.29.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) @@ -4562,7 +4562,7 @@ snapshots: debug: 4.4.0 es-module-lexer: 1.6.0 pathe: 1.1.2 - vite: 5.4.17(@types/node@20.12.7)(lightningcss@1.23.0)(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) transitivePeerDependencies: - '@types/node' - less @@ -4574,7 +4574,7 @@ snapshots: - supports-color - terser - vite-plugin-inspect@0.8.4(rollup@4.39.0)(vite@5.4.17(@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.39.0)(vite@5.4.18(@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) @@ -4585,7 +4585,7 @@ snapshots: perfect-debounce: 1.0.0 picocolors: 1.1.1 sirv: 2.0.4 - vite: 5.4.17(@types/node@20.12.7)(lightningcss@1.23.0)(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) transitivePeerDependencies: - rollup - supports-color @@ -4602,7 +4602,7 @@ snapshots: sass: 1.70.0 terser: 5.27.0 - vite@5.4.17(@types/node@20.12.7)(lightningcss@1.23.0)(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): dependencies: esbuild: 0.21.5 postcss: 8.5.3 @@ -4614,9 +4614,9 @@ snapshots: sass: 1.70.0 terser: 5.27.0 - vitefu@0.2.5(vite@5.4.17(@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)): optionalDependencies: - vite: 5.4.17(@types/node@20.12.7)(lightningcss@1.23.0)(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) 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: From a5240ccfe48ecc2f9eebf3de45ffcd60825bc1b0 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Fri, 11 Apr 2025 18:49:38 -0400 Subject: [PATCH 48/48] use pnpm/action-setup (#15744) --- .github/workflows/pkg.pr.new.yml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/pkg.pr.new.yml b/.github/workflows/pkg.pr.new.yml index b2b521dc6f..b1ba217e5a 100644 --- a/.github/workflows/pkg.pr.new.yml +++ b/.github/workflows/pkg.pr.new.yml @@ -8,10 +8,8 @@ jobs: runs-on: ubuntu-latest steps: - - name: Checkout code - uses: actions/checkout@v4 - - - run: corepack enable + - uses: actions/checkout@v4 + - uses: pnpm/action-setup@v4 - uses: actions/setup-node@v4 with: node-version: 22.x