chore: refactor signal types (#10698)

- use interface instead of type (interface is less forgiving)
- rename SourceSignal to Source, etc
- make Derived extend Source, rather than deriveds sharing a type with effects, since they have much more in common with each other
- have a Value type which is a union of Source and Derived, and a Reaction type which is a union of Derived and Effect
- avoid using the Signal type (which is a union of all three) unless necessary
pull/10699/head
Rich Harris 10 months ago committed by GitHub
parent 0cc74e46fd
commit 86f326531c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -55,8 +55,8 @@ export function create_each_block(flags, anchor) {
}
/**
* @param {any | import('../../types.js').Signal<any>} item
* @param {number | import('../../types.js').Signal<number>} index
* @param {any | import('../../types.js').Value<any>} item
* @param {number | import('../../types.js').Value<number>} index
* @param {null | unknown} key
* @returns {import('../../types.js').EachItemBlock}
*/
@ -110,7 +110,7 @@ function each(anchor_node, collection, flags, key_fn, render_fn, fallback_fn, re
/** @type {Array<string> | null} */
let keys = null;
/** @type {null | import('../../types.js').EffectSignal} */
/** @type {null | import('../../types.js').Effect} */
let render = null;
/**
@ -279,7 +279,7 @@ function each(anchor_node, collection, flags, key_fn, render_fn, fallback_fn, re
}
// Clear the array
reconcile_fn([], block, anchor_node, is_controlled, render_fn, flags, false, keys);
destroy_signal(/** @type {import('../../types.js').EffectSignal} */ (render));
destroy_signal(/** @type {import('../../types.js').Effect} */ (render));
});
block.e = each;
@ -318,7 +318,7 @@ export function each_indexed(anchor_node, collection, flags, render_fn, fallback
* @param {import('../../types.js').EachBlock} each_block
* @param {Element | Comment | Text} dom
* @param {boolean} is_controlled
* @param {(anchor: null, item: V, index: number | import('../../types.js').Signal<number>) => void} render_fn
* @param {(anchor: null, item: V, index: number | import('../../types.js').Source<number>) => void} render_fn
* @param {number} flags
* @param {boolean} apply_transitions
* @returns {void}
@ -433,7 +433,7 @@ function reconcile_indexed_array(
* @param {import('../../types.js').EachBlock} each_block
* @param {Element | Comment | Text} dom
* @param {boolean} is_controlled
* @param {(anchor: null, item: V, index: number | import('../../types.js').Signal<number>) => void} render_fn
* @param {(anchor: null, item: V, index: number | import('../../types.js').Source<number>) => void} render_fn
* @param {number} flags
* @param {boolean} apply_transitions
* @param {Array<string> | null} keys
@ -848,7 +848,7 @@ function update_each_item_block(block, item, index, type) {
each_animation(block, transitions);
}
if (index_is_reactive) {
set(/** @type {import('../../types.js').Signal<number>} */ (block.i), index);
set(/** @type {import('../../types.js').Value<number>} */ (block.i), index);
} else {
block.i = index;
}
@ -890,7 +890,7 @@ export function destroy_each_item_block(
if (!controlled && dom !== null) {
remove(dom);
}
destroy_signal(/** @type {import('../../types.js').EffectSignal} */ (block.e));
destroy_signal(/** @type {import('../../types.js').Effect} */ (block.e));
}
/**
@ -898,7 +898,7 @@ export function destroy_each_item_block(
* @param {V} item
* @param {unknown} key
* @param {number} index
* @param {(anchor: null, item: V, index: number | import('../../types.js').Signal<number>) => void} render_fn
* @param {(anchor: null, item: V, index: number | import('../../types.js').Value<number>) => void} render_fn
* @param {number} flags
* @returns {import('../../types.js').EachItemBlock}
*/

@ -55,7 +55,7 @@ export function if_block(anchor_node, condition_fn, consequent_fn, alternate_fn)
let alternate_dom = null;
let has_mounted = false;
/**
* @type {import('../../types.js').EffectSignal | null}
* @type {import('../../types.js').Effect | null}
*/
let current_branch_effect = null;
@ -117,7 +117,7 @@ export function if_block(anchor_node, condition_fn, consequent_fn, alternate_fn)
const consequent_effect = render_effect(
(
/** @type {any} */ _,
/** @type {import('../../types.js').EffectSignal | null} */ consequent_effect
/** @type {import('../../types.js').Effect | null} */ consequent_effect
) => {
const result = block.v;
if (!result && consequent_dom !== null) {
@ -143,7 +143,7 @@ export function if_block(anchor_node, condition_fn, consequent_fn, alternate_fn)
const alternate_effect = render_effect(
(
/** @type {any} */ _,
/** @type {import('../../types.js').EffectSignal | null} */ alternate_effect
/** @type {import('../../types.js').Effect | null} */ alternate_effect
) => {
const result = block.v;
if (result && alternate_dom !== null) {

@ -148,7 +148,7 @@ export function unstate(value) {
}
/**
* @param {import('./types.js').Signal<number>} signal
* @param {import('./types.js').Source<number>} signal
* @param {1 | -1} [d]
*/
function update_version(signal, d = 1) {

@ -1,32 +1,48 @@
import { DEV } from 'esm-env';
import { CLEAN, DERIVED, UNINITIALIZED, UNOWNED } from '../constants.js';
import { current_block, current_consumer, current_effect } from '../runtime.js';
import { create_computation_signal, push_reference } from './effects.js';
import { push_reference } from './effects.js';
import { default_equals, safe_equal } from './equality.js';
/**
* @template V
* @param {() => V} fn
* @returns {import('../types.js').ComputationSignal<V>}
* @returns {import('../types.js').Derived<V>}
*/
/*#__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').ComputationSignal<V>} */ (
create_computation_signal(flags | CLEAN, UNINITIALIZED, current_block)
);
signal.i = fn;
signal.e = default_equals;
const signal = /** @type {import('../types.js').Derived<V>} */ ({
b: current_block,
c: null,
d: null,
e: default_equals,
f: flags | CLEAN,
i: fn,
r: null,
v: UNINITIALIZED,
w: 0,
x: null,
y: null
});
if (DEV) {
// @ts-expect-error
signal.inspect = new Set();
}
if (current_consumer !== null) {
push_reference(current_consumer, signal);
}
return signal;
}
/**
* @template V
* @param {() => V} fn
* @returns {import('../types.js').ComputationSignal<V>}
* @returns {import('../types.js').Derived<V>}
*/
/*#__NO_SIDE_EFFECTS__*/
export function derived_safe_equal(fn) {

@ -2,59 +2,16 @@ import { DEV } from 'esm-env';
import {
current_block,
current_component_context,
current_consumer,
current_effect,
destroy_signal,
flush_local_render_effects,
schedule_effect
} from '../runtime.js';
import { default_equals, safe_equal } from './equality.js';
import {
DIRTY,
MANAGED,
RENDER_EFFECT,
EFFECT,
PRE_EFFECT,
DERIVED,
UNOWNED,
CLEAN,
UNINITIALIZED
} from '../constants.js';
/**
* @template V
* @param {import('../types.js').SignalFlags} flags
* @param {V} value
* @param {import('../types.js').Block | null} block
*/
export function create_computation_signal(flags, value, block) {
/** @type {import('../types.js').ComputationSignal<V>} */
const signal = {
b: block,
c: null,
d: null,
e: null,
f: flags,
l: 0,
i: null,
r: null,
v: value,
w: 0,
x: null,
y: null
};
if (DEV) {
// @ts-expect-error
signal.inspect = new Set();
}
return signal;
}
import { DIRTY, MANAGED, RENDER_EFFECT, EFFECT, PRE_EFFECT } from '../constants.js';
/**
* @param {import('../types.js').ComputationSignal} target_signal
* @param {import('../types.js').ComputationSignal} ref_signal
* @param {import('../types.js').Reaction} target_signal
* @param {import('../types.js').Reaction} ref_signal
* @returns {void}
*/
export function push_reference(target_signal, ref_signal) {
@ -72,12 +29,25 @@ export function push_reference(target_signal, ref_signal) {
* @param {boolean} sync
* @param {null | import('../types.js').Block} block
* @param {boolean} schedule
* @returns {import('../types.js').EffectSignal}
* @returns {import('../types.js').Effect}
*/
function internal_create_effect(type, fn, sync, block, schedule) {
const signal = create_computation_signal(type | DIRTY, null, block);
signal.i = fn;
signal.x = current_component_context;
/** @type {import('#client').Effect} */
const signal = {
b: block,
c: null,
d: null,
e: null,
f: type | DIRTY,
l: 0,
i: fn,
r: null,
v: null,
w: 0,
x: current_component_context,
y: null
};
if (current_effect !== null) {
signal.l = current_effect.l + 1;
if ((type & MANAGED) === 0) {
@ -100,7 +70,7 @@ export function effect_active() {
/**
* Internal representation of `$effect(...)`
* @param {() => void | (() => void)} fn
* @returns {import('../types.js').EffectSignal}
* @returns {import('../types.js').Effect}
*/
export function user_effect(fn) {
if (current_effect === null) {
@ -147,7 +117,7 @@ export function user_root_effect(fn) {
/**
* @param {() => void | (() => void)} fn
* @returns {import('../types.js').EffectSignal}
* @returns {import('../types.js').Effect}
*/
export function effect(fn) {
return internal_create_effect(EFFECT, fn, false, current_block, true);
@ -155,7 +125,7 @@ export function effect(fn) {
/**
* @param {() => void | (() => void)} fn
* @returns {import('../types.js').EffectSignal}
* @returns {import('../types.js').Effect}
*/
export function managed_effect(fn) {
return internal_create_effect(EFFECT | MANAGED, fn, false, current_block, true);
@ -164,7 +134,7 @@ export function managed_effect(fn) {
/**
* @param {() => void | (() => void)} fn
* @param {boolean} sync
* @returns {import('../types.js').EffectSignal}
* @returns {import('../types.js').Effect}
*/
export function managed_pre_effect(fn, sync) {
return internal_create_effect(PRE_EFFECT | MANAGED, fn, sync, current_block, true);
@ -173,7 +143,7 @@ export function managed_pre_effect(fn, sync) {
/**
* Internal representation of `$effect.pre(...)`
* @param {() => void | (() => void)} fn
* @returns {import('../types.js').EffectSignal}
* @returns {import('../types.js').Effect}
*/
export function pre_effect(fn) {
if (current_effect === null) {
@ -203,7 +173,7 @@ 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').EffectSignal}
* @returns {import('../types.js').Effect}
*/
export function invalidate_effect(fn) {
return internal_create_effect(PRE_EFFECT, fn, true, current_block, true);
@ -215,7 +185,7 @@ export function invalidate_effect(fn) {
* @param {any} block
* @param {any} managed
* @param {any} sync
* @returns {import('../types.js').EffectSignal}
* @returns {import('../types.js').Effect}
*/
export function render_effect(fn, block = current_block, managed = false, sync = true) {
let flags = RENDER_EFFECT;

@ -24,17 +24,30 @@ import { CLEAN, DERIVED, DIRTY, MANAGED, SOURCE } from '../constants.js';
/**
* @template V
* @param {V} initial_value
* @returns {import('../types.js').SourceSignal<V>}
* @returns {import('../types.js').Source<V>}
*/
/*#__NO_SIDE_EFFECTS__*/
export function source(initial_value) {
return create_source_signal(SOURCE | CLEAN, initial_value);
/** @type {import('#client').Source<V>} */
const signal = {
c: null,
e: default_equals,
f: SOURCE | CLEAN,
v: initial_value,
w: 0
};
if (DEV) {
/** @type {import('#client').SourceDebug} */ (signal).inspect = new Set();
}
return signal;
}
/**
* @template V
* @param {V} initial_value
* @returns {import('../types.js').SourceSignal<V>}
* @returns {import('../types.js').Source<V>}
*/
/*#__NO_SIDE_EFFECTS__*/
export function mutable_source(initial_value) {
@ -52,44 +65,7 @@ export function mutable_source(initial_value) {
/**
* @template V
* @param {import('../types.js').SignalFlags} flags
* @param {V} value
* @returns {import('../types.js').SourceSignal<V> | import('../types.js').SourceSignal<V> & import('../types.js').SourceSignalDebug}
*/
function create_source_signal(flags, value) {
if (DEV) {
return {
// consumers
c: null,
// equals
e: default_equals,
// flags
f: flags,
// value
v: value,
// write version
w: 0,
// this is for DEV only
inspect: new Set()
};
}
return {
// consumers
c: null,
// equals
e: default_equals,
// flags
f: flags,
// value
v: value,
// write version
w: 0
};
}
/**
* @template V
* @param {import('./types.js').Signal<V>} signal
* @param {import('./types.js').Source<V>} signal
* @param {V} value
* @returns {void}
*/
@ -99,7 +75,7 @@ export function set_sync(signal, value) {
/**
* @template V
* @param {import('./types.js').Signal<V>} source
* @param {import('./types.js').Value<V>} source
* @param {V} value
*/
export function mutate(source, value) {
@ -112,7 +88,7 @@ export function mutate(source, value) {
/**
* @template V
* @param {import('./types.js').Signal<V>} signal
* @param {import('./types.js').Source<V>} signal
* @param {V} value
* @returns {V}
*/
@ -174,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').SignalDebug} */ (signal));
set_last_inspected_signal(/** @type {import('./types.js').ValueDebug} */ (signal));
} else {
for (const fn of /** @type {import('./types.js').SignalDebug} */ (signal).inspect) fn();
for (const fn of /** @type {import('./types.js').ValueDebug} */ (signal).inspect) fn();
}
}
}

@ -50,7 +50,7 @@ export function store_get(store, store_name, stores) {
/**
* @template V
* @param {import('../types.js').Store<V> | null | undefined} store
* @param {import('../types.js').SourceSignal<V>} source
* @param {import('../types.js').Source<V>} source
*/
function connect_store_to_signal(store, source) {
if (store == null) {

@ -9,14 +9,9 @@ export type SignalFlags =
| typeof RENDER_EFFECT;
export type EffectType = typeof EFFECT | typeof PRE_EFFECT | typeof RENDER_EFFECT;
// We keep two shapes rather than a single monomorphic shape to improve the memory usage.
// Source signals don't need the same shape as they simply don't do as much as computations
// (effects and derived signals). Thus we can improve the memory profile at the slight cost
// of some runtime performance.
export type SourceSignal<V = unknown> = {
export interface Source<V = unknown> {
/** consumers: Signals that read from the current signal */
c: null | ComputationSignal[];
c: null | Reaction[];
/** equals: For value equality */
e: null | EqualsFunctions;
/** flags: The types that the signal represent, as a bitwise value */
@ -25,50 +20,70 @@ export type SourceSignal<V = unknown> = {
v: V;
// write version
w: number;
};
}
export type SourceSignalDebug = {
/** This is DEV only */
export interface SourceDebug<V = unknown> extends Source<V> {
inspect: Set<Function>;
};
}
export interface Derived<V = unknown> extends Source<V> {
/** dependencies: Signals that this signal reads from */
d: null | Value[];
/** The derived function */
i: () => V;
// TODO get rid of these
/** references: Anything that a signal owns */
r: null | Reaction[];
/** block: The block associated with this effect/computed */
b: null | Block;
/** context: The associated component if this signal is an effect/computed */
x: null | ComponentContext;
/** destroy: Thing(s) that need destroying */
y: null | (() => void) | Array<() => void>;
}
export type ComputationSignal<V = unknown> = {
export interface DerivedDebug<V = unknown> extends Derived<V> {
inspect: Set<Function>;
}
export type Effect = {
/** block: The block associated with this effect/computed */
b: null | Block;
/** consumers: Signals that read from the current signal */
c: null | ComputationSignal[];
c: null | Reaction[];
/** context: The associated component if this signal is an effect/computed */
x: null | ComponentContext;
/** dependencies: Signals that this signal reads from */
d: null | Signal<V>[];
d: null | Value[];
/** destroy: Thing(s) that need destroying */
// TODO simplify this, it is only used in one place
y: null | (() => void) | Array<() => void>;
/** equals: For value equality */
e: null | EqualsFunctions;
/** The types that the signal represent, as a bitwise value */
f: SignalFlags;
/** init: The function that we invoke for effects and computeds */
i:
| null
| (() => V)
| (() => void | (() => void))
| ((b: Block, s: Signal) => void | (() => void));
i: null | (() => void | (() => void)) | ((b: Block, s: Signal) => void | (() => void));
/** references: Anything that a signal owns */
r: null | ComputationSignal[];
r: null | Reaction[];
/** value: The latest value for this signal, doubles as the teardown for effects */
v: V;
v: null | Function;
/** level: the depth from the root signal, used for ordering render/pre-effects topologically **/
l: number;
/** write version: used for unowned signals to track if their depdendencies are dirty or not **/
w: number;
};
export type Signal<V = unknown> = SourceSignal<V> | ComputationSignal<V>;
export type Reaction = Derived | Effect;
export type Signal<V = unknown> = Source<V> | Reaction;
export type SignalDebug<V = unknown> = SourceSignalDebug & Signal<V>;
export type MaybeSignal<T = unknown> = T | Source<T>;
export type EffectSignal = ComputationSignal<null | (() => void)>;
export type UnwrappedSignal<T> = T extends Value<infer U> ? U : T;
export type MaybeSignal<T = unknown> = T | Signal<T>;
export type Value<V = unknown> = Source<V> | Derived<V>;
export type UnwrappedSignal<T> = T extends Signal<infer U> ? U : T;
export type ValueDebug<V = unknown> = SourceDebug<V> | DerivedDebug<V>;

@ -727,7 +727,7 @@ export function bind_playback_rate(media, get_value, update) {
// Needs to happen after the element is inserted into the dom, else playback will be set back to 1 by the browser.
// For hydration we could do it immediately but the additional code is not worth the lost microtask.
/** @type {import('./types.js').ComputationSignal | undefined} */
/** @type {import('./types.js').Reaction | undefined} */
let render;
let destroyed = false;
const effect = managed_effect(() => {
@ -2384,7 +2384,7 @@ const rest_props_handler = {
};
/**
* @param {import('./types.js').Signal<Record<string, unknown>> | Record<string, unknown>} props
* @param {Record<string, unknown>} props
* @param {string[]} rest
* @returns {Record<string, unknown>}
*/
@ -2647,7 +2647,7 @@ function _mount(Component, options) {
if (dom !== null) {
remove(dom);
}
destroy_signal(/** @type {import('./types.js').EffectSignal} */ (block.e));
destroy_signal(/** @type {import('./types.js').Effect} */ (block.e));
});
return component;

@ -48,40 +48,40 @@ let is_inspecting_signal = false;
// Handle effect queues
/** @type {import('./types.js').EffectSignal[]} */
/** @type {import('./types.js').Effect[]} */
let current_queued_pre_and_render_effects = [];
/** @type {import('./types.js').EffectSignal[]} */
/** @type {import('./types.js').Effect[]} */
let current_queued_effects = [];
let flush_count = 0;
// Handle signal reactivity tree dependencies and consumer
/** @type {null | import('./types.js').ComputationSignal} */
/** @type {null | import('./types.js').Reaction} */
export let current_consumer = null;
/** @type {null | import('./types.js').EffectSignal} */
/** @type {null | import('./types.js').Effect} */
export let current_effect = null;
/** @type {null | import('./types.js').Signal[]} */
/** @type {null | import('./types.js').Value[]} */
export let current_dependencies = null;
let current_dependencies_index = 0;
/**
* Tracks writes that the effect it's executed in doesn't listen to yet,
* so that the dependency can be added to the effect later on if it then reads it
* @type {null | import('./types.js').Signal[]}
* @type {null | import('./types.js').Source[]}
*/
export let current_untracked_writes = null;
/** @param {null | import('./types.js').Signal[]} value */
/** @param {null | import('./types.js').Source[]} value */
export function set_current_untracked_writes(value) {
current_untracked_writes = value;
}
/** @type {null | import('./types.js').SignalDebug} */
/** @type {null | import('./types.js').ValueDebug} */
export let last_inspected_signal = null;
/** @param {null | import('./types.js').SignalDebug} signal */
/** @param {null | import('./types.js').ValueDebug} signal */
export function set_last_inspected_signal(signal) {
last_inspected_signal = signal;
}
@ -105,7 +105,7 @@ let captured_signals = new Set();
/** @type {Function | null} */
export let inspect_fn = null;
/** @type {Array<import('./types.js').SignalDebug>} */
/** @type {Array<import('./types.js').ValueDebug>} */
let inspect_captured_signals = [];
// Handle rendering tree blocks and anchors
@ -160,8 +160,7 @@ export function batch_inspect(target, prop, receiver) {
}
/**
* @template V
* @param {import('./types.js').Signal<V>} signal
* @param {import('./types.js').Signal} signal
* @returns {boolean}
*/
function is_signal_dirty(signal) {
@ -170,7 +169,7 @@ function is_signal_dirty(signal) {
return true;
}
if ((flags & MAYBE_DIRTY) !== 0) {
const dependencies = /** @type {import('./types.js').ComputationSignal<V>} **/ (signal).d;
const dependencies = /** @type {import('./types.js').Reaction} **/ (signal).d;
if (dependencies !== null) {
const length = dependencies.length;
let i;
@ -183,10 +182,7 @@ function is_signal_dirty(signal) {
// The flags can be marked as dirty from the above is_signal_dirty call.
if ((dependency.f & DIRTY) !== 0) {
if ((dependency.f & DERIVED) !== 0) {
update_derived(
/** @type {import('./types.js').ComputationSignal<V>} **/ (dependency),
true
);
update_derived(/** @type {import('./types.js').Derived} **/ (dependency), true);
// Might have been mutated from above get.
if ((signal.f & DIRTY) !== 0) {
return true;
@ -214,7 +210,7 @@ function is_signal_dirty(signal) {
/**
* @template V
* @param {import('./types.js').ComputationSignal<V>} signal
* @param {import('./types.js').Reaction} signal
* @returns {V}
*/
function execute_signal_fn(signal) {
@ -229,7 +225,7 @@ function execute_signal_fn(signal) {
const previous_skip_consumer = current_skip_consumer;
const is_render_effect = (flags & RENDER_EFFECT) !== 0;
const previous_untracking = current_untracking;
current_dependencies = /** @type {null | import('./types.js').Signal[]} */ (null);
current_dependencies = /** @type {null | import('./types.js').Value[]} */ (null);
current_dependencies_index = 0;
current_untracked_writes = null;
current_consumer = signal;
@ -251,7 +247,7 @@ function execute_signal_fn(signal) {
} else {
res = /** @type {() => V} */ (init)();
}
let dependencies = /** @type {import('./types.js').Signal<unknown>[]} **/ (signal.d);
let dependencies = /** @type {import('./types.js').Value<unknown>[]} **/ (signal.d);
if (current_dependencies !== null) {
let i;
if (dependencies !== null) {
@ -286,7 +282,7 @@ function execute_signal_fn(signal) {
dependencies[current_dependencies_index + i] = current_dependencies[i];
}
} else {
signal.d = /** @type {import('./types.js').Signal<V>[]} **/ (
signal.d = /** @type {import('./types.js').Value<V>[]} **/ (
dependencies = current_dependencies
);
}
@ -326,8 +322,8 @@ function execute_signal_fn(signal) {
/**
* @template V
* @param {import('./types.js').ComputationSignal<V>} signal
* @param {import('./types.js').Signal<V>} dependency
* @param {import('./types.js').Reaction} signal
* @param {import('./types.js').Value<V>} dependency
* @returns {void}
*/
function remove_consumer(signal, dependency) {
@ -349,13 +345,12 @@ function remove_consumer(signal, dependency) {
if (consumers_length === 0 && (dependency.f & UNOWNED) !== 0) {
// If the signal is unowned then we need to make sure to change it to dirty.
set_signal_status(dependency, DIRTY);
remove_consumers(/** @type {import('./types.js').ComputationSignal<V>} **/ (dependency), 0);
remove_consumers(/** @type {import('./types.js').Reaction} **/ (dependency), 0);
}
}
/**
* @template V
* @param {import('./types.js').ComputationSignal<V>} signal
* @param {import('./types.js').Reaction} signal
* @param {number} start_index
* @returns {void}
*/
@ -375,8 +370,7 @@ function remove_consumers(signal, start_index) {
}
/**
* @template V
* @param {import('./types.js').ComputationSignal<V>} signal
* @param {import('./types.js').Reaction} signal
* @returns {void}
*/
function destroy_references(signal) {
@ -405,7 +399,7 @@ function report_error(block, error) {
}
/**
* @param {import('./types.js').EffectSignal} signal
* @param {import('./types.js').Effect} signal
* @returns {void}
*/
export function execute_effect(signal) {
@ -460,7 +454,7 @@ function infinite_loop_guard() {
}
/**
* @param {Array<import('./types.js').EffectSignal>} effects
* @param {Array<import('./types.js').Effect>} effects
* @returns {void}
*/
function flush_queued_effects(effects) {
@ -508,7 +502,7 @@ function process_microtask() {
}
/**
* @param {import('./types.js').EffectSignal} signal
* @param {import('./types.js').Effect} signal
* @param {boolean} sync
* @returns {void}
*/
@ -640,10 +634,10 @@ export function flush_sync(fn, flush_previous = true) {
try {
infinite_loop_guard();
/** @type {import('./types.js').EffectSignal[]} */
/** @type {import('./types.js').Effect[]} */
const pre_and_render_effects = [];
/** @type {import('./types.js').EffectSignal[]} */
/** @type {import('./types.js').Effect[]} */
const effects = [];
current_scheduler_mode = FLUSH_SYNC;
current_queued_pre_and_render_effects = pre_and_render_effects;
@ -681,8 +675,7 @@ export async function tick() {
}
/**
* @template V
* @param {import('./types.js').ComputationSignal<V>} signal
* @param {import('./types.js').Derived} signal
* @param {boolean} force_schedule
* @returns {void}
*/
@ -704,20 +697,20 @@ function update_derived(signal, force_schedule) {
// @ts-expect-error
if (DEV && signal.inspect && force_schedule) {
for (const fn of /** @type {import('./types.js').SignalDebug} */ (signal).inspect) fn();
for (const fn of /** @type {import('./types.js').ValueDebug} */ (signal).inspect) fn();
}
}
}
/**
* @template V
* @param {import('./types.js').Signal<V>} signal
* @param {import('./types.js').Value<V>} signal
* @returns {V}
*/
export function get(signal) {
// @ts-expect-error
if (DEV && signal.inspect && inspect_fn) {
/** @type {import('./types.js').SignalDebug} */ (signal).inspect.add(inspect_fn);
/** @type {import('./types.js').ValueDebug} */ (signal).inspect.add(inspect_fn);
// @ts-expect-error
inspect_captured_signals.push(signal);
}
@ -770,10 +763,10 @@ export function get(signal) {
// we want to avoid tracking indirect dependencies
const previous_inspect_fn = inspect_fn;
inspect_fn = null;
update_derived(/** @type {import('./types.js').ComputationSignal<V>} **/ (signal), false);
update_derived(/** @type {import('./types.js').Derived} **/ (signal), false);
inspect_fn = previous_inspect_fn;
} else {
update_derived(/** @type {import('./types.js').ComputationSignal<V>} **/ (signal), false);
update_derived(/** @type {import('./types.js').Derived} **/ (signal), false);
}
}
return signal.v;
@ -808,7 +801,7 @@ export function invalidate_inner_signals(fn) {
}
/**
* @param {import('./types.js').ComputationSignal} signal
* @param {import('./types.js').Reaction} signal
* @param {boolean} inert
* @param {Set<import('./types.js').Block>} [visited_blocks]
* @returns {void}
@ -827,7 +820,7 @@ function mark_subtree_children_inert(signal, inert, visited_blocks) {
}
/**
* @param {import('./types.js').ComputationSignal} signal
* @param {import('./types.js').Reaction} signal
* @param {boolean} inert
* @param {Set<import('./types.js').Block>} [visited_blocks]
* @returns {void}
@ -838,7 +831,7 @@ export function mark_subtree_inert(signal, inert, visited_blocks = new Set()) {
if (is_already_inert !== inert) {
signal.f ^= INERT;
if (!inert && (flags & IS_EFFECT) !== 0 && (flags & CLEAN) === 0) {
schedule_effect(/** @type {import('./types.js').EffectSignal} */ (signal), false);
schedule_effect(/** @type {import('./types.js').Effect} */ (signal), false);
}
// Nested if block effects
const block = signal.b;
@ -901,7 +894,7 @@ export function mark_signal_consumers(signal, to_status, force_schedule) {
const maybe_dirty = (flags & MAYBE_DIRTY) !== 0;
if ((flags & CLEAN) !== 0 || (maybe_dirty && unowned)) {
if ((consumer.f & IS_EFFECT) !== 0) {
schedule_effect(/** @type {import('./types.js').EffectSignal} */ (consumer), false);
schedule_effect(/** @type {import('./types.js').Effect} */ (consumer), false);
} else {
mark_signal_consumers(consumer, MAYBE_DIRTY, force_schedule);
}
@ -911,8 +904,7 @@ export function mark_signal_consumers(signal, to_status, force_schedule) {
}
/**
* @template V
* @param {import('./types.js').ComputationSignal<V>} signal
* @param {import('./types.js').Reaction} signal
* @returns {void}
*/
export function destroy_signal(signal) {
@ -954,8 +946,7 @@ export function untrack(fn) {
}
/**
* @template V
* @param {import('./types.js').ComputationSignal<V>} signal
* @param {import('./types.js').Reaction} signal
* @param {() => void} destroy_fn
* @returns {void}
*/
@ -1104,7 +1095,7 @@ function get_parent_context(component_context) {
}
/**
* @param {import('./types.js').Signal<number>} signal
* @param {import('./types.js').Value<number>} signal
* @param {1 | -1} [d]
* @returns {number}
*/
@ -1115,7 +1106,7 @@ export function update(signal, d = 1) {
}
/**
* @param {import('./types.js').Signal<number>} signal
* @param {import('./types.js').Value<number>} signal
* @param {1 | -1} [d]
* @returns {number}
*/

@ -295,7 +295,7 @@ const linear = (t) => t;
* @param {HTMLElement} dom
* @param {() => import('./types.js').TransitionPayload} init
* @param {'in' | 'out' | 'both' | 'key'} direction
* @param {import('./types.js').EffectSignal} effect
* @param {import('./types.js').Effect} effect
* @returns {import('./types.js').Transition}
*/
function create_transition(dom, init, direction, effect) {
@ -503,7 +503,7 @@ function is_transition_block(block) {
* @returns {void}
*/
export function bind_transition(dom, get_transition_fn, props_fn, direction, global) {
const transition_effect = /** @type {import('./types.js').EffectSignal} */ (current_effect);
const transition_effect = /** @type {import('./types.js').Effect} */ (current_effect);
const block = current_block;
const is_keyed_transition = direction === 'key';
@ -692,7 +692,7 @@ function if_block_transition(transition) {
// If the block has changed to falsy and has transitions
if (!block.v && c.size === 0) {
const consequent_effect = block.ce;
execute_effect(/** @type {import('./types.js').EffectSignal} */ (consequent_effect));
execute_effect(/** @type {import('./types.js').Effect} */ (consequent_effect));
}
});
} else {
@ -704,7 +704,7 @@ function if_block_transition(transition) {
// If the block has changed to truthy and has transitions
if (block.v && a.size === 0) {
const alternate_effect = block.ae;
execute_effect(/** @type {import('./types.js').EffectSignal} */ (alternate_effect));
execute_effect(/** @type {import('./types.js').Effect} */ (alternate_effect));
}
});
}

@ -11,7 +11,7 @@ import {
SNIPPET_BLOCK,
STATE_SYMBOL
} from './constants.js';
import type { ComputationSignal, EffectSignal, Signal, SourceSignal } from './reactivity/types.js';
import type { Reaction, Effect, Signal, Source, Value } from './reactivity/types.js';
type EventCallback = (event: Event) => boolean;
export type EventCallbackMap = Record<string, EventCallback | EventCallback[]>;
@ -33,7 +33,7 @@ export type ComponentContext = {
/** exports (and props, if `accessors: true`) */
x: Record<string, any> | null;
/** effects */
e: null | Array<EffectSignal>;
e: null | Array<Effect>;
/** mounted */
m: boolean;
/** parent */
@ -71,7 +71,7 @@ export type TemplateNode = Text | Element | Comment;
export type Transition = {
/** effect */
e: EffectSignal;
e: Effect;
/** payload */
p: null | TransitionPayload;
/** init */
@ -95,7 +95,7 @@ export type RootBlock = {
/** dom */
d: null | TemplateNode | Array<TemplateNode>;
/** effect */
e: null | ComputationSignal;
e: null | Reaction;
/** intro */
i: boolean;
/** parent */
@ -112,7 +112,7 @@ export type IfBlock = {
/** dom */
d: null | TemplateNode | Array<TemplateNode>;
/** effect */
e: null | EffectSignal;
e: null | Effect;
/** parent */
p: Block;
/** transition */
@ -122,9 +122,9 @@ export type IfBlock = {
/** alternate transitions */
a: null | Set<Transition>;
/** effect */
ce: null | EffectSignal;
ce: null | Effect;
/** effect */
ae: null | EffectSignal;
ae: null | Effect;
/** type */
t: typeof IF_BLOCK;
};
@ -133,7 +133,7 @@ export type KeyBlock = {
/** dom */
d: null | TemplateNode | Array<TemplateNode>;
/** effect */
e: null | EffectSignal;
e: null | Effect;
/** parent */
p: Block;
/** transition */
@ -146,7 +146,7 @@ export type HeadBlock = {
/** dom */
d: null | TemplateNode | Array<TemplateNode>;
/** effect */
e: null | ComputationSignal;
e: null | Reaction;
/** parent */
p: Block;
/** transition */
@ -159,7 +159,7 @@ export type DynamicElementBlock = {
/** dom */
d: null | TemplateNode | Array<TemplateNode>;
/** effect */
e: null | ComputationSignal;
e: null | Reaction;
/** parent */
p: Block;
/** transition */
@ -172,7 +172,7 @@ export type DynamicComponentBlock = {
/** dom */
d: null | TemplateNode | Array<TemplateNode>;
/** effect */
e: null | ComputationSignal;
e: null | Reaction;
/** parent */
p: Block;
/** transition */
@ -185,7 +185,7 @@ export type AwaitBlock = {
/** dom */
d: null | TemplateNode | Array<TemplateNode>;
/** effect */
e: null | ComputationSignal;
e: null | Reaction;
/** parent */
p: Block;
/** pending */
@ -206,7 +206,7 @@ export type EachBlock = {
/** items */
v: EachItemBlock[];
/** effewct */
e: null | ComputationSignal;
e: null | Reaction;
/** parent */
p: Block;
/** transition */
@ -223,11 +223,11 @@ export type EachItemBlock = {
/** dom */
d: null | TemplateNode | Array<TemplateNode>;
/** effect */
e: null | ComputationSignal;
e: null | Reaction;
/** item */
v: any | Signal<any>;
v: any | Value<any>;
/** index */
i: number | Signal<number>;
i: number | Value<number>;
/** key */
k: unknown;
/** parent */
@ -246,7 +246,7 @@ export type SnippetBlock = {
/** parent */
p: Block;
/** effect */
e: null | ComputationSignal;
e: null | Reaction;
/** transition */
r: null;
/** type */
@ -292,7 +292,7 @@ export type StoreReferencesContainer = Record<
store: Store<any> | null;
last_value: any;
unsubscribe: Function;
value: Signal<any>;
value: Value<any>;
}
>;
@ -302,7 +302,7 @@ export type Render = {
/** dom */
d: null | TemplateNode | Array<TemplateNode>;
/** effect */
e: null | EffectSignal;
e: null | Effect;
/** transitions */
s: Set<Transition>;
/** prev */
@ -325,9 +325,9 @@ export type TaskEntry = { c: TaskCallback; f: () => void };
export interface ProxyMetadata<T = Record<string | symbol, any>> {
/** A map of signals associated to the properties that are reactive */
s: Map<string | symbol, SourceSignal<any>>;
s: Map<string | symbol, Source<any>>;
/** A version counter, used within the proxy to signal changes in places where there's no other way to signal an update */
v: SourceSignal<number>;
v: Source<number>;
/** `true` if the proxified object is an array */
a: boolean;
/** Immutable: Whether to use a source or mutable source under the hood */

@ -3,7 +3,7 @@ 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 { ComputationSignal } from '../../src/internal/client/types';
import type { Derived } from '../../src/internal/client/types';
import { proxy } from '../../src/internal/client/proxy';
/**
@ -206,7 +206,7 @@ describe('signals', () => {
test('correctly cleanup onowned nested derived values', () => {
return () => {
const nested: ComputationSignal<string>[] = [];
const nested: Derived<string>[] = [];
const a = source(0);
const b = source(0);

Loading…
Cancel
Save