feat(transformers): changes transformers to collect information about providers and resolve identifiers during linking

This commit is contained in:
vsavkin 2016-04-10 19:36:16 -07:00
parent 3c2473bac6
commit 3b60503d2b
46 changed files with 2473 additions and 978 deletions

View File

@ -1,13 +1,16 @@
import { import {
isPresent, isPresent,
isBlank, isBlank,
isNumber,
isBoolean,
normalizeBool, normalizeBool,
normalizeBlank, normalizeBlank,
serializeEnum, serializeEnum,
Type, Type,
isString, isString,
RegExpWrapper, RegExpWrapper,
StringWrapper StringWrapper,
isArray
} from 'angular2/src/facade/lang'; } from 'angular2/src/facade/lang';
import {unimplemented} from 'angular2/src/facade/exceptions'; import {unimplemented} from 'angular2/src/facade/exceptions';
import {StringMapWrapper} from 'angular2/src/facade/collection'; 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; var HOST_REG_EXP = /^(?:(?:\[([^\]]+)\])|(?:\(([^\)]+)\)))$/g;
export abstract class CompileMetadataWithIdentifier { export abstract class CompileMetadataWithIdentifier {
static fromJson(data: {[key: string]: any}): CompileMetadataWithIdentifier {
return _COMPILE_METADATA_FROM_JSON[data['class']](data);
}
abstract toJson(): {[key: string]: any}; abstract toJson(): {[key: string]: any};
get identifier(): CompileIdentifierMetadata { return <CompileIdentifierMetadata>unimplemented(); } get identifier(): CompileIdentifierMetadata { return <CompileIdentifierMetadata>unimplemented(); }
} }
export abstract class CompileMetadataWithType extends CompileMetadataWithIdentifier { 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}; abstract toJson(): {[key: string]: any};
get type(): CompileTypeMetadata { return <CompileTypeMetadata>unimplemented(); } get type(): CompileTypeMetadata { return <CompileTypeMetadata>unimplemented(); }
@ -46,43 +41,56 @@ export abstract class CompileMetadataWithType extends CompileMetadataWithIdentif
get identifier(): CompileIdentifierMetadata { return <CompileIdentifierMetadata>unimplemented(); } get identifier(): CompileIdentifierMetadata { return <CompileIdentifierMetadata>unimplemented(); }
} }
export function metadataFromJson(data: {[key: string]: any}): any {
return _COMPILE_METADATA_FROM_JSON[data['class']](data);
}
export class CompileIdentifierMetadata implements CompileMetadataWithIdentifier { export class CompileIdentifierMetadata implements CompileMetadataWithIdentifier {
runtime: any; runtime: any;
name: string; name: string;
prefix: string; prefix: string;
moduleUrl: string; moduleUrl: string;
constConstructor: boolean; constConstructor: boolean;
constructor({runtime, name, moduleUrl, prefix, constConstructor}: { value: any;
constructor({runtime, name, moduleUrl, prefix, constConstructor, value}: {
runtime?: any, runtime?: any,
name?: string, name?: string,
moduleUrl?: string, moduleUrl?: string,
prefix?: string, prefix?: string,
constConstructor?: boolean constConstructor?: boolean,
value?: any
} = {}) { } = {}) {
this.runtime = runtime; this.runtime = runtime;
this.name = name; this.name = name;
this.prefix = prefix; this.prefix = prefix;
this.moduleUrl = moduleUrl; this.moduleUrl = moduleUrl;
this.constConstructor = constConstructor; this.constConstructor = constConstructor;
this.value = value;
} }
static fromJson(data: {[key: string]: any}): CompileIdentifierMetadata { static fromJson(data: {[key: string]: any}): CompileIdentifierMetadata {
let value = isArray(data['value']) ? arrayFromJson(data['value'], metadataFromJson) :
objFromJson(data['value'], metadataFromJson);
return new CompileIdentifierMetadata({ return new CompileIdentifierMetadata({
name: data['name'], name: data['name'],
prefix: data['prefix'], prefix: data['prefix'],
moduleUrl: data['moduleUrl'], moduleUrl: data['moduleUrl'],
constConstructor: data['constConstructor'] constConstructor: data['constConstructor'],
value: value
}); });
} }
toJson(): {[key: string]: any} { toJson(): {[key: string]: any} {
let value = isArray(this.value) ? arrayToJson(this.value) : objToJson(this.value);
return { return {
// Note: Runtime type can't be serialized... // Note: Runtime type can't be serialized...
'class': 'Identifier', 'class': 'Identifier',
'name': this.name, 'name': this.name,
'moduleUrl': this.moduleUrl, 'moduleUrl': this.moduleUrl,
'prefix': this.prefix, 'prefix': this.prefix,
'constConstructor': this.constConstructor 'constConstructor': this.constConstructor,
'value': value
}; };
} }
@ -177,44 +185,78 @@ export class CompileProviderMetadata {
static fromJson(data: {[key: string]: any}): CompileProviderMetadata { static fromJson(data: {[key: string]: any}): CompileProviderMetadata {
return new CompileProviderMetadata({ return new CompileProviderMetadata({
token: objFromJson(data['token'], CompileIdentifierMetadata.fromJson), 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} { toJson(): {[key: string]: any} {
return { return {
// Note: Runtime type can't be serialized... // Note: Runtime type can't be serialized...
'class': 'Provider',
'token': objToJson(this.token), '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; runtime: Function;
name: string; name: string;
prefix: string; prefix: string;
moduleUrl: string; moduleUrl: string;
constConstructor: boolean; constConstructor: boolean;
value: any;
diDeps: CompileDiDependencyMetadata[]; diDeps: CompileDiDependencyMetadata[];
constructor({runtime, name, moduleUrl, constConstructor, diDeps}: { constructor({runtime, name, moduleUrl, prefix, constConstructor, diDeps, value}: {
runtime?: Function, runtime?: Function,
name?: string, name?: string,
prefix?: string,
moduleUrl?: string, moduleUrl?: string,
constConstructor?: boolean, constConstructor?: boolean,
value?: boolean,
diDeps?: CompileDiDependencyMetadata[] diDeps?: CompileDiDependencyMetadata[]
}) { }) {
this.runtime = runtime; this.runtime = runtime;
this.name = name; this.name = name;
this.prefix = prefix;
this.moduleUrl = moduleUrl; this.moduleUrl = moduleUrl;
this.diDeps = diDeps; this.diDeps = diDeps;
this.constConstructor = constConstructor; this.constConstructor = constConstructor;
this.value = value;
} }
get identifier(): CompileIdentifierMetadata { return this; } get identifier(): CompileIdentifierMetadata { return this; }
toJson(): {[key: string]: any} { 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 +269,17 @@ export class CompileTypeMetadata implements CompileIdentifierMetadata, CompileMe
moduleUrl: string; moduleUrl: string;
isHost: boolean; isHost: boolean;
constConstructor: boolean; constConstructor: boolean;
value: any;
diDeps: CompileDiDependencyMetadata[]; diDeps: CompileDiDependencyMetadata[];
constructor({runtime, name, moduleUrl, prefix, isHost, constConstructor, diDeps}: { constructor({runtime, name, moduleUrl, prefix, isHost, constConstructor, value, diDeps}: {
runtime?: Type, runtime?: Type,
name?: string, name?: string,
moduleUrl?: string, moduleUrl?: string,
prefix?: string, prefix?: string,
isHost?: boolean, isHost?: boolean,
constConstructor?: boolean, constConstructor?: boolean,
value?: any,
diDeps?: CompileDiDependencyMetadata[] diDeps?: CompileDiDependencyMetadata[]
} = {}) { } = {}) {
this.runtime = runtime; this.runtime = runtime;
@ -244,6 +288,7 @@ export class CompileTypeMetadata implements CompileIdentifierMetadata, CompileMe
this.prefix = prefix; this.prefix = prefix;
this.isHost = normalizeBool(isHost); this.isHost = normalizeBool(isHost);
this.constConstructor = constConstructor; this.constConstructor = constConstructor;
this.value = value;
this.diDeps = normalizeBlank(diDeps); this.diDeps = normalizeBlank(diDeps);
} }
@ -254,6 +299,7 @@ export class CompileTypeMetadata implements CompileIdentifierMetadata, CompileMe
prefix: data['prefix'], prefix: data['prefix'],
isHost: data['isHost'], isHost: data['isHost'],
constConstructor: data['constConstructor'], constConstructor: data['constConstructor'],
value: data['value'],
diDeps: arrayFromJson(data['diDeps'], CompileDiDependencyMetadata.fromJson) diDeps: arrayFromJson(data['diDeps'], CompileDiDependencyMetadata.fromJson)
}); });
} }
@ -270,6 +316,7 @@ export class CompileTypeMetadata implements CompileIdentifierMetadata, CompileMe
'prefix': this.prefix, 'prefix': this.prefix,
'isHost': this.isHost, 'isHost': this.isHost,
'constConstructor': this.constConstructor, 'constConstructor': this.constConstructor,
'value': this.value,
'diDeps': arrayToJson(this.diDeps) 'diDeps': arrayToJson(this.diDeps)
}; };
} }
@ -382,8 +429,10 @@ export class CompileDirectiveMetadata implements CompileMetadataWithType {
outputs?: string[], outputs?: string[],
host?: {[key: string]: string}, host?: {[key: string]: string},
lifecycleHooks?: LifecycleHooks[], lifecycleHooks?: LifecycleHooks[],
providers?: Array<CompileProviderMetadata | CompileTypeMetadata | any[]>, providers?:
viewProviders?: Array<CompileProviderMetadata | CompileTypeMetadata | any[]>, Array<CompileProviderMetadata | CompileTypeMetadata | CompileIdentifierMetadata | any[]>,
viewProviders?:
Array<CompileProviderMetadata | CompileTypeMetadata | CompileIdentifierMetadata | any[]>,
queries?: CompileQueryMetadata[], queries?: CompileQueryMetadata[],
viewQueries?: CompileQueryMetadata[], viewQueries?: CompileQueryMetadata[],
template?: CompileTemplateMetadata template?: CompileTemplateMetadata
@ -474,8 +523,10 @@ export class CompileDirectiveMetadata implements CompileMetadataWithType {
hostProperties?: {[key: string]: string}, hostProperties?: {[key: string]: string},
hostAttributes?: {[key: string]: string}, hostAttributes?: {[key: string]: string},
lifecycleHooks?: LifecycleHooks[], lifecycleHooks?: LifecycleHooks[],
providers?: Array<CompileProviderMetadata | CompileTypeMetadata | any[]>, providers?:
viewProviders?: Array<CompileProviderMetadata | CompileTypeMetadata | any[]>, Array<CompileProviderMetadata | CompileTypeMetadata | CompileIdentifierMetadata | any[]>,
viewProviders?:
Array<CompileProviderMetadata | CompileTypeMetadata | CompileIdentifierMetadata | any[]>,
queries?: CompileQueryMetadata[], queries?: CompileQueryMetadata[],
viewQueries?: CompileQueryMetadata[], viewQueries?: CompileQueryMetadata[],
template?: CompileTemplateMetadata template?: CompileTemplateMetadata
@ -494,8 +545,8 @@ export class CompileDirectiveMetadata implements CompileMetadataWithType {
this.lifecycleHooks = lifecycleHooks; this.lifecycleHooks = lifecycleHooks;
this.providers = normalizeBlank(providers); this.providers = normalizeBlank(providers);
this.viewProviders = normalizeBlank(viewProviders); this.viewProviders = normalizeBlank(viewProviders);
this.queries = queries; this.queries = normalizeBlank(queries);
this.viewQueries = viewQueries; this.viewQueries = normalizeBlank(viewQueries);
this.template = template; this.template = template;
} }
@ -520,7 +571,10 @@ export class CompileDirectiveMetadata implements CompileMetadataWithType {
(<any[]>data['lifecycleHooks']).map(hookValue => LIFECYCLE_HOOKS_VALUES[hookValue]), (<any[]>data['lifecycleHooks']).map(hookValue => LIFECYCLE_HOOKS_VALUES[hookValue]),
template: isPresent(data['template']) ? CompileTemplateMetadata.fromJson(data['template']) : template: isPresent(data['template']) ? CompileTemplateMetadata.fromJson(data['template']) :
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 +595,10 @@ export class CompileDirectiveMetadata implements CompileMetadataWithType {
'hostAttributes': this.hostAttributes, 'hostAttributes': this.hostAttributes,
'lifecycleHooks': this.lifecycleHooks.map(hook => serializeEnum(hook)), 'lifecycleHooks': this.lifecycleHooks.map(hook => serializeEnum(hook)),
'template': isPresent(this.template) ? this.template.toJson() : this.template, 'template': isPresent(this.template) ? this.template.toJson() : this.template,
'providers': arrayToJson(this.providers) 'providers': arrayToJson(this.providers),
'viewProviders': arrayToJson(this.viewProviders),
'queries': arrayToJson(this.queries),
'viewQueries': arrayToJson(this.viewQueries)
}; };
} }
} }
@ -611,7 +668,9 @@ var _COMPILE_METADATA_FROM_JSON = {
'Directive': CompileDirectiveMetadata.fromJson, 'Directive': CompileDirectiveMetadata.fromJson,
'Pipe': CompilePipeMetadata.fromJson, 'Pipe': CompilePipeMetadata.fromJson,
'Type': CompileTypeMetadata.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 { function arrayFromJson(obj: any[], fn: (a: {[key: string]: any}) => any): any {
@ -623,9 +682,13 @@ function arrayToJson(obj: any[]): string | {[key: string]: any} {
} }
function objFromJson(obj: any, fn: (a: {[key: string]: any}) => any): any { function objFromJson(obj: any, fn: (a: {[key: string]: any}) => any): any {
return (isString(obj) || isBlank(obj)) ? obj : fn(obj); if (isArray(obj)) return arrayFromJson(obj, fn);
if (isString(obj) || isBlank(obj) || isBoolean(obj) || isNumber(obj)) return obj;
return fn(obj);
} }
function objToJson(obj: any): string | {[key: string]: any} { function objToJson(obj: any): string | {[key: string]: any} {
return (isString(obj) || isBlank(obj)) ? obj : obj.toJson(); if (isArray(obj)) return arrayToJson(obj);
if (isString(obj) || isBlank(obj) || isBoolean(obj) || isNumber(obj)) return obj;
return obj.toJson();
} }

View File

@ -110,6 +110,9 @@ export class TemplateCompiler {
hostAttributes: directive.hostAttributes, hostAttributes: directive.hostAttributes,
lifecycleHooks: directive.lifecycleHooks, lifecycleHooks: directive.lifecycleHooks,
providers: directive.providers, providers: directive.providers,
viewProviders: directive.viewProviders,
queries: directive.queries,
viewQueries: directive.viewQueries,
template: normalizedTemplate template: normalizedTemplate
})); }));
} }

View File

