You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
90 lines
3.2 KiB
90 lines
3.2 KiB
import {Reflect} from '../../../lib/src/external/reflect.ts'
|
|
import {DEPENDENCY_KEYS_METADATA_KEY, DependencyKey} from '../type/DependencyKey.ts'
|
|
import {collect, Collection} from '../../../lib/src/collection/Collection.ts'
|
|
import {DependencyRequirement} from '../type/DependencyRequirement.ts'
|
|
|
|
/**
|
|
* Get a collection of dependency requirements for the given target object.
|
|
* @param {Object} target
|
|
* @return Collection<DependencyRequirement>
|
|
*/
|
|
const initDependencyMetadata = (target: Object): Collection<DependencyRequirement> => {
|
|
const param_types = Reflect.getMetadata('design:paramtypes', target)
|
|
return collect<DependencyKey>(param_types).map<DependencyRequirement>((type, index) => {
|
|
return {
|
|
param_index: index,
|
|
key: type,
|
|
overridden: false,
|
|
}
|
|
})
|
|
}
|
|
|
|
/**
|
|
* Class decorator that marks a class as injectable. When this is applied, dependency
|
|
* metadata for the constructors params is resolved and stored in metadata.
|
|
* @constructor
|
|
*/
|
|
const Injectable = (): ClassDecorator => {
|
|
return (target) => {
|
|
const meta = initDependencyMetadata(target)
|
|
const existing = Reflect.getMetadata(DEPENDENCY_KEYS_METADATA_KEY, target)
|
|
const new_meta = new Collection<DependencyRequirement>()
|
|
|
|
if ( existing ) {
|
|
const max_new = meta.max('param_index')
|
|
const max_existing = existing.max('param_index')
|
|
for ( let i = 0; i <= Math.max(max_new, max_existing); i++ ) {
|
|
const existing_dr = existing.firstWhere('param_index', '=', i)
|
|
const new_dr = meta.firstWhere('param_index', '=', i)
|
|
|
|
if ( existing_dr && !new_dr ) {
|
|
new_meta.push(existing_dr)
|
|
} else if ( new_dr && !existing_dr ) {
|
|
new_meta.push(new_dr)
|
|
} else if ( new_dr && existing_dr ) {
|
|
if ( existing_dr.overridden ) {
|
|
new_meta.push(existing_dr)
|
|
} else {
|
|
new_meta.push(new_dr)
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
new_meta.concat(meta)
|
|
}
|
|
|
|
Reflect.defineMetadata(DEPENDENCY_KEYS_METADATA_KEY, new_meta, target)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Parameter decorator to manually mark a parameter as being an injection target on injectable
|
|
* classes. This can be used to override the dependency key of a given parameter.
|
|
* @param {DependencyKey} key
|
|
* @constructor
|
|
*/
|
|
const Inject = (key: DependencyKey): ParameterDecorator => {
|
|
return (target, property, param_index) => {
|
|
if ( !Reflect.getMetadata(DEPENDENCY_KEYS_METADATA_KEY, target) ) {
|
|
Reflect.defineMetadata(DEPENDENCY_KEYS_METADATA_KEY, initDependencyMetadata(target), target)
|
|
}
|
|
|
|
const meta = Reflect.getMetadata(DEPENDENCY_KEYS_METADATA_KEY, target)
|
|
const req = meta.firstWhere('param_index', '=', param_index)
|
|
if ( req ) {
|
|
req.key = key
|
|
req.overridden = true
|
|
} else {
|
|
meta.push({
|
|
param_index,
|
|
key,
|
|
overridden: true
|
|
})
|
|
}
|
|
|
|
Reflect.defineMetadata(DEPENDENCY_KEYS_METADATA_KEY, meta, target)
|
|
}
|
|
}
|
|
|
|
export { Inject, Injectable, initDependencyMetadata }
|