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

81.81% Statements 27/33
75% Branches 6/8
60% Functions 6/10
81.81% Lines 27/33

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 11410x               10x 10x       10x           10x   10x                 149x                                               1160x   1160x 1160x 1160x         134x                       834x       62x 18x     44x   44x   35x     9x   9x           9x 9x       15x       15x 15x 15x 15x      
import {
  ConstructorType,
  Prototype,
  assert,
  defineMetadata,
  getMetadata,
  getPrototype,
} from '@aspectjs/common/utils';
import { AnnotationKind } from '../../annotation.types';
import {
  AnnotationTargetRef,
  ClassAnnotationTarget,
} from '../annotation-target';
import {
  BOUND_INSTANCE_SYMBOL,
  BOUND_VALUE_SYMBOL,
  _AnnotationTargetImpl,
} from '../annotation-target.impl';
 
let _globalTargetId = 0;
 
export class _ClassAnnotationTargetImpl<X = unknown>
  extends _AnnotationTargetImpl<AnnotationKind.CLASS, X>
  implements ClassAnnotationTarget<X>
{
  protected declare [BOUND_INSTANCE_SYMBOL]?: X;
  protected declare [BOUND_VALUE_SYMBOL]?: X;
  private _parentClass?: ClassAnnotationTarget;
 
  private constructor(proto: Prototype<X>, ref: AnnotationTargetRef) {
    super(
      AnnotationKind.CLASS,
      proto,
      proto.constructor.name,
      `class ${proto.constructor.name}`,
      ref,
    );
  }
 
  get value() {
    return this.proto;
  }
 
  override defineMetadata(key: string, value: any): void {
    defineMetadata(key, value, this.proto);
  }
  override getMetadata<T>(
    key: string,
    defaultvalue?: (() => T) | undefined,
  ): T {
    return getMetadata(key, this.proto, defaultvalue);
  }
 
  static of<X>(decoree: ConstructorType<X>) {
    assert(typeof decoree === 'function');
 
    const proto = getPrototype(decoree);
    const ref = `c[${decoree.name}]`;
    return getMetadata(
      `@ajs:tgrf`,
      proto,
      ref,
      () =>
        new _ClassAnnotationTargetImpl<X>(
          proto,
          new AnnotationTargetRef(`${ref}#${_globalTargetId++}`),
        ),
    );
  }
 
  asDecoratorArgs() {
    return [this.proto.constructor];
  }
 
  public get declaringClass() {
    return this;
  }
 
  override get parentClass(): ClassAnnotationTarget | undefined {
    if (this._parentClass) {
      return this._parentClass;
    }
 
    const parentProto = Object.getPrototypeOf(this.proto);
 
    if (!parentProto || parentProto === Object.prototype) {
      // no parent
      return undefined;
    }
 
    const parentClass = _ClassAnnotationTargetImpl.of(parentProto.constructor);
 
    Iif (parentClass && typeof this[BOUND_INSTANCE_SYMBOL] !== 'undefined') {
      return (parentClass as _ClassAnnotationTargetImpl)._bind(
        this[BOUND_INSTANCE_SYMBOL]!,
      );
    }
 
    this._parentClass = parentClass;
    return parentClass;
  }
 
  override _bind(instance: X): ClassAnnotationTarget<X> {
    Iif (this[BOUND_INSTANCE_SYMBOL] === instance) {
      return this;
    }
 
    const bound = new _ClassAnnotationTargetImpl(this.proto, this.ref);
    bound[BOUND_INSTANCE_SYMBOL] = instance;
    bound[BOUND_VALUE_SYMBOL] = instance;
    return bound;
  }
}