refactor(transformer): use the new compiler

Part of #3605
This commit is contained in:
Tim Blasi 2015-10-01 09:10:26 -07:00 committed by Tobias Bosch
parent a5622304de
commit 52236bd765
159 changed files with 2770 additions and 2162 deletions

View File

@ -34,7 +34,6 @@ class _ExtractQueryFieldsFromAnnotation extends Object
}
}
class _ExtractQueryFieldsFromPropMetadata extends Object
with RecursiveAstVisitor<Object> {
final ConstantEvaluator _evaluator = new ConstantEvaluator();
@ -52,7 +51,10 @@ class _ExtractQueryFieldsFromPropMetadata extends Object
var res = false;
list.elements.forEach((item) {
var n = item.constructorName.toString();
if(n == "ContentChild" || n == "ViewChild" || n == "ContentChildren" || n == "ViewChildren") {
if (n == "ContentChild" ||
n == "ViewChild" ||
n == "ContentChildren" ||
n == "ViewChildren") {
res = true;
}
});
@ -65,14 +67,12 @@ class _ExtractQueryFieldsFromPropMetadata extends Object
}
}
Future<String> createNgSettersAndGetters(
AssetReader reader, AssetId entryPoint) async {
NgDeps ngDeps = await NgDeps.parse(reader, entryPoint);
String code = ngDeps.code;
var setters = _generateSetters(_createPropertiesMap(ngDeps));
var getters = _generateGetters(_createEventPropertiesList(ngDeps));
ngDeps.registeredTypes.forEach((t) {
final fromAnnotation = new _ExtractQueryFieldsFromAnnotation();
@ -86,16 +86,13 @@ Future<String> createNgSettersAndGetters(
setters.addAll(_generateSetters(fromPropMetadata.asMap()));
});
if (setters.isEmpty && getters.isEmpty) return code;
if (setters.isEmpty) return code;
var out = new StringBuffer();
var codeInjectIdx = ngDeps.registeredTypes.last.registerMethod.end;
out.write(code.substring(0, codeInjectIdx));
if (setters.isNotEmpty) {
out.write('..registerSetters({${setters.join(', ')}})');
}
if (getters.isNotEmpty) {
out.write('..registerGetters({${getters.join(', ')}})');
}
out.write(code.substring(codeInjectIdx));
return '$out';
}
@ -123,7 +120,7 @@ List<String> _generateSetters(Map<String, String> bindMap) {
/// the bind properties and the values are either the one and only type
/// binding to that property or the empty string.
Map<String, String> _createPropertiesMap(NgDeps ngDeps) {
var visitor = new ExtractNamedExpressionVisitor('inputs');
var visitor = new ExtractNamedExpressionVisitor('properties');
var bindMap = {};
ngDeps.registeredTypes.forEach((RegisteredType t) {
visitor.bindConfig.clear();
@ -147,37 +144,3 @@ Map<String, String> _createPropertiesMap(NgDeps ngDeps) {
});
return bindMap;
}
/// Consumes the list generated by {@link _createEventPropertiesList} to codegen
/// getters.
List<String> _generateGetters(List<String> eventProperties) {
var getters = [];
// TODO(kegluneq): Include types for receivers. See #886.
for (var property in eventProperties) {
if (!prop.isValid(property)) {
// TODO(kegluenq): Eagerly throw here once #1295 is addressed.
getters.add(prop.lazyInvalidGetter(property));
} else {
getters.add(''' '${prop.sanitize(property)}': (o) => o.$property''');
}
}
return getters;
}
/// Collapses all `events` in {@link ngDeps} into a list of corresponding
/// property names.
List<String> _createEventPropertiesList(NgDeps ngDeps) {
var visitor = new ExtractNamedExpressionVisitor('outputs');
var propertyNames = [];
ngDeps.registeredTypes.forEach((RegisteredType t) {
visitor.bindConfig.clear();
t.annotations.accept(visitor);
visitor.bindConfig.forEach((String config) {
// See comments for `Directive` in annotations_impl/annotations.ts for
// details on how `events` is specified. We are pulling out the property
// name only (everything before the first `:`).
propertyNames.add(config.split(':').first.trim());
});
});
return propertyNames;
}

View File

@ -38,17 +38,9 @@ class NgDepsVisitor extends RecursiveAstVisitor<Object> {
}
void _createModel(String libraryUri) {
_model = new NgDepsModel()..libraryUri = libraryUri;
// We need to import & export the original file.
var origDartFile = path.basename(processedFile.path);
_model.imports.add(new ImportModel()..uri = origDartFile);
_model.exports.add(new ExportModel()..uri = origDartFile);
// Used to register reflective information.
_model.imports.add(new ImportModel()
..uri = REFLECTOR_IMPORT
..prefix = REFLECTOR_PREFIX);
_model = new NgDepsModel()
..libraryUri = libraryUri
..sourceFile = path.basename(processedFile.path);
}
@override
@ -139,6 +131,14 @@ abstract class NgDepsWriterMixin
buffer.writeln('library ${model.libraryUri}${DEPS_EXTENSION};\n');
}
// We need to import & export the source file.
writeImportModel(new ImportModel()..uri = model.sourceFile);
// Used to register reflective information.
writeImportModel(new ImportModel()
..uri = REFLECTOR_IMPORT
..prefix = REFLECTOR_PREFIX);
// We do not support `partUris`, so skip outputting them.
for (var importModel in model.imports) {
// Ignore deferred imports here so as to not load the deferred libraries
@ -149,6 +149,7 @@ abstract class NgDepsWriterMixin
writeImportModel(importModel);
}
writeExportModel(new ExportModel()..uri = model.sourceFile);
model.exports.forEach(writeExportModel);
buffer

View File

@ -0,0 +1,34 @@
library angular2.transform.common.code.reflection_info_code;
import 'package:angular2/src/compiler/source_module.dart';
import 'uri.dart';
/// Writes the full Dart code for the provided [SourceModule].
///
/// If `libraryName` is provided, the generated source will be generated with
/// the approprate "library" directive.
String writeSourceModule(SourceModule sourceModule, {String libraryName}) {
if (sourceModule == null) return null;
var buf = new StringBuffer();
var sourceWithImports = sourceModule.getSourceWithImports();
if (libraryName != null && libraryName.isNotEmpty) {
buf..writeln('library $libraryName;')..writeln();
}
sourceWithImports.imports.forEach((import) {
// Format for importLine := [uri, prefix]
if (import.length != 2) {
throw new FormatException(
'Unexpected import format! '
'Angular 2 compiler returned imports in an unexpected format. '
'Expected [<import_uri>, <prefix>].',
import.join(', '));
}
buf.writeln(writeImportUri(import[0],
prefix: import[1], fromAbsolute: sourceModule.moduleUrl));
});
buf..writeln()..writeln(sourceWithImports.source);
return buf.toString();
}

View File

@ -0,0 +1,76 @@
library angular2.transform.common.code.reflection_info_code;
import 'package:angular2/src/transform/common/url_resolver.dart';
import 'package:path/path.dart' as path;
/// Generates an `import` statement for the file specified by `importPath`.
///
/// If `fromAbsolute` is specified, `importPath` may be a relative path,
/// otherwise it is expected to be absolute.
String writeImportUri(String importPath, {String prefix, String fromAbsolute}) {
var codegenImportPath;
var resolver = const TransformerUrlResolver();
var importUri = resolver.toAssetScheme(Uri.parse(importPath));
if (_canPackageImport(importUri) ||
fromAbsolute == null ||
fromAbsolute.isEmpty) {
codegenImportPath = _toPackageImport(importUri);
} else {
var moduleUri = resolver.toAssetScheme(Uri.parse(fromAbsolute));
if (_canImportRelative(importUri, from: moduleUri)) {
codegenImportPath = path.url.relative(importUri.toString(),
from: path.dirname(moduleUri.toString()));
} else {
var errMsg;
if (fromAbsolute == null || fromAbsolute.isEmpty) {
errMsg = 'Can only import $importPath using a relative uri';
} else {
errMsg = 'Cannot import $importPath from $fromAbsolute';
}
throw new FormatException(errMsg, importPath);
}
}
if (prefix != null && prefix.isNotEmpty) {
prefix = ' as $prefix';
}
return 'import \'$codegenImportPath\'$prefix;';
}
// For a relative import, the scheme, first (package) and second (lib|test|web)
// path segments must be equal.
bool _canImportRelative(Uri importUri, {Uri from}) {
if (importUri == null) throw new ArgumentError.notNull('importUri');
if (from == null) throw new ArgumentError.notNull('from');
assert(importUri.scheme == 'asset');
assert(importUri.pathSegments.length >= 2);
assert(from.scheme == 'asset');
assert(from.pathSegments.length >= 2);
return importUri.pathSegments.first == from.pathSegments.first &&
importUri.pathSegments[1] == from.pathSegments[1];
}
/// Pub's package scheme assumes that an asset lives under the lib/ directory,
/// so an asset: Uri is package-importable if its second path segment is lib/.
///
/// For a file located at angular2/lib/src/file.dart:
/// - Asset scheme => asset:angular2/lib/src/file.dart
/// - Package scheme => package:angular2/src/file.dart
bool _canPackageImport(Uri assetImport) {
if (assetImport == null) throw new ArgumentError.notNull('assetImport');
if (!assetImport.isAbsolute || assetImport.scheme != 'asset') {
throw new ArgumentError.value(assetImport, 'assetImport',
'Must be an absolute uri using the asset: scheme');
}
return assetImport.pathSegments.length >= 2 &&
assetImport.pathSegments[1] == 'lib';
}
String _toPackageImport(Uri assetImport) {
assert(_canPackageImport(assetImport));
var subPath = assetImport.pathSegments
.getRange(2, assetImport.pathSegments.length)
.join('/');
return 'package:${assetImport.pathSegments.first}/$subPath';
}

View File

@ -1,212 +1,224 @@
library angular2.transform.common.directive_metadata_reader;
import 'dart:async';
import 'package:analyzer/analyzer.dart';
import 'package:analyzer/src/generated/element.dart';
import 'package:angular2/src/core/render/api.dart';
import 'package:angular2/src/compiler/directive_metadata.dart';
import 'package:angular2/src/compiler/template_compiler.dart';
import 'package:angular2/src/core/change_detection/change_detection.dart';
import 'package:angular2/src/core/compiler/interfaces.dart' show LifecycleHooks;
import 'package:angular2/src/core/render/api.dart' show ViewEncapsulation;
import 'package:angular2/src/transform/common/annotation_matcher.dart';
import 'package:angular2/src/transform/common/interface_matcher.dart';
import 'package:barback/barback.dart' show AssetId;
/// Reads [RenderDirectiveMetadata] from the `node`. `node` is expected to be an
/// instance of [ClassDeclaration] (a class which may have a [Directive] or
/// [Component] annotation) or an [InstanceCreationExpression] (an instantiation
/// of [ReflectionInfo]).
RenderDirectiveMetadata readDirectiveMetadata(AstNode node) {
var visitor;
if (node is ClassDeclaration) {
visitor = new _DeclarationVisitor();
} else if (node is InstanceCreationExpression) {
visitor = new _ReflectionInfoVisitor();
} else {
throw new ArgumentError('Incorrect value passed to readDirectiveMetadata. '
'Expected types are ClassDeclaration and InstanceCreationExpression '
'Provided type was ${node.runtimeType}');
}
node.accept(visitor);
return visitor.meta;
}
class DirectiveMetadataReader {
final _DirectiveMetadataVisitor _visitor;
final TemplateCompiler _templateCompiler;
num _getDirectiveType(String annotationName, Element element) {
var byNameMatch = -1;
// TODO(kegluneq): Detect subtypes & implementations of `Directive`s.
switch (annotationName) {
case 'Directive':
byNameMatch = RenderDirectiveMetadata.DIRECTIVE_TYPE;
break;
case 'Component':
byNameMatch = RenderDirectiveMetadata.COMPONENT_TYPE;
break;
default:
return -1;
DirectiveMetadataReader._(this._visitor, this._templateCompiler);
/// Accepts an [AnnotationMatcher] which tests that an [Annotation]
/// is a [Directive], [Component], or [View].
factory DirectiveMetadataReader(AnnotationMatcher annotationMatcher,
InterfaceMatcher interfaceMatcher, TemplateCompiler templateCompiler) {
var lifecycleVisitor = new _LifecycleHookVisitor(interfaceMatcher);
var visitor =
new _DirectiveMetadataVisitor(annotationMatcher, lifecycleVisitor);
return new DirectiveMetadataReader._(visitor, templateCompiler);
}
if (element != null) {
var byResolvedAst = -1;
var libName = element.library.name;
// If we have resolved, ensure the library is correct.
if (libName == 'angular2.src.core.metadata.directives' ||
libName == 'angular2.src.core.metadata') {
byResolvedAst = byNameMatch;
/// Reads *un-normalized* [CompileDirectiveMetadata] from the
/// [ClassDeclaration] `node`.
///
/// `node` is expected to be a class which may have a [Directive] or [Component]
/// annotation. If `node` does not have one of these annotations, this function
/// returns `null`.
///
/// `assetId` is the [AssetId] from which `node` was read, unless `node` was
/// read from a part file, in which case `assetId` should be the [AssetId] of
/// the parent file.
Future<CompileDirectiveMetadata> readDirectiveMetadata(
ClassDeclaration node, AssetId assetId) {
_visitor.reset(assetId);
node.accept(_visitor);
if (!_visitor.hasMetadata) {
return new Future.value(null);
} else {
final metadata = _visitor.createMetadata();
if (!metadata.isComponent) return new Future.value(metadata);
return _templateCompiler.normalizeDirectiveMetadata(metadata);
}
// TODO(kegluneq): @keertip, can we expose this as a warning?
assert(byNameMatch == byResolvedAst);
}
return byNameMatch;
}
class _ReflectionInfoVisitor extends _DirectiveMetadataVisitor {
// TODO(kegluneq): Create a more robust check that this is a real
// `ReflectionInfo` instantiation.
static bool _isReflectionInfo(InstanceCreationExpression node) {
var name = node.constructorName.type.name;
name = name is PrefixedIdentifier ? name.identifier : name;
return '$name' == 'ReflectionInfo';
}
@override
Object visitInstanceCreationExpression(InstanceCreationExpression node) {
if (_isReflectionInfo(node)) {
// NOTE(kegluneq): This is very tightly coupled with the `Reflector`
// implementation. Clean this up with a better intermediate representation
// for .ng_deps.dart files.
var reflectionInfoArgs = node.argumentList.arguments;
if (reflectionInfoArgs.length > 0) {
// Process annotations to determine information specified via
// `Component` and `Directive` parameters.
reflectionInfoArgs[0].accept(this);
if (_hasMeta && reflectionInfoArgs.length > 3) {
// Process interfaces to determine which lifecycle events we need to
// react to for this `Directive`.
_processInterfaces(reflectionInfoArgs[3]);
}
}
return null;
}
var directiveType = _getDirectiveType(
'${node.constructorName.type.name}', node.staticElement);
if (directiveType >= 0) {
if (_hasMeta) {
throw new FormatException(
'Only one Directive is allowed per class. '
'Found "$node" but already processed "$meta".',
'$node' /* source */);
}
_initializeMetadata(directiveType);
super.visitInstanceCreationExpression(node);
}
// Annotation we do not recognize - no need to visit.
return null;
}
void _processInterfaces(Expression lifecycleValue) {
_checkMeta();
if (lifecycleValue is! ListLiteral) {
throw new FormatException(
'Angular 2 expects a List but could not understand the value for interfaces. '
'$lifecycleValue');
}
ListLiteral l = lifecycleValue;
_populateLifecycle(l.elements.map((s) => s.toSource().split('.').last));
}
}
class _DeclarationVisitor extends _DirectiveMetadataVisitor {
@override
Object visitClassDeclaration(ClassDeclaration node) {
node.metadata.accept(this);
if (this._hasMeta) {
if (node.implementsClause != null &&
node.implementsClause.interfaces != null) {
_populateLifecycle(node.implementsClause.interfaces
.map((s) => s.toSource().split('.').last));
}
}
return null;
/// Visitor that attempts to evaluate a provided `node` syntactically.
///
/// This lack of semantic information means it cannot do much - for
/// example, it can create a list from a list literal and combine adjacent
/// strings but cannot determine that an identifier is a constant string,
/// even if that identifier is defined in the same [CompilationUnit].
///
/// Returns the result of evaluation or [ConstantEvaluator.NOT_A_CONSTANT]
/// where appropriate.
final ConstantEvaluator _evaluator = new ConstantEvaluator();
/// Evaluates the [Map] represented by `expression` and adds all `key`,
/// `value` pairs to `map`. If `expression` does not evaluate to a [Map],
/// throws a descriptive [FormatException].
void _populateMap(Expression expression, Map map, String propertyName) {
var evaluated = expression.accept(_evaluator);
if (evaluated is! Map) {
throw new FormatException(
'Angular 2 expects a Map but could not understand the value for '
'$propertyName.',
'$expression' /* source */);
}
evaluated.forEach((key, value) {
if (value != null) {
map[key] = '$value';
}
});
}
/// Visitor responsible for processing [Directive] code into a
/// [RenderDirectiveMetadata] object.
/// Evaluates the [List] represented by `expression` and adds all values,
/// to `list`. If `expression` does not evaluate to a [List], throws a
/// descriptive [FormatException].
void _populateList(
Expression expression, List<String> list, String propertyName) {
var evaluated = expression.accept(_evaluator);
if (evaluated is! List) {
throw new FormatException(
'Angular 2 expects a List but could not understand the value for '
'$propertyName.',
'$expression' /* source */);
}
list.addAll(evaluated.map((e) => e.toString()));
}
/// Evaluates `node` and expects that the result will be a string. If not,
/// throws a [FormatException].
String _expressionToString(Expression node, String nodeDescription) {
var value = node.accept(_evaluator);
if (value is! String) {
throw new FormatException(
'Angular 2 could not understand the value '
'in $nodeDescription.',
'$node' /* source */);
}
return value;
}
/// Visitor responsible for processing a [Directive] annotated
/// [ClassDeclaration] and creating a [CompileDirectiveMetadata] object.
class _DirectiveMetadataVisitor extends Object
with RecursiveAstVisitor<Object> {
bool get _hasMeta => _type != null;
/// Tests [Annotation]s to determine if they deifne a [Directive],
/// [Component], [View], or none of these.
final AnnotationMatcher _annotationMatcher;
// Annotation fields
num _type;
String _selector;
bool _compileChildren;
List<String> _inputs;
Map<String, String> _host;
List<String> _readAttributes;
String _exportAs;
bool _callOnDestroy;
bool _callOnChange;
bool _callDoCheck;
bool _callOnInit;
bool _callAfterContentInit;
bool _callAfterContentChecked;
bool _callAfterViewInit;
bool _callAfterViewChecked;
ChangeDetectionStrategy _changeDetection;
List<String> _outputs;
final _LifecycleHookVisitor _lifecycleVisitor;
final ConstantEvaluator _evaluator = new ConstantEvaluator();
/// The [AssetId] we are currently processing.
AssetId _assetId;
void _initializeMetadata(num directiveType) {
assert(directiveType >= 0);
_type = directiveType;
_selector = '';
_compileChildren = true;
_inputs = [];
_host = {};
_readAttributes = [];
_exportAs = null;
_callOnDestroy = false;
_callOnChange = false;
_callDoCheck = false;
_callOnInit = false;
_callAfterContentInit = false;
_callAfterContentChecked = false;
_callAfterViewInit = false;
_callAfterViewChecked = false;
_changeDetection = null;
_outputs = [];
_DirectiveMetadataVisitor(this._annotationMatcher, this._lifecycleVisitor) {
reset(null);
}
RenderDirectiveMetadata get meta => RenderDirectiveMetadata.create(
/// Whether the visitor has found a [Component] or [Directive] annotation
/// since the last call to `reset`.
bool _hasMetadata = false;
// Annotation fields
CompileTypeMetadata _type;
bool _isComponent;
String _selector;
String _exportAs;
ChangeDetectionStrategy _changeDetection;
List<String> _properties;
List<String> _events;
Map<String, String> _host;
List<LifecycleHooks> _lifecycleHooks;
CompileTemplateMetadata _template;
void reset(AssetId assetId) {
_lifecycleVisitor.reset(assetId);
_assetId = assetId;
_type = null;
_isComponent = false;
_hasMetadata = false;
_selector = '';
_exportAs = null;
_changeDetection = ChangeDetectionStrategy.Default;
_properties = <String>[];
_events = <String>[];
_host = <String, String>{};
_lifecycleHooks = null;
_template = null;
}
bool get hasMetadata => _hasMetadata;
CompileDirectiveMetadata createMetadata() => CompileDirectiveMetadata.create(
type: _type,
isComponent: _isComponent,
dynamicLoadable: true, // NOTE(kegluneq): For future optimization.
selector: _selector,
compileChildren: _compileChildren,
inputs: _inputs,
host: _host,
readAttributes: _readAttributes,
exportAs: _exportAs,
callOnDestroy: _callOnDestroy,
callOnChanges: _callOnChange,
callDoCheck: _callDoCheck,
callOnInit: _callOnInit,
callAfterContentInit: _callAfterContentInit,
callAfterContentChecked: _callAfterContentChecked,
callAfterViewInit: _callAfterViewInit,
callAfterViewChecked: _callAfterViewChecked,
changeDetection: _changeDetection,
outputs: _outputs);
properties: _properties,
events: _events,
host: _host,
lifecycleHooks: _lifecycleHooks,
template: _template);
@override
Object visitAnnotation(Annotation node) {
var directiveType = _getDirectiveType('${node.name}', node.element);
if (directiveType >= 0) {
if (_hasMeta) {
var isComponent = _annotationMatcher.isComponent(node, _assetId);
var isDirective = _annotationMatcher.isDirective(node, _assetId);
if (isDirective) {
if (_hasMetadata) {
throw new FormatException(
'Only one Directive is allowed per class. '
'Found "$node" but already processed "$meta".',
'Found unexpected "$node".',
'$node' /* source */);
}
_initializeMetadata(directiveType);
_isComponent = isComponent;
_hasMetadata = true;
super.visitAnnotation(node);
} else if (_annotationMatcher.isView(node, _assetId)) {
if (_template != null) {
throw new FormatException(
'Only one View is allowed per class. '
'Found unexpected "$node".',
'$node' /* source */);
}
_template = new _CompileTemplateMetadataVisitor().visitAnnotation(node);
}
// Annotation we do not recognize - no need to visit.
return null;
}
@override
Object visitClassDeclaration(ClassDeclaration node) {
node.metadata.accept(this);
if (this._hasMetadata) {
_type = new CompileTypeMetadata(
moduleUrl: 'asset:${_assetId.package}/${_assetId.path}',
name: node.name.toString(),
runtime: null // Intentionally `null`, cannot be provided here.
);
_lifecycleHooks = node.implementsClause != null
? node.implementsClause.accept(_lifecycleVisitor)
: const [];
}
return null;
}
@override
Object visitNamedExpression(NamedExpression node) {
// TODO(kegluneq): Remove this limitation.
@ -220,10 +232,7 @@ class _DirectiveMetadataVisitor extends Object
case 'selector':
_populateSelector(node.expression);
break;
case 'compileChildren':
_populateCompileChildren(node.expression);
break;
case 'inputs':
case 'properties':
_populateProperties(node.expression);
break;
case 'host':
@ -235,84 +244,29 @@ class _DirectiveMetadataVisitor extends Object
case 'changeDetection':
_populateChangeDetection(node.expression);
break;
case 'outputs':
case 'events':
_populateEvents(node.expression);
break;
}
return null;
}
String _expressionToString(Expression node, String nodeDescription) {
var value = node.accept(_evaluator);
if (value is! String) {
throw new FormatException(
'Angular 2 could not understand the value '
'in $nodeDescription.',
'$node' /* source */);
}
return value;
}
void _populateSelector(Expression selectorValue) {
_checkMeta();
_selector = _expressionToString(selectorValue, 'Directive#selector');
}
void _checkMeta() {
if (!_hasMeta) {
if (!_hasMetadata) {
throw new ArgumentError(
'Incorrect value passed to readDirectiveMetadata. '
'Expected types are ClassDeclaration and InstanceCreationExpression');
'Expected type is ClassDeclaration');
}
}
void _populateCompileChildren(Expression compileChildrenValue) {
_checkMeta();
var evaluated = compileChildrenValue.accept(_evaluator);
if (evaluated is! bool) {
throw new FormatException(
'Angular 2 expects a bool but could not understand the value for '
'Directive#compileChildren.',
'$compileChildrenValue' /* source */);
}
_compileChildren = evaluated;
}
/// Evaluates the [Map] represented by `expression` and adds all `key`,
/// `value` pairs to `map`. If `expression` does not evaluate to a [Map],
/// throws a descriptive [FormatException].
void _populateMap(Expression expression, Map map, String propertyName) {
var evaluated = expression.accept(_evaluator);
if (evaluated is! Map) {
throw new FormatException(
'Angular 2 expects a Map but could not understand the value for '
'$propertyName.',
'$expression' /* source */);
}
evaluated.forEach((key, value) {
if (value != null) {
map[key] = '$value';
}
});
}
/// Evaluates the [List] represented by `expression` and adds all values,
/// to `list`. If `expression` does not evaluate to a [List], throws a
/// descriptive [FormatException].
void _populateList(Expression expression, List list, String propertyName) {
var evaluated = expression.accept(_evaluator);
if (evaluated is! List) {
throw new FormatException(
'Angular 2 expects a List but could not understand the value for '
'$propertyName.',
'$expression' /* source */);
}
list.addAll(evaluated);
}
void _populateProperties(Expression propertiesValue) {
_checkMeta();
_populateList(propertiesValue, _inputs, 'Directive#properties');
_populateList(propertiesValue, _properties, 'Directive#properties');
}
void _populateHost(Expression hostValue) {
@ -325,32 +279,138 @@ class _DirectiveMetadataVisitor extends Object
_exportAs = _expressionToString(exportAsValue, 'Directive#exportAs');
}
void _populateLifecycle(Iterable<String> lifecycleInterfaceNames) {
_checkMeta();
_callOnDestroy = lifecycleInterfaceNames.contains("OnDestroy");
_callOnChange = lifecycleInterfaceNames.contains("OnChanges");
_callDoCheck = lifecycleInterfaceNames.contains("DoCheck");
_callOnInit = lifecycleInterfaceNames.contains("OnInit");
_callAfterContentInit =
lifecycleInterfaceNames.contains("AfterContentInit");
_callAfterContentChecked =
lifecycleInterfaceNames.contains("AfterContentChecked");
_callAfterViewInit = lifecycleInterfaceNames.contains("AfterViewInit");
_callAfterViewChecked =
lifecycleInterfaceNames.contains("AfterViewChecked");
}
void _populateEvents(Expression eventsValue) {
_checkMeta();
_populateList(eventsValue, _outputs, 'Directive#events');
_populateList(eventsValue, _events, 'Directive#events');
}
void _populateChangeDetection(Expression value) {
_checkMeta();
_changeDetection = changeDetectionStrategies[value.toSource()];
_changeDetection = _changeDetectionStrategies[value.toSource()];
}
static final Map<String, ChangeDetectionStrategy> _changeDetectionStrategies =
new Map.fromIterable(ChangeDetectionStrategy.values,
key: (v) => v.toString());
}
/// Visitor responsible for parsing an [ImplementsClause] and returning a
/// [List<LifecycleHooks>] that the [Directive] subscribes to.
class _LifecycleHookVisitor extends SimpleAstVisitor<List<LifecycleHooks>> {
/// Tests [Identifier]s of implemented interfaces to determine if they
/// correspond to [LifecycleHooks] values.
final InterfaceMatcher _ifaceMatcher;
/// The [AssetId] we are currently processing.
AssetId _assetId;
_LifecycleHookVisitor(this._ifaceMatcher);
void reset(AssetId assetId) {
_assetId = assetId;
}
@override
List<LifecycleHooks> visitImplementsClause(ImplementsClause node) {
if (node == null || node.interfaces == null) return const [];
return node.interfaces.map((TypeName ifaceTypeName) {
var id = ifaceTypeName.name;
if (_ifaceMatcher.isAfterContentChecked(id, _assetId)) {
return LifecycleHooks.AfterContentChecked;
} else if (_ifaceMatcher.isAfterContentInit(id, _assetId)) {
return LifecycleHooks.AfterContentInit;
} else if (_ifaceMatcher.isAfterViewChecked(id, _assetId)) {
return LifecycleHooks.AfterViewChecked;
} else if (_ifaceMatcher.isAfterViewInit(id, _assetId)) {
return LifecycleHooks.AfterViewInit;
} else if (_ifaceMatcher.isDoCheck(id, _assetId)) {
return LifecycleHooks.DoCheck;
} else if (_ifaceMatcher.isOnChange(id, _assetId)) {
return LifecycleHooks.OnChanges;
} else if (_ifaceMatcher.isOnDestroy(id, _assetId)) {
return LifecycleHooks.OnDestroy;
} else if (_ifaceMatcher.isOnInit(id, _assetId)) {
return LifecycleHooks.OnInit;
}
return null;
}).where((e) => e != null).toList(growable: false);
}
}
final Map<String, ChangeDetectionStrategy> changeDetectionStrategies =
new Map.fromIterable(ChangeDetectionStrategy.values,
key: (v) => v.toString());
/// Visitor responsible for parsing a @View [Annotation] and producing a
/// [CompileTemplateMetadata].
class _CompileTemplateMetadataVisitor
extends RecursiveAstVisitor<CompileTemplateMetadata> {
ViewEncapsulation _encapsulation = ViewEncapsulation.Emulated;
String _template = null;
String _templateUrl = null;
List<String> _styles = null;
List<String> _styleUrls = null;
@override
CompileTemplateMetadata visitAnnotation(Annotation node) {
super.visitAnnotation(node);
return new CompileTemplateMetadata(
encapsulation: _encapsulation,
template: _template,
templateUrl: _templateUrl,
styles: _styles,
styleUrls: _styleUrls);
}
@override
CompileTemplateMetadata visitNamedExpression(NamedExpression node) {
// TODO(kegluneq): Remove this limitation.
if (node.name is! Label || node.name.label is! SimpleIdentifier) {
throw new FormatException(
'Angular 2 currently only supports simple identifiers in directives.',
'$node' /* source */);
}
var keyString = '${node.name.label}';
switch (keyString) {
case 'encapsulation':
_populateEncapsulation(node.expression);
break;
case 'template':
_populateTemplate(node.expression);
break;
case 'templateUrl':
_populateTemplateUrl(node.expression);
break;
case 'styles':
_populateStyles(node.expression);
break;
case 'styleUrls':
_populateStyleUrls(node.expression);
break;
}
return null;
}
void _populateTemplate(Expression value) {
_template = _expressionToString(value, 'View#template');
}
void _populateTemplateUrl(Expression value) {
_templateUrl = _expressionToString(value, 'View#templateUrl');
}
void _populateStyles(Expression value) {
_styles = <String>[];
_populateList(value, _styles, 'View#styles');
}
void _populateStyleUrls(Expression value) {
_styleUrls = <String>[];
_populateList(value, _styleUrls, 'View#styleUrls');
}
void _populateEncapsulation(Expression value) {
_encapsulation = _viewEncapsulationMap[value.toSource()];
}
static final _viewEncapsulationMap =
new Map.fromIterable(ViewEncapsulation.values, key: (v) => v.toString());
}

View File

@ -11,14 +11,21 @@ class NgDepsModel extends GeneratedMessage {
static final BuilderInfo _i = new BuilderInfo('NgDepsModel')
..a(1, 'libraryUri', PbFieldType.OS)
..p(2, 'partUris', PbFieldType.PS)
..pp(3, 'imports', PbFieldType.PM, ImportModel.$checkItem, ImportModel.create)
..pp(4, 'exports', PbFieldType.PM, ExportModel.$checkItem, ExportModel.create)
..pp(5, 'reflectables', PbFieldType.PM, ReflectionInfoModel.$checkItem, ReflectionInfoModel.create)
;
..pp(3, 'imports', PbFieldType.PM, ImportModel.$checkItem,
ImportModel.create)
..pp(4, 'exports', PbFieldType.PM, ExportModel.$checkItem,
ExportModel.create)
..pp(5, 'reflectables', PbFieldType.PM, ReflectionInfoModel.$checkItem,
ReflectionInfoModel.create)
..a(6, 'sourceFile', PbFieldType.OS);
NgDepsModel() : super();
NgDepsModel.fromBuffer(List<int> i, [ExtensionRegistry r = ExtensionRegistry.EMPTY]) : super.fromBuffer(i, r);
NgDepsModel.fromJson(String i, [ExtensionRegistry r = ExtensionRegistry.EMPTY]) : super.fromJson(i, r);
NgDepsModel.fromBuffer(List<int> i,
[ExtensionRegistry r = ExtensionRegistry.EMPTY])
: super.fromBuffer(i, r);
NgDepsModel.fromJson(String i,
[ExtensionRegistry r = ExtensionRegistry.EMPTY])
: super.fromJson(i, r);
NgDepsModel clone() => new NgDepsModel()..mergeFromMessage(this);
BuilderInfo get info_ => _i;
static NgDepsModel create() => new NgDepsModel();
@ -27,13 +34,17 @@ class NgDepsModel extends GeneratedMessage {
if (_defaultInstance == null) _defaultInstance = new _ReadonlyNgDepsModel();
return _defaultInstance;
}
static NgDepsModel _defaultInstance;
static void $checkItem(NgDepsModel v) {
if (v is !NgDepsModel) checkItemFailed(v, 'NgDepsModel');
if (v is! NgDepsModel) checkItemFailed(v, 'NgDepsModel');
}
String get libraryUri => getField(1);
void set libraryUri(String v) { setField(1, v); }
void set libraryUri(String v) {
setField(1, v);
}
bool hasLibraryUri() => hasField(1);
void clearLibraryUri() => clearField(1);
@ -44,6 +55,14 @@ class NgDepsModel extends GeneratedMessage {
List<ExportModel> get exports => getField(4);
List<ReflectionInfoModel> get reflectables => getField(5);
String get sourceFile => getField(6);
void set sourceFile(String v) {
setField(6, v);
}
bool hasSourceFile() => hasField(6);
void clearSourceFile() => clearField(6);
}
class _ReadonlyNgDepsModel extends NgDepsModel with ReadonlyMessageMixin {}
@ -53,15 +72,34 @@ const NgDepsModel$json = const {
'2': const [
const {'1': 'library_uri', '3': 1, '4': 1, '5': 9},
const {'1': 'part_uris', '3': 2, '4': 3, '5': 9},
const {'1': 'imports', '3': 3, '4': 3, '5': 11, '6': '.angular2.src.transform.common.model.proto.ImportModel'},
const {'1': 'exports', '3': 4, '4': 3, '5': 11, '6': '.angular2.src.transform.common.model.proto.ExportModel'},
const {'1': 'reflectables', '3': 5, '4': 3, '5': 11, '6': '.angular2.src.transform.common.model.proto.ReflectionInfoModel'},
const {
'1': 'imports',
'3': 3,
'4': 3,
'5': 11,
'6': '.angular2.src.transform.common.model.proto.ImportModel'
},
const {
'1': 'exports',
'3': 4,
'4': 3,
'5': 11,
'6': '.angular2.src.transform.common.model.proto.ExportModel'
},
const {
'1': 'reflectables',
'3': 5,
'4': 3,
'5': 11,
'6': '.angular2.src.transform.common.model.proto.ReflectionInfoModel'
},
const {'1': 'source_file', '3': 6, '4': 1, '5': 9},
],
};
/**
* Generated with:
* ng_deps_model.proto (c84f449fc55ef46e38a652f65449c6645b9fe6cb)
* ng_deps_model.proto (83fe43a087fdd0a7ebee360cd6b669570df4d216)
* libprotoc 2.5.0
* dart-protoc-plugin (cc35f743de982a4916588b9c505dd21c7fe87d17)
*/

View File

@ -14,5 +14,10 @@ message NgDepsModel {
repeated ExportModel exports = 4;
// All classes in `source_file` marked with @Injectable or a known subclass.
repeated ReflectionInfoModel reflectables = 5;
// The basename of the file from which the ng_deps were generated.
// Example: component.dart
optional string source_file = 6;
}

View File

@ -4,12 +4,13 @@ const BOOTSTRAP_NAME = 'bootstrap';
const SETUP_METHOD_NAME = 'initReflector';
const REFLECTOR_VAR_NAME = 'reflector';
const TRANSFORM_DYNAMIC_MODE = 'transform_dynamic';
const CSS_EXTENSION = '.css';
const SHIMMED_STYLESHEET_EXTENSION = '.css.shim.dart';
const NON_SHIMMED_STYLESHEET_EXTENSION = '.css.dart';
const DEPS_EXTENSION = '.ng_deps.dart';
const DEPS_JSON_EXTENSION = '.ng_deps.json';
const META_EXTENSION = '.ng_meta.json';
// TODO(sigmund): consider merging into .ng_meta by generating local metadata
// upfront (rather than extracting it from ng_deps).
const ALIAS_EXTENSION = '.aliases.json';
const TEMPLATE_EXTENSION = '.template.dart';
const REFLECTION_CAPABILITIES_NAME = 'ReflectionCapabilities';
const REFLECTOR_IMPORT = 'package:angular2/src/core/reflection/reflection.dart';
const REFLECTOR_PREFIX = '_ngRef';
@ -18,20 +19,40 @@ const REGISTER_GETTERS_METHOD_NAME = 'registerGetters';
const REGISTER_SETTERS_METHOD_NAME = 'registerSetters';
const REGISTER_METHODS_METHOD_NAME = 'registerMethods';
/// Note that due to the implementation of `_toExtension`, ordering is
/// important. For example, putting '.dart' first in this list will cause
/// incorrect behavior.
const ALL_EXTENSIONS = const [
DEPS_EXTENSION,
DEPS_JSON_EXTENSION,
META_EXTENSION,
TEMPLATE_EXTENSION,
'.dart'
];
/// Returns `uri` with its extension updated to [META_EXTENSION].
String toMetaExtension(String uri) =>
_toExtension(uri, const [DEPS_EXTENSION, DEPS_JSON_EXTENSION, '.dart'], META_EXTENSION);
_toExtension(uri, ALL_EXTENSIONS, META_EXTENSION);
/// Returns `uri` with its extension updated to [DEPS_EXTENSION].
String toDepsExtension(String uri) =>
_toExtension(uri, const [META_EXTENSION, DEPS_JSON_EXTENSION, '.dart'], DEPS_EXTENSION);
/// Returns `uri` with its extension updated to [ALIAS_EXTENSION].
String toAliasExtension(String uri) =>
_toExtension(uri, const [DEPS_EXTENSION, '.dart'], ALIAS_EXTENSION);
_toExtension(uri, ALL_EXTENSIONS, DEPS_EXTENSION);
/// Returns `uri` with its extension updated to [DEPS_JSON_EXTENSION].
String toJsonExtension(String uri) =>
_toExtension(uri, const [DEPS_EXTENSION, '.dart'], DEPS_JSON_EXTENSION);
_toExtension(uri, ALL_EXTENSIONS, DEPS_JSON_EXTENSION);
/// Returns `uri` with its extension updated to [TEMPLATES_EXTENSION].
String toTemplateExtension(String uri) =>
_toExtension(uri, ALL_EXTENSIONS, TEMPLATE_EXTENSION);
/// Returns `uri` with its extension updated to [SHIMMED_STYLESHEET_EXTENSION].
String toShimmedStylesheetExtension(String uri) =>
_toExtension(uri, const [CSS_EXTENSION], SHIMMED_STYLESHEET_EXTENSION);
/// Returns `uri` with its extension updated to [NON_SHIMMED_STYLESHEET_EXTENSION].
String toNonShimmedStylesheetExtension(String uri) =>
_toExtension(uri, const [CSS_EXTENSION], NON_SHIMMED_STYLESHEET_EXTENSION);
/// Returns `uri` with its extension updated to `toExtension` if its
/// extension is currently in `fromExtension`.

View File

@ -0,0 +1,40 @@
library angular2.transform.template_compiler.xhr_impl;
import 'package:angular2/src/compiler/command_compiler.dart';
import 'package:angular2/src/compiler/html_parser.dart';
import 'package:angular2/src/compiler/style_compiler.dart';
import 'package:angular2/src/compiler/template_compiler.dart';
import 'package:angular2/src/compiler/template_normalizer.dart';
import 'package:angular2/src/compiler/template_parser.dart';
import 'package:angular2/src/core/change_detection/parser/lexer.dart' as ng;
import 'package:angular2/src/core/change_detection/parser/parser.dart' as ng;
import 'package:angular2/src/core/render/dom/schema/dom_element_schema_registry.dart';
import 'package:angular2/src/transform/common/asset_reader.dart';
import 'package:angular2/src/core/change_detection/interfaces.dart';
import 'package:angular2/src/compiler/change_detector_compiler.dart';
import 'xhr_impl.dart';
import 'url_resolver.dart';
TemplateCompiler createTemplateCompiler(AssetReader reader,
{ChangeDetectorGenConfig changeDetectionConfig}) {
var _xhr = new XhrImpl(reader);
var _htmlParser = new HtmlParser();
var _urlResolver = const TransformerUrlResolver();
var templateParser = new TemplateParser(new ng.Parser(new ng.Lexer()),
new DomElementSchemaRegistry(), _htmlParser);
var cdCompiler = changeDetectionConfig != null
? new ChangeDetectionCompiler(changeDetectionConfig)
: null;
return new TemplateCompiler(
null /* RuntimeMetadataResolver */,
new TemplateNormalizer(_xhr, _urlResolver, _htmlParser),
templateParser,
new StyleCompiler(_xhr, _urlResolver),
new CommandCompiler(),
cdCompiler,
null /* appId */);
}

View File

@ -1,7 +1,6 @@
library angular2.transform.common.ng_meta;
import 'package:angular2/src/core/render/api.dart';
import 'convert.dart';
import 'package:angular2/src/compiler/directive_metadata.dart';
import 'logging.dart';
/// Metadata about directives and directive aliases.
@ -20,37 +19,47 @@ import 'logging.dart';
/// easier.
class NgMeta {
/// Directive metadata for each type annotated as a directive.
final Map<String, RenderDirectiveMetadata> types;
final Map<String, CompileDirectiveMetadata> types;
/// List of other types and names associated with a given name.
final Map<String, List<String>> aliases;
NgMeta(this.types, this.aliases);
/// TODO(kegluneq): Once merged with NgDepsModel, use its exports.
final List<String> exports;
NgMeta.empty() : this({}, {});
NgMeta(this.types, this.aliases, this.exports);
bool get isEmpty => types.isEmpty && aliases.isEmpty;
NgMeta.empty() : this({}, {}, []);
bool get isEmpty => types.isEmpty && aliases.isEmpty && exports.isEmpty;
/// Parse from the serialized form produced by [toJson].
factory NgMeta.fromJson(Map json) {
var exports = <String>[];
var types = {};
var aliases = {};
for (var key in json.keys) {
var entry = json[key];
if (entry['kind'] == 'type') {
types[key] = directiveMetadataFromMap(entry['value']);
} else if (entry['kind'] == 'alias') {
aliases[key] = entry['value'];
if (key == '__exports__') {
exports = json[key];
} else {
var entry = json[key];
if (entry['kind'] == 'type') {
types[key] = CompileDirectiveMetadata.fromJson(entry['value']);
} else if (entry['kind'] == 'alias') {
aliases[key] = entry['value'];
}
}
}
return new NgMeta(types, aliases);
return new NgMeta(types, aliases, exports);
}
/// Serialized representation of this instance.
Map toJson() {
var result = {};
result['__exports__'] = exports;
types.forEach((k, v) {
result[k] = {'kind': 'type', 'value': directiveMetadataToMap(v)};
result[k] = {'kind': 'type', 'value': v.toJson()};
});
aliases.forEach((k, v) {
@ -60,14 +69,15 @@ class NgMeta {
}
/// Merge into this instance all information from [other].
/// This does not include `exports`.
void addAll(NgMeta other) {
types.addAll(other.types);
aliases.addAll(other.aliases);
}
/// Returns the metadata for every type associated with the given [alias].
List<RenderDirectiveMetadata> flatten(String alias) {
var result = [];
List<CompileDirectiveMetadata> flatten(String alias) {
var result = <CompileDirectiveMetadata>[];
var seen = new Set();
helper(name) {
if (!seen.add(name)) {

View File

@ -11,12 +11,15 @@ const DEFAULT_OPTIMIZATION_PHASES = 5;
const CUSTOM_ANNOTATIONS_PARAM = 'custom_annotations';
const ENTRY_POINT_PARAM = 'entry_points';
const FORMAT_CODE_PARAM = 'format_code';
// TODO(kegluenq): Remove this after 30 Oct (i/4433).
const GENERATE_CHANGE_DETECTORS_PARAM = 'generate_change_detectors';
const REFLECT_PROPERTIES_AS_ATTRIBUTES = 'reflectPropertiesAsAttributes';
const INIT_REFLECTOR_PARAM = 'init_reflector';
// TODO(kegluenq): Remove this after 30 Oct (i/4433).
const INLINE_VIEWS_PARAM = 'inline_views';
const MIRROR_MODE_PARAM = 'mirror_mode';
const OPTIMIZATION_PHASES_PARAM = 'optimization_phases';
const PRECOMPILE_PARAM = 'precompile';
const REFLECTION_ENTRY_POINT_PARAM = 'reflection_entry_points';
/// Provides information necessary to transform an Angular2 app.
@ -35,15 +38,9 @@ class TransformerOptions {
/// Whether to generate calls to our generated `initReflector` code
final bool initReflector;
/// Whether to inline html/css urls into the View directive
final bool inlineViews;
/// The [AnnotationMatcher] which is used to identify angular annotations.
final AnnotationMatcher annotationMatcher;
/// Whether to create change detector classes for discovered `@View`s.
final bool generateChangeDetectors;
final bool reflectPropertiesAsAttributes;
/// The number of phases to spend optimizing output size.
@ -68,9 +65,7 @@ class TransformerOptions {
this.initReflector,
this.annotationMatcher,
this.optimizationPhases,
{this.generateChangeDetectors,
this.reflectPropertiesAsAttributes,
this.inlineViews,
{this.reflectPropertiesAsAttributes,
this.formatCode});
factory TransformerOptions(List<String> entryPoints,
@ -80,7 +75,6 @@ class TransformerOptions {
List<ClassDescriptor> customAnnotationDescriptors: const [],
int optimizationPhases: DEFAULT_OPTIMIZATION_PHASES,
bool inlineViews: true,
bool generateChangeDetectors: true,
bool reflectPropertiesAsAttributes: true,
bool formatCode: false}) {
var annotationMatcher = new AnnotationMatcher()
@ -97,9 +91,7 @@ class TransformerOptions {
initReflector,
annotationMatcher,
optimizationPhases,
generateChangeDetectors: generateChangeDetectors,
reflectPropertiesAsAttributes: reflectPropertiesAsAttributes,
inlineViews: inlineViews,
formatCode: formatCode);
}
}

View File

@ -11,9 +11,6 @@ TransformerOptions parseBarbackSettings(BarbackSettings settings) {
var entryPoints = _readFileList(config, ENTRY_POINT_PARAM);
var initReflector =
_readBool(config, INIT_REFLECTOR_PARAM, defaultValue: true);
var inlineViews = _readBool(config, INLINE_VIEWS_PARAM, defaultValue: true);
var generateChangeDetectors =
_readBool(config, GENERATE_CHANGE_DETECTORS_PARAM, defaultValue: true);
var reflectPropertiesAsAttributes =
_readBool(config, REFLECT_PROPERTIES_AS_ATTRIBUTES, defaultValue: false);
var formatCode = _readBool(config, FORMAT_CODE_PARAM, defaultValue: false);
@ -37,10 +34,8 @@ TransformerOptions parseBarbackSettings(BarbackSettings settings) {
modeName: settings.mode.name,
mirrorMode: mirrorMode,
initReflector: initReflector,
inlineViews: inlineViews,
customAnnotationDescriptors: _readCustomAnnotations(config),
optimizationPhases: optimizationPhases,
generateChangeDetectors: generateChangeDetectors,
reflectPropertiesAsAttributes: reflectPropertiesAsAttributes,
formatCode: formatCode);
}
@ -136,4 +131,14 @@ void _warnDeprecated(Map config) {
print('${REFLECTION_ENTRY_POINT_PARAM} is no longer necessary for '
'Angular 2 apps. Please remove it from your pubspec.');
}
if (config.containsKey(GENERATE_CHANGE_DETECTORS_PARAM)) {
print('${GENERATE_CHANGE_DETECTORS_PARAM} is no longer necessary for '
'Angular 2 apps. Please remove it from your pubspec.');
}
if (config.containsKey(INLINE_VIEWS_PARAM)) {
print('Parameter "${INLINE_VIEWS_PARAM}" no longer has any effect on the '
'Angular2 transformer. Inlining of views is only needed for tests that '
'manipulate component metadata. For this purpose, use transformer '
'angular2/src/transform/inliner_for_test.');
}
}

View File

@ -2,8 +2,6 @@ library angular2.transform.common.registered_type;
import 'package:analyzer/analyzer.dart';
import 'package:angular2/src/core/render/api.dart';
import 'package:angular2/src/transform/common/directive_metadata_reader.dart';
import 'package:angular2/src/transform/common/logging.dart';
import 'package:angular2/src/transform/common/names.dart';
/// A call to `Reflector#registerType` generated by `DirectiveProcessor`.
@ -29,8 +27,6 @@ class RegisteredType {
/// The property metadata registered.
final Expression propMetadata;
RenderDirectiveMetadata _directiveMetadata = null;
RegisteredType._(
this.typeName,
this.registerMethod,
@ -49,20 +45,7 @@ class RegisteredType {
visitor.factoryFn, visitor.parameters, visitor.annotations, visitor.propMetadata);
}
RenderDirectiveMetadata get directiveMetadata {
if (_directiveMetadata == null) {
try {
/// TODO(kegluneq): Allow passing a lifecycle interface matcher.
_directiveMetadata = readDirectiveMetadata(reflectionInfoCreate);
if (_directiveMetadata != null) {
_directiveMetadata.id = '$typeName';
}
} on FormatException catch (ex) {
logger.error(ex.message);
}
}
return _directiveMetadata;
}
RenderDirectiveMetadata get directiveMetadata => null;
}
class _ParseRegisterTypeVisitor extends Object

View File

@ -0,0 +1,57 @@
library angular2.transform.template_compiler.url_resolver;
import 'package:angular2/src/core/services/url_resolver.dart';
class TransformerUrlResolver implements UrlResolver {
const TransformerUrlResolver();
@override
String resolve(String baseUrl, String url) {
Uri uri = Uri.parse(url);
if (!uri.isAbsolute) {
uri = Uri.parse(baseUrl).resolveUri(uri);
}
return toAssetScheme(uri).toString();
}
/// Converts `absoluteUri` to use the 'asset' scheme used in the Angular 2
/// template compiler.
///
/// The `scheme` of `absoluteUri` is expected to be either 'package' or
/// 'asset'.
Uri toAssetScheme(Uri absoluteUri) {
if (absoluteUri == null) return null;
if (!absoluteUri.isAbsolute) {
throw new ArgumentError.value(
absoluteUri, 'absoluteUri', 'Value passed must be an absolute uri');
}
if (absoluteUri.scheme == 'asset') {
if (absoluteUri.pathSegments.length < 3) {
throw new FormatException(
'An asset: URI must have at least 3 path '
'segments, for example '
'asset:<package-name>/<first-level-dir>/<path-to-dart-file>.',
absoluteUri);
}
return absoluteUri;
}
if (absoluteUri.scheme != 'package') {
throw new ArgumentError.value(
absoluteUri, 'absoluteUri', 'Unsupported URI scheme encountered');
}
if (absoluteUri.pathSegments.length < 2) {
throw new FormatException(
'A package: URI must have at least 2 path '
'segments, for example '
'package:<package-name>/<path-to-dart-file>',
absoluteUri);
}
var pathSegments = absoluteUri.pathSegments.toList()..insert(1, 'lib');
return new Uri(scheme: 'asset', pathSegments: pathSegments);
}
}

View File

@ -3,29 +3,24 @@ library angular2.transform.template_compiler.xhr_impl;
import 'dart:async';
import 'package:angular2/src/core/render/xhr.dart' show XHR;
import 'package:angular2/src/transform/common/asset_reader.dart';
import 'package:angular2/src/transform/common/logging.dart';
import 'package:barback/barback.dart';
import 'package:code_transformers/assets.dart';
class XhrImpl implements XHR {
final AssetReader _reader;
final AssetId _entryPoint;
XhrImpl(this._reader, this._entryPoint);
XhrImpl(this._reader);
Future<String> get(String url) async {
var assetId = uriToAssetId(_entryPoint, url, logger, null /* span */,
errorOnAbsolute: false);
if (assetId == null) {
logger.error(
'Uri $url not supported from $_entryPoint, could not build AssetId');
return null;
final uri = Uri.parse(url);
if (uri.scheme != 'asset') {
throw new FormatException('Unsupported uri encountered: $uri', url);
}
var templateExists = await _reader.hasInput(assetId);
if (!templateExists) {
logger.error('Could not read asset at uri $url from $_entryPoint');
return null;
final assetId =
new AssetId(uri.pathSegments.first, uri.pathSegments.skip(1).join('/'));
if (!await _reader.hasInput(assetId)) {
throw new ArgumentError.value('Could not read asset at uri $url', 'url');
}
return await _reader.readAsString(assetId);
return _reader.readAsString(assetId);
}
}

View File

@ -1,36 +0,0 @@
library angular2.src.transform.di_transformer;
import 'package:barback/barback.dart';
import 'package:dart_style/dart_style.dart';
import 'directive_linker/transformer.dart';
import 'directive_processor/transformer.dart';
import 'reflection_remover/transformer.dart';
import 'common/formatter.dart' as formatter;
import 'common/options.dart';
import 'common/options_reader.dart';
export 'common/options.dart';
/// Removes the mirror-based initialization logic and replaces it with static
/// logic.
class DiTransformerGroup extends TransformerGroup {
DiTransformerGroup._(phases) : super(phases) {
formatter.init(new DartFormatter());
}
factory DiTransformerGroup(TransformerOptions options) {
var phases = [
[new ReflectionRemover(options)],
[new DirectiveProcessor(null)]
];
phases.addAll(new List.generate(
options.optimizationPhases, (_) => [new EmptyNgDepsRemover()]));
phases.add([new DirectiveLinker()]);
return new DiTransformerGroup._(phases);
}
factory DiTransformerGroup.asPlugin(BarbackSettings settings) {
return new DiTransformerGroup(parseBarbackSettings(settings));
}
}

View File

@ -32,16 +32,16 @@ class DirectiveLinker extends Transformer implements DeclaringTransformer {
Future apply(Transform transform) async {
await log.initZoned(transform, () async {
var reader = new AssetReader.fromTransform(transform);
var assetId = transform.primaryInput.id;
var ngDepsModel = await linkNgDeps(reader, assetId);
var primaryId = transform.primaryInput.id;
var ngDepsModel = await linkNgDeps(reader, primaryId);
// See above
// transform.consumePrimary();
var outputAssetId = _depsAssetId(assetId);
var outputAssetId = _depsAssetId(primaryId);
if (ngDepsModel != null) {
var buf = new StringBuffer();
var writer = new NgDepsWriter(buf);
writer.writeNgDepsModel(ngDepsModel);
var formattedCode = formatter.format('$buf', uri: assetId.path);
var formattedCode = formatter.format('$buf', uri: primaryId.path);
transform.addOutput(new Asset.fromString(outputAssetId, formattedCode));
} else {
transform.addOutput(new Asset.fromString(outputAssetId, ''));

View File

@ -1,74 +0,0 @@
library angular2.transform.directive_metadata_extractor.extractor;
import 'dart:async';
import 'dart:convert';
import 'package:analyzer/analyzer.dart';
import 'package:angular2/src/transform/common/asset_reader.dart';
import 'package:angular2/src/transform/common/logging.dart';
import 'package:angular2/src/transform/common/names.dart';
import 'package:angular2/src/transform/common/ng_deps.dart';
import 'package:angular2/src/transform/common/ng_meta.dart';
import 'package:barback/barback.dart';
import 'package:code_transformers/assets.dart';
/// Returns [NgMeta] associated with [entryPoint].
///
/// This includes entries for every `Directive`-annotated classes and
/// constants that match the directive-aliases pattern, which are visible in a
/// file importing `entryPoint`. That is, this includes all `Directive`
/// annotated public classes in `entryPoint`, all `DirectiveAlias` annotated
/// public variables, and any assets which it `export`s. Returns an empty
/// [NgMeta] if there are no `Directive`-annotated classes in `entryPoint`.
Future<NgMeta> extractDirectiveMetadata(
AssetReader reader, AssetId entryPoint) {
return _extractDirectiveMetadataRecursive(
reader, entryPoint, new Set<AssetId>());
}
final _nullFuture = new Future.value(null);
Future<NgMeta> _extractDirectiveMetadataRecursive(
AssetReader reader, AssetId entryPoint, Set<AssetId> seen) async {
if (seen.contains(entryPoint)) return _nullFuture;
seen.add(entryPoint);
var ngMeta = new NgMeta.empty();
if (!(await reader.hasInput(entryPoint))) return ngMeta;
var ngDeps = await NgDeps.parse(reader, entryPoint);
_extractMetadata(ngDeps, ngMeta);
var aliasesFile =
new AssetId(entryPoint.package, toAliasExtension(entryPoint.path));
if (await reader.hasInput(aliasesFile)) {
ngMeta.addAll(new NgMeta.fromJson(
JSON.decode(await reader.readAsString(aliasesFile))));
}
await Future.wait(ngDeps.exports.map((export) {
var uri = stringLiteralToString(export.uri);
if (uri.startsWith('dart:')) return _nullFuture;
uri = toDepsExtension(uri);
var assetId = uriToAssetId(entryPoint, uri, logger, null /* span */,
errorOnAbsolute: false);
if (assetId == entryPoint) return _nullFuture;
return _extractDirectiveMetadataRecursive(reader, assetId, seen)
.then((exportedNgMeta) {
if (exportedNgMeta != null) {
ngMeta.addAll(exportedNgMeta);
}
});
}));
return ngMeta;
}
// TODO(sigmund): rather than having to parse it from generated code. we should
// be able to produce directly all information we need for ngMeta.
void _extractMetadata(NgDeps ngDeps, NgMeta ngMeta) {
if (ngDeps == null) return;
ngDeps.registeredTypes.forEach((type) {
ngMeta.types[type.typeName.name] = type.directiveMetadata;
});
}

View File

@ -1,49 +0,0 @@
library angular2.transform.directive_metadata_extractor.transformer;
import 'dart:async';
import 'dart:convert';
import 'package:angular2/src/transform/common/asset_reader.dart';
import 'package:angular2/src/transform/common/logging.dart' as log;
import 'package:angular2/src/transform/common/names.dart';
import 'package:barback/barback.dart';
import 'extractor.dart';
/// Transformer responsible for processing .ng_deps.dart files created by
/// {@link DirectiveProcessor} and creating associated `.ng_meta.dart` files.
/// These files contain commented Json-formatted representations of all
/// `Directive`s in the associated file.
class DirectiveMetadataExtractor extends Transformer
implements DeclaringTransformer {
final _encoder = const JsonEncoder.withIndent(' ');
DirectiveMetadataExtractor();
@override
bool isPrimary(AssetId id) => id.path.endsWith(DEPS_EXTENSION);
@override
declareOutputs(DeclaringTransform transform) {
transform.declareOutput(_outputAssetId(transform.primaryId));
}
@override
Future apply(Transform transform) async {
await log.initZoned(transform, () async {
var reader = new AssetReader.fromTransform(transform);
var fromAssetId = transform.primaryInput.id;
var ngMeta = await extractDirectiveMetadata(reader, fromAssetId);
if (ngMeta != null && !ngMeta.isEmpty) {
transform.addOutput(new Asset.fromString(
_outputAssetId(fromAssetId), _encoder.convert(ngMeta.toJson())));
}
});
}
}
AssetId _outputAssetId(AssetId inputAssetId) {
assert(inputAssetId.path.endsWith(DEPS_EXTENSION));
return new AssetId(inputAssetId.package, toMetaExtension(inputAssetId.path));
}

View File

@ -0,0 +1,78 @@
library angular2.transform.directive_metadata_linker.linker;
import 'dart:async';
import 'dart:convert';
import 'package:angular2/src/transform/common/asset_reader.dart';
import 'package:angular2/src/transform/common/logging.dart';
import 'package:angular2/src/transform/common/names.dart';
import 'package:angular2/src/transform/common/ng_meta.dart';
import 'package:barback/barback.dart';
import 'package:code_transformers/assets.dart';
import 'package:path/path.dart' as path;
/// Returns [NgMeta] associated with [entryPoint] combined with the [NgMeta] of
/// all files `export`ed from the original file.
///
/// This includes entries for every `Directive`-annotated class and
/// constants that match the directive-aliases pattern.
///
/// There are entries for each of these which is visible from a file importing
/// the original .dart file that produced `entryPoint`. That is, this includes
/// all `Directive` annotated public classes in that file, all `DirectiveAlias`
/// annotated public variables, and any of those entries which are visible from
/// files which the .dart file `export`ed.
///
/// Returns an empty [NgMeta] if there are no `Directive`-annotated classes or
/// `DirectiveAlias` annotated constants in `entryPoint`.
Future<NgMeta> linkDirectiveMetadata(AssetReader reader, AssetId entryPoint) {
return _linkDirectiveMetadataRecursive(
reader, entryPoint, new Set<AssetId>());
}
final _nullFuture = new Future.value(null);
// TODO(kegluneq): Don't reinvent the wheel? Centalize?
AssetId _fromPackageUri(String packageUri) {
var pathParts = path.url.split(packageUri);
return new AssetId(pathParts[0].substring('package:'.length),
'lib/${pathParts.getRange(1, pathParts.length).join('/')}');
}
Future<NgMeta> _linkDirectiveMetadataRecursive(
AssetReader reader, AssetId entryPoint, Set<AssetId> seen) async {
if (entryPoint == null) {
return new NgMeta.empty();
}
// Break cycles, if they exist.
if (seen.contains(entryPoint)) return _nullFuture;
seen.add(entryPoint);
if (!(await reader.hasInput(entryPoint))) return new NgMeta.empty();
var ngMetaJson = await reader.readAsString(entryPoint);
if (ngMetaJson == null || ngMetaJson.isEmpty) return new NgMeta.empty();
var ngMeta = new NgMeta.fromJson(JSON.decode(ngMetaJson));
if (ngMeta.exports == null) return ngMeta;
// Recursively add NgMeta files from `exports`.
return Future.wait(ngMeta.exports.map((uri) {
if (uri.startsWith('dart:')) return _nullFuture;
var metaUri = toMetaExtension(uri);
var assetId;
if (uri.startsWith('package:')) {
assetId = _fromPackageUri(metaUri);
} else {
assetId = uriToAssetId(entryPoint, metaUri, logger, null /* span */,
errorOnAbsolute: false);
}
return _linkDirectiveMetadataRecursive(reader, assetId, seen)
.then((exportedNgMeta) {
if (exportedNgMeta != null) {
ngMeta.addAll(exportedNgMeta);
}
});
})).then((_) => ngMeta);
}

View File

@ -0,0 +1,56 @@
library angular2.transform.directive_metadata_linker.transformer;
import 'dart:async';
import 'dart:convert';
import 'package:angular2/src/transform/common/asset_reader.dart';
import 'package:angular2/src/transform/common/logging.dart' as log;
import 'package:angular2/src/transform/common/names.dart';
import 'package:barback/barback.dart';
import 'linker.dart';
/// Transformer responsible for processing .ng_meta.json files created by
/// {@link DirectiveProcessor} and "linking" them.
///
/// This step ensures that for libraries that export, all `Directive`s reachable
/// from that library are declared in its associated .ng_meta.json file.
///
/// See `common/ng_meta.dart` for the JSON format of these files are serialized
/// to.
class DirectiveMetadataLinker extends Transformer
implements DeclaringTransformer {
final _encoder = const JsonEncoder.withIndent(' ');
@override
bool isPrimary(AssetId id) => id.path.endsWith(META_EXTENSION);
@override
declareOutputs(DeclaringTransform transform) {
// TODO(kegluenq): We should consume this, but doing so causes barback to
// incorrectly determine what assets are available in this phase.
// transform.consumePrimary();
transform.declareOutput(transform.primaryId);
}
@override
Future apply(Transform transform) {
return log.initZoned(transform, () {
var primaryId = transform.primaryInput.id;
return linkDirectiveMetadata(
new AssetReader.fromTransform(transform), primaryId).then((ngMeta) {
// See above
// transform.consumePrimary();
if (ngMeta != null && !ngMeta.isEmpty) {
transform.addOutput(new Asset.fromString(
primaryId, _encoder.convert(ngMeta.toJson())));
} else {
// Not outputting an asset could confuse barback, so output an
// empty one.
transform.addOutput(transform.primaryInput);
}
});
});
}
}

View File

@ -4,140 +4,102 @@ import 'dart:async';
import 'package:analyzer/analyzer.dart';
import 'package:analyzer/src/generated/ast.dart';
import 'package:analyzer/src/generated/error.dart';
import 'package:analyzer/src/generated/parser.dart';
import 'package:analyzer/src/generated/scanner.dart';
import 'package:analyzer/src/string_source.dart';
import 'package:angular2/src/core/render/xhr.dart' show XHR;
import 'package:angular2/src/transform/common/async_string_writer.dart';
import 'package:angular2/src/transform/common/logging.dart';
import 'package:angular2/src/transform/common/model/annotation_model.pb.dart';
import 'package:angular2/src/transform/common/model/ng_deps_model.pb.dart';
import 'package:code_transformers/assets.dart';
import 'package:angular2/src/transform/common/asset_reader.dart';
import 'package:barback/barback.dart' show AssetId;
import 'package:source_span/source_span.dart';
import 'package:path/path.dart' as path;
Future inlineViewProps(XHR xhr, NgDepsModel model) {
var toWait = <Future>[];
for (var reflectable in model.reflectables) {
for (var annotation in reflectable.annotations) {
if (annotation.isView) {
var rawTemplateUrl = _getTemplateUrlValue(annotation);
if (rawTemplateUrl != null) {
if (_hasTemplateValue(annotation)) {
logger.warning('Both template url and template are specified. '
'Ignoring `templateUrl` value.');
} else {
var url = _dumbEval(rawTemplateUrl);
if (url is String) {
toWait.add(_readOrEmptyString(xhr, url).then((templateText) {
_setTemplateValue(annotation, "r'''$templateText'''");
}));
} else {
logger.warning('template url is not a String ($rawTemplateUrl)');
}
}
}
var rawStyleUrls = _getStyleUrlsValue(annotation);
if (rawStyleUrls != null) {
if (_hasStylesValue(annotation)) {
logger.warning('Both styleUrls and styles are specified. '
'Ignoring `styleUrls` value.');
} else {
var urls = _dumbEval(rawStyleUrls);
if (urls is List) {
var writer = new AsyncStringWriter();
for (var url in urls) {
if (url is String) {
writer.print("r'''");
writer.asyncPrint(_readOrEmptyString(xhr, url));
writer.print("''', ");
} else {
logger.warning('style url is not a String (${url})');
}
}
toWait.add(writer.asyncToString().then((styleUrlText) {
_setStylesValue(annotation, 'const [$styleUrlText]');
_removeStyleUrlsValue(annotation);
}));
} else {
logger.warning(
'styleUrls is not a List of strings ($rawStyleUrls)');
}
}
}
/// Reads the code at `assetId`, inlining any `part` directives in that code.
///
/// Returns `null` if the code represented by `assetId` is a `part`.
///
/// Order of `part`s is preserved. That is, if in the main library we have
/// ```
/// library main;
///
/// part 'lib1.dart'
/// part 'lib2.dart'
/// ```
/// The output will first have the contents of lib1 followed by the contents of
/// lib2.dart, followed by the original code in the library.
Future<String> inlineParts(AssetReader reader, AssetId assetId) async {
var code = await reader.readAsString(assetId);
var directivesVisitor = new _NgDepsDirectivesVisitor();
parseDirectives(code, name: assetId.path)
.directives
.accept(directivesVisitor);
// If this is part of another library, its contents will be processed by its
// parent, so it does not need its own `.ng_deps.dart` file.
if (directivesVisitor.isPart) return null;
return _getAllDeclarations(reader, assetId, code, directivesVisitor);
}
/// Processes `visitor.parts`, reading and appending their contents to the
/// original `code`.
Future<String> _getAllDeclarations(AssetReader reader, AssetId assetId,
String code, _NgDepsDirectivesVisitor visitor) {
if (visitor.parts.isEmpty) return new Future<String>.value(code);
var partsStart = visitor.parts.first.offset,
partsEnd = visitor.parts.last.end;
var asyncWriter = new AsyncStringWriter(code.substring(0, partsStart));
visitor.parts.forEach((partDirective) {
var uri = stringLiteralToString(partDirective.uri);
var partAssetId = uriToAssetId(assetId, uri, logger, null /* span */,
errorOnAbsolute: false);
asyncWriter.asyncPrint(reader.readAsString(partAssetId).then((partCode) {
if (partCode == null || partCode.isEmpty) {
logger.warning('Empty part at "${partDirective.uri}. Ignoring.',
asset: partAssetId);
return '';
}
}
// Remove any directives -- we just want declarations.
var parsedDirectives = parseDirectives(partCode, name: uri).directives;
return partCode.substring(parsedDirectives.last.end);
}).catchError((err, stackTrace) {
logger.warning(
'Failed while reading part at ${partDirective.uri}. Ignoring.\n'
'Error: $err\n'
'Stack Trace: $stackTrace',
asset: partAssetId,
span: new SourceFile(code, url: path.basename(assetId.path))
.span(partDirective.offset, partDirective.end));
}));
});
asyncWriter.print(code.substring(partsEnd));
return asyncWriter.asyncToString();
}
/// Visitor responsible for reading the `part` files of the visited AST and
/// determining if it is a part of another library.
class _NgDepsDirectivesVisitor extends Object with SimpleAstVisitor<Object> {
/// Whether the file we are processing is a part, that is, whether we have
/// visited a `part of` directive.
bool _isPart = false;
final List<PartDirective> _parts = <PartDirective>[];
bool get isPart => _isPart;
/// In the order encountered in the source.
Iterable<PartDirective> get parts => _parts;
@override
Object visitPartDirective(PartDirective node) {
_parts.add(node);
return null;
}
return Future.wait(toWait);
}
String _getNamedArgValue(AnnotationModel model, String argName) {
var value = null;
if (model.namedParameters != null) {
var match = model.namedParameters
.firstWhere((p) => p.name == argName, orElse: () => null);
value = match != null ? match.value : null;
@override
Object visitPartOfDirective(PartOfDirective node) {
_isPart = true;
return null;
}
return value;
}
String _getTemplateUrlValue(AnnotationModel model) =>
_getNamedArgValue(model, 'templateUrl');
String _getStyleUrlsValue(AnnotationModel model) =>
_getNamedArgValue(model, 'styleUrls');
bool _hasTemplateValue(AnnotationModel model) =>
_getNamedArgValue(model, 'template') != null;
bool _hasStylesValue(AnnotationModel model) =>
_getNamedArgValue(model, 'styles') != null;
void _setNamedArgValue(AnnotationModel model, String argName, String argValue) {
var matchedArg = model.namedParameters
.firstWhere((p) => p.name == argName, orElse: () => null);
if (matchedArg == null) {
matchedArg = new NamedParameter()..name = argName;
model.namedParameters.add(matchedArg);
}
matchedArg.value = argValue;
}
void _setTemplateValue(AnnotationModel model, String template) =>
_setNamedArgValue(model, 'template', template);
void _setStylesValue(AnnotationModel model, String styles) =>
_setNamedArgValue(model, 'styles', styles);
void _removeNamedArg(AnnotationModel model, String argName) {
model.namedParameters.removeWhere((p) => p.name == argName);
}
void _removeStyleUrlsValue(AnnotationModel model) =>
_removeNamedArg(model, 'styleUrls');
final _constantEvaluator = new ConstantEvaluator();
/// Attempts to read the content from {@link url}, if it returns null then
/// just return the empty string.
Future<String> _readOrEmptyString(XHR xhr, String url) async {
var content = await xhr.get(url);
if (content == null) {
content = '';
}
return content;
}
dynamic _dumbEval(String code) {
var source = new StringSource(code, code);
// TODO(kegluneq): Report errors.
var errorCollector = AnalysisErrorListener.NULL_LISTENER;
var reader = new CharSequenceReader(code);
var scanner = new Scanner(source, reader, errorCollector);
var parser = new Parser(source, errorCollector)
..currentToken = scanner.tokenize();
var expr = parser.parseExpression2();
var val = null;
if (expr is SimpleStringLiteral) {
val = stringLiteralToString(expr);
} else {
val = expr.accept(_constantEvaluator);
}
return val != ConstantEvaluator.NOT_A_CONSTANT ? val : null;
}

View File

@ -5,16 +5,15 @@ import 'dart:async';
import 'package:analyzer/analyzer.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/async_string_writer.dart';
import 'package:angular2/src/transform/common/code/ng_deps_code.dart';
import 'package:angular2/src/transform/common/directive_metadata_reader.dart';
import 'package:angular2/src/transform/common/interface_matcher.dart';
import 'package:angular2/src/transform/common/logging.dart';
import 'package:angular2/src/transform/common/model/ng_deps_model.pb.dart';
import 'package:angular2/src/transform/common/xhr_impl.dart';
import 'package:angular2/src/transform/common/ng_compiler.dart';
import 'package:angular2/src/transform/common/ng_meta.dart';
import 'package:barback/barback.dart' show AssetId;
import 'package:code_transformers/assets.dart';
import 'package:path/path.dart' as path;
import 'package:source_span/source_span.dart';
import 'package:angular2/src/compiler/template_compiler.dart';
import 'inliner.dart';
@ -26,136 +25,44 @@ import 'inliner.dart';
/// string unless `forceGenerate` is true, in which case an empty ngDeps
/// file is created.
Future<NgDepsModel> createNgDeps(AssetReader reader, AssetId assetId,
AnnotationMatcher annotationMatcher, NgMeta ngMeta,
{bool inlineViews}) async {
AnnotationMatcher annotationMatcher, NgMeta ngMeta) async {
// TODO(kegluneq): Shortcut if we can determine that there are no
// [Directive]s present, taking into account `export`s.
var code = await reader.readAsString(assetId);
var codeWithParts = await inlineParts(reader, assetId);
if (codeWithParts == null || codeWithParts.isEmpty) return null;
var directivesVisitor = new _NgDepsDirectivesVisitor();
parseDirectives(code, name: assetId.path)
.directives
.accept(directivesVisitor);
// If this is part of another library, its contents will be processed by its
// parent, so it does not need its own `.ng_deps.dart` file.
if (directivesVisitor.isPart) return null;
var codeWithParts =
await _getAllDeclarations(reader, assetId, code, directivesVisitor);
var parsedCode =
parseCompilationUnit(codeWithParts, name: '${assetId.path} and parts');
var ngDepsVisitor = new NgDepsVisitor(assetId, annotationMatcher);
// TODO(kegluneq): Parse `declarations` in the NgDepsModel as well.
parsedCode.accept(ngDepsVisitor);
var ngDepsModel = ngDepsVisitor.model;
var ngMetaVisitor = new _NgMetaVisitor(ngMeta);
var templateCompiler = createTemplateCompiler(reader);
var ngMetaVisitor = new _NgMetaVisitor(
ngMeta, assetId, annotationMatcher, _interfaceMatcher, templateCompiler);
parsedCode.accept(ngMetaVisitor);
await ngMetaVisitor.whenDone();
// If this file imports only dart: libraries and does not define any
// reflectables of its own, it doesn't need a .ng_deps.dart file.
if (!directivesVisitor.usesNonLangLibs &&
(ngDepsModel.reflectables == null || ngDepsModel.reflectables.isEmpty)) {
return null;
}
if (inlineViews) {
await inlineViewProps(new XhrImpl(reader, assetId), ngDepsModel);
if (ngDepsModel.reflectables == null || ngDepsModel.reflectables.isEmpty) {
if (ngDepsModel.imports.every(_isDartImport) &&
ngDepsModel.exports.every(_isDartImport)) {
return null;
}
}
return ngDepsModel;
}
/// Processes `visitor.parts`, reading and appending their contents to the
/// original `code`.
/// Order of `part`s is preserved. That is, if in the main library we have
/// ```
/// library main;
///
/// part 'lib1.dart'
/// part 'lib2.dart'
/// ```
/// The output will first have the contents of lib1 followed by the contents of
/// lib2.dart, followed by the original code in the library.
Future<String> _getAllDeclarations(AssetReader reader, AssetId assetId,
String code, _NgDepsDirectivesVisitor visitor) {
if (visitor.parts.isEmpty) return new Future<String>.value(code);
// `model` can be an [ImportModel] or [ExportModel].
bool _isDartImport(dynamic model) => model.uri.startsWith('dart:');
var partsStart = visitor.parts.first.offset,
partsEnd = visitor.parts.last.end;
// TODO(kegluneq): Allow the caller to provide an InterfaceMatcher.
final _interfaceMatcher = new InterfaceMatcher();
var asyncWriter = new AsyncStringWriter(code.substring(0, partsStart));
visitor.parts.forEach((partDirective) {
var uri = stringLiteralToString(partDirective.uri);
var partAssetId = uriToAssetId(assetId, uri, logger, null /* span */,
errorOnAbsolute: false);
asyncWriter.asyncPrint(reader.readAsString(partAssetId).then((partCode) {
if (partCode == null || partCode.isEmpty) {
logger.warning('Empty part at "${partDirective.uri}. Ignoring.',
asset: partAssetId);
return '';
}
// Remove any directives -- we just want declarations.
var parsedDirectives = parseDirectives(partCode, name: uri).directives;
return partCode.substring(parsedDirectives.last.end);
}).catchError((err, stackTrace) {
logger.warning(
'Failed while reading part at ${partDirective.uri}. Ignoring.\n'
'Error: $err\n'
'Stack Trace: $stackTrace',
asset: partAssetId,
span: new SourceFile(code, url: path.basename(assetId.path))
.span(partDirective.offset, partDirective.end));
}));
});
asyncWriter.print(code.substring(partsEnd));
return asyncWriter.asyncToString();
}
/// Visitor responsible for flattening directives passed to it.
/// Once this has visited an Ast, use [#writeTo] to write out the directives
/// for the .ng_deps.dart file. See [#writeTo] for details.
class _NgDepsDirectivesVisitor extends Object with SimpleAstVisitor<Object> {
/// Whether this library `imports` or `exports` any non-'dart:' libraries.
bool _usesNonLangLibs = false;
/// Whether the file we are processing is a part, that is, whether we have
/// visited a `part of` directive.
bool _isPart = false;
final List<PartDirective> _parts = <PartDirective>[];
bool get usesNonLangLibs => _usesNonLangLibs;
bool get isPart => _isPart;
/// In the order encountered in the source.
Iterable<PartDirective> get parts => _parts;
Object _updateUsesNonLangLibs(UriBasedDirective directive) {
_usesNonLangLibs = _usesNonLangLibs ||
!stringLiteralToString(directive.uri).startsWith('dart:');
return null;
}
@override
Object visitExportDirective(ExportDirective node) =>
_updateUsesNonLangLibs(node);
@override
Object visitImportDirective(ImportDirective node) =>
_updateUsesNonLangLibs(node);
@override
Object visitPartDirective(PartDirective node) {
_parts.add(node);
return null;
}
}
/// Visitor responsible for visiting a file's [Declaration]s and outputting the
/// Visitor responsible for visiting a file and outputting the
/// code necessary to register the file with the Angular 2 system.
class _NgMetaVisitor extends Object with SimpleAstVisitor<Object> {
/// Output ngMeta information about aliases.
@ -164,14 +71,52 @@ class _NgMetaVisitor extends Object with SimpleAstVisitor<Object> {
// parsing the ngdeps files later.
final NgMeta ngMeta;
_NgMetaVisitor(this.ngMeta);
/// The [AssetId] we are currently processing.
final AssetId assetId;
final DirectiveMetadataReader _reader;
final _normalizations = <Future>[];
_NgMetaVisitor(this.ngMeta, this.assetId, AnnotationMatcher annotationMatcher,
InterfaceMatcher interfaceMatcher, TemplateCompiler templateCompiler)
: _reader = new DirectiveMetadataReader(
annotationMatcher, interfaceMatcher, templateCompiler);
Future whenDone() {
return Future.wait(_normalizations);
}
@override
Object visitCompilationUnit(CompilationUnit node) {
if (node == null || node.declarations == null) return null;
if (node == null ||
(node.directives == null && node.declarations == null)) {
return null;
}
node.directives.accept(this);
return node.declarations.accept(this);
}
@override
Object visitExportDirective(ExportDirective node) {
ngMeta.exports.add(stringLiteralToString(node.uri));
return null;
}
@override
Object visitClassDeclaration(ClassDeclaration node) {
_normalizations.add(_reader
.readDirectiveMetadata(node, assetId)
.then((compileDirectiveMetadata) {
if (compileDirectiveMetadata != null) {
ngMeta.types[compileDirectiveMetadata.type.name] =
compileDirectiveMetadata;
}
}).catchError((err) {
logger.error('ERROR: $err');
}));
return null;
}
@override
Object visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) {
// We process any top-level declaration that fits the directive-alias

View File

@ -3,6 +3,7 @@ library angular2.transform.directive_processor.transformer;
import 'dart:async';
import 'dart:convert';
import 'package:angular2/src/core/dom/html_adapter.dart';
import 'package:angular2/src/transform/common/asset_reader.dart';
import 'package:angular2/src/transform/common/logging.dart' as log;
import 'package:angular2/src/transform/common/names.dart';
@ -23,6 +24,7 @@ import 'rewriter.dart';
/// be followed by {@link DirectiveLinker}.
class DirectiveProcessor extends Transformer implements DeclaringTransformer {
final TransformerOptions options;
final _encoder = const JsonEncoder.withIndent(' ');
DirectiveProcessor(this.options);
@ -34,33 +36,40 @@ class DirectiveProcessor extends Transformer implements DeclaringTransformer {
/// determine that one or the other will not be emitted.
@override
declareOutputs(DeclaringTransform transform) {
transform.declareOutput(_depsOutputId(transform.primaryId));
transform.declareOutput(_metaOutputId(transform.primaryId));
transform.declareOutput(_ngMetaAssetId(transform.primaryId));
transform.declareOutput(_ngDepsAssetId(transform.primaryId));
}
@override
Future apply(Transform transform) async {
Html5LibDomAdapter.makeCurrent();
await log.initZoned(transform, () async {
var assetId = transform.primaryInput.id;
var primaryId = transform.primaryInput.id;
var reader = new AssetReader.fromTransform(transform);
var ngMeta = new NgMeta.empty();
var ngDepsModel = await createNgDeps(
reader, assetId, options.annotationMatcher, ngMeta,
inlineViews: options.inlineViews);
reader, primaryId, options.annotationMatcher, ngMeta);
// TODO(kegluneq): Combine NgDepsModel with NgMeta in a single .json file.
if (ngDepsModel != null) {
var ngDepsAssetId = _ngDepsAssetId(primaryId);
transform.addOutput(new Asset.fromString(
_depsOutputId(assetId), ngDepsModel.writeToJson()));
ngDepsAssetId, _encoder.convert(ngDepsModel.writeToJsonMap())));
}
var metaOutputId = _metaOutputId(assetId);
var metaOutputId = _ngMetaAssetId(primaryId);
if (!ngMeta.isEmpty) {
transform.addOutput(new Asset.fromString(metaOutputId,
new JsonEncoder.withIndent(" ").convert(ngMeta.toJson())));
transform.addOutput(new Asset.fromString(
metaOutputId, _encoder.convert(ngMeta.toJson())));
}
});
}
}
AssetId _depsOutputId(AssetId primaryId) =>
primaryId.changeExtension(DEPS_JSON_EXTENSION);
AssetId _metaOutputId(AssetId primaryId) =>
primaryId.changeExtension(ALIAS_EXTENSION);
AssetId _ngMetaAssetId(AssetId primaryInputId) {
return new AssetId(
primaryInputId.package, toMetaExtension(primaryInputId.path));
}
AssetId _ngDepsAssetId(AssetId primaryInputId) {
return new AssetId(
primaryInputId.package, toJsonExtension(primaryInputId.path));
}

View File

@ -0,0 +1,151 @@
library angular2.src.transform.transform_for_test;
import 'dart:async';
import 'package:analyzer/analyzer.dart';
import 'package:analyzer/src/generated/ast.dart';
import 'package:angular2/src/core/render/xhr.dart' show XHR;
import 'package:angular2/src/transform/common/asset_reader.dart';
import 'package:barback/barback.dart';
import 'common/options_reader.dart';
import 'common/asset_reader.dart';
import 'common/async_string_writer.dart';
import 'common/logging.dart';
import 'common/url_resolver.dart';
import 'common/xhr_impl.dart';
import 'directive_processor/inliner.dart';
/// Processes .dart files and inlines `templateUrl` and styleUrls` values.
class InlinerForTest extends Transformer implements DeclaringTransformer {
InlinerForTest();
@override
bool isPrimary(AssetId id) => id.extension.endsWith('dart');
@override
declareOutputs(DeclaringTransform transform) {
transform.consumePrimary();
transform.declareOutput(transform.primaryId);
}
@override
Future apply(Transform transform) async {
return log.initZoned(transform, () async {
var primaryId = transform.primaryInput.id;
transform.consumePrimary();
var inlinedCode =
await inline(new AssetReader.fromTransform(transform), primaryId);
if (inlinedCode == null || inlinedCode.isEmpty) {
transform.addOutput(transform.primaryInput);
} else {
transform.addOutput(new Asset.fromString(primaryId, inlinedCode));
}
});
}
factory InlinerForTest.asPlugin(BarbackSettings settings) {
return new InlinerForTest(parseBarbackSettings(settings));
}
}
Future<String> inline(AssetReader reader, AssetId assetId) async {
var codeWithParts = await inlineParts(reader, assetId);
if (codeWithParts == null) return null;
var parsedCode =
parseCompilationUnit(codeWithParts, name: '${assetId.path} and parts');
var writer = new AsyncStringWriter();
var visitor = new _ViewPropInliner(assetId, writer, new XhrImpl(reader));
parsedCode.accept(visitor);
return writer.asyncToString();
}
final _urlResolver = const TransformerUrlResolver();
class _ViewPropInliner extends ToSourceVisitor {
final Uri _baseUri;
final AsyncStringWriter _writer;
final XHR _xhr;
_ViewPropInliner._(this._baseUri, AsyncStringWriter writer, this._xhr)
: _writer = writer,
super(writer);
factory _ViewPropInliner(AssetId assetId, AsyncStringWriter writer, XHR xhr) {
var baseUri =
new Uri(scheme: 'asset', path: '${assetId.package}/${assetId.path}');
return new _ViewPropInliner._(baseUri, writer, xhr);
}
@override
Object visitNamedExpression(NamedExpression node) {
if (node.name is! Label || node.name.label is! SimpleIdentifier) {
throw new FormatException(
'Angular 2 currently only supports simple identifiers in directives.',
'$node' /* source */);
}
var keyString = '${node.name.label}';
switch (keyString) {
case 'templateUrl':
_populateTemplateUrl(node.expression);
break;
case 'styleUrls':
_populateStyleUrls(node.expression);
break;
}
return super.visitNamedExpression(node);
}
void _populateStyleUrls(Expression value) {
var urls = _dumbEval(value);
if (urls is! List) {
logger.warning('styleUrls is not a List of Strings ($value)');
return;
}
_writer.print('styles: const [');
for (var url in urls) {
if (url is String) {
_writer.print("r'''");
_writer.asyncPrint(_readOrEmptyString(url));
_writer.print("''', ");
} else {
logger.warning('style url is not a String (${url})');
}
}
_writer.println('],');
}
void _populateTemplateUrl(Expression value) {
var url = _dumbEval(value);
if (url is! String) {
logger.warning('template url is not a String ($value)');
return;
}
_writer.print("template: r'''");
_writer.asyncPrint(_readOrEmptyString(url));
_writer.println("''',");
}
/// Attempts to read the content from [url]. If [url] is relative, uses
/// [_baseUri] as resolution base.
Future<String> _readOrEmptyString(String url) async {
final resolvedUri = _urlResolver.resolve(_baseUri.toString(), url);
return _xhr.get(resolvedUri).catchError((_) {
logger.error('$_rootAssetId: could not read $url');
return '';
});
}
}
final _constantEvaluator = new ConstantEvaluator();
dynamic _dumbEval(Expression expr) {
var val;
if (expr is SimpleStringLiteral) {
val = stringLiteralToString(expr);
} else {
val = expr.accept(_constantEvaluator);
}
return val != ConstantEvaluator.NOT_A_CONSTANT ? val : null;
}

View File

@ -11,7 +11,8 @@ import 'package:angular2/src/core/change_detection/proto_change_detector.dart';
import 'package:angular2/src/core/change_detection/proto_record.dart';
import 'package:angular2/src/core/change_detection/event_binding.dart';
import 'package:angular2/src/core/change_detection/binding_record.dart';
import 'package:angular2/src/core/change_detection/codegen_facade.dart' show codify;
import 'package:angular2/src/core/change_detection/codegen_facade.dart'
show codify;
import 'package:angular2/src/core/facade/exceptions.dart' show BaseException;
/// Responsible for generating change detector classes for Angular 2.
@ -90,8 +91,9 @@ class _CodegenState {
final ChangeDetectorGenConfig _genConfig;
final List<BindingTarget> _propertyBindingTargets;
String get _changeDetectionStrategyAsCode =>
_changeDetectionStrategy == null ? 'null' : '${_genPrefix}${_changeDetectionStrategy}';
String get _changeDetectionStrategyAsCode => _changeDetectionStrategy == null
? 'null'
: '${_genPrefix}${_changeDetectionStrategy}';
/// The module prefix for pregen_proto_change_detector
final String _genPrefix;
@ -110,13 +112,15 @@ class _CodegenState {
this._names,
this._genConfig);
factory _CodegenState(String genPrefix, String typeName, String changeDetectorTypeName,
ChangeDetectorDefinition def) {
factory _CodegenState(String genPrefix, String typeName,
String changeDetectorTypeName, ChangeDetectorDefinition def) {
var protoRecords = createPropertyRecords(def);
var eventBindings = createEventRecords(def);
var propertyBindingTargets = def.bindingRecords.map((b) => b.target).toList();
var propertyBindingTargets =
def.bindingRecords.map((b) => b.target).toList();
var names = new CodegenNameUtil(protoRecords, eventBindings, def.directiveRecords, '$genPrefix$_UTIL');
var names = new CodegenNameUtil(
protoRecords, eventBindings, def.directiveRecords, '$genPrefix$_UTIL');
var logic = new CodegenLogicUtil(names, '$genPrefix$_UTIL', def.strategy);
return new _CodegenState._(
genPrefix,
@ -141,8 +145,8 @@ class _CodegenState {
$_changeDetectorTypeName(dispatcher)
: super(${codify(_changeDetectorDefId)},
dispatcher, ${_records.length},
${_changeDetectorTypeName}.gen_propertyBindingTargets,
${_changeDetectorTypeName}.gen_directiveIndices,
${_changeDetectorTypeName}.${_GEN_PROPERTY_BINDING_TARGETS_NAME},
${_changeDetectorTypeName}.${_GEN_DIRECTIVE_INDICES_NAME},
${_changeDetectionStrategyAsCode}) {
dehydrateDirectives(false);
}
@ -183,18 +187,20 @@ class _CodegenState {
}
String _genPropertyBindingTargets() {
var targets = _logic.genPropertyBindingTargets(_propertyBindingTargets, this._genConfig.genDebugInfo);
return "static var gen_propertyBindingTargets = ${targets}";
var targets = _logic.genPropertyBindingTargets(
_propertyBindingTargets, this._genConfig.genDebugInfo);
return "static final ${_GEN_PROPERTY_BINDING_TARGETS_NAME} = ${targets}";
}
String _genDirectiveIndices() {
var indices = _logic.genDirectiveIndices(_directiveRecords);
return "static var gen_directiveIndices = ${indices}";
return "static final ${_GEN_DIRECTIVE_INDICES_NAME} = ${indices}";
}
String _maybeGenHandleEventInternal() {
if (_eventBindings.length > 0) {
var handlers = _eventBindings.map((eb) => _genEventBinding(eb)).join("\n");
var handlers =
_eventBindings.map((eb) => _genEventBinding(eb)).join("\n");
return '''
handleEventInternal(eventName, elIndex, locals) {
var ${this._names.getPreventDefaultAccesor()} = false;
@ -216,7 +222,7 @@ class _CodegenState {
}''';
}
String _genEventBindingEval(EventBinding eb, ProtoRecord r){
String _genEventBindingEval(EventBinding eb, ProtoRecord r) {
if (r.lastInBinding) {
var evalRecord = _logic.genEventBindingEvalValue(eb, r);
var markPath = _genMarkPathToRootAsCheckOnce(r);
@ -271,7 +277,8 @@ class _CodegenState {
}
String _maybeGenAfterContentLifecycleCallbacks() {
var directiveNotifications = _logic.genContentLifecycleCallbacks(_directiveRecords);
var directiveNotifications =
_logic.genContentLifecycleCallbacks(_directiveRecords);
if (directiveNotifications.isNotEmpty) {
return '''
void afterContentLifecycleCallbacksInternal() {
@ -284,7 +291,8 @@ class _CodegenState {
}
String _maybeGenAfterViewLifecycleCallbacks() {
var directiveNotifications = _logic.genViewLifecycleCallbacks(_directiveRecords);
var directiveNotifications =
_logic.genViewLifecycleCallbacks(_directiveRecords);
if (directiveNotifications.isNotEmpty) {
return '''
void afterViewLifecycleCallbacksInternal() {
@ -420,7 +428,9 @@ class _CodegenState {
var newValue = _names.getLocalName(r.selfIndex);
var oldValue = _names.getFieldName(r.selfIndex);
var notifyDebug = _genConfig.logBindingUpdate ? "this.logBindingUpdate(${newValue});" : "";
var notifyDebug = _genConfig.logBindingUpdate
? "this.logBindingUpdate(${newValue});"
: "";
var br = r.bindingRecord;
if (br.target.isDirective()) {
@ -527,4 +537,7 @@ const _NOT_IDENTICAL_CHECK_FN = 'looseNotIdentical';
const _IS_CHANGED_LOCAL = 'isChanged';
const _PREGEN_PROTO_CHANGE_DETECTOR_IMPORT =
'package:angular2/src/core/change_detection/pregen_proto_change_detector.dart';
const _GEN_PROPERTY_BINDING_TARGETS_NAME =
'${_GEN_PREFIX}_propertyBindingTargets';
const _GEN_DIRECTIVE_INDICES_NAME = '${_GEN_PREFIX}_directiveIndices';
const _UTIL = 'ChangeDetectionUtil';

View File

@ -1,9 +1,11 @@
library angular2.transform.template_compiler.view_definition_creator;
library angular2.transform.template_compiler.compile_data_creator;
import 'dart:async';
import 'dart:convert';
import 'package:analyzer/analyzer.dart';
import 'package:angular2/src/compiler/directive_metadata.dart';
import 'package:angular2/src/compiler/template_compiler.dart';
import 'package:angular2/src/core/render/api.dart';
import 'package:angular2/src/transform/common/asset_reader.dart';
import 'package:angular2/src/transform/common/logging.dart';
@ -13,28 +15,24 @@ import 'package:angular2/src/transform/common/ng_meta.dart';
import 'package:barback/barback.dart';
import 'package:code_transformers/assets.dart';
/// Creates [ViewDefinition] objects for all `View` `Directive`s defined in
/// `entryPoint`.
Future<ViewDefinitionResults> createViewDefinitions(
/// Creates [NormalizedComponentWithViewDirectives] objects for all `View`
/// `Directive`s defined in `entryPoint`.
///
/// The returned value wraps the [NgDeps] at `entryPoint` as well as these
/// created objects.
Future<CompileDataResults> createCompileData(
AssetReader reader, AssetId entryPoint) async {
return await new _ViewDefinitionCreator(reader, entryPoint).createViewDefs();
return new _CompileDataCreator(reader, entryPoint).createCompileData();
}
class ViewDefinitionResults {
class CompileDataResults {
final NgDeps ngDeps;
final Map<RegisteredType, ViewDefinitionEntry> viewDefinitions;
ViewDefinitionResults._(this.ngDeps, this.viewDefinitions);
final Map<RegisteredType,
NormalizedComponentWithViewDirectives> viewDefinitions;
CompileDataResults._(this.ngDeps, this.viewDefinitions);
}
class ViewDefinitionEntry {
final RenderDirectiveMetadata hostMetadata;
final ViewDefinition viewDef;
ViewDefinitionEntry._(this.hostMetadata, this.viewDef);
}
String _getComponentId(AssetId assetId, String className) => '$className';
// TODO(kegluenq): Improve this test.
bool _isViewAnnotation(InstanceCreationExpression node) {
var constructorName = node.constructorName.type.name;
@ -46,42 +44,38 @@ bool _isViewAnnotation(InstanceCreationExpression node) {
/// Creates [ViewDefinition] objects for all `View` `Directive`s defined in
/// `entryPoint`.
class _ViewDefinitionCreator {
class _CompileDataCreator {
final AssetReader reader;
final AssetId entryPoint;
final Future<NgDeps> ngDepsFuture;
_ViewDefinitionCreator(AssetReader reader, AssetId entryPoint)
_CompileDataCreator(AssetReader reader, AssetId entryPoint)
: this.reader = reader,
this.entryPoint = entryPoint,
ngDepsFuture = NgDeps.parse(reader, entryPoint);
Future<ViewDefinitionResults> createViewDefs() async {
Future<CompileDataResults> createCompileData() async {
var ngDeps = await ngDepsFuture;
var retVal = <RegisteredType, ViewDefinitionEntry>{};
var visitor = new _TemplateExtractVisitor(await _extractNgMeta());
var retVal = <RegisteredType, NormalizedComponentWithViewDirectives>{};
var visitor = new _DirectiveDependenciesVisitor(await _extractNgMeta());
ngDeps.registeredTypes.forEach((rType) {
visitor.reset();
rType.annotations.accept(visitor);
if (visitor.viewDef != null) {
if (visitor.compileData != null) {
// Note: we use '' because the current file maps to the default prefix.
var ngMeta = visitor._metadataMap[''];
var typeName = '${rType.typeName}';
var hostMetadata = null;
if (ngMeta.types.containsKey(typeName)) {
hostMetadata = ngMeta.types[typeName];
visitor.viewDef.componentId = hostMetadata.id;
visitor.compileData.component = ngMeta.types[typeName];
} else {
logger.warning('Missing component "$typeName" in metadata map',
asset: entryPoint);
visitor.viewDef.componentId = _getComponentId(entryPoint, typeName);
}
retVal[rType] =
new ViewDefinitionEntry._(hostMetadata, visitor.viewDef);
retVal[rType] = visitor.compileData;
}
});
return new ViewDefinitionResults._(ngDeps, retVal);
return new CompileDataResults._(ngDeps, retVal);
}
/// Creates a map from [AssetId] to import prefix for `.dart` libraries
@ -89,13 +83,11 @@ class _ViewDefinitionCreator {
/// Unprefixed imports have `null` as their value. `entryPoint` is included
/// in the map with no prefix.
Future<Map<AssetId, String>> _createImportAssetToPrefixMap() async {
// TODO(kegluneq): Support `part` directives.
var ngDeps = await ngDepsFuture;
var importAssetToPrefix = <AssetId, String>{};
// Include the `.ng_meta.json` file associated with `entryPoint`.
importAssetToPrefix[new AssetId(
entryPoint.package, toMetaExtension(entryPoint.path))] = null;
var importAssetToPrefix = <AssetId, String>{
entryPoint: null
};
for (ImportDirective node in ngDeps.imports) {
var uri = stringLiteralToString(node.uri);
@ -113,7 +105,7 @@ class _ViewDefinitionCreator {
/// Reads the `.ng_meta.json` files associated with all of `entryPoint`'s
/// imports and creates a map `Type` name, prefixed if appropriate to the
/// associated [RenderDirectiveMetadata].
/// associated [CompileDirectiveMetadata].
///
/// For example, if in `entryPoint` we have:
///
@ -129,13 +121,13 @@ class _ViewDefinitionCreator {
/// ```
///
/// This method will look for `component.ng_meta.json`to contain the
/// serialized [RenderDirectiveMetadata] for `MyComponent` and any other
/// serialized [CompileDirectiveMetadata] for `MyComponent` and any other
/// `Directive`s declared in `component.dart`. We use this information to
/// build a map:
///
/// ```
/// {
/// "prefix.MyComponent": [RenderDirectiveMetadata for MyComponent],
/// "prefix.MyComponent": [CompileDirectiveMetadata for MyComponent],
/// ...<any other entries>...
/// }
/// ```
@ -153,9 +145,6 @@ class _ViewDefinitionCreator {
try {
var json = JSON.decode(await reader.readAsString(metaAssetId));
var newMetadata = new NgMeta.fromJson(json);
newMetadata.types.forEach((className, metadata) {
metadata.id = _getComponentId(importAssetId, className);
});
ngMeta.addAll(newMetadata);
} catch (ex, stackTrace) {
logger.warning('Failed to decode: $ex, $stackTrace',
@ -168,23 +157,33 @@ class _ViewDefinitionCreator {
}
/// Visitor responsible for processing the `annotations` property of a
/// [RegisterType] object and pulling out [ViewDefinition] information.
class _TemplateExtractVisitor extends Object with RecursiveAstVisitor<Object> {
ViewDefinition viewDef = null;
/// [RegisterType] object, extracting the `directives` dependencies, and adding
/// their associated [CompileDirectiveMetadata] to the `directives` of a
/// created [NormalizedComponentWithViewDirectives] object.
///
/// The `component` property of the created
/// [NormalizedComponentWithViewDirectives] will be null.
///
/// If no `View` annotation is found, `compileData` will be null.
class _DirectiveDependenciesVisitor extends Object
with RecursiveAstVisitor<Object> {
NormalizedComponentWithViewDirectives compileData = null;
final Map<String, NgMeta> _metadataMap;
final ConstantEvaluator _evaluator = new ConstantEvaluator();
_TemplateExtractVisitor(this._metadataMap);
_DirectiveDependenciesVisitor(this._metadataMap);
void reset() {
viewDef = null;
compileData = null;
}
/// These correspond to the annotations themselves.
/// These correspond to the annotations themselves, which are converted into
/// const instance creation expressions so they can be stored in the
/// reflector.
@override
Object visitInstanceCreationExpression(InstanceCreationExpression node) {
if (_isViewAnnotation(node)) {
viewDef = new ViewDefinition(directives: <RenderDirectiveMetadata>[]);
compileData = new NormalizedComponentWithViewDirectives(
null, <CompileDirectiveMetadata>[]);
node.visitChildren(this);
}
return null;
@ -200,43 +199,16 @@ class _TemplateExtractVisitor extends Object with RecursiveAstVisitor<Object> {
' Source: ${node}');
return null;
}
var keyString = '${node.name.label}';
if (keyString == 'directives') {
if ('${node.name.label}' == 'directives') {
_readDirectives(node.expression);
}
if (keyString == 'template' || keyString == 'templateUrl') {
// This could happen in a non-View annotation with a `template` or
// `templateUrl` property.
if (viewDef == null) return null;
var valueString = node.expression.accept(_evaluator);
if (valueString is! String) {
logger.error(
'Angular 2 currently only supports string literals in directives.'
' Source: ${node}');
return null;
}
if (keyString == 'templateUrl') {
if (viewDef.templateAbsUrl != null) {
logger.error(
'Found multiple values for "templateUrl". Source: ${node}');
}
viewDef.templateAbsUrl = valueString;
} else {
// keyString == 'template'
if (viewDef.template != null) {
logger.error('Found multiple values for "template". Source: ${node}');
}
viewDef.template = valueString;
}
}
return null;
}
void _readDirectives(Expression node) {
// This could happen in a non-View annotation with a `directives`
// parameter.
if (viewDef == null) return;
if (compileData == null) return;
if (node is! ListLiteral) {
logger.error('Angular 2 currently only supports list literals as values '
@ -252,7 +224,7 @@ class _TemplateExtractVisitor extends Object with RecursiveAstVisitor<Object> {
name = node.name;
} else if (node is PrefixedIdentifier) {
ngMeta = _metadataMap[node.prefix.name];
name = node.name;
name = node.identifier.name;
} else {
logger.error(
'Angular 2 currently only supports simple and prefixed identifiers '
@ -260,9 +232,9 @@ class _TemplateExtractVisitor extends Object with RecursiveAstVisitor<Object> {
return;
}
if (ngMeta.types.containsKey(name)) {
viewDef.directives.add(ngMeta.types[name]);
compileData.directives.add(ngMeta.types[name]);
} else if (ngMeta.aliases.containsKey(name)) {
viewDef.directives.addAll(ngMeta.flatten(name));
compileData.directives.addAll(ngMeta.flatten(name));
} else {
logger.warning('Could not find Directive entry for $node. '
'Please be aware that Dart transformers have limited support for '

View File

@ -1,25 +0,0 @@
library angular2.transform.template_compiler.compile_step_factory;
import 'package:angular2/src/core/change_detection/parser/parser.dart' as ng;
import 'package:angular2/src/core/render/api.dart';
import 'package:angular2/src/core/render/dom/compiler/compile_step.dart';
import 'package:angular2/src/core/render/dom/compiler/compile_step_factory.dart'
as base;
import 'package:angular2/src/core/render/dom/compiler/directive_parser.dart';
import 'package:angular2/src/core/render/dom/compiler/property_binding_parser.dart';
import 'package:angular2/src/core/render/dom/compiler/text_interpolation_parser.dart';
import 'package:angular2/src/core/render/dom/compiler/view_splitter.dart';
class CompileStepFactory implements base.CompileStepFactory {
final ng.Parser _parser;
CompileStepFactory(this._parser);
List<CompileStep> createSteps(ViewDefinition template) {
return [
new ViewSplitter(_parser),
new PropertyBindingParser(_parser),
new DirectiveParser(_parser, template.directives),
new TextInterpolationParser(_parser)
];
}
}

View File

@ -2,138 +2,135 @@ library angular2.transform.template_compiler.generator;
import 'dart:async';
import 'package:angular2/src/core/change_detection/parser/lexer.dart' as ng;
import 'package:angular2/src/core/change_detection/parser/parser.dart' as ng;
import 'package:analyzer/analyzer.dart';
import 'package:angular2/src/compiler/source_module.dart';
import 'package:angular2/src/compiler/template_compiler.dart';
import 'package:angular2/src/core/change_detection/interfaces.dart';
import 'package:angular2/src/core/compiler/proto_view_factory.dart';
import 'package:angular2/src/core/dom/dom_adapter.dart';
import 'package:angular2/src/core/render/api.dart';
import 'package:angular2/src/core/render/dom/compiler/compile_pipeline.dart';
import 'package:angular2/src/core/render/dom/compiler/style_inliner.dart';
import 'package:angular2/src/core/render/dom/compiler/style_url_resolver.dart';
import 'package:angular2/src/core/render/dom/compiler/view_loader.dart';
import 'package:angular2/src/core/render/dom/schema/element_schema_registry.dart';
import 'package:angular2/src/core/render/dom/schema/dom_element_schema_registry.dart';
import 'package:angular2/src/core/render/dom/template_cloner.dart';
import 'package:angular2/src/core/render/xhr.dart' show XHR;
import 'package:angular2/src/core/reflection/reflection.dart';
import 'package:angular2/src/core/services/url_resolver.dart';
import 'package:angular2/src/transform/common/asset_reader.dart';
import 'package:angular2/src/transform/common/xhr_impl.dart';
import 'package:angular2/src/core/facade/lang.dart';
import 'package:angular2/src/core/reflection/reflection.dart';
import 'package:angular2/src/transform/common/asset_reader.dart';
import 'package:angular2/src/transform/common/code/source_module.dart';
import 'package:angular2/src/transform/common/names.dart';
import 'package:angular2/src/transform/common/ng_compiler.dart';
import 'package:angular2/src/transform/common/ng_deps.dart';
import 'package:barback/barback.dart';
import 'package:path/path.dart' as path;
import 'change_detector_codegen.dart' as change;
import 'compile_step_factory.dart';
import 'reflection/codegen.dart' as reg;
import 'reflection/processor.dart' as reg;
import 'reflection/reflection_capabilities.dart';
import 'view_definition_creator.dart';
import 'compile_data_creator.dart';
/// Reads the `.ng_deps.dart` file represented by `entryPoint` and parses any
/// Angular 2 `View` annotations it declares to generate `getter`s,
/// `setter`s, and `method`s that would otherwise be reflectively accessed.
///
/// This method assumes a {@link DomAdapter} has been registered.
Future<String> processTemplates(AssetReader reader, AssetId entryPoint,
{bool generateRegistrations: true,
bool generateChangeDetectors: true, bool reflectPropertiesAsAttributes: false}) async {
var viewDefResults = await createViewDefinitions(reader, entryPoint);
// Note: TemplateCloner(-1) never serializes Nodes into strings.
// we might want to change this to TemplateCloner(0) to force the serialization
// later when the transformer also stores the proto view template.
var extractor = new _TemplateExtractor(new DomElementSchemaRegistry(),
new TemplateCloner(-1), new XhrImpl(reader, entryPoint));
Future<Outputs> processTemplates(AssetReader reader, AssetId entryPoint,
{bool reflectPropertiesAsAttributes: false}) async {
var viewDefResults = await createCompileData(reader, entryPoint);
final processor = new reg.Processor();
var templateCompiler = createTemplateCompiler(reader,
changeDetectionConfig: new ChangeDetectorGenConfig(assertionsEnabled(),
assertionsEnabled(), reflectPropertiesAsAttributes, false));
var changeDetectorClasses = new change.Codegen();
for (var rType in viewDefResults.viewDefinitions.keys) {
var viewDefEntry = viewDefResults.viewDefinitions[rType];
var protoView = await extractor.extractTemplates(viewDefEntry.viewDef);
if (protoView == null) continue;
var ngDeps = viewDefResults.ngDeps;
var compileData =
viewDefResults.viewDefinitions.values.toList(growable: false);
if (compileData.isEmpty) {
return new Outputs(entryPoint, ngDeps, null, null, null);
}
if (generateRegistrations) {
processor.process(viewDefEntry, protoView);
}
if (generateChangeDetectors) {
var saved = reflector.reflectionCapabilities;
var genConfig = new ChangeDetectorGenConfig(assertionsEnabled(), assertionsEnabled(), reflectPropertiesAsAttributes, false);
var savedReflectionCapabilities = reflector.reflectionCapabilities;
reflector.reflectionCapabilities = const NullReflectionCapabilities();
var compiledTemplates = templateCompiler.compileTemplatesCodeGen(compileData);
reflector.reflectionCapabilities = savedReflectionCapabilities;
reflector.reflectionCapabilities = const NullReflectionCapabilities();
var defs = getChangeDetectorDefinitions(viewDefEntry.hostMetadata,
protoView, viewDefEntry.viewDef.directives, genConfig);
for (var i = 0; i < defs.length; ++i) {
changeDetectorClasses.generate('${rType.typeName}',
'_${rType.typeName}_ChangeDetector$i', defs[i]);
var processor = new reg.Processor();
compileData
.map((withDirectives) => withDirectives.component)
.forEach(processor.process);
var codegen = new reg.Codegen();
codegen.generate(processor);
return new Outputs(entryPoint, ngDeps, codegen,
viewDefResults.viewDefinitions, compiledTemplates);
}
AssetId templatesAssetId(AssetId ngDepsAssetId) =>
new AssetId(ngDepsAssetId.package, toTemplateExtension(ngDepsAssetId.path));
class Outputs {
final String ngDepsCode;
final String templatesCode;
Outputs._(this.ngDepsCode, this.templatesCode);
factory Outputs(
AssetId assetId,
NgDeps ngDeps,
reg.Codegen accessors,
Map<RegisteredType, NormalizedComponentWithViewDirectives> compileDataMap,
SourceModule templatesSource) {
var libraryName =
ngDeps != null && ngDeps.lib != null ? '${ngDeps.lib.name}' : null;
return new Outputs._(
_generateNgDepsCode(assetId, ngDeps, accessors, compileDataMap),
writeSourceModule(templatesSource, libraryName: libraryName));
}
// Updates the NgDeps code with an additional `CompiledTemplate` annotation
// for each Directive we generated one for.
//
// Also adds an import to the `.template.dart` file that we will generate.
static String _generateNgDepsCode(
AssetId id,
NgDeps ngDeps,
reg.Codegen accessors,
Map<RegisteredType,
NormalizedComponentWithViewDirectives> compileDataMap) {
var code = ngDeps.code;
if (compileDataMap == null || compileDataMap.isEmpty) return code;
if (ngDeps.registeredTypes.isEmpty) return code;
var beginRegistrationsIdx =
ngDeps.registeredTypes.first.registerMethod.offset;
var endRegistratationsIdx = ngDeps.registeredTypes.last.registerMethod.end;
var importInjectIdx = ngDeps.lib != null ? ngDeps.lib.end : 0;
// Add everything up to the point where we begin registering classes with
// the reflector, injecting an import to the generated template code.
var buf = new StringBuffer('${code.substring(0, importInjectIdx)}'
'import \'${toTemplateExtension(path.basename(id.path))}\' as _templates;'
'${code.substring(importInjectIdx, beginRegistrationsIdx)}');
for (var registeredType in ngDeps.registeredTypes) {
if (compileDataMap.containsKey(registeredType)) {
// We generated a template for this type, so add the generated
// `CompiledTemplate` value as the final annotation in the list.
var annotations = registeredType.annotations as ListLiteral;
if (annotations.length == 0) {
throw new FormatException('Unexpected format - attempting to codegen '
'a class with no Component annotation ${registeredType.typeName}');
}
buf.write(code.substring(registeredType.registerMethod.offset,
annotations.elements.last.end));
buf.write(', _templates.Host${registeredType.typeName}Template]');
buf.writeln(code.substring(
registeredType.annotations.end, registeredType.registerMethod.end));
} else {
// There is no compiled template for this type, write it out without any
// changes.
buf.writeln(code.substring(registeredType.registerMethod.offset,
registeredType.registerMethod.end));
}
reflector.reflectionCapabilities = saved;
}
}
// TODO(kegluneq): Do not hard-code `false` here once i/3436 is fixed.
final registrations = new reg.Codegen(generateChangeDetectors: false);
registrations.generate(processor);
buf.writeln(accessors.toString());
var code = viewDefResults.ngDeps.code;
if (registrations.isEmpty && changeDetectorClasses.isEmpty) return code;
var importInjectIdx =
viewDefResults.ngDeps.lib != null ? viewDefResults.ngDeps.lib.end : 0;
var codeInjectIdx =
viewDefResults.ngDeps.registeredTypes.last.registerMethod.end;
var initInjectIdx = viewDefResults.ngDeps.setupMethod.end - 1;
return '${code.substring(0, importInjectIdx)}'
'${changeDetectorClasses.imports}'
'${code.substring(importInjectIdx, codeInjectIdx)}'
'${registrations}'
'${code.substring(codeInjectIdx, initInjectIdx)}'
'${changeDetectorClasses.initialize}'
'${code.substring(initInjectIdx)}'
'$changeDetectorClasses';
}
/// Extracts `template` and `url` values from `View` annotations, reads
/// template code if necessary, and determines what values will be
/// reflectively accessed from that template.
class _TemplateExtractor {
final CompileStepFactory _factory;
ViewLoader _loader;
ElementSchemaRegistry _schemaRegistry;
TemplateCloner _templateCloner;
_TemplateExtractor(this._schemaRegistry, this._templateCloner, XHR xhr)
: _factory = new CompileStepFactory(new ng.Parser(new ng.Lexer())) {
var urlResolver = new UrlResolver();
var styleUrlResolver = new StyleUrlResolver(urlResolver);
var styleInliner = new StyleInliner(xhr, styleUrlResolver, urlResolver);
_loader = new ViewLoader(xhr, styleInliner, styleUrlResolver);
}
Future<ProtoViewDto> extractTemplates(ViewDefinition viewDef) async {
// Check for "imperative views".
if (viewDef.template == null && viewDef.templateAbsUrl == null) return null;
var templateAndStyles = await _loader.load(viewDef);
// NOTE(kegluneq): Since this is a global, we must not have any async
// operations between saving and restoring it, otherwise we can get into
// a bad state. See issue #2359 for additional context.
var savedReflectionCapabilities = reflector.reflectionCapabilities;
reflector.reflectionCapabilities = const NullReflectionCapabilities();
var pipeline = new CompilePipeline(_factory.createSteps(viewDef));
var compileElements = pipeline.processElements(
DOM.createTemplate(templateAndStyles.template),
ViewType.COMPONENT,
viewDef);
var protoViewDto = compileElements[0]
.inheritedProtoView
.build(_schemaRegistry, _templateCloner);
reflector.reflectionCapabilities = savedReflectionCapabilities;
return protoViewDto;
// Add everything after the final registration.
buf.writeln(code.substring(endRegistratationsIdx));
return buf.toString();
}
}

View File

@ -8,12 +8,11 @@ import 'model.dart';
class Codegen {
final StringBuffer _buf = new StringBuffer();
/// Whether we are pre-generating change detectors.
/// If we have pre-generated change detectors, we need
final bool generateChangeDetectors;
Codegen({this.generateChangeDetectors});
/// Generates code to register all getters, setters, and methods stored by
/// `model`.
///
/// The code takes the form of zero or more cascaded calls. The receiver of
/// these calls is expected to be an Angular 2 reflector object.
void generate(CodegenModel model) {
if (model != null) {
var calls = _generateGetters(_extractNames(model.getterNames));
@ -35,9 +34,7 @@ class Codegen {
}
Iterable<String> _extractNames(Iterable<ReflectiveAccessor> accessors) {
var names = accessors.where((accessor) {
return accessor.isStaticallyNecessary || !generateChangeDetectors;
}).map((accessor) => accessor.sanitizedName);
var names = accessors.map((accessor) => accessor.sanitizedName);
var nameList = names.toList();
nameList.sort();
return nameList;

View File

@ -27,11 +27,7 @@ class ReflectiveAccessor {
/// setter, or method on the `Component`.
final String sanitizedName;
/// Whether this getter, setter, or method is still necessary when we have
/// pre-generated change detectors.
final bool isStaticallyNecessary;
ReflectiveAccessor(String astValue, {this.isStaticallyNecessary})
ReflectiveAccessor(String astValue)
: this.astValue = astValue,
this.sanitizedName = sanitizePropertyName(astValue);

View File

@ -1,8 +1,6 @@
library angular2.transform.template_compiler.reflection.processor;
import 'package:angular2/src/core/change_detection/parser/ast.dart';
import 'package:angular2/src/core/render/api.dart';
import 'package:angular2/src/transform/template_compiler/view_definition_creator.dart';
import 'package:angular2/src/compiler/directive_metadata.dart';
import 'model.dart';
@ -16,109 +14,9 @@ class Processor implements CodegenModel {
/// The names of all requested `method`s.
final Set<ReflectiveAccessor> methodNames = new Set<ReflectiveAccessor>();
_NgAstVisitor _visitor;
Processor() {
_visitor = new _NgAstVisitor(this);
}
void process(ViewDefinitionEntry viewDefEntry, ProtoViewDto protoViewDto) {
_processViewDefinition(viewDefEntry);
_processProtoViewDto(protoViewDto);
}
/// Extracts the names of necessary getters from the events in host and
/// dependent [DirectiveMetadata].
void _processViewDefinition(ViewDefinitionEntry viewDefEntry) {
// These are necessary even with generated change detectors.
if (viewDefEntry.hostMetadata != null &&
viewDefEntry.hostMetadata.outputs != null) {
viewDefEntry.hostMetadata.outputs.forEach((eventName) {
getterNames.add(
new ReflectiveAccessor(eventName, isStaticallyNecessary: true));
});
}
}
void _processProtoViewDto(ProtoViewDto protoViewDto) {
_visitor.isStaticallyNecessary = false;
protoViewDto.textBindings.forEach((ast) => ast.visit(_visitor));
protoViewDto.elementBinders.forEach((binder) {
binder.propertyBindings.forEach((binding) {
binding.astWithSource.visit(_visitor);
setterNames.add(new ReflectiveAccessor(binding.property,
isStaticallyNecessary: false));
});
binder.directives.forEach((directiveBinding) {
directiveBinding.propertyBindings.values
.forEach((propBinding) => propBinding.visit(_visitor));
directiveBinding.propertyBindings.keys.forEach((bindingName) {
setterNames.add(new ReflectiveAccessor(bindingName,
isStaticallyNecessary: false));
});
directiveBinding.hostPropertyBindings.forEach((elementBinding) {
elementBinding.astWithSource.visit(_visitor);
setterNames.add(new ReflectiveAccessor(elementBinding.property,
isStaticallyNecessary: false));
});
});
binder.eventBindings
.forEach((eventBinding) => eventBinding.source.visit(_visitor));
binder.directives.forEach((directiveBinding) {
directiveBinding.eventBindings
.forEach((eventBinding) => eventBinding.source.visit(_visitor));
});
if (binder.nestedProtoView != null) {
_processProtoViewDto(binder.nestedProtoView);
}
void process(CompileDirectiveMetadata meta) {
meta.events.keys.forEach((eventName) {
getterNames.add(new ReflectiveAccessor(eventName));
});
}
}
class _NgAstVisitor extends RecursiveAstVisitor {
final Processor _result;
/// Whether any getters or setters recorded are necessary when running
/// statically. A getter or setter that is necessary only for change detection
/// is not necessary when running statically because all accesses are handled
/// by the dedicated change detector classes.
bool isStaticallyNecessary = false;
_NgAstVisitor(this._result);
visitMethodCall(MethodCall ast) {
_result.methodNames
.add(new ReflectiveAccessor(ast.name, isStaticallyNecessary: true));
super.visitMethodCall(ast);
}
visitPropertyRead(PropertyRead ast) {
_result.getterNames.add(new ReflectiveAccessor(ast.name,
isStaticallyNecessary: isStaticallyNecessary));
super.visitPropertyRead(ast);
}
visitPropertyWrite(PropertyWrite ast) {
_result.setterNames.add(new ReflectiveAccessor(ast.name,
isStaticallyNecessary: isStaticallyNecessary));
super.visitPropertyWrite(ast);
}
visitSafeMethodCall(SafeMethodCall ast) {
_result.methodNames
.add(new ReflectiveAccessor(ast.name, isStaticallyNecessary: true));
super.visitSafeMethodCall(ast);
}
visitSafePropertyRead(SafePropertyRead ast) {
_result.getterNames.add(new ReflectiveAccessor(ast.name,
isStaticallyNecessary: isStaticallyNecessary));
super.visitSafePropertyRead(ast);
}
}

View File

@ -29,8 +29,6 @@ class NullReflectionCapabilities implements ReflectionCapabilities {
MethodFn method(String name) => _nullMethod;
String importUri(Type type) => './';
String moduleId(Type type) => null;
}
_nullGetter(Object p) => null;

View File

@ -30,6 +30,7 @@ class TemplateCompiler extends Transformer implements DeclaringTransformer {
declareOutputs(DeclaringTransform transform) {
transform.consumePrimary();
transform.declareOutput(transform.primaryId);
transform.declareOutput(templatesAssetId(transform.primaryId));
}
@override
@ -38,13 +39,22 @@ class TemplateCompiler extends Transformer implements DeclaringTransformer {
Html5LibDomAdapter.makeCurrent();
var primaryId = transform.primaryInput.id;
var reader = new AssetReader.fromTransform(transform);
var transformedCode = formatter.format(await processTemplates(
reader, primaryId,
generateChangeDetectors: options.generateChangeDetectors,
reflectPropertiesAsAttributes:
options.reflectPropertiesAsAttributes));
var outputs = await processTemplates(reader, primaryId,
reflectPropertiesAsAttributes: options.reflectPropertiesAsAttributes);
transform.consumePrimary();
transform.addOutput(new Asset.fromString(primaryId, transformedCode));
var ngDepsCode = '';
var templatesCode = '';
if (outputs != null) {
if (outputs.ngDepsCode != null) {
ngDepsCode = formatter.format(outputs.ngDepsCode);
}
if (outputs.templatesCode != null) {
templatesCode = formatter.format(outputs.templatesCode);
}
}
transform.addOutput(new Asset.fromString(primaryId, ngDepsCode));
transform.addOutput(
new Asset.fromString(templatesAssetId(primaryId), templatesCode));
});
}
}

View File

@ -1,14 +1,15 @@
library angular2.transform;
library angular2.src.transform.transformer;
import 'package:barback/barback.dart';
import 'package:dart_style/dart_style.dart';
import 'deferred_rewriter/transformer.dart';
import 'directive_linker/transformer.dart';
import 'directive_metadata_extractor/transformer.dart';
import 'directive_metadata_linker/transformer.dart';
import 'directive_processor/transformer.dart';
import 'bind_generator/transformer.dart';
import 'reflection_remover/transformer.dart';
import 'stylesheet_compiler/transformer.dart';
import 'template_compiler/transformer.dart';
import 'common/formatter.dart' as formatter;
import 'common/options.dart';
@ -32,10 +33,14 @@ class AngularTransformerGroup extends TransformerGroup {
phases.addAll(new List.generate(
options.optimizationPhases, (_) => [new EmptyNgDepsRemover()]));
phases.addAll([
[new DirectiveLinker(), new DeferredRewriter(options)],
[new DirectiveMetadataExtractor()],
[
new DeferredRewriter(options),
new DirectiveLinker(),
new DirectiveMetadataLinker()
],
[new BindGenerator(options)],
[new TemplateCompiler(options)]
[new TemplateCompiler(options)],
[new StylesheetCompiler()],
]);
return new AngularTransformerGroup._(phases,
formatCode: options.formatCode);

View File

@ -38,22 +38,12 @@ void allTests() {
expect(output).toEqual(expected);
});
it('should generate a getter for a `events` property in an annotation.',
() async {
var inputPath = 'bind_generator/events_files/bar.ng_deps.dart';
var expected = formatter.format(
readFile('bind_generator/events_files/expected/bar.ng_deps.dart'));
var output = formatter.format(
await createNgSettersAndGetters(reader, new AssetId('a', inputPath)));
expect(output).toEqual(expected);
});
it('should generate setters for queries defined in the class annotation.',
() async {
var inputPath = 'bind_generator/queries_class_annotation_files/bar.ng_deps.dart';
var expected = formatter.format(
readFile('bind_generator/queries_class_annotation_files/expected/bar.ng_deps.dart'));
var inputPath =
'bind_generator/queries_class_annotation_files/bar.ng_deps.dart';
var expected = formatter.format(readFile(
'bind_generator/queries_class_annotation_files/expected/bar.ng_deps.dart'));
var output = formatter.format(
await createNgSettersAndGetters(reader, new AssetId('a', inputPath)));
@ -62,9 +52,10 @@ void allTests() {
it('should generate setters for queries defined via prop annotations.',
() async {
var inputPath = 'bind_generator/queries_prop_annotations_files/bar.ng_deps.dart';
var expected = formatter.format(
readFile('bind_generator/queries_prop_annotations_files/expected/bar.ng_deps.dart'));
var inputPath =
'bind_generator/queries_prop_annotations_files/bar.ng_deps.dart';
var expected = formatter.format(readFile(
'bind_generator/queries_prop_annotations_files/expected/bar.ng_deps.dart'));
var output = formatter.format(
await createNgSettersAndGetters(reader, new AssetId('a', inputPath)));

View File

@ -1,18 +0,0 @@
library bar.ng_deps.dart;
import 'bar.dart';
import 'package:angular2/src/core/metadata.dart';
var _visited = false;
void initReflector(reflector) {
if (_visited) return;
_visited = true;
reflector
..registerType(
ToolTip,
new ReflectionInfo(const [
const Directive(
selector: '[tool-tip]',
outputs: const ['onOpen', 'close: onClose'])
], const [], () => new ToolTip()));
}

View File

@ -1,19 +0,0 @@
library bar.ng_deps.dart;
import 'bar.dart';
import 'package:angular2/src/core/metadata.dart';
var _visited = false;
void initReflector(reflector) {
if (_visited) return;
_visited = true;
reflector
..registerType(
ToolTip,
new ReflectionInfo(const [
const Directive(
selector: '[tool-tip]',
outputs: const ['onOpen', 'close: onClose'])
], const [], () => new ToolTip()))
..registerGetters({'onOpen': (o) => o.onOpen, 'close': (o) => o.close});
}

View File

@ -0,0 +1,26 @@
library angular2.test.transform.common.compile_directive_metadata;
final ngFor = {
"NgFor": {
"kind": "type",
"value": {
"isComponent": false,
"dynamicLoadable": true,
"selector": "[ng-for][ng-for-of]",
"exportAs": null,
"type": {
"id": 9999,
"name": "NgFor",
"moduleUrl": "asset:angular2/lib/src/core/directives/ng_for.dart"
},
"changeDetection": null,
"properties": {"ngForOf": "ngForOf"},
"events": {},
"hostListeners": {},
"hostProperties": {},
"hostAttributes": {},
"lifecycleHooks": [2],
"template": null
}
}
};

View File

@ -1,6 +1,7 @@
library angular2.test.transform.common.annotation_matcher_test;
import 'package:angular2/src/core/render/api.dart';
import 'package:angular2/src/compiler/directive_metadata.dart';
import 'package:angular2/src/transform/common/ng_meta.dart';
import 'package:guinness/guinness.dart';
@ -8,10 +9,10 @@ main() => allTests();
void allTests() {
var mockData = [
new RenderDirectiveMetadata(id: 'm1'),
new RenderDirectiveMetadata(id: 'm2'),
new RenderDirectiveMetadata(id: 'm3'),
new RenderDirectiveMetadata(id: 'm4')
CompileDirectiveMetadata.create(type: new CompileTypeMetadata(name: 'N1')),
CompileDirectiveMetadata.create(type: new CompileTypeMetadata(name: 'N2')),
CompileDirectiveMetadata.create(type: new CompileTypeMetadata(name: 'N3')),
CompileDirectiveMetadata.create(type: new CompileTypeMetadata(name: 'N4'))
];
it('should allow empty data.', () {
@ -92,7 +93,7 @@ _checkSimilar(NgMeta a, NgMeta b) {
expect(b.types).toContain(k);
var at = a.types[k];
var bt = b.types[k];
expect(at.id).toEqual(bt.id);
expect(at.type.name).toEqual(bt.type.name);
}
for (var k in a.aliases.keys) {
expect(b.aliases).toContain(k);

View File

@ -1,15 +0,0 @@
library bar.ng_deps.dart;
import 'foo.dart';
import 'package:angular2/src/core/metadata.dart';
var _visited = false;
void initReflector(reflector) {
if (_visited) return;
_visited = true;
reflector
..registerType(
FooComponent,
new ReflectionInfo(const [const Component(selector: '[fo' 'o]')],
const [], () => new FooComponent()));
}

View File

@ -1,246 +0,0 @@
library angular2.test.transform.directive_metadata_extractor.all_tests;
import 'dart:async';
import 'package:angular2/src/core/render/api.dart';
import 'package:angular2/src/core/change_detection/change_detection.dart';
import 'package:angular2/src/transform/common/convert.dart';
import 'package:angular2/src/transform/common/directive_metadata_reader.dart';
import 'package:angular2/src/transform/common/logging.dart';
import 'package:angular2/src/transform/common/ng_deps.dart';
import 'package:angular2/src/transform/directive_metadata_extractor/'
'extractor.dart';
import 'package:barback/barback.dart';
import 'package:dart_style/dart_style.dart';
import 'package:guinness/guinness.dart';
import '../common/read_file.dart';
var formatter = new DartFormatter();
main() => allTests();
void allTests() {
TestAssetReader reader = null;
beforeEach(() {
reader = new TestAssetReader();
});
Future<DirectiveMetadata> readMetadata(inputPath) async {
var ngDeps = await NgDeps.parse(reader, new AssetId('a', inputPath));
return ngDeps.registeredTypes.first.directiveMetadata;
}
describe('readMetadata', () {
it('should parse selectors', () async {
var metadata = await readMetadata(
'directive_metadata_extractor/directive_metadata_files/'
'selector.ng_deps.dart');
expect(metadata.selector).toEqual('hello-app');
});
it('should parse compile children values', () async {
var ngDeps = await NgDeps.parse(
reader,
new AssetId(
'a',
'directive_metadata_extractor/'
'directive_metadata_files/compile_children.ng_deps.dart'));
var it = ngDeps.registeredTypes.iterator;
// Unset value defaults to `true`.
it.moveNext();
expect('${it.current.typeName}').toEqual('UnsetComp');
var unsetComp = it.current.directiveMetadata;
expect(unsetComp.compileChildren).toBeTrue();
it.moveNext();
expect('${it.current.typeName}').toEqual('FalseComp');
var falseComp = it.current.directiveMetadata;
expect(falseComp.compileChildren).toBeFalse();
it.moveNext();
expect('${it.current.typeName}').toEqual('TrueComp');
var trueComp = it.current.directiveMetadata;
expect(trueComp.compileChildren).toBeTrue();
});
it('should parse inputs.', () async {
var metadata = await readMetadata('directive_metadata_extractor/'
'directive_metadata_files/properties.ng_deps.dart');
expect(metadata.inputs).toBeNotNull();
expect(metadata.inputs.length).toBe(2);
expect(metadata.inputs).toContain('key1: val1');
expect(metadata.inputs).toContain('key2: val2');
});
it('should parse exportAs.', () async {
var metadata = await readMetadata('directive_metadata_extractor/'
'directive_metadata_files/directive_export_as.ng_deps.dart');
expect(metadata.exportAs).toEqual('exportAsName');
});
it('should parse host.', () async {
var metadata = await readMetadata('directive_metadata_extractor/'
'directive_metadata_files/host_listeners.ng_deps.dart');
expect(metadata.hostListeners).toBeNotNull();
expect(metadata.hostListeners.length).toBe(1);
expect(metadata.hostListeners).toContain('change');
expect(metadata.hostListeners['change']).toEqual('onChange(\$event)');
expect(metadata.hostProperties).toBeNotNull();
expect(metadata.hostProperties.length).toBe(1);
expect(metadata.hostProperties).toContain('value');
expect(metadata.hostProperties['value']).toEqual('value');
expect(metadata.hostAttributes).toBeNotNull();
expect(metadata.hostAttributes.length).toBe(1);
expect(metadata.hostAttributes).toContain('attName');
expect(metadata.hostAttributes['attName']).toEqual('attValue');
});
it('should parse lifecycle events.', () async {
var metadata = await readMetadata('directive_metadata_extractor/'
'directive_metadata_files/lifecycle.ng_deps.dart');
expect(metadata.callOnDestroy).toBe(true);
expect(metadata.callOnChanges).toBe(true);
expect(metadata.callDoCheck).toBe(true);
expect(metadata.callOnInit).toBe(true);
expect(metadata.callAfterContentInit).toBe(true);
expect(metadata.callAfterContentChecked).toBe(true);
expect(metadata.callAfterViewInit).toBe(true);
expect(metadata.callAfterViewChecked).toBe(true);
});
it('should parse outputs.', () async {
var metadata = await readMetadata('directive_metadata_extractor/'
'directive_metadata_files/events.ng_deps.dart');
expect(metadata.outputs).toEqual(['onFoo', 'onBar']);
});
it('should parse changeDetection.', () async {
var metadata = await readMetadata('directive_metadata_extractor/'
'directive_metadata_files/changeDetection.ng_deps.dart');
expect(metadata.changeDetection)
.toEqual(ChangeDetectionStrategy.CheckOnce);
});
it('should fail when a class is annotated with multiple Directives.',
() async {
var ngDeps = await NgDeps.parse(
reader,
new AssetId(
'a',
'directive_metadata_extractor/'
'directive_metadata_files/too_many_directives.ng_deps.dart'));
expect(() => ngDeps.registeredTypes.first.directiveMetadata)
.toThrowWith(anInstanceOf: PrintLoggerError);
});
});
describe('extractMetadata', () {
it('should generate `DirectiveMetadata` from .ng_deps.dart files.',
() async {
var extracted = await extractDirectiveMetadata(
reader,
new AssetId('a',
'directive_metadata_extractor/simple_files/foo.ng_deps.dart'));
expect(extracted.types).toContain('FooComponent');
var extractedMeta = extracted.types['FooComponent'];
expect(extractedMeta.selector).toEqual('[foo]');
});
it(
'should generate `DirectiveMetadata` from .ng_deps.dart files that use '
'automatic adjacent string concatenation.', () async {
var extracted = await extractDirectiveMetadata(
reader,
new AssetId(
'a',
'directive_metadata_extractor/adjacent_strings_files/'
'foo.ng_deps.dart'));
expect(extracted.types).toContain('FooComponent');
var extractedMeta = extracted.types['FooComponent'];
expect(extractedMeta.selector).toEqual('[foo]');
});
it('should include `DirectiveMetadata` from exported files.', () async {
var extracted = await extractDirectiveMetadata(
reader,
new AssetId('a',
'directive_metadata_extractor/export_files/foo.ng_deps.dart'));
expect(extracted.types).toContain('FooComponent');
expect(extracted.types).toContain('BarComponent');
expect(extracted.types['FooComponent'].selector).toEqual('[foo]');
expect(extracted.types['BarComponent'].selector).toEqual('[bar]');
});
it('should include `DirectiveMetadata` recursively from exported files.',
() async {
var extracted = await extractDirectiveMetadata(
reader,
new AssetId('a',
'directive_metadata_extractor/recursive_export_files/foo.ng_deps.dart'));
expect(extracted.types).toContain('FooComponent');
expect(extracted.types).toContain('BarComponent');
expect(extracted.types).toContain('BazComponent');
expect(extracted.types['FooComponent'].selector).toEqual('[foo]');
expect(extracted.types['BarComponent'].selector).toEqual('[bar]');
expect(extracted.types['BazComponent'].selector).toEqual('[baz]');
});
it('should handle `DirectiveMetadata` export cycles gracefully.', () async {
var extracted = await extractDirectiveMetadata(
reader,
new AssetId('a',
'directive_metadata_extractor/export_cycle_files/baz.ng_deps.dart'));
expect(extracted.types).toContain('FooComponent');
expect(extracted.types).toContain('BarComponent');
expect(extracted.types).toContain('BazComponent');
expect(extracted.types['FooComponent'].selector).toEqual('[foo]');
expect(extracted.types['BarComponent'].selector).toEqual('[bar]');
expect(extracted.types['BazComponent'].selector).toEqual('[baz]');
});
it(
'should include `DirectiveMetadata` from exported files '
'expressed as absolute uris', () async {
reader.addAsset(
new AssetId('bar', 'lib/bar.ng_deps.dart'),
readFile(
'directive_metadata_extractor/absolute_export_files/bar.ng_deps.dart'));
var extracted = await extractDirectiveMetadata(
reader,
new AssetId('a',
'directive_metadata_extractor/absolute_export_files/foo.ng_deps.dart'));
expect(extracted.types).toContain('FooComponent');
expect(extracted.types).toContain('BarComponent');
expect(extracted.types['FooComponent'].selector).toEqual('[foo]');
expect(extracted.types['BarComponent'].selector).toEqual('[bar]');
});
it('should include directive aliases', () async {
reader.addAsset(
new AssetId('bar', 'lib/bar.ng_deps.dart'),
readFile(
'directive_metadata_extractor/directive_aliases_files/bar.ng_deps.dart'));
var extracted = await extractDirectiveMetadata(
reader,
new AssetId('a',
'directive_metadata_extractor/directive_aliases_files/foo.ng_deps.dart'));
expect(extracted.aliases).toContain('alias1');
expect(extracted.aliases).toContain('alias2');
expect(extracted.aliases['alias1']).toContain('BarComponent');
expect(extracted.aliases['alias2']).toContain('FooComponent');
expect(extracted.aliases['alias2']).toContain('alias1');
});
});
}

View File

@ -1,8 +0,0 @@
{
"alias1": {
"kind": "alias",
"value": [
"BarComponent"
]
}
}

View File

@ -1,9 +0,0 @@
{
"alias2": {
"kind": "alias",
"value": [
"FooComponent",
"alias1"
]
}
}

View File

@ -1,19 +0,0 @@
library examples.hello_world.index_common_dart.ng_deps.dart;
import 'hello.dart';
import 'package:angular2/angular2.dart'
show Component, Directive, View, NgElement, ChangeDetectionStrategy;
var _visited = false;
void initReflector(reflector) {
if (_visited) return;
_visited = true;
reflector
..registerType(
HelloCmp,
new ReflectionInfo(const [
const Component(changeDetection: ChangeDetectionStrategy.CheckOnce)
], const [
const []
], () => new HelloCmp()));
}

View File

@ -1,23 +0,0 @@
library examples.hello_world.index_common_dart.ng_deps.dart;
import 'hello.dart';
import 'package:angular2/angular2.dart';
var _visited = false;
void initReflector(reflector) {
if (_visited) return;
_visited = true;
reflector
..registerType(
UnsetComp,
new ReflectionInfo(
const [const Directive()], const [const []], () => new UnsetComp()))
..registerType(
FalseComp,
new ReflectionInfo(const [const Directive(compileChildren: false)],
const [const []], () => new FalseComp()))
..registerType(
TrueComp,
new ReflectionInfo(const [const Directive(compileChildren: true)],
const [const []], () => new TrueComp()));
}

View File

@ -1,16 +0,0 @@
library examples.hello_world.index_common_dart.ng_deps.dart;
import 'hello.dart';
import 'package:angular2/angular2.dart'
show Component, Directive, View, NgElement;
var _visited = false;
void initReflector(reflector) {
if (_visited) return;
_visited = true;
reflector
..registerType(
HelloCmp,
new ReflectionInfo(const [const Directive(exportAs: 'exportAsName')],
const [const []], () => new HelloCmp()));
}

View File

@ -1,19 +0,0 @@
library examples.hello_world.index_common_dart.ng_deps.dart;
import 'hello.dart';
import 'package:angular2/angular2.dart'
show Component, Directive, View, NgElement;
var _visited = false;
void initReflector(reflector) {
if (_visited) return;
_visited = true;
reflector
..registerType(
HelloCmp,
new ReflectionInfo(const [
const Component(outputs: const ['onFoo', 'onBar'])
], const [
const []
], () => new HelloCmp()));
}

View File

@ -1,23 +0,0 @@
library examples.hello_world.index_common_dart.ng_deps.dart;
import 'hello.dart';
import 'package:angular2/angular2.dart'
show Component, Directive, View, NgElement;
var _visited = false;
void initReflector(reflector) {
if (_visited) return;
_visited = true;
reflector
..registerType(
HelloCmp,
new ReflectionInfo(const [
const Component(host: const {
'(change)': 'onChange(\$event)',
'[value]': 'value',
'attName': 'attValue'
})
], const [
const []
], () => new HelloCmp()));
}

View File

@ -1,40 +0,0 @@
library examples.hello_world.index_common_dart.ng_deps.dart;
import 'hello.dart';
import 'package:angular2/angular2.dart'
show
Component,
Directive,
View,
NgElement,
OnChanges,
OnDestroy,
OnInit,
DoCheck,
AfterContentInit,
AfterContentChecked,
AfterViewInit,
AfterViewChecked;
var _visited = false;
void initReflector(reflector) {
if (_visited) return;
_visited = true;
reflector
..registerType(
HelloCmp,
new ReflectionInfo(
const [const Component()],
const [const []],
() => new HelloCmp(),
const [
OnChanges,
OnDestroy,
OnInit,
DoCheck,
AfterContentInit,
AfterContentChecked,
AfterViewInit,
AfterViewChecked
]));
}

View File

@ -1,19 +0,0 @@
library examples.hello_world.index_common_dart.ng_deps.dart;
import 'hello.dart';
import 'package:angular2/angular2.dart'
show Component, Directive, View, NgElement;
var _visited = false;
void initReflector(reflector) {
if (_visited) return;
_visited = true;
reflector
..registerType(
HelloCmp,
new ReflectionInfo(const [
const Component(inputs: const ['key1: val1', 'key2: val2'])
], const [
const []
], () => new HelloCmp()));
}

View File

@ -1,16 +0,0 @@
library examples.hello_world.index_common_dart.ng_deps.dart;
import 'hello.dart';
import 'package:angular2/angular2.dart'
show Component, Directive, View, NgElement;
var _visited = false;
void initReflector(reflector) {
if (_visited) return;
_visited = true;
reflector
..registerType(
HelloCmp,
new ReflectionInfo(const [const Component(selector: 'hello-app')],
const [const []], () => new HelloCmp()));
}

View File

@ -1,20 +0,0 @@
library examples.hello_world.index_common_dart.ng_deps.dart;
import 'hello.dart';
import 'package:angular2/angular2.dart'
show Component, Directive, View, NgElement;
var _visited = false;
void initReflector(reflector) {
if (_visited) return;
_visited = true;
reflector
..registerType(
HelloCmp,
new ReflectionInfo(const [
const Component(selector: 'hello-app'),
const Component(selector: 'goodbye-app')
], const [
const []
], () => new HelloCmp()));
}

View File

@ -1,15 +0,0 @@
library foo.ng_deps.dart;
import 'bar.dart';
import 'package:angular2/src/core/metadata.dart';
var _visited = false;
void initReflector(reflector) {
if (_visited) return;
_visited = true;
reflector
..registerType(
BarComponent,
new ReflectionInfo(const [const Component(selector: '[bar]')], const [],
() => new BarComponent()));
}

View File

@ -1,19 +0,0 @@
library foo.ng_deps.dart;
import 'foo.dart';
import 'package:angular2/src/core/metadata.dart';
export 'bar.dart';
import 'bar.ng_deps.dart' as i0;
var _visited = false;
void initReflector(reflector) {
if (_visited) return;
_visited = true;
reflector
..registerType(
FooComponent,
new ReflectionInfo(const [const Component(selector: '[foo]')], const [],
() => new FooComponent()));
i0.initReflector(reflector);
}

View File

@ -1,15 +0,0 @@
library bar.ng_deps.dart;
import 'foo.dart';
import 'package:angular2/src/core/metadata.dart';
var _visited = false;
void initReflector(reflector) {
if (_visited) return;
_visited = true;
reflector
..registerType(
FooComponent,
new ReflectionInfo(const [const Component(selector: '[foo]')], const [],
() => new FooComponent()));
}

View File

@ -0,0 +1,32 @@
{
"BarComponent":
{
"kind": "type",
"value": {
"isComponent": true,
"dynamicLoadable": true,
"selector":"[bar]",
"exportAs": null,
"type": {
"id": 1,
"name": "BarComponent",
"moduleUrl": "asset:angular2/test/transform/directive_metadata_linker/absolute_export_files/bar.dart"
},
"changeDetection": 5,
"properties": {},
"events": {},
"hostListeners": {},
"hostProperties": {},
"hostAttributes": {},
"lifecycleHooks": [],
"template": {
"encapsulation": 0,
"template": "Bar",
"templateUrl": null,
"styles": null,
"styleUrls": null,
"ngContentSelectors": null
}
}
}
}

View File

@ -0,0 +1,33 @@
{
"__exports__": ["bar.dart"],
"FooComponent":
{
"kind": "type",
"value": {
"isComponent": true,
"dynamicLoadable": true,
"selector":"[foo]",
"exportAs": null,
"type": {
"id": 1,
"name": "FooComponent",
"moduleUrl": "asset:angular2/test/transform/directive_metadata_linker/absolute_export_files/foo.dart"
},
"changeDetection": 5,
"properties": {},
"events": {},
"hostListeners": {},
"hostProperties": {},
"hostAttributes": {},
"lifecycleHooks": [],
"template": {
"encapsulation": 0,
"template": "Foo",
"templateUrl": null,
"styles": null,
"styleUrls": null,
"ngContentSelectors": null
}
}
}
}

View File

@ -0,0 +1,79 @@
library angular2.test.transform.directive_metadata_linker.all_tests;
import 'dart:async';
import 'package:angular2/src/core/render/api.dart';
import 'package:angular2/src/core/change_detection/change_detection.dart';
import 'package:angular2/src/transform/common/convert.dart';
import 'package:angular2/src/transform/common/directive_metadata_reader.dart';
import 'package:angular2/src/transform/common/logging.dart';
import 'package:angular2/src/transform/common/ng_deps.dart';
import 'package:angular2/src/transform/directive_metadata_linker/'
'linker.dart';
import 'package:barback/barback.dart';
import 'package:dart_style/dart_style.dart';
import 'package:guinness/guinness.dart';
import '../common/read_file.dart';
var formatter = new DartFormatter();
main() => allTests();
void allTests() {
TestAssetReader reader = null;
beforeEach(() {
reader = new TestAssetReader();
});
it('should include `DirectiveMetadata` from exported files.', () async {
var extracted = await linkDirectiveMetadata(
reader,
new AssetId(
'a', 'directive_metadata_linker/export_files/foo.ng_meta.json'));
expect(extracted.types).toContain('FooComponent');
expect(extracted.types).toContain('BarComponent');
expect(extracted.types['FooComponent'].selector).toEqual('[foo]');
expect(extracted.types['BarComponent'].selector).toEqual('[bar]');
});
it('should include `DirectiveMetadata` recursively from exported files.',
() async {
var extracted = await linkDirectiveMetadata(
reader,
new AssetId('a',
'directive_metadata_linker/recursive_export_files/foo.ng_meta.json'));
expect(extracted.types).toContain('FooComponent');
expect(extracted.types).toContain('BarComponent');
expect(extracted.types).toContain('BazComponent');
expect(extracted.types['FooComponent'].selector).toEqual('[foo]');
expect(extracted.types['BarComponent'].selector).toEqual('[bar]');
expect(extracted.types['BazComponent'].selector).toEqual('[baz]');
});
it('should handle `DirectiveMetadata` export cycles gracefully.', () async {
var extracted = await linkDirectiveMetadata(
reader,
new AssetId('a',
'directive_metadata_linker/export_cycle_files/baz.ng_meta.json'));
expect(extracted.types).toContain('FooComponent');
expect(extracted.types).toContain('BarComponent');
expect(extracted.types).toContain('BazComponent');
});
it(
'should include `DirectiveMetadata` from exported files '
'expressed as absolute uris', () async {
var extracted = await linkDirectiveMetadata(
reader,
new AssetId('a',
'directive_metadata_linker/absolute_export_files/foo.ng_meta.json'));
expect(extracted.types).toContain('FooComponent');
expect(extracted.types).toContain('BarComponent');
expect(extracted.types['FooComponent'].selector).toEqual('[foo]');
expect(extracted.types['BarComponent'].selector).toEqual('[bar]');
});
}

View File

@ -0,0 +1,33 @@
{
"__exports__": ["baz.dart"],
"BarComponent":
{
"kind": "type",
"value": {
"isComponent": true,
"dynamicLoadable": true,
"selector":"[bar]",
"exportAs": null,
"type": {
"id": 1,
"name": "BarComponent",
"moduleUrl": "asset:angular2/test/transform/directive_metadata_linker/export_cycle_files/bar.dart"
},
"changeDetection": 5,
"properties": {},
"events": {},
"hostListeners": {},
"hostProperties": {},
"hostAttributes": {},
"lifecycleHooks": [],
"template": {
"encapsulation": 0,
"template": "Bar",
"templateUrl": null,
"styles": null,
"styleUrls": null,
"ngContentSelectors": null
}
}
}
}

View File

@ -0,0 +1,33 @@
{
"__exports__": ["foo.dart"],
"BazComponent":
{
"kind": "type",
"value": {
"isComponent": true,
"dynamicLoadable": true,
"selector":"[baz]",
"exportAs": null,
"type": {
"id": 1,
"name": "BazComponent",
"moduleUrl": "asset:angular2/test/transform/directive_metadata_linker/export_cycle_files/baz.dart"
},
"changeDetection": 5,
"properties": {},
"events": {},
"hostListeners": {},
"hostProperties": {},
"hostAttributes": {},
"lifecycleHooks": [],
"template": {
"encapsulation": 0,
"template": "Baz",
"templateUrl": null,
"styles": null,
"styleUrls": null,
"ngContentSelectors": null
}
}
}
}

View File

@ -0,0 +1,33 @@
{
"__exports__": ["bar.dart"],
"FooComponent":
{
"kind": "type",
"value": {
"isComponent": true,
"dynamicLoadable": true,
"selector":"[foo]",
"exportAs": null,
"type": {
"id": 1,
"name": "FooComponent",
"moduleUrl": "asset:angular2/test/transform/directive_metadata_linker/export_cycle_files/foo.dart"
},
"changeDetection": 5,
"properties": {},
"events": {},
"hostListeners": {},
"hostProperties": {},
"hostAttributes": {},
"lifecycleHooks": [],
"template": {
"encapsulation": 0,
"template": "Foo",
"templateUrl": null,
"styles": null,
"styleUrls": null,
"ngContentSelectors": null
}
}
}
}

View File

@ -0,0 +1,32 @@
{
"BarComponent":
{
"kind": "type",
"value": {
"isComponent": true,
"dynamicLoadable": true,
"selector":"[bar]",
"exportAs": null,
"type": {
"id": 1,
"name": "BarComponent",
"moduleUrl": "asset:angular2/test/transform/directive_metadata_linker/export_cycle_files/bar.dart"
},
"changeDetection": 5,
"properties": {},
"events": {},
"hostListeners": {},
"hostProperties": {},
"hostAttributes": {},
"lifecycleHooks": [],
"template": {
"encapsulation": 0,
"template": "Bar",
"templateUrl": null,
"styles": null,
"styleUrls": null,
"ngContentSelectors": null
}
}
}
}

View File

@ -0,0 +1,33 @@
{
"__exports__": ["bar.dart"],
"FooComponent":
{
"kind": "type",
"value": {
"isComponent": true,
"dynamicLoadable": true,
"selector":"[foo]",
"exportAs": null,
"type": {
"id": 1,
"name": "FooComponent",
"moduleUrl": "asset:angular2/test/transform/directive_metadata_linker/export_cycle_files/foo.dart"
},
"changeDetection": 5,
"properties": {},
"events": {},
"hostListeners": {},
"hostProperties": {},
"hostAttributes": {},
"lifecycleHooks": [],
"template": {
"encapsulation": 0,
"template": "Foo",
"templateUrl": null,
"styles": null,
"styleUrls": null,
"ngContentSelectors": null
}
}
}
}

View File

@ -0,0 +1,33 @@
{
"__exports__": ["baz.dart"],
"BarComponent":
{
"kind": "type",
"value": {
"isComponent": true,
"dynamicLoadable": true,
"selector":"[bar]",
"exportAs": null,
"type": {
"id": 1,
"name": "BarComponent",
"moduleUrl": "asset:angular2/test/transform/directive_metadata_linker/recursive_export_files/bar.dart"
},
"changeDetection": 5,
"properties": {},
"events": {},
"hostListeners": {},
"hostProperties": {},
"hostAttributes": {},
"lifecycleHooks": [],
"template": {
"encapsulation": 0,
"template": "Bar",
"templateUrl": null,
"styles": null,
"styleUrls": null,
"ngContentSelectors": null
}
}
}
}

View File

@ -0,0 +1,32 @@
{
"BazComponent":
{
"kind": "type",
"value": {
"isComponent": true,
"dynamicLoadable": true,
"selector":"[baz]",
"exportAs": null,
"type": {
"id": 1,
"name": "BazComponent",
"moduleUrl": "asset:angular2/test/transform/directive_metadata_linker/recursive_export_files/baz.dart"
},
"changeDetection": 5,
"properties": {},
"events": {},
"hostListeners": {},
"hostProperties": {},
"hostAttributes": {},
"lifecycleHooks": [],
"template": {
"encapsulation": 0,
"template": "Baz",
"templateUrl": null,
"styles": null,
"styleUrls": null,
"ngContentSelectors": null
}
}
}
}

View File

@ -0,0 +1,33 @@
{
"__exports__": ["bar.dart"],
"FooComponent":
{
"kind": "type",
"value": {
"isComponent": true,
"dynamicLoadable": true,
"selector":"[foo]",
"exportAs": null,
"type": {
"id": 1,
"name": "FooComponent",
"moduleUrl": "asset:angular2/test/transform/directive_metadata_linker/recursive_export_files/foo.dart"
},
"changeDetection": 5,
"properties": {},
"events": {},
"hostListeners": {},
"hostProperties": {},
"hostAttributes": {},
"lifecycleHooks": [],
"template": {
"encapsulation": 0,
"template": "Foo",
"templateUrl": null,
"styles": null,
"styleUrls": null,
"ngContentSelectors": null
}
}
}
}

View File

@ -4,6 +4,9 @@ import 'dart:async';
import 'dart:convert';
import 'package:barback/barback.dart';
import 'package:angular2/src/core/change_detection/change_detection.dart';
import 'package:angular2/src/core/compiler/interfaces.dart' show LifecycleHooks;
import 'package:angular2/src/core/dom/html_adapter.dart';
import 'package:angular2/src/transform/directive_processor/rewriter.dart';
import 'package:angular2/src/transform/common/annotation_matcher.dart';
import 'package:angular2/src/transform/common/asset_reader.dart';
@ -14,13 +17,13 @@ import 'package:angular2/src/transform/common/ng_meta.dart';
import 'package:code_transformers/messages/build_logger.dart';
import 'package:dart_style/dart_style.dart';
import 'package:guinness/guinness.dart';
import 'package:path/path.dart' as path;
import 'package:source_span/source_span.dart';
import '../common/read_file.dart';
var formatter = new DartFormatter();
main() {
Html5LibDomAdapter.makeCurrent();
allTests();
}
@ -122,111 +125,6 @@ void allTests() {
});
});
describe('inliner', () {
var absoluteReader;
beforeEach(() {
absoluteReader = new TestAssetReader();
});
it('should inline `templateUrl` values', () async {
var model = await _testCreateModel('url_expression_files/hello.dart');
expect(model.reflectables.isNotEmpty).toBeTrue();
var view =
model.reflectables.first.annotations.firstWhere((a) => a.isView);
expect(view.namedParameters
.firstWhere((p) => p.name == 'templateUrl')
.value).toContain('template.html');
expect(view.namedParameters.firstWhere((p) => p.name == 'template').value)
.toContain('{{greeting}}');
});
it(
'should inline `templateUrl` and `styleUrls` values expressed as '
'absolute urls.', () async {
absoluteReader.addAsset(
new AssetId('other_package', 'lib/template.html'),
readFile(
'directive_processor/absolute_url_expression_files/template.html'));
absoluteReader.addAsset(
new AssetId('other_package', 'lib/template.css'),
readFile(
'directive_processor/absolute_url_expression_files/template.css'));
var model = await _testCreateModel(
'absolute_url_expression_files/hello.dart',
reader: absoluteReader);
expect(model.reflectables.length).toEqual(2);
var view =
model.reflectables.first.annotations.firstWhere((a) => a.isView);
expect(view.namedParameters
.firstWhere((p) => p.name == 'templateUrl')
.value).toContain('package:other_package/template.html');
expect(view.namedParameters.firstWhere((p) => p.name == 'template').value)
.toContain('{{greeting}}');
expect(view.namedParameters.firstWhere((p) => p.name == 'styles').value)
.toContain('.greeting { .color: blue; }');
// TODO(kegluneq): Split this test out, as it is logically very different.
expect(model.reflectables[1].isFunction).toBeTrue();
expect(model.reflectables[1].name).toEqual('hello');
});
it('should inline multiple `styleUrls` values expressed as absolute urls.',
() async {
var model =
await _testCreateModel('multiple_style_urls_files/hello.dart');
expect(model.reflectables.isNotEmpty).toBeTrue();
var view =
model.reflectables.first.annotations.firstWhere((a) => a.isView);
var expectStyles = expect(
view.namedParameters.firstWhere((p) => p.name == 'styles').value);
expectStyles
..toContain('.greeting { .color: blue; }')
..toContain('.hello { .color: red; }');
});
it(
'should not inline multiple `styleUrls` values expressed as absolute '
'urls.', () async {
absoluteReader.addAsset(
new AssetId('a', 'lib/template.html'),
readFile(
'directive_processor/multiple_style_urls_files/template.html'));
absoluteReader.addAsset(
new AssetId('a', 'lib/template.css'),
readFile(
'directive_processor/multiple_style_urls_files/template.css'));
absoluteReader.addAsset(
new AssetId('a', 'lib/template_other.css'),
readFile(
'directive_processor/multiple_style_urls_files/template_other.css'));
var model = await _testCreateModel(
'multiple_style_urls_not_inlined_files/hello.dart',
inlineViews: false,
reader: absoluteReader);
expect(model.reflectables.isNotEmpty).toBeTrue();
var view =
model.reflectables.first.annotations.firstWhere((a) => a.isView);
expect(view.namedParameters.firstWhere((p) => p.name == 'styles',
orElse: () => null)).toBeNull();
expect(
view.namedParameters.firstWhere((p) => p.name == 'styleUrls').value)
..toContain('package:a/template.css')
..toContain('package:a/template_other.css');
});
it('should inline `templateUrl`s expressed as adjacent strings.', () async {
var model =
await _testCreateModel('split_url_expression_files/hello.dart');
expect(model.reflectables.isNotEmpty).toBeTrue();
var view =
model.reflectables.first.annotations.firstWhere((a) => a.isView);
expect(view.namedParameters.firstWhere((p) => p.name == 'template').value)
.toContain('{{greeting}}');
});
});
describe('interfaces', () {
it('should include implemented types', () async {
var model = await _testCreateModel('interfaces_files/soup.dart');
@ -353,17 +251,8 @@ void allTests() {
await _testCreateModel('invalid_url_files/hello.dart', logger: logger);
expect(logger.hasErrors).toBeTrue();
expect(logger.logs)
..toContain(
'ERROR: Uri /bad/absolute/url.html not supported from angular2|test/'
'transform/directive_processor/invalid_url_files/hello.dart, could not '
'build AssetId')
..toContain(
'ERROR: Could not read asset at uri package:invalid/package.css from '
'angular2|test/transform/directive_processor/invalid_url_files/'
'hello.dart')
..toContain(
'ERROR: Could not read asset at uri bad_relative_url.css from angular2|'
'test/transform/directive_processor/invalid_url_files/hello.dart');
..toContain('ERROR: ERROR: Invalid argument (url): '
'"Could not read asset at uri asset:/bad/absolute/url.html"');
});
it('should find and register static functions.', () async {
@ -375,15 +264,16 @@ void allTests() {
expect(functionReflectable.name).toEqual('getMessage');
});
it('should find direcive aliases patterns.', () async {
var logger = new RecordingLogger();
return log.setZoned(logger, () async {
var inputId = _assetIdForPath('directive_aliases_files/hello.dart');
var reader = new TestAssetReader();
describe('NgMeta', () {
var fakeReader;
beforeEach(() {
fakeReader = new TestAssetReader();
});
it('should find direcive aliases patterns.', () async {
var ngMeta = new NgMeta.empty();
await createNgDeps(reader, inputId, new AnnotationMatcher(), ngMeta,
inlineViews: true);
await _testCreateModel('directive_aliases_files/hello.dart',
ngMeta: ngMeta);
expect(ngMeta.aliases).toContain('alias1');
expect(ngMeta.aliases['alias1']).toContain('HelloCmp');
@ -391,6 +281,121 @@ void allTests() {
expect(ngMeta.aliases).toContain('alias2');
expect(ngMeta.aliases['alias2'])..toContain('HelloCmp')..toContain('Foo');
});
it('should include hooks for implemented types (single)', () async {
var ngMeta = new NgMeta.empty();
await _testCreateModel('interfaces_files/soup.dart', ngMeta: ngMeta);
expect(ngMeta.types.isNotEmpty).toBeTrue();
expect(ngMeta.types['ChangingSoupComponent']).toBeNotNull();
expect(ngMeta.types['ChangingSoupComponent'].selector).toEqual('[soup]');
expect(ngMeta.types['ChangingSoupComponent'].lifecycleHooks)
.toContain(LifecycleHooks.OnChanges);
});
it('should include hooks for implemented types (many)', () async {
var ngMeta = new NgMeta.empty();
await _testCreateModel('multiple_interface_lifecycle_files/soup.dart',
ngMeta: ngMeta);
expect(ngMeta.types.isNotEmpty).toBeTrue();
expect(ngMeta.types['MultiSoupComponent']).toBeNotNull();
expect(ngMeta.types['MultiSoupComponent'].selector).toEqual('[soup]');
expect(ngMeta.types['MultiSoupComponent'].lifecycleHooks)
..toContain(LifecycleHooks.OnChanges)
..toContain(LifecycleHooks.OnDestroy)
..toContain(LifecycleHooks.OnInit);
});
it('should create type entries for Directives', () async {
fakeReader
..addAsset(new AssetId('other_package', 'lib/template.html'), '')
..addAsset(new AssetId('other_package', 'lib/template.css'), '');
var ngMeta = new NgMeta.empty();
await _testCreateModel('absolute_url_expression_files/hello.dart',
ngMeta: ngMeta, reader: fakeReader);
expect(ngMeta.types.isNotEmpty).toBeTrue();
expect(ngMeta.types['HelloCmp']).toBeNotNull();
expect(ngMeta.types['HelloCmp'].selector).toEqual('hello-app');
});
it('should populate all provided values for Components & Directives',
() async {
var ngMeta = new NgMeta.empty();
await _testCreateModel('unusual_component_files/hello.dart',
ngMeta: ngMeta);
expect(ngMeta.types.isNotEmpty).toBeTrue();
var component = ngMeta.types['UnusualComp'];
expect(component).toBeNotNull();
expect(component.selector).toEqual('unusual-comp');
expect(component.isComponent).toBeTrue();
expect(component.exportAs).toEqual('ComponentExportAsValue');
expect(component.changeDetection)
.toEqual(ChangeDetectionStrategy.CheckAlways);
expect(component.properties).toContain('aProperty');
expect(component.properties['aProperty']).toEqual('aProperty');
expect(component.events).toContain('anEvent');
expect(component.events['anEvent']).toEqual('anEvent');
expect(component.hostAttributes).toContain('hostKey');
expect(component.hostAttributes['hostKey']).toEqual('hostValue');
var directive = ngMeta.types['UnusualDirective'];
expect(directive).toBeNotNull();
expect(directive.selector).toEqual('unusual-directive');
expect(directive.isComponent).toBeFalse();
expect(directive.exportAs).toEqual('DirectiveExportAsValue');
expect(directive.properties).toContain('aDirectiveProperty');
expect(directive.properties['aDirectiveProperty'])
.toEqual('aDirectiveProperty');
expect(directive.events).toContain('aDirectiveEvent');
expect(directive.events['aDirectiveEvent']).toEqual('aDirectiveEvent');
expect(directive.hostAttributes).toContain('directiveHostKey');
expect(directive.hostAttributes['directiveHostKey'])
.toEqual('directiveHostValue');
});
it('should include hooks for implemented types (single)', () async {
var ngMeta = new NgMeta.empty();
await _testCreateModel('interfaces_files/soup.dart', ngMeta: ngMeta);
expect(ngMeta.types.isNotEmpty).toBeTrue();
expect(ngMeta.types['ChangingSoupComponent']).toBeNotNull();
expect(ngMeta.types['ChangingSoupComponent'].selector).toEqual('[soup]');
expect(ngMeta.types['ChangingSoupComponent'].lifecycleHooks)
.toContain(LifecycleHooks.OnChanges);
});
it('should include hooks for implemented types (many)', () async {
var ngMeta = new NgMeta.empty();
await _testCreateModel('multiple_interface_lifecycle_files/soup.dart',
ngMeta: ngMeta);
expect(ngMeta.types.isNotEmpty).toBeTrue();
expect(ngMeta.types['MultiSoupComponent']).toBeNotNull();
expect(ngMeta.types['MultiSoupComponent'].selector).toEqual('[soup]');
expect(ngMeta.types['MultiSoupComponent'].lifecycleHooks)
..toContain(LifecycleHooks.OnChanges)
..toContain(LifecycleHooks.OnDestroy)
..toContain(LifecycleHooks.OnInit);
});
it('should parse templates from View annotations', () async {
fakeReader
..addAsset(new AssetId('other_package', 'lib/template.html'), '')
..addAsset(new AssetId('other_package', 'lib/template.css'), '');
var ngMeta = new NgMeta.empty();
await _testCreateModel('absolute_url_expression_files/hello.dart',
ngMeta: ngMeta, reader: fakeReader);
expect(ngMeta.types.isNotEmpty).toBeTrue();
expect(ngMeta.types['HelloCmp']).toBeNotNull();
expect(ngMeta.types['HelloCmp'].template).toBeNotNull();
expect(ngMeta.types['HelloCmp'].template.templateUrl)
.toEqual('asset:other_package/lib/template.html');
});
});
}
@ -399,7 +404,7 @@ Future<NgDepsModel> _testCreateModel(String inputPath,
AssetId assetId,
AssetReader reader,
BuildLogger logger,
bool inlineViews: true}) {
NgMeta ngMeta}) {
if (logger == null) logger = new RecordingLogger();
return log.setZoned(logger, () async {
var inputId = _assetIdForPath(inputPath);
@ -410,11 +415,12 @@ Future<NgDepsModel> _testCreateModel(String inputPath,
reader.addAsset(assetId, await reader.readAsString(inputId));
inputId = assetId;
}
if (ngMeta == null) {
ngMeta = new NgMeta.empty();
}
var annotationMatcher = new AnnotationMatcher()..addAll(customDescriptors);
var ngMeta = new NgMeta.empty();
return createNgDeps(reader, inputId, annotationMatcher, ngMeta,
inlineViews: inlineViews);
return createNgDeps(reader, inputId, annotationMatcher, ngMeta);
});
}

View File

@ -1,8 +1,10 @@
library dinner.package_soup;
import 'package:angular2/angular2.dart';
import 'package:soup/soup.dart';
@Soup()
@View(template: '')
class PackageSoup {
PackageSoup();
}

View File

@ -1,8 +1,10 @@
library dinner.relative_soup;
import 'package:angular2/angular2.dart';
import 'annotations/soup.dart';
@Soup()
@View(template: '')
class RelativeSoup {
RelativeSoup();
}

View File

@ -3,6 +3,7 @@ library dinner.soup;
import 'package:angular2/src/core/metadata.dart';
@Component(selector: '[soup]')
@View(template: '')
class ChangingSoupComponent implements PrimaryInterface {}
class TernaryInterface {}

View File

@ -4,4 +4,5 @@ import 'package:angular2/src/core/metadata.dart';
import 'package:angular2/src/core/compiler.dart';
@Component(selector: '[soup]')
@View(template: '')
class ChangingSoupComponent implements OnChanges, AnotherInterface {}

View File

@ -4,4 +4,5 @@ import 'package:angular2/src/core/metadata.dart';
import 'package:angular2/src/core/compiler.dart';
@Component(selector: '[soup]')
@View(template: '')
class MultiSoupComponent implements OnChanges, OnDestroy, OnInit {}

View File

@ -6,6 +6,7 @@ part 'part1.dart';
part 'part2.dart';
@Component(selector: '[main]')
@View(template: '')
class MainComponent {
MainComponent();
}

View File

@ -1,6 +1,7 @@
part of main;
@Component(selector: '[part1]')
@View(template: '')
class Part1Component {
Part1Component();
}

View File

@ -1,6 +1,7 @@
part of main;
@Component(selector: '[part2]')
@View(template: '')
class Part2Component {
Part2Component();
}

View File

@ -1,10 +0,0 @@
library examples.src.hello_world.multiple_style_urls_not_inlined_files;
import 'package:angular2/angular2.dart'
show Component, Directive, View, NgElement;
@Component(selector: 'hello-app')
@View(
templateUrl: 'package:a/template.html',
styleUrls: const ['package:a/template.css', 'package:a/template_other.css'])
class HelloCmp {}

View File

@ -3,6 +3,7 @@ library dinner.soup;
import 'package:angular2/src/core/metadata.dart';
@Component(selector: '[soup]')
@View(template: '')
class SoupComponent {
SoupComponent(@Tasty() String description, @Inject(Salt) salt);
}

View File

@ -5,6 +5,7 @@ import 'package:angular2/src/core/metadata.dart';
part 'part.dart';
@Component(selector: '[main]')
@View(template: '')
class MainComponent {
MainComponent();
}

View File

@ -1,6 +1,7 @@
part of main;
@Component(selector: '[part]')
@View(template: '')
class PartComponent {
PartComponent();
}

View File

@ -4,4 +4,5 @@ import 'package:angular2/src/core/compiler.dart' as prefix;
import 'package:angular2/src/core/metadata.dart';
@Component(selector: '[soup]')
@View(template: '')
class OnChangeSoupComponent implements prefix.OnChanges {}

View File

@ -3,6 +3,7 @@ library fields;
import 'package:angular2/src/core/metadata.dart';
@Component(selector: '[fields]')
@View(template: '')
class FieldComponent {
@FieldDecorator("field") String field;
}

View File

@ -3,6 +3,7 @@ library fields;
import 'package:angular2/src/core/metadata.dart';
@Component(selector: '[getters]')
@View(template: '')
class FieldComponent {
@GetDecorator("get") String get getVal => 'a';
}

View File

@ -3,6 +3,7 @@ library fields;
import 'package:angular2/src/core/metadata.dart';
@Component(selector: '[getters-and-setters]')
@View(template: '')
class FieldComponent {
String _val;
@GetDecorator("get") String get myVal => _val;

View File

@ -3,6 +3,7 @@ library fields;
import 'package:angular2/src/core/metadata.dart';
@Component(selector: '[setters]')
@View(template: '')
class FieldComponent {
@SetDecorator("set") String set setVal(val) => null;
}

View File

@ -3,6 +3,7 @@ library dinner.soup;
import 'package:angular2/src/core/metadata.dart';
@Component(selector: '[soup]')
@View(template: '')
class ChangingSoupComponent extends Super {}
class Iface {}

View File

@ -4,4 +4,5 @@ import 'package:angular2/src/core/compiler.dart';
import 'package:angular2/src/core/metadata.dart';
@Component(selector: '[soup]')
@View(template: '')
class OnChangeSoupComponent extends OnChanges {}

View File

@ -0,0 +1,8 @@
library examples.src.hello_world.template_files.property;
import 'package:angular2/angular2.dart'
show Component, Directive, View, NgElement;
@Component(selector: 'property')
@View(template: '<div [a]="b">Hi</div>')
class PropertyTestComponent {}

View File

@ -0,0 +1,22 @@
library examples.src.hello_world.unusual_component_files;
import 'package:angular2/angular2.dart'
show Component, Directive, View, NgElement;
@Component(
selector: 'unusual-comp',
exportAs: 'ComponentExportAsValue',
changeDetection: ChangeDetectionStrategy.CheckAlways,
properties: const ['aProperty'],
host: const {'hostKey': 'hostValue'},
events: const ['anEvent'])
@View(templateUrl: 'template.html')
class UnusualComp {}
@Directive(
selector: 'unusual-directive',
exportAs: 'DirectiveExportAsValue',
properties: const ['aDirectiveProperty'],
host: const {'directiveHostKey': 'directiveHostValue'},
events: const ['aDirectiveEvent'])
class UnusualDirective {}

View File

@ -0,0 +1,13 @@
library examples.src.hello_world.absolute_url_expression_files;
import 'package:angular2/angular2.dart'
show Component, Directive, View, NgElement;
@Component(selector: 'hello-app')
@View(
templateUrl: 'package:other_package/template.html',
styleUrls: const ['package:other_package/template.css'])
class HelloCmp {}
@Injectable()
hello() {}

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