All files / common/src/annotation/target annotation-target.impl.ts

59.25% Statements 16/27
28.57% Branches 2/7
50% Functions 2/4
59.25% Lines 16/27

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 120 121 122 123 124 125    10x         10x                 10x 10x 10x       10x                     845x         13x 2x     11x       845x 845x 845x 845x 845x     845x                                                                                                                                              
import { Prototype } from '@aspectjs/common/utils';
import { AnnotationRef } from '../annotation-ref';
import {
  Annotation,
  AnnotationKind,
  AnnotationStub,
} from '../annotation.types';
import { AnnotationContextRegistry } from '../context/registry/annotation-context.registry';
import { AnnotationsSelector } from '../context/registry/selector';
import {
  AnnotationTarget,
  AnnotationTargetRef,
  BaseAnnotationTarget,
  ClassAnnotationTarget,
} from './annotation-target';
 
export const BOUND_INSTANCE_SYMBOL = Symbol.for('@ajs:boundInstance');
export const BOUND_VALUE_SYMBOL = Symbol.for('@ajs:boundValue');
const NOT_BOUND = {};
/**
 * @internal
 */
export abstract class _AnnotationTargetImpl<
  T extends AnnotationKind = AnnotationKind,
  X = unknown,
> implements BaseAnnotationTarget<T, X>
{
  public abstract readonly declaringClass: ClassAnnotationTarget<X>;
  public abstract readonly parentClass:
    | ClassAnnotationTarget<unknown>
    | undefined;
 
  protected readonly [BOUND_INSTANCE_SYMBOL]?: X;
  protected readonly [BOUND_VALUE_SYMBOL]?: unknown = NOT_BOUND;
 
  public readonly ['static']: boolean;
 
  eval(): unknown {
    if (this[BOUND_VALUE_SYMBOL] === NOT_BOUND) {
      throw new Error('AnnotationTarget is not bound to a value');
    }
 
    return this[BOUND_VALUE_SYMBOL];
  }
 
  constructor(
    public readonly kind: T,
    public readonly proto: Prototype<X>,
    public readonly name: string,
    public readonly label: string,
    public readonly ref: AnnotationTargetRef,
    staticAttribute: boolean = false,
  ) {
    this['static'] = staticAttribute;
  }
 
  annotations<
    T extends AnnotationKind,
    S extends AnnotationStub = AnnotationStub,
  >(annotation: S): AnnotationsSelector<T, S>;
  annotations(
    ...annotation: (Annotation | AnnotationRef | string)[]
  ): AnnotationsSelector<AnnotationKind, AnnotationStub>;
  annotations(
    ...annotations: (Annotation | AnnotationRef | string)[]
  ): AnnotationsSelector<AnnotationKind, AnnotationStub> {
    const propertyTarget =
      this as unknown as AnnotationTarget<AnnotationKind.PROPERTY>;
    const methodTarget =
      this as unknown as AnnotationTarget<AnnotationKind.METHOD>;
    const parameterTarget =
      this as unknown as AnnotationTarget<AnnotationKind.PARAMETER>;
 
    // avoid circular dep the dirty way...
    const annotationsReg = (globalThis as any).__reflectContext.get(
      AnnotationContextRegistry,
    );
    switch (this.kind) {
      case AnnotationKind.CLASS:
        return annotationsReg
          .select(...annotations)
          .onClass(this.proto.constructor);
      case AnnotationKind.PROPERTY:
        return annotationsReg
          .select(...annotations)
          .onProperty(this.proto, propertyTarget.propertyKey as any);
      case AnnotationKind.METHOD:
        return annotationsReg
          .select(...annotations)
          .onMethod(methodTarget.proto, methodTarget.propertyKey as any);
      case AnnotationKind.PARAMETER:
        return annotationsReg
          .select(...annotations)
          .onParameter(
            parameterTarget.proto,
            parameterTarget.propertyKey as any,
            parameterTarget.parameterIndex,
          );
 
      default:
        throw new TypeError(`unknown annotation target kind: ${this.kind}`);
    }
  }
  abstract asDecoratorArgs(): any[];
 
  abstract defineMetadata(key: string, value: any): void;
  abstract getMetadata<T>(
    key: string,
    defaultvalue?: (() => T) | undefined,
  ): T | undefined;
 
  toString() {
    return (this as any as AnnotationTarget).label;
  }
 
  /**
   * Binds a value to this AnnotationTarget. The value is either the class instance for ClassAnnotationTarget, the property value for PropertyAnnotationTarget, MethodAnnotationTarget or ParameterAnnotationTarget.
   * In addition, in case of ParameterAnnotationTarget, the bind() method accepts a 2nd argument to bind the parameter value.
   *
   * @param instance The class instance to bind this target to.
   * @param value the value of this target
   */
  abstract _bind(instance: X, args?: unknown[]): AnnotationTarget<T, X>;
}