All files / src compute.ts

100% Statements 18/18
100% Branches 7/7
100% Functions 3/3
100% Lines 17/17

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62                                                      18x     128x 287x 287x   283x   283x     128x 224x 224x   224x 99x 139x       224x 224x     224x 224x         128x    
import { type ReadableLike, type Config, type Get, type OwnedReadable } from "./interface";
import { ReadableImpl } from "./readable";
import { getReadable } from "./utils";
 
export interface ComputeFn<TValue = any> {
  (get: Get): TValue;
}
 
/**
 * Computes a derived value based on other Readables.
 *
 * @category Derivations
 * @param fn - The function that computes the value.
 * @param config - Optional custom {@link Config}.
 * @returns An {@link OwnedReadable} that computes its value based on other Readables.
 *
 * @example
 * ```ts
 * import { compute, writable, reactiveMap } from "@embra/reactivity";
 *
 * const v1$ = writable(0);
 * const v2$ = writable(1);
 * const map$ = reactiveMap([["s", 42]]);
 *
 * const sum$ = compute(get => get(v1$) + get(v2$) + get(map$).get("s") || 0);
 * ```
 */
export const compute = <TValue>(fn: ComputeFn<TValue>, config?: Config<TValue>): OwnedReadable<TValue> => {
  let running: boolean | undefined;
 
  const get: Get = ($?: ReadableLike): unknown => {
    const readable = getReadable($);
    if (!readable) return $;
 
    self.addDep_(readable as ReadableImpl);
 
    return readable.get();
  };
 
  const self = new ReadableImpl(self => {
    const isFirst = !running;
    running = true;
 
    if (isFirst && self.deps_?.size) {
      for (const dep of self.deps_.keys()) {
        self.removeDep_(dep);
      }
    }
 
    try {
      return fn(get);
    } finally {
      /** c8 ignore else -- @preserve */
      if (isFirst) {
        running = false;
      }
    }
  }, config);
 
  return self;
};