From ebe531bf92b931280383a2d5262ea23a0879d891 Mon Sep 17 00:00:00 2001 From: vsavkin Date: Fri, 26 Feb 2016 08:01:07 -0800 Subject: [PATCH] feat(transformers): collect data needed for the template compiler Closes #7299 --- .../src/compiler/directive_metadata.ts | 306 ++++++++++++++++-- .../src/compiler/template_compiler.ts | 1 + .../change_detection/change_detector_ref.ts | 2 + .../angular2/src/core/linker/element_ref.ts | 2 + .../angular2/src/core/linker/template_ref.ts | 2 + .../src/core/linker/view_container_ref.ts | 3 +- modules/angular2/src/core/render/api.ts | 3 +- modules/angular2/src/router/router.ts | 1 + .../test/compiler/directive_metadata_spec.ts | 10 +- modules/angular2/test/public_api_spec.ts | 17 + .../src/components/dialog/dialog.ts | 1 + .../common/code/reflection_info_code.dart | 1 + .../lib/src/transform/common/ng_meta.dart | 67 ++-- .../common/type_metadata_reader.dart | 148 ++++++++- .../ng_meta_linker.dart | 8 +- .../directive_processor/rewriter.dart | 33 +- .../compile_data_creator.dart | 119 ++++++- .../template_compiler/generator.dart | 18 +- .../reflection/processor.dart | 2 +- .../test/transform/common/ng_meta_helper.dart | 2 +- .../test/transform/common/ng_meta_test.dart | 59 ++-- .../directive_metadata_linker/all_tests.dart | 69 ++-- .../directive_processor/all_tests.dart | 211 ++++++++++-- .../directives_files/components.dart | 42 ++- .../directives_files/dep1.dart | 7 +- .../directives_files/dep2.dart | 5 +- .../directives_files/services.dart | 10 + .../identifiers/classes.dart | 11 + .../identifiers/classes_no_injectable.dart | 3 + .../identifiers/constants.dart | 5 + .../identifiers/enums.dart | 5 + .../identifiers/typedefs.dart | 3 + .../expected/bar.template.dart | 2 + .../integration/two_deps_files/bar.dart | 6 +- .../two_deps_files/expected/bar.template.dart | 22 +- .../integration/two_deps_files/foo.dart | 7 +- .../template_compiler/all_tests.dart | 151 ++++++++- tools/public_api_guard/public_api_spec.ts | 16 +- 38 files changed, 1187 insertions(+), 193 deletions(-) create mode 100644 modules_dart/transform/test/transform/directive_processor/directives_files/services.dart create mode 100644 modules_dart/transform/test/transform/directive_processor/identifiers/classes.dart create mode 100644 modules_dart/transform/test/transform/directive_processor/identifiers/classes_no_injectable.dart create mode 100644 modules_dart/transform/test/transform/directive_processor/identifiers/constants.dart create mode 100644 modules_dart/transform/test/transform/directive_processor/identifiers/enums.dart create mode 100644 modules_dart/transform/test/transform/directive_processor/identifiers/typedefs.dart diff --git a/modules/angular2/src/compiler/directive_metadata.ts b/modules/angular2/src/compiler/directive_metadata.ts index 2fe7ef0d38..42ee61d78a 100644 --- a/modules/angular2/src/compiler/directive_metadata.ts +++ b/modules/angular2/src/compiler/directive_metadata.ts @@ -2,8 +2,10 @@ import { isPresent, isBlank, normalizeBool, + normalizeBlank, serializeEnum, Type, + isString, RegExpWrapper, StringWrapper } from 'angular2/src/facade/lang'; @@ -22,7 +24,17 @@ import {LifecycleHooks, LIFECYCLE_HOOKS_VALUES} from 'angular2/src/core/linker/i // group 2: "event" from "(event)" var HOST_REG_EXP = /^(?:(?:\[([^\]]+)\])|(?:\(([^\)]+)\)))$/g; -export abstract class CompileMetadataWithType { +export abstract class CompileMetadataWithIdentifier { + static fromJson(data: {[key: string]: any}): CompileMetadataWithIdentifier { + return _COMPILE_METADATA_FROM_JSON[data['class']](data); + } + + abstract toJson(): {[key: string]: any}; + + get identifier(): CompileIdentifierMetadata { return unimplemented(); } +} + +export abstract class CompileMetadataWithType extends CompileMetadataWithIdentifier { static fromJson(data: {[key: string]: any}): CompileMetadataWithType { return _COMPILE_METADATA_FROM_JSON[data['class']](data); } @@ -30,37 +42,241 @@ export abstract class CompileMetadataWithType { abstract toJson(): {[key: string]: any}; get type(): CompileTypeMetadata { return unimplemented(); } + + get identifier(): CompileIdentifierMetadata { return unimplemented(); } } -/** - * Metadata regarding compilation of a type. - */ -export class CompileTypeMetadata { - runtime: Type; +export class CompileIdentifierMetadata implements CompileMetadataWithIdentifier { + runtime: any; name: string; + prefix: string; moduleUrl: string; - isHost: boolean; - constructor({runtime, name, moduleUrl, isHost}: - {runtime?: Type, name?: string, moduleUrl?: string, isHost?: boolean} = {}) { + constConstructor: boolean; + constructor({runtime, name, moduleUrl, prefix, constConstructor}: { + runtime?: any, + name?: string, + moduleUrl?: string, + prefix?: string, + constConstructor?: boolean + } = {}) { this.runtime = runtime; this.name = name; + this.prefix = prefix; this.moduleUrl = moduleUrl; - this.isHost = normalizeBool(isHost); + this.constConstructor = constConstructor; } - static fromJson(data: {[key: string]: any}): CompileTypeMetadata { - return new CompileTypeMetadata( - {name: data['name'], moduleUrl: data['moduleUrl'], isHost: data['isHost']}); + static fromJson(data: {[key: string]: any}): CompileIdentifierMetadata { + return new CompileIdentifierMetadata({ + name: data['name'], + prefix: data['prefix'], + moduleUrl: data['moduleUrl'], + constConstructor: data['constConstructor'] + }); } toJson(): {[key: string]: any} { return { // Note: Runtime type can't be serialized... + 'class': 'Identifier', 'name': this.name, 'moduleUrl': this.moduleUrl, - 'isHost': this.isHost + 'prefix': this.prefix, + 'constConstructor': this.constConstructor }; } + + get identifier(): CompileIdentifierMetadata { return this; } +} + +export class CompileDiDependencyMetadata { + isAttribute: boolean; + isSelf: boolean; + isHost: boolean; + isSkipSelf: boolean; + isOptional: boolean; + query: CompileQueryMetadata; + viewQuery: CompileQueryMetadata; + token: CompileIdentifierMetadata | string; + + constructor({isAttribute, isSelf, isHost, isSkipSelf, isOptional, query, viewQuery, token}: { + isAttribute?: boolean, + isSelf?: boolean, + isHost?: boolean, + isSkipSelf?: boolean, + isOptional?: boolean, + query?: CompileQueryMetadata, + viewQuery?: CompileQueryMetadata, + token?: CompileIdentifierMetadata | string + } = {}) { + this.isAttribute = normalizeBool(isAttribute); + this.isSelf = normalizeBool(isSelf); + this.isHost = normalizeBool(isHost); + this.isSkipSelf = normalizeBool(isSkipSelf); + this.isOptional = normalizeBool(isOptional); + this.query = query; + this.viewQuery = viewQuery; + this.token = token; + } + + static fromJson(data: {[key: string]: any}): CompileDiDependencyMetadata { + return new CompileDiDependencyMetadata( + {token: objFromJson(data['token'], CompileIdentifierMetadata.fromJson)}); + } + + toJson(): {[key: string]: any} { + return { + // Note: Runtime type can't be serialized... + 'token': objToJson(this.token) + }; + } +} + +export class CompileProviderMetadata { + token: CompileIdentifierMetadata | string; + useClass: CompileTypeMetadata; + useValue: any; + useExisting: CompileIdentifierMetadata | string; + useFactory: CompileFactoryMetadata; + deps: CompileDiDependencyMetadata[]; + multi: boolean; + + constructor({token, useClass, useValue, useExisting, useFactory, deps, multi}: { + token?: CompileIdentifierMetadata | string, + useClass?: CompileTypeMetadata, + useValue?: any, + useExisting?: CompileIdentifierMetadata | string, + useFactory?: CompileFactoryMetadata, + deps?: CompileDiDependencyMetadata[], + multi?: boolean + }) { + this.token = token; + this.useClass = useClass; + this.useValue = useValue; + this.useExisting = useExisting; + this.useFactory = useFactory; + this.deps = deps; + this.multi = multi; + } + + static fromJson(data: {[key: string]: any}): CompileProviderMetadata { + return new CompileProviderMetadata({ + token: objFromJson(data['token'], CompileIdentifierMetadata.fromJson), + useClass: objFromJson(data['useClass'], CompileTypeMetadata.fromJson) + }); + } + + toJson(): {[key: string]: any} { + return { + // Note: Runtime type can't be serialized... + 'token': objToJson(this.token), + 'useClass': objToJson(this.useClass) + }; + } +} + +export class CompileFactoryMetadata implements CompileIdentifierMetadata { + runtime: Function; + name: string; + prefix: string; + moduleUrl: string; + constConstructor: boolean; + diDeps: CompileDiDependencyMetadata[]; + + constructor({runtime, name, moduleUrl, constConstructor, diDeps}: { + runtime?: Function, + name?: string, + moduleUrl?: string, + constConstructor?: boolean, + diDeps?: CompileDiDependencyMetadata[] + }) { + this.runtime = runtime; + this.name = name; + this.moduleUrl = moduleUrl; + this.diDeps = diDeps; + this.constConstructor = constConstructor; + } + + get identifier(): CompileIdentifierMetadata { return this; } + + toJson() { return null; } +} + +/** + * Metadata regarding compilation of a type. + */ +export class CompileTypeMetadata implements CompileIdentifierMetadata, CompileMetadataWithType { + runtime: Type; + name: string; + prefix: string; + moduleUrl: string; + isHost: boolean; + constConstructor: boolean; + diDeps: CompileDiDependencyMetadata[]; + + constructor({runtime, name, moduleUrl, prefix, isHost, constConstructor, diDeps}: { + runtime?: Type, + name?: string, + moduleUrl?: string, + prefix?: string, + isHost?: boolean, + constConstructor?: boolean, + diDeps?: CompileDiDependencyMetadata[] + } = {}) { + this.runtime = runtime; + this.name = name; + this.moduleUrl = moduleUrl; + this.prefix = prefix; + this.isHost = normalizeBool(isHost); + this.constConstructor = constConstructor; + this.diDeps = normalizeBlank(diDeps); + } + + static fromJson(data: {[key: string]: any}): CompileTypeMetadata { + return new CompileTypeMetadata({ + name: data['name'], + moduleUrl: data['moduleUrl'], + prefix: data['prefix'], + isHost: data['isHost'], + constConstructor: data['constConstructor'], + diDeps: arrayFromJson(data['diDeps'], CompileDiDependencyMetadata.fromJson) + }); + } + + get identifier(): CompileIdentifierMetadata { return this; } + get type(): CompileTypeMetadata { return this; } + + toJson(): {[key: string]: any} { + return { + // Note: Runtime type can't be serialized... + 'class': 'Type', + 'name': this.name, + 'moduleUrl': this.moduleUrl, + 'prefix': this.prefix, + 'isHost': this.isHost, + 'constConstructor': this.constConstructor, + 'diDeps': arrayToJson(this.diDeps) + }; + } +} + +export class CompileQueryMetadata { + selectors: Array; + descendants: boolean; + first: boolean; + propertyName: string; + + constructor({selectors, descendants, first, propertyName}: { + selectors?: Array, + descendants?: boolean, + first?: boolean, + propertyName?: string + } = {}) { + this.selectors = selectors; + this.descendants = descendants; + this.first = first; + this.propertyName = propertyName; + } } /** @@ -120,7 +336,8 @@ export class CompileTemplateMetadata { */ export class CompileDirectiveMetadata implements CompileMetadataWithType { static create({type, isComponent, dynamicLoadable, selector, exportAs, changeDetection, inputs, - outputs, host, lifecycleHooks, template}: { + outputs, host, lifecycleHooks, providers, viewProviders, queries, viewQueries, + template}: { type?: CompileTypeMetadata, isComponent?: boolean, dynamicLoadable?: boolean, @@ -131,6 +348,10 @@ export class CompileDirectiveMetadata implements CompileMetadataWithType { outputs?: string[], host?: {[key: string]: string}, lifecycleHooks?: LifecycleHooks[], + providers?: Array, + viewProviders?: Array, + queries?: CompileQueryMetadata[], + viewQueries?: CompileQueryMetadata[], template?: CompileTemplateMetadata } = {}): CompileDirectiveMetadata { var hostListeners: {[key: string]: string} = {}; @@ -180,10 +401,13 @@ export class CompileDirectiveMetadata implements CompileMetadataWithType { hostProperties: hostProperties, hostAttributes: hostAttributes, lifecycleHooks: isPresent(lifecycleHooks) ? lifecycleHooks : [], + providers: providers, + viewProviders: viewProviders, + queries: queries, + viewQueries: viewQueries, template: template }); } - type: CompileTypeMetadata; isComponent: boolean; dynamicLoadable: boolean; @@ -196,9 +420,14 @@ export class CompileDirectiveMetadata implements CompileMetadataWithType { hostProperties: {[key: string]: string}; hostAttributes: {[key: string]: string}; lifecycleHooks: LifecycleHooks[]; + providers: Array; + viewProviders: Array; + queries: CompileQueryMetadata[]; + viewQueries: CompileQueryMetadata[]; template: CompileTemplateMetadata; constructor({type, isComponent, dynamicLoadable, selector, exportAs, changeDetection, inputs, - outputs, hostListeners, hostProperties, hostAttributes, lifecycleHooks, template}: { + outputs, hostListeners, hostProperties, hostAttributes, lifecycleHooks, providers, + viewProviders, queries, viewQueries, template}: { type?: CompileTypeMetadata, isComponent?: boolean, dynamicLoadable?: boolean, @@ -211,6 +440,10 @@ export class CompileDirectiveMetadata implements CompileMetadataWithType { hostProperties?: {[key: string]: string}, hostAttributes?: {[key: string]: string}, lifecycleHooks?: LifecycleHooks[], + providers?: Array, + viewProviders?: Array, + queries?: CompileQueryMetadata[], + viewQueries?: CompileQueryMetadata[], template?: CompileTemplateMetadata } = {}) { this.type = type; @@ -225,9 +458,15 @@ export class CompileDirectiveMetadata implements CompileMetadataWithType { this.hostProperties = hostProperties; this.hostAttributes = hostAttributes; this.lifecycleHooks = lifecycleHooks; + this.providers = normalizeBlank(providers); + this.viewProviders = normalizeBlank(viewProviders); + this.queries = queries; + this.viewQueries = viewQueries; this.template = template; } + get identifier(): CompileIdentifierMetadata { return this.type; } + static fromJson(data: {[key: string]: any}): CompileDirectiveMetadata { return new CompileDirectiveMetadata({ isComponent: data['isComponent'], @@ -246,7 +485,8 @@ export class CompileDirectiveMetadata implements CompileMetadataWithType { lifecycleHooks: (data['lifecycleHooks']).map(hookValue => LIFECYCLE_HOOKS_VALUES[hookValue]), template: isPresent(data['template']) ? CompileTemplateMetadata.fromJson(data['template']) : - data['template'] + data['template'], + providers: arrayFromJson(data['providers'], CompileProviderMetadata.fromJson) }); } @@ -266,7 +506,8 @@ export class CompileDirectiveMetadata implements CompileMetadataWithType { 'hostProperties': this.hostProperties, 'hostAttributes': this.hostAttributes, 'lifecycleHooks': this.lifecycleHooks.map(hook => serializeEnum(hook)), - 'template': isPresent(this.template) ? this.template.toJson() : this.template + 'template': isPresent(this.template) ? this.template.toJson() : this.template, + 'providers': arrayToJson(this.providers) }; } } @@ -293,7 +534,11 @@ export function createHostComponentMeta(componentType: CompileTypeMetadata, lifecycleHooks: [], isComponent: true, dynamicLoadable: false, - selector: '*' + selector: '*', + providers: [], + viewProviders: [], + queries: [], + viewQueries: [] }); } @@ -308,6 +553,7 @@ export class CompilePipeMetadata implements CompileMetadataWithType { this.name = name; this.pure = normalizeBool(pure); } + get identifier(): CompileIdentifierMetadata { return this.type; } static fromJson(data: {[key: string]: any}): CompilePipeMetadata { return new CompilePipeMetadata({ @@ -329,5 +575,23 @@ export class CompilePipeMetadata implements CompileMetadataWithType { var _COMPILE_METADATA_FROM_JSON = { 'Directive': CompileDirectiveMetadata.fromJson, - 'Pipe': CompilePipeMetadata.fromJson + 'Pipe': CompilePipeMetadata.fromJson, + 'Type': CompileTypeMetadata.fromJson, + 'Identifier': CompileIdentifierMetadata.fromJson }; + +function arrayFromJson(obj: any[], fn: (a: {[key: string]: any}) => any): any { + return isBlank(obj) ? null : obj.map(fn); +} + +function arrayToJson(obj: any[]): string | {[key: string]: any} { + return isBlank(obj) ? null : obj.map(o => o.toJson()); +} + +function objFromJson(obj: any, fn: (a: {[key: string]: any}) => any): any { + return (isString(obj) || isBlank(obj)) ? obj : fn(obj); +} + +function objToJson(obj: any): string | {[key: string]: any} { + return (isString(obj) || isBlank(obj)) ? obj : obj.toJson(); +} \ No newline at end of file diff --git a/modules/angular2/src/compiler/template_compiler.ts b/modules/angular2/src/compiler/template_compiler.ts index 29a7cf5e64..9819274d18 100644 --- a/modules/angular2/src/compiler/template_compiler.ts +++ b/modules/angular2/src/compiler/template_compiler.ts @@ -109,6 +109,7 @@ export class TemplateCompiler { hostProperties: directive.hostProperties, hostAttributes: directive.hostAttributes, lifecycleHooks: directive.lifecycleHooks, + providers: directive.providers, template: normalizedTemplate })); } diff --git a/modules/angular2/src/core/change_detection/change_detector_ref.ts b/modules/angular2/src/core/change_detection/change_detector_ref.ts index e34fe44857..86c3195512 100644 --- a/modules/angular2/src/core/change_detection/change_detector_ref.ts +++ b/modules/angular2/src/core/change_detection/change_detector_ref.ts @@ -1,6 +1,8 @@ import {ChangeDetector} from './interfaces'; import {ChangeDetectionStrategy} from './constants'; +import {Injectable} from 'angular2/src/core/di'; +@Injectable() export abstract class ChangeDetectorRef { /** * Marks all {@link ChangeDetectionStrategy#OnPush} ancestors as to be checked. diff --git a/modules/angular2/src/core/linker/element_ref.ts b/modules/angular2/src/core/linker/element_ref.ts index a052fb8fa0..ceef37ec0f 100644 --- a/modules/angular2/src/core/linker/element_ref.ts +++ b/modules/angular2/src/core/linker/element_ref.ts @@ -1,4 +1,5 @@ import {unimplemented} from 'angular2/src/facade/exceptions'; +import {Injectable} from 'angular2/src/core/di'; import {AppElement} from './element'; /** @@ -11,6 +12,7 @@ import {AppElement} from './element'; * An `ElementRef` is backed by a render-specific element. In the browser, this is usually a DOM * element. */ +@Injectable() export abstract class ElementRef { /** * The underlying native element or `null` if direct access to native elements is not supported diff --git a/modules/angular2/src/core/linker/template_ref.ts b/modules/angular2/src/core/linker/template_ref.ts index ad934be9a1..b22b603ff5 100644 --- a/modules/angular2/src/core/linker/template_ref.ts +++ b/modules/angular2/src/core/linker/template_ref.ts @@ -1,4 +1,5 @@ import {ElementRef, ElementRef_} from './element_ref'; +import {Injectable} from 'angular2/src/core/di'; /** * Represents an Embedded Template that can be used to instantiate Embedded Views. @@ -12,6 +13,7 @@ import {ElementRef, ElementRef_} from './element_ref'; * {@link ViewContainerRef#createEmbeddedView}, which will create the View and attach it to the * View Container. */ +@Injectable() export abstract class TemplateRef { /** * The location in the View where the Embedded View logically belongs to. diff --git a/modules/angular2/src/core/linker/view_container_ref.ts b/modules/angular2/src/core/linker/view_container_ref.ts index 0c2b8438b1..8b1c2b827c 100644 --- a/modules/angular2/src/core/linker/view_container_ref.ts +++ b/modules/angular2/src/core/linker/view_container_ref.ts @@ -1,6 +1,6 @@ import {ListWrapper} from 'angular2/src/facade/collection'; import {unimplemented} from 'angular2/src/facade/exceptions'; -import {ResolvedProvider} from 'angular2/src/core/di'; +import {ResolvedProvider, Injectable} from 'angular2/src/core/di'; import {isPresent, isBlank} from 'angular2/src/facade/lang'; import {AppElement} from './element'; @@ -36,6 +36,7 @@ import { * * */ +@Injectable() export abstract class ViewContainerRef { /** * Anchor element that specifies the location of this container in the containing View. diff --git a/modules/angular2/src/core/render/api.ts b/modules/angular2/src/core/render/api.ts index 2e6a529807..37de28fca2 100644 --- a/modules/angular2/src/core/render/api.ts +++ b/modules/angular2/src/core/render/api.ts @@ -1,5 +1,5 @@ import {ViewEncapsulation} from 'angular2/src/core/metadata/view'; -import {Injector} from 'angular2/src/core/di/injector'; +import {Injector, Injectable} from 'angular2/src/core/di'; export class RenderComponentType { constructor(public id: string, public encapsulation: ViewEncapsulation, @@ -13,6 +13,7 @@ export class RenderDebugInfo { export interface ParentRenderer { renderComponent(componentType: RenderComponentType): Renderer; } +@Injectable() export abstract class Renderer implements ParentRenderer { abstract renderComponent(componentType: RenderComponentType): Renderer; diff --git a/modules/angular2/src/router/router.ts b/modules/angular2/src/router/router.ts index 305c4b750d..97be56ce55 100644 --- a/modules/angular2/src/router/router.ts +++ b/modules/angular2/src/router/router.ts @@ -34,6 +34,7 @@ let _resolveToFalse = PromiseWrapper.resolve(false); * `Instruction`. * The router uses the `RouteRegistry` to get an `Instruction`. */ +@Injectable() export class Router { navigating: boolean = false; lastNavigationAttempt: string; diff --git a/modules/angular2/test/compiler/directive_metadata_spec.ts b/modules/angular2/test/compiler/directive_metadata_spec.ts index a801aa73b5..2c1d030f19 100644 --- a/modules/angular2/test/compiler/directive_metadata_spec.ts +++ b/modules/angular2/test/compiler/directive_metadata_spec.ts @@ -15,7 +15,8 @@ import { import { CompileDirectiveMetadata, CompileTypeMetadata, - CompileTemplateMetadata + CompileTemplateMetadata, + CompileProviderMetadata } from 'angular2/src/compiler/directive_metadata'; import {ViewEncapsulation} from 'angular2/src/core/metadata/view'; import {ChangeDetectionStrategy} from 'angular2/src/core/change_detection'; @@ -28,8 +29,8 @@ export function main() { var fullDirectiveMeta: CompileDirectiveMetadata; beforeEach(() => { - fullTypeMeta = - new CompileTypeMetadata({name: 'SomeType', moduleUrl: 'someUrl', isHost: true}); + fullTypeMeta = new CompileTypeMetadata( + {name: 'SomeType', moduleUrl: 'someUrl', isHost: true, diDeps: []}); fullTemplateMeta = new CompileTemplateMetadata({ encapsulation: ViewEncapsulation.Emulated, template: '', @@ -48,7 +49,8 @@ export function main() { inputs: ['someProp'], outputs: ['someEvent'], host: {'(event1)': 'handler1', '[prop1]': 'expr1', 'attr1': 'attrValue2'}, - lifecycleHooks: [LifecycleHooks.OnChanges] + lifecycleHooks: [LifecycleHooks.OnChanges], + providers: [new CompileProviderMetadata({token: 'token', useClass: fullTypeMeta})] }); }); diff --git a/modules/angular2/test/public_api_spec.ts b/modules/angular2/test/public_api_spec.ts index 0714a7d014..249c2fa916 100644 --- a/modules/angular2/test/public_api_spec.ts +++ b/modules/angular2/test/public_api_spec.ts @@ -530,6 +530,15 @@ var NG_COMPILER = [ "CompileDirectiveMetadata.template=", "CompileDirectiveMetadata.type", "CompileDirectiveMetadata.type=", + "CompileDirectiveMetadata.identifier", + "CompileDirectiveMetadata.providers", + "CompileDirectiveMetadata.providers=", + "CompileDirectiveMetadata.queries", + "CompileDirectiveMetadata.queries=", + "CompileDirectiveMetadata.viewProviders", + "CompileDirectiveMetadata.viewProviders=", + "CompileDirectiveMetadata.viewQueries", + "CompileDirectiveMetadata.viewQueries=", "CompileTemplateMetadata.encapsulation", "CompileTemplateMetadata.encapsulation=", "CompileTemplateMetadata.ngContentSelectors", @@ -550,6 +559,14 @@ var NG_COMPILER = [ "CompileTypeMetadata.name=", "CompileTypeMetadata.runtime", "CompileTypeMetadata.runtime=", + "CompileTypeMetadata.diDeps", + "CompileTypeMetadata.diDeps=", + "CompileTypeMetadata.type", + "CompileTypeMetadata.identifier", + "CompileTypeMetadata.prefix", + "CompileTypeMetadata.prefix=", + "CompileTypeMetadata.constConstructor", + "CompileTypeMetadata.constConstructor=", "DirectiveAst.directive", "DirectiveAst.directive=", "DirectiveAst.exportAsVars", diff --git a/modules/angular2_material/src/components/dialog/dialog.ts b/modules/angular2_material/src/components/dialog/dialog.ts index 4d4a241ebf..d78bc8792a 100644 --- a/modules/angular2_material/src/components/dialog/dialog.ts +++ b/modules/angular2_material/src/components/dialog/dialog.ts @@ -127,6 +127,7 @@ export class MdDialog { /** * Reference to an opened dialog. */ +@Injectable() export class MdDialogRef { // Reference to the MdDialogContainer component. containerRef: ComponentRef; diff --git a/modules_dart/transform/lib/src/transform/common/code/reflection_info_code.dart b/modules_dart/transform/lib/src/transform/common/code/reflection_info_code.dart index e43f0cae4d..c80316d8fe 100644 --- a/modules_dart/transform/lib/src/transform/common/code/reflection_info_code.dart +++ b/modules_dart/transform/lib/src/transform/common/code/reflection_info_code.dart @@ -67,6 +67,7 @@ class ReflectionInfoVisitor extends RecursiveAstVisitor { @override ReflectionInfoModel visitClassDeclaration(ClassDeclaration node) { + if (node.isAbstract) return null; if (!node.metadata .any((a) => _annotationMatcher.hasMatch(a.name, assetId))) { return null; diff --git a/modules_dart/transform/lib/src/transform/common/ng_meta.dart b/modules_dart/transform/lib/src/transform/common/ng_meta.dart index 7212b9d77f..716bf0168c 100644 --- a/modules_dart/transform/lib/src/transform/common/ng_meta.dart +++ b/modules_dart/transform/lib/src/transform/common/ng_meta.dart @@ -33,9 +33,9 @@ class NgMeta { static const _TYPE_VALUE = 'type'; static const _VALUE_KEY = 'value'; - /// Metadata for each type annotated as a directive/pipe. - /// Type: [CompileDirectiveMetadata]/[CompilePipeMetadata] - final Map types; + /// Metadata for each identifier + /// Type: [CompileDirectiveMetadata]|[CompilePipeMetadata]|[CompileTypeMetadata]|[CompileIdentifierMetadata] + final Map identifiers; /// List of other types and names associated with a given name. final Map> aliases; @@ -43,12 +43,13 @@ class NgMeta { // The NgDeps generated from final NgDepsModel ngDeps; - NgMeta( - {Map types, - Map> aliases, - this.ngDeps: null}) - : this.types = types != null ? types : {}, - this.aliases = aliases != null ? aliases : {}; + bool definesAlias; + + NgMeta({Map> aliases, + Map identifiers, + this.ngDeps: null, this.definesAlias: false}) + :this.aliases = aliases != null ? aliases : {}, + this.identifiers = identifiers != null ? identifiers : {}; NgMeta.empty() : this(); @@ -69,13 +70,22 @@ class NgMeta { return false; } - bool get isEmpty => types.isEmpty && aliases.isEmpty && isNgDepsEmpty; + bool get isEmpty => identifiers.isEmpty && aliases.isEmpty && isNgDepsEmpty; + + List get linkingUris { + final r = ngDeps.exports.map((r) => r.uri).toList(); + if (definesAlias) { + r.addAll(ngDeps.imports.map((r) => r.uri)); + } + return r; + } /// Parse from the serialized form produced by [toJson]. factory NgMeta.fromJson(Map json) { var ngDeps = null; - final types = {}; final aliases = {}; + final identifiers = {}; + var definesAlias = false; for (var key in json.keys) { if (key == _NG_DEPS_KEY) { var ngDepsJsonMap = json[key]; @@ -85,7 +95,11 @@ class NgMeta { 'Unexpected value $ngDepsJsonMap for key "$key" in NgMeta.'); continue; } - ngDeps = new NgDepsModel()..mergeFromJsonMap(ngDepsJsonMap); + ngDeps = new NgDepsModel() + ..mergeFromJsonMap(ngDepsJsonMap); + } else if (key == 'definesAlias') { + definesAlias = json[key]; + } else { var entry = json[key]; if (entry is! Map) { @@ -93,13 +107,13 @@ class NgMeta { continue; } if (entry[_KIND_KEY] == _TYPE_VALUE) { - types[key] = CompileMetadataWithType.fromJson(entry[_VALUE_KEY]); + identifiers[key] = CompileMetadataWithIdentifier.fromJson(entry[_VALUE_KEY]); } else if (entry[_KIND_KEY] == _ALIAS_VALUE) { aliases[key] = entry[_VALUE_KEY]; } } } - return new NgMeta(types: types, aliases: aliases, ngDeps: ngDeps); + return new NgMeta(identifiers: identifiers, aliases: aliases, ngDeps: ngDeps, definesAlias: definesAlias); } /// Serialized representation of this instance. @@ -107,41 +121,44 @@ class NgMeta { var result = {}; result[_NG_DEPS_KEY] = isNgDepsEmpty ? null : ngDeps.writeToJsonMap(); - types.forEach((k, v) { + identifiers.forEach((k, v) { result[k] = {_KIND_KEY: _TYPE_VALUE, _VALUE_KEY: v.toJson()}; }); aliases.forEach((k, v) { result[k] = {_KIND_KEY: _ALIAS_VALUE, _VALUE_KEY: v}; }); + + result['definesAlias'] = definesAlias; + return result; } /// Merge into this instance all information from [other]. /// This does not include `ngDeps`. void addAll(NgMeta other) { - types.addAll(other.types); aliases.addAll(other.aliases); + identifiers.addAll(other.identifiers); } /// Returns the metadata for every type associated with the given [alias]. List flatten(String alias) { var result = []; - var seen = new Set(); - helper(name) { - if (!seen.add(name)) { - log.warning('Circular alias dependency for "$name".'); + helper(name, path) { + final newPath = []..addAll(path)..add(name); + if (path.contains(name)) { + log.error('Circular alias dependency for "$name". Cycle: ${newPath.join(' -> ')}.'); return; } - if (types.containsKey(name)) { - result.add(types[name]); + if (identifiers.containsKey(name)) { + result.add(identifiers[name]); } else if (aliases.containsKey(name)) { - aliases[name].forEach(helper); + aliases[name].forEach((n) => helper(n, newPath)); } else { - log.warning('Unknown alias: "$name".'); + log.error('Unknown alias: ${newPath.join(' -> ')}. Make sure you export ${name} from the file where ${path.last} is defined.'); } } - helper(alias); + helper(alias, []); return result; } } 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 3191630314..b980fcce88 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 @@ -21,22 +21,24 @@ import 'url_resolver.dart'; class TypeMetadataReader { final _DirectiveMetadataVisitor _directiveVisitor; final _PipeMetadataVisitor _pipeVisitor; + final _CompileTypeMetadataVisitor _typeVisitor; final TemplateCompiler _templateCompiler; TypeMetadataReader._( - this._directiveVisitor, this._pipeVisitor, this._templateCompiler); + this._directiveVisitor, this._pipeVisitor, this._templateCompiler, this._typeVisitor); /// Accepts an [AnnotationMatcher] which tests that an [Annotation] /// is a [Directive], [Component], or [View]. factory TypeMetadataReader(AnnotationMatcher annotationMatcher, InterfaceMatcher interfaceMatcher, TemplateCompiler templateCompiler) { var lifecycleVisitor = new _LifecycleHookVisitor(interfaceMatcher); + var typeVisitor = new _CompileTypeMetadataVisitor(annotationMatcher); var directiveVisitor = - new _DirectiveMetadataVisitor(annotationMatcher, lifecycleVisitor); + new _DirectiveMetadataVisitor(annotationMatcher, lifecycleVisitor, typeVisitor); var pipeVisitor = new _PipeMetadataVisitor(annotationMatcher); return new TypeMetadataReader._( - directiveVisitor, pipeVisitor, templateCompiler); + directiveVisitor, pipeVisitor, templateCompiler, typeVisitor); } /// Reads *un-normalized* [CompileDirectiveMetadata]/[CompilePipeMetadata] from the @@ -52,13 +54,19 @@ class TypeMetadataReader { Future readTypeMetadata(ClassDeclaration node, AssetId assetId) { _directiveVisitor.reset(assetId); _pipeVisitor.reset(assetId); + _typeVisitor.reset(assetId); + node.accept(_directiveVisitor); node.accept(_pipeVisitor); + node.accept(_typeVisitor); + if (_directiveVisitor.hasMetadata) { final metadata = _directiveVisitor.createMetadata(); return _templateCompiler.normalizeDirectiveMetadata(metadata); } else if (_pipeVisitor.hasMetadata) { return new Future.value(_pipeVisitor.createMetadata()); + } else if (_typeVisitor.isInjectable) { + return new Future.value(_typeVisitor.type); } else { return new Future.value(null); } @@ -124,6 +132,67 @@ bool _expressionToBool(Expression node, String nodeDescription) { return value; } +class _CompileTypeMetadataVisitor extends Object + with RecursiveAstVisitor { + + bool _isInjectable = false; + CompileTypeMetadata _type; + AssetId _assetId; + final AnnotationMatcher _annotationMatcher; + + _CompileTypeMetadataVisitor(this._annotationMatcher); + + bool get isInjectable => _isInjectable; + + CompileTypeMetadata get type => _type; + + void reset(AssetId assetId) { + this._assetId = assetId; + this._isInjectable = false; + this._type = null; + } + + @override + Object visitAnnotation(Annotation node) { + final isComponent = _annotationMatcher.isComponent(node, _assetId); + final isDirective = _annotationMatcher.isDirective(node, _assetId); + final isInjectable = _annotationMatcher.isInjectable(node, _assetId); + + _isInjectable = _isInjectable || isComponent || isDirective || isInjectable; + + return null; + } + + @override + Object visitClassDeclaration(ClassDeclaration node) { + node.metadata.accept(this); + if (this._isInjectable) { + _type = new CompileTypeMetadata( + moduleUrl: toAssetUri(_assetId), + name: node.name.toString(), + diDeps: _getCompileDiDependencyMetadata(node), + runtime: null // Intentionally `null`, cannot be provided here. + ); + } + return null; + } + + List _getCompileDiDependencyMetadata(ClassDeclaration node) { + final constructor = node.getConstructor(null); + if (constructor == null) return []; + + return constructor.parameters.parameters.map((p) { + final typeToken = p is SimpleFormalParameter && p.type != null ? _readIdentifier(p.type.name) : null; + final injectTokens = p.metadata.where((m) => m.name.toString() == "Inject").map((m) => _readIdentifier(m.arguments.arguments[0])); + final token = injectTokens.isNotEmpty ? injectTokens.first : typeToken; + return new CompileDiDependencyMetadata(token: token); + }).toList(); + } +} + + + + /// Visitor responsible for processing a [Directive] annotated /// [ClassDeclaration] and creating a [CompileDirectiveMetadata] object. class _DirectiveMetadataVisitor extends Object @@ -134,10 +203,12 @@ class _DirectiveMetadataVisitor extends Object final _LifecycleHookVisitor _lifecycleVisitor; + final _CompileTypeMetadataVisitor _typeVisitor; + /// The [AssetId] we are currently processing. AssetId _assetId; - _DirectiveMetadataVisitor(this._annotationMatcher, this._lifecycleVisitor) { + _DirectiveMetadataVisitor(this._annotationMatcher, this._lifecycleVisitor, this._typeVisitor) { reset(null); } @@ -154,12 +225,14 @@ class _DirectiveMetadataVisitor extends Object List _inputs; List _outputs; Map _host; + List _providers; List _lifecycleHooks; CompileTemplateMetadata _cmpTemplate; CompileTemplateMetadata _viewTemplate; void reset(AssetId assetId) { _lifecycleVisitor.reset(assetId); + _typeVisitor.reset(assetId); _assetId = assetId; _type = null; @@ -171,6 +244,7 @@ class _DirectiveMetadataVisitor extends Object _inputs = []; _outputs = []; _host = {}; + _providers = []; _lifecycleHooks = null; _cmpTemplate = null; _viewTemplate = null; @@ -192,6 +266,7 @@ class _DirectiveMetadataVisitor extends Object inputs: _inputs, outputs: _outputs, host: _host, + providers: _providers, lifecycleHooks: _lifecycleHooks, template: _template); } @@ -316,6 +391,41 @@ class _DirectiveMetadataVisitor extends Object } } + void _populateProviders(Expression providerValues) { + _checkMeta(); + + if (providerValues is ListLiteral) { + final providers = providerValues.elements.map((el) { + if (el is PrefixedIdentifier || el is SimpleIdentifier) { + return new CompileProviderMetadata(token: _readIdentifier(el)); + + } else if (el is InstanceCreationExpression && + (el.constructorName.toString() == "Provider" || + el.constructorName.toString() == "Binding") + + ) { + final token = el.argumentList.arguments.first; + + var useClass; + 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); + } + }); + return new CompileProviderMetadata(token: _readIdentifier(token), useClass: useClass); + + } else { + throw new ArgumentError( + 'Incorrect value. Expected a Provider or a String, but got "${el}".'); + } + }); + _providers.addAll(providers); + } else { + _providers.add(new CompileProviderMetadata(token: _readIdentifier(providerValues))); + } + } + //TODO Use AnnotationMatcher instead of string matching bool _isAnnotation(Annotation node, String annotationName) { var id = node.name; @@ -356,12 +466,10 @@ class _DirectiveMetadataVisitor extends Object @override Object visitClassDeclaration(ClassDeclaration node) { node.metadata.accept(this); + node.accept(_typeVisitor); + _type = _typeVisitor.type; + if (this._hasMetadata) { - _type = new CompileTypeMetadata( - moduleUrl: toAssetUri(_assetId), - name: node.name.toString(), - runtime: null // Intentionally `null`, cannot be provided here. - ); _lifecycleHooks = node.implementsClause != null ? node.implementsClause.accept(_lifecycleVisitor) : const []; @@ -405,6 +513,9 @@ class _DirectiveMetadataVisitor extends Object case 'events': _populateEvents(node.expression); break; + case 'providers': + _populateProviders(node.expression); + break; } return null; } @@ -687,3 +798,22 @@ class _PipeMetadataVisitor extends Object with RecursiveAstVisitor { _pure = _expressionToBool(pureValue, 'Pipe#pure'); } } + + +dynamic _readIdentifier(dynamic el) { + if (el is PrefixedIdentifier) { + return new CompileIdentifierMetadata(name: '${el.identifier}', prefix: '${el.prefix}'); + + } else if (el is SimpleIdentifier) { + return new CompileIdentifierMetadata(name: '$el'); + + } else if (el is SimpleStringLiteral){ + return el.value; + + } else if (el is InstanceCreationExpression){ + return new CompileIdentifierMetadata(name: '${el.constructorName}', constConstructor: true); + + } else { + throw new ArgumentError('Incorrect identifier "${el}".'); + } +} \ No newline at end of file diff --git a/modules_dart/transform/lib/src/transform/directive_metadata_linker/ng_meta_linker.dart b/modules_dart/transform/lib/src/transform/directive_metadata_linker/ng_meta_linker.dart index 874813b127..5de4770c49 100644 --- a/modules_dart/transform/lib/src/transform/directive_metadata_linker/ng_meta_linker.dart +++ b/modules_dart/transform/lib/src/transform/directive_metadata_linker/ng_meta_linker.dart @@ -61,10 +61,10 @@ Future _linkRecursive(NgMeta ngMeta, AssetReader reader, AssetId assetId, } var assetUri = toAssetUri(assetId); - return Future.wait(ngMeta.ngDeps.exports - .where((export) => !isDartCoreUri(export.uri)) - .map((export) => - _urlResolver.resolve(assetUri, toSummaryExtension(export.uri))) + return Future.wait(ngMeta.linkingUris + .where((uri) => !isDartCoreUri(uri)) + .map((uri) => + _urlResolver.resolve(assetUri, toSummaryExtension(uri))) .where((uri) => !seen.contains(uri)) .map((uri) async { seen.add(uri); diff --git a/modules_dart/transform/lib/src/transform/directive_processor/rewriter.dart b/modules_dart/transform/lib/src/transform/directive_processor/rewriter.dart index 0645309af0..3c44e10460 100644 --- a/modules_dart/transform/lib/src/transform/directive_processor/rewriter.dart +++ b/modules_dart/transform/lib/src/transform/directive_processor/rewriter.dart @@ -5,6 +5,7 @@ import 'dart:async'; import 'package:analyzer/analyzer.dart'; import 'package:barback/barback.dart' show AssetId; +import 'package:angular2/src/compiler/directive_metadata.dart' show CompileIdentifierMetadata; import 'package:angular2/src/compiler/template_compiler.dart'; import 'package:angular2/src/transform/common/annotation_matcher.dart'; import 'package:angular2/src/transform/common/asset_reader.dart'; @@ -14,6 +15,7 @@ import 'package:angular2/src/transform/common/interface_matcher.dart'; import 'package:angular2/src/transform/common/logging.dart'; import 'package:angular2/src/transform/common/ng_compiler.dart'; import 'package:angular2/src/transform/common/ng_meta.dart'; +import 'package:angular2/src/transform/common/url_resolver.dart'; import 'package:angular2/src/transform/common/zone.dart' as zone; import 'inliner.dart'; @@ -89,14 +91,17 @@ class _NgMetaVisitor extends Object with SimpleAstVisitor { @override Object visitClassDeclaration(ClassDeclaration node) { _normalizations.add( - _reader.readTypeMetadata(node, assetId).then((compileMetadataWithType) { - if (compileMetadataWithType != null) { - ngMeta.types[compileMetadataWithType.type.name] = - compileMetadataWithType; + _reader.readTypeMetadata(node, assetId).then((compileMetadataWithIdentifier) { + if (compileMetadataWithIdentifier!= null) { + ngMeta.identifiers[compileMetadataWithIdentifier.identifier.name] = + compileMetadataWithIdentifier; + } else { + ngMeta.identifiers[node.name.name] = new CompileIdentifierMetadata(name: node.name.name, moduleUrl: toAssetUri(assetId)); } }).catchError((err) { log.error('ERROR: $err', asset: assetId); })); + return null; } @@ -108,6 +113,11 @@ class _NgMetaVisitor extends Object with SimpleAstVisitor { // doesn't support decorators on variable declarations (see // angular/angular#1747 and angular/ts2dart#249 for context). outer: for (var variable in node.variables.variables) { + if (variable.isConst) { + ngMeta.identifiers[variable.name.name] = + new CompileIdentifierMetadata(name: variable.name.name, moduleUrl: toAssetUri(assetId)); + } + var initializer = variable.initializer; if (initializer != null && initializer is ListLiteral) { var otherNames = []; @@ -117,9 +127,24 @@ class _NgMetaVisitor extends Object with SimpleAstVisitor { if (exp is! SimpleIdentifier) continue outer; otherNames.add(exp.name); } + ngMeta.definesAlias = true; ngMeta.aliases[variable.name.name] = otherNames; } } return null; } + + @override + Object visitFunctionTypeAlias(FunctionTypeAlias node) { + ngMeta.identifiers[node.name.name] = + new CompileIdentifierMetadata(name: node.name.name, moduleUrl: toAssetUri(assetId)); + return null; + } + + @override + Object visitEnumDeclaration(EnumDeclaration node) { + ngMeta.identifiers[node.name.name] = + new CompileIdentifierMetadata(name: node.name.name, moduleUrl: toAssetUri(assetId)); + return null; + } } 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 638a2cfb6d..03ce709415 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 @@ -74,8 +74,8 @@ class _CompileDataCreator { var hasTemplate = ngDeps != null && ngDeps.reflectables != null && ngDeps.reflectables.any((reflectable) { - if (ngMeta.types.containsKey(reflectable.name)) { - final metadata = ngMeta.types[reflectable.name]; + if (ngMeta.identifiers.containsKey(reflectable.name)) { + final metadata = ngMeta.identifiers[reflectable.name]; return metadata is CompileDirectiveMetadata && metadata.template != null; } @@ -91,8 +91,8 @@ class _CompileDataCreator { final platformPipes = await _readPlatformTypes(this.platformPipes, 'pipes'); for (var reflectable in ngDeps.reflectables) { - if (ngMeta.types.containsKey(reflectable.name)) { - final compileDirectiveMetadata = ngMeta.types[reflectable.name]; + if (ngMeta.identifiers.containsKey(reflectable.name)) { + final compileDirectiveMetadata = ngMeta.identifiers[reflectable.name]; if (compileDirectiveMetadata is CompileDirectiveMetadata && compileDirectiveMetadata.template != null) { final compileDatum = new NormalizedComponentWithViewDirectives( @@ -106,6 +106,9 @@ class _CompileDataCreator { compileDatum.pipes .addAll(_resolveTypeMetadata(ngMetaMap, reflectable.pipes)); compileData[reflectable] = compileDatum; + + _resolveDiDependencyMetadata(ngMetaMap, compileDirectiveMetadata.type, compileDirectiveMetadata.type.diDeps); + _resolveProviderMetadata(ngMetaMap, compileDirectiveMetadata); } } } @@ -117,16 +120,16 @@ class _CompileDataCreator { var resolvedMetadata = []; for (var dep in prefixedTypes) { if (!ngMetaMap.containsKey(dep.prefix)) { - log.warning( + log.error( 'Missing prefix "${dep.prefix}" ' - 'needed by "${dep}" from metadata map', + 'needed by "${dep}" from metadata map,', asset: entryPoint); - continue; + return null; } final depNgMeta = ngMetaMap[dep.prefix]; - if (depNgMeta.types.containsKey(dep.name)) { - resolvedMetadata.add(depNgMeta.types[dep.name]); + if (depNgMeta.identifiers.containsKey(dep.name)) { + resolvedMetadata.add(depNgMeta.identifiers[dep.name]); } else if (depNgMeta.aliases.containsKey(dep.name)) { resolvedMetadata.addAll(depNgMeta.flatten(dep.name)); } else { @@ -141,6 +144,98 @@ class _CompileDataCreator { return resolvedMetadata; } + void _resolveProviderMetadata(Map ngMetaMap, CompileDirectiveMetadata dirMeta) { + final neededBy = dirMeta.type; + + if (dirMeta.providers == null) return; + + final resolvedProviders = []; + for (var provider in dirMeta.providers) { + final alias = _resolveAlias(ngMetaMap, neededBy, provider.token); + if (alias != null) { + resolvedProviders.addAll(alias.map((a) => new CompileProviderMetadata(token:a))); + } else { + provider.token = _resolveIdentifier(ngMetaMap, neededBy, provider.token); + if (provider.useClass != null) { + provider.useClass = _resolveIdentifier(ngMetaMap, neededBy, provider.useClass); + } + resolvedProviders.add(provider); + } + } + + dirMeta.providers = resolvedProviders; + } + + void _resolveDiDependencyMetadata( + Map ngMetaMap,CompileTypeMetadata neededBy, List deps) { + if (deps == null) return; + for (var dep in deps) { + dep.token = _resolveIdentifier(ngMetaMap, neededBy, dep.token); + } + } + + dynamic _resolveAlias(Map ngMetaMap, CompileTypeMetadata neededBy, dynamic id) { + if (id is String || id == null) return null; + + final prefix = id.prefix == null ? "" : id.prefix; + + if (!ngMetaMap.containsKey(prefix)) { + log.error( + 'Missing prefix "${prefix}" ' + 'needed by "${neededBy.name}" from metadata map', + asset: entryPoint); + return null; + } + + final depNgMeta = ngMetaMap[prefix]; + if (depNgMeta.aliases.containsKey(id.name)) { + return depNgMeta.flatten(id.name); + } else { + return null; + } + } + + dynamic _resolveIdentifier(Map ngMetaMap, CompileTypeMetadata neededBy, dynamic id) { + if (id is String || id == null) return id; + + final prefix = id.prefix == null ? "" : id.prefix; + + if (!ngMetaMap.containsKey(prefix)) { + log.error( + 'Missing prefix "${prefix}" ' + 'needed by "${neededBy.name}" from metadata map', + asset: entryPoint); + return null; + } + + final depNgMeta = ngMetaMap[prefix]; + if (depNgMeta.identifiers.containsKey(id.name)) { + return depNgMeta.identifiers[id.name]; + + } else if (_isPrimitive(id.name)) { + return id; + + } else if (id.name == "Window") { + return new CompileIdentifierMetadata(name: "Window", moduleUrl: 'dart:html'); + + } else if (id.name == "Clock") { + return new CompileIdentifierMetadata(name: "Clock", moduleUrl: 'dart:time'); + + } else if (id.name == "Profiler") { + return new CompileIdentifierMetadata(name: "Clock", moduleUrl: 'profiler'); + + } else { + log.error( + 'Missing identifier "${id.name}" ' + 'needed by "${neededBy.name}" from metadata map', + asset: entryPoint); + return null; + } + } + + bool _isPrimitive(String typeName) => + typeName == "String" || typeName == "Object" || typeName == "num" || typeName == "int" || typeName == "double" || typeName == "bool"; + Future> _readPlatformTypes( List inputPlatformTypes, String configOption) async { if (inputPlatformTypes == null) return const []; @@ -168,8 +263,8 @@ class _CompileDataCreator { if (jsonString != null && jsonString.isNotEmpty) { var newMetadata = new NgMeta.fromJson(JSON.decode(jsonString)); - if (newMetadata.types.containsKey(token)) { - return [newMetadata.types[token]]; + if (newMetadata.identifiers.containsKey(token)) { + return [newMetadata.identifiers[token]]; } else if (newMetadata.aliases.containsKey(token)) { return newMetadata.flatten(token); } else { @@ -194,7 +289,6 @@ class _CompileDataCreator { return map; } final resolver = const TransformerUrlResolver(); - ngMeta.ngDeps.imports .where((model) => !isDartCoreUri(model.uri)) .forEach((model) { @@ -242,6 +336,7 @@ class _CompileDataCreator { var ngMeta = retVal[prefix] = new NgMeta.empty(); for (var importAssetUri in prefixToImports[prefix]) { var metaAssetId = fromUri(toMetaExtension(importAssetUri)); + if (await reader.hasInput(metaAssetId)) { try { var jsonString = await reader.readAsString(metaAssetId); diff --git a/modules_dart/transform/lib/src/transform/template_compiler/generator.dart b/modules_dart/transform/lib/src/transform/template_compiler/generator.dart index 519feccb90..e36cae442d 100644 --- a/modules_dart/transform/lib/src/transform/template_compiler/generator.dart +++ b/modules_dart/transform/lib/src/transform/template_compiler/generator.dart @@ -42,16 +42,18 @@ Future processTemplates(AssetReader reader, AssetId assetId, var viewDefResults = await createCompileData( reader, assetId, platformDirectives, platformPipes); if (viewDefResults == null) return null; - final compileTypeMetadatas = viewDefResults.ngMeta.types.values; + final compileTypeMetadatas = viewDefResults.ngMeta.identifiers.values; if (compileTypeMetadatas.isNotEmpty) { var processor = new reg.Processor(); compileTypeMetadatas.forEach(processor.process); - viewDefResults.ngMeta.ngDeps.getters - .addAll(processor.getterNames.map((e) => e.sanitizedName)); - viewDefResults.ngMeta.ngDeps.setters - .addAll(processor.setterNames.map((e) => e.sanitizedName)); - viewDefResults.ngMeta.ngDeps.methods - .addAll(processor.methodNames.map((e) => e.sanitizedName)); + if (viewDefResults.ngMeta.ngDeps != null) { + viewDefResults.ngMeta.ngDeps.getters + .addAll(processor.getterNames.map((e) => e.sanitizedName)); + viewDefResults.ngMeta.ngDeps.setters + .addAll(processor.setterNames.map((e) => e.sanitizedName)); + viewDefResults.ngMeta.ngDeps.methods + .addAll(processor.methodNames.map((e) => e.sanitizedName)); + } } var templateCompiler = zone.templateCompiler; if (templateCompiler == null) { @@ -98,4 +100,4 @@ class Outputs { final SourceModule templatesSource; Outputs._(this.ngDeps, this.templatesSource); -} +} \ No newline at end of file diff --git a/modules_dart/transform/lib/src/transform/template_compiler/reflection/processor.dart b/modules_dart/transform/lib/src/transform/template_compiler/reflection/processor.dart index 404a3e29e5..d80e830f2e 100644 --- a/modules_dart/transform/lib/src/transform/template_compiler/reflection/processor.dart +++ b/modules_dart/transform/lib/src/transform/template_compiler/reflection/processor.dart @@ -14,7 +14,7 @@ class Processor implements CodegenModel { /// The names of all requested `method`s. final Set methodNames = new Set(); - void process(CompileMetadataWithType meta) { + void process(Object meta) { if (meta is CompileDirectiveMetadata) { if (meta.outputs != null) { meta.outputs.keys.forEach((eventName) { diff --git a/modules_dart/transform/test/transform/common/ng_meta_helper.dart b/modules_dart/transform/test/transform/common/ng_meta_helper.dart index 6f136163be..493b5355c6 100644 --- a/modules_dart/transform/test/transform/common/ng_meta_helper.dart +++ b/modules_dart/transform/test/transform/common/ng_meta_helper.dart @@ -82,4 +82,4 @@ CompileDirectiveMetadata createBaz([String moduleBase = 'asset:a']) => name: 'BazComponent', moduleUrl: '$moduleBase/export_cycle_files/baz.dart', selector: 'baz', - template: 'Baz'); + template: 'Baz'); \ No newline at end of file diff --git a/modules_dart/transform/test/transform/common/ng_meta_test.dart b/modules_dart/transform/test/transform/common/ng_meta_test.dart index d7f5c1051f..0657ecb168 100644 --- a/modules_dart/transform/test/transform/common/ng_meta_test.dart +++ b/modules_dart/transform/test/transform/common/ng_meta_test.dart @@ -8,7 +8,7 @@ import 'package:guinness/guinness.dart'; main() => allTests(); void allTests() { - var mockData = [ + var mockDirMetadata = [ CompileDirectiveMetadata.create(type: new CompileTypeMetadata(name: 'N1')), CompileDirectiveMetadata.create(type: new CompileTypeMetadata(name: 'N2')), CompileDirectiveMetadata.create(type: new CompileTypeMetadata(name: 'N3')), @@ -28,14 +28,16 @@ void allTests() { it('should be lossless', () { var a = new NgMeta.empty(); - a.types['T0'] = mockData[0]; - a.types['T1'] = mockData[1]; - a.types['T2'] = mockData[2]; - a.types['T3'] = mockData[3]; + a.identifiers['T0'] = mockDirMetadata[0]; + a.identifiers['T1'] = mockDirMetadata[1]; + a.identifiers['T2'] = mockDirMetadata[2]; + a.identifiers['T3'] = mockDirMetadata[3]; + a.aliases['a1'] = ['T1']; a.aliases['a2'] = ['a1']; a.aliases['a3'] = ['T3', 'a2']; a.aliases['a4'] = ['a3', 'T3']; + _checkSimilar(a, new NgMeta.fromJson(a.toJson())); }); }); @@ -43,35 +45,46 @@ void allTests() { describe('flatten', () { it('should include recursive aliases.', () { var a = new NgMeta.empty(); - a.types['T0'] = mockData[0]; - a.types['T1'] = mockData[1]; - a.types['T2'] = mockData[2]; - a.types['T3'] = mockData[3]; + a.identifiers['T0'] = mockDirMetadata[0]; + a.identifiers['T1'] = mockDirMetadata[1]; + a.identifiers['T2'] = mockDirMetadata[2]; + a.identifiers['T3'] = mockDirMetadata[3]; a.aliases['a1'] = ['T1']; a.aliases['a2'] = ['a1']; a.aliases['a3'] = ['T3', 'a2']; a.aliases['a4'] = ['a3', 'T0']; - expect(a.flatten('a4')).toEqual([mockData[3], mockData[1], mockData[0]]); + + expect(a.flatten('a4')).toEqual([mockDirMetadata[3], mockDirMetadata[1], mockDirMetadata[0]]); }); it('should detect cycles.', () { var a = new NgMeta.empty(); - a.types['T0'] = mockData[0]; - a.aliases['a1'] = ['T0', 'a1']; + a.identifiers['T0'] = mockDirMetadata[0]; + a.aliases['a1'] = ['T0', 'a2']; a.aliases['a2'] = ['a1']; - expect(a.flatten('a1')).toEqual([mockData[0]]); + + expect(() => a.flatten('a1')).toThrowWith(message: new RegExp('Cycle: a1 -> a2 -> a1.')); + }); + + it('should allow duplicates.', () { + var a = new NgMeta.empty(); + a.identifiers['T0'] = mockDirMetadata[0]; + a.aliases['a1'] = ['T0', 'a2']; + a.aliases['a2'] = ['T0']; + + expect(() => a.flatten('a1')).not.toThrow(); }); }); describe('merge', () { - it('should merge all types on addAll', () { + it('should merge all identifiers on addAll', () { var a = new NgMeta.empty(); var b = new NgMeta.empty(); - a.types['T0'] = mockData[0]; - b.types['T1'] = mockData[1]; + a.identifiers['T0'] = mockDirMetadata[0]; + b.identifiers['T1'] = mockDirMetadata[1]; a.addAll(b); - expect(a.types).toContain('T1'); - expect(a.types['T1']).toEqual(mockData[1]); + expect(a.identifiers).toContain('T1'); + expect(a.identifiers['T1']).toEqual(mockDirMetadata[1]); }); it('should merge all aliases on addAll', () { @@ -87,12 +100,12 @@ void allTests() { } _checkSimilar(NgMeta a, NgMeta b) { - expect(a.types.length).toEqual(b.types.length); + expect(a.identifiers.length).toEqual(b.identifiers.length); expect(a.aliases.length).toEqual(b.aliases.length); - for (var k in a.types.keys) { - expect(b.types).toContain(k); - var at = a.types[k]; - var bt = b.types[k]; + for (var k in a.identifiers.keys) { + expect(b.identifiers).toContain(k); + var at = a.identifiers[k]; + var bt = b.identifiers[k]; expect(at.type.name).toEqual(bt.type.name); } for (var k in a.aliases.keys) { diff --git a/modules_dart/transform/test/transform/directive_metadata_linker/all_tests.dart b/modules_dart/transform/test/transform/directive_metadata_linker/all_tests.dart index fdfef58938..d64c5542d9 100644 --- a/modules_dart/transform/test/transform/directive_metadata_linker/all_tests.dart +++ b/modules_dart/transform/test/transform/directive_metadata_linker/all_tests.dart @@ -27,13 +27,15 @@ void allTests() { var fooNgMeta, fooAssetId; var barNgMeta, barAssetId; var bazNgMeta, bazAssetId; + var aliasNgMeta, aliasAssetId; /// Call after making changes to `fooNgMeta`, `barNgMeta`, or `bazNgMeta` and /// before trying to read them from `reader`. final updateReader = () => reader ..addAsset(fooAssetId, JSON.encode(fooNgMeta.toJson())) ..addAsset(barAssetId, JSON.encode(barNgMeta.toJson())) - ..addAsset(bazAssetId, JSON.encode(bazNgMeta.toJson())); + ..addAsset(bazAssetId, JSON.encode(bazNgMeta.toJson())) + ..addAsset(aliasAssetId, JSON.encode(aliasNgMeta.toJson())); beforeEach(() { reader = new TestAssetReader(); @@ -41,19 +43,24 @@ void allTests() { // Establish some test NgMeta objects with one Component each. var fooComponentMeta = createFoo(moduleBase); fooNgMeta = new NgMeta(ngDeps: new NgDepsModel()); - fooNgMeta.types[fooComponentMeta.type.name] = fooComponentMeta; + fooNgMeta.identifiers[fooComponentMeta.type.name] = fooComponentMeta; var barComponentMeta = createBar(moduleBase); barNgMeta = new NgMeta(ngDeps: new NgDepsModel()); - barNgMeta.types[barComponentMeta.type.name] = barComponentMeta; + barNgMeta.identifiers[barComponentMeta.type.name] = barComponentMeta; var bazComponentMeta = createBaz(moduleBase); bazNgMeta = new NgMeta(ngDeps: new NgDepsModel()); - barNgMeta.types[bazComponentMeta.type.name] = bazComponentMeta; + barNgMeta.identifiers[bazComponentMeta.type.name] = bazComponentMeta; + + aliasNgMeta = new NgMeta(ngDeps: new NgDepsModel()); + aliasNgMeta.aliases["Providers"] = ["someAlias"]; + aliasNgMeta.definesAlias = true; fooAssetId = new AssetId('a', toSummaryExtension('lib/foo.dart')); barAssetId = new AssetId('a', toSummaryExtension('lib/bar.dart')); bazAssetId = new AssetId('a', toSummaryExtension('lib/baz.dart')); + aliasAssetId = new AssetId('a', toSummaryExtension('lib/alais.dart')); updateReader(); }); @@ -63,11 +70,11 @@ void allTests() { updateReader(); var extracted = await _testLink(reader, fooAssetId); - expect(extracted.types).toContain('FooComponent'); - expect(extracted.types).toContain('BarComponent'); + expect(extracted.identifiers).toContain('FooComponent'); + expect(extracted.identifiers).toContain('BarComponent'); - expect(extracted.types['FooComponent'].selector).toEqual('foo'); - expect(extracted.types['BarComponent'].selector).toEqual('bar'); + expect(extracted.identifiers['FooComponent'].selector).toEqual('foo'); + expect(extracted.identifiers['BarComponent'].selector).toEqual('bar'); }); it('should include `DirectiveMetadata` recursively from exported files.', @@ -77,13 +84,33 @@ void allTests() { updateReader(); var extracted = await _testLink(reader, fooAssetId); - expect(extracted.types).toContain('FooComponent'); - expect(extracted.types).toContain('BarComponent'); - expect(extracted.types).toContain('BazComponent'); + expect(extracted.identifiers).toContain('FooComponent'); + expect(extracted.identifiers).toContain('BarComponent'); + expect(extracted.identifiers).toContain('BazComponent'); - expect(extracted.types['FooComponent'].selector).toEqual('foo'); - expect(extracted.types['BarComponent'].selector).toEqual('bar'); - expect(extracted.types['BazComponent'].selector).toEqual('baz'); + expect(extracted.identifiers['FooComponent'].selector).toEqual('foo'); + expect(extracted.identifiers['BarComponent'].selector).toEqual('bar'); + expect(extracted.identifiers['BazComponent'].selector).toEqual('baz'); + }); + + it('should include metadata recursively from imported files when they are aliases.', + () async { + aliasNgMeta.ngDeps.imports.add(new ImportModel()..uri = 'bar.dart'); + updateReader(); + + var extracted = await _testLink(reader, aliasAssetId); + expect(extracted.identifiers).toContain('BarComponent'); + }); + + it('should NOT include metadata recursively from imported files when no aliases defined.', + () async { + fooNgMeta.ngDeps.imports.add(new ImportModel()..uri = 'bar.dart'); + barNgMeta.ngDeps.imports.add(new ImportModel()..uri = 'baz.dart'); + updateReader(); + + var extracted = await _testLink(reader, fooAssetId); + expect(extracted.identifiers).not.toContain('BarComponent'); + expect(extracted.identifiers).not.toContain('BazComponent'); }); it('should handle `DirectiveMetadata` export cycles gracefully.', () async { @@ -93,9 +120,9 @@ void allTests() { updateReader(); var extracted = await _testLink(reader, bazAssetId); - expect(extracted.types).toContain('FooComponent'); - expect(extracted.types).toContain('BarComponent'); - expect(extracted.types).toContain('BazComponent'); + expect(extracted.identifiers).toContain('FooComponent'); + expect(extracted.identifiers).toContain('BarComponent'); + expect(extracted.identifiers).toContain('BazComponent'); }); it( @@ -109,11 +136,11 @@ void allTests() { var extracted = await _testLink(reader, fooAssetId); - expect(extracted.types).toContain('FooComponent'); - expect(extracted.types).toContain('BarComponent'); + expect(extracted.identifiers).toContain('FooComponent'); + expect(extracted.identifiers).toContain('BarComponent'); - expect(extracted.types['FooComponent'].selector).toEqual('foo'); - expect(extracted.types['BarComponent'].selector).toEqual('bar'); + expect(extracted.identifiers['FooComponent'].selector).toEqual('foo'); + expect(extracted.identifiers['BarComponent'].selector).toEqual('bar'); }); }); 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 871141f9e3..86a4a8b58a 100644 --- a/modules_dart/transform/test/transform/directive_processor/all_tests.dart +++ b/modules_dart/transform/test/transform/directive_processor/all_tests.dart @@ -6,6 +6,7 @@ import 'package:barback/barback.dart'; import 'package:dart_style/dart_style.dart'; import 'package:guinness/guinness.dart'; +import 'package:angular2/src/compiler/directive_metadata.dart' show CompileIdentifierMetadata; import 'package:angular2/src/core/change_detection/change_detection.dart'; import 'package:angular2/src/platform/server/html_adapter.dart'; import 'package:angular2/src/core/linker/interfaces.dart' show LifecycleHooks; @@ -317,10 +318,10 @@ void allTests() { it('should include hooks for implemented types (single)', () async { var ngMeta = await _testCreateModel('interfaces_files/soup.dart'); - expect(ngMeta.types.isNotEmpty).toBeTrue(); - expect(ngMeta.types['ChangingSoupComponent']).toBeNotNull(); - expect(ngMeta.types['ChangingSoupComponent'].selector).toEqual('[soup]'); - expect(ngMeta.types['ChangingSoupComponent'].lifecycleHooks) + expect(ngMeta.identifiers.isNotEmpty).toBeTrue(); + expect(ngMeta.identifiers['ChangingSoupComponent']).toBeNotNull(); + expect(ngMeta.identifiers['ChangingSoupComponent'].selector).toEqual('[soup]'); + expect(ngMeta.identifiers['ChangingSoupComponent'].lifecycleHooks) .toContain(LifecycleHooks.OnChanges); }); @@ -328,10 +329,10 @@ void allTests() { var ngMeta = await _testCreateModel( 'multiple_interface_lifecycle_files/soup.dart'); - expect(ngMeta.types.isNotEmpty).toBeTrue(); - expect(ngMeta.types['MultiSoupComponent']).toBeNotNull(); - expect(ngMeta.types['MultiSoupComponent'].selector).toEqual('[soup]'); - expect(ngMeta.types['MultiSoupComponent'].lifecycleHooks) + expect(ngMeta.identifiers.isNotEmpty).toBeTrue(); + expect(ngMeta.identifiers['MultiSoupComponent']).toBeNotNull(); + expect(ngMeta.identifiers['MultiSoupComponent'].selector).toEqual('[soup]'); + expect(ngMeta.identifiers['MultiSoupComponent'].lifecycleHooks) ..toContain(LifecycleHooks.OnChanges) ..toContain(LifecycleHooks.OnDestroy) ..toContain(LifecycleHooks.OnInit); @@ -345,18 +346,18 @@ void allTests() { 'absolute_url_expression_files/hello.dart', reader: fakeReader); - expect(ngMeta.types.isNotEmpty).toBeTrue(); - expect(ngMeta.types['HelloCmp']).toBeNotNull(); - expect(ngMeta.types['HelloCmp'].selector).toEqual('hello-app'); + expect(ngMeta.identifiers.isNotEmpty).toBeTrue(); + expect(ngMeta.identifiers['HelloCmp']).toBeNotNull(); + expect(ngMeta.identifiers['HelloCmp'].selector).toEqual('hello-app'); }); it('should populate all provided values for Components & Directives', () async { var ngMeta = await _testCreateModel('unusual_component_files/hello.dart'); - expect(ngMeta.types.isNotEmpty).toBeTrue(); + expect(ngMeta.identifiers.isNotEmpty).toBeTrue(); - var component = ngMeta.types['UnusualComp']; + var component = ngMeta.identifiers['UnusualComp']; expect(component).toBeNotNull(); expect(component.selector).toEqual('unusual-comp'); expect(component.isComponent).toBeTrue(); @@ -370,7 +371,7 @@ void allTests() { expect(component.hostAttributes).toContain('hostKey'); expect(component.hostAttributes['hostKey']).toEqual('hostValue'); - var directive = ngMeta.types['UnusualDirective']; + var directive = ngMeta.identifiers['UnusualDirective']; expect(directive).toBeNotNull(); expect(directive.selector).toEqual('unusual-directive'); expect(directive.isComponent).toBeFalse(); @@ -388,10 +389,10 @@ void allTests() { it('should include hooks for implemented types (single)', () async { var ngMeta = await _testCreateModel('interfaces_files/soup.dart'); - expect(ngMeta.types.isNotEmpty).toBeTrue(); - expect(ngMeta.types['ChangingSoupComponent']).toBeNotNull(); - expect(ngMeta.types['ChangingSoupComponent'].selector).toEqual('[soup]'); - expect(ngMeta.types['ChangingSoupComponent'].lifecycleHooks) + expect(ngMeta.identifiers.isNotEmpty).toBeTrue(); + expect(ngMeta.identifiers['ChangingSoupComponent']).toBeNotNull(); + expect(ngMeta.identifiers['ChangingSoupComponent'].selector).toEqual('[soup]'); + expect(ngMeta.identifiers['ChangingSoupComponent'].lifecycleHooks) .toContain(LifecycleHooks.OnChanges); }); @@ -399,10 +400,10 @@ void allTests() { var ngMeta = await _testCreateModel( 'multiple_interface_lifecycle_files/soup.dart'); - expect(ngMeta.types.isNotEmpty).toBeTrue(); - expect(ngMeta.types['MultiSoupComponent']).toBeNotNull(); - expect(ngMeta.types['MultiSoupComponent'].selector).toEqual('[soup]'); - expect(ngMeta.types['MultiSoupComponent'].lifecycleHooks) + expect(ngMeta.identifiers.isNotEmpty).toBeTrue(); + expect(ngMeta.identifiers['MultiSoupComponent']).toBeNotNull(); + expect(ngMeta.identifiers['MultiSoupComponent'].selector).toEqual('[soup]'); + expect(ngMeta.identifiers['MultiSoupComponent'].lifecycleHooks) ..toContain(LifecycleHooks.OnChanges) ..toContain(LifecycleHooks.OnDestroy) ..toContain(LifecycleHooks.OnInit); @@ -416,10 +417,10 @@ void allTests() { 'absolute_url_expression_files/hello.dart', reader: fakeReader); - expect(ngMeta.types.isNotEmpty).toBeTrue(); - expect(ngMeta.types['HelloCmp']).toBeNotNull(); - expect(ngMeta.types['HelloCmp'].template).toBeNotNull(); - expect(ngMeta.types['HelloCmp'].template.templateUrl) + expect(ngMeta.identifiers.isNotEmpty).toBeTrue(); + expect(ngMeta.identifiers['HelloCmp']).toBeNotNull(); + expect(ngMeta.identifiers['HelloCmp'].template).toBeNotNull(); + expect(ngMeta.identifiers['HelloCmp'].template.templateUrl) .toEqual('asset:other_package/lib/template.html'); }); @@ -445,6 +446,49 @@ void allTests() { }); }); + describe("identifiers", () { + it("should populate `identifier` with class types.", () async { + var model = (await _testCreateModel('identifiers/classes.dart')); + final moduleUrl = "asset:angular2/test/transform/directive_processor/identifiers/classes.dart"; + expect(model.identifiers['Service1'].name).toEqual('Service1'); + expect(model.identifiers['Service1'].moduleUrl).toEqual(moduleUrl); + expect(model.identifiers['Service2'].name).toEqual('Service2'); + expect(model.identifiers['Service2'].moduleUrl).toEqual(moduleUrl); + }); + + it("should populate `identifier` with constants.", () async { + var model = (await _testCreateModel('identifiers/constants.dart')); + final moduleUrl = "asset:angular2/test/transform/directive_processor/identifiers/constants.dart"; + expect(model.identifiers['a']). + toHaveSameProps(new CompileIdentifierMetadata(name: 'a', moduleUrl: moduleUrl)); + expect(model.identifiers['b']). + toHaveSameProps(new CompileIdentifierMetadata(name: 'b', moduleUrl: moduleUrl)); + expect(model.identifiers['c']).toBeNull(); + }); + + it("should populate `identifier` with class names that do not have @Injectable;'.", () async { + var model = (await _testCreateModel('identifiers/classes_no_injectable.dart')); + final moduleUrl = "asset:angular2/test/transform/directive_processor/identifiers/classes_no_injectable.dart"; + expect(model.identifiers['ClassA']). + toHaveSameProps(new CompileIdentifierMetadata(name: 'ClassA', moduleUrl: moduleUrl)); + }); + + it("should populate `identifier` with typedefs.", () async { + var model = (await _testCreateModel('identifiers/typedefs.dart')); + final moduleUrl = "asset:angular2/test/transform/directive_processor/identifiers/typedefs.dart"; + expect(model.identifiers['TypeDef']). + toHaveSameProps(new CompileIdentifierMetadata(name: 'TypeDef', moduleUrl: moduleUrl)); + }); + + it("should populate `identifier` with enums.", () async { + var model = (await _testCreateModel('identifiers/enums.dart')); + final moduleUrl = "asset:angular2/test/transform/directive_processor/identifiers/enums.dart"; + + expect(model.identifiers['Enum']). + toHaveSameProps(new CompileIdentifierMetadata(name: 'Enum', moduleUrl: moduleUrl)); + }); + }); + describe('directives', () { final reflectableNamed = (NgDepsModel model, String name) { return model.reflectables @@ -537,27 +581,128 @@ void allTests() { ..prefix = 'dep2'); }); + it('should populate `diDependency`.', + () async { + var cmp = + (await _testCreateModel('directives_files/components.dart')).identifiers['ComponentWithDiDeps']; + + expect(cmp).toBeNotNull(); + var deps = cmp.type.diDeps; + expect(deps).toBeNotNull(); + expect(deps.length).toEqual(2); + expect(deps[0].token.name).toEqual("ServiceDep"); + expect(deps[1].token.name).toEqual("ServiceDep"); + }); + + it('should populate `diDependency` using a string token.', + () async { + var cmp = + (await _testCreateModel('directives_files/components.dart')).identifiers['ComponentWithDiDepsStrToken']; + + var deps = cmp.type.diDeps; + expect(deps).toBeNotNull(); + expect(deps.length).toEqual(1); + expect(deps[0].token).toEqual("StringDep"); + }); + + it('should populate `services`.', + () async { + var service = + (await _testCreateModel('directives_files/services.dart')).identifiers['Service']; + + expect(service).toBeNotNull(); + + var deps = service.diDeps; + expect(deps).toBeNotNull(); + expect(deps.length).toEqual(2); + expect(deps[0].token.name).toEqual("ServiceDep"); + expect(deps[1].token.name).toEqual("ServiceDep"); + }); + + it('should populate `providers` using types.', + () async { + var cmp = + (await _testCreateModel('directives_files/components.dart')).identifiers['ComponentWithProvidersTypes']; + + expect(cmp).toBeNotNull(); + expect(cmp.providers).toBeNotNull(); + expect(cmp.providers.length).toEqual(2); + + var firstToken = cmp.providers.first.token; + expect(firstToken.prefix).toEqual(null); + expect(firstToken.name).toEqual("ServiceDep"); + + var secondToken = cmp.providers[1].token; + expect(secondToken.prefix).toEqual("dep2"); + expect(secondToken.name).toEqual("ServiceDep"); + }); + + it('should populate `providers` using useClass.', + () async { + var cmp = + (await _testCreateModel('directives_files/components.dart')).identifiers['ComponentWithProvidersUseClass']; + + expect(cmp).toBeNotNull(); + expect(cmp.providers).toBeNotNull(); + expect(cmp.providers.length).toEqual(1); + + var token = cmp.providers.first.token; + var useClass = cmp.providers.first.useClass; + expect(token.prefix).toEqual(null); + expect(token.name).toEqual("ServiceDep"); + + expect(useClass.prefix).toEqual(null); + expect(useClass.name).toEqual("ServiceDep"); + }); + + it('should populate `providers` using a string token.', + () async { + var cmp = + (await _testCreateModel('directives_files/components.dart')).identifiers['ComponentWithProvidersStringToken']; + + expect(cmp).toBeNotNull(); + expect(cmp.providers).toBeNotNull(); + expect(cmp.providers.length).toEqual(1); + + var token = cmp.providers.first.token; + expect(token).toEqual("StringDep"); + }); + + it('should populate `providers` using a const token.', + () async { + var cmp = + (await _testCreateModel('directives_files/components.dart')).identifiers['ComponentWithProvidersConstToken']; + + expect(cmp).toBeNotNull(); + expect(cmp.providers).toBeNotNull(); + expect(cmp.providers.length).toEqual(1); + + var token = cmp.providers.first.token; + expect(token.name).toEqual("ServiceDep"); + expect(token.constConstructor).toEqual(true); + }); + it('should merge `outputs` from the annotation and fields.', () async { var model = await _testCreateModel('directives_files/components.dart'); - expect(model.types['ComponentWithOutputs'].outputs).toEqual( + expect(model.identifiers['ComponentWithOutputs'].outputs).toEqual( {'a': 'a', 'b': 'b', 'c': 'renamed', 'd': 'd', 'e': 'get-renamed'}); }); it('should merge `inputs` from the annotation and fields.', () async { var model = await _testCreateModel('directives_files/components.dart'); - expect(model.types['ComponentWithInputs'].inputs).toEqual( + expect(model.identifiers['ComponentWithInputs'].inputs).toEqual( {'a': 'a', 'b': 'b', 'c': 'renamed', 'd': 'd', 'e': 'set-renamed'}); }); it('should merge host bindings from the annotation and fields.', () async { var model = await _testCreateModel('directives_files/components.dart'); - expect(model.types['ComponentWithHostBindings'].hostProperties) + expect(model.identifiers['ComponentWithHostBindings'].hostProperties) .toEqual({'a': 'a', 'b': 'b', 'renamed': 'c', 'd': 'd', 'get-renamed': 'e'}); }); it('should merge host listeners from the annotation and fields.', () async { var model = await _testCreateModel('directives_files/components.dart'); - expect(model.types['ComponentWithHostListeners'].hostListeners).toEqual({ + expect(model.identifiers['ComponentWithHostListeners'].hostListeners).toEqual({ 'a': 'onA()', 'b': 'onB()', 'c': 'onC(\$event.target,\$event.target.value)' @@ -617,13 +762,13 @@ void allTests() { describe('pipes', () { it('should read the pipe name', () async { var model = await _testCreateModel('pipe_files/pipes.dart'); - expect(model.types['NameOnlyPipe'].name).toEqual('nameOnly'); - expect(model.types['NameOnlyPipe'].pure).toBe(false); + expect(model.identifiers['NameOnlyPipe'].name).toEqual('nameOnly'); + expect(model.identifiers['NameOnlyPipe'].pure).toBe(false); }); it('should read the pure flag', () async { var model = await _testCreateModel('pipe_files/pipes.dart'); - expect(model.types['NameAndPurePipe'].pure).toBe(true); + expect(model.identifiers['NameAndPurePipe'].pure).toBe(true); }); }); } 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 b61aabc9e8..f54eaa66bb 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 @@ -1,7 +1,7 @@ library angular2.test.transform.directive_processor.directive_files.components; import 'package:angular2/angular2.dart' - show Component, Directive, View, NgElement, Output, Input; + show Component, Directive, View, NgElement, Output, Input, Provider; import 'dep1.dart'; import 'dep2.dart' as dep2; @@ -84,3 +84,43 @@ class ComponentWithHostListeners { @HostListener('c', ['\$event.target', '\$event.target.value']) void onC( t, v) {} } + +@Component( + selector: 'component-with-providers-types', + template: '', + providers: [ServiceDep, dep2.ServiceDep]) +class ComponentWithProvidersTypes {} + +@Component( + selector: 'component-with-providers-string-token', + template: '', + providers: [const Provider("StringDep", useClass: ServiceDep)]) +class ComponentWithProvidersStringToken {} + +@Component( + selector: 'ComponentWithProvidersConstToken', + template: '', + providers: [const Provider(const ServiceDep(), useClass: ServiceDep)]) +class ComponentWithProvidersConstToken { + ComponentWithProvidersConstToken(); +} + +@Component( + selector: 'component-with-providers-use-class', + template: '', + providers: [const Provider(ServiceDep, useClass: ServiceDep)]) +class ComponentWithProvidersUseClass {} + +@Component( + selector: 'component-with-di-deps', + template: '') +class ComponentWithDiDeps { + ComponentWithDiDeps(ServiceDep arg1, @Inject(ServiceDep) arg2); +} + +@Component( + selector: 'component-with-di-deps-string-token', + template: '') +class ComponentWithDiDepsStrToken { + ComponentWithDiDepsStrToken(@Inject("StringDep") arg1); +} diff --git a/modules_dart/transform/test/transform/directive_processor/directives_files/dep1.dart b/modules_dart/transform/test/transform/directive_processor/directives_files/dep1.dart index ec5d81fdab..2c3bb38bb6 100644 --- a/modules_dart/transform/test/transform/directive_processor/directives_files/dep1.dart +++ b/modules_dart/transform/test/transform/directive_processor/directives_files/dep1.dart @@ -1,6 +1,6 @@ library angular2.test.transform.directive_processor.directive_files.dep1; -import 'package:angular2/angular2.dart' show Component, Directive, View, Pipe; +import 'package:angular2/angular2.dart' show Component, Directive, View, Pipe, Injectable; @Component(selector: 'dep1') @View(template: 'Dep1') @@ -8,3 +8,8 @@ class Dep {} @Pipe(name: 'dep1') class PipeDep {} + +@Injectable() +class ServiceDep { + const ServiceDep(); +} diff --git a/modules_dart/transform/test/transform/directive_processor/directives_files/dep2.dart b/modules_dart/transform/test/transform/directive_processor/directives_files/dep2.dart index 4d027c8a13..cc1787d998 100644 --- a/modules_dart/transform/test/transform/directive_processor/directives_files/dep2.dart +++ b/modules_dart/transform/test/transform/directive_processor/directives_files/dep2.dart @@ -1,6 +1,6 @@ library angular2.test.transform.directive_processor.directive_files.dep2; -import 'package:angular2/angular2.dart' show Component, Directive, View, Pipe; +import 'package:angular2/angular2.dart' show Component, Directive, View, Pipe, Injectable; @Component(selector: 'dep2') @View(template: 'Dep2') @@ -8,3 +8,6 @@ class Dep {} @Pipe(name: 'dep2') class PipeDep {} + +@Injectable() +class ServiceDep {} \ No newline at end of file diff --git a/modules_dart/transform/test/transform/directive_processor/directives_files/services.dart b/modules_dart/transform/test/transform/directive_processor/directives_files/services.dart new file mode 100644 index 0000000000..1aae91f5f1 --- /dev/null +++ b/modules_dart/transform/test/transform/directive_processor/directives_files/services.dart @@ -0,0 +1,10 @@ +library angular2.test.transform.directive_processor.directive_files.components; + +import 'package:angular2/angular2.dart' + show Injectable, Inject; +import 'dep1.dart'; + +@Injectable() +class Service { + Service(ServiceDep arg1, @Inject(ServiceDep) arg2); +} \ No newline at end of file diff --git a/modules_dart/transform/test/transform/directive_processor/identifiers/classes.dart b/modules_dart/transform/test/transform/directive_processor/identifiers/classes.dart new file mode 100644 index 0000000000..e9060fb6de --- /dev/null +++ b/modules_dart/transform/test/transform/directive_processor/identifiers/classes.dart @@ -0,0 +1,11 @@ +library angular2.test.transform.directive_processor.identifiers.classes; + +import 'package:angular2/angular2.dart' show Injectable; + +@Injectable() +class Service1 {} + +@Injectable() +class Service2 {} + +class Service3 {} \ No newline at end of file diff --git a/modules_dart/transform/test/transform/directive_processor/identifiers/classes_no_injectable.dart b/modules_dart/transform/test/transform/directive_processor/identifiers/classes_no_injectable.dart new file mode 100644 index 0000000000..5c7f531512 --- /dev/null +++ b/modules_dart/transform/test/transform/directive_processor/identifiers/classes_no_injectable.dart @@ -0,0 +1,3 @@ +library angular2.test.transform.directive_processor.identifiers.classes_no_injectable; + +abstract class ClassA {} \ No newline at end of file diff --git a/modules_dart/transform/test/transform/directive_processor/identifiers/constants.dart b/modules_dart/transform/test/transform/directive_processor/identifiers/constants.dart new file mode 100644 index 0000000000..4567eb94f8 --- /dev/null +++ b/modules_dart/transform/test/transform/directive_processor/identifiers/constants.dart @@ -0,0 +1,5 @@ +library angular2.test.transform.directive_processor.identifiers.constants; + +const a = "a"; +const b = "b"; +var c = "c"; \ No newline at end of file diff --git a/modules_dart/transform/test/transform/directive_processor/identifiers/enums.dart b/modules_dart/transform/test/transform/directive_processor/identifiers/enums.dart new file mode 100644 index 0000000000..e611f83dd5 --- /dev/null +++ b/modules_dart/transform/test/transform/directive_processor/identifiers/enums.dart @@ -0,0 +1,5 @@ +library angular2.test.transform.directive_processor.identifiers.enums; + +enum Enum { + one +} \ No newline at end of file diff --git a/modules_dart/transform/test/transform/directive_processor/identifiers/typedefs.dart b/modules_dart/transform/test/transform/directive_processor/identifiers/typedefs.dart new file mode 100644 index 0000000000..53e32006ad --- /dev/null +++ b/modules_dart/transform/test/transform/directive_processor/identifiers/typedefs.dart @@ -0,0 +1,3 @@ +library angular2.test.transform.directive_processor.identifiers.typedefs; + +typedef String TypeDef(String); \ No newline at end of file diff --git a/modules_dart/transform/test/transform/integration/list_of_types_files/expected/bar.template.dart b/modules_dart/transform/test/transform/integration/list_of_types_files/expected/bar.template.dart index 963598ded4..bc1690c615 100644 --- a/modules_dart/transform/test/transform/integration/list_of_types_files/expected/bar.template.dart +++ b/modules_dart/transform/test/transform/integration/list_of_types_files/expected/bar.template.dart @@ -5,6 +5,7 @@ import 'package:angular2/src/core/reflection/reflection.dart' as _ngRef; import 'package:angular2/src/core/metadata.dart'; import 'foo.dart'; import 'package:angular2/src/core/metadata.template.dart' as i0; +import 'foo.template.dart' as i1; export 'bar.dart'; var _visited = false; @@ -22,4 +23,5 @@ void initReflector() { const [MyContext] ], (MyContext c) => new MyComponent(c))); i0.initReflector(); + i1.initReflector(); } diff --git a/modules_dart/transform/test/transform/integration/two_deps_files/bar.dart b/modules_dart/transform/test/transform/integration/two_deps_files/bar.dart index f92c12b5ac..b8aee72d6a 100644 --- a/modules_dart/transform/test/transform/integration/two_deps_files/bar.dart +++ b/modules_dart/transform/test/transform/integration/two_deps_files/bar.dart @@ -7,8 +7,8 @@ import 'foo.dart' as prefix; @View(template: '') class MyComponent { final prefix.MyContext c; - final String generatedValue; - MyComponent(this.c, String inValue) { - generatedValue = 'generated ' + inValue; + final prefix.MyDep generatedValue; + MyComponent(this.c, prefix.MyDep inValue) { + generatedValue = inValue; } } diff --git a/modules_dart/transform/test/transform/integration/two_deps_files/expected/bar.template.dart b/modules_dart/transform/test/transform/integration/two_deps_files/expected/bar.template.dart index f2a91f5a01..60ce666998 100644 --- a/modules_dart/transform/test/transform/integration/two_deps_files/expected/bar.template.dart +++ b/modules_dart/transform/test/transform/integration/two_deps_files/expected/bar.template.dart @@ -5,6 +5,7 @@ import 'package:angular2/src/core/reflection/reflection.dart' as _ngRef; import 'package:angular2/src/core/metadata.dart'; import 'foo.dart' as prefix; import 'package:angular2/src/core/metadata.template.dart' as i0; +import 'foo.template.dart' as i1; export 'bar.dart'; var _visited = false; @@ -16,15 +17,16 @@ void initReflector() { MyComponent, new _ngRef.ReflectionInfo( const [ - const Component(selector: 'soup'), - const View(template: ''), - hostViewFactory_MyComponent - ], + const Component(selector: 'soup'), + const View(template: ''), + hostViewFactory_MyComponent + ], const [ - const [prefix.MyContext], - const [String] - ], - (prefix.MyContext c, String inValue) => - new MyComponent(c, inValue))); + const [prefix.MyContext], + const [prefix.MyDep] + ], + (prefix.MyContext c, prefix.MyDep inValue) => + new MyComponent(c, inValue))); i0.initReflector(); -} + i1.initReflector(); +} \ No newline at end of file diff --git a/modules_dart/transform/test/transform/integration/two_deps_files/foo.dart b/modules_dart/transform/test/transform/integration/two_deps_files/foo.dart index e2ccc6af66..f472170871 100644 --- a/modules_dart/transform/test/transform/integration/two_deps_files/foo.dart +++ b/modules_dart/transform/test/transform/integration/two_deps_files/foo.dart @@ -1,6 +1,11 @@ library foo; +import 'package:angular2/angular2.dart'; + +@Injectable() +class MyDep {} + class MyContext { - final String selector; + final MyDep selector; const MyContext(this.selector); } 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 130dcdd9c6..27427e2dd6 100644 --- a/modules_dart/transform/test/transform/template_compiler/all_tests.dart +++ b/modules_dart/transform/test/transform/template_compiler/all_tests.dart @@ -15,7 +15,9 @@ import 'package:angular2/src/transform/common/code/ng_deps_code.dart'; import 'package:angular2/src/transform/common/code/source_module.dart'; import 'package:angular2/src/transform/common/zone.dart' as zone; import 'package:angular2/src/transform/template_compiler/generator.dart'; +import 'package:angular2/src/transform/template_compiler/compile_data_creator.dart'; +import 'package:angular2/src/transform/common/model/parameter_model.pb.dart'; import '../common/compile_directive_metadata/ng_for.ng_meta.dart' as ngMeta; import '../common/ng_meta_helper.dart'; import '../common/read_file.dart'; @@ -57,7 +59,7 @@ void allTests() { fooNgMeta = new NgMeta(ngDeps: new NgDepsModel() ..libraryUri = 'test.foo' ..reflectables.add(new ReflectionInfoModel()..name = fooComponentMeta.type.name)); - fooNgMeta.types[fooComponentMeta.type.name] = fooComponentMeta; + fooNgMeta.identifiers[fooComponentMeta.type.name] = fooComponentMeta; barComponentMeta = createBar(moduleBase); barPipeMeta = createBarPipe(moduleBase); @@ -65,14 +67,14 @@ void allTests() { ..libraryUri = 'test.bar' ..reflectables.add(new ReflectionInfoModel()..name = barPipeMeta.type.name) ..reflectables.add(new ReflectionInfoModel()..name = barComponentMeta.type.name)); - barNgMeta.types[barComponentMeta.type.name] = barComponentMeta; - barNgMeta.types[barPipeMeta.type.name] = barPipeMeta; + barNgMeta.identifiers[barComponentMeta.type.name] = barComponentMeta; + barNgMeta.identifiers[barPipeMeta.type.name] = barPipeMeta; bazComponentMeta = createBaz(moduleBase); bazNgMeta = new NgMeta(ngDeps: new NgDepsModel() ..libraryUri = 'test.baz' ..reflectables.add(new ReflectionInfoModel()..name = bazComponentMeta.type.name)); - barNgMeta.types[bazComponentMeta.type.name] = bazComponentMeta; + barNgMeta.identifiers[bazComponentMeta.type.name] = bazComponentMeta; fooAssetId = new AssetId('a', 'lib/foo.ng_meta.json'); barAssetId = new AssetId('a', 'lib/bar.ng_meta.json'); @@ -114,6 +116,145 @@ void allTests() { expect(_generatedCode(outputs)).not.toContain('notifyDispatcher'); }); + it('should generate generate diDeps of injectable services.', () async { + bazNgMeta.identifiers['Service2'] = new CompileTypeMetadata( + name: 'Service2', + moduleUrl: 'moduleUrl'); + + barNgMeta.identifiers['Service'] = new CompileTypeMetadata( + name: 'Service', + moduleUrl: 'moduleUrl', + diDeps: [new CompileDiDependencyMetadata(token: new CompileIdentifierMetadata(name: 'Service2'))]); + barNgMeta.ngDeps.imports.add(new ImportModel()..uri = 'package:a/baz.dart'); + + fooComponentMeta.template = new CompileTemplateMetadata(template: "import 'bar.dart';"); + fooComponentMeta.providers = [ + new CompileProviderMetadata( + token: new CompileIdentifierMetadata(name: 'Service'), + useClass: new CompileTypeMetadata(name: 'Service') + ) + ]; + + final viewAnnotation = new AnnotationModel()..name = 'View'..isView = true; + final reflectable = fooNgMeta.ngDeps.reflectables.first; + reflectable.annotations.add(viewAnnotation); + fooNgMeta.ngDeps.imports.add(new ImportModel()..uri = 'package:a/bar.dart'); + + updateReader(); + + final viewDefResults = await createCompileData(reader, fooAssetId, [], []); + final cmp = viewDefResults.viewDefinitions.values.first.component; + + expect(cmp.providers.length).toEqual(1); + + expect(cmp.providers[0].useClass.name).toEqual("Service"); + expect(cmp.providers[0].useClass.diDeps.first.token.name).toEqual("Service2"); + }); + + it('should generate providers from types.', () async { + barNgMeta.identifiers['Service'] = new CompileTypeMetadata(name: 'Service', moduleUrl: 'moduleUrl'); + + fooComponentMeta.template = new CompileTemplateMetadata(template: "import 'bar.dart';"); + fooComponentMeta.providers = [new CompileProviderMetadata(token: new CompileIdentifierMetadata(name: 'Service'))]; + fooComponentMeta.type.diDeps = [new CompileDiDependencyMetadata(token: new CompileIdentifierMetadata(name: 'Service'))]; + + final viewAnnotation = new AnnotationModel()..name = 'View'..isView = true; + final reflectable = fooNgMeta.ngDeps.reflectables.first; + reflectable.annotations.add(viewAnnotation); + fooNgMeta.ngDeps.imports.add(new ImportModel()..uri = 'package:a/bar.dart'); + + updateReader(); + + final viewDefResults = await createCompileData(reader, fooAssetId, [], []); + final cmp = viewDefResults.viewDefinitions.values.first.component; + + expect(cmp.providers.length).toEqual(1); + + expect(cmp.providers[0].token.name).toEqual("Service"); + expect(cmp.providers[0].token.moduleUrl).toEqual("moduleUrl"); + + expect(cmp.type.diDeps.length).toEqual(1); + expect(cmp.type.diDeps[0].token.name).toEqual("Service"); + expect(cmp.type.diDeps[0].token.moduleUrl).toEqual("moduleUrl"); + }); + + it('should generate providers from Provider objects (references).', () async { + barNgMeta.identifiers['Service1'] = new CompileTypeMetadata(name: 'Service1', moduleUrl: 'moduleUrl'); + 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'))]; + + final viewAnnotation = new AnnotationModel()..name = 'View'..isView = true; + final reflectable = fooNgMeta.ngDeps.reflectables.first; + reflectable.annotations.add(viewAnnotation); + fooNgMeta.ngDeps.imports.add(new ImportModel()..uri = 'package:a/bar.dart'); + + updateReader(); + + final viewDefResults = await createCompileData(reader, fooAssetId, [], []); + final cmp = viewDefResults.viewDefinitions.values.first.component; + + expect(cmp.providers.length).toEqual(1); + + 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"); + }); + + it('should generate providers from Provider objects (literals).', () async { + barNgMeta.identifiers['Service'] = new CompileTypeMetadata(name: 'Service', moduleUrl: 'moduleUrl'); + + fooComponentMeta.template = new CompileTemplateMetadata(template: "import 'bar.dart';"); + fooComponentMeta.providers = [new CompileProviderMetadata(token: "StrService", useClass: + new CompileTypeMetadata(name: 'Service'))]; + fooComponentMeta.type.diDeps = [new CompileDiDependencyMetadata(token: "StrService")]; + + final viewAnnotation = new AnnotationModel()..name = 'View'..isView = true; + final reflectable = fooNgMeta.ngDeps.reflectables.first; + reflectable.annotations.add(viewAnnotation); + fooNgMeta.ngDeps.imports.add(new ImportModel()..uri = 'package:a/bar.dart'); + + updateReader(); + + final viewDefResults = await createCompileData(reader, fooAssetId, [], []); + final cmp = viewDefResults.viewDefinitions.values.first.component; + + expect(cmp.providers.length).toEqual(1); + + expect(cmp.providers[0].token).toEqual("StrService"); + expect(cmp.providers[0].useClass.name).toEqual("Service"); + expect(cmp.providers[0].useClass.moduleUrl).toEqual("moduleUrl"); + + expect(cmp.type.diDeps.length).toEqual(1); + expect(cmp.type.diDeps[0].token).toEqual("StrService"); + }); + + it('should include providers mentioned in aliases.', () async { + barNgMeta.identifiers['Service'] = new CompileTypeMetadata(name: 'Service', moduleUrl: 'moduleUrl'); + + fooComponentMeta.template = new CompileTemplateMetadata(template: "import 'bar.dart';"); + + fooNgMeta.aliases['providerAlias'] = ['Service']; + + fooComponentMeta.providers = [new CompileProviderMetadata(token: new CompileIdentifierMetadata(name: 'providerAlias'))]; + + final viewAnnotation = new AnnotationModel()..name = 'View'..isView = true; + final reflectable = fooNgMeta.ngDeps.reflectables.first; + reflectable.annotations.add(viewAnnotation); + fooNgMeta.ngDeps.imports.add(new ImportModel()..uri = 'package:a/bar.dart'); + + updateReader(); + + final viewDefResults = await createCompileData(reader, fooAssetId, [], []); + final cmp = viewDefResults.viewDefinitions.values.first.component; + + expect(cmp.providers.length).toEqual(1); + expect(cmp.providers[0].token.name).toEqual("Service"); + }); + it('should parse simple expressions in inline templates.', () async { fooComponentMeta.template = new CompileTemplateMetadata( template: '
{{greeting}}
', @@ -369,7 +510,7 @@ void allTests() { ..name = 'View' ..isView = true; - barNgMeta.types['PLATFORM'] = barComponentMeta; + barNgMeta.identifiers['PLATFORM'] = barComponentMeta; updateReader(); final outputs = await process(fooAssetId, diff --git a/tools/public_api_guard/public_api_spec.ts b/tools/public_api_guard/public_api_spec.ts index dcbfc7a7f1..d854eda9d8 100644 --- a/tools/public_api_guard/public_api_spec.ts +++ b/tools/public_api_guard/public_api_spec.ts @@ -835,8 +835,12 @@ const COMPILER = [ 'BoundTextAst.visit(visitor:TemplateAstVisitor, context:any):any', 'CompileDirectiveMetadata', 'CompileDirectiveMetadata.changeDetection:ChangeDetectionStrategy', - 'CompileDirectiveMetadata.constructor({type,isComponent,dynamicLoadable,selector,exportAs,changeDetection,inputs,outputs,hostListeners,hostProperties,hostAttributes,lifecycleHooks,template}:{type?:CompileTypeMetadata, isComponent?:boolean, dynamicLoadable?:boolean, selector?:string, exportAs?:string, changeDetection?:ChangeDetectionStrategy, inputs?:{[key:string]:string}, outputs?:{[key:string]:string}, hostListeners?:{[key:string]:string}, hostProperties?:{[key:string]:string}, hostAttributes?:{[key:string]:string}, lifecycleHooks?:LifecycleHooks[], template?:CompileTemplateMetadata})', - 'CompileDirectiveMetadata.create({type,isComponent,dynamicLoadable,selector,exportAs,changeDetection,inputs,outputs,host,lifecycleHooks,template}:{type?:CompileTypeMetadata, isComponent?:boolean, dynamicLoadable?:boolean, selector?:string, exportAs?:string, changeDetection?:ChangeDetectionStrategy, inputs?:string[], outputs?:string[], host?:{[key:string]:string}, lifecycleHooks?:LifecycleHooks[], template?:CompileTemplateMetadata}):CompileDirectiveMetadata', + 'CompileDirectiveMetadata.constructor({type,isComponent,dynamicLoadable,selector,exportAs,changeDetection,inputs,outputs,hostListeners,hostProperties,hostAttributes,lifecycleHooks,providers,viewProviders,queries,viewQueries,template}:{type?:CompileTypeMetadata, isComponent?:boolean, dynamicLoadable?:boolean, selector?:string, exportAs?:string, changeDetection?:ChangeDetectionStrategy, inputs?:{[key:string]:string}, outputs?:{[key:string]:string}, hostListeners?:{[key:string]:string}, hostProperties?:{[key:string]:string}, hostAttributes?:{[key:string]:string}, lifecycleHooks?:LifecycleHooks[], providers?:Array, viewProviders?:Array, queries?:CompileQueryMetadata[], viewQueries?:CompileQueryMetadata[], template?:CompileTemplateMetadata})', + 'CompileDirectiveMetadata.create({type,isComponent,dynamicLoadable,selector,exportAs,changeDetection,inputs,outputs,host,lifecycleHooks,providers,viewProviders,queries,viewQueries,template}:{type?:CompileTypeMetadata, isComponent?:boolean, dynamicLoadable?:boolean, selector?:string, exportAs?:string, changeDetection?:ChangeDetectionStrategy, inputs?:string[], outputs?:string[], host?:{[key:string]:string}, lifecycleHooks?:LifecycleHooks[], providers?:Array, viewProviders?:Array, queries?:CompileQueryMetadata[], viewQueries?:CompileQueryMetadata[], template?:CompileTemplateMetadata}):CompileDirectiveMetadata', + 'CompileDirectiveMetadata.providers:Array', + 'CompileDirectiveMetadata.queries:CompileQueryMetadata[]', + 'CompileDirectiveMetadata.viewProviders:Array', + 'CompileDirectiveMetadata.viewQueries:CompileQueryMetadata[]', 'CompileDirectiveMetadata.dynamicLoadable:boolean', 'CompileDirectiveMetadata.exportAs:string', 'CompileDirectiveMetadata.fromJson(data:{[key:string]:any}):CompileDirectiveMetadata', @@ -851,6 +855,7 @@ const COMPILER = [ 'CompileDirectiveMetadata.template:CompileTemplateMetadata', 'CompileDirectiveMetadata.toJson():{[key:string]:any}', 'CompileDirectiveMetadata.type:CompileTypeMetadata', + 'CompileDirectiveMetadata.identifier:CompileIdentifierMetadata', 'CompileTemplateMetadata', 'CompileTemplateMetadata.constructor({encapsulation,template,templateUrl,styles,styleUrls,ngContentSelectors}:{encapsulation?:ViewEncapsulation, template?:string, templateUrl?:string, styles?:string[], styleUrls?:string[], ngContentSelectors?:string[]})', 'CompileTemplateMetadata.encapsulation:ViewEncapsulation', @@ -862,13 +867,18 @@ const COMPILER = [ 'CompileTemplateMetadata.templateUrl:string', 'CompileTemplateMetadata.toJson():{[key:string]:any}', 'CompileTypeMetadata', - 'CompileTypeMetadata.constructor({runtime,name,moduleUrl,isHost}:{runtime?:Type, name?:string, moduleUrl?:string, isHost?:boolean})', + 'CompileTypeMetadata.constructor({runtime,name,moduleUrl,prefix,isHost,constConstructor,diDeps}:{runtime?:Type, name?:string, moduleUrl?:string, prefix?:string, isHost?:boolean, constConstructor?:boolean, diDeps?:CompileDiDependencyMetadata[]})', 'CompileTypeMetadata.fromJson(data:{[key:string]:any}):CompileTypeMetadata', 'CompileTypeMetadata.isHost:boolean', 'CompileTypeMetadata.moduleUrl:string', 'CompileTypeMetadata.name:string', 'CompileTypeMetadata.runtime:Type', 'CompileTypeMetadata.toJson():{[key:string]:any}', + 'CompileTypeMetadata.diDeps:CompileDiDependencyMetadata[]', + 'CompileTypeMetadata.prefix:string', + 'CompileTypeMetadata.constConstructor:boolean', + 'CompileTypeMetadata.identifier:CompileIdentifierMetadata', + 'CompileTypeMetadata.type:CompileTypeMetadata', 'DirectiveAst', 'DirectiveAst.constructor(directive:CompileDirectiveMetadata, inputs:BoundDirectivePropertyAst[], hostProperties:BoundElementPropertyAst[], hostEvents:BoundEventAst[], exportAsVars:VariableAst[], sourceSpan:ParseSourceSpan)', 'DirectiveAst.visit(visitor:TemplateAstVisitor, context:any):any',