blockless
Rich Harris 2 years ago
parent 4abc436fab
commit 8d383aed7e

@ -15,11 +15,11 @@ export function derived(fn) {
/** @type {import('#client').Derived<V>} */
const signal = {
c: null,
consumers: null,
d: null,
e: default_equals,
eq: default_equals,
f: flags,
i: fn,
fn: fn,
r: null,
// @ts-expect-error
v: UNINITIALIZED,
@ -51,6 +51,6 @@ export function derived(fn) {
/*#__NO_SIDE_EFFECTS__*/
export function derived_safe_equal(fn) {
const signal = derived(fn);
signal.e = safe_equal;
signal.eq = safe_equal;
return signal;
}

@ -31,7 +31,7 @@ function create_effect(type, fn, sync, schedule) {
d: null,
f: type | DIRTY,
l: 0,
i: fn,
fn: fn,
r: null,
v: null,
ctx: current_component_context,

@ -12,15 +12,10 @@ import { CLEAN, SOURCE } from '../constants.js';
export function source(value) {
/** @type {import('#client').Source<V>} */
const source = {
// consumers
c: null,
// equals
e: default_equals,
// flags
f: SOURCE | CLEAN,
// value
v: value,
// write version
eq: default_equals,
consumers: null,
w: 0
};
@ -39,7 +34,7 @@ export function source(value) {
/*#__NO_SIDE_EFFECTS__*/
export function mutable_source(initial_value) {
const s = source(initial_value);
s.e = safe_equal;
s.eq = safe_equal;
// bind the signal to the component context, in case we need to
// track updates to trigger beforeUpdate/afterUpdate callbacks

@ -12,9 +12,9 @@ export type EffectType = typeof EFFECT | typeof PRE_EFFECT | typeof RENDER_EFFEC
export interface Source<V = unknown> {
/** consumers: Signals that read from the current signal */
c: null | Reaction[];
consumers: null | Reaction[];
/** equals: For value equality */
e: EqualsFunctions;
eq: EqualsFunctions;
/** flags: The types that the signal represent, as a bitwise value */
f: SignalFlags;
/** value: The latest value for this signal */
@ -27,23 +27,13 @@ export interface SourceDebug<V = unknown> extends Source<V> {
inspect: Set<Function>;
}
export interface Derived<V = unknown> {
/** consumers: Signals that read from the current signal */
c: null | Reaction[];
export interface Derived<V = unknown> extends Source<V> {
/** dependencies: Signals that this signal reads from */
d: null | ValueSignal[];
/** equals: For value equality */
e: EqualsFunctions;
/** The types that the signal represent, as a bitwise value */
f: SignalFlags;
/** init: The function that we invoke for effects and computeds */
i: () => V;
fn: () => V;
/** references: Anything that a signal owns */
r: null | Reaction[];
/** value: The latest value for this signal */
v: V;
/** write version: used for unowned signals to track if their depdendencies are dirty or not **/
w: number;
}
export interface DerivedDebug<V = unknown> extends Derived<V> {
@ -60,7 +50,7 @@ export interface Effect {
/** The types that the signal represent, as a bitwise value */
f: SignalFlags;
/** init: The function that we invoke for effects and computeds */
i: null | (() => void | (() => void));
fn: null | (() => void | (() => void));
/** deriveds belonging to this effect */
r: null | Derived[];
/** teardown */

@ -67,8 +67,8 @@ import {
import { run } from '../common.js';
import { bind_transition } from './transitions.js';
import { mutable_source, source } from './reactivity/sources.js';
import { safe_equal, safe_not_equal } from './reactivity/equality.js';
import { derived } from './reactivity/deriveds.js';
import { safe_not_equal } from './reactivity/equality.js';
import { derived, derived_safe_equal } from './reactivity/deriveds.js';
/** @type {Set<string>} */
const all_registered_events = new Set();
@ -2448,7 +2448,7 @@ export function prop(props, key, flags, initial) {
// The derived returns the current value. The underlying mutable
// source is written to from various places to persist this value.
var inner_current_value = mutable_source(prop_value);
var current_value = derived(() => {
var current_value = (immutable ? derived : derived_safe_equal)(() => {
var parent_value = getter();
var child_value = get(inner_current_value);
@ -2462,8 +2462,6 @@ export function prop(props, key, flags, initial) {
return (inner_current_value.v = parent_value);
});
if (!immutable) current_value.e = safe_equal;
return function (/** @type {V} */ value, mutation = false) {
var current = get(current_value);

@ -212,8 +212,8 @@ function is_signal_dirty(signal) {
* @param {import('#client').Reaction} signal
* @returns {V}
*/
function execute_signal_fn(signal) {
const init = signal.i;
function execute_reaction(signal) {
const init = signal.fn;
const flags = signal.f;
const previous_dependencies = current_dependencies;
const previous_dependencies_index = current_dependencies_index;
@ -273,10 +273,10 @@ function execute_signal_fn(signal) {
if (!current_skip_consumer) {
for (i = current_dependencies_index; i < dependencies.length; i++) {
const dependency = dependencies[i];
const consumers = dependency.c;
const consumers = dependency.consumers;
if (consumers === null) {
dependency.c = [signal];
dependency.consumers = [signal];
} else if (consumers[consumers.length - 1] !== signal) {
// TODO: should this be:
//
@ -308,14 +308,14 @@ function execute_signal_fn(signal) {
* @returns {void}
*/
function remove_consumer(signal, dependency) {
const consumers = dependency.c;
const consumers = dependency.consumers;
let consumers_length = 0;
if (consumers !== null) {
consumers_length = consumers.length - 1;
const index = consumers.indexOf(signal);
if (index !== -1) {
if (consumers_length === 0) {
dependency.c = null;
dependency.consumers = null;
} else {
// Swap with last element and then remove.
consumers[index] = consumers[consumers_length];
@ -394,7 +394,7 @@ export function execute_effect(effect) {
current_component_context = component_context;
const possible_teardown = execute_signal_fn(effect);
const possible_teardown = execute_reaction(effect);
if (typeof possible_teardown === 'function') {
effect.v = possible_teardown;
@ -660,15 +660,17 @@ export async function tick() {
function update_derived(signal, force_schedule) {
const previous_updating_derived = updating_derived;
updating_derived = true;
const value = execute_signal_fn(signal);
const value = execute_reaction(signal);
updating_derived = previous_updating_derived;
const status =
(current_skip_consumer || (signal.f & UNOWNED) !== 0) && signal.d !== null
? MAYBE_DIRTY
: CLEAN;
set_signal_status(signal, status);
const equals = /** @type {import('#client').EqualsFunctions} */ (signal.e);
if (!equals(value, signal.v)) {
if (!signal.eq(value, signal.v)) {
signal.v = value;
mark_signal_consumers(signal, DIRTY, force_schedule);
@ -772,13 +774,13 @@ export function set(signal, value) {
: '')
);
}
if (
(signal.f & SOURCE) !== 0 &&
!(/** @type {import('#client').EqualsFunctions} */ (signal.e)(value, signal.v))
) {
if (!signal.eq(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
@ -807,6 +809,7 @@ export function set(signal, value) {
}
}
}
mark_signal_consumers(signal, DIRTY, true);
// @ts-expect-error
@ -881,7 +884,7 @@ export function mutate(source, value) {
*/
function mark_signal_consumers(signal, to_status, force_schedule) {
const runes = is_runes(null);
const consumers = signal.c;
const consumers = signal.consumers;
if (consumers !== null) {
const length = consumers.length;

@ -192,13 +192,13 @@ describe('signals', () => {
return () => {
$.flushSync(() => $.set(count, 1));
// Ensure we're not leaking consumers
assert.deepEqual(count.c?.length, 1);
assert.deepEqual(count.consumers?.length, 1);
$.flushSync(() => $.set(count, 2));
// Ensure we're not leaking consumers
assert.deepEqual(count.c?.length, 1);
assert.deepEqual(count.consumers?.length, 1);
$.flushSync(() => $.set(count, 3));
// Ensure we're not leaking consumers
assert.deepEqual(count.c?.length, 1);
assert.deepEqual(count.consumers?.length, 1);
assert.deepEqual(log, [0, 1, 2, 3]);
};
});
@ -258,11 +258,11 @@ describe('signals', () => {
$.flushSync(() => $.set(count, 4));
$.flushSync(() => $.set(count, 0));
// Ensure we're not leaking consumers
assert.deepEqual(count.c?.length, 1);
assert.deepEqual(count.consumers?.length, 1);
assert.deepEqual(log, [0, 2, 'limit', 0]);
$.destroy_signal(effect);
// Ensure we're not leaking consumers
assert.deepEqual(count.c, null);
assert.deepEqual(count.consumers, null);
};
});

Loading…
Cancel
Save