Skip to content

Commit

Permalink
feat(core/provider): allow to register pure services without injectio…
Browse files Browse the repository at this point in the history
…ns and check if @Inject is used without @Injectable decorator

- add better error messages
  • Loading branch information
Hotell committed Jan 14, 2016
1 parent d689b64 commit 8e04c2e
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 7 deletions.
52 changes: 45 additions & 7 deletions src/core/di/provider.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {
Type,
isFunction,
isString,
isBlank,
isType,
Expand Down Expand Up @@ -41,7 +42,11 @@ class ProviderBuilder{
if ( type instanceof OpaqueToken ) {

if(isBlank(useValue)){
throw new Error(`you must provide useValue when registering constant/value via OpaqueToken`);
throw new Error(`
Provider registration: "${ type.desc }":
=======================================================
you must provide useValue when registering constant/value via OpaqueToken
`);
}
return [
type.desc,
Expand All @@ -67,18 +72,47 @@ class ProviderBuilder{
: '';

if ( !isType( injectableType ) ) {
throw new Error( `token ${stringify( injectableType )} must be type of Type, You cannot provide non class` );

throw new Error( `
Provider registration: "${stringify( injectableType )}":
=======================================================
token ${ stringify( injectableType ) } must be type of Type, You cannot provide non class
` );

}

injectableType.$inject = _dependenciesFor( injectableType );

const [annotation] = reflector.annotations( injectableType );
const [paramMetadata] = reflector.parameters( injectableType );

if ( isBlank( annotation ) && isPresent( paramMetadata ) ) {

throw new Error( `
Provider registration: "${ stringify(injectableType) }":
=======================================================
cannot create appropriate construct from provided Type.
-> you are injecting to ${ stringify(injectableType) } service, but you're missing @Injectable() decorator
` );

}

// @NOTE:
// this can get confusing if user forgets to provide Annotation @Directive/@Component/@Pipe and has no @Inject
// it will register Service
//
// register service if it doesn't have any @Inject ables
if ( isBlank( annotation ) && isFunction( injectableType ) ) {
return ProviderBuilder._provideService(injectableType,overrideName);
}

if ( isBlank( annotation ) ) {

throw new Error( `
Provider registration: "${ stringify(injectableType) }":
=======================================================
cannot create appropriate construct from provided Type.
-> Type must be on of (@Pipe(),@Component(),@Directive(),@Injectable())
-> Type "${ stringify(injectableType) }" must be on of [ @Pipe(), @Component(), @Directive() ]
` );

}
Expand All @@ -92,13 +126,17 @@ class ProviderBuilder{
}

if ( annotation instanceof InjectableMetadata ) {
return [
overrideName || getTypeName( injectableType ),
injectableType
];
return ProviderBuilder._provideService(injectableType,overrideName);
}

}

private static _provideService( injectableType: Type, overriddenName?: string ): [string,Type] {
return [
overriddenName || getTypeName( injectableType ),
injectableType
]
}
}
/**
* should extract the string token from provided Type and add $inject angular 1 annotation to constructor if @Inject
Expand Down
32 changes: 32 additions & 0 deletions test/core/di/povider.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,38 @@ describe( `di/provider`, ()=> {

} );

describe( `provide:errors`, ()=> {

it( `should throw if registering ng.constant/value via Opaque token and useValue is blank`, ()=> {

const MY_CONST = new OpaqueToken('myConst');

expect( ()=>provide( MY_CONST ) ).to.throw();
expect( ()=>provide( MY_CONST, { useValue: undefined } ) ).to.throw();

} );

it( `should throw if registering type as service which is not a CLass`, ()=> {

expect( ()=>provide( 'foo' ) ).to.throw();
expect( ()=>(provide as any)( 1231 ) ).to.throw();
expect( ()=>(provide as any)( { hello: 'wat' } ) ).to.throw();

} );
it( `should throw if registering service which has @Inject and doesn't uses @Injectable`, ()=> {


class MyService{
constructor(@Inject('$log') private $log){}
}

expect( ()=>provide( MyService ) ).to.throw();


} );

} );

describe( `private API`, ()=> {

describe( `#_getTokenStringFromInjectable`, ()=> {
Expand Down

0 comments on commit 8e04c2e

Please sign in to comment.