From 4e9809bcb276a3ef18482af6782b746a19d96827 Mon Sep 17 00:00:00 2001 From: vsavkin Date: Mon, 4 Apr 2016 10:32:28 -0700 Subject: [PATCH] feat(transformers): changes transformers to collect information about providers and resolve identifiers during linking Closes #7380 --- .../src/compiler/directive_metadata.ts | 120 +++- .../src/compiler/template_compiler.ts | 3 + modules/angular2/src/facade/lang.dart | 1 + modules/angular2/src/facade/lang.ts | 12 +- .../test/compiler/directive_metadata_spec.ts | 61 +- .../transform/common/code/ng_deps_code.dart | 10 +- .../lib/src/transform/common/logging.dart | 44 ++ .../common/model/annotation_model.pb.dart | 8 +- .../common/model/parameter_model.pb.dart | 4 +- .../model/reflection_info_model.pb.dart | 16 +- .../lib/src/transform/common/ng_meta.dart | 74 +-- .../src/transform/common/options_reader.dart | 2 +- .../common/type_metadata_reader.dart | 482 ++++++++++---- .../src/transform/common/url_resolver.dart | 8 +- .../ng_deps_linker.dart | 30 +- .../ng_meta_linker.dart | 436 +++++++++++-- .../transformer.dart | 16 +- .../directive_processor/rewriter.dart | 44 +- .../change_detector_codegen.dart | 4 +- .../compile_data_creator.dart | 132 +--- .../template_compiler/generator.dart | 4 +- .../lib/src/transform/transformer.dart | 2 +- .../transform/lib/transform/codegen.dart | 2 +- .../test/transform/common/ng_meta_helper.dart | 2 +- .../test/transform/common/ng_meta_test.dart | 40 +- .../test/transform/common/read_file.dart | 4 + .../directive_metadata_linker/all_tests.dart | 590 ++++++++++++++++-- .../abstract_classes/classes.dart | 4 +- .../directive_processor/all_tests.dart | 331 +++++++++- .../directives_files/components.dart | 156 ++++- .../directives_files/dep1.dart | 4 +- .../directives_files/dep2.dart | 5 +- .../directives_files/services.dart | 5 +- .../identifiers/classes.dart | 7 +- .../identifiers/classes_no_injectable.dart | 2 +- .../identifiers/constants.dart | 2 +- .../identifiers/enums.dart | 4 +- .../identifiers/factories.dart | 10 + .../identifiers/provider_constants.dart | 13 + .../identifiers/typedefs.dart | 2 +- .../test/transform/integration/all_tests.dart | 171 +++-- .../two_deps_files/expected/bar.template.dart | 4 +- .../template_compiler/all_tests.dart | 251 +------- .../transform.unittest.server.spec.dart | 2 + tools/public_api_guard/public_api_spec.ts | 279 +++++---- 45 files changed, 2424 insertions(+), 979 deletions(-) create mode 100644 modules_dart/transform/test/transform/directive_processor/identifiers/factories.dart create mode 100644 modules_dart/transform/test/transform/directive_processor/identifiers/provider_constants.dart diff --git a/modules/angular2/src/compiler/directive_metadata.ts b/modules/angular2/src/compiler/directive_metadata.ts index bf5c08ea27..3251ec24a5 100644 --- a/modules/angular2/src/compiler/directive_metadata.ts +++ b/modules/angular2/src/compiler/directive_metadata.ts @@ -1,13 +1,16 @@ import { isPresent, isBlank, + isNumber, + isBoolean, normalizeBool, normalizeBlank, serializeEnum, Type, isString, RegExpWrapper, - StringWrapper + StringWrapper, + isArray } from 'angular2/src/facade/lang'; import {unimplemented} from 'angular2/src/facade/exceptions'; import {StringMapWrapper} from 'angular2/src/facade/collection'; @@ -25,20 +28,12 @@ import {LifecycleHooks, LIFECYCLE_HOOKS_VALUES} from 'angular2/src/core/linker/i var HOST_REG_EXP = /^(?:(?:\[([^\]]+)\])|(?:\(([^\)]+)\)))$/g; 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); - } - abstract toJson(): {[key: string]: any}; get type(): CompileTypeMetadata { return unimplemented(); } @@ -46,43 +41,57 @@ export abstract class CompileMetadataWithType extends CompileMetadataWithIdentif get identifier(): CompileIdentifierMetadata { return unimplemented(); } } +export function metadataFromJson(data: {[key: string]: any}): any { + return _COMPILE_METADATA_FROM_JSON[data['class']](data); +} + export class CompileIdentifierMetadata implements CompileMetadataWithIdentifier { runtime: any; name: string; prefix: string; moduleUrl: string; constConstructor: boolean; - constructor({runtime, name, moduleUrl, prefix, constConstructor}: { + value: any; + + constructor({runtime, name, moduleUrl, prefix, constConstructor, value}: { runtime?: any, name?: string, + staticMethodName?: string, moduleUrl?: string, prefix?: string, - constConstructor?: boolean + constConstructor?: boolean, + value?: any } = {}) { this.runtime = runtime; this.name = name; this.prefix = prefix; this.moduleUrl = moduleUrl; this.constConstructor = constConstructor; + this.value = value; } static fromJson(data: {[key: string]: any}): CompileIdentifierMetadata { + let value = isArray(data['value']) ? arrayFromJson(data['value'], metadataFromJson) : + objFromJson(data['value'], metadataFromJson); return new CompileIdentifierMetadata({ name: data['name'], prefix: data['prefix'], moduleUrl: data['moduleUrl'], - constConstructor: data['constConstructor'] + constConstructor: data['constConstructor'], + value: value }); } toJson(): {[key: string]: any} { + let value = isArray(this.value) ? arrayToJson(this.value) : objToJson(this.value); return { // Note: Runtime type can't be serialized... 'class': 'Identifier', 'name': this.name, 'moduleUrl': this.moduleUrl, 'prefix': this.prefix, - 'constConstructor': this.constConstructor + 'constConstructor': this.constConstructor, + 'value': value }; } @@ -177,44 +186,78 @@ export class CompileProviderMetadata { static fromJson(data: {[key: string]: any}): CompileProviderMetadata { return new CompileProviderMetadata({ token: objFromJson(data['token'], CompileIdentifierMetadata.fromJson), - useClass: objFromJson(data['useClass'], CompileTypeMetadata.fromJson) + useClass: objFromJson(data['useClass'], CompileTypeMetadata.fromJson), + useExisting: objFromJson(data['useExisting'], CompileIdentifierMetadata.fromJson), + useValue: objFromJson(data['useValue'], CompileIdentifierMetadata.fromJson), + useFactory: objFromJson(data['useFactory'], CompileFactoryMetadata.fromJson) }); } toJson(): {[key: string]: any} { return { // Note: Runtime type can't be serialized... + 'class': 'Provider', 'token': objToJson(this.token), - 'useClass': objToJson(this.useClass) + 'useClass': objToJson(this.useClass), + 'useExisting': objToJson(this.useExisting), + 'useValue': objToJson(this.useValue), + 'useFactory': objToJson(this.useFactory) }; } } -export class CompileFactoryMetadata implements CompileIdentifierMetadata { +export class CompileFactoryMetadata implements CompileIdentifierMetadata, + CompileMetadataWithIdentifier { runtime: Function; name: string; prefix: string; moduleUrl: string; constConstructor: boolean; + value: any; diDeps: CompileDiDependencyMetadata[]; - constructor({runtime, name, moduleUrl, constConstructor, diDeps}: { + constructor({runtime, name, moduleUrl, prefix, constConstructor, diDeps, value}: { runtime?: Function, name?: string, + prefix?: string, moduleUrl?: string, constConstructor?: boolean, + value?: boolean, diDeps?: CompileDiDependencyMetadata[] }) { this.runtime = runtime; this.name = name; + this.prefix = prefix; this.moduleUrl = moduleUrl; this.diDeps = diDeps; this.constConstructor = constConstructor; + this.value = value; } get identifier(): CompileIdentifierMetadata { return this; } - toJson() { return null; } + static fromJson(data: {[key: string]: any}): CompileFactoryMetadata { + return new CompileFactoryMetadata({ + name: data['name'], + prefix: data['prefix'], + moduleUrl: data['moduleUrl'], + constConstructor: data['constConstructor'], + value: data['value'], + diDeps: arrayFromJson(data['diDeps'], CompileDiDependencyMetadata.fromJson) + }); + } + + toJson(): {[key: string]: any} { + return { + 'class': 'Factory', + 'name': this.name, + 'prefix': this.prefix, + 'moduleUrl': this.moduleUrl, + 'constConstructor': this.constConstructor, + 'value': this.value, + 'diDeps': arrayToJson(this.diDeps) + }; + } } /** @@ -227,15 +270,17 @@ export class CompileTypeMetadata implements CompileIdentifierMetadata, CompileMe moduleUrl: string; isHost: boolean; constConstructor: boolean; + value: any; diDeps: CompileDiDependencyMetadata[]; - constructor({runtime, name, moduleUrl, prefix, isHost, constConstructor, diDeps}: { + constructor({runtime, name, moduleUrl, prefix, isHost, constConstructor, value, diDeps}: { runtime?: Type, name?: string, moduleUrl?: string, prefix?: string, isHost?: boolean, constConstructor?: boolean, + value?: any, diDeps?: CompileDiDependencyMetadata[] } = {}) { this.runtime = runtime; @@ -244,6 +289,7 @@ export class CompileTypeMetadata implements CompileIdentifierMetadata, CompileMe this.prefix = prefix; this.isHost = normalizeBool(isHost); this.constConstructor = constConstructor; + this.value = value; this.diDeps = normalizeBlank(diDeps); } @@ -254,6 +300,7 @@ export class CompileTypeMetadata implements CompileIdentifierMetadata, CompileMe prefix: data['prefix'], isHost: data['isHost'], constConstructor: data['constConstructor'], + value: data['value'], diDeps: arrayFromJson(data['diDeps'], CompileDiDependencyMetadata.fromJson) }); } @@ -270,6 +317,7 @@ export class CompileTypeMetadata implements CompileIdentifierMetadata, CompileMe 'prefix': this.prefix, 'isHost': this.isHost, 'constConstructor': this.constConstructor, + 'value': this.value, 'diDeps': arrayToJson(this.diDeps) }; } @@ -382,8 +430,10 @@ export class CompileDirectiveMetadata implements CompileMetadataWithType { outputs?: string[], host?: {[key: string]: string}, lifecycleHooks?: LifecycleHooks[], - providers?: Array, - viewProviders?: Array, + providers?: + Array, + viewProviders?: + Array, queries?: CompileQueryMetadata[], viewQueries?: CompileQueryMetadata[], template?: CompileTemplateMetadata @@ -474,8 +524,10 @@ export class CompileDirectiveMetadata implements CompileMetadataWithType { hostProperties?: {[key: string]: string}, hostAttributes?: {[key: string]: string}, lifecycleHooks?: LifecycleHooks[], - providers?: Array, - viewProviders?: Array, + providers?: + Array, + viewProviders?: + Array, queries?: CompileQueryMetadata[], viewQueries?: CompileQueryMetadata[], template?: CompileTemplateMetadata @@ -494,8 +546,8 @@ export class CompileDirectiveMetadata implements CompileMetadataWithType { this.lifecycleHooks = lifecycleHooks; this.providers = normalizeBlank(providers); this.viewProviders = normalizeBlank(viewProviders); - this.queries = queries; - this.viewQueries = viewQueries; + this.queries = normalizeBlank(queries); + this.viewQueries = normalizeBlank(viewQueries); this.template = template; } @@ -520,7 +572,10 @@ export class CompileDirectiveMetadata implements CompileMetadataWithType { (data['lifecycleHooks']).map(hookValue => LIFECYCLE_HOOKS_VALUES[hookValue]), template: isPresent(data['template']) ? CompileTemplateMetadata.fromJson(data['template']) : data['template'], - providers: arrayFromJson(data['providers'], CompileProviderMetadata.fromJson) + providers: arrayFromJson(data['providers'], metadataFromJson), + viewProviders: arrayFromJson(data['viewProviders'], metadataFromJson), + queries: arrayFromJson(data['queries'], CompileQueryMetadata.fromJson), + viewQueries: arrayFromJson(data['viewQueries'], CompileQueryMetadata.fromJson) }); } @@ -541,7 +596,10 @@ export class CompileDirectiveMetadata implements CompileMetadataWithType { 'hostAttributes': this.hostAttributes, 'lifecycleHooks': this.lifecycleHooks.map(hook => serializeEnum(hook)), 'template': isPresent(this.template) ? this.template.toJson() : this.template, - 'providers': arrayToJson(this.providers) + 'providers': arrayToJson(this.providers), + 'viewProviders': arrayToJson(this.viewProviders), + 'queries': arrayToJson(this.queries), + 'viewQueries': arrayToJson(this.viewQueries) }; } } @@ -611,7 +669,9 @@ var _COMPILE_METADATA_FROM_JSON = { 'Directive': CompileDirectiveMetadata.fromJson, 'Pipe': CompilePipeMetadata.fromJson, 'Type': CompileTypeMetadata.fromJson, - 'Identifier': CompileIdentifierMetadata.fromJson + 'Provider': CompileProviderMetadata.fromJson, + 'Identifier': CompileIdentifierMetadata.fromJson, + 'Factory': CompileFactoryMetadata.fromJson }; function arrayFromJson(obj: any[], fn: (a: {[key: string]: any}) => any): any { @@ -623,9 +683,9 @@ function arrayToJson(obj: any[]): string | {[key: string]: any} { } function objFromJson(obj: any, fn: (a: {[key: string]: any}) => any): any { - return (isString(obj) || isBlank(obj)) ? obj : fn(obj); + return (isString(obj) || isBlank(obj) || isBoolean(obj) || isNumber(obj)) ? obj : fn(obj); } function objToJson(obj: any): string | {[key: string]: any} { - return (isString(obj) || isBlank(obj)) ? obj : obj.toJson(); + return (isString(obj) || isBlank(obj) || isBoolean(obj) || isNumber(obj)) ? obj : obj.toJson(); } diff --git a/modules/angular2/src/compiler/template_compiler.ts b/modules/angular2/src/compiler/template_compiler.ts index 9819274d18..ed5ff0ef78 100644 --- a/modules/angular2/src/compiler/template_compiler.ts +++ b/modules/angular2/src/compiler/template_compiler.ts @@ -110,6 +110,9 @@ export class TemplateCompiler { hostAttributes: directive.hostAttributes, lifecycleHooks: directive.lifecycleHooks, providers: directive.providers, + viewProviders: directive.viewProviders, + queries: directive.queries, + viewQueries: directive.viewQueries, template: normalizedTemplate })); } diff --git a/modules/angular2/src/facade/lang.dart b/modules/angular2/src/facade/lang.dart index d1d86a0d7e..9d95b2f97a 100644 --- a/modules/angular2/src/facade/lang.dart +++ b/modules/angular2/src/facade/lang.dart @@ -33,6 +33,7 @@ bool isStringMap(Object obj) => obj is Map; bool isArray(Object obj) => obj is List; bool isPromise(Object obj) => obj is Future; bool isNumber(Object obj) => obj is num; +bool isBoolean(Object obj) => obj is bool; bool isDate(Object obj) => obj is DateTime; String stringify(obj) { diff --git a/modules/angular2/src/facade/lang.ts b/modules/angular2/src/facade/lang.ts index bbdb0fbd81..6166d537b2 100644 --- a/modules/angular2/src/facade/lang.ts +++ b/modules/angular2/src/facade/lang.ts @@ -124,6 +124,14 @@ export function isBlank(obj: any): boolean { return obj === undefined || obj === null; } +export function isBoolean(obj: any): boolean { + return typeof obj === "boolean"; +} + +export function isNumber(obj: any): boolean { + return typeof obj === "number"; +} + export function isString(obj: any): boolean { return typeof obj === "string"; } @@ -148,10 +156,6 @@ export function isArray(obj: any): boolean { return Array.isArray(obj); } -export function isNumber(obj): boolean { - return typeof obj === 'number'; -} - export function isDate(obj): boolean { return obj instanceof Date && !isNaN(obj.valueOf()); } diff --git a/modules/angular2/test/compiler/directive_metadata_spec.ts b/modules/angular2/test/compiler/directive_metadata_spec.ts index ac6b5fb627..de26a10212 100644 --- a/modules/angular2/test/compiler/directive_metadata_spec.ts +++ b/modules/angular2/test/compiler/directive_metadata_spec.ts @@ -18,7 +18,9 @@ import { CompileTemplateMetadata, CompileProviderMetadata, CompileDiDependencyMetadata, - CompileQueryMetadata + CompileQueryMetadata, + CompileIdentifierMetadata, + CompileFactoryMetadata } from 'angular2/src/compiler/directive_metadata'; import {ViewEncapsulation} from 'angular2/src/core/metadata/view'; import {ChangeDetectionStrategy} from 'angular2/src/core/change_detection'; @@ -31,25 +33,21 @@ export function main() { var fullDirectiveMeta: CompileDirectiveMetadata; beforeEach(() => { - fullTypeMeta = new CompileTypeMetadata({ - name: 'SomeType', - moduleUrl: 'someUrl', + var diDep = new CompileDiDependencyMetadata({ + isAttribute: true, + isSelf: true, isHost: true, - diDeps: [ - new CompileDiDependencyMetadata({ - isAttribute: true, - isSelf: true, - isHost: true, - isSkipSelf: true, - isOptional: true, - token: 'someToken', - query: new CompileQueryMetadata( - {selectors: ['one'], descendants: true, first: true, propertyName: 'one'}), - viewQuery: new CompileQueryMetadata( - {selectors: ['one'], descendants: true, first: true, propertyName: 'one'}) - }) - ] + isSkipSelf: true, + isOptional: true, + token: 'someToken', + query: new CompileQueryMetadata( + {selectors: ['one'], descendants: true, first: true, propertyName: 'one'}), + viewQuery: new CompileQueryMetadata( + {selectors: ['one'], descendants: true, first: true, propertyName: 'one'}) }); + + fullTypeMeta = new CompileTypeMetadata( + {name: 'SomeType', moduleUrl: 'someUrl', isHost: true, diDeps: [diDep]}); fullTemplateMeta = new CompileTemplateMetadata({ encapsulation: ViewEncapsulation.Emulated, template: '', @@ -69,7 +67,32 @@ export function main() { outputs: ['someEvent'], host: {'(event1)': 'handler1', '[prop1]': 'expr1', 'attr1': 'attrValue2'}, lifecycleHooks: [LifecycleHooks.OnChanges], - providers: [new CompileProviderMetadata({token: 'token', useClass: fullTypeMeta})] + providers: [ + new CompileProviderMetadata({ + token: 'token', + useClass: fullTypeMeta, + useExisting: new CompileIdentifierMetadata({name: 'someName'}), + useFactory: new CompileFactoryMetadata({name: 'someName', diDeps: [diDep]}), + useValue: 'someValue', + }) + ], + viewProviders: [ + new CompileProviderMetadata({ + token: 'token', + useClass: fullTypeMeta, + useExisting: new CompileIdentifierMetadata({name: 'someName'}), + useFactory: new CompileFactoryMetadata({name: 'someName', diDeps: [diDep]}), + useValue: 'someValue', + }) + ], + queries: [ + new CompileQueryMetadata( + {selectors: ['selector'], descendants: true, first: false, propertyName: 'prop'}) + ], + viewQueries: [ + new CompileQueryMetadata( + {selectors: ['selector'], descendants: true, first: false, propertyName: 'prop'}) + ] }); }); diff --git a/modules_dart/transform/lib/src/transform/common/code/ng_deps_code.dart b/modules_dart/transform/lib/src/transform/common/code/ng_deps_code.dart index d779888c0d..83b8573f03 100644 --- a/modules_dart/transform/lib/src/transform/common/code/ng_deps_code.dart +++ b/modules_dart/transform/lib/src/transform/common/code/ng_deps_code.dart @@ -166,11 +166,11 @@ abstract class NgDepsWriterMixin ..writeln('void ${SETUP_METHOD_NAME}() {') ..writeln('if (_visited) return; _visited = true;'); - final needsReceiver = (model.reflectables != null && - model.reflectables.isNotEmpty) || - (model.getters != null && model.getters.isNotEmpty) || - (model.setters != null && model.setters.isNotEmpty) || - (model.methods != null && model.methods.isNotEmpty); + final needsReceiver = + (model.reflectables != null && model.reflectables.isNotEmpty) || + (model.getters != null && model.getters.isNotEmpty) || + (model.setters != null && model.setters.isNotEmpty) || + (model.methods != null && model.methods.isNotEmpty); if (needsReceiver) { buffer.writeln('$REFLECTOR_PREFIX.$REFLECTOR_VAR_NAME'); diff --git a/modules_dart/transform/lib/src/transform/common/logging.dart b/modules_dart/transform/lib/src/transform/common/logging.dart index b4e681642a..42b2ce9e08 100644 --- a/modules_dart/transform/lib/src/transform/common/logging.dart +++ b/modules_dart/transform/lib/src/transform/common/logging.dart @@ -74,6 +74,50 @@ class PrintLogger implements TransformLogger { } } +/// Wraps the logger and prints the messages +/// only if they have not been printed before +class DeduppingLogger implements TransformLogger { + Set _printedMessages; + + final TransformLogger _logger; + + DeduppingLogger(this._logger, this._printedMessages); + + String _key(msg, AssetId asset) => "$msg $asset"; + + @override + void info(msg, {AssetId asset, SourceSpan span}) { + if (!_printedMessages.contains(_key(msg, asset))) { + _printedMessages.add(_key(msg, asset)); + _logger.info(msg, asset: asset, span: span); + } + } + + @override + void fine(msg, {AssetId asset, SourceSpan span}) { + if (!_printedMessages.contains(_key(msg, asset))) { + _printedMessages.add(_key(msg, asset)); + _logger.fine(msg, asset: asset, span: span); + } + } + + @override + void warning(msg, {AssetId asset, SourceSpan span}) { + if (!_printedMessages.contains(_key(msg, asset))) { + _printedMessages.add(_key(msg, asset)); + _logger.warning(msg, asset: asset, span: span); + } + } + + @override + void error(msg, {AssetId asset, SourceSpan span}) { + if (!_printedMessages.contains(_key(msg, asset))) { + _printedMessages.add(_key(msg, asset)); + _logger.error(msg, asset: asset, span: span); + } + } +} + class PrintLoggerError extends Error { final String message; final AssetId asset; diff --git a/modules_dart/transform/lib/src/transform/common/model/annotation_model.pb.dart b/modules_dart/transform/lib/src/transform/common/model/annotation_model.pb.dart index 6a0f1aaeaf..976be4cce4 100644 --- a/modules_dart/transform/lib/src/transform/common/model/annotation_model.pb.dart +++ b/modules_dart/transform/lib/src/transform/common/model/annotation_model.pb.dart @@ -23,8 +23,8 @@ class NamedParameter extends GeneratedMessage { static PbList createRepeated() => new PbList(); static NamedParameter getDefault() { - if (_defaultInstance == null) _defaultInstance = - new _ReadonlyNamedParameter(); + if (_defaultInstance == null) + _defaultInstance = new _ReadonlyNamedParameter(); return _defaultInstance; } @@ -78,8 +78,8 @@ class AnnotationModel extends GeneratedMessage { static PbList createRepeated() => new PbList(); static AnnotationModel getDefault() { - if (_defaultInstance == null) _defaultInstance = - new _ReadonlyAnnotationModel(); + if (_defaultInstance == null) + _defaultInstance = new _ReadonlyAnnotationModel(); return _defaultInstance; } diff --git a/modules_dart/transform/lib/src/transform/common/model/parameter_model.pb.dart b/modules_dart/transform/lib/src/transform/common/model/parameter_model.pb.dart index 949ccd4613..027ff4c9c3 100644 --- a/modules_dart/transform/lib/src/transform/common/model/parameter_model.pb.dart +++ b/modules_dart/transform/lib/src/transform/common/model/parameter_model.pb.dart @@ -26,8 +26,8 @@ class ParameterModel extends GeneratedMessage { static PbList createRepeated() => new PbList(); static ParameterModel getDefault() { - if (_defaultInstance == null) _defaultInstance = - new _ReadonlyParameterModel(); + if (_defaultInstance == null) + _defaultInstance = new _ReadonlyParameterModel(); return _defaultInstance; } diff --git a/modules_dart/transform/lib/src/transform/common/model/reflection_info_model.pb.dart b/modules_dart/transform/lib/src/transform/common/model/reflection_info_model.pb.dart index a96d4e20c4..0aef86f24f 100644 --- a/modules_dart/transform/lib/src/transform/common/model/reflection_info_model.pb.dart +++ b/modules_dart/transform/lib/src/transform/common/model/reflection_info_model.pb.dart @@ -27,15 +27,15 @@ class PropertyMetadataModel extends GeneratedMessage { static PbList createRepeated() => new PbList(); static PropertyMetadataModel getDefault() { - if (_defaultInstance == null) _defaultInstance = - new _ReadonlyPropertyMetadataModel(); + if (_defaultInstance == null) + _defaultInstance = new _ReadonlyPropertyMetadataModel(); return _defaultInstance; } static PropertyMetadataModel _defaultInstance; static void $checkItem(PropertyMetadataModel v) { - if (v - is! PropertyMetadataModel) checkItemFailed(v, 'PropertyMetadataModel'); + if (v is! PropertyMetadataModel) + checkItemFailed(v, 'PropertyMetadataModel'); } String get name => $_get(0, 1, ''); @@ -70,8 +70,8 @@ class PrefixedType extends GeneratedMessage { static PrefixedType create() => new PrefixedType(); static PbList createRepeated() => new PbList(); static PrefixedType getDefault() { - if (_defaultInstance == null) _defaultInstance = - new _ReadonlyPrefixedType(); + if (_defaultInstance == null) + _defaultInstance = new _ReadonlyPrefixedType(); return _defaultInstance; } @@ -130,8 +130,8 @@ class ReflectionInfoModel extends GeneratedMessage { static PbList createRepeated() => new PbList(); static ReflectionInfoModel getDefault() { - if (_defaultInstance == null) _defaultInstance = - new _ReadonlyReflectionInfoModel(); + if (_defaultInstance == null) + _defaultInstance = new _ReadonlyReflectionInfoModel(); return _defaultInstance; } 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 716bf0168c..5ec46d0884 100644 --- a/modules_dart/transform/lib/src/transform/common/ng_meta.dart +++ b/modules_dart/transform/lib/src/transform/common/ng_meta.dart @@ -28,10 +28,8 @@ import 'url_resolver.dart' show isDartCoreUri; /// `.ng_meta.json` files as intermediate assets during the compilation process. class NgMeta { static const _ALIAS_VALUE = 'alias'; - static const _KIND_KEY = 'kind'; static const _NG_DEPS_KEY = 'ngDeps'; static const _TYPE_VALUE = 'type'; - static const _VALUE_KEY = 'value'; /// Metadata for each identifier /// Type: [CompileDirectiveMetadata]|[CompilePipeMetadata]|[CompileTypeMetadata]|[CompileIdentifierMetadata] @@ -43,11 +41,9 @@ class NgMeta { // The NgDeps generated from final NgDepsModel ngDeps; - bool definesAlias; - NgMeta({Map> aliases, - Map identifiers, - this.ngDeps: null, this.definesAlias: false}) + Map identifiers, + this.ngDeps: null}) :this.aliases = aliases != null ? aliases : {}, this.identifiers = identifiers != null ? identifiers : {}; @@ -72,48 +68,43 @@ class NgMeta { 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; + bool get needsResolution { + return identifiers.values.any((id) => + id is CompileDirectiveMetadata || id is CompilePipeMetadata || id is CompileTypeMetadata + || (id is CompileIdentifierMetadata && id.value != null)); } /// Parse from the serialized form produced by [toJson]. factory NgMeta.fromJson(Map json) { var ngDeps = null; - final aliases = {}; - final identifiers = {}; - var definesAlias = false; - for (var key in json.keys) { - if (key == _NG_DEPS_KEY) { - var ngDepsJsonMap = json[key]; - if (ngDepsJsonMap == null) continue; + + if (json.containsKey(_NG_DEPS_KEY)) { + var ngDepsJsonMap = json[_NG_DEPS_KEY]; + if (ngDepsJsonMap != null) { if (ngDepsJsonMap is! Map) { log.warning( - 'Unexpected value $ngDepsJsonMap for key "$key" in NgMeta.'); - continue; + 'Unexpected value $ngDepsJsonMap for key "$_NG_DEPS_KEY" in NgMeta.'); + } else { + ngDeps = new NgDepsModel()..mergeFromJsonMap(ngDepsJsonMap); } - ngDeps = new NgDepsModel() - ..mergeFromJsonMap(ngDepsJsonMap); - } else if (key == 'definesAlias') { - definesAlias = json[key]; + } + } - } else { - var entry = json[key]; + final aliases = json[_ALIAS_VALUE] != null ? json[_ALIAS_VALUE] : {}; + + final identifiers = {}; + if (json.containsKey(_TYPE_VALUE)) { + for (var key in json[_TYPE_VALUE].keys) { + var entry = json[_TYPE_VALUE][key]; if (entry is! Map) { log.warning('Unexpected value $entry for key "$key" in NgMeta.'); continue; } - if (entry[_KIND_KEY] == _TYPE_VALUE) { - identifiers[key] = CompileMetadataWithIdentifier.fromJson(entry[_VALUE_KEY]); - } else if (entry[_KIND_KEY] == _ALIAS_VALUE) { - aliases[key] = entry[_VALUE_KEY]; - } + identifiers[key] = metadataFromJson(entry); } } - return new NgMeta(identifiers: identifiers, aliases: aliases, ngDeps: ngDeps, definesAlias: definesAlias); + + return new NgMeta(identifiers: identifiers, aliases: aliases, ngDeps: ngDeps); } /// Serialized representation of this instance. @@ -121,16 +112,11 @@ class NgMeta { var result = {}; result[_NG_DEPS_KEY] = isNgDepsEmpty ? null : ngDeps.writeToJsonMap(); + result[_TYPE_VALUE] = {}; identifiers.forEach((k, v) { - result[k] = {_KIND_KEY: _TYPE_VALUE, _VALUE_KEY: v.toJson()}; + result[_TYPE_VALUE][k] = v.toJson(); }); - - aliases.forEach((k, v) { - result[k] = {_KIND_KEY: _ALIAS_VALUE, _VALUE_KEY: v}; - }); - - result['definesAlias'] = definesAlias; - + result[_ALIAS_VALUE] = aliases; return result; } @@ -150,10 +136,10 @@ class NgMeta { log.error('Circular alias dependency for "$name". Cycle: ${newPath.join(' -> ')}.'); return; } - if (identifiers.containsKey(name)) { - result.add(identifiers[name]); - } else if (aliases.containsKey(name)) { + if (aliases.containsKey(name)) { aliases[name].forEach((n) => helper(n, newPath)); + } else if (identifiers.containsKey(name)) { + result.add(identifiers[name]); } else { log.error('Unknown alias: ${newPath.join(' -> ')}. Make sure you export ${name} from the file where ${path.last} is defined.'); } diff --git a/modules_dart/transform/lib/src/transform/common/options_reader.dart b/modules_dart/transform/lib/src/transform/common/options_reader.dart index a7c4d96caa..55a8a021ba 100644 --- a/modules_dart/transform/lib/src/transform/common/options_reader.dart +++ b/modules_dart/transform/lib/src/transform/common/options_reader.dart @@ -6,7 +6,7 @@ import 'mirror_mode.dart'; import 'options.dart'; import './url_resolver.dart'; - TransformerOptions parseBarbackSettings(BarbackSettings settings) { +TransformerOptions parseBarbackSettings(BarbackSettings settings) { var config = settings.configuration; var entryPoints = _readStringList(config, ENTRY_POINT_PARAM); var initReflector = diff --git a/modules_dart/transform/lib/src/transform/common/type_metadata_reader.dart b/modules_dart/transform/lib/src/transform/common/type_metadata_reader.dart index fbaad1a5e3..11ddf8a26d 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 @@ -22,10 +22,11 @@ class TypeMetadataReader { final _DirectiveMetadataVisitor _directiveVisitor; final _PipeMetadataVisitor _pipeVisitor; final _CompileTypeMetadataVisitor _typeVisitor; + final _CompileFactoryMetadataVisitor _factoryVisitor; final TemplateCompiler _templateCompiler; - TypeMetadataReader._( - this._directiveVisitor, this._pipeVisitor, this._templateCompiler, this._typeVisitor); + TypeMetadataReader._(this._directiveVisitor, this._pipeVisitor, + this._templateCompiler, this._typeVisitor, this._factoryVisitor); /// Accepts an [AnnotationMatcher] which tests that an [Annotation] /// is a [Directive], [Component], or [View]. @@ -33,12 +34,13 @@ class TypeMetadataReader { InterfaceMatcher interfaceMatcher, TemplateCompiler templateCompiler) { var lifecycleVisitor = new _LifecycleHookVisitor(interfaceMatcher); var typeVisitor = new _CompileTypeMetadataVisitor(annotationMatcher); - var directiveVisitor = - new _DirectiveMetadataVisitor(annotationMatcher, lifecycleVisitor, typeVisitor); + var directiveVisitor = new _DirectiveMetadataVisitor( + annotationMatcher, lifecycleVisitor, typeVisitor); var pipeVisitor = new _PipeMetadataVisitor(annotationMatcher); + var factoryVisitor = new _CompileFactoryMetadataVisitor(annotationMatcher); return new TypeMetadataReader._( - directiveVisitor, pipeVisitor, templateCompiler, typeVisitor); + directiveVisitor, pipeVisitor, templateCompiler, typeVisitor, factoryVisitor); } /// Reads *un-normalized* [CompileDirectiveMetadata]/[CompilePipeMetadata] from the @@ -71,6 +73,47 @@ class TypeMetadataReader { return new Future.value(null); } } + + Future readFactoryMetadata(FunctionDeclaration node, AssetId assetId) { + _factoryVisitor.reset(assetId); + + node.accept(_factoryVisitor); + + if (_factoryVisitor.isInjectable) { + return new Future.value(_factoryVisitor.factory); + } else { + return new Future.value(null); + } + } + + CompileIdentifierMetadata readIdentifierMetadata( + VariableDeclaration decl, AssetId assetId) { + final name = decl.name.name; + return new CompileIdentifierMetadata( + name: name, moduleUrl: toAssetUri(assetId), value: _readValue(decl.initializer)); + } + + dynamic _readValue(dynamic initializer) { + try { + if (initializer is InstanceCreationExpression && + ((initializer as InstanceCreationExpression) + .constructorName + .toString() == + "Provider" || + (initializer as InstanceCreationExpression) + .constructorName + .toString() == + "Binding")) { + return _readProvider(initializer); + } else if (initializer is ListLiteral) { + return _readProviders(initializer); + } else { + return null; + } + } catch (e) { + return null; + } + } } /// Evaluates the [Map] represented by `expression` and adds all `key`, @@ -134,7 +177,6 @@ bool _expressionToBool(Expression node, String nodeDescription) { class _CompileTypeMetadataVisitor extends Object with RecursiveAstVisitor { - bool _isInjectable = false; CompileTypeMetadata _type; AssetId _assetId; @@ -166,58 +208,79 @@ class _CompileTypeMetadataVisitor extends Object @override Object visitClassDeclaration(ClassDeclaration node) { node.metadata.accept(this); + final fieldTypes = _readFields(node); if (this._isInjectable) { + final constructor = node.getConstructor(null); + final diDeps = constructor == null ? [] : + _getCompileDiDependencyMetadata(constructor.parameters, fieldTypes); + _type = new CompileTypeMetadata( moduleUrl: toAssetUri(_assetId), name: node.name.toString(), - diDeps: _getCompileDiDependencyMetadata(node), + diDeps: diDeps, + runtime: null // Intentionally `null`, cannot be provided here. + ); + } + return null; + } + + Map _readFields(ClassDeclaration clazz) { + final res = {}; + clazz.members + .where((member) => member is FieldDeclaration) + .forEach((FieldDeclaration field) { + var type = field.fields.type; + if (type != null) { + field.fields.variables.forEach((VariableDeclaration decl) { + var key = '${decl.name}'; + res[key] = type; + }); + } + }); + return res; + } +} + +class _CompileFactoryMetadataVisitor extends Object + with RecursiveAstVisitor { + bool _isInjectable = false; + CompileFactoryMetadata _factory; + AssetId _assetId; + final AnnotationMatcher _annotationMatcher; + + _CompileFactoryMetadataVisitor(this._annotationMatcher); + + bool get isInjectable => _isInjectable; + + CompileFactoryMetadata get factory => _factory; + + void reset(AssetId assetId) { + this._assetId = assetId; + this._isInjectable = false; + this._factory = null; + } + + @override + Object visitAnnotation(Annotation node) { + _isInjectable = _annotationMatcher.isInjectable(node, _assetId); + return null; + } + + @override + Object visitFunctionDeclaration(FunctionDeclaration node) { + node.metadata.accept(this); + if (this._isInjectable) { + _factory = new CompileFactoryMetadata( + moduleUrl: toAssetUri(_assetId), + name: node.name.toString(), + diDeps: _getCompileDiDependencyMetadata(node.functionExpression.parameters, {}), 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; - final query = _hasAnnotation(p, "Query") ? _createQueryMetadata(_getAnnotation(p, "Query")) : null; - final viewQuery = _hasAnnotation(p, "ViewQuery") ? _createQueryMetadata(_getAnnotation(p, "ViewQuery")) : null; - - return new CompileDiDependencyMetadata( - token: token, - isAttribute: _hasAnnotation(p, "Attribute"), - isSelf: _hasAnnotation(p, "Self"), - isHost: _hasAnnotation(p, "Host"), - isSkipSelf: _hasAnnotation(p, "SkipSelf"), - isOptional: _hasAnnotation(p, "Optional"), - query: query, - viewQuery: viewQuery); - }).toList(); - } - - _getAnnotation(p, String attrName) => p.metadata.where((m) => m.name.toString() == attrName).first; - _hasAnnotation(p, String attrName) => p.metadata.where((m) => m.name.toString() == attrName).isNotEmpty; - _createQueryMetadata(Annotation a) { - final selector = _readIdentifier(a.arguments.arguments.first); - var descendants = false; - a.arguments.arguments.skip(0).forEach((arg) { - if (arg is NamedExpression && arg.name.toString() == "descendants:") - descendants = naiveEval(arg.expression); - }); - - final selectors = selector is String ? selector.split(",") : [selector]; - return new CompileQueryMetadata(selectors: selectors, descendants: descendants); - } } - - - /// Visitor responsible for processing a [Directive] annotated /// [ClassDeclaration] and creating a [CompileDirectiveMetadata] object. class _DirectiveMetadataVisitor extends Object @@ -233,7 +296,8 @@ class _DirectiveMetadataVisitor extends Object /// The [AssetId] we are currently processing. AssetId _assetId; - _DirectiveMetadataVisitor(this._annotationMatcher, this._lifecycleVisitor, this._typeVisitor) { + _DirectiveMetadataVisitor( + this._annotationMatcher, this._lifecycleVisitor, this._typeVisitor) { reset(null); } @@ -250,7 +314,10 @@ class _DirectiveMetadataVisitor extends Object List _inputs; List _outputs; Map _host; - List _providers; + List _providers; + List _viewProviders; + List _queries; + List _viewQueries; List _lifecycleHooks; CompileTemplateMetadata _cmpTemplate; CompileTemplateMetadata _viewTemplate; @@ -269,7 +336,10 @@ class _DirectiveMetadataVisitor extends Object _inputs = []; _outputs = []; _host = {}; - _providers = []; + _providers = []; + _viewProviders = []; + _queries = []; + _viewQueries = []; _lifecycleHooks = null; _cmpTemplate = null; _viewTemplate = null; @@ -292,6 +362,9 @@ class _DirectiveMetadataVisitor extends Object outputs: _outputs, host: _host, providers: _providers, + viewProviders: _viewProviders, + queries: _queries, + viewQueries: _viewQueries, lifecycleHooks: _lifecycleHooks, template: _template); } @@ -367,6 +440,19 @@ class _DirectiveMetadataVisitor extends Object _host['[${variable.name}]'] = '${variable.name}'; } } + + if (_isAnnotation(meta, 'ContentChild')) { + this._queries.add(_createQueryMetadata(meta, false, true, variable.name.toString())); + } + if (_isAnnotation(meta, 'ContentChildren')) { + this._queries.add(_createQueryMetadata(meta, false, false, variable.name.toString())); + } + if (_isAnnotation(meta, 'ViewChild')) { + this._viewQueries.add(_createQueryMetadata(meta, true, true, variable.name.toString())); + } + if (_isAnnotation(meta, 'ViewChildren')) { + this._viewQueries.add(_createQueryMetadata(meta, false, false, variable.name.toString())); + } } } return null; @@ -383,6 +469,19 @@ class _DirectiveMetadataVisitor extends Object _addPropertyToType(_inputs, node.name.toString(), meta); } + if (_isAnnotation(meta, 'ContentChild') && node.isSetter) { + this._queries.add(_createQueryMetadata(meta, false, true, node.name.toString())); + } + if (_isAnnotation(meta, 'ContentChildren') && node.isSetter) { + this._queries.add(_createQueryMetadata(meta, false, false, node.name.toString())); + } + if (_isAnnotation(meta, 'ViewChild') && node.isSetter) { + this._viewQueries.add(_createQueryMetadata(meta, true, true, node.name.toString())); + } + if (_isAnnotation(meta, 'ViewChildren') && node.isSetter) { + this._viewQueries.add(_createQueryMetadata(meta, false, false, node.name.toString())); + } + if (_isAnnotation(meta, 'HostListener')) { if (meta.arguments.arguments.length == 0 || meta.arguments.arguments.length > 2) { @@ -416,38 +515,13 @@ class _DirectiveMetadataVisitor extends Object } } - void _populateProviders(Expression providerValues) { + void _populateProviders(Expression providerValues, List providers) { _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); + providers.addAll(_readProviders(providerValues)); } else { - _providers.add(new CompileProviderMetadata(token: _readIdentifier(providerValues))); + providers.add(_readIdentifier(providerValues)); } } @@ -539,7 +613,16 @@ class _DirectiveMetadataVisitor extends Object _populateEvents(node.expression); break; case 'providers': - _populateProviders(node.expression); + _populateProviders(node.expression, _providers); + break; + case 'bindings': + _populateProviders(node.expression, _providers); + break; + case 'viewProviders': + _populateProviders(node.expression, _viewProviders); + break; + case 'viewBindings': + _populateProviders(node.expression, _viewProviders); break; } return null; @@ -607,27 +690,30 @@ class _LifecycleHookVisitor extends SimpleAstVisitor> { List visitImplementsClause(ImplementsClause node) { if (node == null || node.interfaces == null) return const []; - return node.interfaces.map((TypeName ifaceTypeName) { - var id = ifaceTypeName.name; - if (_ifaceMatcher.isAfterContentChecked(id, _assetId)) { - return LifecycleHooks.AfterContentChecked; - } else if (_ifaceMatcher.isAfterContentInit(id, _assetId)) { - return LifecycleHooks.AfterContentInit; - } else if (_ifaceMatcher.isAfterViewChecked(id, _assetId)) { - return LifecycleHooks.AfterViewChecked; - } else if (_ifaceMatcher.isAfterViewInit(id, _assetId)) { - return LifecycleHooks.AfterViewInit; - } else if (_ifaceMatcher.isDoCheck(id, _assetId)) { - return LifecycleHooks.DoCheck; - } else if (_ifaceMatcher.isOnChange(id, _assetId)) { - return LifecycleHooks.OnChanges; - } else if (_ifaceMatcher.isOnDestroy(id, _assetId)) { - return LifecycleHooks.OnDestroy; - } else if (_ifaceMatcher.isOnInit(id, _assetId)) { - return LifecycleHooks.OnInit; - } - return null; - }).where((e) => e != null).toList(growable: false); + return node.interfaces + .map((TypeName ifaceTypeName) { + var id = ifaceTypeName.name; + if (_ifaceMatcher.isAfterContentChecked(id, _assetId)) { + return LifecycleHooks.AfterContentChecked; + } else if (_ifaceMatcher.isAfterContentInit(id, _assetId)) { + return LifecycleHooks.AfterContentInit; + } else if (_ifaceMatcher.isAfterViewChecked(id, _assetId)) { + return LifecycleHooks.AfterViewChecked; + } else if (_ifaceMatcher.isAfterViewInit(id, _assetId)) { + return LifecycleHooks.AfterViewInit; + } else if (_ifaceMatcher.isDoCheck(id, _assetId)) { + return LifecycleHooks.DoCheck; + } else if (_ifaceMatcher.isOnChange(id, _assetId)) { + return LifecycleHooks.OnChanges; + } else if (_ifaceMatcher.isOnDestroy(id, _assetId)) { + return LifecycleHooks.OnDestroy; + } else if (_ifaceMatcher.isOnInit(id, _assetId)) { + return LifecycleHooks.OnInit; + } + return null; + }) + .where((e) => e != null) + .toList(growable: false); } } @@ -824,21 +910,199 @@ class _PipeMetadataVisitor extends Object with RecursiveAstVisitor { } } +List _readProviders(ListLiteral providerValues) { + return providerValues.elements.map((el) { + if (el is PrefixedIdentifier || el is SimpleIdentifier) { + return _readIdentifier(el); + } else if (el is InstanceCreationExpression && + (el.constructorName.toString() == "Provider" || + el.constructorName.toString() == "Binding")) { + return _readProvider(el); + } else { + throw new ArgumentError( + 'Incorrect value. Expected a Provider or a String, but got "${el}".'); + } + }).toList(); +} + + +CompileProviderMetadata _readProvider(InstanceCreationExpression el) { + final token = el.argumentList.arguments.first; + + var useClass, useExisting, useValue, useFactory, deps; + el.argumentList.arguments.skip(1).forEach((arg) { + switch (arg.name.toString()) { + case "useClass:": + final id = _readIdentifier(arg.expression); + useClass = new CompileTypeMetadata(prefix: id.prefix, name: id.name); + break; + case "toClass:": + final id = _readIdentifier(arg.expression); + useClass = new CompileTypeMetadata(prefix: id.prefix, name: id.name); + break; + case "useExisting:": + useExisting = _readIdentifier(arg.expression); + break; + case "toAlias:": + useExisting = _readIdentifier(arg.expression); + break; + case "useValue:": + useValue = _readIdentifier(arg.expression); + break; + case "toValue:": + useValue = _readIdentifier(arg.expression); + break; + case "useFactory:": + final id = _readIdentifier(arg.expression); + useFactory = new CompileFactoryMetadata( + name: id.name, prefix: id.prefix); + break; + case "toFactory:": + final id = _readIdentifier(arg.expression); + useFactory = new CompileFactoryMetadata( + name: id.name, prefix: id.prefix); + break; + case "deps:": + deps = _readDeps(arg.expression); + break; + } + }); + return new CompileProviderMetadata( + token: _readIdentifier(token), + useClass: useClass, + useExisting: useExisting, + useValue: useValue, + useFactory: useFactory, + deps: deps); +} + +List _readDeps(ListLiteral deps) { + if (deps is! ListLiteral) { + throw new ArgumentError('Incorrect value is set as deps. ' + 'Expected type is ListLiteral'); + } + + return deps.elements.map((p) { + final list = p is ListLiteral ? p.elements : [p]; + final first = list.first; + + var token; + if (first is InstanceCreationExpression && + (first as InstanceCreationExpression).constructorName.toString() == + "Inject") { + token = _readIdentifier(first.argumentList.arguments[0]); + } else { + token = _readIdentifier(first); + } + + return new CompileDiDependencyMetadata( + token: token, + isSelf: _hasConst(list, "Self"), + isHost: _hasConst(list, "Host"), + isSkipSelf: _hasConst(list, "SkipSelf"), + isOptional: _hasConst(list, "Optional")); + }).toList(); +} + +_createQueryMetadata(Annotation a, bool defaultDescendantsValue, bool first, String propertyName) { + final selector = _readIdentifier(a.arguments.arguments.first); + var descendants = defaultDescendantsValue; + a.arguments.arguments.skip(0).forEach((arg) { + if (arg is NamedExpression && arg.name.toString() == "descendants:") + descendants = naiveEval(arg.expression); + }); + + final selectors = selector is String ? selector.split(",") : [selector]; + return new CompileQueryMetadata( + selectors: selectors, descendants: descendants, first: first, propertyName: propertyName); +} + +List _getCompileDiDependencyMetadata( + FormalParameterList params, Map fieldTypes) { + return params.parameters.map((p) { + if (p is DefaultFormalParameter) { + p = p.parameter; + } + + var token; + final isAttribute = _hasAnnotation(p, "Attribute"); + if (isAttribute) { + token = _readIdentifier(_getAnnotation(p, "Attribute").arguments.arguments.first); + } else { + var type = null; + if (p is SimpleFormalParameter) { + type = p.type; + } else if (p is FieldFormalParameter) { + type = fieldTypes[p.identifier.toString()]; + } + final typeToken = type != null ? _readIdentifier(type.name) : null; + final injectTokens = p.metadata + .where((m) => m.name.toString() == "Inject") + .map((m) => _readIdentifier(m.arguments.arguments[0])); + token = injectTokens.isNotEmpty ? injectTokens.first : typeToken; + } + + var query; + if(_hasAnnotation(p, "Query")) { + query = _createQueryMetadata(_getAnnotation(p, "Query"), false, false, null); + } + if(_hasAnnotation(p, "ContentChildren")) { + query = _createQueryMetadata(_getAnnotation(p, "ContentChildren"), true, false, null); + } + + var viewQuery; + if(_hasAnnotation(p, "ViewQuery")) { + viewQuery = _createQueryMetadata(_getAnnotation(p, "ViewQuery"), false, false, null); + } + if(_hasAnnotation(p, "ViewChildren")) { + viewQuery = _createQueryMetadata(_getAnnotation(p, "ViewChildren"), true, false, null); + } + + return new CompileDiDependencyMetadata( + token: token, + isAttribute: _hasAnnotation(p, "Attribute"), + isSelf: _hasAnnotation(p, "Self"), + isHost: _hasAnnotation(p, "Host"), + isSkipSelf: _hasAnnotation(p, "SkipSelf"), + isOptional: _hasAnnotation(p, "Optional"), + query: query, + viewQuery: viewQuery); + }).toList(); +} + +_getAnnotation(p, String attrName) => + p.metadata.where((m) => m.name.toString() == attrName).first; + +_hasAnnotation(p, String attrName) => + p.metadata.where((m) => m.name.toString() == attrName).isNotEmpty; + +bool _hasConst(List list, String name) => list + .where((m) => + m is InstanceCreationExpression && m.constructorName.toString() == name) + .isNotEmpty; dynamic _readIdentifier(dynamic el) { if (el is PrefixedIdentifier) { - return new CompileIdentifierMetadata(name: '${el.identifier}', prefix: '${el.prefix}'); - + final prefix = '${el.prefix}'; + if (prefix.length > 0 && prefix.toUpperCase()[0] == prefix[0]) { + throw new ArgumentError('Incorrect identifier "${el}".'); + } else { + return new CompileIdentifierMetadata( + name: '${el.identifier}', prefix: prefix); + } } else if (el is SimpleIdentifier) { return new CompileIdentifierMetadata(name: '$el'); - - } else if (el is SimpleStringLiteral){ + } else if (el is DoubleLiteral || + el is IntegerLiteral || + el is SimpleStringLiteral || + el is BooleanLiteral) { return el.value; - - } else if (el is InstanceCreationExpression){ - return new CompileIdentifierMetadata(name: '${el.constructorName}', constConstructor: true); - + } else if (el is NullLiteral) { + return null; + } 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/common/url_resolver.dart b/modules_dart/transform/lib/src/transform/common/url_resolver.dart index 494202bc55..ee4c5ec82b 100644 --- a/modules_dart/transform/lib/src/transform/common/url_resolver.dart +++ b/modules_dart/transform/lib/src/transform/common/url_resolver.dart @@ -13,8 +13,8 @@ class TransformerUrlResolver implements UrlResolver { if (!uri.isAbsolute) { if (baseUrl == null) throw new ArgumentError.notNull('baseUrl'); - if (baseUrl.isEmpty) throw new ArgumentError.value( - '(empty string)', 'baseUrl'); + if (baseUrl.isEmpty) + throw new ArgumentError.value('(empty string)', 'baseUrl'); uri = Uri.parse(baseUrl).resolveUri(uri); } @@ -29,8 +29,8 @@ String toAssetUri(AssetId assetId) { AssetId fromUri(String assetUri) { if (assetUri == null) throw new ArgumentError.notNull('assetUri'); - if (assetUri.isEmpty) throw new ArgumentError.value( - '(empty string)', 'assetUri'); + if (assetUri.isEmpty) + throw new ArgumentError.value('(empty string)', 'assetUri'); var uri = toAssetScheme(Uri.parse(assetUri)); return new AssetId( uri.pathSegments.first, uri.pathSegments.skip(1).join('/')); diff --git a/modules_dart/transform/lib/src/transform/directive_metadata_linker/ng_deps_linker.dart b/modules_dart/transform/lib/src/transform/directive_metadata_linker/ng_deps_linker.dart index 35fe33e60c..dbeebcf720 100644 --- a/modules_dart/transform/lib/src/transform/directive_metadata_linker/ng_deps_linker.dart +++ b/modules_dart/transform/lib/src/transform/directive_metadata_linker/ng_deps_linker.dart @@ -64,20 +64,20 @@ Future> _processNgImports(NgDepsModel model, return Future .wait( importsAndExports.where(_isNotDartDirective).map((dynamic directive) { - // Check whether the import or export generated summary NgMeta information. - final summaryJsonUri = - resolver.resolve(assetUri, toSummaryExtension(directive.uri)); - return reader.hasInput(fromUri(summaryJsonUri)).then((hasInput) { - if (hasInput) { - retVal[directive.uri] = summaryJsonUri; - } - }, onError: (err, stack) { - log.warning( - 'Error while looking for $summaryJsonUri. ' - 'Message: $err\n' - 'Stack: $stack', - asset: assetId); - }); - })) + // Check whether the import or export generated summary NgMeta information. + final summaryJsonUri = + resolver.resolve(assetUri, toSummaryExtension(directive.uri)); + return reader.hasInput(fromUri(summaryJsonUri)).then((hasInput) { + if (hasInput) { + retVal[directive.uri] = summaryJsonUri; + } + }, onError: (err, stack) { + log.warning( + 'Error while looking for $summaryJsonUri. ' + 'Message: $err\n' + 'Stack: $stack', + asset: assetId); + }); + })) .then((_) => retVal); } 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 5de4770c49..1728977d49 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 @@ -3,6 +3,7 @@ library angular2.transform.directive_metadata_linker.linker; import 'dart:async'; import 'dart:convert'; +import 'package:angular2/src/compiler/directive_metadata.dart'; import 'package:angular2/src/transform/common/asset_reader.dart'; import 'package:angular2/src/transform/common/logging.dart'; import 'package:angular2/src/transform/common/names.dart'; @@ -12,36 +13,40 @@ import 'package:barback/barback.dart'; import 'ng_deps_linker.dart'; -/// Returns [NgMeta] associated with [entryPoint] combined with the [NgMeta] of +/// Returns [NgMeta] associated with the provided asset combined with the [NgMeta] of /// all files `export`ed from the original file. /// -/// This includes entries for every `Directive`-annotated class and -/// constants that match the directive-aliases pattern. +/// The returned NgMeta has all the identifiers resolved. /// -/// There are entries for each of these which is visible from a file importing -/// the original .dart file that produced `entryPoint`. That is, this includes -/// all `Directive` annotated public classes in that file, all `DirectiveAlias` -/// annotated public variables, and any of those entries which are visible from -/// files which the .dart file `export`ed. -/// -/// Returns an empty [NgMeta] if there are no `Directive`-annotated classes or -/// `DirectiveAlias` annotated constants in `entryPoint`. -Future linkDirectiveMetadata( - AssetReader reader, AssetId assetId) async { - var ngMeta = await _readNgMeta(reader, assetId); +/// `summaryAssetId` - the unlinked asset id (source) +/// `summaryAssetId` - the linked asset id (dest) +/// `resolvedIdentifiers` - preresolved identifiers (e.g., Window) +/// `ngMetas` - in memory cache of linked ngMeta files +Future linkDirectiveMetadata(AssetReader reader, AssetId summaryAssetId, + AssetId metaAssetId, Map resolvedIdentifiers, + [Map ngMetas]) async { + if (ngMetas == null) ngMetas = {}; + + var ngMeta = await _readNgMeta(reader, summaryAssetId, ngMetas); if (ngMeta == null || ngMeta.isEmpty) return null; await Future.wait([ - linkNgDeps(ngMeta.ngDeps, reader, assetId, _urlResolver), + linkNgDeps(ngMeta.ngDeps, reader, summaryAssetId, _urlResolver), logElapsedAsync(() async { - await _linkRecursive(ngMeta, reader, assetId, new Set()); + final linker = new _Linker(reader, ngMetas, resolvedIdentifiers); + await linker.linkRecursive(ngMeta, metaAssetId, new Set()); return ngMeta; - }, operationName: 'linkDirectiveMetadata', assetId: assetId) + }, operationName: 'linkDirectiveMetadata', assetId: summaryAssetId) ]); + return ngMeta; } -Future _readNgMeta(AssetReader reader, AssetId ngMetaAssetId) async { +final _urlResolver = const TransformerUrlResolver(); + +Future _readNgMeta(AssetReader reader, AssetId ngMetaAssetId, + Map ngMetas) async { + if (ngMetas.containsKey(ngMetaAssetId)) return ngMetas[ngMetaAssetId]; if (!(await reader.hasInput(ngMetaAssetId))) return null; var ngMetaJson = await reader.readAsString(ngMetaAssetId); @@ -50,34 +55,381 @@ Future _readNgMeta(AssetReader reader, AssetId ngMetaAssetId) async { return new NgMeta.fromJson(JSON.decode(ngMetaJson)); } -final _urlResolver = const TransformerUrlResolver(); +class _Linker { + final AssetReader reader; + final Map ngMetas; + final Map resolvedIdentifiers; + + _Linker(this.reader, this.ngMetas, this.resolvedIdentifiers); + + Future linkRecursive(NgMeta ngMeta, AssetId assetId, Set seen) async { + if (seen.contains(assetId)) return ngMeta; + + final newSeen = new Set.from(seen) + ..add(assetId); + + await _resolveDeps(ngMeta, assetId, newSeen); + await _resolveIdentifiers(ngMeta, assetId); + await _mergeExports(ngMeta, assetId); + + ngMetas[assetId] = ngMeta; -Future _linkRecursive(NgMeta ngMeta, AssetReader reader, AssetId assetId, - Set seen) async { - if (ngMeta == null || - ngMeta.ngDeps == null || - ngMeta.ngDeps.exports == null) { return ngMeta; } - var assetUri = toAssetUri(assetId); - 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); - try { - final exportAssetId = fromUri(uri); - final exportNgMeta = await _readNgMeta(reader, exportAssetId); - if (exportNgMeta != null) { - await _linkRecursive(exportNgMeta, reader, exportAssetId, seen); - ngMeta.addAll(exportNgMeta); + Future _resolveDeps(NgMeta ngMeta, AssetId assetId, Set seen) async { + final importsAndExports = []; + if (ngMeta != null && + ngMeta.ngDeps != null && + ngMeta.ngDeps.exports != null) + importsAndExports.addAll(ngMeta.ngDeps.exports); + + if (ngMeta != null && + ngMeta.needsResolution && + ngMeta.ngDeps != null && + ngMeta.ngDeps.imports != null) + importsAndExports + .addAll(ngMeta.ngDeps.imports.where((i) => !i.isDeferred)); + + final assetUri = toAssetUri(assetId); + for (var withUri in importsAndExports) { + if (isDartCoreUri(withUri.uri)) continue; + final metaAsset = + fromUri(_urlResolver.resolve(assetUri, toMetaExtension(withUri.uri))); + final summaryAsset = fromUri( + _urlResolver.resolve(assetUri, toSummaryExtension(withUri.uri))); + + if (!await _hasMeta(metaAsset)) { + final ngMeta = await _readSummary(summaryAsset); + if (ngMeta != null) { + await linkRecursive(ngMeta, metaAsset, seen); + } } - } catch (err, st) { - // Log and continue. - log.warning('Failed to fetch $uri. Message: $err.\n$st', asset: assetId); } - })); + } + + Future _resolveIdentifiers(NgMeta ngMeta, AssetId assetId) async { + if (ngMeta.needsResolution) { + final resolver = new _NgMetaIdentifierResolver( + assetId, reader, ngMetas, resolvedIdentifiers); + return resolver.resolveNgMeta(ngMeta); + } else { + return null; + } + } + + Future _mergeExports(NgMeta ngMeta, AssetId assetId) async { + if (ngMeta == null || + ngMeta.ngDeps == null || + ngMeta.ngDeps.exports == null) { + return ngMeta; + } + var assetUri = toAssetUri(assetId); + + return Future.wait(ngMeta.ngDeps.exports.map((r) => r.uri) + .where((export) => !isDartCoreUri(export)) + .map((export) => + _urlResolver.resolve(assetUri, toMetaExtension(export))) + .map((uri) async { + try { + final exportAssetId = fromUri(uri); + final exportNgMeta = await _readMeta(exportAssetId); + if (exportNgMeta != null) { + ngMeta.addAll(exportNgMeta); + } + } catch (err, st) { + // Log and continue. + log.warning('Failed to fetch $uri. Message: $err.\n$st', + asset: assetId); + } + })); + } + + Future _readSummary(AssetId summaryAssetId) async { + if (!(await reader.hasInput(summaryAssetId))) return null; + + var ngMetaJson = await reader.readAsString(summaryAssetId); + if (ngMetaJson == null || ngMetaJson.isEmpty) return null; + return new NgMeta.fromJson(JSON.decode(ngMetaJson)); + } + + Future _readMeta(AssetId metaAssetId) async { + final content = await _readNgMeta(reader, metaAssetId, ngMetas); + if (content != null) { + ngMetas[metaAssetId] = content; + } + return content; + } + + Future _hasMeta(AssetId ngMetaAssetId) async { + return ngMetas.containsKey(ngMetaAssetId) || + await reader.hasInput(ngMetaAssetId); + } +} + +class _NgMetaIdentifierResolver { + final Map resolvedIdentifiers; + final Map ngMetas; + final AssetReader reader; + final AssetId entryPoint; + + _NgMetaIdentifierResolver(this.entryPoint, this.reader, this.ngMetas, this.resolvedIdentifiers); + + Future resolveNgMeta(NgMeta ngMeta) async { + final ngMetaMap = await _extractNgMetaMap(ngMeta); + ngMeta.identifiers.forEach((_, meta) { + if (meta is CompileIdentifierMetadata && meta.value != null) { + meta.value = _resolveProviders(ngMetaMap, meta.value, "root"); + } + }); + + ngMeta.identifiers.forEach((_, meta) { + if (meta is CompileDirectiveMetadata) { + _resolveProviderMetadata(ngMetaMap, meta); + _resolveQueryMetadata(ngMetaMap, meta); + _resolveDiDependencyMetadata(ngMetaMap, meta.type.name, meta.type.diDeps); + } else if (meta is CompileTypeMetadata) { + _resolveDiDependencyMetadata(ngMetaMap, meta.name, meta.diDeps); + } else if (meta is CompileFactoryMetadata) { + _resolveDiDependencyMetadata(ngMetaMap, meta.name, meta.diDeps); + } + }); + } + + List _resolveProviders(Map ngMetaMap, Object value, String neededBy) { + + if (value is List) { + final res = []; + for (var v in value) { + res.addAll(_resolveProviders(ngMetaMap, v, neededBy)); + } + return res; + + } else if (value is CompileProviderMetadata) { + _resolveProvider(ngMetaMap, neededBy, value); + + return [value]; + + } else if (value is CompileIdentifierMetadata) { + final resolved = _resolveIdentifier(ngMetaMap, neededBy, value); + if (resolved == null) return []; + + if (resolved is CompileTypeMetadata) { + return [new CompileProviderMetadata(token: resolved, useClass: resolved)]; + + } else if (resolved is CompileIdentifierMetadata && resolved.value is List) { + return _resolveProviders(ngMetaMap, resolved.value, neededBy); + + } else if (resolved is CompileIdentifierMetadata && resolved.value is CompileProviderMetadata) { + return [_resolveProviders(ngMetaMap, resolved.value, neededBy)]; + + } else { + return []; + } + + } else { + return []; + } + } + + void _resolveProviderMetadata(Map ngMetaMap, CompileDirectiveMetadata dirMeta) { + final neededBy = dirMeta.type.name; + if (dirMeta.providers != null) { + dirMeta.providers = + _resolveProviders(ngMetaMap, dirMeta.providers, neededBy); + } + + if (dirMeta.viewProviders != null) { + dirMeta.viewProviders = + _resolveProviders(ngMetaMap, dirMeta.viewProviders, neededBy); + } + } + + void _resolveQueryMetadata(Map ngMetaMap, CompileDirectiveMetadata dirMeta) { + final neededBy = dirMeta.type.name; + if (dirMeta.queries != null) { + _resolveQueries(ngMetaMap, dirMeta.queries, neededBy); + } + if (dirMeta.viewQueries != null) { + _resolveQueries(ngMetaMap, dirMeta.viewQueries, neededBy); + } + } + + void _resolveQueries(Map ngMetaMap, List queries, String neededBy) { + queries.forEach((q) { + q.selectors = q.selectors.map((s) => _resolveIdentifier(ngMetaMap, neededBy, s)).toList(); + }); + } + + void _resolveProvider(Map ngMetaMap, + String neededBy, CompileProviderMetadata provider) { + provider.token = _resolveIdentifier(ngMetaMap, neededBy, provider.token); + if (provider.useClass != null) { + provider.useClass = + _resolveIdentifier(ngMetaMap, neededBy, provider.useClass); + } + if (provider.useExisting != null) { + provider.useExisting = + _resolveIdentifier(ngMetaMap, neededBy, provider.useExisting); + } + if (provider.useValue != null) { + provider.useValue = + _resolveIdentifier(ngMetaMap, neededBy, provider.useValue); + } + if (provider.useFactory != null) { + provider.useFactory = _resolveIdentifier(ngMetaMap, neededBy, provider.useFactory); + } + if (provider.deps != null) { + _resolveDiDependencyMetadata(ngMetaMap, neededBy, provider.deps); + };; + } + + void _resolveDiDependencyMetadata(Map ngMetaMap, + String neededBy, List deps) { + if (deps == null) return; + for (var dep in deps) { + _setModuleUrl(ngMetaMap, neededBy, dep.token); + if (dep.query != null) { + dep.query.selectors + .forEach((s) => _setModuleUrl(ngMetaMap, neededBy, s)); + } + if (dep.viewQuery != null) { + dep.viewQuery.selectors + .forEach((s) => _setModuleUrl(ngMetaMap, neededBy, s)); + } + } + } + + void _setModuleUrl(Map ngMetaMap, String neededBy, dynamic id) { + final resolved = _resolveIdentifier(ngMetaMap, neededBy, id); + if (resolved != null && id is CompileIdentifierMetadata) { + id.moduleUrl = resolved.moduleUrl; + } + } + + /// Resolves an identifier using the provided ngMetaMap. + /// + /// ngMetaMap - a map of prefixes to the symbols available via those prefixes + /// neededBy - a type using the unresolved symbol. It's used to generate + /// good error message. + /// id - an unresolved id. + dynamic _resolveIdentifier(Map ngMetaMap, String neededBy, dynamic id) { + if (id is String || id is bool || id is num || id == null) return id; + if (id is CompileMetadataWithIdentifier) { + id = id.identifier; + } + + if (id.moduleUrl != null) return id; + + final prefix = id.prefix == null ? "" : id.prefix; + + if (!ngMetaMap.containsKey(prefix)) { + final resolved = _resolveSpecialCases(id); + if (resolved != null) { + return resolved; + } else { + log.error( + 'Missing prefix "${prefix}" ' + 'needed by "${neededBy}" from metadata map', + asset: entryPoint); + return null; + } + } + + final depNgMeta = ngMetaMap[prefix]; + if (depNgMeta.identifiers.containsKey(id.name)) { + final res = depNgMeta.identifiers[id.name]; + if (res is CompileMetadataWithIdentifier) { + return res.identifier; + } else { + return res; + } + } else if (_isPrimitive(id.name)) { + return id; + } else { + final resolved = _resolveSpecialCases(id); + if (resolved != null) { + return resolved; + } else { + log.error( + 'Missing identifier "${id.name}" ' + 'needed by "${neededBy}" from metadata map', + asset: entryPoint); + return null; + } + } + } + + dynamic _resolveSpecialCases(CompileIdentifierMetadata id) { + if (resolvedIdentifiers != null && + resolvedIdentifiers.containsKey(id.name)) { + return new CompileIdentifierMetadata( + name: id.name, moduleUrl: resolvedIdentifiers[id.name]); + + // these are so common that we special case them in the transformer + } else if (id.name == "Window" || id.name == "Document") { + return new CompileIdentifierMetadata(name: id.name, moduleUrl: 'dart:html'); + } else if (id.name == "Logger") { + return new CompileIdentifierMetadata(name: id.name, moduleUrl: 'asset:logging/lib/logging.dart'); + } else if (id.name == "Clock") { + return new CompileIdentifierMetadata(name: id.name, moduleUrl: 'asset:quiver/lib/src/time/clock.dart'); + } else if (id.name == "Log") { + return new CompileIdentifierMetadata(name: id.name, moduleUrl: 'asset:angular2/lib/src/testing/utils.dart'); + } else if (id.name == "TestComponentBuilder") { + return new CompileIdentifierMetadata(name: id.name, moduleUrl: 'asset:angular2/lib/src/testing/test_component_builder.dart'); + } else if (id.name == "FakeAsync") { + return new CompileIdentifierMetadata(name: id.name, moduleUrl: 'asset:angular2/lib/src/testing/fake_async.dart'); + } else if (id.name == "StreamTracer") { + return new CompileIdentifierMetadata(name: id.name, moduleUrl: 'asset:streamy/lib/src/core/tracing.dart'); + } else if (id.name == "Tracer") { + return new CompileIdentifierMetadata(name: id.name, moduleUrl: 'asset:streamy/lib/src/core/tracing.dart'); + } else if (id.name == "RequestHandler") { + return new CompileIdentifierMetadata(name: id.name, moduleUrl: 'asset:streamy/lib/src/core/request_handler.dart'); + } else { + return null; + } + } + + bool _isPrimitive(String typeName) => + typeName == "String" || + typeName == "Object" || + typeName == "num" || + typeName == "int" || + typeName == "double" || + typeName == "bool" || + typeName == "dynamic"; + + /// Walks all the imports and creates a map from prefixes to + /// all the symbols available through those prefixes + Future> _extractNgMetaMap(NgMeta ngMeta) async { + final res = {"": new NgMeta.empty()}; + res[""].addAll(ngMeta); + + if (ngMeta.ngDeps == null || ngMeta.ngDeps.imports == null) return res; + + for (var import in ngMeta.ngDeps.imports) { + if (isDartCoreUri(import.uri)) continue; + + final assetUri = toAssetUri(entryPoint); + final metaAsset = + fromUri(_urlResolver.resolve(assetUri, toMetaExtension(import.uri))); + final newMeta = await _readNgMeta(reader, metaAsset, ngMetas); + + if (!res.containsKey(import.prefix)) { + res[import.prefix] = new NgMeta.empty(); + } + + if (newMeta != null) { + res[import.prefix].addAll(newMeta); + } else { + final summaryAsset = + fromUri(_urlResolver.resolve(assetUri, toSummaryExtension(import.uri))); + final summary = await _readNgMeta(reader, summaryAsset, {}); + if (summary != null) { + res[import.prefix].addAll(summary); + } + } + } + return res; + } } diff --git a/modules_dart/transform/lib/src/transform/directive_metadata_linker/transformer.dart b/modules_dart/transform/lib/src/transform/directive_metadata_linker/transformer.dart index 8ce0e61f2d..20facd15d2 100644 --- a/modules_dart/transform/lib/src/transform/directive_metadata_linker/transformer.dart +++ b/modules_dart/transform/lib/src/transform/directive_metadata_linker/transformer.dart @@ -8,6 +8,8 @@ import 'package:barback/barback.dart'; import 'package:angular2/src/transform/common/asset_reader.dart'; import 'package:angular2/src/transform/common/names.dart'; import 'package:angular2/src/transform/common/zone.dart' as zone; +import 'package:angular2/src/transform/common/options.dart'; +import 'package:angular2/src/transform/common/logging.dart'; import 'ng_meta_linker.dart'; @@ -33,6 +35,12 @@ import 'ng_meta_linker.dart'; class DirectiveMetadataLinker extends Transformer implements LazyTransformer { final _encoder = const JsonEncoder.withIndent(' '); + final TransformerOptions options; + final Map ngMetasCache = {}; + final Set errorMessages = new Set(); + + DirectiveMetadataLinker(this.options); + @override bool isPrimary(AssetId id) => id.path.endsWith(SUMMARY_META_EXTENSION); @@ -47,7 +55,11 @@ class DirectiveMetadataLinker extends Transformer implements LazyTransformer { var primaryId = transform.primaryInput.id; return linkDirectiveMetadata( - new AssetReader.fromTransform(transform), primaryId).then((ngMeta) { + new AssetReader.fromTransform(transform), + primaryId, + _ngLinkedAssetId(primaryId), + options.resolvedIdentifiers, + ngMetasCache).then((ngMeta) { if (ngMeta != null) { final outputId = _ngLinkedAssetId(primaryId); // Not outputting an asset could confuse barback. @@ -55,7 +67,7 @@ class DirectiveMetadataLinker extends Transformer implements LazyTransformer { transform.addOutput(new Asset.fromString(outputId, output)); } }); - }, log: transform.logger); + }, log: new DeduppingLogger(transform.logger, errorMessages)); } } 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 3c44e10460..959297ed6e 100644 --- a/modules_dart/transform/lib/src/transform/directive_processor/rewriter.dart +++ b/modules_dart/transform/lib/src/transform/directive_processor/rewriter.dart @@ -5,7 +5,8 @@ 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/directive_metadata.dart' + show CompileIdentifierMetadata, CompileProviderMetadata; 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'; @@ -90,13 +91,15 @@ class _NgMetaVisitor extends Object with SimpleAstVisitor { @override Object visitClassDeclaration(ClassDeclaration node) { - _normalizations.add( - _reader.readTypeMetadata(node, assetId).then((compileMetadataWithIdentifier) { - if (compileMetadataWithIdentifier!= null) { + _normalizations.add(_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)); + ngMeta.identifiers[node.name.name] = new CompileIdentifierMetadata( + name: node.name.name, moduleUrl: toAssetUri(assetId)); } }).catchError((err) { log.error('ERROR: $err', asset: assetId); @@ -114,8 +117,8 @@ class _NgMetaVisitor extends Object with SimpleAstVisitor { // 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)); + final id = _reader.readIdentifierMetadata(variable, assetId); + ngMeta.identifiers[variable.name.name] = id; } var initializer = variable.initializer; @@ -127,7 +130,6 @@ 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; } } @@ -136,15 +138,33 @@ class _NgMetaVisitor extends Object with SimpleAstVisitor { @override Object visitFunctionTypeAlias(FunctionTypeAlias node) { - ngMeta.identifiers[node.name.name] = - new CompileIdentifierMetadata(name: node.name.name, moduleUrl: toAssetUri(assetId)); + ngMeta.identifiers[node.name.name] = new CompileIdentifierMetadata( + name: node.name.name, moduleUrl: toAssetUri(assetId)); + return null; + } + + @override + Object visitFunctionDeclaration(FunctionDeclaration node) { + _normalizations.add(_reader + .readFactoryMetadata(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; } @override Object visitEnumDeclaration(EnumDeclaration node) { - ngMeta.identifiers[node.name.name] = - new CompileIdentifierMetadata(name: node.name.name, moduleUrl: toAssetUri(assetId)); + 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/change_detector_codegen.dart b/modules_dart/transform/lib/src/transform/template_compiler/change_detector_codegen.dart index fa65adae01..850445d8b3 100644 --- a/modules_dart/transform/lib/src/transform/template_compiler/change_detector_codegen.dart +++ b/modules_dart/transform/lib/src/transform/template_compiler/change_detector_codegen.dart @@ -123,8 +123,8 @@ class _CodegenState { var names = new CodegenNameUtil( protoRecords, eventBindings, def.directiveRecords, '$genPrefix$_UTIL'); - var logic = new CodegenLogicUtil( - names, '$genPrefix$_UTIL', '$genPrefix$_STATE'); + var logic = + new CodegenLogicUtil(names, '$genPrefix$_UTIL', '$genPrefix$_STATE'); return new _CodegenState._( genPrefix, def.id, 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 eafd0fe3cb..0567727b42 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 @@ -29,20 +29,18 @@ Future createCompileData( AssetReader reader, AssetId assetId, List platformDirectives, - List platformPipes, - Map resolvedIdentifiers - ) async { + List platformPipes) async { return logElapsedAsync(() async { final creator = await _CompileDataCreator.create( - reader, assetId, platformDirectives, platformPipes, resolvedIdentifiers); + reader, assetId, platformDirectives, platformPipes); return creator != null ? creator.createCompileData() : null; }, operationName: 'createCompileData', assetId: assetId); } class CompileDataResults { final NgMeta ngMeta; - final Map viewDefinitions; + final Map + viewDefinitions; CompileDataResults._(this.ngMeta, this.viewDefinitions); } @@ -55,20 +53,19 @@ class _CompileDataCreator { final NgMeta ngMeta; final List platformDirectives; final List platformPipes; - final Map resolvedIdentifiers; _CompileDataCreator(this.reader, this.entryPoint, this.ngMeta, - this.platformDirectives, this.platformPipes, this.resolvedIdentifiers); + this.platformDirectives, this.platformPipes); static Future<_CompileDataCreator> create(AssetReader reader, AssetId assetId, - List platformDirectives, List platformPipes, Map resolvedIdentifiers) async { + List platformDirectives, List platformPipes) async { if (!(await reader.hasInput(assetId))) return null; final json = await reader.readAsString(assetId); if (json == null || json.isEmpty) return null; final ngMeta = new NgMeta.fromJson(JSON.decode(json)); return new _CompileDataCreator( - reader, assetId, ngMeta, platformDirectives, platformPipes, resolvedIdentifiers); + reader, assetId, ngMeta, platformDirectives, platformPipes); } NgDepsModel get ngDeps => ngMeta.ngDeps; @@ -84,14 +81,15 @@ class _CompileDataCreator { } return false; }); + if (!hasTemplate) return new CompileDataResults._(ngMeta, const {}); final compileData = {}; - final ngMetaMap = await _extractNgMeta(); final platformDirectives = await _readPlatformTypes(this.platformDirectives, 'directives'); final platformPipes = await _readPlatformTypes(this.platformPipes, 'pipes'); + final ngMetaMap = await _extractNgMeta(); for (var reflectable in ngDeps.reflectables) { if (ngMeta.identifiers.containsKey(reflectable.name)) { @@ -109,9 +107,6 @@ class _CompileDataCreator { compileDatum.pipes .addAll(_resolveTypeMetadata(ngMetaMap, reflectable.pipes)); compileData[reflectable] = compileDatum; - - _resolveDiDependencyMetadata(ngMetaMap, compileDirectiveMetadata.type, compileDirectiveMetadata.type.diDeps); - _resolveProviderMetadata(ngMetaMap, compileDirectiveMetadata); } } } @@ -130,11 +125,12 @@ class _CompileDataCreator { return null; } final depNgMeta = ngMetaMap[dep.prefix]; - - if (depNgMeta.identifiers.containsKey(dep.name)) { - resolvedMetadata.add(depNgMeta.identifiers[dep.name]); - } else if (depNgMeta.aliases.containsKey(dep.name)) { + if (depNgMeta.aliases.containsKey(dep.name)) { resolvedMetadata.addAll(depNgMeta.flatten(dep.name)); + + } else if (depNgMeta.identifiers.containsKey(dep.name)) { + resolvedMetadata.add(depNgMeta.identifiers[dep.name]); + } else { log.error( 'Could not find Directive/Pipe entry for $dep. ' @@ -147,98 +143,6 @@ 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); - if (dep.query != null) { - dep.query.selectors = dep.query.selectors.map((s) => _resolveIdentifier(ngMetaMap, neededBy, s)).toList(); - } - if (dep.viewQuery != null) { - dep.viewQuery.selectors = dep.viewQuery.selectors.map((s) => _resolveIdentifier(ngMetaMap, neededBy, s)).toList(); - } - } - } - - 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 (resolvedIdentifiers != null && resolvedIdentifiers.containsKey(id.name)) { - return new CompileIdentifierMetadata(name: id.name, moduleUrl: resolvedIdentifiers[id.name]); - - } 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 []; @@ -266,10 +170,12 @@ class _CompileDataCreator { if (jsonString != null && jsonString.isNotEmpty) { var newMetadata = new NgMeta.fromJson(JSON.decode(jsonString)); - if (newMetadata.identifiers.containsKey(token)) { - return [newMetadata.identifiers[token]]; - } else if (newMetadata.aliases.containsKey(token)) { + if (newMetadata.aliases.containsKey(token)) { return newMetadata.flatten(token); + + } else if (newMetadata.identifiers.containsKey(token)) { + return [newMetadata.identifiers[token]]; + } else { log.warning('Could not resolve platform type ${token} in ${uri}', asset: 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 a373bf2686..c007b81bda 100644 --- a/modules_dart/transform/lib/src/transform/template_compiler/generator.dart +++ b/modules_dart/transform/lib/src/transform/template_compiler/generator.dart @@ -44,7 +44,7 @@ Future processTemplates(AssetReader reader, AssetId assetId, Map resolvedIdentifiers }) async { var viewDefResults = await createCompileData( - reader, assetId, platformDirectives, platformPipes, resolvedIdentifiers); + reader, assetId, platformDirectives, platformPipes); if (viewDefResults == null) return null; final compileTypeMetadatas = viewDefResults.ngMeta.identifiers.values; if (compileTypeMetadatas.isNotEmpty) { @@ -106,4 +106,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/transformer.dart b/modules_dart/transform/lib/src/transform/transformer.dart index 0583685e96..63dc89dea8 100644 --- a/modules_dart/transform/lib/src/transform/transformer.dart +++ b/modules_dart/transform/lib/src/transform/transformer.dart @@ -34,7 +34,7 @@ class AngularTransformerGroup extends TransformerGroup { } else { phases = [ [new DirectiveProcessor(options)], - [new DirectiveMetadataLinker()], + [new DirectiveMetadataLinker(options)], [new ReflectionRemover(options)], [ new DeferredRewriter(), diff --git a/modules_dart/transform/lib/transform/codegen.dart b/modules_dart/transform/lib/transform/codegen.dart index e48b2c97d0..236807d290 100644 --- a/modules_dart/transform/lib/transform/codegen.dart +++ b/modules_dart/transform/lib/transform/codegen.dart @@ -39,7 +39,7 @@ class CodegenTransformer extends TransformerGroup { } else { phases = [ [new DirectiveProcessor(options)], - [new DirectiveMetadataLinker()], + [new DirectiveMetadataLinker(options)], [new StylesheetCompiler(), new TemplateCompiler(options),], ]; } 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 493b5355c6..6f136163be 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'); \ No newline at end of file + template: 'Baz'); 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 30e4c09398..89998a0ac7 100644 --- a/modules_dart/transform/test/transform/common/ng_meta_test.dart +++ b/modules_dart/transform/test/transform/common/ng_meta_test.dart @@ -55,7 +55,8 @@ void allTests() { a.aliases['a3'] = ['T3', 'a2']; a.aliases['a4'] = ['a3', 'T0']; - expect(a.flatten('a4'), equals([mockDirMetadata[3], mockDirMetadata[1], mockDirMetadata[0]])); + expect(a.flatten('a4'), + equals([mockDirMetadata[3], mockDirMetadata[1], mockDirMetadata[0]])); }); test('should detect cycles.', () { @@ -64,7 +65,10 @@ void allTests() { a.aliases['a1'] = ['T0', 'a2']; a.aliases['a2'] = ['a1']; - expect(() => a.flatten('a1'), throwsA(predicate((ex) => new RegExp('Cycle: a1 -> a2 -> a1.').hasMatch(ex.message)))); + expect( + () => a.flatten('a1'), + throwsA(predicate((ex) => + new RegExp('Cycle: a1 -> a2 -> a1.').hasMatch(ex.message)))); }); test('should allow duplicates.', () { @@ -98,6 +102,38 @@ void allTests() { expect(a.aliases['b'], equals(['y'])); }); }); + + group('needsResolution', () { + test('should be true if there is a provider', () { + var a = new NgMeta.empty(); + a.identifiers["MyIdentifier"] = new CompileIdentifierMetadata(name: 'MyIdentifier', value: new CompileProviderMetadata()); + expect(a.needsResolution, isTrue); + }); + + test('should be true if there is an injectable service', () { + var a = new NgMeta.empty(); + a.identifiers["MyIdentifier"] = new CompileTypeMetadata(); + expect(a.needsResolution, isTrue); + }); + + test('should be true if there is an directive', () { + var a = new NgMeta.empty(); + a.identifiers["MyIdentifier"] = new CompileDirectiveMetadata(); + expect(a.needsResolution, isTrue); + }); + + test('should be true if there is a pipe', () { + var a = new NgMeta.empty(); + a.identifiers["MyIdentifier"] = new CompilePipeMetadata(); + expect(a.needsResolution, isTrue); + }); + + test('should be false otherwise', () { + var a = new NgMeta.empty(); + a.identifiers["MyIdentifier"] = "some value"; + expect(a.needsResolution, isFalse); + }); + }); } _checkSimilar(NgMeta a, NgMeta b) { diff --git a/modules_dart/transform/test/transform/common/read_file.dart b/modules_dart/transform/test/transform/common/read_file.dart index 257835881a..3249e365da 100644 --- a/modules_dart/transform/test/transform/common/read_file.dart +++ b/modules_dart/transform/test/transform/common/read_file.dart @@ -41,6 +41,10 @@ class TestAssetReader implements AssetReader { return new Future.value(exists); } + void clear() { + this._overrideAssets.clear(); + } + void addAsset(AssetId id, String contents) { _overrideAssets[id] = contents; } 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 a8823be881..de81c59f71 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 @@ -21,46 +21,53 @@ var formatter = new DartFormatter(); main() => allTests(); +var oldTest = test; void allTests() { + var test = (name, fn) { +// if (name.contains('indirection')) { + oldTest(name, fn); +// } + }; + TestAssetReader reader = null; final moduleBase = 'asset:a'; - var fooNgMeta, fooAssetId; - var barNgMeta, barAssetId; - var bazNgMeta, bazAssetId; - var aliasNgMeta, aliasAssetId; + var fooNgMeta, fooAssetId, fooMetaAssetId, fooComponentMeta; + var barNgMeta, barAssetId, barMetaAssetId, barComponentMeta; + var bazNgMeta, bazAssetId, bazMetaAssetId, bazComponentMeta; /// 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(barMetaAssetId, JSON.encode(fooNgMeta.toJson())) ..addAsset(barAssetId, JSON.encode(barNgMeta.toJson())) + ..addAsset(barMetaAssetId, JSON.encode(barNgMeta.toJson())) ..addAsset(bazAssetId, JSON.encode(bazNgMeta.toJson())) - ..addAsset(aliasAssetId, JSON.encode(aliasNgMeta.toJson())); + ..addAsset(bazMetaAssetId, JSON.encode(bazNgMeta.toJson())); setUp(() { reader = new TestAssetReader(); // Establish some test NgMeta objects with one Component each. - var fooComponentMeta = createFoo(moduleBase); + fooComponentMeta = createFoo(moduleBase); fooNgMeta = new NgMeta(ngDeps: new NgDepsModel()); fooNgMeta.identifiers[fooComponentMeta.type.name] = fooComponentMeta; - var barComponentMeta = createBar(moduleBase); + barComponentMeta = createBar(moduleBase); barNgMeta = new NgMeta(ngDeps: new NgDepsModel()); barNgMeta.identifiers[barComponentMeta.type.name] = barComponentMeta; - var bazComponentMeta = createBaz(moduleBase); + bazComponentMeta = createBaz(moduleBase); bazNgMeta = new NgMeta(ngDeps: new NgDepsModel()); 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')); + fooMetaAssetId = new AssetId('a', toMetaExtension('lib/foo.dart')); barAssetId = new AssetId('a', toSummaryExtension('lib/bar.dart')); + barMetaAssetId = new AssetId('a', toMetaExtension('lib/bar.dart')); bazAssetId = new AssetId('a', toSummaryExtension('lib/baz.dart')); - aliasAssetId = new AssetId('a', toSummaryExtension('lib/alais.dart')); + bazMetaAssetId = new AssetId('a', toMetaExtension('lib/baz.dart')); + updateReader(); }); @@ -69,9 +76,9 @@ void allTests() { fooNgMeta.ngDeps.exports.add(new ExportModel()..uri = 'bar.dart'); updateReader(); - var extracted = await _testLink(reader, fooAssetId); - expect(extracted.identifiers, contains('FooComponent')); - expect(extracted.identifiers, contains('BarComponent')); + var extracted = await _testLink(reader, fooAssetId, fooMetaAssetId); + expect(extracted.identifiers['FooComponent'], isNotNull); + expect(extracted.identifiers['BarComponent'], isNotNull); expect(extracted.identifiers['FooComponent'].selector, equals('foo')); expect(extracted.identifiers['BarComponent'].selector, equals('bar')); @@ -83,68 +90,527 @@ void allTests() { barNgMeta.ngDeps.exports.add(new ExportModel()..uri = 'baz.dart'); updateReader(); - var extracted = await _testLink(reader, fooAssetId); - expect(extracted.identifiers, contains('FooComponent')); - expect(extracted.identifiers, contains('BarComponent')); - expect(extracted.identifiers, contains('BazComponent')); + var extracted = await _testLink(reader, fooAssetId, fooMetaAssetId); + expect(extracted.identifiers['FooComponent'], isNotNull); + expect(extracted.identifiers['BarComponent'], isNotNull); + expect(extracted.identifiers['BazComponent'], isNotNull); expect(extracted.identifiers['FooComponent'].selector, equals('foo')); expect(extracted.identifiers['BarComponent'].selector, equals('bar')); expect(extracted.identifiers['BazComponent'].selector, equals('baz')); }); - test( - '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, contains('BarComponent')); - }); - - test( - '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, isNot(contains('BarComponent'))); - expect(extracted.identifiers, isNot(contains('BazComponent'))); - }); - - test('should handle `DirectiveMetadata` export cycles gracefully.', - () async { + test('should handle `DirectiveMetadata` export cycles gracefully.', () async { fooNgMeta.ngDeps.exports.add(new ExportModel()..uri = 'bar.dart'); barNgMeta.ngDeps.exports.add(new ExportModel()..uri = 'baz.dart'); bazNgMeta.ngDeps.exports.add(new ExportModel()..uri = 'foo.dart'); updateReader(); - var extracted = await _testLink(reader, bazAssetId); - expect(extracted.identifiers, contains('FooComponent')); - expect(extracted.identifiers, contains('BarComponent')); - expect(extracted.identifiers, contains('BazComponent')); + var extracted = await _testLink(reader, bazAssetId, bazMetaAssetId); + expect(extracted.identifiers['FooComponent'], isNotNull); + expect(extracted.identifiers['BarComponent'], isNotNull); + expect(extracted.identifiers['BazComponent'], isNotNull); }); - test( - 'should include `DirectiveMetadata` from exported files ' - 'expressed as absolute uris', () async { + test('should include `DirectiveMetadata` from exported files ' + 'expressed as absolute uris', () async { fooNgMeta.ngDeps.exports .add(new ExportModel()..uri = 'package:bar/bar.dart'); updateReader(); - reader.addAsset(new AssetId('bar', toSummaryExtension('lib/bar.dart')), + reader.addAsset(new AssetId('bar', toMetaExtension('lib/bar.dart')), JSON.encode(barNgMeta.toJson())); - var extracted = await _testLink(reader, fooAssetId); + var extracted = await _testLink(reader, fooAssetId, fooMetaAssetId); - expect(extracted.identifiers, contains('FooComponent')); - expect(extracted.identifiers, contains('BarComponent')); + expect(extracted.identifiers['FooComponent'], isNotNull); + expect(extracted.identifiers['BarComponent'], isNotNull); expect(extracted.identifiers['FooComponent'].selector, equals('foo')); expect(extracted.identifiers['BarComponent'].selector, equals('bar')); }); + + test('should resolve queries from types.', () async { + barNgMeta.identifiers['Service'] = + new CompileTypeMetadata(name: 'Service', moduleUrl: 'moduleUrl'); + + fooComponentMeta.queries = [ + new CompileQueryMetadata(selectors: [new CompileIdentifierMetadata(name: 'Service')]), + new CompileQueryMetadata(selectors: ['one']) + ]; + + fooComponentMeta.viewQueries = [ + new CompileQueryMetadata(selectors: [new CompileIdentifierMetadata(name: 'Service')]), + new CompileQueryMetadata(selectors: ['one']) + ]; + + fooNgMeta.ngDeps.imports.add(new ImportModel()..uri = 'bar.dart'); + + updateReader(); + + final extracted = await _testLink(reader, fooAssetId, fooMetaAssetId); + final cmp = extracted.identifiers["FooComponent"]; + + expect(cmp.queries.length, equals(2)); + expect(cmp.viewQueries.length, equals(2)); + + expect(cmp.queries[0].selectors[0].name, equals("Service")); + expect(cmp.queries[0].selectors[0].moduleUrl, equals("moduleUrl")); + expect(cmp.queries[1].selectors[0], equals("one")); + + expect(cmp.viewQueries[0].selectors[0].name, equals("Service")); + expect(cmp.viewQueries[0].selectors[0].moduleUrl, equals("moduleUrl")); + expect(cmp.viewQueries[1].selectors[0], equals("one")); + }); + + test('should resolve providers from types.', () async { + barNgMeta.identifiers['Service'] = + new CompileTypeMetadata(name: 'Service', moduleUrl: 'moduleUrl'); + + fooComponentMeta.providers = [ + new CompileIdentifierMetadata(name: 'Service') + ]; + fooComponentMeta.type.diDeps = [ + new CompileDiDependencyMetadata( + token: new CompileIdentifierMetadata(name: 'Service')) + ]; + + fooNgMeta.ngDeps.imports.add(new ImportModel()..uri = 'bar.dart'); + + updateReader(); + + final extracted = await _testLink(reader, fooAssetId, fooMetaAssetId); + final cmp = extracted.identifiers["FooComponent"]; + + expect(cmp.providers.length, equals(1)); + + expect(cmp.providers[0].token.name, equals("Service")); + expect(cmp.providers[0].token.moduleUrl, equals("moduleUrl")); + expect(cmp.providers[0].useClass.name, equals("Service")); + expect(cmp.providers[0].useClass.moduleUrl, equals("moduleUrl")); + + expect(cmp.type.diDeps.length, equals(1)); + expect(cmp.type.diDeps[0].token.name, equals("Service")); + expect(cmp.type.diDeps[0].token.moduleUrl, equals("moduleUrl")); + }); + + test('should resolve providers from functions.', () async { + barNgMeta.identifiers['Service'] = + new CompileTypeMetadata(name: 'Service', moduleUrl: 'moduleUrl'); + + fooNgMeta.identifiers['factory'] = + new CompileFactoryMetadata(name: 'factory', moduleUrl: 'moduleUrl', diDeps: [ + new CompileDiDependencyMetadata( + token: new CompileIdentifierMetadata(name: 'Service')) + ]); + + fooComponentMeta.providers = [ + new CompileProviderMetadata(token: 'someFunc', useFactory: + new CompileFactoryMetadata(name: 'factory')) + ]; + + fooNgMeta.ngDeps.imports.add(new ImportModel()..uri = 'bar.dart'); + + updateReader(); + + final extracted = await _testLink(reader, fooAssetId, fooMetaAssetId); + final cmp = extracted.identifiers["FooComponent"]; + + expect(cmp.providers.length, equals(1)); + + expect(cmp.providers[0].token, equals("someFunc")); + expect(cmp.providers[0].useFactory.name, equals("factory")); + expect(cmp.providers[0].useFactory.moduleUrl, equals("moduleUrl")); + + expect(cmp.providers[0].useFactory.diDeps[0].token.name, equals("Service")); + expect(cmp.providers[0].useFactory.diDeps[0].token.moduleUrl, equals("moduleUrl")); + }); + + test('should resolve viewProviders from types.', () async { + barNgMeta.identifiers['Service'] = + new CompileTypeMetadata(name: 'Service', moduleUrl: 'moduleUrl'); + + fooComponentMeta.viewProviders = [ + new CompileProviderMetadata( + token: new CompileIdentifierMetadata(name: 'Service')) + ]; + fooNgMeta.ngDeps.imports.add(new ImportModel()..uri = 'bar.dart'); + + updateReader(); + + final extracted = await _testLink(reader, fooAssetId, fooMetaAssetId); + final cmp = extracted.identifiers["FooComponent"]; + + expect(cmp.viewProviders.length, equals(1)); + expect(cmp.viewProviders[0].token.name, equals("Service")); + expect(cmp.viewProviders[0].token.moduleUrl, equals("moduleUrl")); + }); + + test('should resolve 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") + ]; + + fooNgMeta.ngDeps.imports + .add(new ImportModel()..uri = 'package:a/bar.dart'); + + updateReader(); + + final extracted = await _testLink(reader, fooAssetId, fooMetaAssetId); + final cmp = extracted.identifiers["FooComponent"]; + + expect(cmp.providers.length, equals(1)); + + expect(cmp.providers[0].token, equals("StrService")); + expect(cmp.providers[0].useClass.name, equals("Service")); + expect(cmp.providers[0].useClass.moduleUrl, equals("moduleUrl")); + + expect(cmp.type.diDeps.length, equals(1)); + expect(cmp.type.diDeps[0].token, equals("StrService")); + }); + + test('should resolve providers from references', () async { + barNgMeta.identifiers['Service'] = + new CompileTypeMetadata(name: 'Service', moduleUrl: 'moduleUrl'); + + fooNgMeta.identifiers["PROVIDER"] = new CompileIdentifierMetadata( + name: 'PROVIDER', + value: new CompileProviderMetadata( + token: new CompileIdentifierMetadata(name: 'Service'))); + + fooComponentMeta.providers = [ + new CompileIdentifierMetadata(name: 'PROVIDER') + ]; + fooNgMeta.ngDeps.imports + .add(new ImportModel()..uri = 'package:a/bar.dart'); + + updateReader(); + + final extracted = await _testLink(reader, fooAssetId, fooMetaAssetId); + final cmp = extracted.identifiers["FooComponent"]; + + expect(cmp.providers.length, equals(1)); + expect(cmp.providers[0].token.name, equals("Service")); + expect(cmp.providers[0].token.moduleUrl, equals("moduleUrl")); + }); + + test('should resolve providers from lists.', () async { + barNgMeta.identifiers['Service'] = + new CompileTypeMetadata(name: 'Service', moduleUrl: 'moduleUrl'); + + fooNgMeta.identifiers["PROVIDERS"] = new CompileIdentifierMetadata(name: 'PROVIDERS', + value: [ + new CompileIdentifierMetadata(name: "Service") + ]); + + fooComponentMeta.providers = [ + new CompileIdentifierMetadata(name: 'PROVIDERS') + ]; + fooNgMeta.ngDeps.imports + .add(new ImportModel()..uri = 'package:a/bar.dart'); + + updateReader(); + + final extracted = await _testLink(reader, fooAssetId, fooMetaAssetId); + final cmp = extracted.identifiers["FooComponent"]; + + expect(cmp.providers.length, equals(1)); + expect(cmp.providers[0].token.name, equals("Service")); + expect(cmp.providers[0].token.moduleUrl, equals("moduleUrl")); + }); + + test('should resolve providers from lists (two lists in the same file).', () async { + barNgMeta.identifiers['Service'] = + new CompileTypeMetadata(name: 'Service', moduleUrl: 'moduleUrl'); + + + fooNgMeta.identifiers["OUTER_PROVIDERS"] = new CompileIdentifierMetadata(name: 'PROVIDERS', + value: [ + new CompileIdentifierMetadata(name: "INNER_PROVIDERS") + ]); + + fooNgMeta.identifiers["INNER_PROVIDERS"] = new CompileIdentifierMetadata(name: 'PROVIDERS', + value: [ + new CompileIdentifierMetadata(name: "Service") + ]); + + fooComponentMeta.providers = [ + new CompileIdentifierMetadata(name: 'OUTER_PROVIDERS') + ]; + fooNgMeta.ngDeps.imports + .add(new ImportModel()..uri = 'package:a/bar.dart'); + + updateReader(); + + final extracted = await _testLink(reader, fooAssetId, fooMetaAssetId); + final cmp = extracted.identifiers["FooComponent"]; + + expect(cmp.providers.length, equals(1)); + expect(cmp.providers[0].token.name, equals("Service")); + expect(cmp.providers[0].token.moduleUrl, equals("moduleUrl")); + }); + + test('should resolve providers when there is a level of indirection.', () async { + bazNgMeta.identifiers['Service'] = + new CompileTypeMetadata(name: 'Service', moduleUrl: 'moduleUrl'); + + barNgMeta.identifiers['BAR_PROVIDERS'] = new CompileIdentifierMetadata(name: 'BAR_PROVIDERS', + moduleUrl: 'moduleUrl', + value: [ + new CompileIdentifierMetadata(name: "Service") + ]); + barNgMeta.ngDeps.imports + .add(new ImportModel()..uri = 'package:a/baz.dart'); + + + fooNgMeta.identifiers["PROVIDERS"] = new CompileIdentifierMetadata(name: 'PROVIDERS', moduleUrl: 'moduleUrl', + value: new CompileIdentifierMetadata(name: 'BAR_PROVIDERS')); + fooNgMeta.ngDeps.imports + .add(new ImportModel()..uri = 'package:a/bar.dart'); + fooComponentMeta.providers = [new CompileIdentifierMetadata(name: 'PROVIDERS')]; + + reader.clear(); + reader + ..addAsset(fooAssetId, JSON.encode(fooNgMeta.toJson())) + ..addAsset(barAssetId, JSON.encode(barNgMeta.toJson())) + ..addAsset(bazAssetId, JSON.encode(bazNgMeta.toJson())); + + final extracted = await _testLink(reader, fooAssetId, fooMetaAssetId); + final cmp = extracted.identifiers["FooComponent"]; + + expect(cmp.providers.length, equals(1)); + expect(cmp.providers[0].token.name, equals("Service")); + expect(cmp.providers[0].token.moduleUrl, equals("moduleUrl")); + }); + + test('should generate generate diDeps of injectable services.', () async { + fooNgMeta.identifiers['Service2'] = + new CompileTypeMetadata(name: 'Service2', moduleUrl: 'moduleUrl'); + + fooNgMeta.identifiers['Service'] = new CompileTypeMetadata( + name: 'Service', + moduleUrl: 'moduleUrl', + diDeps: [ + new CompileDiDependencyMetadata( + token: new CompileIdentifierMetadata(name: 'Service2')) + ]); + + fooComponentMeta.providers = [ + new CompileProviderMetadata( + token: new CompileIdentifierMetadata(name: 'Service'), + useClass: new CompileTypeMetadata(name: 'Service')) + ]; + + updateReader(); + + final extracted = await _testLink(reader, fooAssetId, fooMetaAssetId); + final cmp = extracted.identifiers["FooComponent"]; + + expect(cmp.providers.length, equals(1)); + + expect(cmp.providers[0].useClass.name, equals("Service")); + expect(cmp.providers[0].useClass.moduleUrl, equals("moduleUrl")); + expect(cmp.providers[0].useClass.diDeps.first.token.name, equals("Service2")); + expect(cmp.providers[0].useClass.diDeps.first.token.moduleUrl, equals("moduleUrl")); + }); + + test('should resolve queries and viewQueries.', () async { + barNgMeta.identifiers['Service'] = + new CompileTypeMetadata(name: 'Service', moduleUrl: 'moduleUrl'); + + fooComponentMeta.template = + new CompileTemplateMetadata(template: "import 'bar.dart';"); + fooComponentMeta.type.diDeps = [ + new CompileDiDependencyMetadata( + token: 'someToken', + query: new CompileQueryMetadata( + selectors: [new CompileIdentifierMetadata(name: 'Service')])), + new CompileDiDependencyMetadata( + token: 'someToken', + viewQuery: new CompileQueryMetadata( + selectors: [new CompileIdentifierMetadata(name: 'Service')])) + ]; + + fooNgMeta.ngDeps.imports + .add(new ImportModel()..uri = 'package:a/bar.dart'); + + updateReader(); + + final extracted = await _testLink(reader, fooAssetId, fooMetaAssetId); + final cmp = extracted.identifiers["FooComponent"]; + + expect(cmp.type.diDeps.length, equals(2)); + + expect(cmp.type.diDeps[0].query.selectors[0].name, equals("Service")); + expect(cmp.type.diDeps[0].query.selectors[0].moduleUrl, equals("moduleUrl")); + expect(cmp.type.diDeps[1].viewQuery.selectors[0].name, equals("Service")); + expect(cmp.type.diDeps[1].viewQuery.selectors[0].moduleUrl, equals("moduleUrl")); + }); + + test('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'); + barNgMeta.identifiers['factory'] = + new CompileFactoryMetadata(name: 'factory', moduleUrl: 'moduleUrl', diDeps: [ + new CompileDiDependencyMetadata( + token: new CompileIdentifierMetadata(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')), + new CompileProviderMetadata( + token: new CompileIdentifierMetadata(name: 'Service1'), + useExisting: new CompileIdentifierMetadata(name: 'Service2')), + new CompileProviderMetadata( + token: new CompileIdentifierMetadata(name: 'Service1'), + useValue: new CompileIdentifierMetadata(name: 'Service2')), + new CompileProviderMetadata( + token: new CompileIdentifierMetadata(name: 'Service1'), + useFactory: new CompileFactoryMetadata(name: 'factory')) + ]; + + fooNgMeta.ngDeps.imports + .add(new ImportModel()..uri = 'package:a/bar.dart'); + + updateReader(); + + final extracted = await _testLink(reader, fooAssetId, fooMetaAssetId); + final cmp = extracted.identifiers["FooComponent"]; + + expect(cmp.providers.length, equals(4)); + + expect(cmp.providers[0].token.name, equals("Service1")); + expect(cmp.providers[0].token.moduleUrl, equals("moduleUrl")); + expect(cmp.providers[0].useClass.name, equals("Service2")); + expect(cmp.providers[0].useClass.moduleUrl, equals("moduleUrl")); + + expect(cmp.providers[1].token.name, equals("Service1")); + expect(cmp.providers[1].token.moduleUrl, equals("moduleUrl")); + expect(cmp.providers[1].useExisting.name, equals("Service2")); + expect(cmp.providers[1].useExisting.moduleUrl, equals("moduleUrl")); + + expect(cmp.providers[2].token.name, equals("Service1")); + expect(cmp.providers[2].token.moduleUrl, equals("moduleUrl")); + expect(cmp.providers[2].useValue.name, equals("Service2")); + expect(cmp.providers[2].useValue.moduleUrl, equals("moduleUrl")); + + expect(cmp.providers[3].token.name, equals("Service1")); + expect(cmp.providers[3].token.moduleUrl, equals("moduleUrl")); + expect(cmp.providers[3].useFactory.name, equals("factory")); + expect(cmp.providers[3].useFactory.moduleUrl, equals("moduleUrl")); + expect(cmp.providers[3].useFactory.diDeps[0].token.name, equals("Service2")); + expect(cmp.providers[3].useFactory.diDeps[0].token.moduleUrl, equals("moduleUrl")); + }); + + test('should fallback to the list of resolved identifiers.', () async { + fooNgMeta.identifiers['Service2'] = + new CompileTypeMetadata(name: 'Service2', moduleUrl: 'moduleUrl'); + + fooComponentMeta.providers = [ + new CompileProviderMetadata( + token: new CompileIdentifierMetadata(name: 'Service1'), + useClass: new CompileTypeMetadata(name: 'Service2')) + ]; + + updateReader(); + + final extracted = await _testLink(reader, fooAssetId, fooMetaAssetId, + {"Service1": "someModuleUrl", "Service2": "someModuleUrl"}); + final cmp = extracted.identifiers["FooComponent"]; + + expect(cmp.providers.length, equals(1)); + + expect(cmp.providers[0].token.name, equals("Service1")); + expect(cmp.providers[0].token.moduleUrl, equals("someModuleUrl")); + expect(cmp.providers[0].useClass.name, equals("Service2")); + expect(cmp.providers[0].useClass.moduleUrl, equals("moduleUrl")); + }); + + test('should resolve circular references.', () async { + barNgMeta.identifiers['Service1'] = + new CompileTypeMetadata(name: 'Service1', moduleUrl: 'moduleUrl', + diDeps: [new CompileDiDependencyMetadata(token: new CompileIdentifierMetadata(name: "Service2"))]); + barNgMeta.ngDeps.imports.add(new ImportModel()..uri = 'foo.dart'); + + fooNgMeta.identifiers['Service2'] = + new CompileTypeMetadata(name: 'Service2', moduleUrl: 'moduleUrl', + diDeps: [new CompileDiDependencyMetadata(token: new CompileIdentifierMetadata(name: "Service1"))]); + fooNgMeta.ngDeps.imports.add(new ImportModel()..uri = 'bar.dart'); + + updateReader(); + + final extracted = await _testLink(reader, fooAssetId, fooMetaAssetId); + final service2 = extracted.identifiers["Service2"]; + + expect(service2.diDeps[0].token.name, equals("Service1")); + expect(service2.diDeps[0].token.moduleUrl, equals("moduleUrl")); + }); + + test('should link dependencies (imports and exports first).', () async { + bazNgMeta.identifiers['Service2'] = + new CompileTypeMetadata(name: 'Service2', moduleUrl: 'moduleUrl'); + + barNgMeta.identifiers['Service1'] = new CompileTypeMetadata( + name: 'Service1', + moduleUrl: 'moduleUrl', + diDeps: [ + new CompileDiDependencyMetadata( + token: new CompileIdentifierMetadata(name: 'Service2')) + ]); + barNgMeta.ngDeps..imports.add(new ImportModel()..uri = 'baz.dart'); + + fooComponentMeta.providers = [ + new CompileProviderMetadata( + token: new CompileIdentifierMetadata(name: 'Service1')) + ]; + fooNgMeta.ngDeps..imports.add(new ImportModel()..uri = 'bar.dart'); + + reader.clear(); + reader + ..addAsset(fooAssetId, JSON.encode(fooNgMeta.toJson())) + ..addAsset(barAssetId, JSON.encode(barNgMeta.toJson())) + ..addAsset(bazAssetId, JSON.encode(bazNgMeta.toJson())); + + final extracted = await _testLink(reader, fooAssetId, fooMetaAssetId); + + final cmp = extracted.identifiers["FooComponent"]; + + expect(cmp.providers.length, equals(1)); + final firstProvider = cmp.providers[0]; + + expect(firstProvider.token.diDeps[0].token.name, equals("Service2")); + expect(firstProvider.token.diDeps[0].token.moduleUrl, equals("moduleUrl")); + }); + + test('should not resolve when not needed', () async { + fooNgMeta.identifiers['SomeId'] = new CompileIdentifierMetadata(name: 'SomeId'); + fooNgMeta.ngDeps..imports.add(new ImportModel()..uri = 'bar.dart'); + + fooNgMeta.identifiers.clear(); + reader.clear(); + // there is no bar, so it should throw when trying to resolve + reader + ..addAsset(fooAssetId, JSON.encode(fooNgMeta.toJson())) + ..addAsset(barAssetId, "Invalid"); + + await _testLink(reader, fooAssetId, fooMetaAssetId); + }); }); group('NgDeps linker', () { @@ -157,7 +623,7 @@ void allTests() { barNgMeta.ngDeps.libraryUri = 'test.bar'; updateReader(); - var linked = (await _testLink(reader, fooAssetId)).ngDeps; + var linked = (await _testLink(reader, fooAssetId, fooMetaAssetId)).ngDeps; expect(linked, isNotNull); var linkedImport = linked.depImports .firstWhere((i) => i.uri.endsWith('bar.template.dart')); @@ -172,7 +638,7 @@ void allTests() { barNgMeta.ngDeps.libraryUri = 'test.bar'; updateReader(); - var linked = (await _testLink(reader, fooAssetId)).ngDeps; + var linked = (await _testLink(reader, fooAssetId, fooMetaAssetId)).ngDeps; expect(linked, isNotNull); var linkedImport = linked.depImports .firstWhere((i) => i.uri.endsWith('bar.template.dart')); @@ -190,7 +656,7 @@ void allTests() { barNgMeta.ngDeps.libraryUri = 'test.bar'; updateReader(); - var linked = (await _testLink(reader, fooAssetId)).ngDeps; + var linked = (await _testLink(reader, fooAssetId, fooMetaAssetId)).ngDeps; expect(linked, isNotNull); var linkedImport = linked.depImports.firstWhere( (i) => i.uri.endsWith('bar.template.dart'), @@ -200,7 +666,11 @@ void allTests() { }); } -Future _testLink(AssetReader reader, AssetId assetId) { - return zone.exec(() => linkDirectiveMetadata(reader, assetId), +Future _testLink( + AssetReader reader, AssetId summaryAssetId, AssetId metaAssetId, + [Map resolvedIdentifiers]) { + return zone.exec( + () => linkDirectiveMetadata( + reader, summaryAssetId, metaAssetId, resolvedIdentifiers), log: new RecordingLogger()); } diff --git a/modules_dart/transform/test/transform/directive_processor/abstract_classes/classes.dart b/modules_dart/transform/test/transform/directive_processor/abstract_classes/classes.dart index 71f61b5055..67fdf8ecaf 100644 --- a/modules_dart/transform/test/transform/directive_processor/abstract_classes/classes.dart +++ b/modules_dart/transform/test/transform/directive_processor/abstract_classes/classes.dart @@ -4,7 +4,7 @@ import 'package:angular2/angular2.dart' show Injectable; @Injectable() abstract class Service { - factory Service(){ + factory Service() { return null; } -} \ No newline at end of file +} 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 62a80b49d6..ab78a42714 100644 --- a/modules_dart/transform/test/transform/directive_processor/all_tests.dart +++ b/modules_dart/transform/test/transform/directive_processor/all_tests.dart @@ -7,7 +7,10 @@ import 'package:dart_style/dart_style.dart'; import 'package:test/test.dart'; import 'package:angular2/src/compiler/directive_metadata.dart' - show CompileIdentifierMetadata; + show + CompileIdentifierMetadata, + CompileProviderMetadata, + CompileTypeMetadata; 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; @@ -41,7 +44,14 @@ void _expectSelector(ReflectionInfoModel model, Matcher matcher) { return expect(selectorArg.value, matcher); } +var oldTest = test; void allTests() { + var test = (name, fn) { +// if (name.contains('CompileFactoryMetadata')) { + oldTest(name, fn); +// } + }; + test('should preserve parameter annotations.', () async { var model = (await _testCreateModel('parameter_metadata/soup.dart')).ngDeps; expect(model.reflectables.length, equals(1)); @@ -422,6 +432,7 @@ void allTests() { expect(ngMeta.identifiers.isNotEmpty, isTrue); expect(ngMeta.identifiers['MultiSoupComponent'], isNotNull); expect( + ngMeta.identifiers['MultiSoupComponent'].selector, equals('[soup]')); final hooks = ngMeta.identifiers['MultiSoupComponent'].lifecycleHooks; expect(hooks, contains(LifecycleHooks.OnChanges)); @@ -467,7 +478,7 @@ void allTests() { }); group("identifiers", () { - test("should populate `identifier` with class types.", () async { + test("should populate `identifier` with CompileTypeMetadata.", () async { var model = (await _testCreateModel('identifiers/classes.dart')); final moduleUrl = "asset:angular2/test/transform/directive_processor/identifiers/classes.dart"; @@ -475,6 +486,24 @@ void allTests() { expect(model.identifiers['Service1'].moduleUrl, equals(moduleUrl)); expect(model.identifiers['Service2'].name, equals('Service2')); expect(model.identifiers['Service2'].moduleUrl, equals(moduleUrl)); + + expect(model.identifiers['Service2'].diDeps.length, equals(1)); + expect(model.identifiers['Service2'].diDeps[0].token.name, equals('Service1')); + }); + + test("should populate `identifier` with CompileFactoryMetadata.", () async { + var model = (await _testCreateModel('identifiers/factories' + '.dart')); + final moduleUrl = + "asset:angular2/test/transform/directive_processor/identifiers/factories.dart"; + + expect(model.identifiers['factory1'].name, equals('factory1')); + expect(model.identifiers['factory1'].moduleUrl, equals(moduleUrl)); + expect(model.identifiers['factory2'].name, equals('factory2')); + expect(model.identifiers['factory2'].moduleUrl, equals(moduleUrl)); + + expect(model.identifiers['factory2'].diDeps.length, equals(1)); + expect(model.identifiers['factory2'].diDeps[0].token.name, equals('SomeClass')); }); test("should populate `identifier` with constants.", () async { @@ -492,6 +521,32 @@ void allTests() { expect(model.identifiers['c'], isNull); }); + test("should populate `identifier` with provider constants.", () async { + var model = + (await _testCreateModel('identifiers/provider_constants.dart')); + expect( + model.identifiers['a'].value.toJson(), + equals(new CompileProviderMetadata( + token: 'someToken', + useClass: new CompileTypeMetadata(name: 'SomeClass')) + .toJson())); + }); + + test("should populate `identifier` with lists of providers.", () async { + var model = + (await _testCreateModel('identifiers/provider_constants.dart')); + + final List list = model.identifiers['b'].value; + + expect(list.length, equals(3)); + expect(list[0].name, equals("SomeClass")); + expect(list[1].name, equals("a")); + expect(list[2].toJson(), equals(new CompileProviderMetadata( + token: 'someOtherToken', + useClass: new CompileTypeMetadata(name: 'SomeClass')) + .toJson())); + }); + test( "should populate `identifier` with class names that do not have @Injectable;'.", () async { @@ -642,17 +697,25 @@ void allTests() { expect(cmp, isNotNull); var deps = cmp.type.diDeps; expect(deps, isNotNull); - expect(deps.length, equals(8)); + expect(deps.length, equals(13)); expect(deps[0].token.name, equals("ServiceDep")); expect(deps[1].token.name, equals("ServiceDep")); + expect(deps[2].token, "one"); expect(deps[2].isAttribute, isTrue); expect(deps[3].isSelf, isTrue); expect(deps[4].isSkipSelf, isTrue); expect(deps[5].isOptional, isTrue); expect(deps[6].query.selectors[0].name, equals("ServiceDep")); expect(deps[6].query.descendants, isTrue); - expect(deps[7].viewQuery.selectors[0], equals("one")); - expect(deps[7].viewQuery.selectors[1], equals("two")); + expect(deps[7].query.selectors[0].name, equals("ServiceDep")); + expect(deps[7].query.descendants, isTrue); + expect(deps[8].viewQuery.selectors[0], equals("one")); + expect(deps[8].viewQuery.selectors[1], equals("two")); + expect(deps[9].viewQuery.selectors[0], equals("one")); + expect(deps[9].viewQuery.selectors[1], equals("two")); + expect(deps[10].token.name, equals("ServiceDep")); + expect(deps[11].token.name, equals("ServiceDep")); + expect(deps[12].token.name, equals("ServiceDep")); }); test('should populate `diDependency` using a string token.', () async { @@ -686,15 +749,54 @@ void allTests() { expect(cmp.providers, isNotNull); expect(cmp.providers.length, equals(2)); - var firstToken = cmp.providers.first.token; + var firstToken = cmp.providers.first; expect(firstToken.prefix, isNull); expect(firstToken.name, equals("ServiceDep")); - var secondToken = cmp.providers[1].token; + var secondToken = cmp.providers[1]; expect(secondToken.prefix, equals("dep2")); expect(secondToken.name, equals("ServiceDep")); }); + test('should populate `viewProviders` using types.', () async { + var cmp = (await _testCreateModel('directives_files/components.dart')) + .identifiers['ComponentWithViewProvidersTypes']; + + expect(cmp, isNotNull); + expect(cmp.viewProviders, isNotNull); + expect(cmp.viewProviders.length, equals(1)); + + var firstToken = cmp.viewProviders.first; + expect(firstToken.prefix, isNull); + expect(firstToken.name, equals("ServiceDep")); + }); + + test('should populate `bindings` using types.', () async { + var cmp = (await _testCreateModel('directives_files/components.dart')) + .identifiers['ComponentWithBindingsTypes']; + + expect(cmp, isNotNull); + expect(cmp.providers, isNotNull); + expect(cmp.providers.length, equals(1)); + + var firstToken = cmp.providers.first; + expect(firstToken.prefix, isNull); + expect(firstToken.name, equals("ServiceDep")); + }); + + test('should populate `viewBindings` using types.', () async { + var cmp = (await _testCreateModel('directives_files/components.dart')) + .identifiers['ComponentWithViewBindingsTypes']; + + expect(cmp, isNotNull); + expect(cmp.viewProviders, isNotNull); + expect(cmp.viewProviders.length, equals(1)); + + var firstToken = cmp.viewProviders.first; + expect(firstToken.prefix, isNull); + expect(firstToken.name, equals("ServiceDep")); + }); + test('should populate `providers` using useClass.', () async { var cmp = (await _testCreateModel('directives_files/components.dart')) .identifiers['ComponentWithProvidersUseClass']; @@ -724,6 +826,191 @@ void allTests() { expect(token, equals("StringDep")); }); + test('should populate `providers` using toClass.', () async { + var cmp = (await _testCreateModel('directives_files/components.dart')) + .identifiers['ComponentWithProvidersToClass']; + + expect(cmp, isNotNull); + expect(cmp.providers, isNotNull); + expect(cmp.providers.length, equals(1)); + + var token = cmp.providers.first.token; + var useExisting = cmp.providers.first.useClass; + + expect(useExisting.prefix, isNull); + expect(useExisting.name, equals("ServiceDep")); + }); + + test('should populate `providers` using useExisting.', () async { + var cmp = (await _testCreateModel('directives_files/components.dart')) + .identifiers['ComponentWithProvidersUseExisting']; + + expect(cmp, isNotNull); + expect(cmp.providers, isNotNull); + expect(cmp.providers.length, equals(1)); + + var token = cmp.providers.first.token; + var useExisting = cmp.providers.first.useExisting; + + expect(useExisting.prefix, isNull); + expect(useExisting.name, equals("ServiceDep")); + }); + + test('should populate `providers` using toAlias.', () async { + var cmp = (await _testCreateModel('directives_files/components.dart')) + .identifiers['ComponentWithProvidersToAlias']; + + expect(cmp, isNotNull); + expect(cmp.providers, isNotNull); + expect(cmp.providers.length, equals(1)); + + var token = cmp.providers.first.token; + var useExisting = cmp.providers.first.useExisting; + + expect(useExisting.prefix, isNull); + expect(useExisting.name, equals("ServiceDep")); + }); + + test('should populate `providers` using useExisting (string token).', + () async { + var cmp = (await _testCreateModel('directives_files/components.dart')) + .identifiers['ComponentWithProvidersUseExistingStr']; + + expect(cmp, isNotNull); + expect(cmp.providers, isNotNull); + expect(cmp.providers.length, equals(1)); + + var token = cmp.providers.first.token; + var useExisting = cmp.providers.first.useExisting; + + expect(useExisting, equals("StrToken")); + }); + + test('should populate `providers` using useValue.', () async { + var cmp = (await _testCreateModel('directives_files/components.dart')) + .identifiers['ComponentWithProvidersUseValue']; + + expect(cmp, isNotNull); + expect(cmp.providers, isNotNull); + expect(cmp.providers.length, equals(1)); + + var token = cmp.providers.first.token; + var useValue = cmp.providers.first.useValue; + + expect(useValue.prefix, isNull); + expect(useValue.name, equals("ServiceDep")); + }); + + test('should populate `providers` using toValue.', () async { + var cmp = (await _testCreateModel('directives_files/components.dart')) + .identifiers['ComponentWithProvidersToValue']; + + expect(cmp, isNotNull); + expect(cmp.providers, isNotNull); + expect(cmp.providers.length, equals(1)); + + var token = cmp.providers.first.token; + var useValue = cmp.providers.first.useValue; + + expect(useValue.prefix, isNull); + expect(useValue.name, equals("ServiceDep")); + }); + + test('should populate `providers` using useValue (string token).', + () async { + var cmp = (await _testCreateModel('directives_files/components.dart')) + .identifiers['ComponentWithProvidersUseValueStr']; + + expect(cmp, isNotNull); + expect(cmp.providers, isNotNull); + expect(cmp.providers.length, equals(1)); + + var token = cmp.providers.first.token; + var useValue = cmp.providers.first.useValue; + + expect(useValue, equals("StrToken")); + }); + + test('should populate `providers` using useValue (num token).', () async { + var cmp = (await _testCreateModel('directives_files/components.dart')) + .identifiers['ComponentWithProvidersUseValueNum']; + + expect(cmp, isNotNull); + expect(cmp.providers, isNotNull); + expect(cmp.providers.length, equals(1)); + + var token = cmp.providers.first.token; + var useValue = cmp.providers.first.useValue; + + expect(useValue, equals(42)); + }); + + test('should populate `providers` using useValue (bool token).', () async { + var cmp = (await _testCreateModel('directives_files/components.dart')) + .identifiers['ComponentWithProvidersUseValueBool']; + + expect(cmp, isNotNull); + expect(cmp.providers, isNotNull); + expect(cmp.providers.length, equals(1)); + + var token = cmp.providers.first.token; + var useValue = cmp.providers.first.useValue; + + expect(useValue, equals(true)); + }); + + test('should populate `providers` using useValue (null token).', () async { + var cmp = (await _testCreateModel('directives_files/components.dart')) + .identifiers['ComponentWithProvidersUseValueNull']; + + expect(cmp, isNotNull); + expect(cmp.providers, isNotNull); + expect(cmp.providers.length, equals(1)); + + var token = cmp.providers.first.token; + var useValue = cmp.providers.first.useValue; + + expect(useValue, isNull); + }); + + test('should populate `providers` using useFactory.', () async { + var cmp = (await _testCreateModel('directives_files/components.dart')) + .identifiers['ComponentWithProvidersUseFactory']; + + expect(cmp, isNotNull); + expect(cmp.providers, isNotNull); + expect(cmp.providers.length, equals(1)); + + var useFactory = cmp.providers.first.useFactory; + var deps = cmp.providers.first.deps; + + expect(useFactory.prefix, isNull); + expect(useFactory.name, equals("funcDep")); + + expect(deps[0].token.name, equals("ServiceDep")); + expect(deps[1].token, equals("Str")); + expect(deps[2].token.name, equals("ServiceDep")); + expect(deps[3].token.name, equals("ServiceDep")); + expect(deps[3].isSelf, equals(true)); + expect(deps[4].token.name, equals("ServiceDep")); + expect(deps[4].isSkipSelf, equals(true)); + expect(deps[5].token.name, equals("ServiceDep")); + expect(deps[5].isOptional, equals(true)); + }); + + test('should populate `providers` using toFactory.', () async { + var cmp = (await _testCreateModel('directives_files/components.dart')) + .identifiers['ComponentWithProvidersToFactory']; + + expect(cmp, isNotNull); + expect(cmp.providers, isNotNull); + expect(cmp.providers.length, equals(1)); + + var useFactory = cmp.providers.first.useFactory; + expect(useFactory.prefix, isNull); + expect(useFactory.name, equals("funcDep")); + }); + test('should populate `providers` using a const token.', () async { var cmp = (await _testCreateModel('directives_files/components.dart')) .identifiers['ComponentWithProvidersConstToken']; @@ -737,6 +1024,36 @@ void allTests() { expect(token.constConstructor, isTrue); }); + test('should populate `queries`.', () async { + var cmp = (await _testCreateModel('directives_files/components.dart')) + .identifiers['ComponentWithQueries']; + + expect(cmp, isNotNull); + expect(cmp.queries, isNotNull); + expect(cmp.queries.length, equals(4)); + expect(cmp.queries[0].selectors, equals(["child"])); + expect(cmp.queries[0].first, isTrue); + expect(cmp.queries[1].selectors, equals(["child"])); + expect(cmp.queries[1].first, isFalse); + expect(cmp.queries[1].descendants, isTrue); + expect(cmp.queries[2].selectors, equals(["child"])); + expect(cmp.queries[2].first, isTrue); + expect(cmp.queries[3].selectors, equals(["child"])); + expect(cmp.queries[3].first, isFalse); + expect(cmp.queries[3].descendants, isTrue); + + expect(cmp.viewQueries, isNotNull); + expect(cmp.viewQueries.length, equals(4)); + expect(cmp.viewQueries[0].selectors, equals(["child"])); + expect(cmp.viewQueries[0].first, isTrue); + expect(cmp.viewQueries[1].selectors, equals(["child"])); + expect(cmp.viewQueries[1].first, isFalse); + expect(cmp.viewQueries[2].selectors, equals(["child"])); + expect(cmp.viewQueries[2].first, isTrue); + expect(cmp.viewQueries[3].selectors, equals(["child"])); + expect(cmp.viewQueries[3].first, isFalse); + }); + test('should merge `outputs` from the annotation and fields.', () async { var model = await _testCreateModel('directives_files/components.dart'); expect( diff --git a/modules_dart/transform/test/transform/directive_processor/directives_files/components.dart b/modules_dart/transform/test/transform/directive_processor/directives_files/components.dart index 92a39b51ce..b8a7f55738 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, Provider; + show Component, Directive, View, NgElement, Output, Input, Provider, ContentChild, ContentChildren, ViewChild, ViewChildren; import 'dep1.dart'; import 'dep2.dart' as dep2; @@ -91,6 +91,24 @@ class ComponentWithHostListeners { providers: [ServiceDep, dep2.ServiceDep]) class ComponentWithProvidersTypes {} +@Component( + selector: 'component-with-view-providers-types', + template: '', + viewProviders: [ServiceDep]) +class ComponentWithViewProvidersTypes {} + +@Component( + selector: 'component-with-bindings-types', + template: '', + bindings: [ServiceDep]) +class ComponentWithBindingsTypes {} + +@Component( + selector: 'component-with-view-bindings-types', + template: '', + viewBindings: [ServiceDep]) +class ComponentWithViewBindingsTypes {} + @Component( selector: 'component-with-providers-string-token', template: '', @@ -111,25 +129,135 @@ class ComponentWithProvidersConstToken { providers: [const Provider(ServiceDep, useClass: ServiceDep)]) class ComponentWithProvidersUseClass {} -@Component( - selector: 'component-with-di-deps', - template: '') +@Component(selector: 'component-with-di-deps', template: '') class ComponentWithDiDeps { + ServiceDep arg11; + ServiceDep arg13; + ComponentWithDiDeps( - ServiceDep arg1, - @Inject(ServiceDep) arg2, - @Attribute('one') arg3, - @Self() ServiceDep arg4, - @SkipSelf() ServiceDep arg5, - @Optional() ServiceDep arg6, - @Query(ServiceDep, descendants:true) arg6, - @ViewQuery("one,two") arg7 + ServiceDep arg1, + @Inject(ServiceDep) arg2, + @Attribute('one') arg3, + @Self() ServiceDep arg4, + @SkipSelf() ServiceDep arg5, + @Optional() ServiceDep arg6, + @Query(ServiceDep, descendants: true) arg7, + @ContentChildren(ServiceDep) arg8, + @ViewQuery("one,two") arg9, + @ViewChildren("one,two") arg10, + this.arg11, + [@Optional() ServiceDep arg12, + @Optional() this.arg13] ); } @Component( - selector: 'component-with-di-deps-string-token', - template: '') + selector: 'component-with-providers-use-class', + template: '', + providers: [const Provider(ServiceDep, useClass: ServiceDep)]) +class ComponentWithProvidersUseClass {} + +@Component( + selector: 'component-with-providers-to-class', + template: '', + providers: [const Binding(ServiceDep, toClass: ServiceDep)]) +class ComponentWithProvidersToClass {} + +@Component( + selector: 'component-with-providers-use-existing', + template: '', + providers: [const Provider(ServiceDep, useExisting: ServiceDep)]) +class ComponentWithProvidersUseExisting {} + +@Component( + selector: 'component-with-providers-to-alias', + template: '', + providers: [const Binding(ServiceDep, toAlias: ServiceDep)]) +class ComponentWithProvidersToAlias {} + +@Component( + selector: 'component-with-providers-use-existing-string', + template: '', + providers: [const Provider(ServiceDep, useExisting: 'StrToken')]) +class ComponentWithProvidersUseExistingStr {} + +@Component( + selector: 'component-with-providers-use-value', + template: '', + providers: [const Provider(ServiceDep, useValue: ServiceDep)]) +class ComponentWithProvidersUseValue {} + +@Component( + selector: 'component-with-providers-to-value', + template: '', + providers: [const Binding(ServiceDep, toValue: ServiceDep)]) +class ComponentWithProvidersToValue {} + +@Component( + selector: 'component-with-providers-use-value-string', + template: '', + providers: [const Provider(ServiceDep, useValue: 'StrToken')]) +class ComponentWithProvidersUseValueStr {} + +@Component( + selector: 'component-with-providers-use-value-num', + template: '', + providers: [const Provider(ServiceDep, useValue: 42)]) +class ComponentWithProvidersUseValueNum {} + +@Component( + selector: 'component-with-providers-use-value-bool', + template: '', + providers: [const Provider(ServiceDep, useValue: true)]) +class ComponentWithProvidersUseValueBool {} + +@Component( + selector: 'component-with-providers-use-value-null', + template: '', + providers: [const Provider(ServiceDep, useValue: null)]) +class ComponentWithProvidersUseValueNull {} + +@Component( + selector: 'component-with-providers-use-factory', + template: '', + providers: [ + const Provider(ServiceDep, useFactory: funcDep, deps: const [ + ServiceDep, + "Str", + [const Inject(ServiceDep)], + [ServiceDep, const Self()], + [ServiceDep, const SkipSelf()], + [ServiceDep, const Optional()] + ]) + ]) +class ComponentWithProvidersUseFactory {} + +@Component( + selector: 'component-with-providers-to-factory', + template: '', + providers: [const Binding(ServiceDep, toFactory: funcDep)]) +class ComponentWithProvidersToFactory {} + +@Component(selector: 'component-with-di-deps-string-token', template: '') class ComponentWithDiDepsStrToken { ComponentWithDiDepsStrToken(@Inject("StringDep") arg1); } + +@Component( + selector: 'component-with-queries', + template: '') +class ComponentWithQueries { + @ContentChild('child') var contentChild; + @ContentChildren('child', descendants: true) var contentChildren; + + @ViewChild('child') var viewChild; + @ViewChildren('child') var viewChildren; + + @ContentChild('child') set contentChildSetter(s){} + @ContentChildren('child', descendants: true) set contentChildrenSetter(s){} + + @ViewChild('child') set viewChildSetter(s){} + @ViewChildren('child') set viewChildrenSetter(s){} +} + +funcDep() {} 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 2c3bb38bb6..335558ca4b 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,7 @@ library angular2.test.transform.directive_processor.directive_files.dep1; -import 'package:angular2/angular2.dart' show Component, Directive, View, Pipe, Injectable; +import 'package:angular2/angular2.dart' + show Component, Directive, View, Pipe, Injectable; @Component(selector: 'dep1') @View(template: 'Dep1') @@ -12,4 +13,5 @@ class PipeDep {} @Injectable() class ServiceDep { const ServiceDep(); + static someFactory() {} } 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 cc1787d998..fc502a5d51 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,7 @@ library angular2.test.transform.directive_processor.directive_files.dep2; -import 'package:angular2/angular2.dart' show Component, Directive, View, Pipe, Injectable; +import 'package:angular2/angular2.dart' + show Component, Directive, View, Pipe, Injectable; @Component(selector: 'dep2') @View(template: 'Dep2') @@ -10,4 +11,4 @@ class Dep {} class PipeDep {} @Injectable() -class ServiceDep {} \ No newline at end of file +class ServiceDep {} 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 index 1aae91f5f1..8deaa140b1 100644 --- a/modules_dart/transform/test/transform/directive_processor/directives_files/services.dart +++ b/modules_dart/transform/test/transform/directive_processor/directives_files/services.dart @@ -1,10 +1,9 @@ library angular2.test.transform.directive_processor.directive_files.components; -import 'package:angular2/angular2.dart' - show Injectable, Inject; +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 index e9060fb6de..d99cd73c83 100644 --- a/modules_dart/transform/test/transform/directive_processor/identifiers/classes.dart +++ b/modules_dart/transform/test/transform/directive_processor/identifiers/classes.dart @@ -2,10 +2,9 @@ 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 +class Service2 { + Service2(Service1 service1) {} +} \ 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 index 5c7f531512..2f5b6f52c9 100644 --- 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 @@ -1,3 +1,3 @@ library angular2.test.transform.directive_processor.identifiers.classes_no_injectable; -abstract class ClassA {} \ No newline at end of file +abstract class ClassA {} diff --git a/modules_dart/transform/test/transform/directive_processor/identifiers/constants.dart b/modules_dart/transform/test/transform/directive_processor/identifiers/constants.dart index 4567eb94f8..5fcf54ae47 100644 --- a/modules_dart/transform/test/transform/directive_processor/identifiers/constants.dart +++ b/modules_dart/transform/test/transform/directive_processor/identifiers/constants.dart @@ -2,4 +2,4 @@ library angular2.test.transform.directive_processor.identifiers.constants; const a = "a"; const b = "b"; -var c = "c"; \ No newline at end of file +var c = "c"; diff --git a/modules_dart/transform/test/transform/directive_processor/identifiers/enums.dart b/modules_dart/transform/test/transform/directive_processor/identifiers/enums.dart index e611f83dd5..a39cc11610 100644 --- a/modules_dart/transform/test/transform/directive_processor/identifiers/enums.dart +++ b/modules_dart/transform/test/transform/directive_processor/identifiers/enums.dart @@ -1,5 +1,3 @@ library angular2.test.transform.directive_processor.identifiers.enums; -enum Enum { - one -} \ No newline at end of file +enum Enum { one } diff --git a/modules_dart/transform/test/transform/directive_processor/identifiers/factories.dart b/modules_dart/transform/test/transform/directive_processor/identifiers/factories.dart new file mode 100644 index 0000000000..5d34d97b80 --- /dev/null +++ b/modules_dart/transform/test/transform/directive_processor/identifiers/factories.dart @@ -0,0 +1,10 @@ +library angular2.test.transform.directive_processor.identifiers.classes; + +import 'package:angular2/angular2.dart' show Injectable; + +class SomeClass {} + +factory1(SomeClass c) {} + +@Injectable() +factory2(SomeClass c) {} \ No newline at end of file diff --git a/modules_dart/transform/test/transform/directive_processor/identifiers/provider_constants.dart b/modules_dart/transform/test/transform/directive_processor/identifiers/provider_constants.dart new file mode 100644 index 0000000000..29a73f031b --- /dev/null +++ b/modules_dart/transform/test/transform/directive_processor/identifiers/provider_constants.dart @@ -0,0 +1,13 @@ +library angular2.test.transform.directive_processor.identifiers.constants; + +import 'package:angular2/angular2.dart' show Provider; + +class SomeClass {} + +const a = const Provider("someToken", useClass: SomeClass); + +const b = const [ + SomeClass, + a, + const Provider("someOtherToken", useClass: SomeClass) +]; \ 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 index 53e32006ad..2072a601b3 100644 --- a/modules_dart/transform/test/transform/directive_processor/identifiers/typedefs.dart +++ b/modules_dart/transform/test/transform/directive_processor/identifiers/typedefs.dart @@ -1,3 +1,3 @@ library angular2.test.transform.directive_processor.identifiers.typedefs; -typedef String TypeDef(String); \ No newline at end of file +typedef String TypeDef(String); diff --git a/modules_dart/transform/test/transform/integration/all_tests.dart b/modules_dart/transform/test/transform/integration/all_tests.dart index f8c044ff35..1bfde031b5 100644 --- a/modules_dart/transform/test/transform/integration/all_tests.dart +++ b/modules_dart/transform/test/transform/integration/all_tests.dart @@ -51,50 +51,47 @@ void allTests() { Html5LibDomAdapter.makeCurrent(); var tests = [ - new IntegrationTestConfig( - 'should generate proper code for a Component defining only a selector.', + new IntegrationTestConfig('should generate proper code for a Component defining only a selector.', inputs: { - 'a|web/index.dart': 'simple_annotation_files/index.dart', - 'a|web/bar.dart': 'simple_annotation_files/bar.dart' - }, + 'a|web/index.dart': 'simple_annotation_files/index.dart', + 'a|web/bar.dart': 'simple_annotation_files/bar.dart' + }, outputs: { - 'a|web/bar.template.dart': - 'simple_annotation_files/expected/bar.template.dart', - 'a|web/index.template.dart': - 'simple_annotation_files/expected/index.template.dart' - }), - new IntegrationTestConfig( - 'should generate proper code for a Component with multiple deps.', + 'a|web/bar.template.dart': + 'simple_annotation_files/expected/bar.template.dart', + 'a|web/index.template.dart': + 'simple_annotation_files/expected/index.template.dart' + }), + new IntegrationTestConfig('should generate proper code for a Component with multiple deps.', inputs: { - 'a|web/index.dart': 'two_deps_files/index.dart', - 'a|web/foo.dart': 'two_deps_files/foo.dart', - 'a|web/bar.dart': 'two_deps_files/bar.dart' - }, + 'a|web/index.dart': 'two_deps_files/index.dart', + 'a|web/foo.dart': 'two_deps_files/foo.dart', + 'a|web/bar.dart': 'two_deps_files/bar.dart' + }, outputs: { - 'a|web/bar.template.dart': 'two_deps_files/expected/bar.template.dart' - }), + 'a|web/bar.template.dart': 'two_deps_files/expected/bar.template.dart' + }), new IntegrationTestConfig( 'should generate proper code for a Component declaring a ' 'componentService defined in another file.', inputs: { - 'a|web/index.dart': 'list_of_types_files/index.dart', - 'a|web/foo.dart': 'list_of_types_files/foo.dart', - 'a|web/bar.dart': 'list_of_types_files/bar.dart' - }, + 'a|web/index.dart': 'list_of_types_files/index.dart', + 'a|web/foo.dart': 'list_of_types_files/foo.dart', + 'a|web/bar.dart': 'list_of_types_files/bar.dart' + }, outputs: { - 'a|web/bar.template.dart': - 'list_of_types_files/expected/bar.template.dart' - }), - new IntegrationTestConfig( - 'should generate a factory for a class with no declared ctor.', + 'a|web/bar.template.dart': + 'list_of_types_files/expected/bar.template.dart' + }), + new IntegrationTestConfig('should generate a factory for a class with no declared ctor.', inputs: { - 'a|web/index.dart': 'synthetic_ctor_files/index.dart', - 'a|web/bar.dart': 'synthetic_ctor_files/bar.dart' - }, + 'a|web/index.dart': 'synthetic_ctor_files/index.dart', + 'a|web/bar.dart': 'synthetic_ctor_files/bar.dart' + }, outputs: { - 'a|web/bar.template.dart': - 'synthetic_ctor_files/expected/bar.template.dart' - }), + 'a|web/bar.template.dart': + 'synthetic_ctor_files/expected/bar.template.dart' + }), new IntegrationTestConfig('should preserve multiple annotations.', inputs: { 'a|web/index.dart': 'two_annotations_files/index.dart', 'a|web/bar.dart': 'two_annotations_files/bar.dart', @@ -104,77 +101,71 @@ void allTests() { 'a|web/bar.template.dart': 'two_annotations_files/expected/bar.template.dart' }), - new IntegrationTestConfig( - 'should generate getters for output events defined on a Component.', + new IntegrationTestConfig('should generate getters for output events defined on a Component.', inputs: { - 'a|web/index.dart': 'event_getter_files/index.dart', - 'a|web/bar.dart': 'event_getter_files/bar.dart' - }, + 'a|web/index.dart': 'event_getter_files/index.dart', + 'a|web/bar.dart': 'event_getter_files/bar.dart' + }, outputs: { - 'a|web/bar.template.dart': 'event_getter_files/expected/bar.template.dart' - }), - new IntegrationTestConfig( - 'should handle Directive dependencies declared on a View.', + 'a|web/bar.template.dart': + 'event_getter_files/expected/bar.template.dart' + }), + new IntegrationTestConfig('should handle Directive dependencies declared on a View.', inputs: { - 'a|web/index.dart': 'directive_dep_files/index.dart', - 'a|web/foo.dart': 'directive_dep_files/foo.dart', - 'a|web/bar.dart': 'directive_dep_files/bar.dart' - }, + 'a|web/index.dart': 'directive_dep_files/index.dart', + 'a|web/foo.dart': 'directive_dep_files/foo.dart', + 'a|web/bar.dart': 'directive_dep_files/bar.dart' + }, outputs: { - 'a|web/bar.template.dart': - 'directive_dep_files/expected/bar.template.dart' - }), - new IntegrationTestConfig( - 'should handle chained Directive dependencies declared on a View.', + 'a|web/bar.template.dart': + 'directive_dep_files/expected/bar.template.dart' + }), + new IntegrationTestConfig('should handle chained Directive dependencies declared on a View.', inputs: { - 'a|web/index.dart': 'directive_chain_files/index.dart', - 'a|web/foo.dart': 'directive_chain_files/foo.dart', - 'a|web/bar.dart': 'directive_chain_files/bar.dart', - 'a|web/baz.dart': 'directive_chain_files/baz.dart' - }, + 'a|web/index.dart': 'directive_chain_files/index.dart', + 'a|web/foo.dart': 'directive_chain_files/foo.dart', + 'a|web/bar.dart': 'directive_chain_files/bar.dart', + 'a|web/baz.dart': 'directive_chain_files/baz.dart' + }, outputs: { - 'a|web/bar.template.dart': - 'directive_chain_files/expected/bar.template.dart' - }), - new IntegrationTestConfig( - 'should handle empty template files that define directive aliases.', + 'a|web/bar.template.dart': + 'directive_chain_files/expected/bar.template.dart' + }), + new IntegrationTestConfig('should handle empty template files that define directive aliases.', inputs: { - 'a|web/foo.dart': 'empty_ng_deps_files/foo.dart', - 'a|web/bar.dart': 'empty_ng_deps_files/bar.dart' - }, + 'a|web/foo.dart': 'empty_ng_deps_files/foo.dart', + 'a|web/bar.dart': 'empty_ng_deps_files/bar.dart' + }, outputs: { - 'a|web/foo.template.dart': - 'empty_ng_deps_files/expected/foo.template.dart', - 'a|web/bar.template.dart': - 'empty_ng_deps_files/expected/bar.template.dart' - }), - new IntegrationTestConfig( - 'should generate setters for annotated properties.', + 'a|web/foo.template.dart': + 'empty_ng_deps_files/expected/foo.template.dart', + 'a|web/bar.template.dart': + 'empty_ng_deps_files/expected/bar.template.dart' + }), + new IntegrationTestConfig('should generate setters for annotated properties.', inputs: { - 'a|web/bar.dart': 'queries_prop_annotation_files/bar.dart' - }, + 'a|web/bar.dart': 'queries_prop_annotation_files/bar.dart' + }, outputs: { - 'a|web/bar.template.dart': - 'queries_prop_annotation_files/expected/bar.template.dart' - }), - new IntegrationTestConfig( - 'should generate setters for `queries` values in Directives.', + 'a|web/bar.template.dart': + 'queries_prop_annotation_files/expected/bar.template.dart' + }), + new IntegrationTestConfig('should generate setters for `queries` values in Directives.', inputs: { - 'a|web/bar.dart': 'queries_class_annotation_files/bar.dart' - }, + 'a|web/bar.dart': 'queries_class_annotation_files/bar.dart' + }, outputs: { - 'a|web/bar.template.dart': - 'queries_class_annotation_files/expected/bar.template.dart' - }), - new IntegrationTestConfig( - 'should handle @override annotations in properties on Directives.', + 'a|web/bar.template.dart': + 'queries_class_annotation_files/expected/bar.template.dart' + }), + new IntegrationTestConfig('should handle @override annotations in properties on Directives.', inputs: { - 'a|web/bar.dart': 'override_annotation_files/bar.dart' - }, + 'a|web/bar.dart': 'override_annotation_files/bar.dart' + }, outputs: { - 'a|web/bar.template.dart': - 'override_annotation_files/expected/bar.template.dart' - }) + 'a|web/bar.template.dart': + 'override_annotation_files/expected/bar.template.dart' + }) ]; var cache = {}; 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 60ce666998..0fc51dd030 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 @@ -26,7 +26,7 @@ void initReflector() { const [prefix.MyDep] ], (prefix.MyContext c, prefix.MyDep inValue) => - new MyComponent(c, inValue))); + new MyComponent(c, inValue))); i0.initReflector(); i1.initReflector(); -} \ No newline at end of file +} 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 ac47db14f7..f6eaee1719 100644 --- a/modules_dart/transform/test/transform/template_compiler/all_tests.dart +++ b/modules_dart/transform/test/transform/template_compiler/all_tests.dart @@ -57,24 +57,31 @@ void allTests() { // lacking some details that would be created by DirectiveProcessor but // which are not used in the template compiler. fooComponentMeta = createFoo(moduleBase); - fooNgMeta = new NgMeta(ngDeps: new NgDepsModel() - ..libraryUri = 'test.foo' - ..reflectables.add(new ReflectionInfoModel()..name = fooComponentMeta.type.name)); + fooNgMeta = new NgMeta( + ngDeps: new NgDepsModel() + ..libraryUri = 'test.foo' + ..reflectables.add( + new ReflectionInfoModel()..name = fooComponentMeta.type.name)); fooNgMeta.identifiers[fooComponentMeta.type.name] = fooComponentMeta; barComponentMeta = createBar(moduleBase); barPipeMeta = createBarPipe(moduleBase); - barNgMeta = new NgMeta(ngDeps: new NgDepsModel() - ..libraryUri = 'test.bar' - ..reflectables.add(new ReflectionInfoModel()..name = barPipeMeta.type.name) - ..reflectables.add(new ReflectionInfoModel()..name = barComponentMeta.type.name)); + barNgMeta = new NgMeta( + ngDeps: new NgDepsModel() + ..libraryUri = 'test.bar' + ..reflectables + .add(new ReflectionInfoModel()..name = barPipeMeta.type.name) + ..reflectables.add( + new ReflectionInfoModel()..name = barComponentMeta.type.name)); 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)); + bazNgMeta = new NgMeta( + ngDeps: new NgDepsModel() + ..libraryUri = 'test.baz' + ..reflectables.add( + new ReflectionInfoModel()..name = bazComponentMeta.type.name)); barNgMeta.identifiers[bazComponentMeta.type.name] = bazComponentMeta; fooAssetId = new AssetId('a', 'lib/foo.ng_meta.json'); @@ -92,206 +99,11 @@ void allTests() { platformDirectives: platformDirectives, platformPipes: platformPipes, resolvedIdentifiers: resolvedIdentifiers, - translations: translations), + translations: translations + ), log: logger); } - // TODO(tbosch): This is just a temporary test that makes sure that the dart - // server and dart browser is in sync. - it('should not contain notifyBinding', () async { - fooComponentMeta.template = new CompileTemplateMetadata( - template: '
  • test
  • '); - final viewAnnotation = new AnnotationModel() - ..name = 'View' - ..isView = true; - fooNgMeta.ngDeps.reflectables.first.annotations.add(viewAnnotation); - fooNgMeta.ngDeps.reflectables.first.directives - .add(new PrefixedType()..name = 'NgFor'); - fooNgMeta.ngDeps.imports.add( - new ImportModel()..uri = 'package:angular2/src/directives/ng_for.dart'); - - reader.addAsset(new AssetId('angular2', 'lib/src/directives/ng_for.dart'), - JSON.encode(ngMeta.ngFor)); - - updateReader(); - - final outputs = await process(fooAssetId); - // TODO(kegluenq): Does this next line need to be updated as well? - 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 resolve queries and viewQueries.', () async { - barNgMeta.identifiers['Service'] = new CompileTypeMetadata(name: 'Service', moduleUrl: 'moduleUrl'); - - fooComponentMeta.template = new CompileTemplateMetadata(template: "import 'bar.dart';"); - fooComponentMeta.type.diDeps = [ - new CompileDiDependencyMetadata( - token: 'someToken', - query: new CompileQueryMetadata(selectors: [new CompileIdentifierMetadata(name: 'Service')]) - ), - new CompileDiDependencyMetadata( - token: 'someToken', - viewQuery: new CompileQueryMetadata(selectors: [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.type.diDeps.length).toEqual(2); - - expect(cmp.type.diDeps[0].query.selectors[0].name).toEqual("Service"); - expect(cmp.type.diDeps[0].query.selectors[0].moduleUrl).toEqual("moduleUrl"); - expect(cmp.type.diDeps[1].viewQuery.selectors[0].name).toEqual("Service"); - expect(cmp.type.diDeps[1].viewQuery.selectors[0].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}}
    ', @@ -625,31 +437,6 @@ void allTests() { ..toContain(barPipeMeta.name); }); - it('should fallback to the list of resolved identifiers.', () async { - 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, [], [], {"Service1": "someModuleUrl", "Service2": "someModuleUrl"}); - 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("someModuleUrl"); - expect(cmp.providers[0].useClass.name).toEqual("Service2"); - expect(cmp.providers[0].useClass.moduleUrl).toEqual("moduleUrl"); - }); - it('should use i18n parser when translations are provided.', () async { fooComponentMeta.template = new CompileTemplateMetadata( template: '
    content
    ', diff --git a/modules_dart/transform/test/transform/transform.unittest.server.spec.dart b/modules_dart/transform/test/transform/transform.unittest.server.spec.dart index 981d740d9c..0f9b51a58f 100644 --- a/modules_dart/transform/test/transform/transform.unittest.server.spec.dart +++ b/modules_dart/transform/test/transform/transform.unittest.server.spec.dart @@ -14,3 +14,5 @@ main() { describe('Template Compiler', templateCompiler.allTests); describe('Stylesheet Compiler', stylesheetCompiler.allTests); } + + diff --git a/tools/public_api_guard/public_api_spec.ts b/tools/public_api_guard/public_api_spec.ts index 1705d8ec75..a00eaa67ed 100644 --- a/tools/public_api_guard/public_api_spec.ts +++ b/tools/public_api_guard/public_api_spec.ts @@ -833,136 +833,138 @@ const COMMON = [ 'var workaround_empty_observable_list_diff:any' ]; -const COMPILER = [ - 'AttrAst', - 'AttrAst.constructor(name:string, value:string, sourceSpan:ParseSourceSpan)', - 'AttrAst.visit(visitor:TemplateAstVisitor, context:any):any', - 'BoundDirectivePropertyAst', - 'BoundDirectivePropertyAst.constructor(directiveName:string, templateName:string, value:AST, sourceSpan:ParseSourceSpan)', - 'BoundDirectivePropertyAst.visit(visitor:TemplateAstVisitor, context:any):any', - 'BoundElementPropertyAst', - 'BoundElementPropertyAst.constructor(name:string, type:PropertyBindingType, value:AST, unit:string, sourceSpan:ParseSourceSpan)', - 'BoundElementPropertyAst.visit(visitor:TemplateAstVisitor, context:any):any', - 'BoundEventAst', - 'BoundEventAst.constructor(name:string, target:string, handler:AST, sourceSpan:ParseSourceSpan)', - 'BoundEventAst.fullName:any', - 'BoundEventAst.visit(visitor:TemplateAstVisitor, context:any):any', - 'BoundTextAst', - 'BoundTextAst.constructor(value:AST, ngContentIndex:number, sourceSpan:ParseSourceSpan)', - '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,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', - 'CompileDirectiveMetadata.hostAttributes:{[key:string]:string}', - 'CompileDirectiveMetadata.hostListeners:{[key:string]:string}', - 'CompileDirectiveMetadata.hostProperties:{[key:string]:string}', - 'CompileDirectiveMetadata.inputs:{[key:string]:string}', - 'CompileDirectiveMetadata.isComponent:boolean', - 'CompileDirectiveMetadata.lifecycleHooks:LifecycleHooks[]', - 'CompileDirectiveMetadata.outputs:{[key:string]:string}', - 'CompileDirectiveMetadata.selector:string', - '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', - 'CompileTemplateMetadata.fromJson(data:{[key:string]:any}):CompileTemplateMetadata', - 'CompileTemplateMetadata.ngContentSelectors:string[]', - 'CompileTemplateMetadata.styleUrls:string[]', - 'CompileTemplateMetadata.styles:string[]', - 'CompileTemplateMetadata.template:string', - 'CompileTemplateMetadata.templateUrl:string', - 'CompileTemplateMetadata.toJson():{[key:string]:any}', - 'CompileTypeMetadata', - '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', - 'ElementAst', - 'ElementAst.constructor(name:string, attrs:AttrAst[], inputs:BoundElementPropertyAst[], outputs:BoundEventAst[], exportAsVars:VariableAst[], directives:DirectiveAst[], children:TemplateAst[], ngContentIndex:number, sourceSpan:ParseSourceSpan)', - 'ElementAst.getComponent():CompileDirectiveMetadata', - 'ElementAst.isBound():boolean', - 'ElementAst.visit(visitor:TemplateAstVisitor, context:any):any', - 'EmbeddedTemplateAst', - 'EmbeddedTemplateAst.constructor(attrs:AttrAst[], outputs:BoundEventAst[], vars:VariableAst[], directives:DirectiveAst[], children:TemplateAst[], ngContentIndex:number, sourceSpan:ParseSourceSpan)', - 'EmbeddedTemplateAst.visit(visitor:TemplateAstVisitor, context:any):any', - 'NgContentAst', - 'NgContentAst.constructor(index:number, ngContentIndex:number, sourceSpan:ParseSourceSpan)', - 'NgContentAst.visit(visitor:TemplateAstVisitor, context:any):any', - 'PropertyBindingType', - 'PropertyBindingType.Attribute', - 'PropertyBindingType.Class', - 'PropertyBindingType.Property', - 'PropertyBindingType.Style', - 'SourceModule', - 'SourceModule.constructor(moduleUrl:string, sourceWithModuleRefs:string)', - 'SourceModule.getSourceWithImports():SourceWithImports', - 'SourceModule.getSourceWithoutImports(sourceWithModuleRefs:string):string', - 'SourceWithImports', - 'SourceWithImports.constructor(source:string, imports:string[][])', - 'TemplateAst', - 'TemplateAst.sourceSpan:ParseSourceSpan', - 'TemplateAst.visit(visitor:TemplateAstVisitor, context:any):any', - 'TemplateAstVisitor', - 'TemplateAstVisitor.visitAttr(ast:AttrAst, context:any):any', - 'TemplateAstVisitor.visitBoundText(ast:BoundTextAst, context:any):any', - 'TemplateAstVisitor.visitDirective(ast:DirectiveAst, context:any):any', - 'TemplateAstVisitor.visitDirectiveProperty(ast:BoundDirectivePropertyAst, context:any):any', - 'TemplateAstVisitor.visitElement(ast:ElementAst, context:any):any', - 'TemplateAstVisitor.visitElementProperty(ast:BoundElementPropertyAst, context:any):any', - 'TemplateAstVisitor.visitEmbeddedTemplate(ast:EmbeddedTemplateAst, context:any):any', - 'TemplateAstVisitor.visitEvent(ast:BoundEventAst, context:any):any', - 'TemplateAstVisitor.visitNgContent(ast:NgContentAst, context:any):any', - 'TemplateAstVisitor.visitText(ast:TextAst, context:any):any', - 'TemplateAstVisitor.visitVariable(ast:VariableAst, context:any):any', - 'TemplateCompiler', - 'TemplateCompiler.clearCache():any', - 'TemplateCompiler.compileHostComponentRuntime(type:Type):Promise', - 'TemplateCompiler.compileStylesheetCodeGen(stylesheetUrl:string, cssText:string):SourceModule[]', - 'TemplateCompiler.compileTemplatesCodeGen(components:NormalizedComponentWithViewDirectives[]):SourceModule', - 'TemplateCompiler.constructor(_runtimeMetadataResolver:RuntimeMetadataResolver, _templateNormalizer:TemplateNormalizer, _templateParser:TemplateParser, _styleCompiler:StyleCompiler, _cdCompiler:ChangeDetectionCompiler, _protoViewCompiler:ProtoViewCompiler, _viewCompiler:ViewCompiler, _resolvedMetadataCache:ResolvedMetadataCache, _genConfig:ChangeDetectorGenConfig)', - 'TemplateCompiler.normalizeDirectiveMetadata(directive:CompileDirectiveMetadata):Promise', - 'TextAst', - 'TextAst.constructor(value:string, ngContentIndex:number, sourceSpan:ParseSourceSpan)', - 'TextAst.visit(visitor:TemplateAstVisitor, context:any):any', - 'UrlResolver', - 'UrlResolver.constructor(packagePrefix:string)', - 'UrlResolver.resolve(baseUrl:string, url:string):string', - 'VariableAst', - 'VariableAst.constructor(name:string, value:string, sourceSpan:ParseSourceSpan)', - 'VariableAst.visit(visitor:TemplateAstVisitor, context:any):any', - 'XHR', - 'XHR.get(url:string):Promise', - 'const COMPILER_PROVIDERS:Array', - 'const PLATFORM_DIRECTIVES:OpaqueToken', - 'const PLATFORM_PIPES:OpaqueToken', - 'const TEMPLATE_TRANSFORMS:any', - 'createWithoutPackagePrefix():UrlResolver', - 'getUrlScheme(url:string):string', - 'templateVisitAll(visitor:TemplateAstVisitor, asts:TemplateAst[], context:any):any[]', - 'var DEFAULT_PACKAGE_URL_PROVIDER:any' -]; +const COMPILER = + [ + 'AttrAst', + 'AttrAst.constructor(name:string, value:string, sourceSpan:ParseSourceSpan)', + 'AttrAst.visit(visitor:TemplateAstVisitor, context:any):any', + 'BoundDirectivePropertyAst', + 'BoundDirectivePropertyAst.constructor(directiveName:string, templateName:string, value:AST, sourceSpan:ParseSourceSpan)', + 'BoundDirectivePropertyAst.visit(visitor:TemplateAstVisitor, context:any):any', + 'BoundElementPropertyAst', + 'BoundElementPropertyAst.constructor(name:string, type:PropertyBindingType, value:AST, unit:string, sourceSpan:ParseSourceSpan)', + 'BoundElementPropertyAst.visit(visitor:TemplateAstVisitor, context:any):any', + 'BoundEventAst', + 'BoundEventAst.constructor(name:string, target:string, handler:AST, sourceSpan:ParseSourceSpan)', + 'BoundEventAst.fullName:any', + 'BoundEventAst.visit(visitor:TemplateAstVisitor, context:any):any', + 'BoundTextAst', + 'BoundTextAst.constructor(value:AST, ngContentIndex:number, sourceSpan:ParseSourceSpan)', + '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,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', + 'CompileDirectiveMetadata.hostAttributes:{[key:string]:string}', + 'CompileDirectiveMetadata.hostListeners:{[key:string]:string}', + 'CompileDirectiveMetadata.hostProperties:{[key:string]:string}', + 'CompileDirectiveMetadata.inputs:{[key:string]:string}', + 'CompileDirectiveMetadata.isComponent:boolean', + 'CompileDirectiveMetadata.lifecycleHooks:LifecycleHooks[]', + 'CompileDirectiveMetadata.outputs:{[key:string]:string}', + 'CompileDirectiveMetadata.selector:string', + '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', + 'CompileTemplateMetadata.fromJson(data:{[key:string]:any}):CompileTemplateMetadata', + 'CompileTemplateMetadata.ngContentSelectors:string[]', + 'CompileTemplateMetadata.styleUrls:string[]', + 'CompileTemplateMetadata.styles:string[]', + 'CompileTemplateMetadata.template:string', + 'CompileTemplateMetadata.templateUrl:string', + 'CompileTemplateMetadata.toJson():{[key:string]:any}', + 'CompileTypeMetadata', + 'CompileTypeMetadata.constructor({runtime,name,moduleUrl,prefix,isHost,constConstructor,value,diDeps}:{runtime?:Type, name?:string, moduleUrl?:string, prefix?:string, isHost?:boolean, constConstructor?:boolean, value?:any, diDeps?:CompileDiDependencyMetadata[]})', + 'CompileTypeMetadata.fromJson(data:{[key:string]:any}):CompileTypeMetadata', + 'CompileTypeMetadata.value:any', + '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', + 'ElementAst', + 'ElementAst.constructor(name:string, attrs:AttrAst[], inputs:BoundElementPropertyAst[], outputs:BoundEventAst[], exportAsVars:VariableAst[], directives:DirectiveAst[], children:TemplateAst[], ngContentIndex:number, sourceSpan:ParseSourceSpan)', + 'ElementAst.getComponent():CompileDirectiveMetadata', + 'ElementAst.isBound():boolean', + 'ElementAst.visit(visitor:TemplateAstVisitor, context:any):any', + 'EmbeddedTemplateAst', + 'EmbeddedTemplateAst.constructor(attrs:AttrAst[], outputs:BoundEventAst[], vars:VariableAst[], directives:DirectiveAst[], children:TemplateAst[], ngContentIndex:number, sourceSpan:ParseSourceSpan)', + 'EmbeddedTemplateAst.visit(visitor:TemplateAstVisitor, context:any):any', + 'NgContentAst', + 'NgContentAst.constructor(index:number, ngContentIndex:number, sourceSpan:ParseSourceSpan)', + 'NgContentAst.visit(visitor:TemplateAstVisitor, context:any):any', + 'PropertyBindingType', + 'PropertyBindingType.Attribute', + 'PropertyBindingType.Class', + 'PropertyBindingType.Property', + 'PropertyBindingType.Style', + 'SourceModule', + 'SourceModule.constructor(moduleUrl:string, sourceWithModuleRefs:string)', + 'SourceModule.getSourceWithImports():SourceWithImports', + 'SourceModule.getSourceWithoutImports(sourceWithModuleRefs:string):string', + 'SourceWithImports', + 'SourceWithImports.constructor(source:string, imports:string[][])', + 'TemplateAst', + 'TemplateAst.sourceSpan:ParseSourceSpan', + 'TemplateAst.visit(visitor:TemplateAstVisitor, context:any):any', + 'TemplateAstVisitor', + 'TemplateAstVisitor.visitAttr(ast:AttrAst, context:any):any', + 'TemplateAstVisitor.visitBoundText(ast:BoundTextAst, context:any):any', + 'TemplateAstVisitor.visitDirective(ast:DirectiveAst, context:any):any', + 'TemplateAstVisitor.visitDirectiveProperty(ast:BoundDirectivePropertyAst, context:any):any', + 'TemplateAstVisitor.visitElement(ast:ElementAst, context:any):any', + 'TemplateAstVisitor.visitElementProperty(ast:BoundElementPropertyAst, context:any):any', + 'TemplateAstVisitor.visitEmbeddedTemplate(ast:EmbeddedTemplateAst, context:any):any', + 'TemplateAstVisitor.visitEvent(ast:BoundEventAst, context:any):any', + 'TemplateAstVisitor.visitNgContent(ast:NgContentAst, context:any):any', + 'TemplateAstVisitor.visitText(ast:TextAst, context:any):any', + 'TemplateAstVisitor.visitVariable(ast:VariableAst, context:any):any', + 'TemplateCompiler', + 'TemplateCompiler.clearCache():any', + 'TemplateCompiler.compileHostComponentRuntime(type:Type):Promise', + 'TemplateCompiler.compileStylesheetCodeGen(stylesheetUrl:string, cssText:string):SourceModule[]', + 'TemplateCompiler.compileTemplatesCodeGen(components:NormalizedComponentWithViewDirectives[]):SourceModule', + 'TemplateCompiler.constructor(_runtimeMetadataResolver:RuntimeMetadataResolver, _templateNormalizer:TemplateNormalizer, _templateParser:TemplateParser, _styleCompiler:StyleCompiler, _cdCompiler:ChangeDetectionCompiler, _protoViewCompiler:ProtoViewCompiler, _viewCompiler:ViewCompiler, _resolvedMetadataCache:ResolvedMetadataCache, _genConfig:ChangeDetectorGenConfig)', + 'TemplateCompiler.normalizeDirectiveMetadata(directive:CompileDirectiveMetadata):Promise', + 'TextAst', + 'TextAst.constructor(value:string, ngContentIndex:number, sourceSpan:ParseSourceSpan)', + 'TextAst.visit(visitor:TemplateAstVisitor, context:any):any', + 'UrlResolver', + 'UrlResolver.constructor(packagePrefix:string)', + 'UrlResolver.resolve(baseUrl:string, url:string):string', + 'VariableAst', + 'VariableAst.constructor(name:string, value:string, sourceSpan:ParseSourceSpan)', + 'VariableAst.visit(visitor:TemplateAstVisitor, context:any):any', + 'XHR', + 'XHR.get(url:string):Promise', + 'const COMPILER_PROVIDERS:Array', + 'const PLATFORM_DIRECTIVES:OpaqueToken', + 'const PLATFORM_PIPES:OpaqueToken', + 'const TEMPLATE_TRANSFORMS:any', + 'createWithoutPackagePrefix():UrlResolver', + 'getUrlScheme(url:string):string', + 'templateVisitAll(visitor:TemplateAstVisitor, asts:TemplateAst[], context:any):any[]', + 'var DEFAULT_PACKAGE_URL_PROVIDER:any' + ]; const INSTRUMENTATION = [ 'WtfScopeFn', @@ -1133,7 +1135,18 @@ describe('public API', () => { function checkPublicApi(file: string, expected: string[]) { const sortedActual = publicApi(file).sort(); const sortedExpected = expected.sort(); - const missing = sortedActual.filter((i) => sortedExpected.indexOf(i) < 0).map(s => `+${s}`); - const extra = sortedExpected.filter((i) => sortedActual.indexOf(i) < 0).map(s => `-${s}`); - expect(missing.concat(extra)).toEqual([]); + const missing = sortedActual.filter((i) => sortedExpected.indexOf(i) < 0); + const extra = sortedExpected.filter((i) => sortedActual.indexOf(i) < 0); + + if (missing.length > 0) { + console.log("Missing:"); + missing.forEach((m) => console.log(m)); + } + + if (extra.length > 0) { + console.log("Extra:"); + extra.forEach((m) => console.log(m)); + } + + expect(missing.map(s => `+${s}`).concat(extra.map(s => `-${s}`))).toEqual([]); }