All files / src/decorators factory.decorator.mts

85.71% Statements 6/7
91.66% Branches 11/12
100% Functions 2/2
85.71% Lines 6/7

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                                                                                                              16x               16x                   16x   16x     16x   16x        
import type { Factorable, FactorableWithArgs } from '../interfaces/index.mjs'
import type {
  ClassTypeWithInstance,
  InjectionTokenSchemaType,
} from '../token/injection-token.mjs'
import type { Registry } from '../token/registry.mjs'
 
import { InjectableScope, InjectableType } from '../enums/index.mjs'
import { InjectableTokenMeta } from '../symbols/index.mjs'
import { InjectionToken } from '../token/injection-token.mjs'
import { globalRegistry } from '../token/registry.mjs'
 
export interface FactoryOptions {
  scope?: InjectableScope
  token?: InjectionToken<any, any>
  registry?: Registry
  priority?: number
}
 
// #1 Factory without arguments
export function Factory<R>(options?: {
  scope?: InjectableScope
  registry?: Registry
  priority?: number
}): <T extends ClassTypeWithInstance<Factorable<R>>>(
  target: T,
  context?: ClassDecoratorContext,
) => T
 
// #2 Factory with typed token
export function Factory<R, S>(options: {
  scope?: InjectableScope
  token: InjectionToken<R, S>
  registry?: Registry
  priority?: number
}): R extends undefined
  ? never
  : S extends InjectionTokenSchemaType
    ? <T extends ClassTypeWithInstance<FactorableWithArgs<R, S>>>(
        target: T,
        context?: ClassDecoratorContext,
      ) => T
    : S extends undefined
      ? <T extends ClassTypeWithInstance<Factorable<R>>>(
          target: T,
          context?: ClassDecoratorContext,
        ) => T
      : never
 
export function Factory({
  scope = InjectableScope.Singleton,
  token,
  registry = globalRegistry,
  priority = 0,
}: FactoryOptions = {}) {
  return <
    T extends ClassTypeWithInstance<
      Factorable<any> | FactorableWithArgs<any, any>
    >,
  >(
    target: T,
    context?: ClassDecoratorContext,
  ): T => {
    Iif (
      (context && context.kind !== 'class') ||
      (target instanceof Function && !context)
    ) {
      throw new Error(
        '[DI] @Factory decorator can only be used on classes.',
      )
    }
 
    let injectableToken: InjectionToken<any, any> =
      token ?? InjectionToken.create(target)
 
    registry.set(injectableToken, scope, target, InjectableType.Factory, priority)
 
    // @ts-expect-error
    target[InjectableTokenMeta] = injectableToken
 
    return target
  }
}