feat(transformers): collect data needed for the template compiler

Closes #7299
This commit is contained in:
vsavkin
2016-02-26 17:56:40 +00:00
committed by Victor Savkin
parent 1779caf5f8
commit ebe531bf92
38 changed files with 1187 additions and 193 deletions
File diff suppressed because it is too large Load Diff
@@ -109,6 +109,7 @@ export class TemplateCompiler {
hostProperties: directive.hostProperties,
hostAttributes: directive.hostAttributes,
lifecycleHooks: directive.lifecycleHooks,
providers: directive.providers,
template: normalizedTemplate
}));
}
@@ -1,6 +1,8 @@
import {ChangeDetector} from './interfaces';
import {ChangeDetectionStrategy} from './constants';
import {Injectable} from 'angular2/src/core/di';
@Injectable()
export abstract class ChangeDetectorRef {
/**
* Marks all {@link ChangeDetectionStrategy#OnPush} ancestors as to be checked.
@@ -1,4 +1,5 @@
import {unimplemented} from 'angular2/src/facade/exceptions';
import {Injectable} from 'angular2/src/core/di';
import {AppElement} from './element';
/**
@@ -11,6 +12,7 @@ import {AppElement} from './element';
* An `ElementRef` is backed by a render-specific element. In the browser, this is usually a DOM
* element.
*/
@Injectable()
export abstract class ElementRef {
/**
* The underlying native element or `null` if direct access to native elements is not supported
@@ -1,4 +1,5 @@
import {ElementRef, ElementRef_} from './element_ref';
import {Injectable} from 'angular2/src/core/di';
/**
* Represents an Embedded Template that can be used to instantiate Embedded Views.
@@ -12,6 +13,7 @@ import {ElementRef, ElementRef_} from './element_ref';
* {@link ViewContainerRef#createEmbeddedView}, which will create the View and attach it to the
* View Container.
*/
@Injectable()
export abstract class TemplateRef {
/**
* The location in the View where the Embedded View logically belongs to.
@@ -1,6 +1,6 @@
import {ListWrapper} from 'angular2/src/facade/collection';
import {unimplemented} from 'angular2/src/facade/exceptions';
import {ResolvedProvider} from 'angular2/src/core/di';
import {ResolvedProvider, Injectable} from 'angular2/src/core/di';
import {isPresent, isBlank} from 'angular2/src/facade/lang';
import {AppElement} from './element';
@@ -36,6 +36,7 @@ import {
*
* <!-- TODO(i): we are also considering ElementRef#viewContainer api -->
*/
@Injectable()
export abstract class ViewContainerRef {
/**
* Anchor element that specifies the location of this container in the containing View.
+2 -1
View File
@@ -1,5 +1,5 @@
import {ViewEncapsulation} from 'angular2/src/core/metadata/view';
import {Injector} from 'angular2/src/core/di/injector';
import {Injector, Injectable} from 'angular2/src/core/di';
export class RenderComponentType {
constructor(public id: string, public encapsulation: ViewEncapsulation,
@@ -13,6 +13,7 @@ export class RenderDebugInfo {
export interface ParentRenderer { renderComponent(componentType: RenderComponentType): Renderer; }
@Injectable()
export abstract class Renderer implements ParentRenderer {
abstract renderComponent(componentType: RenderComponentType): Renderer;
+1
View File
@@ -34,6 +34,7 @@ let _resolveToFalse = PromiseWrapper.resolve(false);
* `Instruction`.
* The router uses the `RouteRegistry` to get an `Instruction`.
*/
@Injectable()
export class Router {
navigating: boolean = false;
lastNavigationAttempt: string;
@@ -15,7 +15,8 @@ import {
import {
CompileDirectiveMetadata,
CompileTypeMetadata,
CompileTemplateMetadata
CompileTemplateMetadata,
CompileProviderMetadata
} from 'angular2/src/compiler/directive_metadata';
import {ViewEncapsulation} from 'angular2/src/core/metadata/view';
import {ChangeDetectionStrategy} from 'angular2/src/core/change_detection';
@@ -28,8 +29,8 @@ export function main() {
var fullDirectiveMeta: CompileDirectiveMetadata;
beforeEach(() => {
fullTypeMeta =
new CompileTypeMetadata({name: 'SomeType', moduleUrl: 'someUrl', isHost: true});
fullTypeMeta = new CompileTypeMetadata(
{name: 'SomeType', moduleUrl: 'someUrl', isHost: true, diDeps: []});
fullTemplateMeta = new CompileTemplateMetadata({
encapsulation: ViewEncapsulation.Emulated,
template: '<a></a>',
@@ -48,7 +49,8 @@ export function main() {
inputs: ['someProp'],
outputs: ['someEvent'],
host: {'(event1)': 'handler1', '[prop1]': 'expr1', 'attr1': 'attrValue2'},
lifecycleHooks: [LifecycleHooks.OnChanges]
lifecycleHooks: [LifecycleHooks.OnChanges],
providers: [new CompileProviderMetadata({token: 'token', useClass: fullTypeMeta})]
});
});
+17
View File
@@ -530,6 +530,15 @@ var NG_COMPILER = [
"CompileDirectiveMetadata.template=",
"CompileDirectiveMetadata.type",
"CompileDirectiveMetadata.type=",
"CompileDirectiveMetadata.identifier",
"CompileDirectiveMetadata.providers",
"CompileDirectiveMetadata.providers=",
"CompileDirectiveMetadata.queries",
"CompileDirectiveMetadata.queries=",
"CompileDirectiveMetadata.viewProviders",
"CompileDirectiveMetadata.viewProviders=",
"CompileDirectiveMetadata.viewQueries",
"CompileDirectiveMetadata.viewQueries=",
"CompileTemplateMetadata.encapsulation",
"CompileTemplateMetadata.encapsulation=",
"CompileTemplateMetadata.ngContentSelectors",
@@ -550,6 +559,14 @@ var NG_COMPILER = [
"CompileTypeMetadata.name=",
"CompileTypeMetadata.runtime",
"CompileTypeMetadata.runtime=",
"CompileTypeMetadata.diDeps",
"CompileTypeMetadata.diDeps=",
"CompileTypeMetadata.type",
"CompileTypeMetadata.identifier",
"CompileTypeMetadata.prefix",
"CompileTypeMetadata.prefix=",
"CompileTypeMetadata.constConstructor",
"CompileTypeMetadata.constConstructor=",
"DirectiveAst.directive",
"DirectiveAst.directive=",
"DirectiveAst.exportAsVars",
@@ -127,6 +127,7 @@ export class MdDialog {
/**
* Reference to an opened dialog.
*/
@Injectable()
export class MdDialogRef {
// Reference to the MdDialogContainer component.
containerRef: ComponentRef;
@@ -67,6 +67,7 @@ class ReflectionInfoVisitor extends RecursiveAstVisitor<ReflectionInfoModel> {
@override
ReflectionInfoModel visitClassDeclaration(ClassDeclaration node) {
if (node.isAbstract) return null;
if (!node.metadata
.any((a) => _annotationMatcher.hasMatch(a.name, assetId))) {
return null;
@@ -33,9 +33,9 @@ class NgMeta {
static const _TYPE_VALUE = 'type';
static const _VALUE_KEY = 'value';
/// Metadata for each type annotated as a directive/pipe.
/// Type: [CompileDirectiveMetadata]/[CompilePipeMetadata]
final Map<String, dynamic> types;
/// Metadata for each identifier
/// Type: [CompileDirectiveMetadata]|[CompilePipeMetadata]|[CompileTypeMetadata]|[CompileIdentifierMetadata]
final Map<String, dynamic> identifiers;
/// List of other types and names associated with a given name.
final Map<String, List<String>> aliases;
@@ -43,12 +43,13 @@ class NgMeta {
// The NgDeps generated from
final NgDepsModel ngDeps;
NgMeta(
{Map<String, dynamic> types,
Map<String, List<String>> aliases,
this.ngDeps: null})
: this.types = types != null ? types : {},
this.aliases = aliases != null ? aliases : {};
bool definesAlias;
NgMeta({Map<String, List<String>> aliases,
Map<String, dynamic> identifiers,
this.ngDeps: null, this.definesAlias: false})
:this.aliases = aliases != null ? aliases : {},
this.identifiers = identifiers != null ? identifiers : {};
NgMeta.empty() : this();
@@ -69,13 +70,22 @@ class NgMeta {
return false;
}
bool get isEmpty => types.isEmpty && aliases.isEmpty && isNgDepsEmpty;
bool get isEmpty => identifiers.isEmpty && aliases.isEmpty && isNgDepsEmpty;
List<String> get linkingUris {
final r = ngDeps.exports.map((r) => r.uri).toList();
if (definesAlias) {
r.addAll(ngDeps.imports.map((r) => r.uri));
}
return r;
}
/// Parse from the serialized form produced by [toJson].
factory NgMeta.fromJson(Map json) {
var ngDeps = null;
final types = {};
final aliases = {};
final identifiers = {};
var definesAlias = false;
for (var key in json.keys) {
if (key == _NG_DEPS_KEY) {
var ngDepsJsonMap = json[key];
@@ -85,7 +95,11 @@ class NgMeta {
'Unexpected value $ngDepsJsonMap for key "$key" in NgMeta.');
continue;
}
ngDeps = new NgDepsModel()..mergeFromJsonMap(ngDepsJsonMap);
ngDeps = new NgDepsModel()
..mergeFromJsonMap(ngDepsJsonMap);
} else if (key == 'definesAlias') {
definesAlias = json[key];
} else {
var entry = json[key];
if (entry is! Map) {
@@ -93,13 +107,13 @@ class NgMeta {
continue;
}
if (entry[_KIND_KEY] == _TYPE_VALUE) {
types[key] = CompileMetadataWithType.fromJson(entry[_VALUE_KEY]);
identifiers[key] = CompileMetadataWithIdentifier.fromJson(entry[_VALUE_KEY]);
} else if (entry[_KIND_KEY] == _ALIAS_VALUE) {
aliases[key] = entry[_VALUE_KEY];
}
}
}
return new NgMeta(types: types, aliases: aliases, ngDeps: ngDeps);
return new NgMeta(identifiers: identifiers, aliases: aliases, ngDeps: ngDeps, definesAlias: definesAlias);
}
/// Serialized representation of this instance.
@@ -107,41 +121,44 @@ class NgMeta {
var result = {};
result[_NG_DEPS_KEY] = isNgDepsEmpty ? null : ngDeps.writeToJsonMap();
types.forEach((k, v) {
identifiers.forEach((k, v) {
result[k] = {_KIND_KEY: _TYPE_VALUE, _VALUE_KEY: v.toJson()};
});
aliases.forEach((k, v) {
result[k] = {_KIND_KEY: _ALIAS_VALUE, _VALUE_KEY: v};
});
result['definesAlias'] = definesAlias;
return result;
}
/// Merge into this instance all information from [other].
/// This does not include `ngDeps`.
void addAll(NgMeta other) {
types.addAll(other.types);
aliases.addAll(other.aliases);
identifiers.addAll(other.identifiers);
}
/// Returns the metadata for every type associated with the given [alias].
List<dynamic> flatten(String alias) {
var result = [];
var seen = new Set();
helper(name) {
if (!seen.add(name)) {
log.warning('Circular alias dependency for "$name".');
helper(name, path) {
final newPath = []..addAll(path)..add(name);
if (path.contains(name)) {
log.error('Circular alias dependency for "$name". Cycle: ${newPath.join(' -> ')}.');
return;
}
if (types.containsKey(name)) {
result.add(types[name]);
if (identifiers.containsKey(name)) {
result.add(identifiers[name]);
} else if (aliases.containsKey(name)) {
aliases[name].forEach(helper);
aliases[name].forEach((n) => helper(n, newPath));
} else {
log.warning('Unknown alias: "$name".');
log.error('Unknown alias: ${newPath.join(' -> ')}. Make sure you export ${name} from the file where ${path.last} is defined.');
}
}
helper(alias);
helper(alias, []);
return result;
}
}
@@ -21,22 +21,24 @@ import 'url_resolver.dart';
class TypeMetadataReader {
final _DirectiveMetadataVisitor _directiveVisitor;
final _PipeMetadataVisitor _pipeVisitor;
final _CompileTypeMetadataVisitor _typeVisitor;
final TemplateCompiler _templateCompiler;
TypeMetadataReader._(
this._directiveVisitor, this._pipeVisitor, this._templateCompiler);
this._directiveVisitor, this._pipeVisitor, this._templateCompiler, this._typeVisitor);
/// Accepts an [AnnotationMatcher] which tests that an [Annotation]
/// is a [Directive], [Component], or [View].
factory TypeMetadataReader(AnnotationMatcher annotationMatcher,
InterfaceMatcher interfaceMatcher, TemplateCompiler templateCompiler) {
var lifecycleVisitor = new _LifecycleHookVisitor(interfaceMatcher);
var typeVisitor = new _CompileTypeMetadataVisitor(annotationMatcher);
var directiveVisitor =
new _DirectiveMetadataVisitor(annotationMatcher, lifecycleVisitor);
new _DirectiveMetadataVisitor(annotationMatcher, lifecycleVisitor, typeVisitor);
var pipeVisitor = new _PipeMetadataVisitor(annotationMatcher);
return new TypeMetadataReader._(
directiveVisitor, pipeVisitor, templateCompiler);
directiveVisitor, pipeVisitor, templateCompiler, typeVisitor);
}
/// Reads *un-normalized* [CompileDirectiveMetadata]/[CompilePipeMetadata] from the
@@ -52,13 +54,19 @@ class TypeMetadataReader {
Future<dynamic> readTypeMetadata(ClassDeclaration node, AssetId assetId) {
_directiveVisitor.reset(assetId);
_pipeVisitor.reset(assetId);
_typeVisitor.reset(assetId);
node.accept(_directiveVisitor);
node.accept(_pipeVisitor);
node.accept(_typeVisitor);
if (_directiveVisitor.hasMetadata) {
final metadata = _directiveVisitor.createMetadata();
return _templateCompiler.normalizeDirectiveMetadata(metadata);
} else if (_pipeVisitor.hasMetadata) {
return new Future.value(_pipeVisitor.createMetadata());
} else if (_typeVisitor.isInjectable) {
return new Future.value(_typeVisitor.type);
} else {
return new Future.value(null);
}
@@ -124,6 +132,67 @@ bool _expressionToBool(Expression node, String nodeDescription) {
return value;
}
class _CompileTypeMetadataVisitor extends Object
with RecursiveAstVisitor<CompileTypeMetadata> {
bool _isInjectable = false;
CompileTypeMetadata _type;
AssetId _assetId;
final AnnotationMatcher _annotationMatcher;
_CompileTypeMetadataVisitor(this._annotationMatcher);
bool get isInjectable => _isInjectable;
CompileTypeMetadata get type => _type;
void reset(AssetId assetId) {
this._assetId = assetId;
this._isInjectable = false;
this._type = null;
}
@override
Object visitAnnotation(Annotation node) {
final isComponent = _annotationMatcher.isComponent(node, _assetId);
final isDirective = _annotationMatcher.isDirective(node, _assetId);
final isInjectable = _annotationMatcher.isInjectable(node, _assetId);
_isInjectable = _isInjectable || isComponent || isDirective || isInjectable;
return null;
}
@override
Object visitClassDeclaration(ClassDeclaration node) {
node.metadata.accept(this);
if (this._isInjectable) {
_type = new CompileTypeMetadata(
moduleUrl: toAssetUri(_assetId),
name: node.name.toString(),
diDeps: _getCompileDiDependencyMetadata(node),
runtime: null // Intentionally `null`, cannot be provided here.
);
}
return null;
}
List<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;
return new CompileDiDependencyMetadata(token: token);
}).toList();
}
}
/// Visitor responsible for processing a [Directive] annotated
/// [ClassDeclaration] and creating a [CompileDirectiveMetadata] object.
class _DirectiveMetadataVisitor extends Object
@@ -134,10 +203,12 @@ class _DirectiveMetadataVisitor extends Object
final _LifecycleHookVisitor _lifecycleVisitor;
final _CompileTypeMetadataVisitor _typeVisitor;
/// The [AssetId] we are currently processing.
AssetId _assetId;
_DirectiveMetadataVisitor(this._annotationMatcher, this._lifecycleVisitor) {
_DirectiveMetadataVisitor(this._annotationMatcher, this._lifecycleVisitor, this._typeVisitor) {
reset(null);
}
@@ -154,12 +225,14 @@ class _DirectiveMetadataVisitor extends Object
List<String> _inputs;
List<String> _outputs;
Map<String, String> _host;
List<CompileProviderMetadata> _providers;
List<LifecycleHooks> _lifecycleHooks;
CompileTemplateMetadata _cmpTemplate;
CompileTemplateMetadata _viewTemplate;
void reset(AssetId assetId) {
_lifecycleVisitor.reset(assetId);
_typeVisitor.reset(assetId);
_assetId = assetId;
_type = null;
@@ -171,6 +244,7 @@ class _DirectiveMetadataVisitor extends Object
_inputs = <String>[];
_outputs = <String>[];
_host = <String, String>{};
_providers = <CompileProviderMetadata>[];
_lifecycleHooks = null;
_cmpTemplate = null;
_viewTemplate = null;
@@ -192,6 +266,7 @@ class _DirectiveMetadataVisitor extends Object
inputs: _inputs,
outputs: _outputs,
host: _host,
providers: _providers,
lifecycleHooks: _lifecycleHooks,
template: _template);
}
@@ -316,6 +391,41 @@ class _DirectiveMetadataVisitor extends Object
}
}
void _populateProviders(Expression providerValues) {
_checkMeta();
if (providerValues is ListLiteral) {
final providers = providerValues.elements.map((el) {
if (el is PrefixedIdentifier || el is SimpleIdentifier) {
return new CompileProviderMetadata(token: _readIdentifier(el));
} else if (el is InstanceCreationExpression &&
(el.constructorName.toString() == "Provider" ||
el.constructorName.toString() == "Binding")
) {
final token = el.argumentList.arguments.first;
var useClass;
el.argumentList.arguments.skip(1).forEach((arg) {
if (arg.name.toString() == "useClass:") {
final id = _readIdentifier(arg.expression);
useClass = new CompileTypeMetadata(prefix: id.prefix, name: id.name);
}
});
return new CompileProviderMetadata(token: _readIdentifier(token), useClass: useClass);
} else {
throw new ArgumentError(
'Incorrect value. Expected a Provider or a String, but got "${el}".');
}
});
_providers.addAll(providers);
} else {
_providers.add(new CompileProviderMetadata(token: _readIdentifier(providerValues)));
}
}
//TODO Use AnnotationMatcher instead of string matching
bool _isAnnotation(Annotation node, String annotationName) {
var id = node.name;
@@ -356,12 +466,10 @@ class _DirectiveMetadataVisitor extends Object
@override
Object visitClassDeclaration(ClassDeclaration node) {
node.metadata.accept(this);
node.accept(_typeVisitor);
_type = _typeVisitor.type;
if (this._hasMetadata) {
_type = new CompileTypeMetadata(
moduleUrl: toAssetUri(_assetId),
name: node.name.toString(),
runtime: null // Intentionally `null`, cannot be provided here.
);
_lifecycleHooks = node.implementsClause != null
? node.implementsClause.accept(_lifecycleVisitor)
: const [];
@@ -405,6 +513,9 @@ class _DirectiveMetadataVisitor extends Object
case 'events':
_populateEvents(node.expression);
break;
case 'providers':
_populateProviders(node.expression);
break;
}
return null;
}
@@ -687,3 +798,22 @@ class _PipeMetadataVisitor extends Object with RecursiveAstVisitor<Object> {
_pure = _expressionToBool(pureValue, 'Pipe#pure');
}
}
dynamic _readIdentifier(dynamic el) {
if (el is PrefixedIdentifier) {
return new CompileIdentifierMetadata(name: '${el.identifier}', prefix: '${el.prefix}');
} else if (el is SimpleIdentifier) {
return new CompileIdentifierMetadata(name: '$el');
} else if (el is SimpleStringLiteral){
return el.value;
} else if (el is InstanceCreationExpression){
return new CompileIdentifierMetadata(name: '${el.constructorName}', constConstructor: true);
} else {
throw new ArgumentError('Incorrect identifier "${el}".');
}
}
@@ -61,10 +61,10 @@ Future _linkRecursive(NgMeta ngMeta, AssetReader reader, AssetId assetId,
}
var assetUri = toAssetUri(assetId);
return Future.wait(ngMeta.ngDeps.exports
.where((export) => !isDartCoreUri(export.uri))
.map((export) =>
_urlResolver.resolve(assetUri, toSummaryExtension(export.uri)))
return Future.wait(ngMeta.linkingUris
.where((uri) => !isDartCoreUri(uri))
.map((uri) =>
_urlResolver.resolve(assetUri, toSummaryExtension(uri)))
.where((uri) => !seen.contains(uri))
.map((uri) async {
seen.add(uri);
@@ -5,6 +5,7 @@ import 'dart:async';
import 'package:analyzer/analyzer.dart';
import 'package:barback/barback.dart' show AssetId;
import 'package:angular2/src/compiler/directive_metadata.dart' show CompileIdentifierMetadata;
import 'package:angular2/src/compiler/template_compiler.dart';
import 'package:angular2/src/transform/common/annotation_matcher.dart';
import 'package:angular2/src/transform/common/asset_reader.dart';
@@ -14,6 +15,7 @@ import 'package:angular2/src/transform/common/interface_matcher.dart';
import 'package:angular2/src/transform/common/logging.dart';
import 'package:angular2/src/transform/common/ng_compiler.dart';
import 'package:angular2/src/transform/common/ng_meta.dart';
import 'package:angular2/src/transform/common/url_resolver.dart';
import 'package:angular2/src/transform/common/zone.dart' as zone;
import 'inliner.dart';
@@ -89,14 +91,17 @@ class _NgMetaVisitor extends Object with SimpleAstVisitor<Object> {
@override
Object visitClassDeclaration(ClassDeclaration node) {
_normalizations.add(
_reader.readTypeMetadata(node, assetId).then((compileMetadataWithType) {
if (compileMetadataWithType != null) {
ngMeta.types[compileMetadataWithType.type.name] =
compileMetadataWithType;
_reader.readTypeMetadata(node, assetId).then((compileMetadataWithIdentifier) {
if (compileMetadataWithIdentifier!= null) {
ngMeta.identifiers[compileMetadataWithIdentifier.identifier.name] =
compileMetadataWithIdentifier;
} else {
ngMeta.identifiers[node.name.name] = new CompileIdentifierMetadata(name: node.name.name, moduleUrl: toAssetUri(assetId));
}
}).catchError((err) {
log.error('ERROR: $err', asset: assetId);
}));
return null;
}
@@ -108,6 +113,11 @@ class _NgMetaVisitor extends Object with SimpleAstVisitor<Object> {
// doesn't support decorators on variable declarations (see
// angular/angular#1747 and angular/ts2dart#249 for context).
outer: for (var variable in node.variables.variables) {
if (variable.isConst) {
ngMeta.identifiers[variable.name.name] =
new CompileIdentifierMetadata(name: variable.name.name, moduleUrl: toAssetUri(assetId));
}
var initializer = variable.initializer;
if (initializer != null && initializer is ListLiteral) {
var otherNames = [];
@@ -117,9 +127,24 @@ class _NgMetaVisitor extends Object with SimpleAstVisitor<Object> {
if (exp is! SimpleIdentifier) continue outer;
otherNames.add(exp.name);
}
ngMeta.definesAlias = true;
ngMeta.aliases[variable.name.name] = otherNames;
}
}
return null;
}
@override
Object visitFunctionTypeAlias(FunctionTypeAlias node) {
ngMeta.identifiers[node.name.name] =
new CompileIdentifierMetadata(name: node.name.name, moduleUrl: toAssetUri(assetId));
return null;
}
@override
Object visitEnumDeclaration(EnumDeclaration node) {
ngMeta.identifiers[node.name.name] =
new CompileIdentifierMetadata(name: node.name.name, moduleUrl: toAssetUri(assetId));
return null;
}
}
@@ -74,8 +74,8 @@ class _CompileDataCreator {
var hasTemplate = ngDeps != null &&
ngDeps.reflectables != null &&
ngDeps.reflectables.any((reflectable) {
if (ngMeta.types.containsKey(reflectable.name)) {
final metadata = ngMeta.types[reflectable.name];
if (ngMeta.identifiers.containsKey(reflectable.name)) {
final metadata = ngMeta.identifiers[reflectable.name];
return metadata is CompileDirectiveMetadata &&
metadata.template != null;
}
@@ -91,8 +91,8 @@ class _CompileDataCreator {
final platformPipes = await _readPlatformTypes(this.platformPipes, 'pipes');
for (var reflectable in ngDeps.reflectables) {
if (ngMeta.types.containsKey(reflectable.name)) {
final compileDirectiveMetadata = ngMeta.types[reflectable.name];
if (ngMeta.identifiers.containsKey(reflectable.name)) {
final compileDirectiveMetadata = ngMeta.identifiers[reflectable.name];
if (compileDirectiveMetadata is CompileDirectiveMetadata &&
compileDirectiveMetadata.template != null) {
final compileDatum = new NormalizedComponentWithViewDirectives(
@@ -106,6 +106,9 @@ class _CompileDataCreator {
compileDatum.pipes
.addAll(_resolveTypeMetadata(ngMetaMap, reflectable.pipes));
compileData[reflectable] = compileDatum;
_resolveDiDependencyMetadata(ngMetaMap, compileDirectiveMetadata.type, compileDirectiveMetadata.type.diDeps);
_resolveProviderMetadata(ngMetaMap, compileDirectiveMetadata);
}
}
}
@@ -117,16 +120,16 @@ class _CompileDataCreator {
var resolvedMetadata = [];
for (var dep in prefixedTypes) {
if (!ngMetaMap.containsKey(dep.prefix)) {
log.warning(
log.error(
'Missing prefix "${dep.prefix}" '
'needed by "${dep}" from metadata map',
'needed by "${dep}" from metadata map,',
asset: entryPoint);
continue;
return null;
}
final depNgMeta = ngMetaMap[dep.prefix];
if (depNgMeta.types.containsKey(dep.name)) {
resolvedMetadata.add(depNgMeta.types[dep.name]);
if (depNgMeta.identifiers.containsKey(dep.name)) {
resolvedMetadata.add(depNgMeta.identifiers[dep.name]);
} else if (depNgMeta.aliases.containsKey(dep.name)) {
resolvedMetadata.addAll(depNgMeta.flatten(dep.name));
} else {
@@ -141,6 +144,98 @@ class _CompileDataCreator {
return resolvedMetadata;
}
void _resolveProviderMetadata(Map<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);
}
}
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 (id.name == "Window") {
return new CompileIdentifierMetadata(name: "Window", moduleUrl: 'dart:html');
} else if (id.name == "Clock") {
return new CompileIdentifierMetadata(name: "Clock", moduleUrl: 'dart:time');
} else if (id.name == "Profiler") {
return new CompileIdentifierMetadata(name: "Clock", moduleUrl: 'profiler');
} else {
log.error(
'Missing identifier "${id.name}" '
'needed by "${neededBy.name}" from metadata map',
asset: entryPoint);
return null;
}
}
bool _isPrimitive(String typeName) =>
typeName == "String" || typeName == "Object" || typeName == "num" || typeName == "int" || typeName == "double" || typeName == "bool";
Future<List<dynamic>> _readPlatformTypes(
List<String> inputPlatformTypes, String configOption) async {
if (inputPlatformTypes == null) return const [];
@@ -168,8 +263,8 @@ class _CompileDataCreator {
if (jsonString != null && jsonString.isNotEmpty) {
var newMetadata = new NgMeta.fromJson(JSON.decode(jsonString));
if (newMetadata.types.containsKey(token)) {
return [newMetadata.types[token]];
if (newMetadata.identifiers.containsKey(token)) {
return [newMetadata.identifiers[token]];
} else if (newMetadata.aliases.containsKey(token)) {
return newMetadata.flatten(token);
} else {
@@ -194,7 +289,6 @@ class _CompileDataCreator {
return map;
}
final resolver = const TransformerUrlResolver();
ngMeta.ngDeps.imports
.where((model) => !isDartCoreUri(model.uri))
.forEach((model) {
@@ -242,6 +336,7 @@ class _CompileDataCreator {
var ngMeta = retVal[prefix] = new NgMeta.empty();
for (var importAssetUri in prefixToImports[prefix]) {
var metaAssetId = fromUri(toMetaExtension(importAssetUri));
if (await reader.hasInput(metaAssetId)) {
try {
var jsonString = await reader.readAsString(metaAssetId);
@@ -42,16 +42,18 @@ Future<Outputs> processTemplates(AssetReader reader, AssetId assetId,
var viewDefResults = await createCompileData(
reader, assetId, platformDirectives, platformPipes);
if (viewDefResults == null) return null;
final compileTypeMetadatas = viewDefResults.ngMeta.types.values;
final compileTypeMetadatas = viewDefResults.ngMeta.identifiers.values;
if (compileTypeMetadatas.isNotEmpty) {
var processor = new reg.Processor();
compileTypeMetadatas.forEach(processor.process);
viewDefResults.ngMeta.ngDeps.getters
.addAll(processor.getterNames.map((e) => e.sanitizedName));
viewDefResults.ngMeta.ngDeps.setters
.addAll(processor.setterNames.map((e) => e.sanitizedName));
viewDefResults.ngMeta.ngDeps.methods
.addAll(processor.methodNames.map((e) => e.sanitizedName));
if (viewDefResults.ngMeta.ngDeps != null) {
viewDefResults.ngMeta.ngDeps.getters
.addAll(processor.getterNames.map((e) => e.sanitizedName));
viewDefResults.ngMeta.ngDeps.setters
.addAll(processor.setterNames.map((e) => e.sanitizedName));
viewDefResults.ngMeta.ngDeps.methods
.addAll(processor.methodNames.map((e) => e.sanitizedName));
}
}
var templateCompiler = zone.templateCompiler;
if (templateCompiler == null) {
@@ -98,4 +100,4 @@ class Outputs {
final SourceModule templatesSource;
Outputs._(this.ngDeps, this.templatesSource);
}
}
@@ -14,7 +14,7 @@ class Processor implements CodegenModel {
/// The names of all requested `method`s.
final Set<ReflectiveAccessor> methodNames = new Set<ReflectiveAccessor>();
void process(CompileMetadataWithType meta) {
void process(Object meta) {
if (meta is CompileDirectiveMetadata) {
if (meta.outputs != null) {
meta.outputs.keys.forEach((eventName) {
@@ -82,4 +82,4 @@ CompileDirectiveMetadata createBaz([String moduleBase = 'asset:a']) =>
name: 'BazComponent',
moduleUrl: '$moduleBase/export_cycle_files/baz.dart',
selector: 'baz',
template: 'Baz');
template: 'Baz');
@@ -8,7 +8,7 @@ import 'package:guinness/guinness.dart';
main() => allTests();
void allTests() {
var mockData = [
var mockDirMetadata = [
CompileDirectiveMetadata.create(type: new CompileTypeMetadata(name: 'N1')),
CompileDirectiveMetadata.create(type: new CompileTypeMetadata(name: 'N2')),
CompileDirectiveMetadata.create(type: new CompileTypeMetadata(name: 'N3')),
@@ -28,14 +28,16 @@ void allTests() {
it('should be lossless', () {
var a = new NgMeta.empty();
a.types['T0'] = mockData[0];
a.types['T1'] = mockData[1];
a.types['T2'] = mockData[2];
a.types['T3'] = mockData[3];
a.identifiers['T0'] = mockDirMetadata[0];
a.identifiers['T1'] = mockDirMetadata[1];
a.identifiers['T2'] = mockDirMetadata[2];
a.identifiers['T3'] = mockDirMetadata[3];
a.aliases['a1'] = ['T1'];
a.aliases['a2'] = ['a1'];
a.aliases['a3'] = ['T3', 'a2'];
a.aliases['a4'] = ['a3', 'T3'];
_checkSimilar(a, new NgMeta.fromJson(a.toJson()));
});
});
@@ -43,35 +45,46 @@ void allTests() {
describe('flatten', () {
it('should include recursive aliases.', () {
var a = new NgMeta.empty();
a.types['T0'] = mockData[0];
a.types['T1'] = mockData[1];
a.types['T2'] = mockData[2];
a.types['T3'] = mockData[3];
a.identifiers['T0'] = mockDirMetadata[0];
a.identifiers['T1'] = mockDirMetadata[1];
a.identifiers['T2'] = mockDirMetadata[2];
a.identifiers['T3'] = mockDirMetadata[3];
a.aliases['a1'] = ['T1'];
a.aliases['a2'] = ['a1'];
a.aliases['a3'] = ['T3', 'a2'];
a.aliases['a4'] = ['a3', 'T0'];
expect(a.flatten('a4')).toEqual([mockData[3], mockData[1], mockData[0]]);
expect(a.flatten('a4')).toEqual([mockDirMetadata[3], mockDirMetadata[1], mockDirMetadata[0]]);
});
it('should detect cycles.', () {
var a = new NgMeta.empty();
a.types['T0'] = mockData[0];
a.aliases['a1'] = ['T0', 'a1'];
a.identifiers['T0'] = mockDirMetadata[0];
a.aliases['a1'] = ['T0', 'a2'];
a.aliases['a2'] = ['a1'];
expect(a.flatten('a1')).toEqual([mockData[0]]);
expect(() => a.flatten('a1')).toThrowWith(message: new RegExp('Cycle: a1 -> a2 -> a1.'));
});
it('should allow duplicates.', () {
var a = new NgMeta.empty();
a.identifiers['T0'] = mockDirMetadata[0];
a.aliases['a1'] = ['T0', 'a2'];
a.aliases['a2'] = ['T0'];
expect(() => a.flatten('a1')).not.toThrow();
});
});
describe('merge', () {
it('should merge all types on addAll', () {
it('should merge all identifiers on addAll', () {
var a = new NgMeta.empty();
var b = new NgMeta.empty();
a.types['T0'] = mockData[0];
b.types['T1'] = mockData[1];
a.identifiers['T0'] = mockDirMetadata[0];
b.identifiers['T1'] = mockDirMetadata[1];
a.addAll(b);
expect(a.types).toContain('T1');
expect(a.types['T1']).toEqual(mockData[1]);
expect(a.identifiers).toContain('T1');
expect(a.identifiers['T1']).toEqual(mockDirMetadata[1]);
});
it('should merge all aliases on addAll', () {
@@ -87,12 +100,12 @@ void allTests() {
}
_checkSimilar(NgMeta a, NgMeta b) {
expect(a.types.length).toEqual(b.types.length);
expect(a.identifiers.length).toEqual(b.identifiers.length);
expect(a.aliases.length).toEqual(b.aliases.length);
for (var k in a.types.keys) {
expect(b.types).toContain(k);
var at = a.types[k];
var bt = b.types[k];
for (var k in a.identifiers.keys) {
expect(b.identifiers).toContain(k);
var at = a.identifiers[k];
var bt = b.identifiers[k];
expect(at.type.name).toEqual(bt.type.name);
}
for (var k in a.aliases.keys) {
@@ -27,13 +27,15 @@ void allTests() {
var fooNgMeta, fooAssetId;
var barNgMeta, barAssetId;
var bazNgMeta, bazAssetId;
var aliasNgMeta, aliasAssetId;
/// Call after making changes to `fooNgMeta`, `barNgMeta`, or `bazNgMeta` and
/// before trying to read them from `reader`.
final updateReader = () => reader
..addAsset(fooAssetId, JSON.encode(fooNgMeta.toJson()))
..addAsset(barAssetId, JSON.encode(barNgMeta.toJson()))
..addAsset(bazAssetId, JSON.encode(bazNgMeta.toJson()));
..addAsset(bazAssetId, JSON.encode(bazNgMeta.toJson()))
..addAsset(aliasAssetId, JSON.encode(aliasNgMeta.toJson()));
beforeEach(() {
reader = new TestAssetReader();
@@ -41,19 +43,24 @@ void allTests() {
// Establish some test NgMeta objects with one Component each.
var fooComponentMeta = createFoo(moduleBase);
fooNgMeta = new NgMeta(ngDeps: new NgDepsModel());
fooNgMeta.types[fooComponentMeta.type.name] = fooComponentMeta;
fooNgMeta.identifiers[fooComponentMeta.type.name] = fooComponentMeta;
var barComponentMeta = createBar(moduleBase);
barNgMeta = new NgMeta(ngDeps: new NgDepsModel());
barNgMeta.types[barComponentMeta.type.name] = barComponentMeta;
barNgMeta.identifiers[barComponentMeta.type.name] = barComponentMeta;
var bazComponentMeta = createBaz(moduleBase);
bazNgMeta = new NgMeta(ngDeps: new NgDepsModel());
barNgMeta.types[bazComponentMeta.type.name] = bazComponentMeta;
barNgMeta.identifiers[bazComponentMeta.type.name] = bazComponentMeta;
aliasNgMeta = new NgMeta(ngDeps: new NgDepsModel());
aliasNgMeta.aliases["Providers"] = ["someAlias"];
aliasNgMeta.definesAlias = true;
fooAssetId = new AssetId('a', toSummaryExtension('lib/foo.dart'));
barAssetId = new AssetId('a', toSummaryExtension('lib/bar.dart'));
bazAssetId = new AssetId('a', toSummaryExtension('lib/baz.dart'));
aliasAssetId = new AssetId('a', toSummaryExtension('lib/alais.dart'));
updateReader();
});
@@ -63,11 +70,11 @@ void allTests() {
updateReader();
var extracted = await _testLink(reader, fooAssetId);
expect(extracted.types).toContain('FooComponent');
expect(extracted.types).toContain('BarComponent');
expect(extracted.identifiers).toContain('FooComponent');
expect(extracted.identifiers).toContain('BarComponent');
expect(extracted.types['FooComponent'].selector).toEqual('foo');
expect(extracted.types['BarComponent'].selector).toEqual('bar');
expect(extracted.identifiers['FooComponent'].selector).toEqual('foo');
expect(extracted.identifiers['BarComponent'].selector).toEqual('bar');
});
it('should include `DirectiveMetadata` recursively from exported files.',
@@ -77,13 +84,33 @@ void allTests() {
updateReader();
var extracted = await _testLink(reader, fooAssetId);
expect(extracted.types).toContain('FooComponent');
expect(extracted.types).toContain('BarComponent');
expect(extracted.types).toContain('BazComponent');
expect(extracted.identifiers).toContain('FooComponent');
expect(extracted.identifiers).toContain('BarComponent');
expect(extracted.identifiers).toContain('BazComponent');
expect(extracted.types['FooComponent'].selector).toEqual('foo');
expect(extracted.types['BarComponent'].selector).toEqual('bar');
expect(extracted.types['BazComponent'].selector).toEqual('baz');
expect(extracted.identifiers['FooComponent'].selector).toEqual('foo');
expect(extracted.identifiers['BarComponent'].selector).toEqual('bar');
expect(extracted.identifiers['BazComponent'].selector).toEqual('baz');
});
it('should include metadata recursively from imported files when they are aliases.',
() async {
aliasNgMeta.ngDeps.imports.add(new ImportModel()..uri = 'bar.dart');
updateReader();
var extracted = await _testLink(reader, aliasAssetId);
expect(extracted.identifiers).toContain('BarComponent');
});
it('should NOT include metadata recursively from imported files when no aliases defined.',
() async {
fooNgMeta.ngDeps.imports.add(new ImportModel()..uri = 'bar.dart');
barNgMeta.ngDeps.imports.add(new ImportModel()..uri = 'baz.dart');
updateReader();
var extracted = await _testLink(reader, fooAssetId);
expect(extracted.identifiers).not.toContain('BarComponent');
expect(extracted.identifiers).not.toContain('BazComponent');
});
it('should handle `DirectiveMetadata` export cycles gracefully.', () async {
@@ -93,9 +120,9 @@ void allTests() {
updateReader();
var extracted = await _testLink(reader, bazAssetId);
expect(extracted.types).toContain('FooComponent');
expect(extracted.types).toContain('BarComponent');
expect(extracted.types).toContain('BazComponent');
expect(extracted.identifiers).toContain('FooComponent');
expect(extracted.identifiers).toContain('BarComponent');
expect(extracted.identifiers).toContain('BazComponent');
});
it(
@@ -109,11 +136,11 @@ void allTests() {
var extracted = await _testLink(reader, fooAssetId);
expect(extracted.types).toContain('FooComponent');
expect(extracted.types).toContain('BarComponent');
expect(extracted.identifiers).toContain('FooComponent');
expect(extracted.identifiers).toContain('BarComponent');
expect(extracted.types['FooComponent'].selector).toEqual('foo');
expect(extracted.types['BarComponent'].selector).toEqual('bar');
expect(extracted.identifiers['FooComponent'].selector).toEqual('foo');
expect(extracted.identifiers['BarComponent'].selector).toEqual('bar');
});
});
File diff suppressed because it is too large Load Diff
@@ -1,7 +1,7 @@
library angular2.test.transform.directive_processor.directive_files.components;
import 'package:angular2/angular2.dart'
show Component, Directive, View, NgElement, Output, Input;
show Component, Directive, View, NgElement, Output, Input, Provider;
import 'dep1.dart';
import 'dep2.dart' as dep2;
@@ -84,3 +84,43 @@ class ComponentWithHostListeners {
@HostListener('c', ['\$event.target', '\$event.target.value']) void onC(
t, v) {}
}
@Component(
selector: 'component-with-providers-types',
template: '',
providers: [ServiceDep, dep2.ServiceDep])
class ComponentWithProvidersTypes {}
@Component(
selector: 'component-with-providers-string-token',
template: '',
providers: [const Provider("StringDep", useClass: ServiceDep)])
class ComponentWithProvidersStringToken {}
@Component(
selector: 'ComponentWithProvidersConstToken',
template: '',
providers: [const Provider(const ServiceDep(), useClass: ServiceDep)])
class ComponentWithProvidersConstToken {
ComponentWithProvidersConstToken();
}
@Component(
selector: 'component-with-providers-use-class',
template: '',
providers: [const Provider(ServiceDep, useClass: ServiceDep)])
class ComponentWithProvidersUseClass {}
@Component(
selector: 'component-with-di-deps',
template: '')
class ComponentWithDiDeps {
ComponentWithDiDeps(ServiceDep arg1, @Inject(ServiceDep) arg2);
}
@Component(
selector: 'component-with-di-deps-string-token',
template: '')
class ComponentWithDiDepsStrToken {
ComponentWithDiDepsStrToken(@Inject("StringDep") arg1);
}
@@ -1,6 +1,6 @@
library angular2.test.transform.directive_processor.directive_files.dep1;
import 'package:angular2/angular2.dart' show Component, Directive, View, Pipe;
import 'package:angular2/angular2.dart' show Component, Directive, View, Pipe, Injectable;
@Component(selector: 'dep1')
@View(template: 'Dep1')
@@ -8,3 +8,8 @@ class Dep {}
@Pipe(name: 'dep1')
class PipeDep {}
@Injectable()
class ServiceDep {
const ServiceDep();
}
@@ -1,6 +1,6 @@
library angular2.test.transform.directive_processor.directive_files.dep2;
import 'package:angular2/angular2.dart' show Component, Directive, View, Pipe;
import 'package:angular2/angular2.dart' show Component, Directive, View, Pipe, Injectable;
@Component(selector: 'dep2')
@View(template: 'Dep2')
@@ -8,3 +8,6 @@ class Dep {}
@Pipe(name: 'dep2')
class PipeDep {}
@Injectable()
class ServiceDep {}
@@ -0,0 +1,10 @@
library angular2.test.transform.directive_processor.directive_files.components;
import 'package:angular2/angular2.dart'
show Injectable, Inject;
import 'dep1.dart';
@Injectable()
class Service {
Service(ServiceDep arg1, @Inject(ServiceDep) arg2);
}
@@ -0,0 +1,11 @@
library angular2.test.transform.directive_processor.identifiers.classes;
import 'package:angular2/angular2.dart' show Injectable;
@Injectable()
class Service1 {}
@Injectable()
class Service2 {}
class Service3 {}
@@ -0,0 +1,3 @@
library angular2.test.transform.directive_processor.identifiers.classes_no_injectable;
abstract class ClassA {}
@@ -0,0 +1,5 @@
library angular2.test.transform.directive_processor.identifiers.constants;
const a = "a";
const b = "b";
var c = "c";

Some files were not shown because too many files have changed in this diff Show More