@@ -22,18 +22,26 @@ import {
2222 getMetadata ,
2323 getParamMetadata ,
2424 isClass ,
25+ isFunction ,
2526 isPrimitiveFunction ,
2627 isUndefined ,
2728 recursiveGetMetadata ,
2829} from './util' ;
29- import { NotFoundError , NoTypeError , NoHandlerError } from './error' ;
30+
31+ import {
32+ NotFoundError ,
33+ NoTypeError ,
34+ NoHandlerError ,
35+ NoIdentifierError ,
36+ InjectionError ,
37+ } from './error' ;
3038
3139export default class Container implements ContainerType {
3240 private registry : Map < Identifier , InjectableMetadata > ;
3341 private tags : Map < string , Set < any > > ;
3442 // @ts -ignore
3543 protected name : string ;
36- protected handlerMap : Map < string , HandlerFunction > ;
44+ protected handlerMap : Map < string | symbol , HandlerFunction > ;
3745
3846 constructor ( name : string ) {
3947 this . name = name ;
@@ -72,40 +80,42 @@ export default class Container implements ContainerType {
7280 }
7381
7482 const { type, id, scope } = this . getDefinedMetaData ( options ) ;
75- const args = getMetadata ( CLASS_CONSTRUCTOR_ARGS , type ) as ReflectMetadataType [ ] ;
76- const props = recursiveGetMetadata ( CLASS_PROPERTY , type ) as ReflectMetadataType [ ] ;
77- const initMethodMd = getMetadata ( CLASS_ASYNC_INIT_METHOD , type ) as ReflectMetadataType ;
78- const handlerArgs = getMetadata ( INJECT_HANDLER_ARGS , type ) as ReflectMetadataType [ ] ;
79- const handlerProps = recursiveGetMetadata (
80- INJECT_HANDLER_PROPS ,
81- type
82- ) as ReflectMetadataType [ ] ;
83-
8483 const md : InjectableMetadata = {
8584 ...options ,
8685 id,
8786 type,
8887 scope,
89- constructorArgs : ( args ?? [ ] ) . concat ( handlerArgs ?? [ ] ) ,
90- properties : ( props ?? [ ] ) . concat ( handlerProps ?? [ ] ) ,
91- initMethod : initMethodMd ?. propertyName ?? 'init' ,
9288 } ;
89+ if ( type ) {
90+ const args = getMetadata ( CLASS_CONSTRUCTOR_ARGS , type ) as ReflectMetadataType [ ] ;
91+ const props = recursiveGetMetadata ( CLASS_PROPERTY , type ) as ReflectMetadataType [ ] ;
92+ const initMethodMd = getMetadata ( CLASS_ASYNC_INIT_METHOD , type ) as ReflectMetadataType ;
93+ const handlerArgs = getMetadata ( INJECT_HANDLER_ARGS , type ) as ReflectMetadataType [ ] ;
94+ const handlerProps = recursiveGetMetadata (
95+ INJECT_HANDLER_PROPS ,
96+ type
97+ ) as ReflectMetadataType [ ] ;
98+
99+ md . constructorArgs = ( args ?? [ ] ) . concat ( handlerArgs ?? [ ] ) ;
100+ md . properties = ( props ?? [ ] ) . concat ( handlerProps ?? [ ] ) ;
101+ md . initMethod = initMethodMd ?. propertyName ?? 'init' ;
102+ /**
103+ * compatible with inject type identifier when identifier is string
104+ */
105+ if ( md . id !== type ) {
106+ md [ MAP_TYPE ] = type ;
107+ this . registry . set ( type , md ) ;
108+ }
93109
94- /**
95- * compatible with inject type identifier when identifier is string
96- */
97- if ( md . id !== type ) {
98- md [ MAP_TYPE ] = type ;
99- this . registry . set ( type , md ) ;
110+ this . handleTag ( type ) ;
100111 }
101- this . registry . set ( md . id , md ) ;
102112
113+ this . registry . set ( md . id , md ) ;
103114 if ( md . eager && md . scope !== ScopeEnum . TRANSIENT ) {
115+ // TODO: handle async
104116 this . get ( md . id ) ;
105117 }
106118
107- this . handleTag ( type ) ;
108-
109119 return this ;
110120 }
111121
@@ -128,11 +138,11 @@ export default class Container implements ContainerType {
128138 return Promise . all ( clazzes . map ( clazz => this . getAsync ( clazz ) ) ) ;
129139 }
130140
131- public registerHandler ( name : string , handler : HandlerFunction ) {
141+ public registerHandler ( name : string | symbol , handler : HandlerFunction ) {
132142 this . handlerMap . set ( name , handler ) ;
133143 }
134144
135- public getHandler ( name : string ) {
145+ public getHandler ( name : string | symbol ) {
136146 return this . handlerMap . get ( name ) ;
137147 }
138148
@@ -146,10 +156,18 @@ export default class Container implements ContainerType {
146156 if ( ! isUndefined ( md . value ) ) {
147157 return md . value ;
148158 }
149- const clazz = md . type ! ;
150- const params = this . resolveParams ( clazz , md . constructorArgs ) ;
151- const value = new clazz ( ...params ) ;
152- this . handleProps ( value , md . properties ?? [ ] ) ;
159+ let value ;
160+ if ( md . factory ) {
161+ value = md . factory ( md . id , this ) ;
162+ }
163+
164+ if ( ! value && md . type ) {
165+ const clazz = md . type ! ;
166+ const params = this . resolveParams ( clazz , md . constructorArgs ) ;
167+ value = new clazz ( ...params ) ;
168+ this . handleProps ( value , md . properties ?? [ ] ) ;
169+ }
170+
153171 if ( md . scope === ScopeEnum . SINGLETON ) {
154172 md . value = value ;
155173 }
@@ -160,10 +178,18 @@ export default class Container implements ContainerType {
160178 if ( ! isUndefined ( md . value ) ) {
161179 return md . value ;
162180 }
163- const clazz = md . type ! ;
164- const params = await this . resolveParamsAsync ( clazz , md . constructorArgs ) ;
165- const value = new clazz ( ...params ) ;
166- await this . handlePropsAsync ( value , md . properties ?? [ ] ) ;
181+ let value ;
182+ if ( md . factory ) {
183+ value = await md . factory ( md . id , this ) ;
184+ }
185+
186+ if ( ! value && md . type ) {
187+ const clazz = md . type ! ;
188+ const params = await this . resolveParamsAsync ( clazz , md . constructorArgs ) ;
189+ value = new clazz ( ...params ) ;
190+ await this . handlePropsAsync ( value , md . properties ?? [ ] ) ;
191+ }
192+
167193 if ( md . scope === ScopeEnum . SINGLETON ) {
168194 md . value = value ;
169195 }
@@ -179,26 +205,36 @@ export default class Container implements ContainerType {
179205 }
180206
181207 private getDefinedMetaData ( options : Partial < InjectableDefinition > ) : {
182- type : Constructable ;
183208 id : Identifier ;
184209 scope : ScopeEnum ;
210+ type ?: Constructable | null ;
185211 } {
186- let type = options . type ;
212+ let { type, id , scope = ScopeEnum . SINGLETON , factory } = options ;
187213 if ( ! type ) {
188- if ( options . id && isClass ( options . id ) ) {
189- type = options . id as Constructable ;
214+ if ( id && isClass ( id ) ) {
215+ type = id as Constructable ;
190216 }
191217 }
192218
193- if ( ! type ) {
194- throw new NoTypeError ( 'type is required' ) ;
219+ if ( ! type && ! factory ) {
220+ throw new NoTypeError ( `injectable ${ id ?. toString ( ) } ` ) ;
195221 }
196222
197- const targetMd = ( getMetadata ( CLASS_CONSTRUCTOR , type ) as ReflectMetadataType ) || { } ;
198- const id = targetMd . id ?? options . id ?? type ;
199- const scope = targetMd . scope ?? options . scope ?? ScopeEnum . SINGLETON ;
223+ if ( factory && ! isFunction ( factory ) ) {
224+ throw new InjectionError ( 'factory option must be function' ) ;
225+ }
200226
201- return { type, id, scope } ;
227+ if ( type ) {
228+ const targetMd = ( getMetadata ( CLASS_CONSTRUCTOR , type ) as ReflectMetadataType ) || { } ;
229+ id = targetMd . id ?? id ?? type ;
230+ scope = targetMd . scope ?? scope ;
231+ }
232+
233+ if ( ! id && factory ) {
234+ throw new NoIdentifierError ( `injectable with factory option` ) ;
235+ }
236+
237+ return { type, id : id ! , scope } ;
202238 }
203239
204240 private resolveParams ( clazz : any , args ?: ReflectMetadataType [ ] ) : any [ ] {
@@ -280,12 +316,13 @@ export default class Container implements ContainerType {
280316 } ) ;
281317 }
282318
283- private resolveHandler ( handlerName : string , id ?: Identifier ) : any {
319+ private resolveHandler ( handlerName : string | symbol , id ?: Identifier ) : any {
284320 const handler = this . getHandler ( handlerName ) ;
285321
286322 if ( ! handler ) {
287- throw new NoHandlerError ( handlerName ) ;
323+ throw new NoHandlerError ( handlerName . toString ( ) ) ;
288324 }
289- return handler ( id , this ) ;
325+
326+ return id ? handler ( id , this ) : handler ( this ) ;
290327 }
291328}
0 commit comments