diff --git a/packages/svelte/src/internal/client/reactivity/deriveds.js b/packages/svelte/src/internal/client/reactivity/deriveds.js index fe29bc40594e..ef59b4b94041 100644 --- a/packages/svelte/src/internal/client/reactivity/deriveds.js +++ b/packages/svelte/src/internal/client/reactivity/deriveds.js @@ -11,25 +11,27 @@ import { default_equals, safe_equal } from './equality.js'; */ /*#__NO_SIDE_EFFECTS__*/ export function derived(fn) { - const is_unowned = current_effect === null; - const flags = is_unowned ? DERIVED | UNOWNED : DERIVED; - const signal = /** @type {import('../types.js').Derived} */ ({ + let flags = DERIVED | CLEAN; + if (current_effect === null) flags |= UNOWNED; + + /** @type {import('#client').Derived} */ + const signal = { b: current_block, c: null, d: null, e: default_equals, - f: flags | CLEAN, + f: flags, i: fn, r: null, + // @ts-expect-error v: UNINITIALIZED, w: 0, x: null, y: null - }); + }; if (DEV) { - // @ts-expect-error - signal.inspect = new Set(); + /** @type {import('#client').DerivedDebug} */ (signal).inspect = new Set(); } if (current_consumer !== null) { @@ -42,7 +44,7 @@ export function derived(fn) { /** * @template V * @param {() => V} fn - * @returns {import('../types.js').Derived} + * @returns {import('#client').Derived} */ /*#__NO_SIDE_EFFECTS__*/ export function derived_safe_equal(fn) { diff --git a/packages/svelte/src/internal/client/reactivity/effects.js b/packages/svelte/src/internal/client/reactivity/effects.js index 2570f60bec09..8ca43f841450 100644 --- a/packages/svelte/src/internal/client/reactivity/effects.js +++ b/packages/svelte/src/internal/client/reactivity/effects.js @@ -10,8 +10,8 @@ import { import { DIRTY, MANAGED, RENDER_EFFECT, EFFECT, PRE_EFFECT } from '../constants.js'; /** - * @param {import('../types.js').Reaction} target_signal - * @param {import('../types.js').Reaction} ref_signal + * @param {import('#client').Reaction} target_signal + * @param {import('#client').Reaction} ref_signal * @returns {void} */ export function push_reference(target_signal, ref_signal) { @@ -24,14 +24,14 @@ export function push_reference(target_signal, ref_signal) { } /** - * @param {import('../types.js').EffectType} type - * @param {(() => void | (() => void)) | ((b: import('../types.js').Block) => void | (() => void))} fn + * @param {import('./types.js').EffectType} type + * @param {(() => void | (() => void)) | ((b: import('#client').Block) => void | (() => void))} fn * @param {boolean} sync - * @param {null | import('../types.js').Block} block + * @param {null | import('#client').Block} block * @param {boolean} schedule - * @returns {import('../types.js').Effect} + * @returns {import('#client').Effect} */ -function internal_create_effect(type, fn, sync, block, schedule) { +function create_effect(type, fn, sync, block, schedule) { /** @type {import('#client').Effect} */ const signal = { b: block, @@ -54,13 +54,16 @@ function internal_create_effect(type, fn, sync, block, schedule) { push_reference(current_effect, signal); } } + if (schedule) { schedule_effect(signal, sync); } + return signal; } /** + * Internal representation of `$effect.active()` * @returns {boolean} */ export function effect_active() { @@ -70,7 +73,7 @@ export function effect_active() { /** * Internal representation of `$effect(...)` * @param {() => void | (() => void)} fn - * @returns {import('../types.js').Effect} + * @returns {import('#client').Effect} */ export function user_effect(fn) { if (current_effect === null) { @@ -85,7 +88,7 @@ export function user_effect(fn) { current_component_context !== null && !current_component_context.m; - const effect = internal_create_effect( + const effect = create_effect( EFFECT, fn, false, @@ -94,9 +97,7 @@ export function user_effect(fn) { ); if (apply_component_effect_heuristics) { - const context = /** @type {import('../types.js').ComponentContext} */ ( - current_component_context - ); + const context = /** @type {import('#client').ComponentContext} */ (current_component_context); (context.e ??= []).push(effect); } @@ -117,33 +118,33 @@ export function user_root_effect(fn) { /** * @param {() => void | (() => void)} fn - * @returns {import('../types.js').Effect} + * @returns {import('#client').Effect} */ export function effect(fn) { - return internal_create_effect(EFFECT, fn, false, current_block, true); + return create_effect(EFFECT, fn, false, current_block, true); } /** * @param {() => void | (() => void)} fn - * @returns {import('../types.js').Effect} + * @returns {import('#client').Effect} */ export function managed_effect(fn) { - return internal_create_effect(EFFECT | MANAGED, fn, false, current_block, true); + return create_effect(EFFECT | MANAGED, fn, false, current_block, true); } /** * @param {() => void | (() => void)} fn * @param {boolean} sync - * @returns {import('../types.js').Effect} + * @returns {import('#client').Effect} */ export function managed_pre_effect(fn, sync) { - return internal_create_effect(PRE_EFFECT | MANAGED, fn, sync, current_block, true); + return create_effect(PRE_EFFECT | MANAGED, fn, sync, current_block, true); } /** * Internal representation of `$effect.pre(...)` * @param {() => void | (() => void)} fn - * @returns {import('../types.js').Effect} + * @returns {import('#client').Effect} */ export function pre_effect(fn) { if (current_effect === null) { @@ -155,7 +156,7 @@ export function pre_effect(fn) { ); } const sync = current_effect !== null && (current_effect.f & RENDER_EFFECT) !== 0; - return internal_create_effect( + return create_effect( PRE_EFFECT, () => { const val = fn(); @@ -173,24 +174,24 @@ export function pre_effect(fn) { * bindings which are in later effects. However, we don't use a pre_effect directly as we don't want to flush anything. * * @param {() => void | (() => void)} fn - * @returns {import('../types.js').Effect} + * @returns {import('#client').Effect} */ export function invalidate_effect(fn) { - return internal_create_effect(PRE_EFFECT, fn, true, current_block, true); + return create_effect(PRE_EFFECT, fn, true, current_block, true); } /** - * @template {import('../types.js').Block} B + * @template {import('#client').Block} B * @param {(block: B) => void | (() => void)} fn * @param {any} block * @param {any} managed * @param {any} sync - * @returns {import('../types.js').Effect} + * @returns {import('#client').Effect} */ export function render_effect(fn, block = current_block, managed = false, sync = true) { let flags = RENDER_EFFECT; if (managed) { flags |= MANAGED; } - return internal_create_effect(flags, /** @type {any} */ (fn), sync, block, true); + return create_effect(flags, /** @type {any} */ (fn), sync, block, true); } diff --git a/packages/svelte/src/internal/client/reactivity/sources.js b/packages/svelte/src/internal/client/reactivity/sources.js index 73174f208f3e..60403ddc3d47 100644 --- a/packages/svelte/src/internal/client/reactivity/sources.js +++ b/packages/svelte/src/internal/client/reactivity/sources.js @@ -23,31 +23,31 @@ import { CLEAN, DERIVED, DIRTY, MANAGED, SOURCE } from '../constants.js'; /** * @template V - * @param {V} initial_value - * @returns {import('../types.js').Source} + * @param {V} value + * @returns {import('#client').Source} */ /*#__NO_SIDE_EFFECTS__*/ -export function source(initial_value) { +export function source(value) { /** @type {import('#client').Source} */ - const signal = { + const source = { c: null, e: default_equals, f: SOURCE | CLEAN, - v: initial_value, + v: value, w: 0 }; if (DEV) { - /** @type {import('#client').SourceDebug} */ (signal).inspect = new Set(); + /** @type {import('#client').SourceDebug} */ (source).inspect = new Set(); } - return signal; + return source; } /** * @template V * @param {V} initial_value - * @returns {import('../types.js').Source} + * @returns {import('#client').Source} */ /*#__NO_SIDE_EFFECTS__*/ export function mutable_source(initial_value) { @@ -65,7 +65,7 @@ export function mutable_source(initial_value) { /** * @template V - * @param {import('./types.js').Source} signal + * @param {import('#client').Source} signal * @param {V} value * @returns {void} */ @@ -75,7 +75,7 @@ export function set_sync(signal, value) { /** * @template V - * @param {import('./types.js').Value} source + * @param {import('#client').Value} source * @param {V} value */ export function mutate(source, value) { @@ -88,7 +88,7 @@ export function mutate(source, value) { /** * @template V - * @param {import('./types.js').Source} signal + * @param {import('#client').Source} signal * @param {V} value * @returns {V} */ @@ -150,9 +150,9 @@ export function set(signal, value) { // @ts-expect-error if (DEV && signal.inspect) { if (is_batching_effect) { - set_last_inspected_signal(/** @type {import('./types.js').ValueDebug} */ (signal)); + set_last_inspected_signal(/** @type {import('#client').ValueDebug} */ (signal)); } else { - for (const fn of /** @type {import('./types.js').ValueDebug} */ (signal).inspect) fn(); + for (const fn of /** @type {import('#client').ValueDebug} */ (signal).inspect) fn(); } } } diff --git a/packages/svelte/src/internal/client/reactivity/store.js b/packages/svelte/src/internal/client/reactivity/store.js index 3cf286713910..fe2ef12971c4 100644 --- a/packages/svelte/src/internal/client/reactivity/store.js +++ b/packages/svelte/src/internal/client/reactivity/store.js @@ -10,13 +10,13 @@ import { mutable_source, set } from './sources.js'; * signal that will be updated when the store is. The store references container is needed to * track reassignments to stores and to track the correct component context. * @template V - * @param {import('../types.js').Store | null | undefined} store + * @param {import('#client').Store | null | undefined} store * @param {string} store_name - * @param {import('../types.js').StoreReferencesContainer} stores + * @param {import('#client').StoreReferencesContainer} stores * @returns {V} */ export function store_get(store, store_name, stores) { - /** @type {import('../types.js').StoreReferencesContainer[''] | undefined} */ + /** @type {import('#client').StoreReferencesContainer[''] | undefined} */ let entry = stores[store_name]; const is_new = entry === undefined; @@ -29,8 +29,8 @@ export function store_get(store, store_name, stores) { }; // TODO: can we remove this code? it was refactored out when we split up source/comptued signals // push_destroy_fn(entry.value, () => { - // /** @type {import('../types.js').StoreReferencesContainer['']} */ (entry).last_value = - // /** @type {import('../types.js').StoreReferencesContainer['']} */ (entry).value.value; + // /** @type {import('#client').StoreReferencesContainer['']} */ (entry).last_value = + // /** @type {import('#client').StoreReferencesContainer['']} */ (entry).value.value; // }); stores[store_name] = entry; } @@ -49,8 +49,8 @@ export function store_get(store, store_name, stores) { /** * @template V - * @param {import('../types.js').Store | null | undefined} store - * @param {import('../types.js').Source} source + * @param {import('#client').Store | null | undefined} store + * @param {import('#client').Source} source */ function connect_store_to_signal(store, source) { if (store == null) { @@ -70,7 +70,7 @@ function connect_store_to_signal(store, source) { /** * Sets the new value of a store and returns that value. * @template V - * @param {import('../types.js').Store} store + * @param {import('#client').Store} store * @param {V} value * @returns {V} */ @@ -81,7 +81,7 @@ export function store_set(store, value) { /** * Unsubscribes from all auto-subscribed stores on destroy - * @param {import('../types.js').StoreReferencesContainer} stores + * @param {import('#client').StoreReferencesContainer} stores */ export function unsubscribe_on_destroy(stores) { on_destroy(() => { @@ -97,7 +97,7 @@ export function unsubscribe_on_destroy(stores) { /** * Updates a store with a new value. - * @param {import('../types.js').Store} store the store to update + * @param {import('#client').Store} store the store to update * @param {any} expression the expression that mutates the store * @param {V} new_value the new store value * @template V @@ -110,18 +110,18 @@ export function mutate_store(store, expression, new_value) { /** * @template V * @param {unknown} val - * @returns {val is import('../types.js').Store} + * @returns {val is import('#client').Store} */ export function is_store(val) { return ( typeof val === 'object' && val !== null && - typeof (/** @type {import('../types.js').Store} */ (val).subscribe) === 'function' + typeof (/** @type {import('#client').Store} */ (val).subscribe) === 'function' ); } /** - * @param {import('../types.js').Store} store + * @param {import('#client').Store} store * @param {number} store_value * @param {1 | -1} [d] * @returns {number} @@ -132,7 +132,7 @@ export function update_store(store, store_value, d = 1) { } /** - * @param {import('../types.js').Store} store + * @param {import('#client').Store} store * @param {number} store_value * @param {1 | -1} [d] * @returns {number} diff --git a/packages/svelte/src/internal/client/reactivity/types.d.ts b/packages/svelte/src/internal/client/reactivity/types.d.ts index 0dc0b2d5a470..a390d83b03e3 100644 --- a/packages/svelte/src/internal/client/reactivity/types.d.ts +++ b/packages/svelte/src/internal/client/reactivity/types.d.ts @@ -78,8 +78,6 @@ export type Effect = { export type Reaction = Derived | Effect; -export type Signal = Source | Reaction; - export type MaybeSignal = T | Source; export type UnwrappedSignal = T extends Value ? U : T; @@ -87,3 +85,5 @@ export type UnwrappedSignal = T extends Value ? U : T; export type Value = Source | Derived; export type ValueDebug = SourceDebug | DerivedDebug; + +export type Signal = Source | Derived | Effect; diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index 10cd320165db..abe296907ceb 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -865,8 +865,7 @@ export function mark_subtree_inert(signal, inert, visited_blocks = new Set()) { } /** - * @template V - * @param {import('./types.js').Signal} signal + * @param {import('./types.js').Signal} signal * @param {number} to_status * @param {boolean} force_schedule * @returns {void} @@ -962,9 +961,9 @@ export function push_destroy_fn(signal, destroy_fn) { } const STATUS_MASK = ~(DIRTY | MAYBE_DIRTY | CLEAN); + /** - * @template V - * @param {import('./types.js').Signal} signal + * @param {import('./types.js').Signal} signal * @param {number} status * @returns {void} */ @@ -974,14 +973,14 @@ export function set_signal_status(signal, status) { /** * @template V - * @param {V | import('./types.js').Signal} val - * @returns {val is import('./types.js').Signal} + * @param {V | import('./types.js').Value} val + * @returns {val is import('./types.js').Value} */ export function is_signal(val) { return ( typeof val === 'object' && val !== null && - typeof (/** @type {import('./types.js').Signal} */ (val).f) === 'number' + typeof (/** @type {import('./types.js').Value} */ (val).f) === 'number' ); } diff --git a/packages/svelte/src/internal/client/types.d.ts b/packages/svelte/src/internal/client/types.d.ts index c1b42a7b949d..563835384d54 100644 --- a/packages/svelte/src/internal/client/types.d.ts +++ b/packages/svelte/src/internal/client/types.d.ts @@ -27,13 +27,13 @@ export type Store = { export type ComponentContext = { /** local signals (needed for beforeUpdate/afterUpdate) */ - d: null | Signal[]; + d: null | Source[]; /** props */ s: Record; /** exports (and props, if `accessors: true`) */ x: Record | null; /** effects */ - e: null | Array; + e: null | Effect[]; /** mounted */ m: boolean; /** parent */ @@ -223,11 +223,11 @@ export type EachItemBlock = { /** dom */ d: null | TemplateNode | Array; /** effect */ - e: null | Reaction; + e: null | Effect; /** item */ - v: any | Value; + v: any | Source; /** index */ - i: number | Value; + i: number | Source; /** key */ k: unknown; /** parent */ @@ -292,7 +292,7 @@ export type StoreReferencesContainer = Record< store: Store | null; last_value: any; unsubscribe: Function; - value: Value; + value: Source; } >; diff --git a/packages/svelte/tests/signals/test.ts b/packages/svelte/tests/signals/test.ts index 07e5716edf31..8791a434d440 100644 --- a/packages/svelte/tests/signals/test.ts +++ b/packages/svelte/tests/signals/test.ts @@ -1,10 +1,10 @@ import { describe, assert, it } from 'vitest'; import * as $ from '../../src/internal/client/runtime'; -import { derived } from '../../src/internal/client/reactivity/deriveds'; import { effect, render_effect, user_effect } from '../../src/internal/client/reactivity/effects'; import { source, set } from '../../src/internal/client/reactivity/sources'; import type { Derived } from '../../src/internal/client/types'; import { proxy } from '../../src/internal/client/proxy'; +import { derived } from '../../src/internal/client/reactivity/deriveds'; /** * @param runes runes mode