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 */ const initDependencyMetadata = (target: Object): Collection => { const param_types = Reflect.getMetadata('design:paramtypes', target) return collect(param_types).map((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() 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 }