All files / core/src/utility singleton.ts

100% Statements 59/59
78.94% Branches 15/19
80% Functions 4/5
100% Lines 59/59

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 601x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 371x 371x 371x 371x 371x 371x 371x 371x 371x 371x 371x 371x 371x 371x 706x 447x 447x 447x 447x 348x 348x 246x 259x 706x 371x 371x 371x 371x 371x 371x 371x 371x  
/**
 * Class decorator factory. Enforces a transparent **Singleton** pattern on the
 * decorated class. When calling the `new` operator on a decorated class for the
 * first time, an instance of the decorated class is created using the supplied
 * arguments, if any. This instance will remain the **Singleton** instance of
 * the decorated class indefinitely. When calling the `new` operator on a
 * decorated class already instantiated, the **Singleton** pattern is enforced
 * and the previously constructed instance is returned. Instead, if provided,
 * the `apply` callback is fired with the **Singleton** instance and the `new`
 * invocation parameters.
 *
 * @param apply - The callback to `apply` on subsequent `new` invocations.
 * @typeParam T - The type of the decorated constructor.
 * @returns A class constructor decorator.
 *
 * @example
 * **Singleton** class:
 * ```ts
 * import { Singleton } from '@sgrud/core';
 *
 * ⁠@Singleton()
 * export class Service {}
 *
 * new Service() === new Service(); // true
 * ```
 */
export function Singleton<T extends new (...args: any[]) => any>(
  apply?: (
    self: InstanceType<T>,
    args: ConstructorParameters<T>
  ) => InstanceType<T>
) {
 
  /**
   * @param constructor - The class `constructor` to be decorated.
   * @returns The decorated class `constructor`.
   */
  return function(constructor: T): T {
    class Class extends constructor {
 
      public constructor(...args: any[]) {
        if (!self) {
          super(...args);
          self = this;
        } else {
          return apply?.(
            self as InstanceType<T>,
            args as ConstructorParameters<T>
          ) || self;
        }
      }
 
    }
 
    let self: Class;
    return Class;
  };
 
}