-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #46 from kitesjs/dev-ioc
Dev ioc
- Loading branch information
Showing
40 changed files
with
1,141 additions
and
314 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import * as interfaces from '../interfaces'; | ||
|
||
export const TargetTypeEnum: interfaces.TargetTypeEnum = { | ||
ClassProperty: 'ClassProperty', | ||
ConstructorArgument: 'ConstructorArgument', | ||
Variable: 'Variable' | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
export const STACK_OVERFLOW = 'Maximum call stack size exceeded'; | ||
export const DUPLICATED_INJECTABLE_DECORATOR = 'Duplicated injectable decorator'; | ||
export const DUPLICATED_METADATA = 'Metadata key was used more than once in a parameter'; | ||
export const NULL_ARGUMENT = 'NULL argument'; | ||
export const KEY_NOT_FOUND = 'Key Not Found'; | ||
export const MISSING_INJECTABLE_ANNOTATION = 'Missing required @Injectable annotation in:'; | ||
export const MISSING_INJECT_ANNOTATION = 'Missing required @Inject or @MultiInject annotation in:'; | ||
|
||
export const UNDEFINED_INJECT_ANNOTATION = (name: string) => | ||
`@Inject called with undefined this could mean that the class ${name} has ` + | ||
'a circular dependency problem. You can use a LazyServiceIdentifer to ' + | ||
'overcome this limitation.'; | ||
|
||
export const INVALID_DECORATOR_OPERATION = 'The @Inject @Tagged and @Named decorators ' + | ||
'must be applied to the parameters of a class constructor or a class property.'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export * from './metadata.keys'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
export const METADATA = { | ||
CONTROLLERS: 'controllers', | ||
EXPORTS: 'exports', | ||
IMPORTS: 'imports', | ||
PROVIDERS: 'providers', | ||
}; | ||
|
||
export const PATH_METADATA = 'path'; | ||
export const METHOD_METADATA = 'method'; | ||
export const RENDER_METADATA = '__renderTemplate__'; | ||
|
||
// Used for named bindings | ||
export const NAMED_TAG = 'named'; | ||
|
||
// The name of the target at design time | ||
export const NAME_TAG = 'name'; | ||
|
||
// The for unmanaged injections (in base classes when using inheritance) | ||
export const UNMANAGED_TAG = 'unmanaged'; | ||
|
||
// The type of the binding at design time | ||
export const INJECT_TAG = 'inject'; | ||
|
||
// The type of the binding at design type for multi-injections | ||
export const MULTI_INJECT_TAG = 'multi_inject'; | ||
|
||
// used to store constructor arguments tags | ||
export const TAGGED = 'kites:tagged'; | ||
|
||
// used to store class properties tags | ||
export const TAGGED_PROP = 'kites:tagged_props'; | ||
|
||
// used to access design time types | ||
export const DESIGN_PARAM_TYPES = 'design:paramtypes'; | ||
|
||
// used to store types to be injected | ||
export const PARAM_TYPES = 'kites:paramtypes'; | ||
|
||
export const INJECT_METADATA_KEY = Symbol('INJECT_KEY'); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,35 +1,84 @@ | ||
import 'reflect-metadata'; | ||
|
||
import { expect } from 'chai'; | ||
import { INJECT_METADATA_KEY, SELF_DECLARED_DEPS_METADATA } from '../../constants'; | ||
import { UNDEFINED_INJECT_ANNOTATION } from '../../constants/error.messages'; | ||
import * as METADATA_KEY from '../../constants/metadata.keys'; | ||
import { InjectionToken } from '../../interfaces'; | ||
import { Inject } from './inject.decorator'; | ||
import * as interfaces from '../../interfaces'; | ||
import { Decorate } from '../decorate'; | ||
import { Inject, LazyServiceIdentifer } from './inject.decorator'; | ||
|
||
describe('@Inject', () => { | ||
const USER_STRING_TOKEN = new InjectionToken('user-identifier'); | ||
|
||
class ABasicClass { | ||
class PingService { | ||
constructor(public x: number) { } | ||
} | ||
|
||
class ServiceTest { | ||
class DomainService { } | ||
class DiagnosticService { } | ||
|
||
const lazyDiagnosticId = new LazyServiceIdentifer(() => 'Diagnostic'); | ||
|
||
class DecoratedServiceTest { | ||
constructor( | ||
@Inject(USER_STRING_TOKEN) param1, | ||
@Inject(ABasicClass) param2, | ||
// @Inject('Test') param3, | ||
@Inject(PingService) private svPing: PingService, | ||
@Inject('Domain') private svDomain: DomainService, | ||
@Inject(lazyDiagnosticId) private svDiagnostic: DiagnosticService, | ||
) { } | ||
} | ||
|
||
it('should enhance class with expected constructor params metadata', () => { | ||
const metadata = Reflect.getMetadata(INJECT_METADATA_KEY, ServiceTest); | ||
class InvalidDecoratorUsageService { | ||
private svDomain: DomainService; | ||
private svPing: PingService; | ||
|
||
constructor( | ||
svDomain: DomainService, | ||
svPing: PingService, | ||
) { | ||
this.svDomain = svDomain; | ||
this.svPing = svPing; | ||
} | ||
} | ||
|
||
it('should enhance class with expected constructor params metadata using named parameters', () => { | ||
const metadata = Reflect.getMetadata(METADATA_KEY.TAGGED, DecoratedServiceTest); | ||
|
||
const expectedMetadata = [ | ||
{ index: 1, param: USER_STRING_TOKEN.injectionIdentifier }, | ||
{ index: 0, param: ABasicClass.name }, | ||
]; | ||
expect(metadata).to.be.an('object'); | ||
|
||
// assert metadata for first argument | ||
expect(metadata['0']).to.be.instanceOf(Array); | ||
const arg1: interfaces.Metadata = metadata['0'][0]; | ||
expect(arg1.key).to.be.eq(METADATA_KEY.INJECT_TAG); | ||
expect(arg1.value).to.be.eql(PingService); | ||
expect(metadata['0'][1]).to.be.eq(undefined); | ||
|
||
// assert metadata for second argument | ||
expect(metadata['1']).to.be.instanceOf(Array); | ||
const arg2: interfaces.Metadata = metadata['1'][0]; | ||
expect(arg2.key).to.be.eq(METADATA_KEY.INJECT_TAG); | ||
expect(arg2.value).to.be.eql('Domain'); | ||
expect(metadata['1'][1]).to.be.eq(undefined); | ||
|
||
// assert metadata for third argument | ||
expect(metadata['2']).to.be.instanceOf(Array); | ||
const arg3: interfaces.Metadata = metadata['2'][0]; | ||
expect(arg3.key).to.be.eq(METADATA_KEY.INJECT_TAG); | ||
expect(arg3.value).to.be.eql(lazyDiagnosticId); | ||
expect(metadata['2'][1]).to.be.eq(undefined); | ||
|
||
// no more metadata should be available | ||
expect(metadata['3']).to.be.eq(undefined); | ||
|
||
}); | ||
|
||
// console.log('AAAAA', metadata, '123', expectedMetadata, '456'); | ||
it('should throw when applied with undefined token', () => { | ||
// this can be happen when there is a circluar dependency between tokens | ||
const useDecoratorWithUndefinedToken = function () { | ||
Decorate(Inject(undefined as any) as any, InvalidDecoratorUsageService, 0); | ||
}; | ||
|
||
// expect(metadata, 'Get metadata of ServiceTest').to.be.eql(expectedMetadata); | ||
const errMsg = `${UNDEFINED_INJECT_ANNOTATION('InvalidDecoratorUsageService')}`; | ||
expect(useDecoratorWithUndefinedToken).to.throw(errMsg); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,41 @@ | ||
import { INJECT_METADATA_KEY } from '../../constants'; | ||
import { UNDEFINED_INJECT_ANNOTATION } from '../../constants/error.messages'; | ||
import * as METADATA_KEY from '../../constants/metadata.keys'; | ||
import { Token } from '../../interfaces/provider.interface'; | ||
import { Metadata } from '../metadata'; | ||
import { tagParameter, tagProperty } from './tag.decorator'; | ||
|
||
export function Inject(token: Token<any>) { | ||
return function (target: any, _: string | symbol, index: number) { | ||
Reflect.defineMetadata(INJECT_METADATA_KEY, token, target, `index-${index}`); | ||
export type TokenIdentifierOrFunc = Token<any> | LazyServiceIdentifer; | ||
|
||
export class LazyServiceIdentifer<T = any> { | ||
private _cb: () => Token<T>; | ||
public constructor(cb: () => Token<T>) { | ||
this._cb = cb; | ||
} | ||
|
||
public unwrap() { | ||
return this._cb(); | ||
} | ||
} | ||
|
||
export function Inject(token: TokenIdentifierOrFunc) { | ||
return function (target: any, targetKey: string, index?: number) { | ||
|
||
if (token === undefined) { | ||
throw new Error(UNDEFINED_INJECT_ANNOTATION(target.name)); | ||
} | ||
|
||
// Reflect.defineMetadata(INJECT_METADATA_KEY, token, target, `index-${index}`); | ||
const metadata = new Metadata(METADATA_KEY.INJECT_TAG, token); | ||
|
||
if (typeof index === 'number') { | ||
tagParameter(target, targetKey, index, metadata); | ||
} else { | ||
tagProperty(target, targetKey, metadata); | ||
} | ||
return target; | ||
}; | ||
} | ||
|
||
export function getInjectionToken(target: any, index: number) { | ||
return Reflect.getMetadata(INJECT_METADATA_KEY, target, `index-${index}`) as Token<any> | undefined; | ||
} | ||
// export function getInjectionToken(target: any, index: number) { | ||
// return Reflect.getMetadata(INJECT_METADATA_KEY, target, `index-${index}`) as Token<any> | undefined; | ||
// } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.