From 8f4dc6b86b450d86b59b02311c2f4c5a4284779b Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Fri, 23 Feb 2024 23:57:27 -0500 Subject: [PATCH] tidy up --- .../src/internal/client/dom/blocks/each.js | 6 +- packages/svelte/src/internal/client/proxy.js | 7 +- .../svelte/src/internal/client/runtime.js | 162 ++++++++---------- 3 files changed, 78 insertions(+), 97 deletions(-) diff --git a/packages/svelte/src/internal/client/dom/blocks/each.js b/packages/svelte/src/internal/client/dom/blocks/each.js index 744cc546ac..9b80aa2cea 100644 --- a/packages/svelte/src/internal/client/dom/blocks/each.js +++ b/packages/svelte/src/internal/client/dom/blocks/each.js @@ -15,7 +15,7 @@ import { } from '../../hydration.js'; import { empty, map_get, map_set } from '../../operations.js'; import { insert, remove } from '../../reconciler.js'; -import { set_signal_value } from '../../runtime.js'; +import { set } from '../../runtime.js'; import { destroy_effect, pause_effect, @@ -551,7 +551,7 @@ export function get_first_element(block) { function update_each_item_block(block, item, index, type) { const block_v = block.v; if ((type & EACH_ITEM_REACTIVE) !== 0) { - set_signal_value(block_v, item); + set(block_v, item); } // const transitions = block.s; const transitions = null; @@ -563,7 +563,7 @@ function update_each_item_block(block, item, index, type) { // each_animation(block, transitions); } if (index_is_reactive) { - set_signal_value(/** @type {import('#client').Source} */ (block.i), index); + set(/** @type {import('#client').Source} */ (block.i), index); } else { block.i = index; } diff --git a/packages/svelte/src/internal/client/proxy.js b/packages/svelte/src/internal/client/proxy.js index 504a1b7c76..cbe981ef66 100644 --- a/packages/svelte/src/internal/client/proxy.js +++ b/packages/svelte/src/internal/client/proxy.js @@ -5,8 +5,7 @@ import { updating_derived, batch_inspect, current_component_context, - untrack, - set_signal_value + untrack } from './runtime.js'; import { effect_active } from './reactivity/effects.js'; import { @@ -150,12 +149,12 @@ export function unstate(value) { } /** - * @param {import('./types.js').ValueSignal} signal + * @param {import('./types.js').Source} signal * @param {1 | -1} [d] */ function update_version(signal, d = 1) { const value = untrack(() => get(signal)); - set_signal_value(signal, value + d); + set(signal, value + d); } /** @type {ProxyHandler>} */ diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index 1f88eb00b8..70ec202bdd 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -225,15 +225,7 @@ function execute_signal_fn(signal) { current_untracking = false; try { - let res; - if (is_render_effect) { - res = /** @type {(block: null, signal: import('#client').Signal) => V} */ (init)( - null, - /** @type {import('#client').Signal} */ (signal) - ); - } else { - res = /** @type {() => V} */ (init)(); - } + const res = /** @type {() => V} */ (init)(); let dependencies = /** @type {import('#client').ValueSignal[]} **/ (signal.d); if (current_dependencies !== null) { let i; @@ -269,7 +261,7 @@ function execute_signal_fn(signal) { dependencies[current_dependencies_index + i] = current_dependencies[i]; } } else { - signal.d = /** @type {import('#client').Signal[]} **/ ( + signal.d = /** @type {import('#client').ValueSignal[]} **/ ( dependencies = current_dependencies ); } @@ -747,18 +739,81 @@ export function get(signal) { /** * @template V - * @param {import('#client').Signal} signal + * @param {import('#client').Source} signal * @param {V} value * @returns {V} */ export function set(signal, value) { - set_signal_value(signal, value); + if ( + !current_untracking && + !ignore_mutation_validation && + current_consumer !== null && + is_runes(null) && + (current_consumer.f & DERIVED) !== 0 + ) { + throw new Error( + 'ERR_SVELTE_UNSAFE_MUTATION' + + (DEV + ? ": Unsafe mutations during Svelte's render or derived phase are not permitted in runes mode. " + + 'This can lead to unexpected errors and possibly cause infinite loops.\n\nIf this mutation is not meant ' + + 'to be reactive do not use the "$state" rune for that declaration.' + : '') + ); + } + if ( + (signal.f & SOURCE) !== 0 && + !(/** @type {import('#client').EqualsFunctions} */ (signal.e)(value, signal.v)) + ) { + signal.v = value; + // Increment write version so that unowned signals can properly track dirtyness + signal.w++; + // If the current signal is running for the first time, it won't have any + // consumers as we only allocate and assign the consumers after the signal + // has fully executed. So in the case of ensuring it registers the consumer + // properly for itself, we need to ensure the current effect actually gets + // scheduled. i.e: + // + // $effect(() => x++) + // + // We additionally want to skip this logic for when ignore_mutation_validation is + // true, as stores write to source signal on initialization. + if ( + is_runes(null) && + !ignore_mutation_validation && + current_effect !== null && + current_effect.c === null && + (current_effect.f & CLEAN) !== 0 && + (current_effect.f & MANAGED) === 0 + ) { + if (current_dependencies !== null && current_dependencies.includes(signal)) { + set_signal_status(current_effect, DIRTY); + schedule_effect(current_effect, false); + } else { + if (current_untracked_writes === null) { + current_untracked_writes = [signal]; + } else { + current_untracked_writes.push(signal); + } + } + } + mark_signal_consumers(signal, DIRTY, true); + + // @ts-expect-error + if (DEV && signal.inspect) { + if (is_batching_effect) { + last_inspected_signal = /** @type {import('#client').SourceDebug} */ (signal); + } else { + for (const fn of /** @type {import('#client').SourceDebug} */ (signal).inspect) fn(); + } + } + } + return value; } /** * @template V - * @param {import('#client').Signal} signal + * @param {import('#client').Source} signal * @param {V} value * @returns {void} */ @@ -796,11 +851,11 @@ export function invalidate_inner_signals(fn) { /** * @template V - * @param {import('#client').Signal} source + * @param {import('#client').Source} source * @param {V} value */ export function mutate(source, value) { - set_signal_value( + set( source, untrack(() => get(source)) ); @@ -847,79 +902,6 @@ function mark_signal_consumers(signal, to_status, force_schedule) { } /** - * @template V - * @param {import('#client').Signal} signal - * @param {V} value - * @returns {void} - */ -export function set_signal_value(signal, value) { - if ( - !current_untracking && - !ignore_mutation_validation && - current_consumer !== null && - is_runes(null) && - (current_consumer.f & DERIVED) !== 0 - ) { - throw new Error( - 'ERR_SVELTE_UNSAFE_MUTATION' + - (DEV - ? ": Unsafe mutations during Svelte's render or derived phase are not permitted in runes mode. " + - 'This can lead to unexpected errors and possibly cause infinite loops.\n\nIf this mutation is not meant ' + - 'to be reactive do not use the "$state" rune for that declaration.' - : '') - ); - } - if ( - (signal.f & SOURCE) !== 0 && - !(/** @type {import('#client').EqualsFunctions} */ (signal.e)(value, signal.v)) - ) { - signal.v = value; - // Increment write version so that unowned signals can properly track dirtyness - signal.w++; - // If the current signal is running for the first time, it won't have any - // consumers as we only allocate and assign the consumers after the signal - // has fully executed. So in the case of ensuring it registers the consumer - // properly for itself, we need to ensure the current effect actually gets - // scheduled. i.e: - // - // $effect(() => x++) - // - // We additionally want to skip this logic for when ignore_mutation_validation is - // true, as stores write to source signal on initialization. - if ( - is_runes(null) && - !ignore_mutation_validation && - current_effect !== null && - current_effect.c === null && - (current_effect.f & CLEAN) !== 0 && - (current_effect.f & MANAGED) === 0 - ) { - if (current_dependencies !== null && current_dependencies.includes(signal)) { - set_signal_status(current_effect, DIRTY); - schedule_effect(current_effect, false); - } else { - if (current_untracked_writes === null) { - current_untracked_writes = [signal]; - } else { - current_untracked_writes.push(signal); - } - } - } - mark_signal_consumers(signal, DIRTY, true); - - // @ts-expect-error - if (DEV && signal.inspect) { - if (is_batching_effect) { - last_inspected_signal = /** @type {import('#client').SourceDebug} */ (signal); - } else { - for (const fn of /** @type {import('#client').SourceDebug} */ (signal).inspect) fn(); - } - } - } -} - -/** - * @template V * @param {import('#client').Reaction} signal * @returns {void} */ @@ -1021,7 +1003,7 @@ function get_parent_context(component_context) { */ export function update(signal, d = 1) { const value = get(signal); - set_signal_value(signal, value + d); + set(signal, value + d); return value; } @@ -1043,7 +1025,7 @@ export function update_prop(fn, d = 1) { */ export function update_pre(signal, d = 1) { const value = get(signal) + d; - set_signal_value(signal, value); + set(signal, value); return value; }