diff --git a/.changeset/cuddly-walls-pretend.md b/.changeset/cuddly-walls-pretend.md deleted file mode 100644 index f51147a30c..0000000000 --- a/.changeset/cuddly-walls-pretend.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: omit unnecessary nullish coallescing in template expressions diff --git a/.changeset/cyan-games-cheat.md b/.changeset/cyan-games-cheat.md deleted file mode 100644 index d90901783b..0000000000 --- a/.changeset/cyan-games-cheat.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: more efficient template effect grouping diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 59ac9f2864..d16bdbc327 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,17 @@ # svelte +## 5.19.1 + +### Patch Changes + +- fix: omit unnecessary nullish coallescing in template expressions ([#15056](https://github.com/sveltejs/svelte/pull/15056)) + +- fix: more efficient template effect grouping ([#15050](https://github.com/sveltejs/svelte/pull/15050)) + +- fix: ensure untrack correctly retains the active reaction ([#15065](https://github.com/sveltejs/svelte/pull/15065)) + +- fix: initialize `files` bind on hydration ([#15059](https://github.com/sveltejs/svelte/pull/15059)) + ## 5.19.0 ### Minor Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 95e7a7319d..b725e52fed 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.19.0", + "version": "5.19.1", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/internal/client/dom/elements/bindings/input.js b/packages/svelte/src/internal/client/dom/elements/bindings/input.js index b8d4b07c9b..166dcbc738 100644 --- a/packages/svelte/src/internal/client/dom/elements/bindings/input.js +++ b/packages/svelte/src/internal/client/dom/elements/bindings/input.js @@ -259,6 +259,15 @@ export function bind_files(input, get, set = get) { set(input.files); }); + if ( + // If we are hydrating and the value has since changed, + // then use the updated value from the input instead. + hydrating && + input.files + ) { + set(input.files); + } + render_effect(() => { input.files = get(); }); diff --git a/packages/svelte/src/internal/client/reactivity/effects.js b/packages/svelte/src/internal/client/reactivity/effects.js index f84a28acc4..1cd390d17a 100644 --- a/packages/svelte/src/internal/client/reactivity/effects.js +++ b/packages/svelte/src/internal/client/reactivity/effects.js @@ -16,7 +16,8 @@ import { set_is_flushing_effect, set_signal_status, untrack, - skip_reaction + skip_reaction, + untracking } from '../runtime.js'; import { DIRTY, @@ -43,8 +44,7 @@ import * as e from '../errors.js'; import { DEV } from 'esm-env'; import { define_property } from '../../shared/utils.js'; import { get_next_sibling } from '../dom/operations.js'; -import { derived, derived_safe_equal, destroy_derived } from './deriveds.js'; -import { legacy_mode_flag } from '../../flags/index.js'; +import { derived, destroy_derived } from './deriveds.js'; /** * @param {'$effect' | '$effect.pre' | '$inspect'} rune @@ -166,7 +166,7 @@ function create_effect(type, fn, sync, push = true) { * @returns {boolean} */ export function effect_tracking() { - if (active_reaction === null) { + if (active_reaction === null || untracking) { return false; } diff --git a/packages/svelte/src/internal/client/reactivity/sources.js b/packages/svelte/src/internal/client/reactivity/sources.js index 4500a7c5a8..d10008dae2 100644 --- a/packages/svelte/src/internal/client/reactivity/sources.js +++ b/packages/svelte/src/internal/client/reactivity/sources.js @@ -17,7 +17,8 @@ import { set_derived_sources, check_dirtiness, set_is_flushing_effect, - is_flushing_effect + is_flushing_effect, + untracking } from '../runtime.js'; import { equals, safe_equals } from './equality.js'; import { @@ -148,6 +149,7 @@ export function mutate(source, value) { export function set(source, value) { if ( active_reaction !== null && + !untracking && is_runes() && (active_reaction.f & (DERIVED | BLOCK_EFFECT)) !== 0 && // If the source was created locally within the current derived, then diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index 183ed47eb9..75942c9b4c 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -83,6 +83,8 @@ let dev_effect_stack = []; /** @type {null | Reaction} */ export let active_reaction = null; +export let untracking = false; + /** @param {null | Reaction} reaction */ export function set_active_reaction(reaction) { active_reaction = reaction; @@ -428,6 +430,7 @@ export function update_reaction(reaction) { var previous_skip_reaction = skip_reaction; var prev_derived_sources = derived_sources; var previous_component_context = component_context; + var previous_untracking = untracking; var flags = reaction.f; new_deps = /** @type {null | Value[]} */ (null); @@ -437,6 +440,7 @@ export function update_reaction(reaction) { skip_reaction = !is_flushing_effect && (flags & UNOWNED) !== 0; derived_sources = null; component_context = reaction.ctx; + untracking = false; read_version++; try { @@ -502,6 +506,7 @@ export function update_reaction(reaction) { skip_reaction = previous_skip_reaction; derived_sources = prev_derived_sources; component_context = previous_component_context; + untracking = previous_untracking; } } @@ -944,7 +949,7 @@ export function get(signal) { } // Register the dependency on the current reaction signal. - if (active_reaction !== null) { + if (active_reaction !== null && !untracking) { if (derived_sources !== null && derived_sources.includes(signal)) { e.state_unsafe_local_read(); } @@ -1117,12 +1122,12 @@ export function invalidate_inner_signals(fn) { * @returns {T} */ export function untrack(fn) { - const previous_reaction = active_reaction; + var previous_untracking = untracking; try { - active_reaction = null; + untracking = true; return fn(); } finally { - active_reaction = previous_reaction; + untracking = previous_untracking; } } diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index 7347e7546d..b0ea99b6f4 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.19.0'; +export const VERSION = '5.19.1'; export const PUBLIC_VERSION = '5'; diff --git a/packages/svelte/tests/signals/test.ts b/packages/svelte/tests/signals/test.ts index a9d29920cf..e147fd1d0d 100644 --- a/packages/svelte/tests/signals/test.ts +++ b/packages/svelte/tests/signals/test.ts @@ -803,6 +803,46 @@ describe('signals', () => { }; }); + test('deriveds containing effects work correctly when used with untrack', () => { + return () => { + let a = render_effect(() => {}); + let b = state(0); + let c; + let effects = []; + + const destroy = effect_root(() => { + a = render_effect(() => { + c = derived(() => { + $.untrack(() => { + effects.push( + effect(() => { + $.get(b); + }) + ); + }); + $.get(b); + }); + $.get(c); + }); + }); + + assert.deepEqual(c!.children?.length, 1); + assert.deepEqual(a.first, a.last); + + set(b, 1); + + flushSync(); + + assert.deepEqual(c!.children?.length, 1); + assert.deepEqual(a.first, a.last); + + destroy(); + + assert.deepEqual(a.deriveds, null); + assert.deepEqual(a.first, null); + }; + }); + test('bigint states update correctly', () => { return () => { const count = state(0n);