|
|
|
@ -2,16 +2,16 @@ import 'reflect-metadata'
|
|
|
|
|
import {collect, Collection} from '../../util'
|
|
|
|
|
import {logIfDebugging} from '../../util/support/debug'
|
|
|
|
|
import {
|
|
|
|
|
DEPENDENCY_KEYS_METADATA_KEY,
|
|
|
|
|
DEPENDENCY_KEYS_SERVICE_TYPE_KEY,
|
|
|
|
|
DependencyKey,
|
|
|
|
|
DependencyRequirement,
|
|
|
|
|
DEPENDENCY_KEYS_METADATA_KEY,
|
|
|
|
|
DEPENDENCY_KEYS_PROPERTY_METADATA_KEY,
|
|
|
|
|
isInstantiable,
|
|
|
|
|
InjectionType,
|
|
|
|
|
DEPENDENCY_KEYS_SERVICE_TYPE_KEY,
|
|
|
|
|
isInstantiable,
|
|
|
|
|
PropertyDependency,
|
|
|
|
|
} from '../types'
|
|
|
|
|
import {ContainerBlueprint} from '../ContainerBlueprint'
|
|
|
|
|
import {propertyInjectionMetadata} from './propertyInjectionMetadata'
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get a collection of dependency requirements for the given target object.
|
|
|
|
@ -67,6 +67,7 @@ export const Injectable = (): ClassDecorator => {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Mark the given class property to be injected by the container.
|
|
|
|
|
* If a `key` is specified, that DependencyKey will be injected.
|
|
|
|
@ -77,10 +78,27 @@ export const Injectable = (): ClassDecorator => {
|
|
|
|
|
*/
|
|
|
|
|
export const Inject = (key?: DependencyKey, { debug = false } = {}): PropertyDecorator => {
|
|
|
|
|
return (target, property) => {
|
|
|
|
|
let propertyMetadata = Reflect.getMetadata(DEPENDENCY_KEYS_PROPERTY_METADATA_KEY, target?.constructor || target) as Collection<PropertyDependency>
|
|
|
|
|
if ( !target?.constructor ) {
|
|
|
|
|
logIfDebugging('extollo.di.decoration', '[DEBUG] @Inject(): target has no constructor', target)
|
|
|
|
|
throw new Error('Unable to define property injection: target has no constructor. Enable `extollo.di.decoration` logging to debug')
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const propertyTarget = target.constructor
|
|
|
|
|
|
|
|
|
|
// let propertyMetadata = Reflect.getMetadata(DEPENDENCY_KEYS_PROPERTY_METADATA_KEY, propertyTarget) as Collection<PropertyDependency>
|
|
|
|
|
// Okay, this is a little fucky. We can't use Reflect's metadata capabilities because we need to write the metadata to
|
|
|
|
|
// the constructor, not the `target`. Because Reflect is using the prototype to store data, defining a metadata key on the constructor
|
|
|
|
|
// will define it for its parent constructors as well.
|
|
|
|
|
// So, if you have class A, class B extends A, and class C extends A, the properties for B and C will be defined on A, causing
|
|
|
|
|
// BOTH B and C's properties to be injected on any class extending A.
|
|
|
|
|
// To get around this, we instead define a custom property on the constructor itself, then use hasOwnProperty to make sure we're not
|
|
|
|
|
// getting the one for the parent class via the prototype chain.
|
|
|
|
|
let propertyMetadata = Object.prototype.hasOwnProperty.call(propertyTarget, propertyInjectionMetadata) ?
|
|
|
|
|
(propertyTarget as any)[propertyInjectionMetadata] as Collection<PropertyDependency> : undefined
|
|
|
|
|
|
|
|
|
|
if ( !propertyMetadata ) {
|
|
|
|
|
propertyMetadata = new Collection<PropertyDependency>()
|
|
|
|
|
Reflect.defineMetadata(DEPENDENCY_KEYS_PROPERTY_METADATA_KEY, propertyMetadata, target?.constructor || target)
|
|
|
|
|
;(propertyTarget as any)[propertyInjectionMetadata] = propertyMetadata
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const type = Reflect.getMetadata('design:type', target, property)
|
|
|
|
@ -103,7 +121,7 @@ export const Inject = (key?: DependencyKey, { debug = false } = {}): PropertyDec
|
|
|
|
|
|
|
|
|
|
logIfDebugging('extollo.di.decoration', '[DEBUG] @Inject() - key:', key, 'property:', property, 'target:', target, 'target constructor:', target?.constructor, 'type:', type)
|
|
|
|
|
|
|
|
|
|
Reflect.defineMetadata(DEPENDENCY_KEYS_PROPERTY_METADATA_KEY, propertyMetadata, target?.constructor || target)
|
|
|
|
|
;(propertyTarget as any)[propertyInjectionMetadata] = propertyMetadata
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|