From 81beb1c7888622881f79453ee1a411bc3f88501f Mon Sep 17 00:00:00 2001 From: vsavkin Date: Wed, 2 Mar 2016 11:17:02 -0800 Subject: [PATCH] feat(transformers): collect provider information --- .../src/compiler/directive_metadata.ts | 34 +++- .../test/compiler/directive_metadata_spec.ts | 44 ++--- .../common/type_metadata_reader.dart | 51 +++++- .../compile_data_creator.dart | 11 ++ .../directive_processor/all_tests.dart | 154 ++++++++++++++++++ .../directives_files/components.dart | 73 +++++++++ .../template_compiler/all_tests.dart | 32 +++- 7 files changed, 371 insertions(+), 28 deletions(-) diff --git a/modules/angular2/src/compiler/directive_metadata.ts b/modules/angular2/src/compiler/directive_metadata.ts index 360acf64bd..3296ff0565 100644 --- a/modules/angular2/src/compiler/directive_metadata.ts +++ b/modules/angular2/src/compiler/directive_metadata.ts @@ -177,7 +177,10 @@ export class CompileProviderMetadata { static fromJson(data: {[key: string]: any}): CompileProviderMetadata { return new CompileProviderMetadata({ token: objFromJson(data['token'], CompileIdentifierMetadata.fromJson), - useClass: objFromJson(data['useClass'], CompileTypeMetadata.fromJson) + useClass: objFromJson(data['useClass'], CompileTypeMetadata.fromJson), + useExisting: objFromJson(data['useExisting'], CompileIdentifierMetadata.fromJson), + useValue: objFromJson(data['useValue'], CompileIdentifierMetadata.fromJson), + useFactory: objFromJson(data['useFactory'], CompileFactoryMetadata.fromJson) }); } @@ -185,7 +188,10 @@ export class CompileProviderMetadata { return { // Note: Runtime type can't be serialized... 'token': objToJson(this.token), - 'useClass': objToJson(this.useClass) + 'useClass': objToJson(this.useClass), + 'useExisting': objToJson(this.useExisting), + 'useValue': objToJson(this.useValue), + 'useFactory': objToJson(this.useFactory) }; } } @@ -198,15 +204,17 @@ export class CompileFactoryMetadata implements CompileIdentifierMetadata { constConstructor: boolean; diDeps: CompileDiDependencyMetadata[]; - constructor({runtime, name, moduleUrl, constConstructor, diDeps}: { + constructor({runtime, name, moduleUrl, prefix, constConstructor, diDeps}: { runtime?: Function, name?: string, + prefix?: string, moduleUrl?: string, constConstructor?: boolean, diDeps?: CompileDiDependencyMetadata[] }) { this.runtime = runtime; this.name = name; + this.prefix = prefix; this.moduleUrl = moduleUrl; this.diDeps = diDeps; this.constConstructor = constConstructor; @@ -214,7 +222,25 @@ export class CompileFactoryMetadata implements CompileIdentifierMetadata { get identifier(): CompileIdentifierMetadata { return this; } - toJson() { return null; } + static fromJson(data: {[key: string]: any}): CompileFactoryMetadata { + return new CompileFactoryMetadata({ + name: data['name'], + prefix: data['prefix'], + moduleUrl: data['moduleUrl'], + constConstructor: data['constConstructor'], + diDeps: arrayFromJson(data['diDeps'], CompileDiDependencyMetadata.fromJson) + }); + } + + toJson(): {[key: string]: any} { + return { + 'name': this.name, + 'prefix': this.prefix, + 'moduleUrl': this.moduleUrl, + 'constConstructor': this.constConstructor, + 'diDeps': arrayToJson(this.diDeps) + }; + } } /** diff --git a/modules/angular2/test/compiler/directive_metadata_spec.ts b/modules/angular2/test/compiler/directive_metadata_spec.ts index ac6b5fb627..f5346c8d02 100644 --- a/modules/angular2/test/compiler/directive_metadata_spec.ts +++ b/modules/angular2/test/compiler/directive_metadata_spec.ts @@ -18,7 +18,9 @@ import { CompileTemplateMetadata, CompileProviderMetadata, CompileDiDependencyMetadata, - CompileQueryMetadata + CompileQueryMetadata, + CompileIdentifierMetadata, + CompileFactoryMetadata } from 'angular2/src/compiler/directive_metadata'; import {ViewEncapsulation} from 'angular2/src/core/metadata/view'; import {ChangeDetectionStrategy} from 'angular2/src/core/change_detection'; @@ -31,25 +33,21 @@ export function main() { var fullDirectiveMeta: CompileDirectiveMetadata; beforeEach(() => { - fullTypeMeta = new CompileTypeMetadata({ - name: 'SomeType', - moduleUrl: 'someUrl', + var diDep = new CompileDiDependencyMetadata({ + isAttribute: true, + isSelf: true, isHost: true, - diDeps: [ - new CompileDiDependencyMetadata({ - isAttribute: true, - isSelf: true, - isHost: true, - isSkipSelf: true, - isOptional: true, - token: 'someToken', - query: new CompileQueryMetadata( - {selectors: ['one'], descendants: true, first: true, propertyName: 'one'}), - viewQuery: new CompileQueryMetadata( - {selectors: ['one'], descendants: true, first: true, propertyName: 'one'}) - }) - ] + isSkipSelf: true, + isOptional: true, + token: 'someToken', + query: new CompileQueryMetadata( + {selectors: ['one'], descendants: true, first: true, propertyName: 'one'}), + viewQuery: new CompileQueryMetadata( + {selectors: ['one'], descendants: true, first: true, propertyName: 'one'}) }); + + fullTypeMeta = new CompileTypeMetadata( + {name: 'SomeType', moduleUrl: 'someUrl', isHost: true, diDeps: [diDep]}); fullTemplateMeta = new CompileTemplateMetadata({ encapsulation: ViewEncapsulation.Emulated, template: '', @@ -69,7 +67,15 @@ export function main() { outputs: ['someEvent'], host: {'(event1)': 'handler1', '[prop1]': 'expr1', 'attr1': 'attrValue2'}, lifecycleHooks: [LifecycleHooks.OnChanges], - providers: [new CompileProviderMetadata({token: 'token', useClass: fullTypeMeta})] + providers: [ + new CompileProviderMetadata({ + token: 'token', + useClass: fullTypeMeta, + useExisting: new CompileIdentifierMetadata({name: 'someName'}), + useFactory: new CompileFactoryMetadata({name: 'someName', diDeps: [diDep]}), + useValue: 'someValue', + }) + ] }); }); diff --git a/modules_dart/transform/lib/src/transform/common/type_metadata_reader.dart b/modules_dart/transform/lib/src/transform/common/type_metadata_reader.dart index fbaad1a5e3..a249e3f657 100644 --- a/modules_dart/transform/lib/src/transform/common/type_metadata_reader.dart +++ b/modules_dart/transform/lib/src/transform/common/type_metadata_reader.dart @@ -431,14 +431,38 @@ class _DirectiveMetadataVisitor extends Object ) { final token = el.argumentList.arguments.first; - var useClass; + var useClass, useExisting, useValue, factoryId, useFactory, deps; el.argumentList.arguments.skip(1).forEach((arg) { if (arg.name.toString() == "useClass:") { final id = _readIdentifier(arg.expression); useClass = new CompileTypeMetadata(prefix: id.prefix, name: id.name); + } else if (arg.name.toString() == "toClass:") { + final id = _readIdentifier(arg.expression); + useClass = new CompileTypeMetadata(prefix: id.prefix, name: id.name); + + } else if (arg.name.toString() == "useExisting:") { + useExisting = _readIdentifier(arg.expression); + } else if (arg.name.toString() == "toAlias:") { + useExisting = _readIdentifier(arg.expression); + + } else if (arg.name.toString() == "useValue:") { + useValue = _readIdentifier(arg.expression); + } else if (arg.name.toString() == "toValue:") { + useValue = _readIdentifier(arg.expression); + + } else if (arg.name.toString() == "useFactory:") { + factoryId = _readIdentifier(arg.expression); + } else if (arg.name.toString() == "toFactory:") { + factoryId = _readIdentifier(arg.expression); + + } else if (arg.name.toString() == "deps:") { + deps = _readDeps(arg.expression); } }); - return new CompileProviderMetadata(token: _readIdentifier(token), useClass: useClass); + if (factoryId != null) { + useFactory = new CompileFactoryMetadata(name: factoryId.name, prefix: factoryId.prefix); + } + return new CompileProviderMetadata(token: _readIdentifier(token), useClass: useClass, useExisting: useExisting, useValue: useValue, useFactory: useFactory, deps: deps); } else { throw new ArgumentError( @@ -451,6 +475,29 @@ class _DirectiveMetadataVisitor extends Object } } + List _readDeps(ListLiteral deps) { + return deps.elements.map((p) { + final list = p is ListLiteral ? p.elements : [p]; + final first = list.first; + + var token; + if (first is InstanceCreationExpression && first.constructorName.toString() == "Inject") { + token = _readIdentifier(first.argumentList.arguments[0]); + } else { + token = _readIdentifier(first); + } + + return new CompileDiDependencyMetadata( + token: token, + isSelf: _hasConst(list, "Self"), + isHost: _hasConst(list, "Host"), + isSkipSelf: _hasConst(list, "SkipSelf"), + isOptional: _hasConst(list, "Optional")); + }).toList(); + } + + bool _hasConst(List list, String name) => list.where((m) => m is InstanceCreationExpression && m.constructorName.toString() == name).isNotEmpty; + //TODO Use AnnotationMatcher instead of string matching bool _isAnnotation(Annotation node, String annotationName) { var id = node.name; diff --git a/modules_dart/transform/lib/src/transform/template_compiler/compile_data_creator.dart b/modules_dart/transform/lib/src/transform/template_compiler/compile_data_creator.dart index 61e90cc9f3..2d725354fd 100644 --- a/modules_dart/transform/lib/src/transform/template_compiler/compile_data_creator.dart +++ b/modules_dart/transform/lib/src/transform/template_compiler/compile_data_creator.dart @@ -162,6 +162,17 @@ class _CompileDataCreator { if (provider.useClass != null) { provider.useClass = _resolveIdentifier(ngMetaMap, neededBy, provider.useClass); } + if (provider.useExisting != null) { + provider.useExisting = _resolveIdentifier(ngMetaMap, neededBy, provider.useExisting); + } + if (provider.useValue != null) { + provider.useValue = _resolveIdentifier(ngMetaMap, neededBy, provider.useValue); + } + if (provider.useFactory != null) { + final resolved = _resolveIdentifier(ngMetaMap, neededBy, provider.useFactory); + provider.useFactory.moduleUrl = resolved.moduleUrl; + _resolveDiDependencyMetadata(ngMetaMap, neededBy, provider.useFactory.diDeps); + } resolvedProviders.add(provider); } } diff --git a/modules_dart/transform/test/transform/directive_processor/all_tests.dart b/modules_dart/transform/test/transform/directive_processor/all_tests.dart index 5c023a1772..ba6eb15e45 100644 --- a/modules_dart/transform/test/transform/directive_processor/all_tests.dart +++ b/modules_dart/transform/test/transform/directive_processor/all_tests.dart @@ -671,6 +671,160 @@ void allTests() { expect(useClass.name).toEqual("ServiceDep"); }); + it('should populate `providers` using toClass.', + () async { + var cmp = + (await _testCreateModel('directives_files/components.dart')).identifiers['ComponentWithProvidersToClass']; + + expect(cmp).toBeNotNull(); + expect(cmp.providers).toBeNotNull(); + expect(cmp.providers.length).toEqual(1); + + var token = cmp.providers.first.token; + var useExisting = cmp.providers.first.useClass; + + expect(useExisting.prefix).toEqual(null); + expect(useExisting.name).toEqual("ServiceDep"); + }); + + it('should populate `providers` using useExisting.', + () async { + var cmp = + (await _testCreateModel('directives_files/components.dart')).identifiers['ComponentWithProvidersUseExisting']; + + expect(cmp).toBeNotNull(); + expect(cmp.providers).toBeNotNull(); + expect(cmp.providers.length).toEqual(1); + + var token = cmp.providers.first.token; + var useExisting = cmp.providers.first.useExisting; + + expect(useExisting.prefix).toEqual(null); + expect(useExisting.name).toEqual("ServiceDep"); + }); + + it('should populate `providers` using toAlias.', + () async { + var cmp = + (await _testCreateModel('directives_files/components.dart')).identifiers['ComponentWithProvidersToAlias']; + + expect(cmp).toBeNotNull(); + expect(cmp.providers).toBeNotNull(); + expect(cmp.providers.length).toEqual(1); + + var token = cmp.providers.first.token; + var useExisting = cmp.providers.first.useExisting; + + expect(useExisting.prefix).toEqual(null); + expect(useExisting.name).toEqual("ServiceDep"); + }); + + it('should populate `providers` using useExisting (string token).', + () async { + var cmp = + (await _testCreateModel('directives_files/components.dart')).identifiers['ComponentWithProvidersUseExistingStr']; + + expect(cmp).toBeNotNull(); + expect(cmp.providers).toBeNotNull(); + expect(cmp.providers.length).toEqual(1); + + var token = cmp.providers.first.token; + var useExisting = cmp.providers.first.useExisting; + + expect(useExisting).toEqual("StrToken"); + }); + + it('should populate `providers` using useValue.', + () async { + var cmp = + (await _testCreateModel('directives_files/components.dart')).identifiers['ComponentWithProvidersUseValue']; + + expect(cmp).toBeNotNull(); + expect(cmp.providers).toBeNotNull(); + expect(cmp.providers.length).toEqual(1); + + var token = cmp.providers.first.token; + var useValue = cmp.providers.first.useValue; + + expect(useValue.prefix).toEqual(null); + expect(useValue.name).toEqual("ServiceDep"); + }); + + it('should populate `providers` using toValue.', + () async { + var cmp = + (await _testCreateModel('directives_files/components.dart')).identifiers['ComponentWithProvidersToValue']; + + expect(cmp).toBeNotNull(); + expect(cmp.providers).toBeNotNull(); + expect(cmp.providers.length).toEqual(1); + + var token = cmp.providers.first.token; + var useValue = cmp.providers.first.useValue; + + expect(useValue.prefix).toEqual(null); + expect(useValue.name).toEqual("ServiceDep"); + }); + + it('should populate `providers` using useValue (string token).', + () async { + var cmp = + (await _testCreateModel('directives_files/components.dart')).identifiers['ComponentWithProvidersUseValueStr']; + + expect(cmp).toBeNotNull(); + expect(cmp.providers).toBeNotNull(); + expect(cmp.providers.length).toEqual(1); + + var token = cmp.providers.first.token; + var useValue = cmp.providers.first.useValue; + + expect(useValue).toEqual("StrToken"); + }); + + it('should populate `providers` using useFactory.', + () async { + var cmp = + (await _testCreateModel('directives_files/components.dart')).identifiers['ComponentWithProvidersUseFactory']; + + expect(cmp).toBeNotNull(); + expect(cmp.providers).toBeNotNull(); + expect(cmp.providers.length).toEqual(1); + + var token = cmp.providers.first.token; + var useFactory = cmp.providers.first.useFactory; + var deps = cmp.providers.first.deps; + + expect(useFactory.prefix).toEqual(null); + expect(useFactory.name).toEqual("funcDep"); + + expect(deps[0].token.name).toEqual("ServiceDep"); + expect(deps[1].token).toEqual("Str"); + expect(deps[2].token.name).toEqual("ServiceDep"); + expect(deps[3].token.name).toEqual("ServiceDep"); + expect(deps[3].isSelf).toEqual(true); + expect(deps[4].token.name).toEqual("ServiceDep"); + expect(deps[4].isSkipSelf).toEqual(true); + expect(deps[5].token.name).toEqual("ServiceDep"); + expect(deps[5].isOptional).toEqual(true); + }); + + it('should populate `providers` using toFactory.', + () async { + var cmp = + (await _testCreateModel('directives_files/components.dart')).identifiers['ComponentWithProvidersToFactory']; + + expect(cmp).toBeNotNull(); + expect(cmp.providers).toBeNotNull(); + expect(cmp.providers.length).toEqual(1); + + var token = cmp.providers.first.token; + var useFactory = cmp.providers.first.useFactory; + var deps = cmp.providers.first.deps; + + expect(useFactory.prefix).toEqual(null); + expect(useFactory.name).toEqual("funcDep"); + }); + it('should populate `providers` using a string token.', () async { var cmp = diff --git a/modules_dart/transform/test/transform/directive_processor/directives_files/components.dart b/modules_dart/transform/test/transform/directive_processor/directives_files/components.dart index 92a39b51ce..d2ea2d0f60 100644 --- a/modules_dart/transform/test/transform/directive_processor/directives_files/components.dart +++ b/modules_dart/transform/test/transform/directive_processor/directives_files/components.dart @@ -127,9 +127,82 @@ class ComponentWithDiDeps { ); } +@Component( + selector: 'component-with-providers-use-class', + template: '', + providers: [const Provider(ServiceDep, useClass: ServiceDep)]) +class ComponentWithProvidersUseClass {} + +@Component( + selector: 'component-with-providers-to-class', + template: '', + providers: [const Binding(ServiceDep, toClass: ServiceDep)]) +class ComponentWithProvidersToClass{} + + +@Component( + selector: 'component-with-providers-use-existing', + template: '', + providers: [const Provider(ServiceDep, useExisting: ServiceDep)]) +class ComponentWithProvidersUseExisting{} + +@Component( + selector: 'component-with-providers-to-alias', + template: '', + providers: [const Binding(ServiceDep, toAlias: ServiceDep)]) +class ComponentWithProvidersToAlias{} + +@Component( + selector: 'component-with-providers-use-existing-string', + template: '', + providers: [const Provider(ServiceDep, useExisting: 'StrToken')]) +class ComponentWithProvidersUseExistingStr{} + + +@Component( + selector: 'component-with-providers-use-value', + template: '', + providers: [const Provider(ServiceDep, useValue: ServiceDep)]) +class ComponentWithProvidersUseValue{} + +@Component( + selector: 'component-with-providers-to-value', + template: '', + providers: [const Binding(ServiceDep, toValue: ServiceDep)]) +class ComponentWithProvidersToValue{} + +@Component( + selector: 'component-with-providers-use-value-string', + template: '', + providers: [const Provider(ServiceDep, useValue: 'StrToken')]) +class ComponentWithProvidersUseValueStr{} + + +@Component( + selector: 'component-with-providers-use-factory', + template: '', + providers: [const Provider(ServiceDep, useFactory: funcDep, deps: + const [ + ServiceDep, + "Str", + [const Inject(ServiceDep)], + [ServiceDep, const Self()], + [ServiceDep, const SkipSelf()], + [ServiceDep, const Optional()] + ])]) +class ComponentWithProvidersUseFactory{} + +@Component( + selector: 'component-with-providers-to-factory', + template: '', + providers: [const Binding(ServiceDep, toFactory: funcDep)]) +class ComponentWithProvidersToFactory{} + @Component( selector: 'component-with-di-deps-string-token', template: '') class ComponentWithDiDepsStrToken { ComponentWithDiDepsStrToken(@Inject("StringDep") arg1); } + +funcDep(){} diff --git a/modules_dart/transform/test/transform/template_compiler/all_tests.dart b/modules_dart/transform/test/transform/template_compiler/all_tests.dart index bbd0fca8a8..dfd092c2bc 100644 --- a/modules_dart/transform/test/transform/template_compiler/all_tests.dart +++ b/modules_dart/transform/test/transform/template_compiler/all_tests.dart @@ -217,8 +217,17 @@ void allTests() { barNgMeta.identifiers['Service2'] = new CompileTypeMetadata(name: 'Service2', moduleUrl: 'moduleUrl'); fooComponentMeta.template = new CompileTemplateMetadata(template: "import 'bar.dart';"); - fooComponentMeta.providers = [new CompileProviderMetadata(token: new CompileIdentifierMetadata(name: 'Service1'), useClass: - new CompileTypeMetadata(name: 'Service2'))]; + fooComponentMeta.providers = [ + new CompileProviderMetadata(token: new CompileIdentifierMetadata(name: 'Service1'), useClass: new CompileTypeMetadata(name: 'Service2')), + new CompileProviderMetadata(token: new CompileIdentifierMetadata(name: 'Service1'), useExisting: new CompileIdentifierMetadata(name: 'Service2')), + new CompileProviderMetadata(token: new CompileIdentifierMetadata(name: 'Service1'), useValue: new CompileIdentifierMetadata(name: 'Service2')), + new CompileProviderMetadata(token: new CompileIdentifierMetadata(name: 'Service1'), useFactory: + new CompileFactoryMetadata( + name: 'Service2', + diDeps: [new CompileDiDependencyMetadata(token: new CompileIdentifierMetadata(name: 'Service2'))] + ) + ) + ]; final viewAnnotation = new AnnotationModel()..name = 'View'..isView = true; final reflectable = fooNgMeta.ngDeps.reflectables.first; @@ -230,12 +239,29 @@ void allTests() { final viewDefResults = await createCompileData(reader, fooAssetId, [], [], {}); final cmp = viewDefResults.viewDefinitions.values.first.component; - expect(cmp.providers.length).toEqual(1); + expect(cmp.providers.length).toEqual(4); expect(cmp.providers[0].token.name).toEqual("Service1"); expect(cmp.providers[0].token.moduleUrl).toEqual("moduleUrl"); expect(cmp.providers[0].useClass.name).toEqual("Service2"); expect(cmp.providers[0].useClass.moduleUrl).toEqual("moduleUrl"); + + expect(cmp.providers[1].token.name).toEqual("Service1"); + expect(cmp.providers[1].token.moduleUrl).toEqual("moduleUrl"); + expect(cmp.providers[1].useExisting.name).toEqual("Service2"); + expect(cmp.providers[1].useExisting.moduleUrl).toEqual("moduleUrl"); + + expect(cmp.providers[2].token.name).toEqual("Service1"); + expect(cmp.providers[2].token.moduleUrl).toEqual("moduleUrl"); + expect(cmp.providers[2].useValue.name).toEqual("Service2"); + expect(cmp.providers[2].useValue.moduleUrl).toEqual("moduleUrl"); + + expect(cmp.providers[3].token.name).toEqual("Service1"); + expect(cmp.providers[3].token.moduleUrl).toEqual("moduleUrl"); + expect(cmp.providers[3].useFactory.name).toEqual("Service2"); + expect(cmp.providers[3].useFactory.moduleUrl).toEqual("moduleUrl"); + expect(cmp.providers[3].useFactory.diDeps[0].token.name).toEqual("Service2"); + expect(cmp.providers[3].useFactory.diDeps[0].token.moduleUrl).toEqual("moduleUrl"); }); it('should generate providers from Provider objects (literals).', () async {