You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
svelte/packages/svelte/src/internal/client/reactivity/deriveds.js

174 lines
3.6 KiB

/** @import { Derived, Effect } from '#client' */
import { DEV } from 'esm-env';
import {
CLEAN,
DERIVED,
DESTROYED,
DIRTY,
EFFECT_HAS_DERIVED,
MAYBE_DIRTY,
UNOWNED
} from '../constants.js';
import {
active_reaction,
active_effect,
remove_reactions,
set_signal_status,
skip_reaction,
update_reaction,
increment_version,
set_active_effect
} from '../runtime.js';
import { equals, safe_equals } from './equality.js';
import * as e from '../errors.js';
import { destroy_effect } from './effects.js';
import { inspect_effects, set_inspect_effects } from './sources.js';
/**
* @template V
* @param {() => V} fn
* @returns {Derived<V>}
*/
/*#__NO_SIDE_EFFECTS__*/
export function derived(fn) {
var flags = DERIVED | DIRTY;
if (active_effect === null) {
flags |= UNOWNED;
} else {
// Since deriveds are evaluated lazily, any effects created inside them are
// created too late to ensure that the parent effect is added to the tree
active_effect.f |= EFFECT_HAS_DERIVED;
}
/** @type {Derived<V>} */
const signal = {
children: null,
deps: null,
equals,
f: flags,
fn,
reactions: null,
v: /** @type {V} */ (null),
version: 0,
parent: active_effect
};
if (active_reaction !== null && (active_reaction.f & DERIVED) !== 0) {
var derived = /** @type {Derived} */ (active_reaction);
(derived.children ??= []).push(signal);
}
return signal;
}
/**
* @template V
* @param {() => V} fn
* @returns {Derived<V>}
*/
/*#__NO_SIDE_EFFECTS__*/
export function derived_safe_equal(fn) {
const signal = derived(fn);
signal.equals = safe_equals;
return signal;
}
/**
* @param {Derived} derived
* @returns {void}
*/
function destroy_derived_children(derived) {
var children = derived.children;
if (children !== null) {
derived.children = null;
for (var i = 0; i < children.length; i += 1) {
var child = children[i];
if ((child.f & DERIVED) !== 0) {
destroy_derived(/** @type {Derived} */ (child));
} else {
destroy_effect(/** @type {Effect} */ (child));
}
}
}
}
/**
* The currently updating deriveds, used to detect infinite recursion
* in dev mode and provide a nicer error than 'too much recursion'
* @type {Derived[]}
*/
let stack = [];
/**
* @template T
* @param {Derived} derived
* @returns {T}
*/
export function execute_derived(derived) {
var value;
var prev_active_effect = active_effect;
set_active_effect(derived.parent);
if (DEV) {
let prev_inspect_effects = inspect_effects;
set_inspect_effects(new Set());
try {
if (stack.includes(derived)) {
e.derived_references_self();
}
stack.push(derived);
destroy_derived_children(derived);
value = update_reaction(derived);
} finally {
set_active_effect(prev_active_effect);
set_inspect_effects(prev_inspect_effects);
stack.pop();
}
} else {
try {
destroy_derived_children(derived);
value = update_reaction(derived);
} finally {
set_active_effect(prev_active_effect);
}
}
return value;
}
/**
* @param {Derived} derived
* @returns {void}
*/
export function update_derived(derived) {
var value = execute_derived(derived);
var status =
(skip_reaction || (derived.f & UNOWNED) !== 0) && derived.deps !== null ? MAYBE_DIRTY : CLEAN;
set_signal_status(derived, status);
if (!derived.equals(value)) {
derived.v = value;
derived.version = increment_version();
}
}
/**
* @param {Derived} signal
* @returns {void}
*/
export function destroy_derived(signal) {
destroy_derived_children(signal);
remove_reactions(signal, 0);
set_signal_status(signal, DESTROYED);
// TODO we need to ensure we remove the derived from any parent derives
signal.v = signal.children = signal.deps = signal.reactions = null;
}