@ -33,6 +33,7 @@ bool isStringMap(Object obj) => obj is Map;
bool isArray(Object obj) => obj is List; bool isArray(Object obj) => obj is List;
bool isPromise(Object obj) => obj is Future; bool isPromise(Object obj) => obj is Future;
bool isNumber(Object obj) => obj is num; bool isNumber(Object obj) => obj is num;
bool isBoolean(Object obj) => obj is bool;
bool isDate(Object obj) => obj is DateTime; bool isDate(Object obj) => obj is DateTime;
String stringify(obj) { String stringify(obj) {

View File

@ -124,6 +124,14 @@ export function isBlank(obj: any): boolean {
return obj === undefined || obj === null; 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 { export function isString(obj: any): boolean {
return typeof obj === "string"; return typeof obj === "string";
} }
@ -148,10 +156,6 @@ export function isArray(obj: any): boolean {
return Array.isArray(obj); return Array.isArray(obj);
} }
export function isNumber(obj): boolean {
return typeof obj === 'number';
}
export function isDate(obj): boolean { export function isDate(obj): boolean {
return obj instanceof Date && !isNaN(obj.valueOf()); return obj instanceof Date && !isNaN(obj.valueOf());
} }

View File

@ -18,7 +18,9 @@ import {
CompileTemplateMetadata, CompileTemplateMetadata,
CompileProviderMetadata, CompileProviderMetadata,
CompileDiDependencyMetadata, CompileDiDependencyMetadata,
CompileQueryMetadata CompileQueryMetadata,
CompileIdentifierMetadata,
CompileFactoryMetadata
} from 'angular2/src/compiler/directive_metadata'; } from 'angular2/src/compiler/directive_metadata';
import {ViewEncapsulation} from 'angular2/src/core/metadata/view'; import {ViewEncapsulation} from 'angular2/src/core/metadata/view';
import {ChangeDetectionStrategy} from 'angular2/src/core/change_detection'; import {ChangeDetectionStrategy} from 'angular2/src/core/change_detection';
@ -31,25 +33,21 @@ export function main() {
var fullDirectiveMeta: CompileDirectiveMetadata; var fullDirectiveMeta: CompileDirectiveMetadata;
beforeEach(() => { beforeEach(() => {
fullTypeMeta = new CompileTypeMetadata({ var diDep = new CompileDiDependencyMetadata({
name: 'SomeType', isAttribute: true,
moduleUrl: 'someUrl', isSelf: true,
isHost: true, isHost: true,
diDeps: [ isSkipSelf: true,
new CompileDiDependencyMetadata({ isOptional: true,
isAttribute: true, token: 'someToken',
isSelf: true, query: new CompileQueryMetadata(
isHost: true, {selectors: ['one'], descendants: true, first: true, propertyName: 'one'}),
isSkipSelf: true, viewQuery: new CompileQueryMetadata(
isOptional: true, {selectors: ['one'], descendants: true, first: true, propertyName: 'one'})
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({ fullTemplateMeta = new CompileTemplateMetadata({
encapsulation: ViewEncapsulation.Emulated, encapsulation: ViewEncapsulation.Emulated,
template: '<a></a>', template: '<a></a>',
@ -69,11 +67,49 @@ export function main() {
outputs: ['someEvent'], outputs: ['someEvent'],
host: {'(event1)': 'handler1', '[prop1]': 'expr1', 'attr1': 'attrValue2'}, host: {'(event1)': 'handler1', '[prop1]': 'expr1', 'attr1': 'attrValue2'},
lifecycleHooks: [LifecycleHooks.OnChanges], 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'})
]
}); });
}); });
describe('CompileIdentifierMetadata', () => {
it('should serialize with full data', () => {
let full = new CompileIdentifierMetadata(
{name: 'name', moduleUrl: 'module', constConstructor: true, value: ['one', ['two']]});
expect(CompileIdentifierMetadata.fromJson(full.toJson())).toEqual(full);
});
it('should serialize with no data', () => {
let empty = new CompileIdentifierMetadata();
expect(CompileIdentifierMetadata.fromJson(empty.toJson())).toEqual(empty);
});
});
describe('DirectiveMetadata', () => { describe('DirectiveMetadata', () => {
it('should serialize with full data', () => { it('should serialize with full data', () => {
expect(CompileDirectiveMetadata.fromJson(fullDirectiveMeta.toJson())) expect(CompileDirectiveMetadata.fromJson(fullDirectiveMeta.toJson()))

View File

@ -166,11 +166,11 @@ abstract class NgDepsWriterMixin
..writeln('void ${SETUP_METHOD_NAME}() {') ..writeln('void ${SETUP_METHOD_NAME}() {')
..writeln('if (_visited) return; _visited = true;'); ..writeln('if (_visited) return; _visited = true;');
final needsReceiver = (model.reflectables != null && final needsReceiver =
model.reflectables.isNotEmpty) || (model.reflectables != null && model.reflectables.isNotEmpty) ||
(model.getters != null && model.getters.isNotEmpty) || (model.getters != null && model.getters.isNotEmpty) ||
(model.setters != null && model.setters.isNotEmpty) || (model.setters != null && model.setters.isNotEmpty) ||
(model.methods != null && model.methods.isNotEmpty); (model.methods != null && model.methods.isNotEmpty);
if (needsReceiver) { if (needsReceiver) {
buffer.writeln('$REFLECTOR_PREFIX.$REFLECTOR_VAR_NAME'); buffer.writeln('$REFLECTOR_PREFIX.$REFLECTOR_VAR_NAME');

View File

@ -87,6 +87,50 @@ class PrintLogger extends SinkLogger {
PrintLogger() : super(stderr); PrintLogger() : super(stderr);
} }
/// Wraps the logger and prints the messages
/// only if they have not been printed before
class DeduppingLogger implements TransformLogger {
Set<String> _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 { class PrintLoggerError extends Error {
final String message; final String message;
final AssetId asset; final AssetId asset;

View File

@ -23,8 +23,8 @@ class NamedParameter extends GeneratedMessage {
static PbList<NamedParameter> createRepeated() => static PbList<NamedParameter> createRepeated() =>
new PbList<NamedParameter>(); new PbList<NamedParameter>();
static NamedParameter getDefault() { static NamedParameter getDefault() {
if (_defaultInstance == null) _defaultInstance = if (_defaultInstance == null)
new _ReadonlyNamedParameter(); _defaultInstance = new _ReadonlyNamedParameter();
return _defaultInstance; return _defaultInstance;
} }
@ -78,8 +78,8 @@ class AnnotationModel extends GeneratedMessage {
static PbList<AnnotationModel> createRepeated() => static PbList<AnnotationModel> createRepeated() =>
new PbList<AnnotationModel>(); new PbList<AnnotationModel>();
static AnnotationModel getDefault() { static AnnotationModel getDefault() {
if (_defaultInstance == null) _defaultInstance = if (_defaultInstance == null)
new _ReadonlyAnnotationModel(); _defaultInstance = new _ReadonlyAnnotationModel();
return _defaultInstance; return _defaultInstance;
} }

View File

@ -26,8 +26,8 @@ class ParameterModel extends GeneratedMessage {
static PbList<ParameterModel> createRepeated() => static PbList<ParameterModel> createRepeated() =>
new PbList<ParameterModel>(); new PbList<ParameterModel>();
static ParameterModel getDefault() { static ParameterModel getDefault() {
if (_defaultInstance == null) _defaultInstance = if (_defaultInstance == null)
new _ReadonlyParameterModel(); _defaultInstance = new _ReadonlyParameterModel();
return _defaultInstance; return _defaultInstance;
} }

View File

@ -27,15 +27,15 @@ class PropertyMetadataModel extends GeneratedMessage {
static PbList<PropertyMetadataModel> createRepeated() => static PbList<PropertyMetadataModel> createRepeated() =>
new PbList<PropertyMetadataModel>(); new PbList<PropertyMetadataModel>();
static PropertyMetadataModel getDefault() { static PropertyMetadataModel getDefault() {
if (_defaultInstance == null) _defaultInstance = if (_defaultInstance == null)
new _ReadonlyPropertyMetadataModel(); _defaultInstance = new _ReadonlyPropertyMetadataModel();
return _defaultInstance; return _defaultInstance;
} }
static PropertyMetadataModel _defaultInstance; static PropertyMetadataModel _defaultInstance;
static void $checkItem(PropertyMetadataModel v) { static void $checkItem(PropertyMetadataModel v) {
if (v if (v is! PropertyMetadataModel)
is! PropertyMetadataModel) checkItemFailed(v, 'PropertyMetadataModel'); checkItemFailed(v, 'PropertyMetadataModel');
} }
String get name => $_get(0, 1, ''); String get name => $_get(0, 1, '');
@ -70,8 +70,8 @@ class PrefixedType extends GeneratedMessage {
static PrefixedType create() => new PrefixedType(); static PrefixedType create() => new PrefixedType();
static PbList<PrefixedType> createRepeated() => new PbList<PrefixedType>(); static PbList<PrefixedType> createRepeated() => new PbList<PrefixedType>();
static PrefixedType getDefault() { static PrefixedType getDefault() {
if (_defaultInstance == null) _defaultInstance = if (_defaultInstance == null)
new _ReadonlyPrefixedType(); _defaultInstance = new _ReadonlyPrefixedType();
return _defaultInstance; return _defaultInstance;
} }
@ -130,8 +130,8 @@ class ReflectionInfoModel extends GeneratedMessage {
static PbList<ReflectionInfoModel> createRepeated() => static PbList<ReflectionInfoModel> createRepeated() =>
new PbList<ReflectionInfoModel>(); new PbList<ReflectionInfoModel>();
static ReflectionInfoModel getDefault() { static ReflectionInfoModel getDefault() {
if (_defaultInstance == null) _defaultInstance = if (_defaultInstance == null)
new _ReadonlyReflectionInfoModel(); _defaultInstance = new _ReadonlyReflectionInfoModel();
return _defaultInstance; return _defaultInstance;
} }

View File

@ -28,10 +28,8 @@ import 'url_resolver.dart' show isDartCoreUri;
/// `.ng_meta.json` files as intermediate assets during the compilation process. /// `.ng_meta.json` files as intermediate assets during the compilation process.
class NgMeta { class NgMeta {
static const _ALIAS_VALUE = 'alias'; static const _ALIAS_VALUE = 'alias';
static const _KIND_KEY = 'kind';
static const _NG_DEPS_KEY = 'ngDeps'; static const _NG_DEPS_KEY = 'ngDeps';
static const _TYPE_VALUE = 'type'; static const _TYPE_VALUE = 'type';
static const _VALUE_KEY = 'value';
/// Metadata for each identifier /// Metadata for each identifier
/// Type: [CompileDirectiveMetadata]|[CompilePipeMetadata]|[CompileTypeMetadata]|[CompileIdentifierMetadata] /// Type: [CompileDirectiveMetadata]|[CompilePipeMetadata]|[CompileTypeMetadata]|[CompileIdentifierMetadata]
@ -43,11 +41,9 @@ class NgMeta {
// The NgDeps generated from // The NgDeps generated from
final NgDepsModel ngDeps; final NgDepsModel ngDeps;
bool definesAlias;
NgMeta({Map<String, List<String>> aliases, NgMeta({Map<String, List<String>> aliases,
Map<String, dynamic> identifiers, Map<String, dynamic> identifiers,
this.ngDeps: null, this.definesAlias: false}) this.ngDeps: null})
:this.aliases = aliases != null ? aliases : {}, :this.aliases = aliases != null ? aliases : {},
this.identifiers = identifiers != null ? identifiers : {}; this.identifiers = identifiers != null ? identifiers : {};
@ -72,48 +68,43 @@ class NgMeta {
bool get isEmpty => identifiers.isEmpty && aliases.isEmpty && isNgDepsEmpty; bool get isEmpty => identifiers.isEmpty && aliases.isEmpty && isNgDepsEmpty;
List<String> get linkingUris { bool get needsResolution {
final r = ngDeps.exports.map((r) => r.uri).toList(); return identifiers.values.any((id) =>
if (definesAlias) { id is CompileDirectiveMetadata || id is CompilePipeMetadata || id is CompileTypeMetadata || id is CompileFactoryMetadata
r.addAll(ngDeps.imports.map((r) => r.uri)); || (id is CompileIdentifierMetadata && id.value != null));
}
return r;
} }
/// Parse from the serialized form produced by [toJson]. /// Parse from the serialized form produced by [toJson].
factory NgMeta.fromJson(Map json) { factory NgMeta.fromJson(Map json) {
var ngDeps = null; var ngDeps = null;
final aliases = {};
final identifiers = {}; if (json.containsKey(_NG_DEPS_KEY)) {
var definesAlias = false; var ngDepsJsonMap = json[_NG_DEPS_KEY];
for (var key in json.keys) { if (ngDepsJsonMap != null) {
if (key == _NG_DEPS_KEY) {
var ngDepsJsonMap = json[key];
if (ngDepsJsonMap == null) continue;
if (ngDepsJsonMap is! Map) { if (ngDepsJsonMap is! Map) {
log.warning( log.warning(
'Unexpected value $ngDepsJsonMap for key "$key" in NgMeta.'); 'Unexpected value $ngDepsJsonMap for key "$_NG_DEPS_KEY" in NgMeta.');
continue; } else {
ngDeps = new NgDepsModel()..mergeFromJsonMap(ngDepsJsonMap);
} }
ngDeps = new NgDepsModel() }
..mergeFromJsonMap(ngDepsJsonMap); }
} else if (key == 'definesAlias') {
definesAlias = json[key];
} else { final aliases = json[_ALIAS_VALUE] != null ? json[_ALIAS_VALUE] : {};
var entry = json[key];
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) { if (entry is! Map) {
log.warning('Unexpected value $entry for key "$key" in NgMeta.'); log.warning('Unexpected value $entry for key "$key" in NgMeta.');
continue; continue;
} }
if (entry[_KIND_KEY] == _TYPE_VALUE) { identifiers[key] = metadataFromJson(entry);
identifiers[key] = CompileMetadataWithIdentifier.fromJson(entry[_VALUE_KEY]);
} else if (entry[_KIND_KEY] == _ALIAS_VALUE) {
aliases[key] = entry[_VALUE_KEY];
}
} }
} }
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. /// Serialized representation of this instance.
@ -121,16 +112,11 @@ class NgMeta {
var result = {}; var result = {};
result[_NG_DEPS_KEY] = isNgDepsEmpty ? null : ngDeps.writeToJsonMap(); result[_NG_DEPS_KEY] = isNgDepsEmpty ? null : ngDeps.writeToJsonMap();
result[_TYPE_VALUE] = {};
identifiers.forEach((k, v) { identifiers.forEach((k, v) {
result[k] = {_KIND_KEY: _TYPE_VALUE, _VALUE_KEY: v.toJson()}; result[_TYPE_VALUE][k] = v.toJson();
}); });
result[_ALIAS_VALUE] = aliases;
aliases.forEach((k, v) {
result[k] = {_KIND_KEY: _ALIAS_VALUE, _VALUE_KEY: v};
});
result['definesAlias'] = definesAlias;
return result; return result;
} }
@ -150,10 +136,10 @@ class NgMeta {
log.error('Circular alias dependency for "$name". Cycle: ${newPath.join(' -> ')}.'); log.error('Circular alias dependency for "$name". Cycle: ${newPath.join(' -> ')}.');
return; return;
} }
if (identifiers.containsKey(name)) { if (aliases.containsKey(name)) {
result.add(identifiers[name]);
} else if (aliases.containsKey(name)) {
aliases[name].forEach((n) => helper(n, newPath)); aliases[name].forEach((n) => helper(n, newPath));
} else if (identifiers.containsKey(name)) {
result.add(identifiers[name]);
} else { } else {
log.error('Unknown alias: ${newPath.join(' -> ')}. Make sure you export ${name} from the file where ${path.last} is defined.'); log.error('Unknown alias: ${newPath.join(' -> ')}. Make sure you export ${name} from the file where ${path.last} is defined.');
} }

View File

@ -13,6 +13,7 @@ const REFLECT_PROPERTIES_AS_ATTRIBUTES = 'reflect_properties_as_attributes';
const PLATFORM_DIRECTIVES = 'platform_directives'; const PLATFORM_DIRECTIVES = 'platform_directives';
const PLATFORM_PIPES = 'platform_pipes'; const PLATFORM_PIPES = 'platform_pipes';
const RESOLVED_IDENTIFIERS = 'resolved_identifiers'; const RESOLVED_IDENTIFIERS = 'resolved_identifiers';
const ERROR_ON_MISSING_IDENTIFIERS = 'error_on_missing_identifiers';
const INIT_REFLECTOR_PARAM = 'init_reflector'; const INIT_REFLECTOR_PARAM = 'init_reflector';
const INLINE_VIEWS_PARAM = 'inline_views'; const INLINE_VIEWS_PARAM = 'inline_views';
const MIRROR_MODE_PARAM = 'mirror_mode'; const MIRROR_MODE_PARAM = 'mirror_mode';
@ -60,6 +61,9 @@ class TransformerOptions {
/// A map of identifier/asset pairs used when resolving identifiers. /// A map of identifier/asset pairs used when resolving identifiers.
final Map<String, String> resolvedIdentifiers; final Map<String, String> resolvedIdentifiers;
/// when set ot false, the transformer will warn about missing identifiers but not error
final bool errorOnMissingIdentifiers;
/// Whether to format generated code. /// Whether to format generated code.
/// Code that is only modified will never be formatted because doing so may /// Code that is only modified will never be formatted because doing so may
/// invalidate the source maps generated by `dart2js` and/or other tools. /// invalidate the source maps generated by `dart2js` and/or other tools.
@ -108,6 +112,7 @@ class TransformerOptions {
this.platformDirectives, this.platformDirectives,
this.platformPipes, this.platformPipes,
this.resolvedIdentifiers, this.resolvedIdentifiers,
this.errorOnMissingIdentifiers,
this.translations, this.translations,
this.reflectPropertiesAsAttributes}); this.reflectPropertiesAsAttributes});
@ -120,6 +125,7 @@ class TransformerOptions {
bool genChangeDetectionDebugInfo: false, bool genChangeDetectionDebugInfo: false,
bool genCompiledTemplates: true, bool genCompiledTemplates: true,
bool reflectPropertiesAsAttributes: false, bool reflectPropertiesAsAttributes: false,
bool errorOnMissingIdentifiers: true,
List<String> platformDirectives, List<String> platformDirectives,
List<String> platformPipes, List<String> platformPipes,
Map<String, String> resolvedIdentifiers, Map<String, String> resolvedIdentifiers,
@ -139,6 +145,7 @@ class TransformerOptions {
platformDirectives: platformDirectives, platformDirectives: platformDirectives,
platformPipes: platformPipes, platformPipes: platformPipes,
resolvedIdentifiers: resolvedIdentifiers, resolvedIdentifiers: resolvedIdentifiers,
errorOnMissingIdentifiers: errorOnMissingIdentifiers,
inlineViews: inlineViews, inlineViews: inlineViews,
lazyTransformers: lazyTransformers, lazyTransformers: lazyTransformers,
translations: translations, translations: translations,

View File

@ -19,6 +19,7 @@ TransformerOptions parseBarbackSettings(BarbackSettings settings) {
var platformDirectives = _readStringList(config, PLATFORM_DIRECTIVES); var platformDirectives = _readStringList(config, PLATFORM_DIRECTIVES);
var platformPipes = _readStringList(config, PLATFORM_PIPES); var platformPipes = _readStringList(config, PLATFORM_PIPES);
var resolvedIdentifiers = config[RESOLVED_IDENTIFIERS]; var resolvedIdentifiers = config[RESOLVED_IDENTIFIERS];
var errorOnMissingIdentifiers = _readBool(config, ERROR_ON_MISSING_IDENTIFIERS, defaultValue: true);
var formatCode = _readBool(config, FORMAT_CODE_PARAM, defaultValue: false); var formatCode = _readBool(config, FORMAT_CODE_PARAM, defaultValue: false);
String mirrorModeVal = String mirrorModeVal =
config.containsKey(MIRROR_MODE_PARAM) ? config[MIRROR_MODE_PARAM] : ''; config.containsKey(MIRROR_MODE_PARAM) ? config[MIRROR_MODE_PARAM] : '';
@ -44,6 +45,7 @@ TransformerOptions parseBarbackSettings(BarbackSettings settings) {
platformDirectives: platformDirectives, platformDirectives: platformDirectives,
platformPipes: platformPipes, platformPipes: platformPipes,
resolvedIdentifiers: resolvedIdentifiers, resolvedIdentifiers: resolvedIdentifiers,
errorOnMissingIdentifiers: errorOnMissingIdentifiers,
inlineViews: _readBool(config, INLINE_VIEWS_PARAM, defaultValue: false), inlineViews: _readBool(config, INLINE_VIEWS_PARAM, defaultValue: false),
lazyTransformers: lazyTransformers:
_readBool(config, LAZY_TRANSFORMERS, defaultValue: false), _readBool(config, LAZY_TRANSFORMERS, defaultValue: false),

View File

@ -22,10 +22,11 @@ class TypeMetadataReader {
final _DirectiveMetadataVisitor _directiveVisitor; final _DirectiveMetadataVisitor _directiveVisitor;
final _PipeMetadataVisitor _pipeVisitor; final _PipeMetadataVisitor _pipeVisitor;
final _CompileTypeMetadataVisitor _typeVisitor; final _CompileTypeMetadataVisitor _typeVisitor;
final _CompileFactoryMetadataVisitor _factoryVisitor;
final TemplateCompiler _templateCompiler; final TemplateCompiler _templateCompiler;
TypeMetadataReader._( TypeMetadataReader._(this._directiveVisitor, this._pipeVisitor,
this._directiveVisitor, this._pipeVisitor, this._templateCompiler, this._typeVisitor); this._templateCompiler, this._typeVisitor, this._factoryVisitor);
/// Accepts an [AnnotationMatcher] which tests that an [Annotation] /// Accepts an [AnnotationMatcher] which tests that an [Annotation]
/// is a [Directive], [Component], or [View]. /// is a [Directive], [Component], or [View].
@ -33,12 +34,13 @@ class TypeMetadataReader {
InterfaceMatcher interfaceMatcher, TemplateCompiler templateCompiler) { InterfaceMatcher interfaceMatcher, TemplateCompiler templateCompiler) {
var lifecycleVisitor = new _LifecycleHookVisitor(interfaceMatcher); var lifecycleVisitor = new _LifecycleHookVisitor(interfaceMatcher);
var typeVisitor = new _CompileTypeMetadataVisitor(annotationMatcher); var typeVisitor = new _CompileTypeMetadataVisitor(annotationMatcher);
var directiveVisitor = var directiveVisitor = new _DirectiveMetadataVisitor(
new _DirectiveMetadataVisitor(annotationMatcher, lifecycleVisitor, typeVisitor); annotationMatcher, lifecycleVisitor, typeVisitor);
var pipeVisitor = new _PipeMetadataVisitor(annotationMatcher); var pipeVisitor = new _PipeMetadataVisitor(annotationMatcher);
var factoryVisitor = new _CompileFactoryMetadataVisitor(annotationMatcher);
return new TypeMetadataReader._( return new TypeMetadataReader._(
directiveVisitor, pipeVisitor, templateCompiler, typeVisitor); directiveVisitor, pipeVisitor, templateCompiler, typeVisitor, factoryVisitor);
} }
/// Reads *un-normalized* [CompileDirectiveMetadata]/[CompilePipeMetadata] from the /// Reads *un-normalized* [CompileDirectiveMetadata]/[CompilePipeMetadata] from the
@ -71,6 +73,47 @@ class TypeMetadataReader {
return new Future.value(null); return new Future.value(null);
} }
} }
Future<dynamic> 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`, /// Evaluates the [Map] represented by `expression` and adds all `key`,
@ -134,7 +177,6 @@ bool _expressionToBool(Expression node, String nodeDescription) {
class _CompileTypeMetadataVisitor extends Object class _CompileTypeMetadataVisitor extends Object
with RecursiveAstVisitor<CompileTypeMetadata> { with RecursiveAstVisitor<CompileTypeMetadata> {
bool _isInjectable = false; bool _isInjectable = false;
CompileTypeMetadata _type; CompileTypeMetadata _type;
AssetId _assetId; AssetId _assetId;
@ -166,58 +208,79 @@ class _CompileTypeMetadataVisitor extends Object
@override @override
Object visitClassDeclaration(ClassDeclaration node) { Object visitClassDeclaration(ClassDeclaration node) {
node.metadata.accept(this); node.metadata.accept(this);
final fieldTypes = _readFields(node);
if (this._isInjectable) { if (this._isInjectable) {
final constructor = node.getConstructor(null);
final diDeps = constructor == null ? [] :
_getCompileDiDependencyMetadata(constructor.parameters, fieldTypes);
_type = new CompileTypeMetadata( _type = new CompileTypeMetadata(
moduleUrl: toAssetUri(_assetId), moduleUrl: toAssetUri(_assetId),
name: node.name.toString(), name: node.name.toString(),
diDeps: _getCompileDiDependencyMetadata(node), diDeps: diDeps,
runtime: null // Intentionally `null`, cannot be provided here.
);
}
return null;
}
Map<String, TypeName> _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<CompileFactoryMetadata> {
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. runtime: null // Intentionally `null`, cannot be provided here.
); );
} }
return null; return null;
} }
List<CompileDiDependencyMetadata> _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 /// Visitor responsible for processing a [Directive] annotated
/// [ClassDeclaration] and creating a [CompileDirectiveMetadata] object. /// [ClassDeclaration] and creating a [CompileDirectiveMetadata] object.
class _DirectiveMetadataVisitor extends Object class _DirectiveMetadataVisitor extends Object
@ -233,7 +296,8 @@ class _DirectiveMetadataVisitor extends Object
/// The [AssetId] we are currently processing. /// The [AssetId] we are currently processing.
AssetId _assetId; AssetId _assetId;
_DirectiveMetadataVisitor(this._annotationMatcher, this._lifecycleVisitor, this._typeVisitor) { _DirectiveMetadataVisitor(
this._annotationMatcher, this._lifecycleVisitor, this._typeVisitor) {
reset(null); reset(null);
} }
@ -250,7 +314,10 @@ class _DirectiveMetadataVisitor extends Object
List<String> _inputs; List<String> _inputs;
List<String> _outputs; List<String> _outputs;
Map<String, String> _host; Map<String, String> _host;
List<CompileProviderMetadata> _providers; List _providers;
List _viewProviders;
List _queries;
List _viewQueries;
List<LifecycleHooks> _lifecycleHooks; List<LifecycleHooks> _lifecycleHooks;
CompileTemplateMetadata _cmpTemplate; CompileTemplateMetadata _cmpTemplate;
CompileTemplateMetadata _viewTemplate; CompileTemplateMetadata _viewTemplate;
@ -269,7 +336,10 @@ class _DirectiveMetadataVisitor extends Object
_inputs = <String>[]; _inputs = <String>[];
_outputs = <String>[]; _outputs = <String>[];
_host = <String, String>{}; _host = <String, String>{};
_providers = <CompileProviderMetadata>[]; _providers = [];
_viewProviders = [];
_queries = [];
_viewQueries = [];
_lifecycleHooks = null; _lifecycleHooks = null;
_cmpTemplate = null; _cmpTemplate = null;
_viewTemplate = null; _viewTemplate = null;
@ -292,6 +362,9 @@ class _DirectiveMetadataVisitor extends Object
outputs: _outputs, outputs: _outputs,
host: _host, host: _host,
providers: _providers, providers: _providers,
viewProviders: _viewProviders,
queries: _queries,
viewQueries: _viewQueries,
lifecycleHooks: _lifecycleHooks, lifecycleHooks: _lifecycleHooks,
template: _template); template: _template);
} }
@ -367,6 +440,19 @@ class _DirectiveMetadataVisitor extends Object
_host['[${variable.name}]'] = '${variable.name}'; _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; return null;
@ -383,6 +469,19 @@ class _DirectiveMetadataVisitor extends Object
_addPropertyToType(_inputs, node.name.toString(), meta); _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 (_isAnnotation(meta, 'HostListener')) {
if (meta.arguments.arguments.length == 0 || if (meta.arguments.arguments.length == 0 ||
meta.arguments.arguments.length > 2) { meta.arguments.arguments.length > 2) {
@ -416,38 +515,13 @@ class _DirectiveMetadataVisitor extends Object
} }
} }
void _populateProviders(Expression providerValues) { void _populateProviders(Expression providerValues, List providers) {
_checkMeta(); _checkMeta();
if (providerValues is ListLiteral) { if (providerValues is ListLiteral) {
final providers = providerValues.elements.map((el) { providers.addAll(_readProviders(providerValues));
if (el is PrefixedIdentifier || el is SimpleIdentifier) {
return new CompileProviderMetadata(token: _readIdentifier(el));
} else if (el is InstanceCreationExpression &&
(el.constructorName.toString() == "Provider" ||
el.constructorName.toString() == "Binding")
) {
final token = el.argumentList.arguments.first;
var useClass;
el.argumentList.arguments.skip(1).forEach((arg) {
if (arg.name.toString() == "useClass:") {
final id = _readIdentifier(arg.expression);
useClass = new CompileTypeMetadata(prefix: id.prefix, name: id.name);
}
});
return new CompileProviderMetadata(token: _readIdentifier(token), useClass: useClass);
} else {
throw new ArgumentError(
'Incorrect value. Expected a Provider or a String, but got "${el}".');
}
});
_providers.addAll(providers);
} else { } else {
_providers.add(new CompileProviderMetadata(token: _readIdentifier(providerValues))); providers.add(_readIdentifier(providerValues));
} }
} }
@ -539,7 +613,16 @@ class _DirectiveMetadataVisitor extends Object
_populateEvents(node.expression); _populateEvents(node.expression);
break; break;
case 'providers': 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; break;
} }
return null; return null;
@ -607,27 +690,30 @@ class _LifecycleHookVisitor extends SimpleAstVisitor<List<LifecycleHooks>> {
List<LifecycleHooks> visitImplementsClause(ImplementsClause node) { List<LifecycleHooks> visitImplementsClause(ImplementsClause node) {
if (node == null || node.interfaces == null) return const []; if (node == null || node.interfaces == null) return const [];
return node.interfaces.map((TypeName ifaceTypeName) { return node.interfaces
var id = ifaceTypeName.name; .map((TypeName ifaceTypeName) {
if (_ifaceMatcher.isAfterContentChecked(id, _assetId)) { var id = ifaceTypeName.name;
return LifecycleHooks.AfterContentChecked; if (_ifaceMatcher.isAfterContentChecked(id, _assetId)) {
} else if (_ifaceMatcher.isAfterContentInit(id, _assetId)) { return LifecycleHooks.AfterContentChecked;
return LifecycleHooks.AfterContentInit; } else if (_ifaceMatcher.isAfterContentInit(id, _assetId)) {
} else if (_ifaceMatcher.isAfterViewChecked(id, _assetId)) { return LifecycleHooks.AfterContentInit;
return LifecycleHooks.AfterViewChecked; } else if (_ifaceMatcher.isAfterViewChecked(id, _assetId)) {
} else if (_ifaceMatcher.isAfterViewInit(id, _assetId)) { return LifecycleHooks.AfterViewChecked;
return LifecycleHooks.AfterViewInit; } else if (_ifaceMatcher.isAfterViewInit(id, _assetId)) {
} else if (_ifaceMatcher.isDoCheck(id, _assetId)) { return LifecycleHooks.AfterViewInit;
return LifecycleHooks.DoCheck; } else if (_ifaceMatcher.isDoCheck(id, _assetId)) {
} else if (_ifaceMatcher.isOnChange(id, _assetId)) { return LifecycleHooks.DoCheck;
return LifecycleHooks.OnChanges; } else if (_ifaceMatcher.isOnChange(id, _assetId)) {
} else if (_ifaceMatcher.isOnDestroy(id, _assetId)) { return LifecycleHooks.OnChanges;
return LifecycleHooks.OnDestroy; } else if (_ifaceMatcher.isOnDestroy(id, _assetId)) {
} else if (_ifaceMatcher.isOnInit(id, _assetId)) { return LifecycleHooks.OnDestroy;
return LifecycleHooks.OnInit; } else if (_ifaceMatcher.isOnInit(id, _assetId)) {
} return LifecycleHooks.OnInit;
return null; }
}).where((e) => e != null).toList(growable: false); return null;
})
.where((e) => e != null)
.toList(growable: false);
} }
} }
@ -824,21 +910,199 @@ class _PipeMetadataVisitor extends Object with RecursiveAstVisitor<Object> {
} }
} }
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<CompileDiDependencyMetadata> _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<CompileDiDependencyMetadata> _getCompileDiDependencyMetadata(
FormalParameterList params, Map<String, TypeName> 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) { dynamic _readIdentifier(dynamic el) {
if (el is PrefixedIdentifier) { 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) { } else if (el is SimpleIdentifier) {
return new CompileIdentifierMetadata(name: '$el'); return new CompileIdentifierMetadata(name: '$el');
} else if (el is DoubleLiteral ||
} else if (el is SimpleStringLiteral){ el is IntegerLiteral ||
el is SimpleStringLiteral ||
el is BooleanLiteral) {
return el.value; return el.value;
} else if (el is NullLiteral) {
} else if (el is InstanceCreationExpression){ return null;
return new CompileIdentifierMetadata(name: '${el.constructorName}', constConstructor: true); } else if (el is InstanceCreationExpression) {
return new CompileIdentifierMetadata(
name: '${el.constructorName}', constConstructor: true);
} else { } else {
throw new ArgumentError('Incorrect identifier "${el}".'); throw new ArgumentError('Incorrect identifier "${el}".');
} }
} }

View File

@ -13,8 +13,8 @@ class TransformerUrlResolver implements UrlResolver {
if (!uri.isAbsolute) { if (!uri.isAbsolute) {
if (baseUrl == null) throw new ArgumentError.notNull('baseUrl'); if (baseUrl == null) throw new ArgumentError.notNull('baseUrl');
if (baseUrl.isEmpty) throw new ArgumentError.value( if (baseUrl.isEmpty)
'(empty string)', 'baseUrl'); throw new ArgumentError.value('(empty string)', 'baseUrl');
uri = Uri.parse(baseUrl).resolveUri(uri); uri = Uri.parse(baseUrl).resolveUri(uri);
} }
@ -29,8 +29,8 @@ String toAssetUri(AssetId assetId) {
AssetId fromUri(String assetUri) { AssetId fromUri(String assetUri) {
if (assetUri == null) throw new ArgumentError.notNull('assetUri'); if (assetUri == null) throw new ArgumentError.notNull('assetUri');
if (assetUri.isEmpty) throw new ArgumentError.value( if (assetUri.isEmpty)
'(empty string)', 'assetUri'); throw new ArgumentError.value('(empty string)', 'assetUri');
var uri = toAssetScheme(Uri.parse(assetUri)); var uri = toAssetScheme(Uri.parse(assetUri));
return new AssetId( return new AssetId(
uri.pathSegments.first, uri.pathSegments.skip(1).join('/')); uri.pathSegments.first, uri.pathSegments.skip(1).join('/'));

View File

@ -64,20 +64,20 @@ Future<Map<String, String>> _processNgImports(NgDepsModel model,
return Future return Future
.wait( .wait(
importsAndExports.where(_isNotDartDirective).map((dynamic directive) { importsAndExports.where(_isNotDartDirective).map((dynamic directive) {
// Check whether the import or export generated summary NgMeta information. // Check whether the import or export generated summary NgMeta information.
final summaryJsonUri = final summaryJsonUri =
resolver.resolve(assetUri, toSummaryExtension(directive.uri)); resolver.resolve(assetUri, toSummaryExtension(directive.uri));
return reader.hasInput(fromUri(summaryJsonUri)).then((hasInput) { return reader.hasInput(fromUri(summaryJsonUri)).then((hasInput) {
if (hasInput) { if (hasInput) {
retVal[directive.uri] = summaryJsonUri; retVal[directive.uri] = summaryJsonUri;
} }
}, onError: (err, stack) { }, onError: (err, stack) {
log.warning( log.warning(
'Error while looking for $summaryJsonUri. ' 'Error while looking for $summaryJsonUri. '
'Message: $err\n' 'Message: $err\n'
'Stack: $stack', 'Stack: $stack',
asset: assetId); asset: assetId);
}); });
})) }))
.then((_) => retVal); .then((_) => retVal);
} }

View File

@ -3,6 +3,7 @@ library angular2.transform.directive_metadata_linker.linker;
import 'dart:async'; import 'dart:async';
import 'dart:convert'; 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/asset_reader.dart';
import 'package:angular2/src/transform/common/logging.dart'; import 'package:angular2/src/transform/common/logging.dart';
import 'package:angular2/src/transform/common/names.dart'; import 'package:angular2/src/transform/common/names.dart';
@ -12,36 +13,40 @@ import 'package:barback/barback.dart';
import 'ng_deps_linker.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. /// all files `export`ed from the original file.
/// ///
/// This includes entries for every `Directive`-annotated class and /// The returned NgMeta has all the identifiers resolved.
/// constants that match the directive-aliases pattern.
/// ///
/// There are entries for each of these which is visible from a file importing /// `summaryAssetId` - the unlinked asset id (source)
/// the original .dart file that produced `entryPoint`. That is, this includes /// `summaryAssetId` - the linked asset id (dest)
/// all `Directive` annotated public classes in that file, all `DirectiveAlias` /// `resolvedIdentifiers` - preresolved identifiers (e.g., Window)
/// annotated public variables, and any of those entries which are visible from /// `ngMetas` - in memory cache of linked ngMeta files
/// files which the .dart file `export`ed. Future<NgMeta> linkDirectiveMetadata(AssetReader reader, AssetId summaryAssetId,
/// AssetId metaAssetId, Map<String, String> resolvedIdentifiers,
/// Returns an empty [NgMeta] if there are no `Directive`-annotated classes or [bool errorOnMissingIdentifiers = true, Map<AssetId, NgMeta> ngMetas]) async {
/// `DirectiveAlias` annotated constants in `entryPoint`. if (ngMetas == null) ngMetas = {};
Future<NgMeta> linkDirectiveMetadata(
AssetReader reader, AssetId assetId) async { var ngMeta = await _readNgMeta(reader, summaryAssetId, ngMetas);
var ngMeta = await _readNgMeta(reader, assetId);
if (ngMeta == null || ngMeta.isEmpty) return null; if (ngMeta == null || ngMeta.isEmpty) return null;
await Future.wait([ await Future.wait([
linkNgDeps(ngMeta.ngDeps, reader, assetId, _urlResolver), linkNgDeps(ngMeta.ngDeps, reader, summaryAssetId, _urlResolver),
logElapsedAsync(() async { logElapsedAsync(() async {
await _linkRecursive(ngMeta, reader, assetId, new Set<String>()); final linker = new _Linker(reader, ngMetas, resolvedIdentifiers, errorOnMissingIdentifiers);
await linker.linkRecursive(ngMeta, metaAssetId, new Set<AssetId>());
return ngMeta; return ngMeta;
}, operationName: 'linkDirectiveMetadata', assetId: assetId) }, operationName: 'linkDirectiveMetadata', assetId: summaryAssetId)
]); ]);
return ngMeta; return ngMeta;
} }
Future<NgMeta> _readNgMeta(AssetReader reader, AssetId ngMetaAssetId) async { final _urlResolver = const TransformerUrlResolver();
Future<NgMeta> _readNgMeta(AssetReader reader, AssetId ngMetaAssetId,
Map<AssetId, NgMeta> ngMetas) async {
if (ngMetas.containsKey(ngMetaAssetId)) return ngMetas[ngMetaAssetId];
if (!(await reader.hasInput(ngMetaAssetId))) return null; if (!(await reader.hasInput(ngMetaAssetId))) return null;
var ngMetaJson = await reader.readAsString(ngMetaAssetId); var ngMetaJson = await reader.readAsString(ngMetaAssetId);
@ -50,34 +55,399 @@ Future<NgMeta> _readNgMeta(AssetReader reader, AssetId ngMetaAssetId) async {
return new NgMeta.fromJson(JSON.decode(ngMetaJson)); return new NgMeta.fromJson(JSON.decode(ngMetaJson));
} }
final _urlResolver = const TransformerUrlResolver(); class _Linker {
final AssetReader reader;
final Map<AssetId, NgMeta> ngMetas;
final Map<String, String> resolvedIdentifiers;
final bool errorOnMissingIdentifiers;
_Linker(this.reader, this.ngMetas, this.resolvedIdentifiers, this.errorOnMissingIdentifiers);
Future<NgMeta> linkRecursive(NgMeta ngMeta, AssetId assetId, Set<AssetId> 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<String> seen) async {
if (ngMeta == null ||
ngMeta.ngDeps == null ||
ngMeta.ngDeps.exports == null) {
return ngMeta; return ngMeta;
} }
var assetUri = toAssetUri(assetId);
return Future.wait(ngMeta.linkingUris Future _resolveDeps(NgMeta ngMeta, AssetId assetId, Set<AssetId> seen) async {
.where((uri) => !isDartCoreUri(uri)) final importsAndExports = [];
.map((uri) => if (ngMeta != null &&
_urlResolver.resolve(assetUri, toSummaryExtension(uri))) ngMeta.ngDeps != null &&
.where((uri) => !seen.contains(uri)) ngMeta.ngDeps.exports != null)
.map((uri) async { importsAndExports.addAll(ngMeta.ngDeps.exports);
seen.add(uri);
try { if (ngMeta != null &&
final exportAssetId = fromUri(uri); ngMeta.needsResolution &&
final exportNgMeta = await _readNgMeta(reader, exportAssetId); ngMeta.ngDeps != null &&
if (exportNgMeta != null) { ngMeta.ngDeps.imports != null)
await _linkRecursive(exportNgMeta, reader, exportAssetId, seen); importsAndExports
ngMeta.addAll(exportNgMeta); .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, errorOnMissingIdentifiers);
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<NgMeta> _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<NgMeta> _readMeta(AssetId metaAssetId) async {
final content = await _readNgMeta(reader, metaAssetId, ngMetas);
if (content != null) {
ngMetas[metaAssetId] = content;
}
return content;
}
Future<bool> _hasMeta(AssetId ngMetaAssetId) async {
return ngMetas.containsKey(ngMetaAssetId) ||
await reader.hasInput(ngMetaAssetId);
}
}
class _NgMetaIdentifierResolver {
final Map<String, String> resolvedIdentifiers;
final Map<AssetId, NgMeta> ngMetas;
final AssetReader reader;
final AssetId entryPoint;
final bool errorOnMissingIdentifiers;
_NgMetaIdentifierResolver(this.entryPoint, this.reader, this.ngMetas, this.resolvedIdentifiers, this.errorOnMissingIdentifiers);
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<CompileProviderMetadata> _resolveProviders(Map<String, NgMeta> 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<String, NgMeta> 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<String, NgMeta> 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<String, NgMeta> ngMetaMap, List queries, String neededBy) {
queries.forEach((q) {
q.selectors = q.selectors.map((s) => _resolveIdentifier(ngMetaMap, neededBy, s)).toList();
});
}
void _resolveProvider(Map<String, NgMeta> 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<String, NgMeta> ngMetaMap,
String neededBy, List<CompileDiDependencyMetadata> 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<String, NgMeta> 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<String, NgMeta> 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 {
final message = 'Missing prefix "${prefix}" '
'needed by "${neededBy}" from metadata map';
if (errorOnMissingIdentifiers) {
log.error(message, asset: entryPoint);
} else {
log.warning(message, 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 {
final message = 'Missing identifier "${id.name}" '
'needed by "${neededBy}" from metadata map';
if (errorOnMissingIdentifiers) {
log.error(message, asset: entryPoint);
} else {
log.warning(message, 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 == "Stream") {
return new CompileIdentifierMetadata(name: id.name, moduleUrl: 'dart:async');
} else if (id.name == "StreamController") {
return new CompileIdentifierMetadata(name: id.name, moduleUrl: 'dart:async');
} 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 if (id.name == "BatchingStrategy") {
return new CompileIdentifierMetadata(name: id.name, moduleUrl: 'asset:streamy/lib/src/extra/request_handler/batching.dart');
} else if (id.name == "ProxyClient") {
return new CompileIdentifierMetadata(name: id.name, moduleUrl: 'asset:streamy/lib/src/extra/request_handler/proxy.dart');
} else if (id.name == "StreamyHttpService") {
return new CompileIdentifierMetadata(name: id.name, moduleUrl: 'asset:streamy/lib/src/toolbox/http.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<Map<String, NgMeta>> _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;
}
} }

View File

@ -8,6 +8,8 @@ import 'package:barback/barback.dart';
import 'package:angular2/src/transform/common/asset_reader.dart'; import 'package:angular2/src/transform/common/asset_reader.dart';
import 'package:angular2/src/transform/common/names.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/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'; import 'ng_meta_linker.dart';
@ -33,6 +35,12 @@ import 'ng_meta_linker.dart';
class DirectiveMetadataLinker extends Transformer implements LazyTransformer { class DirectiveMetadataLinker extends Transformer implements LazyTransformer {
final _encoder = const JsonEncoder.withIndent(' '); final _encoder = const JsonEncoder.withIndent(' ');
final TransformerOptions options;
final Map ngMetasCache = {};
final Set<String> errorMessages = new Set<String>();
DirectiveMetadataLinker(this.options);
@override @override
bool isPrimary(AssetId id) => id.path.endsWith(SUMMARY_META_EXTENSION); bool isPrimary(AssetId id) => id.path.endsWith(SUMMARY_META_EXTENSION);
@ -47,7 +55,12 @@ class DirectiveMetadataLinker extends Transformer implements LazyTransformer {
var primaryId = transform.primaryInput.id; var primaryId = transform.primaryInput.id;
return linkDirectiveMetadata( return linkDirectiveMetadata(
new AssetReader.fromTransform(transform), primaryId).then((ngMeta) { new AssetReader.fromTransform(transform),
primaryId,
_ngLinkedAssetId(primaryId),
options.resolvedIdentifiers,
options.errorOnMissingIdentifiers,
ngMetasCache).then((ngMeta) {
if (ngMeta != null) { if (ngMeta != null) {
final outputId = _ngLinkedAssetId(primaryId); final outputId = _ngLinkedAssetId(primaryId);
// Not outputting an asset could confuse barback. // Not outputting an asset could confuse barback.
@ -55,7 +68,7 @@ class DirectiveMetadataLinker extends Transformer implements LazyTransformer {
transform.addOutput(new Asset.fromString(outputId, output)); transform.addOutput(new Asset.fromString(outputId, output));
} }
}); });
}, log: transform.logger); }, log: new DeduppingLogger(transform.logger, errorMessages));
} }
} }

View File

@ -5,7 +5,8 @@ import 'dart:async';
import 'package:analyzer/analyzer.dart'; import 'package:analyzer/analyzer.dart';
import 'package:barback/barback.dart' show AssetId; 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/compiler/template_compiler.dart';
import 'package:angular2/src/transform/common/annotation_matcher.dart'; import 'package:angular2/src/transform/common/annotation_matcher.dart';
import 'package:angular2/src/transform/common/asset_reader.dart'; import 'package:angular2/src/transform/common/asset_reader.dart';
@ -90,13 +91,15 @@ class _NgMetaVisitor extends Object with SimpleAstVisitor<Object> {
@override @override
Object visitClassDeclaration(ClassDeclaration node) { Object visitClassDeclaration(ClassDeclaration node) {
_normalizations.add( _normalizations.add(_reader
_reader.readTypeMetadata(node, assetId).then((compileMetadataWithIdentifier) { .readTypeMetadata(node, assetId)
if (compileMetadataWithIdentifier!= null) { .then((compileMetadataWithIdentifier) {
if (compileMetadataWithIdentifier != null) {
ngMeta.identifiers[compileMetadataWithIdentifier.identifier.name] = ngMeta.identifiers[compileMetadataWithIdentifier.identifier.name] =
compileMetadataWithIdentifier; compileMetadataWithIdentifier;
} else { } 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) { }).catchError((err) {
log.error('ERROR: $err', asset: assetId); log.error('ERROR: $err', asset: assetId);
@ -114,8 +117,8 @@ class _NgMetaVisitor extends Object with SimpleAstVisitor<Object> {
// angular/angular#1747 and angular/ts2dart#249 for context). // angular/angular#1747 and angular/ts2dart#249 for context).
outer: for (var variable in node.variables.variables) { outer: for (var variable in node.variables.variables) {
if (variable.isConst) { if (variable.isConst) {
ngMeta.identifiers[variable.name.name] = final id = _reader.readIdentifierMetadata(variable, assetId);
new CompileIdentifierMetadata(name: variable.name.name, moduleUrl: toAssetUri(assetId)); ngMeta.identifiers[variable.name.name] = id;
} }
var initializer = variable.initializer; var initializer = variable.initializer;
@ -127,7 +130,6 @@ class _NgMetaVisitor extends Object with SimpleAstVisitor<Object> {
if (exp is! SimpleIdentifier) continue outer; if (exp is! SimpleIdentifier) continue outer;
otherNames.add(exp.name); otherNames.add(exp.name);
} }
ngMeta.definesAlias = true;
ngMeta.aliases[variable.name.name] = otherNames; ngMeta.aliases[variable.name.name] = otherNames;
} }
} }
@ -136,15 +138,33 @@ class _NgMetaVisitor extends Object with SimpleAstVisitor<Object> {
@override @override
Object visitFunctionTypeAlias(FunctionTypeAlias node) { Object visitFunctionTypeAlias(FunctionTypeAlias node) {
ngMeta.identifiers[node.name.name] = ngMeta.identifiers[node.name.name] = new CompileIdentifierMetadata(
new CompileIdentifierMetadata(name: node.name.name, moduleUrl: toAssetUri(assetId)); 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; return null;
} }
@override @override
Object visitEnumDeclaration(EnumDeclaration node) { Object visitEnumDeclaration(EnumDeclaration node) {
ngMeta.identifiers[node.name.name] = ngMeta.identifiers[node.name.name] = new CompileIdentifierMetadata(
new CompileIdentifierMetadata(name: node.name.name, moduleUrl: toAssetUri(assetId)); name: node.name.name, moduleUrl: toAssetUri(assetId));
return null; return null;
} }
} }

View File

@ -123,8 +123,8 @@ class _CodegenState {
var names = new CodegenNameUtil( var names = new CodegenNameUtil(
protoRecords, eventBindings, def.directiveRecords, '$genPrefix$_UTIL'); protoRecords, eventBindings, def.directiveRecords, '$genPrefix$_UTIL');
var logic = new CodegenLogicUtil( var logic =
names, '$genPrefix$_UTIL', '$genPrefix$_STATE'); new CodegenLogicUtil(names, '$genPrefix$_UTIL', '$genPrefix$_STATE');
return new _CodegenState._( return new _CodegenState._(
genPrefix, genPrefix,
def.id, def.id,

View File

@ -29,20 +29,18 @@ Future<CompileDataResults> createCompileData(
AssetReader reader, AssetReader reader,
AssetId assetId, AssetId assetId,
List<String> platformDirectives, List<String> platformDirectives,
List<String> platformPipes, List<String> platformPipes) async {
Map<String, String> resolvedIdentifiers
) async {
return logElapsedAsync(() async { return logElapsedAsync(() async {
final creator = await _CompileDataCreator.create( final creator = await _CompileDataCreator.create(
reader, assetId, platformDirectives, platformPipes, resolvedIdentifiers); reader, assetId, platformDirectives, platformPipes);
return creator != null ? creator.createCompileData() : null; return creator != null ? creator.createCompileData() : null;
}, operationName: 'createCompileData', assetId: assetId); }, operationName: 'createCompileData', assetId: assetId);
} }
class CompileDataResults { class CompileDataResults {
final NgMeta ngMeta; final NgMeta ngMeta;
final Map<ReflectionInfoModel, final Map<ReflectionInfoModel, NormalizedComponentWithViewDirectives>
NormalizedComponentWithViewDirectives> viewDefinitions; viewDefinitions;
CompileDataResults._(this.ngMeta, this.viewDefinitions); CompileDataResults._(this.ngMeta, this.viewDefinitions);
} }
@ -55,20 +53,19 @@ class _CompileDataCreator {
final NgMeta ngMeta; final NgMeta ngMeta;
final List<String> platformDirectives; final List<String> platformDirectives;
final List<String> platformPipes; final List<String> platformPipes;
final Map<String, String> resolvedIdentifiers;
_CompileDataCreator(this.reader, this.entryPoint, this.ngMeta, _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, static Future<_CompileDataCreator> create(AssetReader reader, AssetId assetId,
List<String> platformDirectives, List<String> platformPipes, Map<String, String> resolvedIdentifiers) async { List<String> platformDirectives, List<String> platformPipes) async {
if (!(await reader.hasInput(assetId))) return null; if (!(await reader.hasInput(assetId))) return null;
final json = await reader.readAsString(assetId); final json = await reader.readAsString(assetId);
if (json == null || json.isEmpty) return null; if (json == null || json.isEmpty) return null;
final ngMeta = new NgMeta.fromJson(JSON.decode(json)); final ngMeta = new NgMeta.fromJson(JSON.decode(json));
return new _CompileDataCreator( return new _CompileDataCreator(
reader, assetId, ngMeta, platformDirectives, platformPipes, resolvedIdentifiers); reader, assetId, ngMeta, platformDirectives, platformPipes);
} }
NgDepsModel get ngDeps => ngMeta.ngDeps; NgDepsModel get ngDeps => ngMeta.ngDeps;
@ -84,14 +81,15 @@ class _CompileDataCreator {
} }
return false; return false;
}); });
if (!hasTemplate) return new CompileDataResults._(ngMeta, const {}); if (!hasTemplate) return new CompileDataResults._(ngMeta, const {});
final compileData = final compileData =
<ReflectionInfoModel, NormalizedComponentWithViewDirectives>{}; <ReflectionInfoModel, NormalizedComponentWithViewDirectives>{};
final ngMetaMap = await _extractNgMeta();
final platformDirectives = final platformDirectives =
await _readPlatformTypes(this.platformDirectives, 'directives'); await _readPlatformTypes(this.platformDirectives, 'directives');
final platformPipes = await _readPlatformTypes(this.platformPipes, 'pipes'); final platformPipes = await _readPlatformTypes(this.platformPipes, 'pipes');
final ngMetaMap = await _extractNgMeta();
for (var reflectable in ngDeps.reflectables) { for (var reflectable in ngDeps.reflectables) {
if (ngMeta.identifiers.containsKey(reflectable.name)) { if (ngMeta.identifiers.containsKey(reflectable.name)) {
@ -109,9 +107,6 @@ class _CompileDataCreator {
compileDatum.pipes compileDatum.pipes
.addAll(_resolveTypeMetadata(ngMetaMap, reflectable.pipes)); .addAll(_resolveTypeMetadata(ngMetaMap, reflectable.pipes));
compileData[reflectable] = compileDatum; compileData[reflectable] = compileDatum;
_resolveDiDependencyMetadata(ngMetaMap, compileDirectiveMetadata.type, compileDirectiveMetadata.type.diDeps);
_resolveProviderMetadata(ngMetaMap, compileDirectiveMetadata);
} }
} }
} }
@ -130,11 +125,12 @@ class _CompileDataCreator {
return null; return null;
} }
final depNgMeta = ngMetaMap[dep.prefix]; final depNgMeta = ngMetaMap[dep.prefix];
if (depNgMeta.aliases.containsKey(dep.name)) {
if (depNgMeta.identifiers.containsKey(dep.name)) {
resolvedMetadata.add(depNgMeta.identifiers[dep.name]);
} else if (depNgMeta.aliases.containsKey(dep.name)) {
resolvedMetadata.addAll(depNgMeta.flatten(dep.name)); resolvedMetadata.addAll(depNgMeta.flatten(dep.name));
} else if (depNgMeta.identifiers.containsKey(dep.name)) {
resolvedMetadata.add(depNgMeta.identifiers[dep.name]);
} else { } else {
log.error( log.error(
'Could not find Directive/Pipe entry for $dep. ' 'Could not find Directive/Pipe entry for $dep. '
@ -147,98 +143,6 @@ class _CompileDataCreator {
return resolvedMetadata; return resolvedMetadata;
} }
void _resolveProviderMetadata(Map<String, NgMeta> 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<String, NgMeta> ngMetaMap,CompileTypeMetadata neededBy, List<CompileDiDependencyMetadata> 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<String, NgMeta> 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<String, NgMeta> 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<List<dynamic>> _readPlatformTypes( Future<List<dynamic>> _readPlatformTypes(
List<String> inputPlatformTypes, String configOption) async { List<String> inputPlatformTypes, String configOption) async {
if (inputPlatformTypes == null) return const []; if (inputPlatformTypes == null) return const [];
@ -266,10 +170,12 @@ class _CompileDataCreator {
if (jsonString != null && jsonString.isNotEmpty) { if (jsonString != null && jsonString.isNotEmpty) {
var newMetadata = new NgMeta.fromJson(JSON.decode(jsonString)); var newMetadata = new NgMeta.fromJson(JSON.decode(jsonString));
if (newMetadata.identifiers.containsKey(token)) { if (newMetadata.aliases.containsKey(token)) {
return [newMetadata.identifiers[token]];
} else if (newMetadata.aliases.containsKey(token)) {
return newMetadata.flatten(token); return newMetadata.flatten(token);
} else if (newMetadata.identifiers.containsKey(token)) {
return [newMetadata.identifiers[token]];
} else { } else {
log.warning('Could not resolve platform type ${token} in ${uri}', log.warning('Could not resolve platform type ${token} in ${uri}',
asset: metaAssetId); asset: metaAssetId);

View File

@ -44,7 +44,7 @@ Future<Outputs> processTemplates(AssetReader reader, AssetId assetId,
Map<String, String> resolvedIdentifiers Map<String, String> resolvedIdentifiers
}) async { }) async {
var viewDefResults = await createCompileData( var viewDefResults = await createCompileData(
reader, assetId, platformDirectives, platformPipes, resolvedIdentifiers); reader, assetId, platformDirectives, platformPipes);
if (viewDefResults == null) return null; if (viewDefResults == null) return null;
final compileTypeMetadatas = viewDefResults.ngMeta.identifiers.values; final compileTypeMetadatas = viewDefResults.ngMeta.identifiers.values;
if (compileTypeMetadatas.isNotEmpty) { if (compileTypeMetadatas.isNotEmpty) {
@ -106,4 +106,4 @@ class Outputs {
final SourceModule templatesSource; final SourceModule templatesSource;
Outputs._(this.ngDeps, this.templatesSource); Outputs._(this.ngDeps, this.templatesSource);
} }

View File

@ -34,7 +34,7 @@ class AngularTransformerGroup extends TransformerGroup {
} else { } else {
phases = [ phases = [
[new DirectiveProcessor(options)], [new DirectiveProcessor(options)],
[new DirectiveMetadataLinker()], [new DirectiveMetadataLinker(options)],
[new ReflectionRemover(options)], [new ReflectionRemover(options)],
[ [
new DeferredRewriter(), new DeferredRewriter(),

View File

@ -39,7 +39,7 @@ class CodegenTransformer extends TransformerGroup {
} else { } else {
phases = [ phases = [
[new DirectiveProcessor(options)], [new DirectiveProcessor(options)],
[new DirectiveMetadataLinker()], [new DirectiveMetadataLinker(options)],
[new StylesheetCompiler(), new TemplateCompiler(options),], [new StylesheetCompiler(), new TemplateCompiler(options),],
]; ];
} }

View File

@ -82,4 +82,4 @@ CompileDirectiveMetadata createBaz([String moduleBase = 'asset:a']) =>
name: 'BazComponent', name: 'BazComponent',
moduleUrl: '$moduleBase/export_cycle_files/baz.dart', moduleUrl: '$moduleBase/export_cycle_files/baz.dart',
selector: 'baz', selector: 'baz',
template: 'Baz'); template: 'Baz');

View File

@ -55,7 +55,8 @@ void allTests() {
a.aliases['a3'] = ['T3', 'a2']; a.aliases['a3'] = ['T3', 'a2'];
a.aliases['a4'] = ['a3', 'T0']; 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.', () { test('should detect cycles.', () {
@ -64,7 +65,10 @@ void allTests() {
a.aliases['a1'] = ['T0', 'a2']; a.aliases['a1'] = ['T0', 'a2'];
a.aliases['a2'] = ['a1']; 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.', () { test('should allow duplicates.', () {
@ -98,6 +102,44 @@ void allTests() {
expect(a.aliases['b'], equals(['y'])); 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 true if there is a factory', () {
var a = new NgMeta.empty();
a.identifiers["MyIdentifier"] = new CompileFactoryMetadata();
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) { _checkSimilar(NgMeta a, NgMeta b) {

View File

@ -41,6 +41,10 @@ class TestAssetReader implements AssetReader {
return new Future.value(exists); return new Future.value(exists);
} }
void clear() {
this._overrideAssets.clear();
}
void addAsset(AssetId id, String contents) { void addAsset(AssetId id, String contents) {
_overrideAssets[id] = contents; _overrideAssets[id] = contents;
} }

View File

@ -21,46 +21,53 @@ var formatter = new DartFormatter();
main() => allTests(); main() => allTests();
var oldTest = test;
void allTests() { void allTests() {
var test = (name, fn) {
// if (name.contains('indirection')) {
oldTest(name, fn);
// }
};
TestAssetReader reader = null; TestAssetReader reader = null;
final moduleBase = 'asset:a'; final moduleBase = 'asset:a';
var fooNgMeta, fooAssetId; var fooNgMeta, fooAssetId, fooMetaAssetId, fooComponentMeta;
var barNgMeta, barAssetId; var barNgMeta, barAssetId, barMetaAssetId, barComponentMeta;
var bazNgMeta, bazAssetId; var bazNgMeta, bazAssetId, bazMetaAssetId, bazComponentMeta;
var aliasNgMeta, aliasAssetId;
/// Call after making changes to `fooNgMeta`, `barNgMeta`, or `bazNgMeta` and /// Call after making changes to `fooNgMeta`, `barNgMeta`, or `bazNgMeta` and
/// before trying to read them from `reader`. /// before trying to read them from `reader`.
final updateReader = () => reader final updateReader = () => reader
..addAsset(fooAssetId, JSON.encode(fooNgMeta.toJson())) ..addAsset(fooAssetId, JSON.encode(fooNgMeta.toJson()))
..addAsset(barMetaAssetId, JSON.encode(fooNgMeta.toJson()))
..addAsset(barAssetId, JSON.encode(barNgMeta.toJson())) ..addAsset(barAssetId, JSON.encode(barNgMeta.toJson()))
..addAsset(barMetaAssetId, JSON.encode(barNgMeta.toJson()))
..addAsset(bazAssetId, JSON.encode(bazNgMeta.toJson())) ..addAsset(bazAssetId, JSON.encode(bazNgMeta.toJson()))
..addAsset(aliasAssetId, JSON.encode(aliasNgMeta.toJson())); ..addAsset(bazMetaAssetId, JSON.encode(bazNgMeta.toJson()));
setUp(() { setUp(() {
reader = new TestAssetReader(); reader = new TestAssetReader();
// Establish some test NgMeta objects with one Component each. // Establish some test NgMeta objects with one Component each.
var fooComponentMeta = createFoo(moduleBase); fooComponentMeta = createFoo(moduleBase);
fooNgMeta = new NgMeta(ngDeps: new NgDepsModel()); fooNgMeta = new NgMeta(ngDeps: new NgDepsModel());
fooNgMeta.identifiers[fooComponentMeta.type.name] = fooComponentMeta; fooNgMeta.identifiers[fooComponentMeta.type.name] = fooComponentMeta;
var barComponentMeta = createBar(moduleBase); barComponentMeta = createBar(moduleBase);
barNgMeta = new NgMeta(ngDeps: new NgDepsModel()); barNgMeta = new NgMeta(ngDeps: new NgDepsModel());
barNgMeta.identifiers[barComponentMeta.type.name] = barComponentMeta; barNgMeta.identifiers[barComponentMeta.type.name] = barComponentMeta;
var bazComponentMeta = createBaz(moduleBase); bazComponentMeta = createBaz(moduleBase);
bazNgMeta = new NgMeta(ngDeps: new NgDepsModel()); bazNgMeta = new NgMeta(ngDeps: new NgDepsModel());
barNgMeta.identifiers[bazComponentMeta.type.name] = bazComponentMeta; barNgMeta.identifiers[bazComponentMeta.type.name] = bazComponentMeta;
aliasNgMeta = new NgMeta(ngDeps: new NgDepsModel());
aliasNgMeta.aliases["Providers"] = ["someAlias"];
aliasNgMeta.definesAlias = true;
fooAssetId = new AssetId('a', toSummaryExtension('lib/foo.dart')); fooAssetId = new AssetId('a', toSummaryExtension('lib/foo.dart'));
fooMetaAssetId = new AssetId('a', toMetaExtension('lib/foo.dart'));
barAssetId = new AssetId('a', toSummaryExtension('lib/bar.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')); 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(); updateReader();
}); });
@ -69,9 +76,9 @@ void allTests() {
fooNgMeta.ngDeps.exports.add(new ExportModel()..uri = 'bar.dart'); fooNgMeta.ngDeps.exports.add(new ExportModel()..uri = 'bar.dart');
updateReader(); updateReader();
var extracted = await _testLink(reader, fooAssetId); var extracted = await _testLink(reader, fooAssetId, fooMetaAssetId);
expect(extracted.identifiers, contains('FooComponent')); expect(extracted.identifiers['FooComponent'], isNotNull);
expect(extracted.identifiers, contains('BarComponent')); expect(extracted.identifiers['BarComponent'], isNotNull);
expect(extracted.identifiers['FooComponent'].selector, equals('foo')); expect(extracted.identifiers['FooComponent'].selector, equals('foo'));
expect(extracted.identifiers['BarComponent'].selector, equals('bar')); expect(extracted.identifiers['BarComponent'].selector, equals('bar'));
@ -83,68 +90,527 @@ void allTests() {
barNgMeta.ngDeps.exports.add(new ExportModel()..uri = 'baz.dart'); barNgMeta.ngDeps.exports.add(new ExportModel()..uri = 'baz.dart');
updateReader(); updateReader();
var extracted = await _testLink(reader, fooAssetId); var extracted = await _testLink(reader, fooAssetId, fooMetaAssetId);
expect(extracted.identifiers, contains('FooComponent')); expect(extracted.identifiers['FooComponent'], isNotNull);
expect(extracted.identifiers, contains('BarComponent')); expect(extracted.identifiers['BarComponent'], isNotNull);
expect(extracted.identifiers, contains('BazComponent')); expect(extracted.identifiers['BazComponent'], isNotNull);
expect(extracted.identifiers['FooComponent'].selector, equals('foo')); expect(extracted.identifiers['FooComponent'].selector, equals('foo'));
expect(extracted.identifiers['BarComponent'].selector, equals('bar')); expect(extracted.identifiers['BarComponent'].selector, equals('bar'));
expect(extracted.identifiers['BazComponent'].selector, equals('baz')); expect(extracted.identifiers['BazComponent'].selector, equals('baz'));
}); });
test( test('should handle `DirectiveMetadata` export cycles gracefully.', () async {
'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 {
fooNgMeta.ngDeps.exports.add(new ExportModel()..uri = 'bar.dart'); fooNgMeta.ngDeps.exports.add(new ExportModel()..uri = 'bar.dart');
barNgMeta.ngDeps.exports.add(new ExportModel()..uri = 'baz.dart'); barNgMeta.ngDeps.exports.add(new ExportModel()..uri = 'baz.dart');
bazNgMeta.ngDeps.exports.add(new ExportModel()..uri = 'foo.dart'); bazNgMeta.ngDeps.exports.add(new ExportModel()..uri = 'foo.dart');
updateReader(); updateReader();
var extracted = await _testLink(reader, bazAssetId); var extracted = await _testLink(reader, bazAssetId, bazMetaAssetId);
expect(extracted.identifiers, contains('FooComponent')); expect(extracted.identifiers['FooComponent'], isNotNull);
expect(extracted.identifiers, contains('BarComponent')); expect(extracted.identifiers['BarComponent'], isNotNull);
expect(extracted.identifiers, contains('BazComponent')); expect(extracted.identifiers['BazComponent'], isNotNull);
}); });
test( test('should include `DirectiveMetadata` from exported files '
'should include `DirectiveMetadata` from exported files ' 'expressed as absolute uris', () async {
'expressed as absolute uris', () async {
fooNgMeta.ngDeps.exports fooNgMeta.ngDeps.exports
.add(new ExportModel()..uri = 'package:bar/bar.dart'); .add(new ExportModel()..uri = 'package:bar/bar.dart');
updateReader(); updateReader();
reader.addAsset(new AssetId('bar', toSummaryExtension('lib/bar.dart')), reader.addAsset(new AssetId('bar', toMetaExtension('lib/bar.dart')),
JSON.encode(barNgMeta.toJson())); 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['FooComponent'], isNotNull);
expect(extracted.identifiers, contains('BarComponent')); expect(extracted.identifiers['BarComponent'], isNotNull);
expect(extracted.identifiers['FooComponent'].selector, equals('foo')); expect(extracted.identifiers['FooComponent'].selector, equals('foo'));
expect(extracted.identifiers['BarComponent'].selector, equals('bar')); 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', () { group('NgDeps linker', () {
@ -157,7 +623,7 @@ void allTests() {
barNgMeta.ngDeps.libraryUri = 'test.bar'; barNgMeta.ngDeps.libraryUri = 'test.bar';
updateReader(); updateReader();
var linked = (await _testLink(reader, fooAssetId)).ngDeps; var linked = (await _testLink(reader, fooAssetId, fooMetaAssetId)).ngDeps;
expect(linked, isNotNull); expect(linked, isNotNull);
var linkedImport = linked.depImports var linkedImport = linked.depImports
.firstWhere((i) => i.uri.endsWith('bar.template.dart')); .firstWhere((i) => i.uri.endsWith('bar.template.dart'));
@ -172,7 +638,7 @@ void allTests() {
barNgMeta.ngDeps.libraryUri = 'test.bar'; barNgMeta.ngDeps.libraryUri = 'test.bar';
updateReader(); updateReader();
var linked = (await _testLink(reader, fooAssetId)).ngDeps; var linked = (await _testLink(reader, fooAssetId, fooMetaAssetId)).ngDeps;
expect(linked, isNotNull); expect(linked, isNotNull);
var linkedImport = linked.depImports var linkedImport = linked.depImports
.firstWhere((i) => i.uri.endsWith('bar.template.dart')); .firstWhere((i) => i.uri.endsWith('bar.template.dart'));
@ -190,7 +656,7 @@ void allTests() {
barNgMeta.ngDeps.libraryUri = 'test.bar'; barNgMeta.ngDeps.libraryUri = 'test.bar';
updateReader(); updateReader();
var linked = (await _testLink(reader, fooAssetId)).ngDeps; var linked = (await _testLink(reader, fooAssetId, fooMetaAssetId)).ngDeps;
expect(linked, isNotNull); expect(linked, isNotNull);
var linkedImport = linked.depImports.firstWhere( var linkedImport = linked.depImports.firstWhere(
(i) => i.uri.endsWith('bar.template.dart'), (i) => i.uri.endsWith('bar.template.dart'),
@ -200,7 +666,11 @@ void allTests() {
}); });
} }
Future<NgMeta> _testLink(AssetReader reader, AssetId assetId) { Future<NgMeta> _testLink(
return zone.exec(() => linkDirectiveMetadata(reader, assetId), AssetReader reader, AssetId summaryAssetId, AssetId metaAssetId,
[Map<String, String> resolvedIdentifiers]) {
return zone.exec(
() => linkDirectiveMetadata(
reader, summaryAssetId, metaAssetId, resolvedIdentifiers),
log: new RecordingLogger()); log: new RecordingLogger());
} }

View File

@ -4,7 +4,7 @@ import 'package:angular2/angular2.dart' show Injectable;
@Injectable() @Injectable()
abstract class Service { abstract class Service {
factory Service(){ factory Service() {
return null; return null;
} }
} }

View File

@ -7,7 +7,10 @@ import 'package:dart_style/dart_style.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
import 'package:angular2/src/compiler/directive_metadata.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/core/change_detection/change_detection.dart';
import 'package:angular2/src/platform/server/html_adapter.dart'; import 'package:angular2/src/platform/server/html_adapter.dart';
import 'package:angular2/src/core/linker/interfaces.dart' show LifecycleHooks; 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); return expect(selectorArg.value, matcher);
} }
var oldTest = test;
void allTests() { void allTests() {
var test = (name, fn) {
// if (name.contains('CompileFactoryMetadata')) {
oldTest(name, fn);
// }
};
test('should preserve parameter annotations.', () async { test('should preserve parameter annotations.', () async {
var model = (await _testCreateModel('parameter_metadata/soup.dart')).ngDeps; var model = (await _testCreateModel('parameter_metadata/soup.dart')).ngDeps;
expect(model.reflectables.length, equals(1)); expect(model.reflectables.length, equals(1));
@ -422,6 +432,7 @@ void allTests() {
expect(ngMeta.identifiers.isNotEmpty, isTrue); expect(ngMeta.identifiers.isNotEmpty, isTrue);
expect(ngMeta.identifiers['MultiSoupComponent'], isNotNull); expect(ngMeta.identifiers['MultiSoupComponent'], isNotNull);
expect( expect(
ngMeta.identifiers['MultiSoupComponent'].selector, equals('[soup]')); ngMeta.identifiers['MultiSoupComponent'].selector, equals('[soup]'));
final hooks = ngMeta.identifiers['MultiSoupComponent'].lifecycleHooks; final hooks = ngMeta.identifiers['MultiSoupComponent'].lifecycleHooks;
expect(hooks, contains(LifecycleHooks.OnChanges)); expect(hooks, contains(LifecycleHooks.OnChanges));
@ -467,7 +478,7 @@ void allTests() {
}); });
group("identifiers", () { group("identifiers", () {
test("should populate `identifier` with class types.", () async { test("should populate `identifier` with CompileTypeMetadata.", () async {
var model = (await _testCreateModel('identifiers/classes.dart')); var model = (await _testCreateModel('identifiers/classes.dart'));
final moduleUrl = final moduleUrl =
"asset:angular2/test/transform/directive_processor/identifiers/classes.dart"; "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['Service1'].moduleUrl, equals(moduleUrl));
expect(model.identifiers['Service2'].name, equals('Service2')); expect(model.identifiers['Service2'].name, equals('Service2'));
expect(model.identifiers['Service2'].moduleUrl, equals(moduleUrl)); 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 { test("should populate `identifier` with constants.", () async {
@ -492,6 +521,32 @@ void allTests() {
expect(model.identifiers['c'], isNull); 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( test(
"should populate `identifier` with class names that do not have @Injectable;'.", "should populate `identifier` with class names that do not have @Injectable;'.",
() async { () async {
@ -642,17 +697,25 @@ void allTests() {
expect(cmp, isNotNull); expect(cmp, isNotNull);
var deps = cmp.type.diDeps; var deps = cmp.type.diDeps;
expect(deps, isNotNull); expect(deps, isNotNull);
expect(deps.length, equals(8)); expect(deps.length, equals(13));
expect(deps[0].token.name, equals("ServiceDep")); expect(deps[0].token.name, equals("ServiceDep"));
expect(deps[1].token.name, equals("ServiceDep")); expect(deps[1].token.name, equals("ServiceDep"));
expect(deps[2].token, "one");
expect(deps[2].isAttribute, isTrue); expect(deps[2].isAttribute, isTrue);
expect(deps[3].isSelf, isTrue); expect(deps[3].isSelf, isTrue);
expect(deps[4].isSkipSelf, isTrue); expect(deps[4].isSkipSelf, isTrue);
expect(deps[5].isOptional, isTrue); expect(deps[5].isOptional, isTrue);
expect(deps[6].query.selectors[0].name, equals("ServiceDep")); expect(deps[6].query.selectors[0].name, equals("ServiceDep"));
expect(deps[6].query.descendants, isTrue); expect(deps[6].query.descendants, isTrue);
expect(deps[7].viewQuery.selectors[0], equals("one")); expect(deps[7].query.selectors[0].name, equals("ServiceDep"));
expect(deps[7].viewQuery.selectors[1], equals("two")); 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 { test('should populate `diDependency` using a string token.', () async {
@ -686,15 +749,54 @@ void allTests() {
expect(cmp.providers, isNotNull); expect(cmp.providers, isNotNull);
expect(cmp.providers.length, equals(2)); expect(cmp.providers.length, equals(2));
var firstToken = cmp.providers.first.token; var firstToken = cmp.providers.first;
expect(firstToken.prefix, isNull); expect(firstToken.prefix, isNull);
expect(firstToken.name, equals("ServiceDep")); expect(firstToken.name, equals("ServiceDep"));
var secondToken = cmp.providers[1].token; var secondToken = cmp.providers[1];
expect(secondToken.prefix, equals("dep2")); expect(secondToken.prefix, equals("dep2"));
expect(secondToken.name, equals("ServiceDep")); 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 { test('should populate `providers` using useClass.', () async {
var cmp = (await _testCreateModel('directives_files/components.dart')) var cmp = (await _testCreateModel('directives_files/components.dart'))
.identifiers['ComponentWithProvidersUseClass']; .identifiers['ComponentWithProvidersUseClass'];
@ -724,6 +826,191 @@ void allTests() {
expect(token, equals("StringDep")); 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 { test('should populate `providers` using a const token.', () async {
var cmp = (await _testCreateModel('directives_files/components.dart')) var cmp = (await _testCreateModel('directives_files/components.dart'))
.identifiers['ComponentWithProvidersConstToken']; .identifiers['ComponentWithProvidersConstToken'];
@ -737,6 +1024,36 @@ void allTests() {
expect(token.constConstructor, isTrue); 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 { test('should merge `outputs` from the annotation and fields.', () async {
var model = await _testCreateModel('directives_files/components.dart'); var model = await _testCreateModel('directives_files/components.dart');
expect( expect(

View File

@ -1,7 +1,7 @@
library angular2.test.transform.directive_processor.directive_files.components; library angular2.test.transform.directive_processor.directive_files.components;
import 'package:angular2/angular2.dart' 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 'dep1.dart';
import 'dep2.dart' as dep2; import 'dep2.dart' as dep2;
@ -91,6 +91,24 @@ class ComponentWithHostListeners {
providers: [ServiceDep, dep2.ServiceDep]) providers: [ServiceDep, dep2.ServiceDep])
class ComponentWithProvidersTypes {} 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( @Component(
selector: 'component-with-providers-string-token', selector: 'component-with-providers-string-token',
template: '', template: '',
@ -111,25 +129,135 @@ class ComponentWithProvidersConstToken {
providers: [const Provider(ServiceDep, useClass: ServiceDep)]) providers: [const Provider(ServiceDep, useClass: ServiceDep)])
class ComponentWithProvidersUseClass {} class ComponentWithProvidersUseClass {}
@Component( @Component(selector: 'component-with-di-deps', template: '')
selector: 'component-with-di-deps',
template: '')
class ComponentWithDiDeps { class ComponentWithDiDeps {
ServiceDep arg11;
ServiceDep arg13;
ComponentWithDiDeps( ComponentWithDiDeps(
ServiceDep arg1, ServiceDep arg1,
@Inject(ServiceDep) arg2, @Inject(ServiceDep) arg2,
@Attribute('one') arg3, @Attribute('one') arg3,
@Self() ServiceDep arg4, @Self() ServiceDep arg4,
@SkipSelf() ServiceDep arg5, @SkipSelf() ServiceDep arg5,
@Optional() ServiceDep arg6, @Optional() ServiceDep arg6,
@Query(ServiceDep, descendants:true) arg6, @Query(ServiceDep, descendants: true) arg7,
@ViewQuery("one,two") arg7 @ContentChildren(ServiceDep) arg8,
@ViewQuery("one,two") arg9,
@ViewChildren("one,two") arg10,
this.arg11,
[@Optional() ServiceDep arg12,
@Optional() this.arg13]
); );
} }
@Component( @Component(
selector: 'component-with-di-deps-string-token', selector: 'component-with-providers-use-class',
template: '') 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 { class ComponentWithDiDepsStrToken {
ComponentWithDiDepsStrToken(@Inject("StringDep") arg1); 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() {}

View File

@ -1,6 +1,7 @@
library angular2.test.transform.directive_processor.directive_files.dep1; 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') @Component(selector: 'dep1')
@View(template: 'Dep1') @View(template: 'Dep1')
@ -12,4 +13,5 @@ class PipeDep {}
@Injectable() @Injectable()
class ServiceDep { class ServiceDep {
const ServiceDep(); const ServiceDep();
static someFactory() {}
} }

View File

@ -1,6 +1,7 @@
library angular2.test.transform.directive_processor.directive_files.dep2; 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') @Component(selector: 'dep2')
@View(template: 'Dep2') @View(template: 'Dep2')
@ -10,4 +11,4 @@ class Dep {}
class PipeDep {} class PipeDep {}
@Injectable() @Injectable()
class ServiceDep {} class ServiceDep {}

View File

@ -1,10 +1,9 @@
library angular2.test.transform.directive_processor.directive_files.components; library angular2.test.transform.directive_processor.directive_files.components;
import 'package:angular2/angular2.dart' import 'package:angular2/angular2.dart' show Injectable, Inject;
show Injectable, Inject;
import 'dep1.dart'; import 'dep1.dart';
@Injectable() @Injectable()
class Service { class Service {
Service(ServiceDep arg1, @Inject(ServiceDep) arg2); Service(ServiceDep arg1, @Inject(ServiceDep) arg2);
} }

View File

@ -2,10 +2,9 @@ library angular2.test.transform.directive_processor.identifiers.classes;
import 'package:angular2/angular2.dart' show Injectable; import 'package:angular2/angular2.dart' show Injectable;
@Injectable()
class Service1 {} class Service1 {}
@Injectable() @Injectable()
class Service2 {} class Service2 {
Service2(Service1 service1) {}
class Service3 {} }

View File

@ -1,3 +1,3 @@
library angular2.test.transform.directive_processor.identifiers.classes_no_injectable; library angular2.test.transform.directive_processor.identifiers.classes_no_injectable;
abstract class ClassA {} abstract class ClassA {}

View File

@ -2,4 +2,4 @@ library angular2.test.transform.directive_processor.identifiers.constants;
const a = "a"; const a = "a";
const b = "b"; const b = "b";
var c = "c"; var c = "c";

View File

@ -1,5 +1,3 @@
library angular2.test.transform.directive_processor.identifiers.enums; library angular2.test.transform.directive_processor.identifiers.enums;
enum Enum { enum Enum { one }
one
}

View File

@ -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) {}

View File

@ -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)
];

View File

@ -1,3 +1,3 @@
library angular2.test.transform.directive_processor.identifiers.typedefs; library angular2.test.transform.directive_processor.identifiers.typedefs;
typedef String TypeDef(String); typedef String TypeDef(String);

View File

@ -51,50 +51,47 @@ void allTests() {
Html5LibDomAdapter.makeCurrent(); Html5LibDomAdapter.makeCurrent();
var tests = [ var tests = [
new IntegrationTestConfig( new IntegrationTestConfig('should generate proper code for a Component defining only a selector.',
'should generate proper code for a Component defining only a selector.',
inputs: { inputs: {
'a|web/index.dart': 'simple_annotation_files/index.dart', 'a|web/index.dart': 'simple_annotation_files/index.dart',
'a|web/bar.dart': 'simple_annotation_files/bar.dart' 'a|web/bar.dart': 'simple_annotation_files/bar.dart'
}, },
outputs: { outputs: {
'a|web/bar.template.dart': 'a|web/bar.template.dart':
'simple_annotation_files/expected/bar.template.dart', 'simple_annotation_files/expected/bar.template.dart',
'a|web/index.template.dart': 'a|web/index.template.dart':
'simple_annotation_files/expected/index.template.dart' 'simple_annotation_files/expected/index.template.dart'
}), }),
new IntegrationTestConfig( new IntegrationTestConfig('should generate proper code for a Component with multiple deps.',
'should generate proper code for a Component with multiple deps.',
inputs: { inputs: {
'a|web/index.dart': 'two_deps_files/index.dart', 'a|web/index.dart': 'two_deps_files/index.dart',
'a|web/foo.dart': 'two_deps_files/foo.dart', 'a|web/foo.dart': 'two_deps_files/foo.dart',
'a|web/bar.dart': 'two_deps_files/bar.dart' 'a|web/bar.dart': 'two_deps_files/bar.dart'
}, },
outputs: { 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( new IntegrationTestConfig(
'should generate proper code for a Component declaring a ' 'should generate proper code for a Component declaring a '
'componentService defined in another file.', 'componentService defined in another file.',
inputs: { inputs: {
'a|web/index.dart': 'list_of_types_files/index.dart', 'a|web/index.dart': 'list_of_types_files/index.dart',
'a|web/foo.dart': 'list_of_types_files/foo.dart', 'a|web/foo.dart': 'list_of_types_files/foo.dart',
'a|web/bar.dart': 'list_of_types_files/bar.dart' 'a|web/bar.dart': 'list_of_types_files/bar.dart'
}, },
outputs: { outputs: {
'a|web/bar.template.dart': 'a|web/bar.template.dart':
'list_of_types_files/expected/bar.template.dart' 'list_of_types_files/expected/bar.template.dart'
}), }),
new IntegrationTestConfig( new IntegrationTestConfig('should generate a factory for a class with no declared ctor.',
'should generate a factory for a class with no declared ctor.',
inputs: { inputs: {
'a|web/index.dart': 'synthetic_ctor_files/index.dart', 'a|web/index.dart': 'synthetic_ctor_files/index.dart',
'a|web/bar.dart': 'synthetic_ctor_files/bar.dart' 'a|web/bar.dart': 'synthetic_ctor_files/bar.dart'
}, },
outputs: { outputs: {
'a|web/bar.template.dart': 'a|web/bar.template.dart':
'synthetic_ctor_files/expected/bar.template.dart' 'synthetic_ctor_files/expected/bar.template.dart'
}), }),
new IntegrationTestConfig('should preserve multiple annotations.', inputs: { new IntegrationTestConfig('should preserve multiple annotations.', inputs: {
'a|web/index.dart': 'two_annotations_files/index.dart', 'a|web/index.dart': 'two_annotations_files/index.dart',
'a|web/bar.dart': 'two_annotations_files/bar.dart', 'a|web/bar.dart': 'two_annotations_files/bar.dart',
@ -104,77 +101,71 @@ void allTests() {
'a|web/bar.template.dart': 'a|web/bar.template.dart':
'two_annotations_files/expected/bar.template.dart' 'two_annotations_files/expected/bar.template.dart'
}), }),
new IntegrationTestConfig( new IntegrationTestConfig('should generate getters for output events defined on a Component.',
'should generate getters for output events defined on a Component.',
inputs: { inputs: {
'a|web/index.dart': 'event_getter_files/index.dart', 'a|web/index.dart': 'event_getter_files/index.dart',
'a|web/bar.dart': 'event_getter_files/bar.dart' 'a|web/bar.dart': 'event_getter_files/bar.dart'
}, },
outputs: { outputs: {
'a|web/bar.template.dart': 'event_getter_files/expected/bar.template.dart' 'a|web/bar.template.dart':
}), 'event_getter_files/expected/bar.template.dart'
new IntegrationTestConfig( }),
'should handle Directive dependencies declared on a View.', new IntegrationTestConfig('should handle Directive dependencies declared on a View.',
inputs: { inputs: {
'a|web/index.dart': 'directive_dep_files/index.dart', 'a|web/index.dart': 'directive_dep_files/index.dart',
'a|web/foo.dart': 'directive_dep_files/foo.dart', 'a|web/foo.dart': 'directive_dep_files/foo.dart',
'a|web/bar.dart': 'directive_dep_files/bar.dart' 'a|web/bar.dart': 'directive_dep_files/bar.dart'
}, },
outputs: { outputs: {
'a|web/bar.template.dart': 'a|web/bar.template.dart':
'directive_dep_files/expected/bar.template.dart' 'directive_dep_files/expected/bar.template.dart'
}), }),
new IntegrationTestConfig( new IntegrationTestConfig('should handle chained Directive dependencies declared on a View.',
'should handle chained Directive dependencies declared on a View.',
inputs: { inputs: {
'a|web/index.dart': 'directive_chain_files/index.dart', 'a|web/index.dart': 'directive_chain_files/index.dart',
'a|web/foo.dart': 'directive_chain_files/foo.dart', 'a|web/foo.dart': 'directive_chain_files/foo.dart',
'a|web/bar.dart': 'directive_chain_files/bar.dart', 'a|web/bar.dart': 'directive_chain_files/bar.dart',
'a|web/baz.dart': 'directive_chain_files/baz.dart' 'a|web/baz.dart': 'directive_chain_files/baz.dart'
}, },
outputs: { outputs: {
'a|web/bar.template.dart': 'a|web/bar.template.dart':
'directive_chain_files/expected/bar.template.dart' 'directive_chain_files/expected/bar.template.dart'
}), }),
new IntegrationTestConfig( new IntegrationTestConfig('should handle empty template files that define directive aliases.',
'should handle empty template files that define directive aliases.',
inputs: { inputs: {
'a|web/foo.dart': 'empty_ng_deps_files/foo.dart', 'a|web/foo.dart': 'empty_ng_deps_files/foo.dart',
'a|web/bar.dart': 'empty_ng_deps_files/bar.dart' 'a|web/bar.dart': 'empty_ng_deps_files/bar.dart'
}, },
outputs: { outputs: {
'a|web/foo.template.dart': 'a|web/foo.template.dart':
'empty_ng_deps_files/expected/foo.template.dart', 'empty_ng_deps_files/expected/foo.template.dart',
'a|web/bar.template.dart': 'a|web/bar.template.dart':
'empty_ng_deps_files/expected/bar.template.dart' 'empty_ng_deps_files/expected/bar.template.dart'
}), }),
new IntegrationTestConfig( new IntegrationTestConfig('should generate setters for annotated properties.',
'should generate setters for annotated properties.',
inputs: { inputs: {
'a|web/bar.dart': 'queries_prop_annotation_files/bar.dart' 'a|web/bar.dart': 'queries_prop_annotation_files/bar.dart'
}, },
outputs: { outputs: {
'a|web/bar.template.dart': 'a|web/bar.template.dart':
'queries_prop_annotation_files/expected/bar.template.dart' 'queries_prop_annotation_files/expected/bar.template.dart'
}), }),
new IntegrationTestConfig( new IntegrationTestConfig('should generate setters for `queries` values in Directives.',
'should generate setters for `queries` values in Directives.',
inputs: { inputs: {
'a|web/bar.dart': 'queries_class_annotation_files/bar.dart' 'a|web/bar.dart': 'queries_class_annotation_files/bar.dart'
}, },
outputs: { outputs: {
'a|web/bar.template.dart': 'a|web/bar.template.dart':
'queries_class_annotation_files/expected/bar.template.dart' 'queries_class_annotation_files/expected/bar.template.dart'
}), }),
new IntegrationTestConfig( new IntegrationTestConfig('should handle @override annotations in properties on Directives.',
'should handle @override annotations in properties on Directives.',
inputs: { inputs: {
'a|web/bar.dart': 'override_annotation_files/bar.dart' 'a|web/bar.dart': 'override_annotation_files/bar.dart'
}, },
outputs: { outputs: {
'a|web/bar.template.dart': 'a|web/bar.template.dart':
'override_annotation_files/expected/bar.template.dart' 'override_annotation_files/expected/bar.template.dart'
}) })
]; ];
var cache = {}; var cache = {};

View File

@ -26,7 +26,7 @@ void initReflector() {
const [prefix.MyDep] const [prefix.MyDep]
], ],
(prefix.MyContext c, prefix.MyDep inValue) => (prefix.MyContext c, prefix.MyDep inValue) =>
new MyComponent(c, inValue))); new MyComponent(c, inValue)));
i0.initReflector(); i0.initReflector();
i1.initReflector(); i1.initReflector();
} }

View File

@ -57,24 +57,31 @@ void allTests() {
// lacking some details that would be created by DirectiveProcessor but // lacking some details that would be created by DirectiveProcessor but
// which are not used in the template compiler. // which are not used in the template compiler.
fooComponentMeta = createFoo(moduleBase); fooComponentMeta = createFoo(moduleBase);
fooNgMeta = new NgMeta(ngDeps: new NgDepsModel() fooNgMeta = new NgMeta(
..libraryUri = 'test.foo' ngDeps: new NgDepsModel()
..reflectables.add(new ReflectionInfoModel()..name = fooComponentMeta.type.name)); ..libraryUri = 'test.foo'
..reflectables.add(
new ReflectionInfoModel()..name = fooComponentMeta.type.name));
fooNgMeta.identifiers[fooComponentMeta.type.name] = fooComponentMeta; fooNgMeta.identifiers[fooComponentMeta.type.name] = fooComponentMeta;
barComponentMeta = createBar(moduleBase); barComponentMeta = createBar(moduleBase);
barPipeMeta = createBarPipe(moduleBase); barPipeMeta = createBarPipe(moduleBase);
barNgMeta = new NgMeta(ngDeps: new NgDepsModel() barNgMeta = new NgMeta(
..libraryUri = 'test.bar' ngDeps: new NgDepsModel()
..reflectables.add(new ReflectionInfoModel()..name = barPipeMeta.type.name) ..libraryUri = 'test.bar'
..reflectables.add(new ReflectionInfoModel()..name = barComponentMeta.type.name)); ..reflectables
.add(new ReflectionInfoModel()..name = barPipeMeta.type.name)
..reflectables.add(
new ReflectionInfoModel()..name = barComponentMeta.type.name));
barNgMeta.identifiers[barComponentMeta.type.name] = barComponentMeta; barNgMeta.identifiers[barComponentMeta.type.name] = barComponentMeta;
barNgMeta.identifiers[barPipeMeta.type.name] = barPipeMeta; barNgMeta.identifiers[barPipeMeta.type.name] = barPipeMeta;
bazComponentMeta = createBaz(moduleBase); bazComponentMeta = createBaz(moduleBase);
bazNgMeta = new NgMeta(ngDeps: new NgDepsModel() bazNgMeta = new NgMeta(
..libraryUri = 'test.baz' ngDeps: new NgDepsModel()
..reflectables.add(new ReflectionInfoModel()..name = bazComponentMeta.type.name)); ..libraryUri = 'test.baz'
..reflectables.add(
new ReflectionInfoModel()..name = bazComponentMeta.type.name));
barNgMeta.identifiers[bazComponentMeta.type.name] = bazComponentMeta; barNgMeta.identifiers[bazComponentMeta.type.name] = bazComponentMeta;
fooAssetId = new AssetId('a', 'lib/foo.ng_meta.json'); fooAssetId = new AssetId('a', 'lib/foo.ng_meta.json');
@ -92,206 +99,11 @@ void allTests() {
platformDirectives: platformDirectives, platformDirectives: platformDirectives,
platformPipes: platformPipes, platformPipes: platformPipes,
resolvedIdentifiers: resolvedIdentifiers, resolvedIdentifiers: resolvedIdentifiers,
translations: translations), translations: translations
),
log: logger); 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: '<li *ngFor="#thing of things"><div>test</div></li>');
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 { it('should parse simple expressions in inline templates.', () async {
fooComponentMeta.template = new CompileTemplateMetadata( fooComponentMeta.template = new CompileTemplateMetadata(
template: '<div [a]="b">{{greeting}}</div>', template: '<div [a]="b">{{greeting}}</div>',
@ -625,31 +437,6 @@ void allTests() {
..toContain(barPipeMeta.name); ..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 { it('should use i18n parser when translations are provided.', () async {
fooComponentMeta.template = new CompileTemplateMetadata( fooComponentMeta.template = new CompileTemplateMetadata(
template: '<div i18n>content</div>', template: '<div i18n>content</div>',

View File

@ -14,3 +14,5 @@ main() {
describe('Template Compiler', templateCompiler.allTests); describe('Template Compiler', templateCompiler.allTests);
describe('Stylesheet Compiler', stylesheetCompiler.allTests); describe('Stylesheet Compiler', stylesheetCompiler.allTests);
} }

View File

@ -838,136 +838,138 @@ const COMMON = [
'var workaround_empty_observable_list_diff:any' 'var workaround_empty_observable_list_diff:any'
]; ];
const COMPILER = [ const COMPILER =
'AttrAst', [
'AttrAst.constructor(name:string, value:string, sourceSpan:ParseSourceSpan)', 'AttrAst',
'AttrAst.visit(visitor:TemplateAstVisitor, context:any):any', 'AttrAst.constructor(name:string, value:string, sourceSpan:ParseSourceSpan)',
'BoundDirectivePropertyAst', 'AttrAst.visit(visitor:TemplateAstVisitor, context:any):any',
'BoundDirectivePropertyAst.constructor(directiveName:string, templateName:string, value:AST, sourceSpan:ParseSourceSpan)', 'BoundDirectivePropertyAst',
'BoundDirectivePropertyAst.visit(visitor:TemplateAstVisitor, context:any):any', 'BoundDirectivePropertyAst.constructor(directiveName:string, templateName:string, value:AST, sourceSpan:ParseSourceSpan)',
'BoundElementPropertyAst', 'BoundDirectivePropertyAst.visit(visitor:TemplateAstVisitor, context:any):any',
'BoundElementPropertyAst.constructor(name:string, type:PropertyBindingType, value:AST, unit:string, sourceSpan:ParseSourceSpan)', 'BoundElementPropertyAst',
'BoundElementPropertyAst.visit(visitor:TemplateAstVisitor, context:any):any', 'BoundElementPropertyAst.constructor(name:string, type:PropertyBindingType, value:AST, unit:string, sourceSpan:ParseSourceSpan)',
'BoundEventAst', 'BoundElementPropertyAst.visit(visitor:TemplateAstVisitor, context:any):any',
'BoundEventAst.constructor(name:string, target:string, handler:AST, sourceSpan:ParseSourceSpan)', 'BoundEventAst',
'BoundEventAst.fullName:any', 'BoundEventAst.constructor(name:string, target:string, handler:AST, sourceSpan:ParseSourceSpan)',
'BoundEventAst.visit(visitor:TemplateAstVisitor, context:any):any', 'BoundEventAst.fullName:any',
'BoundTextAst', 'BoundEventAst.visit(visitor:TemplateAstVisitor, context:any):any',
'BoundTextAst.constructor(value:AST, ngContentIndex:number, sourceSpan:ParseSourceSpan)', 'BoundTextAst',
'BoundTextAst.visit(visitor:TemplateAstVisitor, context:any):any', 'BoundTextAst.constructor(value:AST, ngContentIndex:number, sourceSpan:ParseSourceSpan)',
'CompileDirectiveMetadata', 'BoundTextAst.visit(visitor:TemplateAstVisitor, context:any):any',
'CompileDirectiveMetadata.changeDetection:ChangeDetectionStrategy', 'CompileDirectiveMetadata',
'CompileDirectiveMetadata.constructor({type,isComponent,dynamicLoadable,selector,exportAs,changeDetection,inputs,outputs,hostListeners,hostProperties,hostAttributes,lifecycleHooks,providers,viewProviders,queries,viewQueries,template}:{type?:CompileTypeMetadata, isComponent?:boolean, dynamicLoadable?:boolean, selector?:string, exportAs?:string, changeDetection?:ChangeDetectionStrategy, inputs?:{[key:string]:string}, outputs?:{[key:string]:string}, hostListeners?:{[key:string]:string}, hostProperties?:{[key:string]:string}, hostAttributes?:{[key:string]:string}, lifecycleHooks?:LifecycleHooks[], providers?:Array<CompileProviderMetadata|CompileTypeMetadata|any[]>, viewProviders?:Array<CompileProviderMetadata|CompileTypeMetadata|any[]>, queries?:CompileQueryMetadata[], viewQueries?:CompileQueryMetadata[], template?:CompileTemplateMetadata})', 'CompileDirectiveMetadata.changeDetection:ChangeDetectionStrategy',
'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<CompileProviderMetadata|CompileTypeMetadata|any[]>, viewProviders?:Array<CompileProviderMetadata|CompileTypeMetadata|any[]>, queries?:CompileQueryMetadata[], viewQueries?:CompileQueryMetadata[], template?:CompileTemplateMetadata}):CompileDirectiveMetadata', 'CompileDirectiveMetadata.constructor({type,isComponent,dynamicLoadable,selector,exportAs,changeDetection,inputs,outputs,hostListeners,hostProperties,hostAttributes,lifecycleHooks,providers,viewProviders,queries,viewQueries,template}:{type?:CompileTypeMetadata, isComponent?:boolean, dynamicLoadable?:boolean, selector?:string, exportAs?:string, changeDetection?:ChangeDetectionStrategy, inputs?:{[key:string]:string}, outputs?:{[key:string]:string}, hostListeners?:{[key:string]:string}, hostProperties?:{[key:string]:string}, hostAttributes?:{[key:string]:string}, lifecycleHooks?:LifecycleHooks[], providers?:Array<CompileProviderMetadata|CompileTypeMetadata|CompileIdentifierMetadata|any[]>, viewProviders?:Array<CompileProviderMetadata|CompileTypeMetadata|CompileIdentifierMetadata|any[]>, queries?:CompileQueryMetadata[], viewQueries?:CompileQueryMetadata[], template?:CompileTemplateMetadata})',
'CompileDirectiveMetadata.providers:Array<CompileProviderMetadata|CompileTypeMetadata|any[]>', '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<CompileProviderMetadata|CompileTypeMetadata|CompileIdentifierMetadata|any[]>, viewProviders?:Array<CompileProviderMetadata|CompileTypeMetadata|CompileIdentifierMetadata|any[]>, queries?:CompileQueryMetadata[], viewQueries?:CompileQueryMetadata[], template?:CompileTemplateMetadata}):CompileDirectiveMetadata',
'CompileDirectiveMetadata.queries:CompileQueryMetadata[]', 'CompileDirectiveMetadata.providers:Array<CompileProviderMetadata|CompileTypeMetadata|any[]>',
'CompileDirectiveMetadata.viewProviders:Array<CompileProviderMetadata|CompileTypeMetadata|any[]>', 'CompileDirectiveMetadata.queries:CompileQueryMetadata[]',
'CompileDirectiveMetadata.viewQueries:CompileQueryMetadata[]', 'CompileDirectiveMetadata.viewProviders:Array<CompileProviderMetadata|CompileTypeMetadata|any[]>',
'CompileDirectiveMetadata.dynamicLoadable:boolean', 'CompileDirectiveMetadata.viewQueries:CompileQueryMetadata[]',
'CompileDirectiveMetadata.exportAs:string', 'CompileDirectiveMetadata.dynamicLoadable:boolean',
'CompileDirectiveMetadata.fromJson(data:{[key:string]:any}):CompileDirectiveMetadata', 'CompileDirectiveMetadata.exportAs:string',
'CompileDirectiveMetadata.hostAttributes:{[key:string]:string}', 'CompileDirectiveMetadata.fromJson(data:{[key:string]:any}):CompileDirectiveMetadata',
'CompileDirectiveMetadata.hostListeners:{[key:string]:string}', 'CompileDirectiveMetadata.hostAttributes:{[key:string]:string}',
'CompileDirectiveMetadata.hostProperties:{[key:string]:string}', 'CompileDirectiveMetadata.hostListeners:{[key:string]:string}',
'CompileDirectiveMetadata.inputs:{[key:string]:string}', 'CompileDirectiveMetadata.hostProperties:{[key:string]:string}',
'CompileDirectiveMetadata.isComponent:boolean', 'CompileDirectiveMetadata.inputs:{[key:string]:string}',
'CompileDirectiveMetadata.lifecycleHooks:LifecycleHooks[]', 'CompileDirectiveMetadata.isComponent:boolean',
'CompileDirectiveMetadata.outputs:{[key:string]:string}', 'CompileDirectiveMetadata.lifecycleHooks:LifecycleHooks[]',
'CompileDirectiveMetadata.selector:string', 'CompileDirectiveMetadata.outputs:{[key:string]:string}',
'CompileDirectiveMetadata.template:CompileTemplateMetadata', 'CompileDirectiveMetadata.selector:string',
'CompileDirectiveMetadata.toJson():{[key:string]:any}', 'CompileDirectiveMetadata.template:CompileTemplateMetadata',
'CompileDirectiveMetadata.type:CompileTypeMetadata', 'CompileDirectiveMetadata.toJson():{[key:string]:any}',
'CompileDirectiveMetadata.identifier:CompileIdentifierMetadata', 'CompileDirectiveMetadata.type:CompileTypeMetadata',
'CompileTemplateMetadata', 'CompileDirectiveMetadata.identifier:CompileIdentifierMetadata',
'CompileTemplateMetadata.constructor({encapsulation,template,templateUrl,styles,styleUrls,ngContentSelectors}:{encapsulation?:ViewEncapsulation, template?:string, templateUrl?:string, styles?:string[], styleUrls?:string[], ngContentSelectors?:string[]})', 'CompileTemplateMetadata',
'CompileTemplateMetadata.encapsulation:ViewEncapsulation', 'CompileTemplateMetadata.constructor({encapsulation,template,templateUrl,styles,styleUrls,ngContentSelectors}:{encapsulation?:ViewEncapsulation, template?:string, templateUrl?:string, styles?:string[], styleUrls?:string[], ngContentSelectors?:string[]})',
'CompileTemplateMetadata.fromJson(data:{[key:string]:any}):CompileTemplateMetadata', 'CompileTemplateMetadata.encapsulation:ViewEncapsulation',
'CompileTemplateMetadata.ngContentSelectors:string[]', 'CompileTemplateMetadata.fromJson(data:{[key:string]:any}):CompileTemplateMetadata',
'CompileTemplateMetadata.styleUrls:string[]', 'CompileTemplateMetadata.ngContentSelectors:string[]',
'CompileTemplateMetadata.styles:string[]', 'CompileTemplateMetadata.styleUrls:string[]',
'CompileTemplateMetadata.template:string', 'CompileTemplateMetadata.styles:string[]',
'CompileTemplateMetadata.templateUrl:string', 'CompileTemplateMetadata.template:string',
'CompileTemplateMetadata.toJson():{[key:string]:any}', 'CompileTemplateMetadata.templateUrl:string',
'CompileTypeMetadata', 'CompileTemplateMetadata.toJson():{[key:string]:any}',
'CompileTypeMetadata.constructor({runtime,name,moduleUrl,prefix,isHost,constConstructor,diDeps}:{runtime?:Type, name?:string, moduleUrl?:string, prefix?:string, isHost?:boolean, constConstructor?:boolean, diDeps?:CompileDiDependencyMetadata[]})', 'CompileTypeMetadata',
'CompileTypeMetadata.fromJson(data:{[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.isHost:boolean', 'CompileTypeMetadata.fromJson(data:{[key:string]:any}):CompileTypeMetadata',
'CompileTypeMetadata.moduleUrl:string', 'CompileTypeMetadata.value:any',
'CompileTypeMetadata.name:string', 'CompileTypeMetadata.isHost:boolean',
'CompileTypeMetadata.runtime:Type', 'CompileTypeMetadata.moduleUrl:string',
'CompileTypeMetadata.toJson():{[key:string]:any}', 'CompileTypeMetadata.name:string',
'CompileTypeMetadata.diDeps:CompileDiDependencyMetadata[]', 'CompileTypeMetadata.runtime:Type',
'CompileTypeMetadata.prefix:string', 'CompileTypeMetadata.toJson():{[key:string]:any}',
'CompileTypeMetadata.constConstructor:boolean', 'CompileTypeMetadata.diDeps:CompileDiDependencyMetadata[]',
'CompileTypeMetadata.identifier:CompileIdentifierMetadata', 'CompileTypeMetadata.prefix:string',
'CompileTypeMetadata.type:CompileTypeMetadata', 'CompileTypeMetadata.constConstructor:boolean',
'DirectiveAst', 'CompileTypeMetadata.identifier:CompileIdentifierMetadata',
'DirectiveAst.constructor(directive:CompileDirectiveMetadata, inputs:BoundDirectivePropertyAst[], hostProperties:BoundElementPropertyAst[], hostEvents:BoundEventAst[], exportAsVars:VariableAst[], sourceSpan:ParseSourceSpan)', 'CompileTypeMetadata.type:CompileTypeMetadata',
'DirectiveAst.visit(visitor:TemplateAstVisitor, context:any):any', 'DirectiveAst',
'ElementAst', 'DirectiveAst.constructor(directive:CompileDirectiveMetadata, inputs:BoundDirectivePropertyAst[], hostProperties:BoundElementPropertyAst[], hostEvents:BoundEventAst[], exportAsVars:VariableAst[], sourceSpan:ParseSourceSpan)',
'ElementAst.constructor(name:string, attrs:AttrAst[], inputs:BoundElementPropertyAst[], outputs:BoundEventAst[], exportAsVars:VariableAst[], directives:DirectiveAst[], children:TemplateAst[], ngContentIndex:number, sourceSpan:ParseSourceSpan)', 'DirectiveAst.visit(visitor:TemplateAstVisitor, context:any):any',
'ElementAst.getComponent():CompileDirectiveMetadata', 'ElementAst',
'ElementAst.isBound():boolean', 'ElementAst.constructor(name:string, attrs:AttrAst[], inputs:BoundElementPropertyAst[], outputs:BoundEventAst[], exportAsVars:VariableAst[], directives:DirectiveAst[], children:TemplateAst[], ngContentIndex:number, sourceSpan:ParseSourceSpan)',
'ElementAst.visit(visitor:TemplateAstVisitor, context:any):any', 'ElementAst.getComponent():CompileDirectiveMetadata',
'EmbeddedTemplateAst', 'ElementAst.isBound():boolean',
'EmbeddedTemplateAst.constructor(attrs:AttrAst[], outputs:BoundEventAst[], vars:VariableAst[], directives:DirectiveAst[], children:TemplateAst[], ngContentIndex:number, sourceSpan:ParseSourceSpan)', 'ElementAst.visit(visitor:TemplateAstVisitor, context:any):any',
'EmbeddedTemplateAst.visit(visitor:TemplateAstVisitor, context:any):any', 'EmbeddedTemplateAst',
'NgContentAst', 'EmbeddedTemplateAst.constructor(attrs:AttrAst[], outputs:BoundEventAst[], vars:VariableAst[], directives:DirectiveAst[], children:TemplateAst[], ngContentIndex:number, sourceSpan:ParseSourceSpan)',
'NgContentAst.constructor(index:number, ngContentIndex:number, sourceSpan:ParseSourceSpan)', 'EmbeddedTemplateAst.visit(visitor:TemplateAstVisitor, context:any):any',
'NgContentAst.visit(visitor:TemplateAstVisitor, context:any):any', 'NgContentAst',
'PropertyBindingType', 'NgContentAst.constructor(index:number, ngContentIndex:number, sourceSpan:ParseSourceSpan)',
'PropertyBindingType.Attribute', 'NgContentAst.visit(visitor:TemplateAstVisitor, context:any):any',
'PropertyBindingType.Class', 'PropertyBindingType',
'PropertyBindingType.Property', 'PropertyBindingType.Attribute',
'PropertyBindingType.Style', 'PropertyBindingType.Class',
'SourceModule', 'PropertyBindingType.Property',
'SourceModule.constructor(moduleUrl:string, sourceWithModuleRefs:string)', 'PropertyBindingType.Style',
'SourceModule.getSourceWithImports():SourceWithImports', 'SourceModule',
'SourceModule.getSourceWithoutImports(sourceWithModuleRefs:string):string', 'SourceModule.constructor(moduleUrl:string, sourceWithModuleRefs:string)',
'SourceWithImports', 'SourceModule.getSourceWithImports():SourceWithImports',
'SourceWithImports.constructor(source:string, imports:string[][])', 'SourceModule.getSourceWithoutImports(sourceWithModuleRefs:string):string',
'TemplateAst', 'SourceWithImports',
'TemplateAst.sourceSpan:ParseSourceSpan', 'SourceWithImports.constructor(source:string, imports:string[][])',
'TemplateAst.visit(visitor:TemplateAstVisitor, context:any):any', 'TemplateAst',
'TemplateAstVisitor', 'TemplateAst.sourceSpan:ParseSourceSpan',
'TemplateAstVisitor.visitAttr(ast:AttrAst, context:any):any', 'TemplateAst.visit(visitor:TemplateAstVisitor, context:any):any',
'TemplateAstVisitor.visitBoundText(ast:BoundTextAst, context:any):any', 'TemplateAstVisitor',
'TemplateAstVisitor.visitDirective(ast:DirectiveAst, context:any):any', 'TemplateAstVisitor.visitAttr(ast:AttrAst, context:any):any',
'TemplateAstVisitor.visitDirectiveProperty(ast:BoundDirectivePropertyAst, context:any):any', 'TemplateAstVisitor.visitBoundText(ast:BoundTextAst, context:any):any',
'TemplateAstVisitor.visitElement(ast:ElementAst, context:any):any', 'TemplateAstVisitor.visitDirective(ast:DirectiveAst, context:any):any',
'TemplateAstVisitor.visitElementProperty(ast:BoundElementPropertyAst, context:any):any', 'TemplateAstVisitor.visitDirectiveProperty(ast:BoundDirectivePropertyAst, context:any):any',
'TemplateAstVisitor.visitEmbeddedTemplate(ast:EmbeddedTemplateAst, context:any):any', 'TemplateAstVisitor.visitElement(ast:ElementAst, context:any):any',
'TemplateAstVisitor.visitEvent(ast:BoundEventAst, context:any):any', 'TemplateAstVisitor.visitElementProperty(ast:BoundElementPropertyAst, context:any):any',
'TemplateAstVisitor.visitNgContent(ast:NgContentAst, context:any):any', 'TemplateAstVisitor.visitEmbeddedTemplate(ast:EmbeddedTemplateAst, context:any):any',
'TemplateAstVisitor.visitText(ast:TextAst, context:any):any', 'TemplateAstVisitor.visitEvent(ast:BoundEventAst, context:any):any',
'TemplateAstVisitor.visitVariable(ast:VariableAst, context:any):any', 'TemplateAstVisitor.visitNgContent(ast:NgContentAst, context:any):any',
'TemplateCompiler', 'TemplateAstVisitor.visitText(ast:TextAst, context:any):any',
'TemplateCompiler.clearCache():any', 'TemplateAstVisitor.visitVariable(ast:VariableAst, context:any):any',
'TemplateCompiler.compileHostComponentRuntime(type:Type):Promise<HostViewFactory>', 'TemplateCompiler',
'TemplateCompiler.compileStylesheetCodeGen(stylesheetUrl:string, cssText:string):SourceModule[]', 'TemplateCompiler.clearCache():any',
'TemplateCompiler.compileTemplatesCodeGen(components:NormalizedComponentWithViewDirectives[]):SourceModule', 'TemplateCompiler.compileHostComponentRuntime(type:Type):Promise<HostViewFactory>',
'TemplateCompiler.constructor(_runtimeMetadataResolver:RuntimeMetadataResolver, _templateNormalizer:TemplateNormalizer, _templateParser:TemplateParser, _styleCompiler:StyleCompiler, _cdCompiler:ChangeDetectionCompiler, _protoViewCompiler:ProtoViewCompiler, _viewCompiler:ViewCompiler, _resolvedMetadataCache:ResolvedMetadataCache, _genConfig:ChangeDetectorGenConfig)', 'TemplateCompiler.compileStylesheetCodeGen(stylesheetUrl:string, cssText:string):SourceModule[]',
'TemplateCompiler.normalizeDirectiveMetadata(directive:CompileDirectiveMetadata):Promise<CompileDirectiveMetadata>', 'TemplateCompiler.compileTemplatesCodeGen(components:NormalizedComponentWithViewDirectives[]):SourceModule',
'TextAst', 'TemplateCompiler.constructor(_runtimeMetadataResolver:RuntimeMetadataResolver, _templateNormalizer:TemplateNormalizer, _templateParser:TemplateParser, _styleCompiler:StyleCompiler, _cdCompiler:ChangeDetectionCompiler, _protoViewCompiler:ProtoViewCompiler, _viewCompiler:ViewCompiler, _resolvedMetadataCache:ResolvedMetadataCache, _genConfig:ChangeDetectorGenConfig)',
'TextAst.constructor(value:string, ngContentIndex:number, sourceSpan:ParseSourceSpan)', 'TemplateCompiler.normalizeDirectiveMetadata(directive:CompileDirectiveMetadata):Promise<CompileDirectiveMetadata>',
'TextAst.visit(visitor:TemplateAstVisitor, context:any):any', 'TextAst',
'UrlResolver', 'TextAst.constructor(value:string, ngContentIndex:number, sourceSpan:ParseSourceSpan)',
'UrlResolver.constructor(packagePrefix:string)', 'TextAst.visit(visitor:TemplateAstVisitor, context:any):any',
'UrlResolver.resolve(baseUrl:string, url:string):string', 'UrlResolver',
'VariableAst', 'UrlResolver.constructor(packagePrefix:string)',
'VariableAst.constructor(name:string, value:string, sourceSpan:ParseSourceSpan)', 'UrlResolver.resolve(baseUrl:string, url:string):string',
'VariableAst.visit(visitor:TemplateAstVisitor, context:any):any', 'VariableAst',
'XHR', 'VariableAst.constructor(name:string, value:string, sourceSpan:ParseSourceSpan)',
'XHR.get(url:string):Promise<string>', 'VariableAst.visit(visitor:TemplateAstVisitor, context:any):any',
'const COMPILER_PROVIDERS:Array<Type|Provider|any[]>', 'XHR',
'const PLATFORM_DIRECTIVES:OpaqueToken', 'XHR.get(url:string):Promise<string>',
'const PLATFORM_PIPES:OpaqueToken', 'const COMPILER_PROVIDERS:Array<Type|Provider|any[]>',
'const TEMPLATE_TRANSFORMS:any', 'const PLATFORM_DIRECTIVES:OpaqueToken',
'createWithoutPackagePrefix():UrlResolver', 'const PLATFORM_PIPES:OpaqueToken',
'getUrlScheme(url:string):string', 'const TEMPLATE_TRANSFORMS:any',
'templateVisitAll(visitor:TemplateAstVisitor, asts:TemplateAst[], context:any):any[]', 'createWithoutPackagePrefix():UrlResolver',
'var DEFAULT_PACKAGE_URL_PROVIDER:any' 'getUrlScheme(url:string):string',
]; 'templateVisitAll(visitor:TemplateAstVisitor, asts:TemplateAst[], context:any):any[]',
'var DEFAULT_PACKAGE_URL_PROVIDER:any'
];
const INSTRUMENTATION = [ const INSTRUMENTATION = [
'WtfScopeFn', 'WtfScopeFn',
@ -1138,7 +1140,18 @@ describe('public API', () => {
function checkPublicApi(file: string, expected: string[]) { function checkPublicApi(file: string, expected: string[]) {
const sortedActual = publicApi(file).sort(); const sortedActual = publicApi(file).sort();
const sortedExpected = expected.sort(); const sortedExpected = expected.sort();
const missing = sortedActual.filter((i) => sortedExpected.indexOf(i) < 0).map(s => `+${s}`); const missing = sortedActual.filter((i) => sortedExpected.indexOf(i) < 0);
const extra = sortedExpected.filter((i) => sortedActual.indexOf(i) < 0).map(s => `-${s}`); const extra = sortedExpected.filter((i) => sortedActual.indexOf(i) < 0);
expect(missing.concat(extra)).toEqual([]);
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([]);
} }