All files / src/lifecycle lifecycle.ts

100% Statements 58/58
100% Branches 14/14
100% Functions 6/6
100% Lines 58/58

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 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 1201x                     1x                   1x 195x 195x 195x 195x 195x   195x   128x 128x 128x   128x 146x 146x 128x 128x 128x 128x 128x     195x 195x 195x 195x   195x 195x   193x 193x   1x   278x 278x 278x 278x   278x 233x 233x 278x           1x 135x 135x                                             1x 4x 4x   3x 3x 3x 3x 3x         1x 5x 5x 5x 3x 3x 2x 2x 2x 2x  
import { devWarning } from '../error';
import type { Context , Disposer } from '../types';
 
type Owner = {
  parent: Owner | null;
  cleanups: Disposer[];
  // NEW: Add a map to store context values for this scope
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  contexts?: Map<symbol, any>;
} | null;
 
let currentOwner: Owner = null;
 
/**
 * Creates a new ownership scope and returns a disposer function for it.
 * All nested effects and resources created within this scope will be cleaned up
 * when the disposer is called.
 *
 * @param fn The function to execute within the new scope.
 * @returns A disposer function to clean up the entire scope.
 */
export function createRoot(fn: (dispose: Disposer) => void): Disposer {
  const parentOwner = currentOwner;
  const root: Owner = {
    parent: parentOwner,
    cleanups: [],
  };
 
  const dispose = () => {
    // Set the owner to this root during cleanup to catch any nested cleanups
    const prevOwner = currentOwner;
    currentOwner = root;
    try {
      // Run cleanups in reverse order to handle dependencies correctly
      for (let i = root.cleanups.length - 1; i >= 0; i--) {
        root.cleanups[i]();
      }
      root.cleanups = [];
    } finally {
      currentOwner = prevOwner;
    }
  };
 
  // Set the current owner for the function execution
  currentOwner = root;
  try {
    fn(dispose);
  } finally {
    // Restore the parent owner
    currentOwner = parentOwner;
  }
 
  return dispose;
}
 
export function onCleanup(fn: Disposer): void {
  // Use devWarning to provide a helpful message
  devWarning(
    !!currentOwner,
    'onCleanup() was called outside of a reactive scope (like createRoot or createEffect). The cleanup function will not be registered.'
  );
 
  if (currentOwner) {
    currentOwner.cleanups.push(fn);
  }
}
 
/**
 * Checks if there is a current reactive owner.
 * @internal
 */
export function hasOwner(): boolean {
  return !!currentOwner;
}
 
/**
 * Retrieves a value from the context hierarchy.
 * It walks up the owner tree to find the nearest provider.
 *
 * @param context The Context object to look up.
 * @returns The value from the nearest Provider, or the context's default value.
 */
// export function useContext<T>(context: Context<T>): T {
//   let owner = currentOwner;
//   while (owner) {
//     if (owner.contexts?.has(context.id)) {
//       return owner.contexts.get(context.id);
//     }
//     owner = owner.parent;
//   }
//   return context.defaultValue;
// }
 
/**
 * Provides a value for a given context within the current scope.
 */
export function provide<T>(context: Context<T>, value: T): void {
  devWarning(!!currentOwner, 'provide() was called outside of a reactive scope.');
  if (!currentOwner) return;
 
  if (!currentOwner.contexts) {
    currentOwner.contexts = new Map();
  }
  currentOwner.contexts.set(context.id, value);
}
 
/**
 * Injects a value from the context hierarchy.
 */
export function inject<T>(context: Context<T>): T | undefined {
  let owner = currentOwner;
  while (owner) {
    if (owner.contexts?.has(context.id)) {
      return owner.contexts.get(context.id);
    }
    owner = owner.parent;
  }
  return context.defaultValue;
}