From 18dd45640b14873ea3485fd1c19b0efaaaccf05f Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 9 Sep 2025 10:28:14 -0400 Subject: [PATCH 001/347] fix: send `$effect.pending` count to the correct boundary (#16732) * fix: send `$effect.pending` count to the correct boundary * make boundary.pending private, use boundary.is_pending consistently * move error to correct place * we need that error * update JSDoc --- .changeset/dirty-cycles-smash.md | 5 + .../src/internal/client/dom/blocks/async.js | 4 +- .../internal/client/dom/blocks/boundary.js | 66 ++++++++----- .../src/internal/client/reactivity/async.js | 4 +- .../src/internal/client/reactivity/batch.js | 11 ++- .../internal/client/reactivity/deriveds.js | 2 +- .../async-effect-pending-nested/_config.js | 95 +++++++++++++++++++ .../async-effect-pending-nested/main.svelte | 34 +++++++ 8 files changed, 189 insertions(+), 32 deletions(-) create mode 100644 .changeset/dirty-cycles-smash.md create mode 100644 packages/svelte/tests/runtime-runes/samples/async-effect-pending-nested/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/async-effect-pending-nested/main.svelte diff --git a/.changeset/dirty-cycles-smash.md b/.changeset/dirty-cycles-smash.md new file mode 100644 index 0000000000..1b031cf0af --- /dev/null +++ b/.changeset/dirty-cycles-smash.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: send `$effect.pending` count to the correct boundary diff --git a/packages/svelte/src/internal/client/dom/blocks/async.js b/packages/svelte/src/internal/client/dom/blocks/async.js index 82f107ab29..5ec50a5988 100644 --- a/packages/svelte/src/internal/client/dom/blocks/async.js +++ b/packages/svelte/src/internal/client/dom/blocks/async.js @@ -1,7 +1,7 @@ /** @import { TemplateNode, Value } from '#client' */ import { flatten } from '../../reactivity/async.js'; import { get } from '../../runtime.js'; -import { get_pending_boundary } from './boundary.js'; +import { get_boundary } from './boundary.js'; /** * @param {TemplateNode} node @@ -9,7 +9,7 @@ import { get_pending_boundary } from './boundary.js'; * @param {(anchor: TemplateNode, ...deriveds: Value[]) => void} fn */ export function async(node, expressions, fn) { - var boundary = get_pending_boundary(); + var boundary = get_boundary(); boundary.update_pending_count(1); diff --git a/packages/svelte/src/internal/client/dom/blocks/boundary.js b/packages/svelte/src/internal/client/dom/blocks/boundary.js index 12ca547608..b7f1803782 100644 --- a/packages/svelte/src/internal/client/dom/blocks/boundary.js +++ b/packages/svelte/src/internal/client/dom/blocks/boundary.js @@ -49,11 +49,11 @@ export function boundary(node, props, children) { } export class Boundary { - pending = false; - /** @type {Boundary | null} */ parent; + #pending = false; + /** @type {TemplateNode} */ #anchor; @@ -81,6 +81,7 @@ export class Boundary { /** @type {DocumentFragment | null} */ #offscreen_fragment = null; + #local_pending_count = 0; #pending_count = 0; #is_creating_fallback = false; @@ -95,12 +96,12 @@ export class Boundary { #effect_pending_update = () => { if (this.#effect_pending) { - internal_set(this.#effect_pending, this.#pending_count); + internal_set(this.#effect_pending, this.#local_pending_count); } }; #effect_pending_subscriber = createSubscriber(() => { - this.#effect_pending = source(this.#pending_count); + this.#effect_pending = source(this.#local_pending_count); if (DEV) { tag(this.#effect_pending, '$effect.pending()'); @@ -125,7 +126,7 @@ export class Boundary { this.parent = /** @type {Effect} */ (active_effect).b; - this.pending = !!this.#props.pending; + this.#pending = !!this.#props.pending; this.#effect = block(() => { /** @type {Effect} */ (active_effect).b = this; @@ -156,7 +157,7 @@ export class Boundary { this.#pending_effect = null; }); - this.pending = false; + this.#pending = false; } }); } else { @@ -169,7 +170,7 @@ export class Boundary { if (this.#pending_count > 0) { this.#show_pending_snippet(); } else { - this.pending = false; + this.#pending = false; } } }, flags); @@ -179,6 +180,14 @@ export class Boundary { } } + /** + * Returns `true` if the effect exists inside a boundary whose pending snippet is shown + * @returns {boolean} + */ + is_pending() { + return this.#pending || (!!this.parent && this.parent.is_pending()); + } + has_pending_snippet() { return !!this.#props.pending; } @@ -220,12 +229,25 @@ export class Boundary { } } - /** @param {1 | -1} d */ + /** + * Updates the pending count associated with the currently visible pending snippet, + * if any, such that we can replace the snippet with content once work is done + * @param {1 | -1} d + */ #update_pending_count(d) { + if (!this.has_pending_snippet()) { + if (this.parent) { + this.parent.#update_pending_count(d); + return; + } + + e.await_outside_boundary(); + } + this.#pending_count += d; if (this.#pending_count === 0) { - this.pending = false; + this.#pending = false; if (this.#pending_effect) { pause_effect(this.#pending_effect, () => { @@ -240,14 +262,16 @@ export class Boundary { } } - /** @param {1 | -1} d */ + /** + * Update the source that powers `$effect.pending()` inside this boundary, + * and controls when the current `pending` snippet (if any) is removed. + * Do not call from inside the class + * @param {1 | -1} d + */ update_pending_count(d) { - if (this.has_pending_snippet()) { - this.#update_pending_count(d); - } else if (this.parent) { - this.parent.#update_pending_count(d); - } + this.#update_pending_count(d); + this.#local_pending_count += d; effect_pending_updates.add(this.#effect_pending_update); } @@ -308,7 +332,7 @@ export class Boundary { }); } - this.pending = true; + this.#pending = true; this.#main_effect = this.#run(() => { this.#is_creating_fallback = false; @@ -318,7 +342,7 @@ export class Boundary { if (this.#pending_count > 0) { this.#show_pending_snippet(); } else { - this.pending = false; + this.#pending = false; } }; @@ -384,12 +408,8 @@ function move_effect(effect, fragment) { } } -export function get_pending_boundary() { - var boundary = /** @type {Effect} */ (active_effect).b; - - while (boundary !== null && !boundary.has_pending_snippet()) { - boundary = boundary.parent; - } +export function get_boundary() { + const boundary = /** @type {Effect} */ (active_effect).b; if (boundary === null) { e.await_outside_boundary(); diff --git a/packages/svelte/src/internal/client/reactivity/async.js b/packages/svelte/src/internal/client/reactivity/async.js index 65d004137f..b7a5d5cdb7 100644 --- a/packages/svelte/src/internal/client/reactivity/async.js +++ b/packages/svelte/src/internal/client/reactivity/async.js @@ -3,7 +3,7 @@ import { DESTROYED } from '#client/constants'; import { DEV } from 'esm-env'; import { component_context, is_runes, set_component_context } from '../context.js'; -import { get_pending_boundary } from '../dom/blocks/boundary.js'; +import { get_boundary } from '../dom/blocks/boundary.js'; import { invoke_error_boundary } from '../error-handling.js'; import { active_effect, @@ -39,7 +39,7 @@ export function flatten(sync, async, fn) { var parent = /** @type {Effect} */ (active_effect); var restore = capture(); - var boundary = get_pending_boundary(); + var boundary = get_boundary(); Promise.all(async.map((expression) => async_derived(expression))) .then((result) => { diff --git a/packages/svelte/src/internal/client/reactivity/batch.js b/packages/svelte/src/internal/client/reactivity/batch.js index 82f1de67a9..5176a4f74b 100644 --- a/packages/svelte/src/internal/client/reactivity/batch.js +++ b/packages/svelte/src/internal/client/reactivity/batch.js @@ -15,7 +15,7 @@ import { } from '#client/constants'; import { async_mode_flag } from '../../flags/index.js'; import { deferred, define_property } from '../../shared/utils.js'; -import { get_pending_boundary } from '../dom/blocks/boundary.js'; +import { get_boundary } from '../dom/blocks/boundary.js'; import { active_effect, is_dirty, @@ -298,7 +298,10 @@ export class Batch { this.#render_effects.push(effect); } else if ((flags & CLEAN) === 0) { if ((flags & ASYNC) !== 0) { - var effects = effect.b?.pending ? this.#boundary_async_effects : this.#async_effects; + var effects = effect.b?.is_pending() + ? this.#boundary_async_effects + : this.#async_effects; + effects.push(effect); } else if (is_dirty(effect)) { if ((effect.f & BLOCK_EFFECT) !== 0) this.#block_effects.push(effect); @@ -668,9 +671,9 @@ export function schedule_effect(signal) { } export function suspend() { - var boundary = get_pending_boundary(); + var boundary = get_boundary(); var batch = /** @type {Batch} */ (current_batch); - var pending = boundary.pending; + var pending = boundary.is_pending(); boundary.update_pending_count(1); if (!pending) batch.increment(); diff --git a/packages/svelte/src/internal/client/reactivity/deriveds.js b/packages/svelte/src/internal/client/reactivity/deriveds.js index 31dc267960..299251a2dc 100644 --- a/packages/svelte/src/internal/client/reactivity/deriveds.js +++ b/packages/svelte/src/internal/client/reactivity/deriveds.js @@ -135,7 +135,7 @@ export function async_derived(fn, location) { prev = promise; var batch = /** @type {Batch} */ (current_batch); - var pending = boundary.pending; + var pending = boundary.is_pending(); if (should_suspend) { boundary.update_pending_count(1); diff --git a/packages/svelte/tests/runtime-runes/samples/async-effect-pending-nested/_config.js b/packages/svelte/tests/runtime-runes/samples/async-effect-pending-nested/_config.js new file mode 100644 index 0000000000..9fe354bac0 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/async-effect-pending-nested/_config.js @@ -0,0 +1,95 @@ +import { tick } from 'svelte'; +import { test } from '../../test'; + +export default test({ + async test({ assert, target }) { + const [increment, shift] = target.querySelectorAll('button'); + + assert.htmlEqual( + target.innerHTML, + ` + + +

loading...

+ ` + ); + + shift.click(); + shift.click(); + shift.click(); + + await tick(); + assert.htmlEqual( + target.innerHTML, + ` + + +

0

+

0

+

0

+

inner pending: 0

+

outer pending: 0

+ ` + ); + + increment.click(); + await tick(); + assert.htmlEqual( + target.innerHTML, + ` + + +

0

+

0

+

0

+

inner pending: 3

+

outer pending: 0

+ ` + ); + + shift.click(); + await tick(); + assert.htmlEqual( + target.innerHTML, + ` + + +

0

+

0

+

0

+

inner pending: 2

+

outer pending: 0

+ ` + ); + + shift.click(); + await tick(); + assert.htmlEqual( + target.innerHTML, + ` + + +

0

+

0

+

0

+

inner pending: 1

+

outer pending: 0

+ ` + ); + + shift.click(); + await tick(); + assert.htmlEqual( + target.innerHTML, + ` + + +

1

+

1

+

1

+

inner pending: 0

+

outer pending: 0

+ ` + ); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/async-effect-pending-nested/main.svelte b/packages/svelte/tests/runtime-runes/samples/async-effect-pending-nested/main.svelte new file mode 100644 index 0000000000..eeafbdc3c4 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/async-effect-pending-nested/main.svelte @@ -0,0 +1,34 @@ + + + + + + + +

{await push(value)}

+

{await push(value)}

+

{await push(value)}

+

inner pending: {$effect.pending()}

+
+

outer pending: {$effect.pending()}

+ + {#snippet pending()} +

loading...

+ {/snippet} +
+ + From 7f9fe999270a727d1129ff11e0cf6bb3701b15e6 Mon Sep 17 00:00:00 2001 From: "Dominik G." Date: Tue, 9 Sep 2025 17:53:35 +0200 Subject: [PATCH 002/347] refactor(release): run with node24 and remove NPM_TOKEN to use oidc publish (#16733) --- .github/workflows/release.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6debe5662a..f9d683a2ae 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -27,7 +27,7 @@ jobs: - name: Setup Node.js uses: actions/setup-node@v4 with: - node-version: 18.x + node-version: 24.x cache: pnpm - name: Install @@ -45,4 +45,3 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} NPM_CONFIG_PROVENANCE: true - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} From f778975b760bed4d5ae366d95ddc8d60e94531f3 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 9 Sep 2025 11:57:51 -0400 Subject: [PATCH 003/347] Version Packages (#16734) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .changeset/dirty-cycles-smash.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/dirty-cycles-smash.md diff --git a/.changeset/dirty-cycles-smash.md b/.changeset/dirty-cycles-smash.md deleted file mode 100644 index 1b031cf0af..0000000000 --- a/.changeset/dirty-cycles-smash.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: send `$effect.pending` count to the correct boundary diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 535214781c..20ae98a500 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,11 @@ # svelte +## 5.38.8 + +### Patch Changes + +- fix: send `$effect.pending` count to the correct boundary ([#16732](https://github.com/sveltejs/svelte/pull/16732)) + ## 5.38.7 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index c6bc40ae2c..02fa445b0e 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.38.7", + "version": "5.38.8", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index d499c06797..8cb69aac5b 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.38.7'; +export const VERSION = '5.38.8'; export const PUBLIC_VERSION = '5'; From f343b00cc6b6d2c14a5b6fe941e75928dd841a83 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Wed, 10 Sep 2025 18:32:09 +0200 Subject: [PATCH 004/347] fix: clean up scheduling system (#16741) Consolidates our scheduling system that had diverged into two - queue_micro_task and Batch.ensure-queues and resolves some edge case race conditions related to flushSync along the way. --- .changeset/old-taxis-relate.md | 5 ++++ .../svelte/src/internal/client/dom/task.js | 20 +++++++++++++-- .../src/internal/client/reactivity/batch.js | 25 +++---------------- .../main.svelte | 5 ++++ .../dynamic-component-transition/_config.js | 5 ++-- .../_config.js | 5 ++-- 6 files changed, 38 insertions(+), 27 deletions(-) create mode 100644 .changeset/old-taxis-relate.md diff --git a/.changeset/old-taxis-relate.md b/.changeset/old-taxis-relate.md new file mode 100644 index 0000000000..f38dc03f5e --- /dev/null +++ b/.changeset/old-taxis-relate.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: clean up scheduling system diff --git a/packages/svelte/src/internal/client/dom/task.js b/packages/svelte/src/internal/client/dom/task.js index 48a2fbe660..938f3ccda2 100644 --- a/packages/svelte/src/internal/client/dom/task.js +++ b/packages/svelte/src/internal/client/dom/task.js @@ -1,4 +1,5 @@ import { run_all } from '../../shared/utils.js'; +import { is_flushing_sync } from '../reactivity/batch.js'; // Fallback for when requestIdleCallback is not available const request_idle_callback = @@ -24,12 +25,27 @@ function run_idle_tasks() { run_all(tasks); } +export function has_pending_tasks() { + return micro_tasks.length > 0 || idle_tasks.length > 0; +} + /** * @param {() => void} fn */ export function queue_micro_task(fn) { - if (micro_tasks.length === 0) { - queueMicrotask(run_micro_tasks); + if (micro_tasks.length === 0 && !is_flushing_sync) { + var tasks = micro_tasks; + queueMicrotask(() => { + // If this is false, a flushSync happened in the meantime. Do _not_ run new scheduled microtasks in that case + // as the ordering of microtasks would be broken at that point - consider this case: + // - queue_micro_task schedules microtask A to flush task X + // - synchronously after, flushSync runs, processing task X + // - synchronously after, some other microtask B is scheduled, but not through queue_micro_task but for example a Promise.resolve() in user code + // - synchronously after, queue_micro_task schedules microtask C to flush task Y + // - one tick later, microtask A now resolves, flushing task Y before microtask B, which is incorrect + // This if check prevents that race condition (that realistically will only happen in tests) + if (tasks === micro_tasks) run_micro_tasks(); + }); } micro_tasks.push(fn); diff --git a/packages/svelte/src/internal/client/reactivity/batch.js b/packages/svelte/src/internal/client/reactivity/batch.js index 5176a4f74b..c28617608e 100644 --- a/packages/svelte/src/internal/client/reactivity/batch.js +++ b/packages/svelte/src/internal/client/reactivity/batch.js @@ -25,7 +25,7 @@ import { update_effect } from '../runtime.js'; import * as e from '../errors.js'; -import { flush_tasks } from '../dom/task.js'; +import { flush_tasks, has_pending_tasks, queue_micro_task } from '../dom/task.js'; import { DEV } from 'esm-env'; import { invoke_error_boundary } from '../error-handling.js'; import { old_values } from './sources.js'; @@ -56,19 +56,6 @@ export let batch_deriveds = null; /** @type {Set<() => void>} */ export let effect_pending_updates = new Set(); -/** @type {Array<() => void>} */ -let tasks = []; - -function dequeue() { - const task = /** @type {() => void} */ (tasks.shift()); - - if (tasks.length > 0) { - queueMicrotask(dequeue); - } - - task(); -} - /** @type {Effect[]} */ let queued_root_effects = []; @@ -76,7 +63,7 @@ let queued_root_effects = []; let last_scheduled_effect = null; let is_flushing = false; -let is_flushing_sync = false; +export let is_flushing_sync = false; export class Batch { /** @@ -470,11 +457,7 @@ export class Batch { /** @param {() => void} task */ static enqueue(task) { - if (tasks.length === 0) { - queueMicrotask(dequeue); - } - - tasks.unshift(task); + queue_micro_task(task); } } @@ -505,7 +488,7 @@ export function flushSync(fn) { while (true) { flush_tasks(); - if (queued_root_effects.length === 0) { + if (queued_root_effects.length === 0 && !has_pending_tasks()) { current_batch?.flush(); // we need to check again, in case we just updated an `$effect.pending()` diff --git a/packages/svelte/tests/runtime-runes/samples/async-top-level-error-nested-obsolete/main.svelte b/packages/svelte/tests/runtime-runes/samples/async-top-level-error-nested-obsolete/main.svelte index 2f461e96c8..2d8032228a 100644 --- a/packages/svelte/tests/runtime-runes/samples/async-top-level-error-nested-obsolete/main.svelte +++ b/packages/svelte/tests/runtime-runes/samples/async-top-level-error-nested-obsolete/main.svelte @@ -3,6 +3,11 @@ export let route = $state({ current: 'home' }); + + diff --git a/packages/svelte/tests/runtime-runes/samples/dynamic-component-transition/_config.js b/packages/svelte/tests/runtime-runes/samples/dynamic-component-transition/_config.js index 9c741d2b8c..19f0c38227 100644 --- a/packages/svelte/tests/runtime-runes/samples/dynamic-component-transition/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/dynamic-component-transition/_config.js @@ -5,7 +5,8 @@ export default test({ async test({ assert, target, raf }) { const btn = target.querySelector('button'); - raf.tick(0); + // one tick to not be at 0. Else the flushSync would revert the in-transition which hasn't started, and directly remove the button + raf.tick(1); flushSync(() => { btn?.click(); @@ -13,7 +14,7 @@ export default test({ assert.htmlEqual(target.innerHTML, `

Outside

`); - raf.tick(100); + raf.tick(101); assert.htmlEqual(target.innerHTML, `

Outside

`); } diff --git a/packages/svelte/tests/runtime-runes/samples/dynamic-if-component-transition/_config.js b/packages/svelte/tests/runtime-runes/samples/dynamic-if-component-transition/_config.js index 9c741d2b8c..19f0c38227 100644 --- a/packages/svelte/tests/runtime-runes/samples/dynamic-if-component-transition/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/dynamic-if-component-transition/_config.js @@ -5,7 +5,8 @@ export default test({ async test({ assert, target, raf }) { const btn = target.querySelector('button'); - raf.tick(0); + // one tick to not be at 0. Else the flushSync would revert the in-transition which hasn't started, and directly remove the button + raf.tick(1); flushSync(() => { btn?.click(); @@ -13,7 +14,7 @@ export default test({ assert.htmlEqual(target.innerHTML, `

Outside

`); - raf.tick(100); + raf.tick(101); assert.htmlEqual(target.innerHTML, `

Outside

`); } From 68d27f1c4f64b070f322229e1f51460d1bf934b3 Mon Sep 17 00:00:00 2001 From: ComputerGuy <63362464+Ocean-OS@users.noreply.github.com> Date: Wed, 10 Sep 2025 13:52:11 -0700 Subject: [PATCH 005/347] chore: generate CSS hash using the filename (#16740) * chore: generate CSS hash using the filename * fix all tests but one * slightly kludgy fix * try this * fix --------- Co-authored-by: Rich Harris --- .changeset/fast-boxes-sort.md | 5 ++++ .../src/compiler/phases/2-analyze/index.js | 20 +++++++++------- packages/svelte/src/compiler/types/index.d.ts | 2 +- .../svelte/src/compiler/validate-options.js | 4 ++-- packages/svelte/tests/html_equal.js | 8 ++++++- .../svelte/tests/runtime-browser/assert.js | 1 + .../_config.js | 24 +++++++++---------- .../_config.js | 20 ++++++++-------- .../_config.js | 24 +++++++++---------- .../_config.js | 20 ++++++++-------- .../custom-element-svelte-class/_config.js | 4 ++-- .../_config.js | 6 ++--- .../_expected.html | 2 +- .../_expected_head.html | 2 +- .../css-injected-options-nested/_expected.css | 2 +- .../_expected.html | 2 +- .../_expected_head.html | 2 +- .../css-injected-options/_expected.css | 2 +- .../css-injected-options/_expected.html | 2 +- .../css-injected-options/_expected_head.html | 2 +- .../samples/css/_expected.css | 2 +- .../samples/css/_expected.html | 2 +- .../samples/css/_expected_head.html | 2 +- .../samples/attached-sourcemap/_config.js | 2 +- .../tests/sourcemaps/samples/css/_config.js | 2 +- packages/svelte/types/index.d.ts | 4 ++-- 26 files changed, 91 insertions(+), 77 deletions(-) create mode 100644 .changeset/fast-boxes-sort.md diff --git a/.changeset/fast-boxes-sort.md b/.changeset/fast-boxes-sort.md new file mode 100644 index 0000000000..1edabf0582 --- /dev/null +++ b/.changeset/fast-boxes-sort.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +chore: generate CSS hash using the filename diff --git a/packages/svelte/src/compiler/phases/2-analyze/index.js b/packages/svelte/src/compiler/phases/2-analyze/index.js index 92b89c588e..de27c4623b 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/index.js +++ b/packages/svelte/src/compiler/phases/2-analyze/index.js @@ -456,10 +456,19 @@ export function analyze_component(root, source, options) { const is_custom_element = !!options.customElementOptions || options.customElement; + const name = module.scope.generate(options.name ?? component_name); + + state.adjust({ + component_name: name, + dev: options.dev, + rootDir: options.rootDir, + runes + }); + // TODO remove all the ?? stuff, we don't need it now that we're validating the config /** @type {ComponentAnalysis} */ const analysis = { - name: module.scope.generate(options.name ?? component_name), + name, root: scope_root, module, instance, @@ -520,7 +529,7 @@ export function analyze_component(root, source, options) { hash: root.css ? options.cssHash({ css: root.css.content.styles, - filename: options.filename, + filename: state.filename, name: component_name, hash }) @@ -534,13 +543,6 @@ export function analyze_component(root, source, options) { async_deriveds: new Set() }; - state.adjust({ - component_name: analysis.name, - dev: options.dev, - rootDir: options.rootDir, - runes - }); - if (!runes) { // every exported `let` or `var` declaration becomes a prop, everything else becomes an export for (const node of instance.ast.body) { diff --git a/packages/svelte/src/compiler/types/index.d.ts b/packages/svelte/src/compiler/types/index.d.ts index 6211e69bd3..e13c9a9e22 100644 --- a/packages/svelte/src/compiler/types/index.d.ts +++ b/packages/svelte/src/compiler/types/index.d.ts @@ -105,7 +105,7 @@ export interface CompileOptions extends ModuleCompileOptions { css?: 'injected' | 'external'; /** * A function that takes a `{ hash, css, name, filename }` argument and returns the string that is used as a classname for scoped CSS. - * It defaults to returning `svelte-${hash(css)}`. + * It defaults to returning `svelte-${hash(filename ?? css)}`. * * @default undefined */ diff --git a/packages/svelte/src/compiler/validate-options.js b/packages/svelte/src/compiler/validate-options.js index 2b727ad093..a94a553311 100644 --- a/packages/svelte/src/compiler/validate-options.js +++ b/packages/svelte/src/compiler/validate-options.js @@ -70,8 +70,8 @@ const component_options = { return input; }), - cssHash: fun(({ css, hash }) => { - return `svelte-${hash(css)}`; + cssHash: fun(({ css, filename, hash }) => { + return `svelte-${hash(filename === '(unknown)' ? css : filename ?? css)}`; }), // TODO this is a sourcemap option, would be good to put under a sourcemap namespace diff --git a/packages/svelte/tests/html_equal.js b/packages/svelte/tests/html_equal.js index b637e4d538..76a4a957a5 100644 --- a/packages/svelte/tests/html_equal.js +++ b/packages/svelte/tests/html_equal.js @@ -32,7 +32,13 @@ function clean_children(node, opts) { return; } - node.setAttribute(attr.name, attr.value); + let value = attr.value; + + if (attr.name === 'class') { + value = value.replace(/svelte-\w+/, 'svelte-xyz123'); + } + + node.setAttribute(attr.name, value); }); for (let child of [...node.childNodes]) { diff --git a/packages/svelte/tests/runtime-browser/assert.js b/packages/svelte/tests/runtime-browser/assert.js index 9a294a48c7..e331c8b677 100644 --- a/packages/svelte/tests/runtime-browser/assert.js +++ b/packages/svelte/tests/runtime-browser/assert.js @@ -74,6 +74,7 @@ function normalize_html(window, html) { node.innerHTML = html .replace(//g, '') .replace(/>[\s\r\n]+<') + .replace(/svelte-\w+/g, 'svelte-xyz123') .trim(); normalize_children(node); diff --git a/packages/svelte/tests/runtime-legacy/samples/attribute-null-classname-with-style/_config.js b/packages/svelte/tests/runtime-legacy/samples/attribute-null-classname-with-style/_config.js index cbd0456e13..1fc15c7eda 100644 --- a/packages/svelte/tests/runtime-legacy/samples/attribute-null-classname-with-style/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/attribute-null-classname-with-style/_config.js @@ -1,43 +1,43 @@ import { ok, test } from '../../test'; export default test({ - html: '
', + html: '
', test({ assert, component, target }) { const div = target.querySelector('div'); ok(div); component.testName = null; - assert.equal(div.className, 'svelte-x1o6ra'); + assert.equal(div.className, 'svelte-70s021'); component.testName = undefined; - assert.equal(div.className, 'svelte-x1o6ra'); + assert.equal(div.className, 'svelte-70s021'); component.testName = undefined + ''; - assert.equal(div.className, 'undefined svelte-x1o6ra'); + assert.equal(div.className, 'undefined svelte-70s021'); component.testName = null + ''; - assert.equal(div.className, 'null svelte-x1o6ra'); + assert.equal(div.className, 'null svelte-70s021'); component.testName = 1; - assert.equal(div.className, '1 svelte-x1o6ra'); + assert.equal(div.className, '1 svelte-70s021'); component.testName = 0; - assert.equal(div.className, '0 svelte-x1o6ra'); + assert.equal(div.className, '0 svelte-70s021'); component.testName = false; - assert.equal(div.className, 'false svelte-x1o6ra'); + assert.equal(div.className, 'false svelte-70s021'); component.testName = true; - assert.equal(div.className, 'true svelte-x1o6ra'); + assert.equal(div.className, 'true svelte-70s021'); component.testName = {}; - assert.equal(div.className, 'svelte-x1o6ra'); + assert.equal(div.className, 'svelte-70s021'); component.testName = ''; - assert.equal(div.className, 'svelte-x1o6ra'); + assert.equal(div.className, 'svelte-70s021'); component.testName = 'testClassName'; - assert.equal(div.className, 'testClassName svelte-x1o6ra'); + assert.equal(div.className, 'testClassName svelte-70s021'); } }); diff --git a/packages/svelte/tests/runtime-legacy/samples/attribute-null-classnames-with-style/_config.js b/packages/svelte/tests/runtime-legacy/samples/attribute-null-classnames-with-style/_config.js index d2511a4403..2f4b6242cb 100644 --- a/packages/svelte/tests/runtime-legacy/samples/attribute-null-classnames-with-style/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/attribute-null-classnames-with-style/_config.js @@ -10,43 +10,43 @@ export default test({ }; }, - html: '
', + html: '
', test({ assert, component, target }) { const div = target.querySelector('div'); ok(div); - assert.equal(div.className, 'test1test2 svelte-x1o6ra'); + assert.equal(div.className, 'test1test2 svelte-70s021'); component.testName1 = null; component.testName2 = null; - assert.equal(div.className, '0 svelte-x1o6ra'); + assert.equal(div.className, '0 svelte-70s021'); component.testName1 = null; component.testName2 = 'test'; - assert.equal(div.className, 'nulltest svelte-x1o6ra'); + assert.equal(div.className, 'nulltest svelte-70s021'); component.testName1 = undefined; component.testName2 = 'test'; - assert.equal(div.className, 'undefinedtest svelte-x1o6ra'); + assert.equal(div.className, 'undefinedtest svelte-70s021'); component.testName1 = undefined; component.testName2 = undefined; - assert.equal(div.className, 'NaN svelte-x1o6ra'); + assert.equal(div.className, 'NaN svelte-70s021'); component.testName1 = null; component.testName2 = 1; - assert.equal(div.className, '1 svelte-x1o6ra'); + assert.equal(div.className, '1 svelte-70s021'); component.testName1 = undefined; component.testName2 = 1; - assert.equal(div.className, 'NaN svelte-x1o6ra'); + assert.equal(div.className, 'NaN svelte-70s021'); component.testName1 = null; component.testName2 = 0; - assert.equal(div.className, '0 svelte-x1o6ra'); + assert.equal(div.className, '0 svelte-70s021'); component.testName1 = undefined; component.testName2 = 0; - assert.equal(div.className, 'NaN svelte-x1o6ra'); + assert.equal(div.className, 'NaN svelte-70s021'); } }); diff --git a/packages/svelte/tests/runtime-legacy/samples/attribute-null-func-classname-with-style/_config.js b/packages/svelte/tests/runtime-legacy/samples/attribute-null-func-classname-with-style/_config.js index 081fceecf2..b06103992f 100644 --- a/packages/svelte/tests/runtime-legacy/samples/attribute-null-func-classname-with-style/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/attribute-null-func-classname-with-style/_config.js @@ -8,41 +8,41 @@ export default test({ }; }, - html: '
', + html: '
', test({ assert, component, target }) { const div = target.querySelector('div'); ok(div); - assert.equal(div.className, 'testClassName svelte-x1o6ra'); + assert.equal(div.className, 'testClassName svelte-70s021'); component.testName = null; - assert.equal(div.className, 'svelte-x1o6ra'); + assert.equal(div.className, 'svelte-70s021'); component.testName = undefined; - assert.equal(div.className, 'svelte-x1o6ra'); + assert.equal(div.className, 'svelte-70s021'); component.testName = undefined + ''; - assert.equal(div.className, 'undefined svelte-x1o6ra'); + assert.equal(div.className, 'undefined svelte-70s021'); component.testName = null + ''; - assert.equal(div.className, 'null svelte-x1o6ra'); + assert.equal(div.className, 'null svelte-70s021'); component.testName = 1; - assert.equal(div.className, '1 svelte-x1o6ra'); + assert.equal(div.className, '1 svelte-70s021'); component.testName = 0; - assert.equal(div.className, '0 svelte-x1o6ra'); + assert.equal(div.className, '0 svelte-70s021'); component.testName = false; - assert.equal(div.className, 'false svelte-x1o6ra'); + assert.equal(div.className, 'false svelte-70s021'); component.testName = true; - assert.equal(div.className, 'true svelte-x1o6ra'); + assert.equal(div.className, 'true svelte-70s021'); component.testName = {}; - assert.equal(div.className, 'svelte-x1o6ra'); + assert.equal(div.className, 'svelte-70s021'); component.testName = ''; - assert.equal(div.className, 'svelte-x1o6ra'); + assert.equal(div.className, 'svelte-70s021'); } }); diff --git a/packages/svelte/tests/runtime-legacy/samples/attribute-null-func-classnames-with-style/_config.js b/packages/svelte/tests/runtime-legacy/samples/attribute-null-func-classnames-with-style/_config.js index d55a0079c0..7fc3dd85f4 100644 --- a/packages/svelte/tests/runtime-legacy/samples/attribute-null-func-classnames-with-style/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/attribute-null-func-classnames-with-style/_config.js @@ -10,43 +10,43 @@ export default test({ }; }, - html: '
', + html: '
', async test({ assert, component, target }) { const div = target.querySelector('div'); ok(div); - assert.equal(div.className, 'test1test2 svelte-x1o6ra'); + assert.equal(div.className, 'test1test2 svelte-70s021'); component.testName1 = null; component.testName2 = null; - assert.equal(div.className, '0 svelte-x1o6ra'); + assert.equal(div.className, '0 svelte-70s021'); component.testName1 = null; component.testName2 = 'test'; - assert.equal(div.className, 'nulltest svelte-x1o6ra'); + assert.equal(div.className, 'nulltest svelte-70s021'); component.testName1 = undefined; component.testName2 = 'test'; - assert.equal(div.className, 'undefinedtest svelte-x1o6ra'); + assert.equal(div.className, 'undefinedtest svelte-70s021'); component.testName1 = undefined; component.testName2 = undefined; - assert.equal(div.className, 'NaN svelte-x1o6ra'); + assert.equal(div.className, 'NaN svelte-70s021'); component.testName1 = null; component.testName2 = 1; - assert.equal(div.className, '1 svelte-x1o6ra'); + assert.equal(div.className, '1 svelte-70s021'); component.testName1 = undefined; component.testName2 = 1; - assert.equal(div.className, 'NaN svelte-x1o6ra'); + assert.equal(div.className, 'NaN svelte-70s021'); component.testName1 = null; component.testName2 = 0; - assert.equal(div.className, '0 svelte-x1o6ra'); + assert.equal(div.className, '0 svelte-70s021'); component.testName1 = undefined; component.testName2 = 0; - assert.equal(div.className, 'NaN svelte-x1o6ra'); + assert.equal(div.className, 'NaN svelte-70s021'); } }); diff --git a/packages/svelte/tests/runtime-runes/samples/custom-element-svelte-class/_config.js b/packages/svelte/tests/runtime-runes/samples/custom-element-svelte-class/_config.js index 0069a7dfd7..930de12585 100644 --- a/packages/svelte/tests/runtime-runes/samples/custom-element-svelte-class/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/custom-element-svelte-class/_config.js @@ -3,7 +3,7 @@ import { test } from '../../test'; export default test({ async test({ assert, target, logs }) { const [my_element, my_element_1] = target.querySelectorAll('my-element'); - assert.equal(my_element.classList.contains('svelte-1koh33s'), true); - assert.equal(my_element_1.classList.contains('svelte-1koh33s'), true); + assert.equal(my_element.classList.contains('svelte-70s021'), true); + assert.equal(my_element_1.classList.contains('svelte-70s021'), true); } }); diff --git a/packages/svelte/tests/runtime-runes/samples/svelte-element-custom-element-css-hash/_config.js b/packages/svelte/tests/runtime-runes/samples/svelte-element-custom-element-css-hash/_config.js index eae8101e6e..cb532b28b5 100644 --- a/packages/svelte/tests/runtime-runes/samples/svelte-element-custom-element-css-hash/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/svelte-element-custom-element-css-hash/_config.js @@ -1,14 +1,14 @@ import { ok, test } from '../../test'; export default test({ - html: ``, + html: ``, async test({ assert, target }) { const [el, el2] = target.querySelectorAll('custom-element'); ok(el); ok(el2); - assert.deepEqual(el.className, 'red svelte-p153w3'); - assert.deepEqual(el2.className, 'red svelte-p153w3'); + assert.deepEqual(el.className, 'red svelte-70s021'); + assert.deepEqual(el2.className, 'red svelte-70s021'); } }); diff --git a/packages/svelte/tests/server-side-rendering/samples/css-injected-options-minify/_expected.html b/packages/svelte/tests/server-side-rendering/samples/css-injected-options-minify/_expected.html index 60e974bd1a..7ba4de394e 100644 --- a/packages/svelte/tests/server-side-rendering/samples/css-injected-options-minify/_expected.html +++ b/packages/svelte/tests/server-side-rendering/samples/css-injected-options-minify/_expected.html @@ -1 +1 @@ -
foo
\ No newline at end of file +
foo
diff --git a/packages/svelte/tests/server-side-rendering/samples/css-injected-options-minify/_expected_head.html b/packages/svelte/tests/server-side-rendering/samples/css-injected-options-minify/_expected_head.html index 5350e77a49..99db8fc6dc 100644 --- a/packages/svelte/tests/server-side-rendering/samples/css-injected-options-minify/_expected_head.html +++ b/packages/svelte/tests/server-side-rendering/samples/css-injected-options-minify/_expected_head.html @@ -1 +1 @@ - \ No newline at end of file + diff --git a/packages/svelte/tests/server-side-rendering/samples/css-injected-options-nested/_expected.css b/packages/svelte/tests/server-side-rendering/samples/css-injected-options-nested/_expected.css index bddefdd00c..81c1111967 100644 --- a/packages/svelte/tests/server-side-rendering/samples/css-injected-options-nested/_expected.css +++ b/packages/svelte/tests/server-side-rendering/samples/css-injected-options-nested/_expected.css @@ -1,4 +1,4 @@ -.foo.svelte-sg04hs { +.foo.svelte-1nvcr6w { color: red; } diff --git a/packages/svelte/tests/server-side-rendering/samples/css-injected-options-nested/_expected.html b/packages/svelte/tests/server-side-rendering/samples/css-injected-options-nested/_expected.html index 1f0b2b95fe..a6eba00942 100644 --- a/packages/svelte/tests/server-side-rendering/samples/css-injected-options-nested/_expected.html +++ b/packages/svelte/tests/server-side-rendering/samples/css-injected-options-nested/_expected.html @@ -1 +1 @@ -
bar
foo
\ No newline at end of file +
bar
foo
diff --git a/packages/svelte/tests/server-side-rendering/samples/css-injected-options-nested/_expected_head.html b/packages/svelte/tests/server-side-rendering/samples/css-injected-options-nested/_expected_head.html index 6d795670ff..516a9576b3 100644 --- a/packages/svelte/tests/server-side-rendering/samples/css-injected-options-nested/_expected_head.html +++ b/packages/svelte/tests/server-side-rendering/samples/css-injected-options-nested/_expected_head.html @@ -1 +1 @@ - \ No newline at end of file + diff --git a/packages/svelte/tests/server-side-rendering/samples/css-injected-options/_expected.css b/packages/svelte/tests/server-side-rendering/samples/css-injected-options/_expected.css index 8882c6ec7e..eb1f3e7a9b 100644 --- a/packages/svelte/tests/server-side-rendering/samples/css-injected-options/_expected.css +++ b/packages/svelte/tests/server-side-rendering/samples/css-injected-options/_expected.css @@ -1,4 +1,4 @@ - .foo.svelte-sg04hs { + .foo.svelte-okauro { color: red; } diff --git a/packages/svelte/tests/server-side-rendering/samples/css-injected-options/_expected.html b/packages/svelte/tests/server-side-rendering/samples/css-injected-options/_expected.html index 8ebe1ad73e..01ebd79914 100644 --- a/packages/svelte/tests/server-side-rendering/samples/css-injected-options/_expected.html +++ b/packages/svelte/tests/server-side-rendering/samples/css-injected-options/_expected.html @@ -1 +1 @@ -
foo
\ No newline at end of file +
foo
diff --git a/packages/svelte/tests/server-side-rendering/samples/css-injected-options/_expected_head.html b/packages/svelte/tests/server-side-rendering/samples/css-injected-options/_expected_head.html index 9c4f8a8538..1a1511f55e 100644 --- a/packages/svelte/tests/server-side-rendering/samples/css-injected-options/_expected_head.html +++ b/packages/svelte/tests/server-side-rendering/samples/css-injected-options/_expected_head.html @@ -1 +1 @@ - \ No newline at end of file + diff --git a/packages/svelte/tests/server-side-rendering/samples/css/_expected.css b/packages/svelte/tests/server-side-rendering/samples/css/_expected.css index 8882c6ec7e..ec86890478 100644 --- a/packages/svelte/tests/server-side-rendering/samples/css/_expected.css +++ b/packages/svelte/tests/server-side-rendering/samples/css/_expected.css @@ -1,4 +1,4 @@ - .foo.svelte-sg04hs { + .foo.svelte-e9omc { color: red; } diff --git a/packages/svelte/tests/server-side-rendering/samples/css/_expected.html b/packages/svelte/tests/server-side-rendering/samples/css/_expected.html index 8ebe1ad73e..dc9409fa03 100644 --- a/packages/svelte/tests/server-side-rendering/samples/css/_expected.html +++ b/packages/svelte/tests/server-side-rendering/samples/css/_expected.html @@ -1 +1 @@ -
foo
\ No newline at end of file +
foo
diff --git a/packages/svelte/tests/server-side-rendering/samples/css/_expected_head.html b/packages/svelte/tests/server-side-rendering/samples/css/_expected_head.html index 9c4f8a8538..941c1f13b4 100644 --- a/packages/svelte/tests/server-side-rendering/samples/css/_expected_head.html +++ b/packages/svelte/tests/server-side-rendering/samples/css/_expected_head.html @@ -1 +1 @@ - \ No newline at end of file + diff --git a/packages/svelte/tests/sourcemaps/samples/attached-sourcemap/_config.js b/packages/svelte/tests/sourcemaps/samples/attached-sourcemap/_config.js index ee9a3d92c4..3a292ff428 100644 --- a/packages/svelte/tests/sourcemaps/samples/attached-sourcemap/_config.js +++ b/packages/svelte/tests/sourcemaps/samples/attached-sourcemap/_config.js @@ -57,7 +57,7 @@ export default test({ { str: 'replace_me_script', strGenerated: 'done_replace_script_2' }, { str: 'done_replace_script_2', idxGenerated: 1 } ], - css: [{ str: '.replace_me_style', strGenerated: '.done_replace_style_2.svelte-o6vre' }], + css: [{ str: '.replace_me_style', strGenerated: '.done_replace_style_2.svelte-1vsrjd4' }], test({ assert, code_preprocessed, code_css }) { assert.equal( code_preprocessed.includes('\n/*# sourceMappingURL=data:application/json;base64,'), diff --git a/packages/svelte/tests/sourcemaps/samples/css/_config.js b/packages/svelte/tests/sourcemaps/samples/css/_config.js index df3c83c703..fce38d401c 100644 --- a/packages/svelte/tests/sourcemaps/samples/css/_config.js +++ b/packages/svelte/tests/sourcemaps/samples/css/_config.js @@ -1,5 +1,5 @@ import { test } from '../../test'; export default test({ - css: [{ str: '.foo', strGenerated: '.foo.svelte-sg04hs' }] + css: [{ str: '.foo', strGenerated: '.foo.svelte-1eyw86p' }] }); diff --git a/packages/svelte/types/index.d.ts b/packages/svelte/types/index.d.ts index 97e6f0f5a3..9888de59b2 100644 --- a/packages/svelte/types/index.d.ts +++ b/packages/svelte/types/index.d.ts @@ -998,7 +998,7 @@ declare module 'svelte/compiler' { css?: 'injected' | 'external'; /** * A function that takes a `{ hash, css, name, filename }` argument and returns the string that is used as a classname for scoped CSS. - * It defaults to returning `svelte-${hash(css)}`. + * It defaults to returning `svelte-${hash(filename ?? css)}`. * * @default undefined */ @@ -2933,7 +2933,7 @@ declare module 'svelte/types/compiler/interfaces' { css?: 'injected' | 'external'; /** * A function that takes a `{ hash, css, name, filename }` argument and returns the string that is used as a classname for scoped CSS. - * It defaults to returning `svelte-${hash(css)}`. + * It defaults to returning `svelte-${hash(filename ?? css)}`. * * @default undefined */ From f09f25e6a3ba7aa3562f62713e7cdc00b267fac7 Mon Sep 17 00:00:00 2001 From: Elliott Johnson Date: Wed, 10 Sep 2025 16:29:31 -0600 Subject: [PATCH 006/347] fix: Don't destroy boundary contents on error unless the boundary is an error boundary (#16746) * fix: Don't destroy contents of boundaries on errors if they're not error boundaries * changeset * test * prettier * prettier * simplify test * oops --------- Co-authored-by: Rich Harris --- .changeset/tasty-trainers-sell.md | 5 +++++ .../internal/client/dom/blocks/boundary.js | 12 +++++------ .../_config.js | 20 +++++++++++++++++++ .../main.svelte | 19 ++++++++++++++++++ 4 files changed, 50 insertions(+), 6 deletions(-) create mode 100644 .changeset/tasty-trainers-sell.md create mode 100644 packages/svelte/tests/runtime-runes/samples/non-error-boundary-preserve-on-error/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/non-error-boundary-preserve-on-error/main.svelte diff --git a/.changeset/tasty-trainers-sell.md b/.changeset/tasty-trainers-sell.md new file mode 100644 index 0000000000..4a2dd09fa6 --- /dev/null +++ b/.changeset/tasty-trainers-sell.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: don't destroy contents of `svelte:boundary` unless the boundary is an error boundary diff --git a/packages/svelte/src/internal/client/dom/blocks/boundary.js b/packages/svelte/src/internal/client/dom/blocks/boundary.js index b7f1803782..d4582024f7 100644 --- a/packages/svelte/src/internal/client/dom/blocks/boundary.js +++ b/packages/svelte/src/internal/client/dom/blocks/boundary.js @@ -285,6 +285,12 @@ export class Boundary { var onerror = this.#props.onerror; let failed = this.#props.failed; + // If we have nothing to capture the error, or if we hit an error while + // rendering the fallback, re-throw for another boundary to handle + if (this.#is_creating_fallback || (!onerror && !failed)) { + throw error; + } + if (this.#main_effect) { destroy_effect(this.#main_effect); this.#main_effect = null; @@ -346,12 +352,6 @@ export class Boundary { } }; - // If we have nothing to capture the error, or if we hit an error while - // rendering the fallback, re-throw for another boundary to handle - if (this.#is_creating_fallback || (!onerror && !failed)) { - throw error; - } - var previous_reaction = active_reaction; try { diff --git a/packages/svelte/tests/runtime-runes/samples/non-error-boundary-preserve-on-error/_config.js b/packages/svelte/tests/runtime-runes/samples/non-error-boundary-preserve-on-error/_config.js new file mode 100644 index 0000000000..7654cf1360 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/non-error-boundary-preserve-on-error/_config.js @@ -0,0 +1,20 @@ +import { test } from '../../test'; +import { flushSync } from 'svelte'; + +export default test({ + test({ assert, target }) { + const [button] = target.querySelectorAll('button'); + + assert.throws(() => { + flushSync(() => button.click()); + }, /oops/); + + assert.htmlEqual( + target.innerHTML, + ` + +

some content

+ ` + ); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/non-error-boundary-preserve-on-error/main.svelte b/packages/svelte/tests/runtime-runes/samples/non-error-boundary-preserve-on-error/main.svelte new file mode 100644 index 0000000000..1c3f062b43 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/non-error-boundary-preserve-on-error/main.svelte @@ -0,0 +1,19 @@ + + + + + +

some content

+ + {#if should_throw} + {throw_error()} + {/if} +
From 2743cd0fe5a305586e87b65301dc3466b7c265f9 Mon Sep 17 00:00:00 2001 From: ComputerGuy <63362464+Ocean-OS@users.noreply.github.com> Date: Wed, 10 Sep 2025 17:20:04 -0700 Subject: [PATCH 007/347] fix: correctly analyze `` components (#16711) * fix: correctly analyze `` components * add test --------- Co-authored-by: Rich Harris --- .changeset/khaki-flies-remember.md | 5 +++++ packages/svelte/src/compiler/phases/scope.js | 2 +- .../samples/snippet-hoisting-4/Component.svelte | 1 + .../samples/snippet-hoisting-4/_config.js | 5 +++++ .../samples/snippet-hoisting-4/main.svelte | 13 +++++++++++++ 5 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 .changeset/khaki-flies-remember.md create mode 100644 packages/svelte/tests/runtime-runes/samples/snippet-hoisting-4/Component.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/snippet-hoisting-4/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/snippet-hoisting-4/main.svelte diff --git a/.changeset/khaki-flies-remember.md b/.changeset/khaki-flies-remember.md new file mode 100644 index 0000000000..16d0f79e0f --- /dev/null +++ b/.changeset/khaki-flies-remember.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: correctly analyze `` components diff --git a/packages/svelte/src/compiler/phases/scope.js b/packages/svelte/src/compiler/phases/scope.js index 76157d406f..887bc47c56 100644 --- a/packages/svelte/src/compiler/phases/scope.js +++ b/packages/svelte/src/compiler/phases/scope.js @@ -1032,7 +1032,7 @@ export function create_scopes(ast, root, allow_reactive_declarations, parent) { }, Component: (node, context) => { - context.state.scope.reference(b.id(node.name), context.path); + context.state.scope.reference(b.id(node.name.split('.')[0]), context.path); Component(node, context); }, SvelteSelf: Component, diff --git a/packages/svelte/tests/runtime-runes/samples/snippet-hoisting-4/Component.svelte b/packages/svelte/tests/runtime-runes/samples/snippet-hoisting-4/Component.svelte new file mode 100644 index 0000000000..597ecf5fc4 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/snippet-hoisting-4/Component.svelte @@ -0,0 +1 @@ +

Hello world!

diff --git a/packages/svelte/tests/runtime-runes/samples/snippet-hoisting-4/_config.js b/packages/svelte/tests/runtime-runes/samples/snippet-hoisting-4/_config.js new file mode 100644 index 0000000000..240263603d --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/snippet-hoisting-4/_config.js @@ -0,0 +1,5 @@ +import { test } from '../../test'; + +export default test({ + html: '

Hello world!

' +}); diff --git a/packages/svelte/tests/runtime-runes/samples/snippet-hoisting-4/main.svelte b/packages/svelte/tests/runtime-runes/samples/snippet-hoisting-4/main.svelte new file mode 100644 index 0000000000..d3130a99bd --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/snippet-hoisting-4/main.svelte @@ -0,0 +1,13 @@ + + +{#snippet not_hoisted()} + +{/snippet} + +{@render not_hoisted()} From 558a3c963bca4423cdd7b93d9588d05546dfa629 Mon Sep 17 00:00:00 2001 From: dai Date: Thu, 11 Sep 2025 03:10:45 +0200 Subject: [PATCH 008/347] fix: transform input defaults from spread (#16481) * fix: transform input defaults from spread * chore: add changeset * fix: prevent duplicates * do not remove defaults if they are in spreads * fix spreading twice * tweak * tweak * drive-by: remove unused export * tweak * undo comment change, to minimise diff * oops * tweak --------- Co-authored-by: 7nik Co-authored-by: Rich Harris --- .changeset/quiet-planes-doubt.md | 5 ++++ .../client/visitors/RegularElement.js | 18 +++++++++-- .../client/visitors/shared/element.js | 5 +++- .../server/visitors/shared/element.js | 3 ++ packages/svelte/src/constants.js | 1 + .../client/dom/elements/attributes.js | 30 +++++++++++++++++-- packages/svelte/src/internal/client/index.js | 1 - packages/svelte/src/internal/server/index.js | 11 ++++++- .../form-default-value-from-spread/_config.js | 10 +++++++ .../main.svelte | 8 +++++ 10 files changed, 85 insertions(+), 7 deletions(-) create mode 100644 .changeset/quiet-planes-doubt.md create mode 100644 packages/svelte/tests/runtime-runes/samples/form-default-value-from-spread/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/form-default-value-from-spread/main.svelte diff --git a/.changeset/quiet-planes-doubt.md b/.changeset/quiet-planes-doubt.md new file mode 100644 index 0000000000..bd895f00ef --- /dev/null +++ b/.changeset/quiet-planes-doubt.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: transform input defaults from spread diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js index 4296aa959e..e906061650 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js @@ -72,6 +72,7 @@ export function RegularElement(node, context) { let has_spread = node.metadata.has_spread; let has_use = false; + let should_remove_defaults = false; for (const attribute of node.attributes) { switch (attribute.type) { @@ -172,7 +173,12 @@ export function RegularElement(node, context) { bindings.has('group') || (!bindings.has('group') && has_value_attribute)) ) { - context.state.init.push(b.stmt(b.call('$.remove_input_defaults', context.state.node))); + if (has_spread) { + // remove_input_defaults will be called inside set_attributes + should_remove_defaults = true; + } else { + context.state.init.push(b.stmt(b.call('$.remove_input_defaults', context.state.node))); + } } } @@ -202,7 +208,15 @@ export function RegularElement(node, context) { bindings.has('checked'); if (has_spread) { - build_attribute_effect(attributes, class_directives, style_directives, context, node, node_id); + build_attribute_effect( + attributes, + class_directives, + style_directives, + context, + node, + node_id, + should_remove_defaults + ); } else { for (const attribute of /** @type {AST.Attribute[]} */ (attributes)) { if (is_event_attribute(attribute)) { diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/element.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/element.js index 9143a57025..4b32dab82a 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/element.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/element.js @@ -16,6 +16,7 @@ import { build_expression, build_template_chunk, Memoizer } from './utils.js'; * @param {ComponentContext} context * @param {AST.RegularElement | AST.SvelteElement} element * @param {Identifier} element_id + * @param {boolean} [should_remove_defaults] */ export function build_attribute_effect( attributes, @@ -23,7 +24,8 @@ export function build_attribute_effect( style_directives, context, element, - element_id + element_id, + should_remove_defaults = false ) { /** @type {ObjectExpression['properties']} */ const values = []; @@ -91,6 +93,7 @@ export function build_attribute_effect( element.metadata.scoped && context.state.analysis.css.hash !== '' && b.literal(context.state.analysis.css.hash), + should_remove_defaults && b.true, is_ignored(element, 'hydration_attribute_changed') && b.true ) ) diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/element.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/element.js index 7207564ef9..84692fca9c 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/element.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/element.js @@ -11,6 +11,7 @@ import { import { regex_starts_with_newline } from '../../../../patterns.js'; import * as b from '#compiler/builders'; import { + ELEMENT_IS_INPUT, ELEMENT_IS_NAMESPACED, ELEMENT_PRESERVE_ATTRIBUTE_CASE } from '../../../../../../constants.js'; @@ -401,6 +402,8 @@ function build_element_spread_attributes( flags |= ELEMENT_IS_NAMESPACED | ELEMENT_PRESERVE_ATTRIBUTE_CASE; } else if (is_custom_element_node(element)) { flags |= ELEMENT_PRESERVE_ATTRIBUTE_CASE; + } else if (element.type === 'RegularElement' && element.name === 'input') { + flags |= ELEMENT_IS_INPUT; } const object = build_spread_object(element, attributes, context); diff --git a/packages/svelte/src/constants.js b/packages/svelte/src/constants.js index 69cd213940..63324c860f 100644 --- a/packages/svelte/src/constants.js +++ b/packages/svelte/src/constants.js @@ -28,6 +28,7 @@ export const HYDRATION_ERROR = {}; export const ELEMENT_IS_NAMESPACED = 1; export const ELEMENT_PRESERVE_ATTRIBUTE_CASE = 1 << 1; +export const ELEMENT_IS_INPUT = 1 << 2; export const UNINITIALIZED = Symbol(); diff --git a/packages/svelte/src/internal/client/dom/elements/attributes.js b/packages/svelte/src/internal/client/dom/elements/attributes.js index a5b7140f25..fb6a92cc82 100644 --- a/packages/svelte/src/internal/client/dom/elements/attributes.js +++ b/packages/svelte/src/internal/client/dom/elements/attributes.js @@ -268,10 +268,27 @@ export function set_custom_element_data(node, prop, value) { * @param {Record | undefined} prev * @param {Record} next New attributes - this function mutates this object * @param {string} [css_hash] + * @param {boolean} [should_remove_defaults] * @param {boolean} [skip_warning] * @returns {Record} */ -export function set_attributes(element, prev, next, css_hash, skip_warning = false) { +function set_attributes( + element, + prev, + next, + css_hash, + should_remove_defaults = false, + skip_warning = false +) { + if (hydrating && should_remove_defaults && element.tagName === 'INPUT') { + var input = /** @type {HTMLInputElement} */ (element); + var attribute = input.type === 'checkbox' ? 'defaultChecked' : 'defaultValue'; + + if (!(attribute in next)) { + remove_input_defaults(input); + } + } + var attributes = get_attributes(element); var is_custom_element = attributes[IS_CUSTOM_ELEMENT]; @@ -467,6 +484,7 @@ export function set_attributes(element, prev, next, css_hash, skip_warning = fal * @param {Array<() => any>} sync * @param {Array<() => Promise>} async * @param {string} [css_hash] + * @param {boolean} [should_remove_defaults] * @param {boolean} [skip_warning] */ export function attribute_effect( @@ -475,6 +493,7 @@ export function attribute_effect( sync = [], async = [], css_hash, + should_remove_defaults = false, skip_warning = false ) { flatten(sync, async, (values) => { @@ -490,7 +509,14 @@ export function attribute_effect( block(() => { var next = fn(...values.map(get)); /** @type {Record} */ - var current = set_attributes(element, prev, next, css_hash, skip_warning); + var current = set_attributes( + element, + prev, + next, + css_hash, + should_remove_defaults, + skip_warning + ); if (inited && is_select && 'value' in next) { select_option(/** @type {HTMLSelectElement} */ (element), next.value); diff --git a/packages/svelte/src/internal/client/index.js b/packages/svelte/src/internal/client/index.js index c5b7bb845c..dbff5c4599 100644 --- a/packages/svelte/src/internal/client/index.js +++ b/packages/svelte/src/internal/client/index.js @@ -28,7 +28,6 @@ export { attach } from './dom/elements/attachments.js'; export { remove_input_defaults, set_attribute, - set_attributes, attribute_effect, set_custom_element_data, set_xlink_attribute, diff --git a/packages/svelte/src/internal/server/index.js b/packages/svelte/src/internal/server/index.js index 62ee22d6fc..3aa44f2daa 100644 --- a/packages/svelte/src/internal/server/index.js +++ b/packages/svelte/src/internal/server/index.js @@ -8,7 +8,8 @@ import { subscribe_to_store } from '../../store/utils.js'; import { UNINITIALIZED, ELEMENT_PRESERVE_ATTRIBUTE_CASE, - ELEMENT_IS_NAMESPACED + ELEMENT_IS_NAMESPACED, + ELEMENT_IS_INPUT } from '../../constants.js'; import { escape_html } from '../../escaping.js'; import { DEV } from 'esm-env'; @@ -187,6 +188,7 @@ export function spread_attributes(attrs, css_hash, classes, styles, flags = 0) { const is_html = (flags & ELEMENT_IS_NAMESPACED) === 0; const lowercase = (flags & ELEMENT_PRESERVE_ATTRIBUTE_CASE) === 0; + const is_input = (flags & ELEMENT_IS_INPUT) !== 0; for (name in attrs) { // omit functions, internal svelte properties and invalid attribute names @@ -200,6 +202,13 @@ export function spread_attributes(attrs, css_hash, classes, styles, flags = 0) { name = name.toLowerCase(); } + if (is_input) { + if (name === 'defaultvalue' || name === 'defaultchecked') { + name = name === 'defaultvalue' ? 'value' : 'checked'; + if (attrs[name]) continue; + } + } + attr_str += attr(name, value, is_html && is_boolean_attribute(name)); } diff --git a/packages/svelte/tests/runtime-runes/samples/form-default-value-from-spread/_config.js b/packages/svelte/tests/runtime-runes/samples/form-default-value-from-spread/_config.js new file mode 100644 index 0000000000..3808ae6530 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/form-default-value-from-spread/_config.js @@ -0,0 +1,10 @@ +import { test } from '../../test'; + +export default test({ + mode: ['server'], + html: ` + + + +` +}); diff --git a/packages/svelte/tests/runtime-runes/samples/form-default-value-from-spread/main.svelte b/packages/svelte/tests/runtime-runes/samples/form-default-value-from-spread/main.svelte new file mode 100644 index 0000000000..7c0ce9cbe3 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/form-default-value-from-spread/main.svelte @@ -0,0 +1,8 @@ + + + + + \ No newline at end of file From 96a4b16842e62a2e4224b084425cac6a191e6c2d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 10 Sep 2025 21:22:12 -0400 Subject: [PATCH 009/347] Version Packages (#16744) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .changeset/fast-boxes-sort.md | 5 ----- .changeset/khaki-flies-remember.md | 5 ----- .changeset/old-taxis-relate.md | 5 ----- .changeset/quiet-planes-doubt.md | 5 ----- .changeset/tasty-trainers-sell.md | 5 ----- packages/svelte/CHANGELOG.md | 14 ++++++++++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 8 files changed, 16 insertions(+), 27 deletions(-) delete mode 100644 .changeset/fast-boxes-sort.md delete mode 100644 .changeset/khaki-flies-remember.md delete mode 100644 .changeset/old-taxis-relate.md delete mode 100644 .changeset/quiet-planes-doubt.md delete mode 100644 .changeset/tasty-trainers-sell.md diff --git a/.changeset/fast-boxes-sort.md b/.changeset/fast-boxes-sort.md deleted file mode 100644 index 1edabf0582..0000000000 --- a/.changeset/fast-boxes-sort.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -chore: generate CSS hash using the filename diff --git a/.changeset/khaki-flies-remember.md b/.changeset/khaki-flies-remember.md deleted file mode 100644 index 16d0f79e0f..0000000000 --- a/.changeset/khaki-flies-remember.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: correctly analyze `` components diff --git a/.changeset/old-taxis-relate.md b/.changeset/old-taxis-relate.md deleted file mode 100644 index f38dc03f5e..0000000000 --- a/.changeset/old-taxis-relate.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: clean up scheduling system diff --git a/.changeset/quiet-planes-doubt.md b/.changeset/quiet-planes-doubt.md deleted file mode 100644 index bd895f00ef..0000000000 --- a/.changeset/quiet-planes-doubt.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: transform input defaults from spread diff --git a/.changeset/tasty-trainers-sell.md b/.changeset/tasty-trainers-sell.md deleted file mode 100644 index 4a2dd09fa6..0000000000 --- a/.changeset/tasty-trainers-sell.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: don't destroy contents of `svelte:boundary` unless the boundary is an error boundary diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 20ae98a500..4e384d5697 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,19 @@ # svelte +## 5.38.9 + +### Patch Changes + +- chore: generate CSS hash using the filename ([#16740](https://github.com/sveltejs/svelte/pull/16740)) + +- fix: correctly analyze `` components ([#16711](https://github.com/sveltejs/svelte/pull/16711)) + +- fix: clean up scheduling system ([#16741](https://github.com/sveltejs/svelte/pull/16741)) + +- fix: transform input defaults from spread ([#16481](https://github.com/sveltejs/svelte/pull/16481)) + +- fix: don't destroy contents of `svelte:boundary` unless the boundary is an error boundary ([#16746](https://github.com/sveltejs/svelte/pull/16746)) + ## 5.38.8 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 02fa445b0e..d445e0acf8 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.38.8", + "version": "5.38.9", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index 8cb69aac5b..edb787c00c 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.38.8'; +export const VERSION = '5.38.9'; export const PUBLIC_VERSION = '5'; From 0b5bcc891e1e4582131b3e5eca1c48229f1f57d4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 10 Sep 2025 21:29:26 -0400 Subject: [PATCH 010/347] chore(deps-dev): bump vite from 5.4.19 to 5.4.20 (#16749) Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 5.4.19 to 5.4.20. - [Release notes](https://github.com/vitejs/vite/releases) - [Changelog](https://github.com/vitejs/vite/blob/v5.4.20/packages/vite/CHANGELOG.md) - [Commits](https://github.com/vitejs/vite/commits/v5.4.20/packages/vite) --- updated-dependencies: - dependency-name: vite dependency-version: 5.4.20 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 | 420 ++++++++++++++++++------------- 2 files changed, 252 insertions(+), 170 deletions(-) diff --git a/playgrounds/sandbox/package.json b/playgrounds/sandbox/package.json index f11da983f6..8100084832 100644 --- a/playgrounds/sandbox/package.json +++ b/playgrounds/sandbox/package.json @@ -19,7 +19,7 @@ "polka": "^1.0.0-next.25", "svelte": "workspace:*", "tinyglobby": "^0.2.12", - "vite": "^5.4.19", + "vite": "^5.4.20", "vite-plugin-inspect": "^0.8.4" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cad5fefdf8..1f6bff9bd4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -155,7 +155,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.19(@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.20(@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 @@ -166,11 +166,11 @@ importers: specifier: ^0.2.12 version: 0.2.12 vite: - specifier: ^5.4.19 - version: 5.4.19(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) + specifier: ^5.4.20 + version: 5.4.20(@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.40.2)(vite@5.4.19(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)) + version: 0.8.4(rollup@4.50.1)(vite@5.4.20(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)) packages: @@ -417,6 +417,12 @@ packages: peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + '@eslint-community/eslint-utils@4.9.0': + resolution: {integrity: sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + '@eslint-community/regexpp@4.12.1': resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} @@ -557,8 +563,8 @@ packages: cpu: [arm] os: [android] - '@rollup/rollup-android-arm-eabi@4.40.2': - resolution: {integrity: sha512-JkdNEq+DFxZfUwxvB58tHMHBHVgX23ew41g1OQinthJ+ryhdRk67O31S7sYw8u2lTjHUPFxwar07BBt1KHp/hg==} + '@rollup/rollup-android-arm-eabi@4.50.1': + resolution: {integrity: sha512-HJXwzoZN4eYTdD8bVV22DN8gsPCAj3V20NHKOs8ezfXanGpmVPR7kalUHd+Y31IJp9stdB87VKPFbsGY3H/2ag==} cpu: [arm] os: [android] @@ -567,8 +573,8 @@ packages: cpu: [arm64] os: [android] - '@rollup/rollup-android-arm64@4.40.2': - resolution: {integrity: sha512-13unNoZ8NzUmnndhPTkWPWbX3vtHodYmy+I9kuLxN+F+l+x3LdVF7UCu8TWVMt1POHLh6oDHhnOA04n8oJZhBw==} + '@rollup/rollup-android-arm64@4.50.1': + resolution: {integrity: sha512-PZlsJVcjHfcH53mOImyt3bc97Ep3FJDXRpk9sMdGX0qgLmY0EIWxCag6EigerGhLVuL8lDVYNnSo8qnTElO4xw==} cpu: [arm64] os: [android] @@ -577,8 +583,8 @@ packages: cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-arm64@4.40.2': - resolution: {integrity: sha512-Gzf1Hn2Aoe8VZzevHostPX23U7N5+4D36WJNHK88NZHCJr7aVMG4fadqkIf72eqVPGjGc0HJHNuUaUcxiR+N/w==} + '@rollup/rollup-darwin-arm64@4.50.1': + resolution: {integrity: sha512-xc6i2AuWh++oGi4ylOFPmzJOEeAa2lJeGUGb4MudOtgfyyjr4UPNK+eEWTPLvmPJIY/pgw6ssFIox23SyrkkJw==} cpu: [arm64] os: [darwin] @@ -587,18 +593,18 @@ packages: cpu: [x64] os: [darwin] - '@rollup/rollup-darwin-x64@4.40.2': - resolution: {integrity: sha512-47N4hxa01a4x6XnJoskMKTS8XZ0CZMd8YTbINbi+w03A2w4j1RTlnGHOz/P0+Bg1LaVL6ufZyNprSg+fW5nYQQ==} + '@rollup/rollup-darwin-x64@4.50.1': + resolution: {integrity: sha512-2ofU89lEpDYhdLAbRdeyz/kX3Y2lpYc6ShRnDjY35bZhd2ipuDMDi6ZTQ9NIag94K28nFMofdnKeHR7BT0CATw==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.40.2': - resolution: {integrity: sha512-8t6aL4MD+rXSHHZUR1z19+9OFJ2rl1wGKvckN47XFRVO+QL/dUSpKA2SLRo4vMg7ELA8pzGpC+W9OEd1Z/ZqoQ==} + '@rollup/rollup-freebsd-arm64@4.50.1': + resolution: {integrity: sha512-wOsE6H2u6PxsHY/BeFHA4VGQN3KUJFZp7QJBmDYI983fgxq5Th8FDkVuERb2l9vDMs1D5XhOrhBrnqcEY6l8ZA==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.40.2': - resolution: {integrity: sha512-C+AyHBzfpsOEYRFjztcYUFsH4S7UsE9cDtHCtma5BK8+ydOZYgMmWg1d/4KBytQspJCld8ZIujFMAdKG1xyr4Q==} + '@rollup/rollup-freebsd-x64@4.50.1': + resolution: {integrity: sha512-A/xeqaHTlKbQggxCqispFAcNjycpUEHP52mwMQZUNqDUJFFYtPHCXS1VAG29uMlDzIVr+i00tSFWFLivMcoIBQ==} cpu: [x64] os: [freebsd] @@ -607,8 +613,8 @@ packages: cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-gnueabihf@4.40.2': - resolution: {integrity: sha512-de6TFZYIvJwRNjmW3+gaXiZ2DaWL5D5yGmSYzkdzjBDS3W+B9JQ48oZEsmMvemqjtAFzE16DIBLqd6IQQRuG9Q==} + '@rollup/rollup-linux-arm-gnueabihf@4.50.1': + resolution: {integrity: sha512-54v4okehwl5TaSIkpp97rAHGp7t3ghinRd/vyC1iXqXMfjYUTm7TfYmCzXDoHUPTTf36L8pr0E7YsD3CfB3ZDg==} cpu: [arm] os: [linux] @@ -617,8 +623,8 @@ packages: cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-musleabihf@4.40.2': - resolution: {integrity: sha512-urjaEZubdIkacKc930hUDOfQPysezKla/O9qV+O89enqsqUmQm8Xj8O/vh0gHg4LYfv7Y7UsE3QjzLQzDYN1qg==} + '@rollup/rollup-linux-arm-musleabihf@4.50.1': + resolution: {integrity: sha512-p/LaFyajPN/0PUHjv8TNyxLiA7RwmDoVY3flXHPSzqrGcIp/c2FjwPPP5++u87DGHtw+5kSH5bCJz0mvXngYxw==} cpu: [arm] os: [linux] @@ -627,8 +633,8 @@ packages: cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.40.2': - resolution: {integrity: sha512-KlE8IC0HFOC33taNt1zR8qNlBYHj31qGT1UqWqtvR/+NuCVhfufAq9fxO8BMFC22Wu0rxOwGVWxtCMvZVLmhQg==} + '@rollup/rollup-linux-arm64-gnu@4.50.1': + resolution: {integrity: sha512-2AbMhFFkTo6Ptna1zO7kAXXDLi7H9fGTbVaIq2AAYO7yzcAsuTNWPHhb2aTA6GPiP+JXh85Y8CiS54iZoj4opw==} cpu: [arm64] os: [linux] @@ -637,13 +643,13 @@ packages: cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-musl@4.40.2': - resolution: {integrity: sha512-j8CgxvfM0kbnhu4XgjnCWJQyyBOeBI1Zq91Z850aUddUmPeQvuAy6OiMdPS46gNFgy8gN1xkYyLgwLYZG3rBOg==} + '@rollup/rollup-linux-arm64-musl@4.50.1': + resolution: {integrity: sha512-Cgef+5aZwuvesQNw9eX7g19FfKX5/pQRIyhoXLCiBOrWopjo7ycfB292TX9MDcDijiuIJlx1IzJz3IoCPfqs9w==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-loongarch64-gnu@4.40.2': - resolution: {integrity: sha512-Ybc/1qUampKuRF4tQXc7G7QY9YRyeVSykfK36Y5Qc5dmrIxwFhrOzqaVTNoZygqZ1ZieSWTibfFhQ5qK8jpWxw==} + '@rollup/rollup-linux-loongarch64-gnu@4.50.1': + resolution: {integrity: sha512-RPhTwWMzpYYrHrJAS7CmpdtHNKtt2Ueo+BlLBjfZEhYBhK00OsEqM08/7f+eohiF6poe0YRDDd8nAvwtE/Y62Q==} cpu: [loong64] os: [linux] @@ -652,8 +658,8 @@ packages: cpu: [ppc64] os: [linux] - '@rollup/rollup-linux-powerpc64le-gnu@4.40.2': - resolution: {integrity: sha512-3FCIrnrt03CCsZqSYAOW/k9n625pjpuMzVfeI+ZBUSDT3MVIFDSPfSUgIl9FqUftxcUXInvFah79hE1c9abD+Q==} + '@rollup/rollup-linux-ppc64-gnu@4.50.1': + resolution: {integrity: sha512-eSGMVQw9iekut62O7eBdbiccRguuDgiPMsw++BVUg+1K7WjZXHOg/YOT9SWMzPZA+w98G+Fa1VqJgHZOHHnY0Q==} cpu: [ppc64] os: [linux] @@ -662,13 +668,13 @@ packages: cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-riscv64-gnu@4.40.2': - resolution: {integrity: sha512-QNU7BFHEvHMp2ESSY3SozIkBPaPBDTsfVNGx3Xhv+TdvWXFGOSH2NJvhD1zKAT6AyuuErJgbdvaJhYVhVqrWTg==} + '@rollup/rollup-linux-riscv64-gnu@4.50.1': + resolution: {integrity: sha512-S208ojx8a4ciIPrLgazF6AgdcNJzQE4+S9rsmOmDJkusvctii+ZvEuIC4v/xFqzbuP8yDjn73oBlNDgF6YGSXQ==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-riscv64-musl@4.40.2': - resolution: {integrity: sha512-5W6vNYkhgfh7URiXTO1E9a0cy4fSgfE4+Hl5agb/U1sa0kjOLMLC1wObxwKxecE17j0URxuTrYZZME4/VH57Hg==} + '@rollup/rollup-linux-riscv64-musl@4.50.1': + resolution: {integrity: sha512-3Ag8Ls1ggqkGUvSZWYcdgFwriy2lWo+0QlYgEFra/5JGtAd6C5Hw59oojx1DeqcA2Wds2ayRgvJ4qxVTzCHgzg==} cpu: [riscv64] os: [linux] @@ -677,8 +683,8 @@ packages: cpu: [s390x] os: [linux] - '@rollup/rollup-linux-s390x-gnu@4.40.2': - resolution: {integrity: sha512-B7LKIz+0+p348JoAL4X/YxGx9zOx3sR+o6Hj15Y3aaApNfAshK8+mWZEf759DXfRLeL2vg5LYJBB7DdcleYCoQ==} + '@rollup/rollup-linux-s390x-gnu@4.50.1': + resolution: {integrity: sha512-t9YrKfaxCYe7l7ldFERE1BRg/4TATxIg+YieHQ966jwvo7ddHJxPj9cNFWLAzhkVsbBvNA4qTbPVNsZKBO4NSg==} cpu: [s390x] os: [linux] @@ -687,8 +693,8 @@ packages: cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-gnu@4.40.2': - resolution: {integrity: sha512-lG7Xa+BmBNwpjmVUbmyKxdQJ3Q6whHjMjzQplOs5Z+Gj7mxPtWakGHqzMqNER68G67kmCX9qX57aRsW5V0VOng==} + '@rollup/rollup-linux-x64-gnu@4.50.1': + resolution: {integrity: sha512-MCgtFB2+SVNuQmmjHf+wfI4CMxy3Tk8XjA5Z//A0AKD7QXUYFMQcns91K6dEHBvZPCnhJSyDWLApk40Iq/H3tA==} cpu: [x64] os: [linux] @@ -697,18 +703,23 @@ packages: cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-musl@4.40.2': - resolution: {integrity: sha512-tD46wKHd+KJvsmije4bUskNuvWKFcTOIM9tZ/RrmIvcXnbi0YK/cKS9FzFtAm7Oxi2EhV5N2OpfFB348vSQRXA==} + '@rollup/rollup-linux-x64-musl@4.50.1': + resolution: {integrity: sha512-nEvqG+0jeRmqaUMuwzlfMKwcIVffy/9KGbAGyoa26iu6eSngAYQ512bMXuqqPrlTyfqdlB9FVINs93j534UJrg==} cpu: [x64] os: [linux] + '@rollup/rollup-openharmony-arm64@4.50.1': + resolution: {integrity: sha512-RDsLm+phmT3MJd9SNxA9MNuEAO/J2fhW8GXk62G/B4G7sLVumNFbRwDL6v5NrESb48k+QMqdGbHgEtfU0LCpbA==} + cpu: [arm64] + os: [openharmony] + '@rollup/rollup-win32-arm64-msvc@4.22.4': resolution: {integrity: sha512-BjI+NVVEGAXjGWYHz/vv0pBqfGoUH0IGZ0cICTn7kB9PyjrATSkX+8WkguNjWoj2qSr1im/+tTGRaY+4/PdcQw==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-arm64-msvc@4.40.2': - resolution: {integrity: sha512-Bjv/HG8RRWLNkXwQQemdsWw4Mg+IJ29LK+bJPW2SCzPKOUaMmPEppQlu/Fqk1d7+DX3V7JbFdbkh/NMmurT6Pg==} + '@rollup/rollup-win32-arm64-msvc@4.50.1': + resolution: {integrity: sha512-hpZB/TImk2FlAFAIsoElM3tLzq57uxnGYwplg6WDyAxbYczSi8O2eQ+H2Lx74504rwKtZ3N2g4bCUkiamzS6TQ==} cpu: [arm64] os: [win32] @@ -717,8 +728,8 @@ packages: cpu: [ia32] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.40.2': - resolution: {integrity: sha512-dt1llVSGEsGKvzeIO76HToiYPNPYPkmjhMHhP00T9S4rDern8P2ZWvWAQUEJ+R1UdMWJ/42i/QqJ2WV765GZcA==} + '@rollup/rollup-win32-ia32-msvc@4.50.1': + resolution: {integrity: sha512-SXjv8JlbzKM0fTJidX4eVsH+Wmnp0/WcD8gJxIZyR6Gay5Qcsmdbi9zVtnbkGPG8v2vMR1AD06lGWy5FLMcG7A==} cpu: [ia32] os: [win32] @@ -727,8 +738,8 @@ packages: cpu: [x64] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.40.2': - resolution: {integrity: sha512-bwspbWB04XJpeElvsp+DCylKfF4trJDa2Y9Go8O6A7YLX2LIKGcNK/CYImJN6ZP4DcuOHB4Utl3iCbnR62DudA==} + '@rollup/rollup-win32-x64-msvc@4.50.1': + resolution: {integrity: sha512-StxAO/8ts62KZVRAm4JZYq9+NqNsV7RvimNK+YM7ry//zebEH6meuugqW/P5OFUCjyQgui+9fUxT6d5NShvMvA==} cpu: [x64] os: [win32] @@ -785,8 +796,8 @@ packages: '@types/estree@1.0.6': resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} - '@types/estree@1.0.7': - resolution: {integrity: sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==} + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} @@ -818,13 +829,25 @@ packages: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.9.0' + '@typescript-eslint/project-service@8.43.0': + resolution: {integrity: sha512-htB/+D/BIGoNTQYffZw4uM4NzzuolCoaA/BusuSIcC8YjmBYQioew5VUZAYdAETPjeed0hqCaW7EHg+Robq8uw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + '@typescript-eslint/scope-manager@8.26.0': resolution: {integrity: sha512-E0ntLvsfPqnPwng8b8y4OGuzh/iIOm2z8U3S9zic2TeMLW61u5IH2Q1wu0oSTkfrSzwbDJIB/Lm8O3//8BWMPA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/scope-manager@8.32.1': - resolution: {integrity: sha512-7IsIaIDeZn7kffk7qXC3o6Z4UblZJKV3UBpkvRNpr5NSyLji7tvTcvmnMNYuYLyh26mN8W723xpo3i4MlD33vA==} + '@typescript-eslint/scope-manager@8.43.0': + resolution: {integrity: sha512-daSWlQ87ZhsjrbMLvpuuMAt3y4ba57AuvadcR7f3nl8eS3BjRc8L9VLxFLk92RL5xdXOg6IQ+qKjjqNEimGuAg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/tsconfig-utils@8.43.0': + resolution: {integrity: sha512-ALC2prjZcj2YqqL5X/bwWQmHA2em6/94GcbB/KKu5SX3EBDOsqztmmX1kMkvAJHzxk7TazKzJfFiEIagNV3qEA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' '@typescript-eslint/type-utils@8.26.0': resolution: {integrity: sha512-ruk0RNChLKz3zKGn2LwXuVoeBcUMh+jaqzN461uMMdxy5H9epZqIBtYj7UiPXRuOpaALXGbmRuZQhmwHhaS04Q==} @@ -837,8 +860,8 @@ packages: resolution: {integrity: sha512-89B1eP3tnpr9A8L6PZlSjBvnJhWXtYfZhECqlBl1D9Lme9mHO6iWlsprBtVenQvY1HMhax1mWOjhtL3fh/u+pA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/types@8.32.1': - resolution: {integrity: sha512-YmybwXUJcgGqgAp6bEsgpPXEg6dcCyPyCSr0CAAueacR/CCBi25G3V8gGQ2kRzQRBNol7VQknxMs9HvVa9Rvfg==} + '@typescript-eslint/types@8.43.0': + resolution: {integrity: sha512-vQ2FZaxJpydjSZJKiSW/LJsabFFvV7KgLC5DiLhkBcykhQj8iK9BOaDmQt74nnKdLvceM5xmhaTF+pLekrxEkw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@typescript-eslint/typescript-estree@8.26.0': @@ -847,11 +870,11 @@ packages: peerDependencies: typescript: '>=4.8.4 <5.9.0' - '@typescript-eslint/typescript-estree@8.32.1': - resolution: {integrity: sha512-Y3AP9EIfYwBb4kWGb+simvPaqQoT5oJuzzj9m0i6FCY6SPvlomY2Ei4UEMm7+FXtlNJbor80ximyslzaQF6xhg==} + '@typescript-eslint/typescript-estree@8.43.0': + resolution: {integrity: sha512-7Vv6zlAhPb+cvEpP06WXXy/ZByph9iL6BQRBDj4kmBsW98AqEeQHlj/13X+sZOrKSo9/rNKH4Ul4f6EICREFdw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - typescript: '>=4.8.4 <5.9.0' + typescript: '>=4.8.4 <6.0.0' '@typescript-eslint/utils@8.26.0': resolution: {integrity: sha512-2L2tU3FVwhvU14LndnQCA2frYC8JnPDVKyQtWFPf8IYFMt/ykEN1bPolNhNbCVgOmdzTlWdusCTKA/9nKrf8Ig==} @@ -860,19 +883,19 @@ packages: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.9.0' - '@typescript-eslint/utils@8.32.1': - resolution: {integrity: sha512-DsSFNIgLSrc89gpq1LJB7Hm1YpuhK086DRDJSNrewcGvYloWW1vZLHBTIvarKZDcAORIy/uWNx8Gad+4oMpkSA==} + '@typescript-eslint/utils@8.43.0': + resolution: {integrity: sha512-S1/tEmkUeeswxd0GGcnwuVQPFWo8NzZTOMxCvw8BX7OMxnNae+i8Tm7REQen/SwUIPoPqfKn7EaZ+YLpiB3k9g==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <5.9.0' + typescript: '>=4.8.4 <6.0.0' '@typescript-eslint/visitor-keys@8.26.0': resolution: {integrity: sha512-2z8JQJWAzPdDd51dRQ/oqIJxe99/hoLIqmf8RMCAJQtYDc535W/Jt2+RTP4bP0aKeBG1F65yjIZuczOXCmbWwg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/visitor-keys@8.32.1': - resolution: {integrity: sha512-ar0tjQfObzhSaW3C3QNmTc5ofj0hDoNQ5XWrCy6zDyabdr0TWhCkClp+rywGNj/odAFBVzzJrK4tEq5M4Hmu4w==} + '@typescript-eslint/visitor-keys@8.43.0': + resolution: {integrity: sha512-T+S1KqRD4sg/bHfLwrpF/K3gQLBM1n7Rp7OjjikjTEssI2YJzQpi5WXoynOaQ93ERIuq3O8RBTOUYDKszUCEHw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@vitest/coverage-v8@2.1.9': @@ -923,8 +946,8 @@ packages: engines: {node: '>=0.4.0'} hasBin: true - acorn@8.14.1: - resolution: {integrity: sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==} + acorn@8.15.0: + resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} engines: {node: '>=0.4.0'} hasBin: true @@ -1099,6 +1122,15 @@ packages: supports-color: optional: true + debug@4.4.1: + resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + decimal.js@10.4.3: resolution: {integrity: sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==} @@ -1161,8 +1193,8 @@ packages: emoji-regex@9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} - enhanced-resolve@5.18.1: - resolution: {integrity: sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==} + enhanced-resolve@5.18.3: + resolution: {integrity: sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==} engines: {node: '>=10.13.0'} enquirer@2.4.1: @@ -1241,6 +1273,10 @@ packages: resolution: {integrity: sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + eslint-visitor-keys@4.2.1: + resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + eslint@9.9.1: resolution: {integrity: sha512-dHvhrbfr4xFQ9/dq+jcVneZMyRYLjggWjk6RVsIiHsP8Rz6yZ8LvZ//iU4TrZF+SXWG+JkNF2OyiZRvzgRDqMg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -1387,8 +1423,8 @@ packages: function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} - get-tsconfig@4.10.0: - resolution: {integrity: sha512-kGzZ3LWWQcGIAmg6iWvXn0ei6WDtV26wzHRMwDSzmAbcXrTEXxHy6IehI6/4eT6VRKyMP1eF1VqwrVUmE/LR7A==} + get-tsconfig@4.10.1: + resolution: {integrity: sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==} glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} @@ -1862,6 +1898,10 @@ packages: resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} engines: {node: '>=12'} + picomatch@4.0.3: + resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} + engines: {node: '>=12'} + pify@4.0.1: resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} engines: {node: '>=6'} @@ -1912,6 +1952,10 @@ packages: resolution: {integrity: sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==} engines: {node: ^10 || ^12 || >=14} + postcss@8.5.6: + resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} + engines: {node: ^10 || ^12 || >=14} + prelude-ls@1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} @@ -1984,8 +2028,8 @@ packages: engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true - rollup@4.40.2: - resolution: {integrity: sha512-tfUOg6DTP4rhQ3VjOO6B4wyrJnGOX85requAXvqYTHsOgb2TFJdZ3aWpT8W2kPoypSGP7dZUyzxJ9ee4buM5Fg==} + rollup@4.50.1: + resolution: {integrity: sha512-78E9voJHwnXQMiQdiqswVLZwJIzdBKJ1GdI5Zx6XwoFKUIk09/sSrr+05QFzvYb8q6Y9pPV45zzDuYa3907TZA==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -2139,8 +2183,8 @@ packages: symbol-tree@3.2.4: resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} - tapable@2.2.1: - resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==} + tapable@2.2.3: + resolution: {integrity: sha512-ZL6DDuAlRlLGghwcfmSn9sK3Hr6ArtyudlSAiCqQ6IfE+b+HHbydbYDIG15IfS5do+7XQQBdBiubF/cV2dnDzg==} engines: {node: '>=6'} term-size@2.2.1: @@ -2325,8 +2369,8 @@ packages: terser: optional: true - vite@5.4.19: - resolution: {integrity: sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA==} + vite@5.4.20: + resolution: {integrity: sha512-j3lYzGC3P+B5Yfy/pfKNgVEg4+UtcIJcVRt2cDjIOmhLourAqPqf8P7acgxeiSgUB7E3p2P8/3gNIgDLpwzs4g==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: @@ -2731,6 +2775,11 @@ snapshots: eslint: 9.9.1 eslint-visitor-keys: 3.4.3 + '@eslint-community/eslint-utils@4.9.0(eslint@9.9.1)': + dependencies: + eslint: 9.9.1 + eslint-visitor-keys: 3.4.3 + '@eslint-community/regexpp@4.12.1': {} '@eslint/config-array@0.18.0': @@ -2880,126 +2929,129 @@ snapshots: optionalDependencies: rollup: 4.22.4 - '@rollup/pluginutils@5.1.0(rollup@4.40.2)': + '@rollup/pluginutils@5.1.0(rollup@4.50.1)': dependencies: '@types/estree': 1.0.6 estree-walker: 2.0.2 picomatch: 2.3.1 optionalDependencies: - rollup: 4.40.2 + rollup: 4.50.1 '@rollup/rollup-android-arm-eabi@4.22.4': optional: true - '@rollup/rollup-android-arm-eabi@4.40.2': + '@rollup/rollup-android-arm-eabi@4.50.1': optional: true '@rollup/rollup-android-arm64@4.22.4': optional: true - '@rollup/rollup-android-arm64@4.40.2': + '@rollup/rollup-android-arm64@4.50.1': optional: true '@rollup/rollup-darwin-arm64@4.22.4': optional: true - '@rollup/rollup-darwin-arm64@4.40.2': + '@rollup/rollup-darwin-arm64@4.50.1': optional: true '@rollup/rollup-darwin-x64@4.22.4': optional: true - '@rollup/rollup-darwin-x64@4.40.2': + '@rollup/rollup-darwin-x64@4.50.1': optional: true - '@rollup/rollup-freebsd-arm64@4.40.2': + '@rollup/rollup-freebsd-arm64@4.50.1': optional: true - '@rollup/rollup-freebsd-x64@4.40.2': + '@rollup/rollup-freebsd-x64@4.50.1': optional: true '@rollup/rollup-linux-arm-gnueabihf@4.22.4': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.40.2': + '@rollup/rollup-linux-arm-gnueabihf@4.50.1': optional: true '@rollup/rollup-linux-arm-musleabihf@4.22.4': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.40.2': + '@rollup/rollup-linux-arm-musleabihf@4.50.1': optional: true '@rollup/rollup-linux-arm64-gnu@4.22.4': optional: true - '@rollup/rollup-linux-arm64-gnu@4.40.2': + '@rollup/rollup-linux-arm64-gnu@4.50.1': optional: true '@rollup/rollup-linux-arm64-musl@4.22.4': optional: true - '@rollup/rollup-linux-arm64-musl@4.40.2': + '@rollup/rollup-linux-arm64-musl@4.50.1': optional: true - '@rollup/rollup-linux-loongarch64-gnu@4.40.2': + '@rollup/rollup-linux-loongarch64-gnu@4.50.1': optional: true '@rollup/rollup-linux-powerpc64le-gnu@4.22.4': optional: true - '@rollup/rollup-linux-powerpc64le-gnu@4.40.2': + '@rollup/rollup-linux-ppc64-gnu@4.50.1': optional: true '@rollup/rollup-linux-riscv64-gnu@4.22.4': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.40.2': + '@rollup/rollup-linux-riscv64-gnu@4.50.1': optional: true - '@rollup/rollup-linux-riscv64-musl@4.40.2': + '@rollup/rollup-linux-riscv64-musl@4.50.1': optional: true '@rollup/rollup-linux-s390x-gnu@4.22.4': optional: true - '@rollup/rollup-linux-s390x-gnu@4.40.2': + '@rollup/rollup-linux-s390x-gnu@4.50.1': optional: true '@rollup/rollup-linux-x64-gnu@4.22.4': optional: true - '@rollup/rollup-linux-x64-gnu@4.40.2': + '@rollup/rollup-linux-x64-gnu@4.50.1': optional: true '@rollup/rollup-linux-x64-musl@4.22.4': optional: true - '@rollup/rollup-linux-x64-musl@4.40.2': + '@rollup/rollup-linux-x64-musl@4.50.1': + optional: true + + '@rollup/rollup-openharmony-arm64@4.50.1': optional: true '@rollup/rollup-win32-arm64-msvc@4.22.4': optional: true - '@rollup/rollup-win32-arm64-msvc@4.40.2': + '@rollup/rollup-win32-arm64-msvc@4.50.1': optional: true '@rollup/rollup-win32-ia32-msvc@4.22.4': optional: true - '@rollup/rollup-win32-ia32-msvc@4.40.2': + '@rollup/rollup-win32-ia32-msvc@4.50.1': optional: true '@rollup/rollup-win32-x64-msvc@4.22.4': optional: true - '@rollup/rollup-win32-x64-msvc@4.40.2': + '@rollup/rollup-win32-x64-msvc@4.50.1': optional: true '@stylistic/eslint-plugin-js@1.8.0(eslint@9.9.1)': dependencies: '@types/eslint': 8.56.12 - acorn: 8.14.1 + acorn: 8.15.0 escape-string-regexp: 4.0.0 eslint: 9.9.1 eslint-visitor-keys: 3.4.3 @@ -3020,25 +3072,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.19(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)))(svelte@packages+svelte)(vite@5.4.19(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0))': + '@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.20(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)))(svelte@packages+svelte)(vite@5.4.20(@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.19(@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.20(@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.19(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) + vite: 5.4.20(@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.19(@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.20(@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.19(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)))(svelte@packages+svelte)(vite@5.4.19(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)) + '@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.20(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)))(svelte@packages+svelte)(vite@5.4.20(@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.19(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) - vitefu: 0.2.5(vite@5.4.19(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)) + vite: 5.4.20(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) + vitefu: 0.2.5(vite@5.4.20(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)) transitivePeerDependencies: - supports-color @@ -3053,14 +3105,14 @@ snapshots: '@types/eslint@8.56.12': dependencies: - '@types/estree': 1.0.7 + '@types/estree': 1.0.8 '@types/json-schema': 7.0.15 '@types/estree@1.0.5': {} '@types/estree@1.0.6': {} - '@types/estree@1.0.7': {} + '@types/estree@1.0.8': {} '@types/json-schema@7.0.15': {} @@ -3103,15 +3155,28 @@ snapshots: transitivePeerDependencies: - supports-color + '@typescript-eslint/project-service@8.43.0(typescript@5.5.4)': + dependencies: + '@typescript-eslint/tsconfig-utils': 8.43.0(typescript@5.5.4) + '@typescript-eslint/types': 8.43.0 + debug: 4.4.1 + typescript: 5.5.4 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/scope-manager@8.26.0': dependencies: '@typescript-eslint/types': 8.26.0 '@typescript-eslint/visitor-keys': 8.26.0 - '@typescript-eslint/scope-manager@8.32.1': + '@typescript-eslint/scope-manager@8.43.0': dependencies: - '@typescript-eslint/types': 8.32.1 - '@typescript-eslint/visitor-keys': 8.32.1 + '@typescript-eslint/types': 8.43.0 + '@typescript-eslint/visitor-keys': 8.43.0 + + '@typescript-eslint/tsconfig-utils@8.43.0(typescript@5.5.4)': + dependencies: + typescript: 5.5.4 '@typescript-eslint/type-utils@8.26.0(eslint@9.9.1)(typescript@5.5.4)': dependencies: @@ -3126,7 +3191,7 @@ snapshots: '@typescript-eslint/types@8.26.0': {} - '@typescript-eslint/types@8.32.1': {} + '@typescript-eslint/types@8.43.0': {} '@typescript-eslint/typescript-estree@8.26.0(typescript@5.5.4)': dependencies: @@ -3142,11 +3207,13 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/typescript-estree@8.32.1(typescript@5.5.4)': + '@typescript-eslint/typescript-estree@8.43.0(typescript@5.5.4)': dependencies: - '@typescript-eslint/types': 8.32.1 - '@typescript-eslint/visitor-keys': 8.32.1 - debug: 4.4.0 + '@typescript-eslint/project-service': 8.43.0(typescript@5.5.4) + '@typescript-eslint/tsconfig-utils': 8.43.0(typescript@5.5.4) + '@typescript-eslint/types': 8.43.0 + '@typescript-eslint/visitor-keys': 8.43.0 + debug: 4.4.1 fast-glob: 3.3.3 is-glob: 4.0.3 minimatch: 9.0.5 @@ -3167,12 +3234,12 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.32.1(eslint@9.9.1)(typescript@5.5.4)': + '@typescript-eslint/utils@8.43.0(eslint@9.9.1)(typescript@5.5.4)': dependencies: - '@eslint-community/eslint-utils': 4.7.0(eslint@9.9.1) - '@typescript-eslint/scope-manager': 8.32.1 - '@typescript-eslint/types': 8.32.1 - '@typescript-eslint/typescript-estree': 8.32.1(typescript@5.5.4) + '@eslint-community/eslint-utils': 4.9.0(eslint@9.9.1) + '@typescript-eslint/scope-manager': 8.43.0 + '@typescript-eslint/types': 8.43.0 + '@typescript-eslint/typescript-estree': 8.43.0(typescript@5.5.4) eslint: 9.9.1 typescript: 5.5.4 transitivePeerDependencies: @@ -3183,10 +3250,10 @@ snapshots: '@typescript-eslint/types': 8.26.0 eslint-visitor-keys: 4.2.0 - '@typescript-eslint/visitor-keys@8.32.1': + '@typescript-eslint/visitor-keys@8.43.0': dependencies: - '@typescript-eslint/types': 8.32.1 - eslint-visitor-keys: 4.2.0 + '@typescript-eslint/types': 8.43.0 + eslint-visitor-keys: 4.2.1 '@vitest/coverage-v8@2.1.9(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: @@ -3250,13 +3317,13 @@ snapshots: dependencies: acorn: 8.14.0 - acorn-jsx@5.3.2(acorn@8.14.1): + acorn-jsx@5.3.2(acorn@8.15.0): dependencies: - acorn: 8.14.1 + acorn: 8.15.0 acorn@8.14.0: {} - acorn@8.14.1: {} + acorn@8.15.0: {} agent-base@7.1.1: dependencies: @@ -3416,6 +3483,10 @@ snapshots: dependencies: ms: 2.1.3 + debug@4.4.1: + dependencies: + ms: 2.1.3 + decimal.js@10.4.3: {} deep-eql@5.0.2: {} @@ -3464,10 +3535,10 @@ snapshots: emoji-regex@9.2.2: {} - enhanced-resolve@5.18.1: + enhanced-resolve@5.18.3: dependencies: graceful-fs: 4.2.11 - tapable: 2.2.1 + tapable: 2.2.3 enquirer@2.4.1: dependencies: @@ -3519,7 +3590,7 @@ snapshots: eslint-plugin-es-x@7.8.0(eslint@9.9.1): dependencies: - '@eslint-community/eslint-utils': 4.7.0(eslint@9.9.1) + '@eslint-community/eslint-utils': 4.9.0(eslint@9.9.1) '@eslint-community/regexpp': 4.12.1 eslint: 9.9.1 eslint-compat-utils: 0.5.1(eslint@9.9.1) @@ -3528,12 +3599,12 @@ snapshots: eslint-plugin-n@17.16.1(eslint@9.9.1)(typescript@5.5.4): dependencies: - '@eslint-community/eslint-utils': 4.7.0(eslint@9.9.1) - '@typescript-eslint/utils': 8.32.1(eslint@9.9.1)(typescript@5.5.4) - enhanced-resolve: 5.18.1 + '@eslint-community/eslint-utils': 4.9.0(eslint@9.9.1) + '@typescript-eslint/utils': 8.43.0(eslint@9.9.1)(typescript@5.5.4) + enhanced-resolve: 5.18.3 eslint: 9.9.1 eslint-plugin-es-x: 7.8.0(eslint@9.9.1) - get-tsconfig: 4.10.0 + get-tsconfig: 4.10.1 globals: 15.15.0 ignore: 5.3.2 minimatch: 9.0.5 @@ -3575,6 +3646,8 @@ snapshots: eslint-visitor-keys@4.2.0: {} + eslint-visitor-keys@4.2.1: {} + eslint@9.9.1: dependencies: '@eslint-community/eslint-utils': 4.4.1(eslint@9.9.1) @@ -3624,8 +3697,8 @@ snapshots: espree@9.6.1: dependencies: - acorn: 8.14.1 - acorn-jsx: 5.3.2(acorn@8.14.1) + acorn: 8.15.0 + acorn-jsx: 5.3.2(acorn@8.15.0) eslint-visitor-keys: 3.4.3 esprima@4.0.1: {} @@ -3754,7 +3827,7 @@ snapshots: function-bind@1.1.2: {} - get-tsconfig@4.10.0: + get-tsconfig@4.10.1: dependencies: resolve-pkg-maps: 1.0.0 @@ -4195,6 +4268,8 @@ snapshots: picomatch@4.0.2: {} + picomatch@4.0.3: {} + pify@4.0.1: {} playwright-core@1.46.1: {} @@ -4221,9 +4296,9 @@ snapshots: dependencies: postcss: 8.5.3 - postcss-scss@4.0.9(postcss@8.5.3): + postcss-scss@4.0.9(postcss@8.5.6): dependencies: - postcss: 8.5.3 + postcss: 8.5.6 postcss-selector-parser@7.1.0: dependencies: @@ -4236,6 +4311,12 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 + postcss@8.5.6: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + prelude-ls@1.2.1: {} prettier-plugin-svelte@3.4.0(prettier@3.2.4)(svelte@packages+svelte): @@ -4309,30 +4390,31 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.22.4 fsevents: 2.3.3 - rollup@4.40.2: + rollup@4.50.1: dependencies: - '@types/estree': 1.0.7 + '@types/estree': 1.0.8 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.40.2 - '@rollup/rollup-android-arm64': 4.40.2 - '@rollup/rollup-darwin-arm64': 4.40.2 - '@rollup/rollup-darwin-x64': 4.40.2 - '@rollup/rollup-freebsd-arm64': 4.40.2 - '@rollup/rollup-freebsd-x64': 4.40.2 - '@rollup/rollup-linux-arm-gnueabihf': 4.40.2 - '@rollup/rollup-linux-arm-musleabihf': 4.40.2 - '@rollup/rollup-linux-arm64-gnu': 4.40.2 - '@rollup/rollup-linux-arm64-musl': 4.40.2 - '@rollup/rollup-linux-loongarch64-gnu': 4.40.2 - '@rollup/rollup-linux-powerpc64le-gnu': 4.40.2 - '@rollup/rollup-linux-riscv64-gnu': 4.40.2 - '@rollup/rollup-linux-riscv64-musl': 4.40.2 - '@rollup/rollup-linux-s390x-gnu': 4.40.2 - '@rollup/rollup-linux-x64-gnu': 4.40.2 - '@rollup/rollup-linux-x64-musl': 4.40.2 - '@rollup/rollup-win32-arm64-msvc': 4.40.2 - '@rollup/rollup-win32-ia32-msvc': 4.40.2 - '@rollup/rollup-win32-x64-msvc': 4.40.2 + '@rollup/rollup-android-arm-eabi': 4.50.1 + '@rollup/rollup-android-arm64': 4.50.1 + '@rollup/rollup-darwin-arm64': 4.50.1 + '@rollup/rollup-darwin-x64': 4.50.1 + '@rollup/rollup-freebsd-arm64': 4.50.1 + '@rollup/rollup-freebsd-x64': 4.50.1 + '@rollup/rollup-linux-arm-gnueabihf': 4.50.1 + '@rollup/rollup-linux-arm-musleabihf': 4.50.1 + '@rollup/rollup-linux-arm64-gnu': 4.50.1 + '@rollup/rollup-linux-arm64-musl': 4.50.1 + '@rollup/rollup-linux-loongarch64-gnu': 4.50.1 + '@rollup/rollup-linux-ppc64-gnu': 4.50.1 + '@rollup/rollup-linux-riscv64-gnu': 4.50.1 + '@rollup/rollup-linux-riscv64-musl': 4.50.1 + '@rollup/rollup-linux-s390x-gnu': 4.50.1 + '@rollup/rollup-linux-x64-gnu': 4.50.1 + '@rollup/rollup-linux-x64-musl': 4.50.1 + '@rollup/rollup-openharmony-arm64': 4.50.1 + '@rollup/rollup-win32-arm64-msvc': 4.50.1 + '@rollup/rollup-win32-ia32-msvc': 4.50.1 + '@rollup/rollup-win32-x64-msvc': 4.50.1 fsevents: 2.3.3 rrweb-cssom@0.7.1: {} @@ -4455,15 +4537,15 @@ snapshots: eslint-scope: 8.4.0 eslint-visitor-keys: 4.2.0 espree: 10.1.0 - postcss: 8.5.3 - postcss-scss: 4.0.9(postcss@8.5.3) + postcss: 8.5.6 + postcss-scss: 4.0.9(postcss@8.5.6) postcss-selector-parser: 7.1.0 optionalDependencies: svelte: link:packages/svelte symbol-tree@3.2.4: {} - tapable@2.2.1: {} + tapable@2.2.3: {} term-size@2.2.1: {} @@ -4543,7 +4625,7 @@ snapshots: ts-declaration-location@1.0.7(typescript@5.5.4): dependencies: - picomatch: 4.0.2 + picomatch: 4.0.3 typescript: 5.5.4 type-check@0.4.0: @@ -4582,7 +4664,7 @@ snapshots: debug: 4.4.0 es-module-lexer: 1.6.0 pathe: 1.1.2 - vite: 5.4.19(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) + vite: 5.4.20(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) transitivePeerDependencies: - '@types/node' - less @@ -4594,10 +4676,10 @@ snapshots: - supports-color - terser - vite-plugin-inspect@0.8.4(rollup@4.40.2)(vite@5.4.19(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)): + vite-plugin-inspect@0.8.4(rollup@4.50.1)(vite@5.4.20(@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.40.2) + '@rollup/pluginutils': 5.1.0(rollup@4.50.1) debug: 4.4.0 error-stack-parser-es: 0.1.1 fs-extra: 11.2.0 @@ -4605,7 +4687,7 @@ snapshots: perfect-debounce: 1.0.0 picocolors: 1.1.1 sirv: 2.0.4 - vite: 5.4.19(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) + vite: 5.4.20(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) transitivePeerDependencies: - rollup - supports-color @@ -4613,7 +4695,7 @@ snapshots: vite@5.4.14(@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 + postcss: 8.5.6 rollup: 4.22.4 optionalDependencies: '@types/node': 20.12.7 @@ -4622,11 +4704,11 @@ snapshots: sass: 1.70.0 terser: 5.27.0 - vite@5.4.19(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0): + vite@5.4.20(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0): dependencies: esbuild: 0.21.5 - postcss: 8.5.3 - rollup: 4.40.2 + postcss: 8.5.6 + rollup: 4.50.1 optionalDependencies: '@types/node': 20.12.7 fsevents: 2.3.3 @@ -4634,9 +4716,9 @@ snapshots: sass: 1.70.0 terser: 5.27.0 - vitefu@0.2.5(vite@5.4.19(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)): + vitefu@0.2.5(vite@5.4.20(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)): optionalDependencies: - vite: 5.4.19(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) + vite: 5.4.20(@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 72ce753278a3e1f0046d1d13171d3b86445a241a Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 11 Sep 2025 10:58:56 -0400 Subject: [PATCH 011/347] fix: flush effects scheduled during boundary's pending phase (#16738) Alternative to #16721, partial alternative to #16709. Closes #16691, closes #16627, closes #16582 and #16651 as well. --- .changeset/ninety-pandas-move.md | 5 +++++ .../src/internal/client/dom/blocks/boundary.js | 2 +- .../src/internal/client/reactivity/batch.js | 2 +- .../samples/async-attachment/Inner.svelte | 10 ++++++++++ .../samples/async-attachment/_config.js | 18 ++++++++++++++++++ .../samples/async-attachment/main.svelte | 16 ++++++++++++++++ .../async-effect-after-await/Child.svelte | 7 +++++++ .../async-effect-after-await/_config.js | 9 +++++++++ .../async-effect-after-await/main.svelte | 9 +++++++++ 9 files changed, 76 insertions(+), 2 deletions(-) create mode 100644 .changeset/ninety-pandas-move.md create mode 100644 packages/svelte/tests/runtime-runes/samples/async-attachment/Inner.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/async-attachment/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/async-attachment/main.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/async-effect-after-await/Child.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/async-effect-after-await/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/async-effect-after-await/main.svelte diff --git a/.changeset/ninety-pandas-move.md b/.changeset/ninety-pandas-move.md new file mode 100644 index 0000000000..65f57ddbbf --- /dev/null +++ b/.changeset/ninety-pandas-move.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: flush effects scheduled during boundary's pending phase diff --git a/packages/svelte/src/internal/client/dom/blocks/boundary.js b/packages/svelte/src/internal/client/dom/blocks/boundary.js index d4582024f7..2a9736553c 100644 --- a/packages/svelte/src/internal/client/dom/blocks/boundary.js +++ b/packages/svelte/src/internal/client/dom/blocks/boundary.js @@ -23,7 +23,7 @@ import { queue_micro_task } from '../task.js'; import * as e from '../../errors.js'; import * as w from '../../warnings.js'; import { DEV } from 'esm-env'; -import { Batch, effect_pending_updates } from '../../reactivity/batch.js'; +import { Batch, current_batch, effect_pending_updates } from '../../reactivity/batch.js'; import { internal_set, source } from '../../reactivity/sources.js'; import { tag } from '../../dev/tracing.js'; import { createSubscriber } from '../../../../reactivity/create-subscriber.js'; diff --git a/packages/svelte/src/internal/client/reactivity/batch.js b/packages/svelte/src/internal/client/reactivity/batch.js index c28617608e..e504ae2e3f 100644 --- a/packages/svelte/src/internal/client/reactivity/batch.js +++ b/packages/svelte/src/internal/client/reactivity/batch.js @@ -668,7 +668,7 @@ export function suspend() { batch.activate(); batch.decrement(); } else { - batch.deactivate(); + batch.flush(); } unset_context(); diff --git a/packages/svelte/tests/runtime-runes/samples/async-attachment/Inner.svelte b/packages/svelte/tests/runtime-runes/samples/async-attachment/Inner.svelte new file mode 100644 index 0000000000..b9b9d7a3d0 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/async-attachment/Inner.svelte @@ -0,0 +1,10 @@ + + +

{test}

+
diff --git a/packages/svelte/tests/runtime-runes/samples/async-attachment/_config.js b/packages/svelte/tests/runtime-runes/samples/async-attachment/_config.js new file mode 100644 index 0000000000..f6b48b38b1 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/async-attachment/_config.js @@ -0,0 +1,18 @@ +import { tick } from 'svelte'; +import { test } from '../../test'; + +export default test({ + async test({ assert, target }) { + await tick(); + assert.htmlEqual(target.innerHTML, '

foo

foo
'); + + const [toggle] = target.querySelectorAll('button'); + toggle.click(); + await tick(); + assert.htmlEqual(target.innerHTML, ''); + + toggle.click(); + await tick(); + assert.htmlEqual(target.innerHTML, '

foo

foo
'); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/async-attachment/main.svelte b/packages/svelte/tests/runtime-runes/samples/async-attachment/main.svelte new file mode 100644 index 0000000000..6cef6e8f5c --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/async-attachment/main.svelte @@ -0,0 +1,16 @@ + + + + + {#if show} + + {/if} + + {#snippet pending()} +

pending

+ {/snippet} +
diff --git a/packages/svelte/tests/runtime-runes/samples/async-effect-after-await/Child.svelte b/packages/svelte/tests/runtime-runes/samples/async-effect-after-await/Child.svelte new file mode 100644 index 0000000000..682f7a0631 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/async-effect-after-await/Child.svelte @@ -0,0 +1,7 @@ + diff --git a/packages/svelte/tests/runtime-runes/samples/async-effect-after-await/_config.js b/packages/svelte/tests/runtime-runes/samples/async-effect-after-await/_config.js new file mode 100644 index 0000000000..81548a25ea --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/async-effect-after-await/_config.js @@ -0,0 +1,9 @@ +import { tick } from 'svelte'; +import { test } from '../../test'; + +export default test({ + async test({ assert, logs }) { + await tick(); + assert.deepEqual(logs, ['hello']); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/async-effect-after-await/main.svelte b/packages/svelte/tests/runtime-runes/samples/async-effect-after-await/main.svelte new file mode 100644 index 0000000000..d4b67f8803 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/async-effect-after-await/main.svelte @@ -0,0 +1,9 @@ + + + + + + {#snippet pending()}{/snippet} + From df13be8727ab16319a4f01ac39ff2ab195e953ae Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 11 Sep 2025 11:30:50 -0400 Subject: [PATCH 012/347] Version Packages (#16754) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .changeset/ninety-pandas-move.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/ninety-pandas-move.md diff --git a/.changeset/ninety-pandas-move.md b/.changeset/ninety-pandas-move.md deleted file mode 100644 index 65f57ddbbf..0000000000 --- a/.changeset/ninety-pandas-move.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: flush effects scheduled during boundary's pending phase diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 4e384d5697..62f109c82f 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,11 @@ # svelte +## 5.38.10 + +### Patch Changes + +- fix: flush effects scheduled during boundary's pending phase ([#16738](https://github.com/sveltejs/svelte/pull/16738)) + ## 5.38.9 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index d445e0acf8..6c91ec6bbb 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.38.9", + "version": "5.38.10", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index edb787c00c..9bfa7a5421 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.38.9'; +export const VERSION = '5.38.10'; export const PUBLIC_VERSION = '5'; From 808fbb4989d0e1687577cb4534e4d8350fb8971d Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 11 Sep 2025 11:49:14 -0400 Subject: [PATCH 013/347] chore: inline `suspend` (#16755) * chore: inline `suspend` * invert condition --- packages/svelte/src/internal/client/index.js | 2 +- .../src/internal/client/reactivity/async.js | 21 +++++++++++++++--- .../src/internal/client/reactivity/batch.js | 22 ------------------- 3 files changed, 19 insertions(+), 26 deletions(-) diff --git a/packages/svelte/src/internal/client/index.js b/packages/svelte/src/internal/client/index.js index dbff5c4599..3c5409bcfe 100644 --- a/packages/svelte/src/internal/client/index.js +++ b/packages/svelte/src/internal/client/index.js @@ -103,7 +103,7 @@ export { save, track_reactivity_loss } from './reactivity/async.js'; -export { flushSync as flush, suspend } from './reactivity/batch.js'; +export { flushSync as flush } from './reactivity/batch.js'; export { async_derived, user_derived as derived, diff --git a/packages/svelte/src/internal/client/reactivity/async.js b/packages/svelte/src/internal/client/reactivity/async.js index b7a5d5cdb7..a109a1f4d8 100644 --- a/packages/svelte/src/internal/client/reactivity/async.js +++ b/packages/svelte/src/internal/client/reactivity/async.js @@ -11,7 +11,7 @@ import { set_active_effect, set_active_reaction } from '../runtime.js'; -import { current_batch, suspend } from './batch.js'; +import { Batch, current_batch } from './batch.js'; import { async_derived, current_async_effect, @@ -178,7 +178,13 @@ export function unset_context() { * @param {() => Promise} fn */ export async function async_body(fn) { - var unsuspend = suspend(); + var boundary = get_boundary(); + var batch = /** @type {Batch} */ (current_batch); + var pending = boundary.is_pending(); + + boundary.update_pending_count(1); + if (!pending) batch.increment(); + var active = /** @type {Effect} */ (active_effect); try { @@ -188,6 +194,15 @@ export async function async_body(fn) { invoke_error_boundary(error, active); } } finally { - unsuspend(); + boundary.update_pending_count(-1); + + if (pending) { + batch.flush(); + } else { + batch.activate(); + batch.decrement(); + } + + unset_context(); } } diff --git a/packages/svelte/src/internal/client/reactivity/batch.js b/packages/svelte/src/internal/client/reactivity/batch.js index e504ae2e3f..3d234f5bba 100644 --- a/packages/svelte/src/internal/client/reactivity/batch.js +++ b/packages/svelte/src/internal/client/reactivity/batch.js @@ -653,28 +653,6 @@ export function schedule_effect(signal) { queued_root_effects.push(effect); } -export function suspend() { - var boundary = get_boundary(); - var batch = /** @type {Batch} */ (current_batch); - var pending = boundary.is_pending(); - - boundary.update_pending_count(1); - if (!pending) batch.increment(); - - return function unsuspend() { - boundary.update_pending_count(-1); - - if (!pending) { - batch.activate(); - batch.decrement(); - } else { - batch.flush(); - } - - unset_context(); - }; -} - /** * Forcibly remove all current batches, to prevent cross-talk between tests */ From a0598014d2b634566d8a62acca6b0c7601054e18 Mon Sep 17 00:00:00 2001 From: Aaron Ajose Date: Sun, 14 Sep 2025 22:09:40 +0300 Subject: [PATCH 014/347] docs: Fix some inaccuracies (#16759) * docs: Fix some inaccuracies * Apply suggestions from code review --------- Co-authored-by: Rich Harris --- CONTRIBUTING.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0653b08b76..e940252892 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -9,7 +9,7 @@ The [Open Source Guides](https://opensource.guide/) website has a collection of ## Get involved -There are many ways to contribute to Svelte, and many of them do not involve writing any code. Here's a few ideas to get started: +There are many ways to contribute to Svelte, and many of them do not involve writing any code. Here are a few ideas to get started: - Simply start using Svelte. Go through the [Getting Started](https://svelte.dev/docs#getting-started) guide. Does everything work as expected? If not, we're always looking for improvements. Let us know by [opening an issue](#reporting-new-issues). - Look through the [open issues](https://github.com/sveltejs/svelte/issues). A good starting point would be issues tagged [good first issue](https://github.com/sveltejs/svelte/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22). Provide workarounds, ask for clarification, or suggest labels. Help [triage issues](#triaging-issues-and-pull-requests). @@ -90,9 +90,9 @@ A good test plan has the exact commands you ran and their output, provides scree #### Writing tests -All tests are located in `/test` folder. +All tests are located in the `/tests` folder. -Test samples are kept in `/test/xxx/samples` folder. +Test samples are kept in `/tests/xxx/samples` folders. #### Running tests From 8b4e1fcb7aa321c15382652b60d430bcd0bda960 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 15 Sep 2025 10:00:45 -0400 Subject: [PATCH 015/347] chore: extract a couple of drive-by fixes from other branch (#16772) * chore: extract a couple of drive-by fixes from other branch * more --- packages/svelte/src/internal/client/dom/operations.js | 6 +++--- packages/svelte/src/internal/client/dom/template.js | 3 +++ packages/svelte/src/internal/client/reactivity/batch.js | 3 --- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/svelte/src/internal/client/dom/operations.js b/packages/svelte/src/internal/client/dom/operations.js index abc29a7670..c527ca23e3 100644 --- a/packages/svelte/src/internal/client/dom/operations.js +++ b/packages/svelte/src/internal/client/dom/operations.js @@ -130,11 +130,11 @@ export function child(node, is_text) { /** * Don't mark this as side-effect-free, hydration needs to walk all nodes - * @param {DocumentFragment | TemplateNode[]} fragment - * @param {boolean} is_text + * @param {DocumentFragment | TemplateNode | TemplateNode[]} fragment + * @param {boolean} [is_text] * @returns {Node | null} */ -export function first_child(fragment, is_text) { +export function first_child(fragment, is_text = false) { if (!hydrating) { // when not hydrating, `fragment` is a `DocumentFragment` (the result of calling `open_frag`) var first = /** @type {DocumentFragment} */ (get_first_child(/** @type {Node} */ (fragment))); diff --git a/packages/svelte/src/internal/client/dom/template.js b/packages/svelte/src/internal/client/dom/template.js index 265a52262f..135ca86610 100644 --- a/packages/svelte/src/internal/client/dom/template.js +++ b/packages/svelte/src/internal/client/dom/template.js @@ -316,6 +316,9 @@ export function text(value = '') { return node; } +/** + * @returns {TemplateNode | DocumentFragment} + */ export function comment() { // we're not delegating to `template` here for performance reasons if (hydrating) { diff --git a/packages/svelte/src/internal/client/reactivity/batch.js b/packages/svelte/src/internal/client/reactivity/batch.js index 3d234f5bba..35aff7d4c9 100644 --- a/packages/svelte/src/internal/client/reactivity/batch.js +++ b/packages/svelte/src/internal/client/reactivity/batch.js @@ -10,12 +10,10 @@ import { INERT, RENDER_EFFECT, ROOT_EFFECT, - USER_EFFECT, MAYBE_DIRTY } from '#client/constants'; import { async_mode_flag } from '../../flags/index.js'; import { deferred, define_property } from '../../shared/utils.js'; -import { get_boundary } from '../dom/blocks/boundary.js'; import { active_effect, is_dirty, @@ -30,7 +28,6 @@ import { DEV } from 'esm-env'; import { invoke_error_boundary } from '../error-handling.js'; import { old_values } from './sources.js'; import { unlink_effect } from './effects.js'; -import { unset_context } from './async.js'; /** @type {Set} */ const batches = new Set(); From 8b106b94f41a87ebfff251f70d602dda70265dc9 Mon Sep 17 00:00:00 2001 From: 7nik Date: Mon, 15 Sep 2025 20:24:31 +0300 Subject: [PATCH 016/347] fix: correctly SSR hidden="until-found" (#16773) --- .changeset/pink-gifts-sell.md | 5 +++++ packages/svelte/src/internal/shared/attributes.js | 4 ++++ packages/svelte/src/utils.js | 1 - .../samples/attribute-spread-hidden-2/_expected.html | 1 + .../samples/attribute-spread-hidden-2/main.svelte | 3 +++ 5 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 .changeset/pink-gifts-sell.md create mode 100644 packages/svelte/tests/runtime-runes/samples/attribute-spread-hidden-2/_expected.html create mode 100644 packages/svelte/tests/runtime-runes/samples/attribute-spread-hidden-2/main.svelte diff --git a/.changeset/pink-gifts-sell.md b/.changeset/pink-gifts-sell.md new file mode 100644 index 0000000000..f3f91ff7d9 --- /dev/null +++ b/.changeset/pink-gifts-sell.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: correctly SSR hidden="until-found" diff --git a/packages/svelte/src/internal/shared/attributes.js b/packages/svelte/src/internal/shared/attributes.js index c8758c1d4d..a96e71ff6f 100644 --- a/packages/svelte/src/internal/shared/attributes.js +++ b/packages/svelte/src/internal/shared/attributes.js @@ -23,6 +23,10 @@ const replacements = { */ export function attr(name, value, is_boolean = false) { if (value == null || (!value && is_boolean)) return ''; + // attribute hidden for values other than "until-found" behaves like a boolean attribute + if (name === 'hidden' && value !== 'until-found') { + is_boolean = true; + } const normalized = (name in replacements && replacements[name].get(value)) || value; const assignment = is_boolean ? '' : `="${escape_html(normalized, true)}"`; return ` ${name}${assignment}`; diff --git a/packages/svelte/src/utils.js b/packages/svelte/src/utils.js index f8c39253ac..f8a7e8d46d 100644 --- a/packages/svelte/src/utils.js +++ b/packages/svelte/src/utils.js @@ -154,7 +154,6 @@ const DOM_BOOLEAN_ATTRIBUTES = [ 'default', 'disabled', 'formnovalidate', - 'hidden', 'indeterminate', 'inert', 'ismap', diff --git a/packages/svelte/tests/runtime-runes/samples/attribute-spread-hidden-2/_expected.html b/packages/svelte/tests/runtime-runes/samples/attribute-spread-hidden-2/_expected.html new file mode 100644 index 0000000000..80937efaee --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/attribute-spread-hidden-2/_expected.html @@ -0,0 +1 @@ +
A
diff --git a/packages/svelte/tests/runtime-runes/samples/attribute-spread-hidden-2/main.svelte b/packages/svelte/tests/runtime-runes/samples/attribute-spread-hidden-2/main.svelte new file mode 100644 index 0000000000..6738b34054 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/attribute-spread-hidden-2/main.svelte @@ -0,0 +1,3 @@ +
A
+
B
+
C
\ No newline at end of file From 8c982f61013a517bec2edf68a82ce1a1188fcd8f Mon Sep 17 00:00:00 2001 From: 7nik Date: Tue, 16 Sep 2025 11:45:18 +0300 Subject: [PATCH 017/347] fix: correct wrong fix, get test to actually do something (#16779) #16773 added a test with _expected.html to a wrong suit, so it was ignored, and this allowed to slip in a new bug of printing the falsy hidden attribute as hidden (shortened enabled form). That fix wasn't released yet, so no changeset. --- packages/svelte/src/internal/shared/attributes.js | 2 +- .../samples/attribute-spread-hidden}/_expected.html | 0 .../samples/attribute-spread-hidden}/main.svelte | 0 3 files changed, 1 insertion(+), 1 deletion(-) rename packages/svelte/tests/{runtime-runes/samples/attribute-spread-hidden-2 => server-side-rendering/samples/attribute-spread-hidden}/_expected.html (100%) rename packages/svelte/tests/{runtime-runes/samples/attribute-spread-hidden-2 => server-side-rendering/samples/attribute-spread-hidden}/main.svelte (100%) diff --git a/packages/svelte/src/internal/shared/attributes.js b/packages/svelte/src/internal/shared/attributes.js index a96e71ff6f..4ad550e8d6 100644 --- a/packages/svelte/src/internal/shared/attributes.js +++ b/packages/svelte/src/internal/shared/attributes.js @@ -22,11 +22,11 @@ const replacements = { * @returns {string} */ export function attr(name, value, is_boolean = false) { - if (value == null || (!value && is_boolean)) return ''; // attribute hidden for values other than "until-found" behaves like a boolean attribute if (name === 'hidden' && value !== 'until-found') { is_boolean = true; } + if (value == null || (!value && is_boolean)) return ''; const normalized = (name in replacements && replacements[name].get(value)) || value; const assignment = is_boolean ? '' : `="${escape_html(normalized, true)}"`; return ` ${name}${assignment}`; diff --git a/packages/svelte/tests/runtime-runes/samples/attribute-spread-hidden-2/_expected.html b/packages/svelte/tests/server-side-rendering/samples/attribute-spread-hidden/_expected.html similarity index 100% rename from packages/svelte/tests/runtime-runes/samples/attribute-spread-hidden-2/_expected.html rename to packages/svelte/tests/server-side-rendering/samples/attribute-spread-hidden/_expected.html diff --git a/packages/svelte/tests/runtime-runes/samples/attribute-spread-hidden-2/main.svelte b/packages/svelte/tests/server-side-rendering/samples/attribute-spread-hidden/main.svelte similarity index 100% rename from packages/svelte/tests/runtime-runes/samples/attribute-spread-hidden-2/main.svelte rename to packages/svelte/tests/server-side-rendering/samples/attribute-spread-hidden/main.svelte From b8fd326d96c3a57feff3a4fafcd2d1651001b109 Mon Sep 17 00:00:00 2001 From: Elliott Johnson Date: Wed, 17 Sep 2025 15:49:57 -0600 Subject: [PATCH 018/347] feat: async SSR (#16748) * feat: First pass at payload * first crack * snapshots * checkpoint * fix: cloning * add test option * big dumb * today's hard work; few tests left to fix * improve * tests passing no wayyyyy yo * lots of progress, couple of failing tests around selects * meh * solve async tree stuff * fix select/option stuff * whoop, tests * simplify * feat: hoisting * fix: `$effect.pending` sends updates to incorrect boundary * changeset * stuff from upstream * feat: first hydrationgaa * remove docs * snapshots * silly fix * checkpoint * meh * ALKASJDFALSKDFJ the test passes * chore: Update a bunch of tests for hydration markers * chore: remove snippet and is_async * naming * better errors for sync-in-async * test improvements * idk man * merge local branches (#16757) * use fragment as async hoist boundary * remove async_hoist_boundary * only dewaterfall when necessary * unused * simplify/fix * de-waterfall awaits in separate elements * update snapshots * remove unnecessary wrapper * fix * fix * remove suspends_without_fallback --------- Co-authored-by: Rich Harris * Update payload.js Co-authored-by: Rich Harris * checkpoint * got the extra children to go away * just gonna go ahead and merge this as the review comments take up too much space * chore: remove hoisted_promises (#16766) * chore: remove hoisted_promises * WIP optimise promises * WIP * fix with await in prop * tweak * fix type error * Update packages/svelte/src/compiler/phases/3-transform/server/visitors/SvelteHead.js * chore: fix hydration treeshaking (#16767) * chore: fix hydration treeshaking * fix * remove await_outside_boundary error (#16762) * chore: remove unused analysis.boundary (#16763) * chore: simplify slots (#16765) * chore: simplify slots * unused * Apply suggestions from code review * chore: remove metadata.pending (#16764) * Update packages/svelte/src/compiler/phases/3-transform/server/visitors/SnippetBlock.js * put this back where it was, keep the diff small * Update packages/svelte/src/compiler/phases/types.d.ts Co-authored-by: Rich Harris * chore: remove analysis.state.title (#16771) * chore: remove analysis.state.title * unused * chore: remove is_async (#16769) * chore: remove is_async * unused * Apply suggestions from code review Co-authored-by: Rich Harris * cleanup * lint * clean up payload a bit * compiler work * run ssr on sync and async * prettier * inline capture * Update packages/svelte/src/compiler/phases/3-transform/server/visitors/EachBlock.js * chore: simplify build_template (#16780) * small tweak to aid greppability * chore: fix SSR context (#16781) * at least passing * cleanup * fix * remove push/pop from exports, not needed with payload * I think this is better but tbh not sure * async SSR * qualification * errors: * I have lost the plot * finally * ugh * tweak error codes to better align with existing conventions, such as they are * tweak messages * remove unused args * DRY out a bit * unused * unused * unused * simplify - we can enforce readonly at a type level * unused * simplify * avoid magical accessors * simplify algorithm * unused * unused * reduce indirection * TreeState -> SSRState * mark deprecated methods * grab this.local from parent directly * rename render -> fn per conventions (fn indicates 'arbitrary code') * reduce indirection * Revert "reduce indirection" This reverts commit 3ec461baad451654db6d518734aeeb7b366f7c2e. * tweak * okay works this time * no way chat, it works * fix context stuff * tweak * make it chainable * lint * clean up * lint * Update packages/svelte/src/internal/server/types.d.ts Co-authored-by: Rich Harris * sunset html for async * types * we use 'deprecated' in other messages * oops --------- Co-authored-by: Rich Harris --- .changeset/forty-insects-cheat.md | 5 + .prettierignore | 1 + .../98-reference/.generated/server-errors.md | 14 + .../.generated/server-warnings.md | 9 + .../98-reference/.generated/shared-errors.md | 20 - eslint.config.js | 1 + .../svelte/messages/server-errors/errors.md | 15 + .../messages/server-errors/lifecycle.md | 5 - .../messages/server-warnings/warnings.md | 5 + .../svelte/messages/shared-errors/errors.md | 18 - .../svelte/scripts/process-messages/index.js | 1 + .../templates/server-warnings.js | 20 + .../compiler/phases/1-parse/state/element.js | 3 +- .../src/compiler/phases/2-analyze/index.js | 15 +- .../src/compiler/phases/2-analyze/types.d.ts | 5 + .../2-analyze/visitors/AwaitExpression.js | 122 +++- .../2-analyze/visitors/CallExpression.js | 1 + .../phases/2-analyze/visitors/ConstTag.js | 6 +- .../2-analyze/visitors/RegularElement.js | 2 +- .../2-analyze/visitors/VariableDeclarator.js | 6 + .../3-transform/client/transform-client.js | 2 - .../phases/3-transform/client/types.d.ts | 8 +- .../client/visitors/AwaitExpression.js | 106 +-- .../client/visitors/CallExpression.js | 4 +- .../3-transform/client/visitors/ConstTag.js | 9 +- .../client/visitors/RegularElement.js | 41 +- .../client/visitors/VariableDeclaration.js | 14 +- .../3-transform/server/transform-server.js | 26 +- .../3-transform/server/visitors/AwaitBlock.js | 38 +- .../server/visitors/AwaitExpression.js | 22 +- .../3-transform/server/visitors/EachBlock.js | 27 +- .../3-transform/server/visitors/IfBlock.js | 22 +- .../server/visitors/RegularElement.js | 76 ++- .../server/visitors/SlotElement.js | 27 +- .../server/visitors/SvelteBoundary.js | 26 +- .../server/visitors/TitleElement.js | 4 +- .../server/visitors/shared/component.js | 60 +- .../server/visitors/shared/utils.js | 149 +++-- .../svelte/src/compiler/phases/types.d.ts | 4 +- .../svelte/src/compiler/types/template.d.ts | 2 + packages/svelte/src/compiler/utils/ast.js | 16 + .../svelte/src/compiler/utils/builders.js | 12 +- packages/svelte/src/index-server.js | 8 +- .../internal/client/dom/blocks/boundary.js | 106 +-- .../src/internal/client/reactivity/async.js | 3 +- packages/svelte/src/internal/client/render.js | 55 +- .../src/internal/server/blocks/snippet.js | 2 +- .../svelte/src/internal/server/context.js | 58 +- packages/svelte/src/internal/server/dev.js | 46 +- packages/svelte/src/internal/server/errors.js | 24 + packages/svelte/src/internal/server/index.js | 232 ++++--- .../svelte/src/internal/server/payload.js | 625 ++++++++++++++++-- .../src/internal/server/payload.test.ts | 364 ++++++++++ .../svelte/src/internal/server/types.d.ts | 23 +- .../svelte/src/internal/server/warnings.js | 17 + packages/svelte/src/internal/shared/errors.js | 16 - packages/svelte/src/legacy/legacy-server.js | 1 + .../samples/binding-input/_expected.html | 2 +- .../_override.html | 2 +- .../dynamic-text-changed/_expected.html | 2 +- .../_expected.html | 2 +- .../_expected.html | 2 +- .../_expected.html | 2 +- .../_expected.html | 2 +- .../element-attribute-added/_expected.html | 2 +- .../element-attribute-changed/_expected.html | 2 +- .../element-attribute-removed/_expected.html | 2 +- .../if-block-mismatch-2/_expected.html | 2 +- .../samples/if-block-mismatch/_expected.html | 2 +- .../_expected.html | 2 +- .../input-value-changed/_expected.html | 2 +- .../hydration/samples/noscript/_expected.html | 2 +- .../pre-first-node-newline/_expected.html | 4 +- .../_expected.html | 2 +- .../repair-mismatched-a-href/_expected.html | 2 +- .../samples/safari-borking/_override.html | 2 +- .../hydration/samples/script/_expected.html | 2 +- .../snippet-raw-hydrate/_expected.html | 2 +- .../standalone-component/_expected.html | 2 +- .../samples/standalone-snippet/_expected.html | 2 +- .../surrounding-whitespace/_expected.html | 2 +- .../surrounding-whitespace/_override.html | 2 +- .../samples/text-empty-2/_expected.html | 2 +- .../samples/text-empty/_expected.html | 2 +- .../whitespace-at-block-start/_override.html | 4 +- .../tests/runtime-browser/driver-ssr.js | 6 +- .../after-render-prevents-loop/_config.js | 2 +- .../after-render-triggers-update/_config.js | 2 +- .../before-render-prevents-loop/_config.js | 2 +- .../_config.js | 18 +- .../_config.js | 6 +- .../samples/binding-select-late-2/_config.js | 6 +- .../samples/binding-select-late-3/_config.js | 6 +- .../samples/binding-select-late/_config.js | 6 +- .../binding-select-unmatched-2/_config.js | 30 +- .../binding-select-unmatched-3/_config.js | 4 +- .../_config.js | 2 +- .../samples/textarea-content/_config.js | 4 +- .../svelte/tests/runtime-legacy/shared.ts | 53 +- .../async-no-pending-throws-sync/_config.js | 7 + .../async-no-pending-throws-sync/main.svelte | 1 + .../samples/async-no-pending/_config.js | 17 + .../samples/async-no-pending/main.svelte | 1 + .../samples/async-ondestroy-ordering/A.svelte | 9 + .../samples/async-ondestroy-ordering/B.svelte | 9 + .../samples/async-ondestroy-ordering/C.svelte | 5 + .../async-ondestroy-ordering/_config.js | 14 + .../async-ondestroy-ordering/destroyed.js | 4 + .../async-ondestroy-ordering/main.svelte | 13 + .../samples/snippet-slot-let-error/_config.js | 1 + .../_config.js | 6 + .../main.svelte | 7 + .../_expected.html | 1 + .../async-each-fallback-hoisting/main.svelte | 5 + .../async-each-hoisting/_expected.html | 1 + .../samples/async-each-hoisting/main.svelte | 9 + .../A.svelte | 9 + .../B.svelte | 7 + .../_config.js | 3 + .../_expected.html | 0 .../_expected_head.html | 1 + .../main.svelte | 27 + .../_expected.html | 1 + .../async-if-alternate-hoisting/main.svelte | 5 + .../samples/async-if-hoisting/_expected.html | 1 + .../samples/async-if-hoisting/main.svelte | 5 + .../_expected.html | 3 + .../main.svelte | 3 + .../_expected.html | 3 + .../main.svelte | 3 + .../Option.svelte | 5 + .../_expected.html | 1 + .../async-select-value-component/main.svelte | 8 + .../_expected.html | 1 + .../main.svelte | 13 + .../_expected.html | 1 + .../main.svelte | 5 + .../samples/comment-preserve/_expected.html | 2 +- .../samples/context/Child.svelte | 8 + .../samples/context/_config.js | 5 + .../samples/context/_expected.html | 3 + .../samples/context/main.svelte | 11 + .../_expected.html | 2 +- .../_expected.html | 2 + .../invalid-nested-svelte-element/_config.js | 4 +- .../tests/server-side-rendering/test.ts | 146 ++-- .../async-each-fallback-hoisting/_config.js | 3 + .../_expected/client/index.svelte.js | 35 + .../_expected/server/index.svelte.js | 25 + .../async-each-fallback-hoisting/index.svelte | 5 + .../samples/async-each-hoisting/_config.js | 3 + .../_expected/client/index.svelte.js | 24 + .../_expected/server/index.svelte.js | 23 + .../samples/async-each-hoisting/index.svelte | 9 + .../async-if-alternate-hoisting/_config.js | 3 + .../_expected/client/index.svelte.js | 30 + .../_expected/server/index.svelte.js | 16 + .../async-if-alternate-hoisting/index.svelte | 5 + .../samples/async-if-hoisting/_config.js | 3 + .../_expected/client/index.svelte.js | 30 + .../_expected/server/index.svelte.js | 16 + .../samples/async-if-hoisting/index.svelte | 5 + .../_expected/server/index.svelte.js | 4 +- .../_expected/server/index.svelte.js | 8 +- .../_expected/server/index.svelte.js | 64 +- .../_expected/server/index.svelte.js | 8 +- .../_expected/server/main.svelte.js | 2 +- .../_expected/server/index.svelte.js | 8 +- .../_expected/server/index.svelte.js | 8 +- .../_expected/server/index.svelte.js | 2 +- .../_expected/server/index.svelte.js | 2 +- .../_expected/server/index.svelte.js | 2 +- .../hmr/_expected/server/index.svelte.js | 2 +- .../_expected/server/index.svelte.js | 2 +- .../_expected/server/index.svelte.js | 21 +- .../purity/_expected/server/index.svelte.js | 4 +- .../_expected/server/index.svelte.js | 2 +- .../_expected/server/index.svelte.js | 2 +- .../_expected/server/index.svelte.js | 2 +- packages/svelte/types/index.d.ts | 4 +- playgrounds/sandbox/package.json | 3 +- 181 files changed, 2638 insertions(+), 901 deletions(-) create mode 100644 .changeset/forty-insects-cheat.md create mode 100644 documentation/docs/98-reference/.generated/server-warnings.md create mode 100644 packages/svelte/messages/server-errors/errors.md delete mode 100644 packages/svelte/messages/server-errors/lifecycle.md create mode 100644 packages/svelte/messages/server-warnings/warnings.md create mode 100644 packages/svelte/scripts/process-messages/templates/server-warnings.js create mode 100644 packages/svelte/src/internal/server/payload.test.ts create mode 100644 packages/svelte/src/internal/server/warnings.js create mode 100644 packages/svelte/tests/runtime-runes/samples/async-no-pending-throws-sync/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/async-no-pending-throws-sync/main.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/async-no-pending/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/async-no-pending/main.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/async-ondestroy-ordering/A.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/async-ondestroy-ordering/B.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/async-ondestroy-ordering/C.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/async-ondestroy-ordering/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/async-ondestroy-ordering/destroyed.js create mode 100644 packages/svelte/tests/runtime-runes/samples/async-ondestroy-ordering/main.svelte create mode 100644 packages/svelte/tests/server-side-rendering/samples/async-context-throws-after-await/_config.js create mode 100644 packages/svelte/tests/server-side-rendering/samples/async-context-throws-after-await/main.svelte create mode 100644 packages/svelte/tests/server-side-rendering/samples/async-each-fallback-hoisting/_expected.html create mode 100644 packages/svelte/tests/server-side-rendering/samples/async-each-fallback-hoisting/main.svelte create mode 100644 packages/svelte/tests/server-side-rendering/samples/async-each-hoisting/_expected.html create mode 100644 packages/svelte/tests/server-side-rendering/samples/async-each-hoisting/main.svelte create mode 100644 packages/svelte/tests/server-side-rendering/samples/async-head-multiple-title-order-preserved/A.svelte create mode 100644 packages/svelte/tests/server-side-rendering/samples/async-head-multiple-title-order-preserved/B.svelte create mode 100644 packages/svelte/tests/server-side-rendering/samples/async-head-multiple-title-order-preserved/_config.js create mode 100644 packages/svelte/tests/server-side-rendering/samples/async-head-multiple-title-order-preserved/_expected.html create mode 100644 packages/svelte/tests/server-side-rendering/samples/async-head-multiple-title-order-preserved/_expected_head.html create mode 100644 packages/svelte/tests/server-side-rendering/samples/async-head-multiple-title-order-preserved/main.svelte create mode 100644 packages/svelte/tests/server-side-rendering/samples/async-if-alternate-hoisting/_expected.html create mode 100644 packages/svelte/tests/server-side-rendering/samples/async-if-alternate-hoisting/main.svelte create mode 100644 packages/svelte/tests/server-side-rendering/samples/async-if-hoisting/_expected.html create mode 100644 packages/svelte/tests/server-side-rendering/samples/async-if-hoisting/main.svelte create mode 100644 packages/svelte/tests/server-side-rendering/samples/async-option-implicit-complex-value/_expected.html create mode 100644 packages/svelte/tests/server-side-rendering/samples/async-option-implicit-complex-value/main.svelte create mode 100644 packages/svelte/tests/server-side-rendering/samples/async-option-implicit-simple-value/_expected.html create mode 100644 packages/svelte/tests/server-side-rendering/samples/async-option-implicit-simple-value/main.svelte create mode 100644 packages/svelte/tests/server-side-rendering/samples/async-select-value-component/Option.svelte create mode 100644 packages/svelte/tests/server-side-rendering/samples/async-select-value-component/_expected.html create mode 100644 packages/svelte/tests/server-side-rendering/samples/async-select-value-component/main.svelte create mode 100644 packages/svelte/tests/server-side-rendering/samples/async-select-value-implicit-value-complex/_expected.html create mode 100644 packages/svelte/tests/server-side-rendering/samples/async-select-value-implicit-value-complex/main.svelte create mode 100644 packages/svelte/tests/server-side-rendering/samples/async-select-value-implicit-value/_expected.html create mode 100644 packages/svelte/tests/server-side-rendering/samples/async-select-value-implicit-value/main.svelte create mode 100644 packages/svelte/tests/server-side-rendering/samples/context/Child.svelte create mode 100644 packages/svelte/tests/server-side-rendering/samples/context/_config.js create mode 100644 packages/svelte/tests/server-side-rendering/samples/context/_expected.html create mode 100644 packages/svelte/tests/server-side-rendering/samples/context/main.svelte create mode 100644 packages/svelte/tests/snapshot/samples/async-each-fallback-hoisting/_config.js create mode 100644 packages/svelte/tests/snapshot/samples/async-each-fallback-hoisting/_expected/client/index.svelte.js create mode 100644 packages/svelte/tests/snapshot/samples/async-each-fallback-hoisting/_expected/server/index.svelte.js create mode 100644 packages/svelte/tests/snapshot/samples/async-each-fallback-hoisting/index.svelte create mode 100644 packages/svelte/tests/snapshot/samples/async-each-hoisting/_config.js create mode 100644 packages/svelte/tests/snapshot/samples/async-each-hoisting/_expected/client/index.svelte.js create mode 100644 packages/svelte/tests/snapshot/samples/async-each-hoisting/_expected/server/index.svelte.js create mode 100644 packages/svelte/tests/snapshot/samples/async-each-hoisting/index.svelte create mode 100644 packages/svelte/tests/snapshot/samples/async-if-alternate-hoisting/_config.js create mode 100644 packages/svelte/tests/snapshot/samples/async-if-alternate-hoisting/_expected/client/index.svelte.js create mode 100644 packages/svelte/tests/snapshot/samples/async-if-alternate-hoisting/_expected/server/index.svelte.js create mode 100644 packages/svelte/tests/snapshot/samples/async-if-alternate-hoisting/index.svelte create mode 100644 packages/svelte/tests/snapshot/samples/async-if-hoisting/_config.js create mode 100644 packages/svelte/tests/snapshot/samples/async-if-hoisting/_expected/client/index.svelte.js create mode 100644 packages/svelte/tests/snapshot/samples/async-if-hoisting/_expected/server/index.svelte.js create mode 100644 packages/svelte/tests/snapshot/samples/async-if-hoisting/index.svelte diff --git a/.changeset/forty-insects-cheat.md b/.changeset/forty-insects-cheat.md new file mode 100644 index 0000000000..993a8fdb24 --- /dev/null +++ b/.changeset/forty-insects-cheat.md @@ -0,0 +1,5 @@ +--- +'svelte': minor +--- + +feat: experimental async SSR diff --git a/.prettierignore b/.prettierignore index 5e1d9b1aa7..9cf9a4bfe1 100644 --- a/.prettierignore +++ b/.prettierignore @@ -15,6 +15,7 @@ packages/svelte/src/internal/client/warnings.js packages/svelte/src/internal/shared/errors.js packages/svelte/src/internal/shared/warnings.js packages/svelte/src/internal/server/errors.js +packages/svelte/src/internal/server/warnings.js packages/svelte/tests/migrate/samples/*/output.svelte packages/svelte/tests/**/*.svelte packages/svelte/tests/**/_expected* diff --git a/documentation/docs/98-reference/.generated/server-errors.md b/documentation/docs/98-reference/.generated/server-errors.md index c3e8b53c31..6263032212 100644 --- a/documentation/docs/98-reference/.generated/server-errors.md +++ b/documentation/docs/98-reference/.generated/server-errors.md @@ -1,5 +1,19 @@ +### await_invalid + +``` +Encountered asynchronous work while rendering synchronously. +``` + +You (or the framework you're using) called [`render(...)`](svelte-server#render) with a component containing an `await` expression. Either `await` the result of `render` or wrap the `await` (or the component containing it) in a [``](svelte-boundary) with a `pending` snippet. + +### html_deprecated + +``` +The `html` property of server render results has been deprecated. Use `body` instead. +``` + ### lifecycle_function_unavailable ``` diff --git a/documentation/docs/98-reference/.generated/server-warnings.md b/documentation/docs/98-reference/.generated/server-warnings.md new file mode 100644 index 0000000000..26b3628be9 --- /dev/null +++ b/documentation/docs/98-reference/.generated/server-warnings.md @@ -0,0 +1,9 @@ + + +### experimental_async_ssr + +``` +Attempted to use asynchronous rendering without `experimental.async` enabled +``` + +Set `experimental.async: true` in your compiler options (usually in `svelte.config.js`) to use async server rendering. This render ran synchronously. diff --git a/documentation/docs/98-reference/.generated/shared-errors.md b/documentation/docs/98-reference/.generated/shared-errors.md index de34b3f5da..6c31aaafd0 100644 --- a/documentation/docs/98-reference/.generated/shared-errors.md +++ b/documentation/docs/98-reference/.generated/shared-errors.md @@ -1,25 +1,5 @@ -### await_outside_boundary - -``` -Cannot await outside a `` with a `pending` snippet -``` - -The `await` keyword can only appear in a `$derived(...)` or template expression, or at the top level of a component's ``); -} - -export function reset_elements() { - let old_parent = parent; - parent = null; - return () => { - parent = old_parent; - }; + payload.child( + (payload) => payload.push(``), + 'head' + ); } /** @@ -58,10 +52,12 @@ export function reset_elements() { * @param {number} column */ export function push_element(payload, tag, line, column) { - var filename = /** @type {Component} */ (current_component).function[FILENAME]; - var child = { tag, parent, filename, line, column }; + var context = /** @type {SSRContext} */ (ssr_context); + var filename = context.function[FILENAME]; + var parent = context.element; + var element = { tag, parent, filename, line, column }; - if (parent !== null) { + if (parent !== undefined) { var ancestor = parent.parent; var ancestors = [parent.tag]; @@ -86,11 +82,11 @@ export function push_element(payload, tag, line, column) { } } - parent = child; + set_ssr_context({ ...context, p: context, element }); } export function pop_element() { - parent = /** @type {Element} */ (parent).parent; + set_ssr_context(/** @type {SSRContext} */ (ssr_context).p); } /** @@ -100,7 +96,7 @@ export function validate_snippet_args(payload) { if ( typeof payload !== 'object' || // for some reason typescript consider the type of payload as never after the first instanceof - !(payload instanceof Payload || /** @type {any} */ (payload) instanceof HeadPayload) + !(payload instanceof Payload) ) { e.invalid_snippet_arguments(); } diff --git a/packages/svelte/src/internal/server/errors.js b/packages/svelte/src/internal/server/errors.js index 458937218f..bde49fe935 100644 --- a/packages/svelte/src/internal/server/errors.js +++ b/packages/svelte/src/internal/server/errors.js @@ -2,6 +2,30 @@ export * from '../shared/errors.js'; +/** + * Encountered asynchronous work while rendering synchronously. + * @returns {never} + */ +export function await_invalid() { + const error = new Error(`await_invalid\nEncountered asynchronous work while rendering synchronously.\nhttps://svelte.dev/e/await_invalid`); + + error.name = 'Svelte error'; + + throw error; +} + +/** + * The `html` property of server render results has been deprecated. Use `body` instead. + * @returns {never} + */ +export function html_deprecated() { + const error = new Error(`html_deprecated\nThe \`html\` property of server render results has been deprecated. Use \`body\` instead.\nhttps://svelte.dev/e/html_deprecated`); + + error.name = 'Svelte error'; + + throw error; +} + /** * `%name%(...)` is not available on the server * @param {string} name diff --git a/packages/svelte/src/internal/server/index.js b/packages/svelte/src/internal/server/index.js index 3aa44f2daa..a2cf222da6 100644 --- a/packages/svelte/src/internal/server/index.js +++ b/packages/svelte/src/internal/server/index.js @@ -1,6 +1,7 @@ -/** @import { ComponentType, SvelteComponent } from 'svelte' */ -/** @import { Component, RenderOutput } from '#server' */ +/** @import { ComponentType, SvelteComponent, Component } from 'svelte' */ +/** @import { RenderOutput, SSRContext } from '#server' */ /** @import { Store } from '#shared' */ +/** @import { AccumulatedContent } from './payload.js' */ export { FILENAME, HMR } from '../../constants.js'; import { attr, clsx, to_class, to_style } from '../shared/attributes.js'; import { is_promise, noop } from '../shared/utils.js'; @@ -13,13 +14,14 @@ import { } from '../../constants.js'; import { escape_html } from '../../escaping.js'; import { DEV } from 'esm-env'; -import { current_component, pop, push } from './context.js'; +import { ssr_context, pop, push, set_ssr_context } from './context.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'; -import { Payload } from './payload.js'; +import { Payload, SSRState } from './payload.js'; import { abort } from './abort-signal.js'; +import { async_mode_flag } from '../flags/index.js'; +import * as e from './errors.js'; // https://html.spec.whatwg.org/multipage/syntax.html#attributes-2 // https://infra.spec.whatwg.org/#noncharacter @@ -34,102 +36,48 @@ const INVALID_ATTR_NAME_CHAR_REGEX = * @returns {void} */ export function element(payload, tag, attributes_fn = noop, children_fn = noop) { - payload.out.push(''); + payload.push(''); if (tag) { - payload.out.push(`<${tag}`); + payload.push(`<${tag}`); attributes_fn(); - payload.out.push(`>`); + payload.push(`>`); if (!is_void(tag)) { children_fn(); if (!is_raw_text_element(tag)) { - payload.out.push(EMPTY_COMMENT); + payload.push(EMPTY_COMMENT); } - payload.out.push(``); + payload.push(``); } } - payload.out.push(''); + payload.push(''); } -/** - * Array of `onDestroy` callbacks that should be called at the end of the server render function - * @type {Function[]} - */ -export let on_destroy = []; - /** * 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. * @template {Record} Props - * @param {import('svelte').Component | ComponentType>} component + * @param {Component | ComponentType>} component * @param {{ props?: Omit; context?: Map; idPrefix?: string }} [options] * @returns {RenderOutput} */ export function render(component, options = {}) { - try { - const payload = new Payload(options.idPrefix ? options.idPrefix + '-' : ''); - - const prev_on_destroy = on_destroy; - on_destroy = []; - payload.out.push(BLOCK_OPEN); - - let reset_reset_element; - - if (DEV) { - // prevent parent/child element state being corrupted by a bad render - reset_reset_element = reset_elements(); - } - - if (options.context) { - push(); - /** @type {Component} */ (current_component).c = options.context; - } - - // @ts-expect-error - component(payload, options.props ?? {}, {}, {}); - - if (options.context) { - pop(); - } - - if (reset_reset_element) { - reset_reset_element(); - } - - payload.out.push(BLOCK_CLOSE); - for (const cleanup of on_destroy) cleanup(); - on_destroy = prev_on_destroy; - - let head = payload.head.out.join('') + payload.head.title; - - for (const { hash, code } of payload.css) { - head += ``; - } - - const body = payload.out.join(''); - - return { - head, - html: body, - body: body - }; - } finally { - abort(); - } + return Payload.render(/** @type {Component} */ (component), options); } /** * @param {Payload} payload - * @param {(head_payload: Payload['head']) => void} fn + * @param {(payload: Payload) => Promise | void} fn * @returns {void} */ export function head(payload, fn) { - const head_payload = payload.head; - head_payload.out.push(BLOCK_OPEN); - fn(head_payload); - head_payload.out.push(BLOCK_CLOSE); + payload.child((payload) => { + payload.push(BLOCK_OPEN); + payload.child(fn); + payload.push(BLOCK_CLOSE); + }, 'head'); } /** @@ -144,21 +92,21 @@ export function css_props(payload, is_html, props, component, dynamic = false) { const styles = style_object_to_string(props); if (is_html) { - payload.out.push(``); + payload.push(``); } else { - payload.out.push(``); + payload.push(``); } if (dynamic) { - payload.out.push(''); + payload.push(''); } component(); if (is_html) { - payload.out.push(``); + payload.push(``); } else { - payload.out.push(``); + payload.push(``); } } @@ -451,13 +399,13 @@ export function bind_props(props_parent, props_now) { */ function await_block(payload, promise, pending_fn, then_fn) { if (is_promise(promise)) { - payload.out.push(BLOCK_OPEN); + payload.push(BLOCK_OPEN); promise.then(null, noop); if (pending_fn !== null) { pending_fn(); } } else if (then_fn !== null) { - payload.out.push(BLOCK_OPEN_ELSE); + payload.push(BLOCK_OPEN_ELSE); then_fn(promise); } } @@ -503,8 +451,8 @@ export function once(get_value) { * @returns {string} */ export function props_id(payload) { - const uid = payload.uid(); - payload.out.push(''); + const uid = payload.global.uid(); + payload.push(''); return uid; } @@ -512,12 +460,10 @@ export { attr, clsx }; export { html } from './blocks/html.js'; -export { push, pop } from './context.js'; +export { save } from './context.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'; export { fallback, to_array } from '../shared/utils.js'; @@ -531,8 +477,6 @@ export { export { escape_html as escape }; -export { await_outside_boundary } from '../shared/errors.js'; - /** * @template T * @param {()=>T} fn @@ -557,29 +501,117 @@ export function derived(fn) { /** * * @param {Payload} payload - * @param {*} value + * @param {unknown} value */ export function maybe_selected(payload, value) { - return value === payload.select_value ? ' selected' : ''; + return value === payload.local.select_value ? ' selected' : ''; } /** + * When an `option` element has no `value` attribute, we need to treat the child + * content as its `value` to determine whether we should apply the `selected` attribute. + * This has to be done at runtime, for hopefully obvious reasons. It is also complicated, + * for sad reasons. * @param {Payload} payload - * @param {() => void} children + * @param {((payload: Payload) => void | Promise)} children * @returns {void} */ export function valueless_option(payload, children) { - var i = payload.out.length; + const i = payload.length; + + // prior to children, `payload` has some combination of string/unresolved payload that ends in `