refactor(dart/transform): Use a protobufs representation during transform

To simplify processing and testing in the future, use protobufs to
represent of `.ng_deps.dart` files rather than always dealing
directly in Dart code.

This update does not actually use the protobuf representation, but this
is a step towards moving all phases to parse and use protobufs rather than
Dart code.
This commit is contained in:
Tim Blasi 2015-09-14 17:16:34 -07:00
parent cb4ff7491a
commit e889ec8335
41 changed files with 1152 additions and 711 deletions

View File

@ -0,0 +1,92 @@
library angular2.transform.common.code.annotation_code;
import 'package:analyzer/analyzer.dart';
import 'package:analyzer/src/generated/ast.dart';
import 'package:angular2/src/transform/common/annotation_matcher.dart';
import 'package:angular2/src/transform/common/model/annotation_model.pb.dart';
import 'package:barback/barback.dart' show AssetId;
import 'constify.dart' show constify;
/// Visitor responsible for parsing [Annotation]s into [AnnotationModel]s.
class AnnotationVisitor extends SimpleAstVisitor<AnnotationModel> {
/// The file we are processing.
final AssetId assetId;
/// Responsible for testing whether [Annotation]s are those recognized by
/// Angular 2, for example `@Component`.
final AnnotationMatcher _annotationMatcher;
AnnotationVisitor(this.assetId, this._annotationMatcher);
@override
AnnotationModel visitAnnotation(Annotation node) {
var name = constify(node.name);
if (node.constructorName != null) {
name += '.${constify(node.constructorName)}';
}
var isComponent = _annotationMatcher.isComponent(node, assetId);
var isDirective =
isComponent || _annotationMatcher.isDirective(node, assetId);
var isInjectable =
isDirective || _annotationMatcher.isInjectable(node, assetId);
var isView = _annotationMatcher.isView(node, assetId);
var model = new AnnotationModel()
..name = name
..isComponent = isComponent
..isDirective = isDirective
..isInjectable = isInjectable
..isView = isView;
if (node.arguments != null) {
for (var arg in node.arguments.arguments) {
if (arg is NamedExpression) {
model.namedParameters.add(new NamedParameter()
..name = constify(arg.name.label)
..value = constify(arg.expression));
} else {
model.parameters.add(constify(arg));
}
}
}
return model;
}
}
/// Defines the format in which an [AnnotationModel] is expressed as Dart code
/// in a `.ng_deps.dart` file.
abstract class AnnotationWriterMixin {
StringBuffer get buffer;
void writeAnnotationModel(AnnotationModel model) {
if (model.parameters != null || model.namedParameters != null) {
buffer.write('const ${model.name}(');
var first = true;
for (var param in model.parameters) {
if (!first) {
buffer.write(', ');
}
first = false;
buffer.write(param);
}
// TODO(kegluneq): We are currently outputting these sorted to ensure we
// have repeatable output for testing purposes.
// Remove this sorting once we are not testing output code directly.
var namedParameters = model.namedParameters.toList();
namedParameters.sort((a, b) => a.name.compareTo(b.name));
for (var param in namedParameters) {
if (!first) {
buffer.write(', ');
}
first = false;
buffer.write('${param.name}: ${param.value}');
}
buffer.write(')');
} else {
// This is a const instance, not a ctor invocation and does not need a
// const instance creation expression.
buffer.write(model.name);
}
}
}

View File

@ -0,0 +1,64 @@
library angular2.transform.common.code.constify;
import 'package:analyzer/analyzer.dart';
import 'package:analyzer/src/generated/java_core.dart';
/// Serializes the provided [AstNode] to Dart source, replacing `new` in
/// [InstanceCreationExpression]s and the `@` in [Annotation]s with `const`.
String constify(AstNode node) {
var writer = new PrintStringWriter();
node.accept(new _ConstifyingVisitor(writer));
return '$writer';
}
class _ConstifyingVisitor extends ToSourceVisitor {
final PrintWriter writer;
_ConstifyingVisitor(PrintWriter writer)
: this.writer = writer,
super(writer);
@override
Object visitInstanceCreationExpression(InstanceCreationExpression node) {
if (node.keyword.lexeme == 'const') {
return super.visitInstanceCreationExpression(node);
} else if (node.keyword.lexeme == 'new') {
writer.print('const ');
if (node.constructorName != null) {
node.constructorName.accept(this);
}
if (node.argumentList != null) {
node.argumentList.accept(this);
}
}
return null;
}
@override
Object visitAnnotation(Annotation node) {
var hasArguments =
node.arguments != null && node.arguments.arguments != null;
if (hasArguments) {
writer.print('const ');
}
if (node.name != null) {
node.name.accept(this);
}
if (node.constructorName != null) {
writer.print('.');
node.constructorName.accept(this);
}
if (hasArguments) {
var args = node.arguments.arguments;
writer.print('(');
for (var i = 0, iLen = args.length; i < iLen; ++i) {
if (i != 0) {
writer.print(', ');
}
args[i].accept(this);
}
writer.print(')');
}
return null;
}
}

View File

@ -0,0 +1,98 @@
library angular2.transform.common.code.import_export_code;
import 'package:analyzer/analyzer.dart';
import 'package:angular2/src/transform/common/model/import_export_model.pb.dart';
/// Visitor responsible for parsing [ImportDirective]s into [ImportModel]s.
class ImportVisitor extends SimpleAstVisitor<ImportModel> {
@override
ImportModel visitImportDirective(ImportDirective node) {
if (node.isSynthetic) return null;
var model = new ImportModel()
..uri = stringLiteralToString(node.uri)
..isDeferred = node.deferredKeyword != null;
if (node.prefix != null) {
model.prefix = node.prefix.name;
}
_populateCombinators(node, model);
return model;
}
}
/// Visitor responsible for parsing [ExportDirective]s into [ExportModel]s.
class ExportVisitor extends SimpleAstVisitor<ExportModel> {
@override
ExportModel visitExportDirective(ExportDirective node) {
if (node.isSynthetic) return null;
var model = new ExportModel()..uri = stringLiteralToString(node.uri);
_populateCombinators(node, model);
return model;
}
}
/// Parses `combinators` in `node` and adds them to `model`, which should be
/// either an [ImportModel] or an [ExportModel].
void _populateCombinators(NamespaceDirective node, dynamic model) {
if (node.combinators != null) {
node.combinators.forEach((c) {
if (c is ShowCombinator) {
model.showCombinators.addAll(c.shownNames.map((id) => '$id'));
} else if (c is HideCombinator) {
model.hideCombinators.addAll(c.hiddenNames.map((id) => '$id'));
}
});
}
}
/// Defines the format in which an [ImportModel] is expressed as Dart code in a
/// `.ng_deps.dart` file.
abstract class ImportWriterMixin {
StringBuffer get buffer;
void writeImportModel(ImportModel model) {
buffer.write("import '${model.uri}'");
if (model.isDeferred) {
buffer.write(' deferred');
}
if (model.prefix != null && model.prefix.isNotEmpty) {
buffer.write(' as ${model.prefix}');
}
_writeCombinators(buffer, model);
buffer.writeln(';');
}
}
/// Defines the format in which an [ExportModel] is expressed as Dart code in a
/// `.ng_deps.dart` file.
abstract class ExportWriterMixin {
StringBuffer get buffer;
void writeExportModel(ExportModel model) {
buffer.write("export '${model.uri}'");
_writeCombinators(buffer, model);
buffer.writeln(';');
}
}
void _writeCombinators(StringBuffer buffer, dynamic model) {
if (model.showCombinators != null && model.showCombinators.isNotEmpty) {
buffer.write(' show ');
for (var i = 0; i < model.showCombinators.length; ++i) {
if (i != 0) {
buffer.write(', ');
}
buffer.write(model.showCombinators[i]);
}
}
if (model.hideCombinators != null && model.hideCombinators.isNotEmpty) {
buffer.write(' hide ');
for (var i = 0; i < model.hideCombinators.length; ++i) {
if (i != 0) {
buffer.write(', ');
}
buffer.write(model.hideCombinators[i]);
}
}
}

View File

@ -0,0 +1,167 @@
library angular2.transform.common.code.ng_deps_code;
import 'package:analyzer/analyzer.dart';
import 'package:angular2/src/transform/common/annotation_matcher.dart';
import 'package:angular2/src/transform/common/model/ng_deps_model.pb.dart';
import 'package:angular2/src/transform/common/model/import_export_model.pb.dart';
import 'package:angular2/src/transform/common/names.dart';
import 'package:barback/barback.dart' show AssetId;
import 'package:path/path.dart' as path;
import 'annotation_code.dart';
import 'import_export_code.dart';
import 'reflection_info_code.dart';
import 'parameter_code.dart';
/// Visitor responsible for parsing source Dart files (that is, not
/// `.ng_deps.dart` files) into [NgDepsModel] objects.
class NgDepsVisitor extends RecursiveAstVisitor<Object> {
final AssetId processedFile;
final ImportVisitor _importVisitor = new ImportVisitor();
final ExportVisitor _exportVisitor = new ExportVisitor();
final ReflectionInfoVisitor _reflectableVisitor;
bool _isPart = false;
NgDepsModel _model = null;
NgDepsVisitor(AssetId processedFile, AnnotationMatcher annotationMatcher)
: this.processedFile = processedFile,
_reflectableVisitor =
new ReflectionInfoVisitor(processedFile, annotationMatcher);
bool get isPart => _isPart;
NgDepsModel get model {
if (_model == null) {
_createModel('');
}
return _model;
}
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);
}
@override
Object visitClassDeclaration(ClassDeclaration node) {
var reflectableModel = _reflectableVisitor.visitClassDeclaration(node);
if (reflectableModel != null) {
model.reflectables.add(reflectableModel);
}
return null;
}
@override
Object visitExportDirective(ExportDirective node) {
var export = _exportVisitor.visitExportDirective(node);
if (export != null) {
model.exports.add(export);
}
return null;
}
@override
Object visitImportDirective(ImportDirective node) {
var import = _importVisitor.visitImportDirective(node);
if (import != null) {
model.imports.add(import);
}
return null;
}
@override
Object visitLibraryDirective(LibraryDirective node) {
if (node != null) {
assert(_model == null);
_createModel('${node.name}');
}
return null;
}
@override
Object visitPartDirective(PartDirective node) {
model.partUris.add(stringLiteralToString(node.uri));
return null;
}
@override
Object visitPartOfDirective(PartOfDirective node) {
_isPart = true;
return null;
}
@override
Object visitFunctionDeclaration(FunctionDeclaration node) {
var reflectableModel = _reflectableVisitor.visitFunctionDeclaration(node);
if (reflectableModel != null) {
model.reflectables.add(reflectableModel);
}
return null;
}
}
/// Defines the format in which an [NgDepsModel] is expressed as Dart code
/// in a `.ng_deps.dart` file.
class NgDepsWriter extends Object
with
AnnotationWriterMixin,
ExportWriterMixin,
ImportWriterMixin,
NgDepsWriterMixin,
ParameterWriterMixin,
ReflectionWriterMixin {
final StringBuffer buffer;
NgDepsWriter([StringBuffer buffer])
: this.buffer = buffer != null ? buffer : new StringBuffer();
}
abstract class NgDepsWriterMixin
implements
AnnotationWriterMixin,
ExportWriterMixin,
ImportWriterMixin,
ParameterWriterMixin,
ReflectionWriterMixin {
StringBuffer get buffer;
void writeNgDepsModel(NgDepsModel model) {
if (model.libraryUri.isNotEmpty) {
buffer.writeln('library ${model.libraryUri}${DEPS_EXTENSION};\n');
}
// We do not support `partUris`, so skip outputting them.
model.imports.forEach((importModel) {
// Ignore deferred imports here so as to not load the deferred libraries
// code in the current library causing much of the code to not be
// deferred. Instead `DeferredRewriter` will rewrite the code as to load
// `ng_deps` in a deferred way.
if (importModel.isDeferred) return;
writeImportModel(importModel);
});
model.exports.forEach(writeExportModel);
buffer
..writeln('var _visited = false;')
..writeln('void ${SETUP_METHOD_NAME}() {')
..writeln('if (_visited) return; _visited = true;');
if (model.reflectables != null && model.reflectables.isNotEmpty) {
buffer.writeln('$REFLECTOR_PREFIX.$REFLECTOR_VAR_NAME');
model.reflectables.forEach(writeRegistration);
buffer.writeln(';');
}
buffer.writeln('}');
}
}

View File

@ -0,0 +1,138 @@
library angular2.transform.common.code.parameter_code;
import 'package:analyzer/analyzer.dart';
import 'package:analyzer/src/generated/ast.dart';
import 'package:angular2/src/transform/common/logging.dart';
import 'package:angular2/src/transform/common/model/parameter_model.pb.dart';
import 'constify.dart';
/// Visitor responsible for parsing [FormalParameter]s into
/// [ParameterModel]s.
class ParameterVisitor extends SimpleAstVisitor<ParameterModel> {
/// Maps field names to their declared types. See `_populateFieldMap`
final Map<String, TypeName> _fieldNameToType = {};
final Set<AstNode> _seen = new Set();
void _populateFieldMap(AstNode node) {
ClassDeclaration clazz =
node.getAncestor((node) => node is ClassDeclaration);
if (_seen.contains(clazz)) return;
_seen.add(clazz);
clazz.members
.where((member) => member is FieldDeclaration)
.forEach((FieldDeclaration field) {
var type = field.fields.type;
if (type != null) {
field.fields.variables.forEach((VariableDeclaration decl) {
var key = '${decl.name}';
if (_fieldNameToType.containsKey(key)) {
// Need to clear our `seen` list as the type for a var name has
// changed and could be incorrect.
_seen.clear();
}
_fieldNameToType[key] = type;
});
}
});
}
ParameterModel _visitNormalFormalParameter(
NodeList<Annotation> metadata, TypeName type, SimpleIdentifier name) {
var model = new ParameterModel();
if (name != null && name.name != null && name.name.isNotEmpty) {
model.paramName = '$name';
}
if (type != null) {
var sTypeName = '${type.name}';
if (sTypeName.isNotEmpty) {
model.typeName = sTypeName;
}
if (type.typeArguments != null) {
model.typeArgs = '${type.typeArguments}';
}
}
if (metadata != null) {
model.metadata.addAll(metadata.map(constify));
}
return model;
}
@override
ParameterModel visitSimpleFormalParameter(SimpleFormalParameter node) {
return _visitNormalFormalParameter(
node.metadata, node.type, node.identifier);
}
@override
ParameterModel visitFieldFormalParameter(FieldFormalParameter node) {
if (node.parameters != null) {
logger.error('Parameters in ctor not supported '
'(${node.toSource()})');
}
var type = node.type;
if (type == null) {
_populateFieldMap(node);
type = _fieldNameToType[node.identifier.toString()];
}
return _visitNormalFormalParameter(node.metadata, type, node.identifier);
}
@override
ParameterModel visitFunctionTypedFormalParameter(
FunctionTypedFormalParameter node) {
logger.error('Function typed formal parameters not supported '
'(${node.toSource()})');
return _visitNormalFormalParameter(node.metadata, null, node.identifier);
}
@override
ParameterModel visitDefaultFormalParameter(DefaultFormalParameter node) {
// Ignore the declared default value.
return node.parameter != null ? node.parameter.accept(this) : null;
}
}
/// Defines the format in which a [ParameterModel] is expressed as Dart code
/// in a `.ng_deps.dart` file.
abstract class ParameterWriterMixin {
StringBuffer get buffer;
void writeParameterModelForList(ParameterModel model) {
buffer.write('const [');
var first = true;
if (model.typeName != null && model.typeName.isNotEmpty) {
if (!first) {
buffer.write(', ');
}
first = false;
buffer.write('${model.typeName}');
}
for (var meta in model.metadata) {
if (!first) {
buffer.write(', ');
}
first = false;
buffer.write('$meta');
}
buffer.write(']');
}
void writeParameterModelForDeclaration(ParameterModel model) {
if (model.typeName != null && model.typeName.isNotEmpty) {
buffer.write(model.typeName);
if (model.typeArgs != null && model.typeArgs.isNotEmpty) {
buffer.write(model.typeArgs);
}
buffer.write(' ');
}
if (model.paramName != null && model.paramName.isNotEmpty) {
buffer.write(model.paramName);
}
}
void writeParameterModelForImpl(ParameterModel model) {
buffer.write(model.paramName);
}
}

View File

@ -0,0 +1,181 @@
library angular2.transform.common.code.reflection_info_code;
import 'package:analyzer/analyzer.dart';
import 'package:angular2/src/transform/common/annotation_matcher.dart';
import 'package:angular2/src/transform/common/logging.dart';
import 'package:angular2/src/transform/common/model/reflection_info_model.pb.dart';
import 'package:angular2/src/transform/common/names.dart';
import 'package:barback/barback.dart' show AssetId;
import 'annotation_code.dart';
import 'parameter_code.dart';
/// Visitor responsible for parsing [ClassDeclaration]s into
/// [ReflectionInfoModel]s.
class ReflectionInfoVisitor extends RecursiveAstVisitor<ReflectionInfoModel> {
/// The file we are processing.
final AssetId assetId;
final AnnotationVisitor _annotationVisitor;
final ParameterVisitor _parameterVisitor = new ParameterVisitor();
/// Whether an Angular 2 `Reflection` has been found.
bool _foundNgReflection = false;
/// Responsible for testing whether [Annotation]s are those recognized by
/// Angular 2, for example `@Component`.
final AnnotationMatcher _annotationMatcher;
ReflectionInfoVisitor(AssetId assetId, AnnotationMatcher annotationMatcher)
: this.assetId = assetId,
_annotationMatcher = annotationMatcher,
_annotationVisitor = new AnnotationVisitor(assetId, annotationMatcher);
bool get shouldCreateNgDeps => _foundNgReflection;
ConstructorDeclaration _getCtor(ClassDeclaration node) {
int numCtorsFound = 0;
var ctor = null;
for (ClassMember classMember in node.members) {
if (classMember is ConstructorDeclaration) {
numCtorsFound++;
ConstructorDeclaration constructor = classMember;
// Use the unnnamed constructor if it is present.
// Otherwise, use the first encountered.
if (ctor == null) {
ctor = constructor;
} else if (constructor.name == null) {
ctor = constructor;
}
}
}
if (numCtorsFound > 1) {
var ctorName = ctor.name;
if (ctorName != null) {
logger.warning('Found ${numCtorsFound} ctors for class ${node.name},'
'Using constructor ${ctorName}.');
}
}
return ctor;
}
@override
ReflectionInfoModel visitClassDeclaration(ClassDeclaration node) {
if (!node.metadata
.any((a) => _annotationMatcher.hasMatch(a.name, assetId))) {
return null;
}
var ctor = _getCtor(node);
var model = new ReflectionInfoModel()..name = '${node.name}';
if (ctor != null && ctor.name != null) {
model.ctorName = '${ctor.name}';
}
if (node.metadata != null) {
node.metadata.forEach((node) {
model.annotations.add(_annotationVisitor.visitAnnotation(node));
});
}
if (ctor != null &&
ctor.parameters != null &&
ctor.parameters.parameters != null) {
ctor.parameters.parameters.forEach((node) {
model.parameters.add(node.accept(_parameterVisitor));
});
}
if (node.implementsClause != null &&
node.implementsClause.interfaces != null &&
node.implementsClause.interfaces.isNotEmpty) {
model.interfaces.addAll(node.implementsClause.interfaces
.map((interface) => '${interface.name}'));
}
return model;
}
@override
ReflectionInfoModel visitFunctionDeclaration(FunctionDeclaration node) {
if (!node.metadata
.any((a) => _annotationMatcher.hasMatch(a.name, assetId))) {
return null;
}
var model = new ReflectionInfoModel()
..name = '${node.name}'
..isFunction = true;
if (node.metadata != null) {
node.metadata.forEach((node) {
var annotation = _annotationVisitor.visitAnnotation(node);
if (annotation != null) {
model.annotations.add(annotation);
}
});
}
if (node.functionExpression.parameters != null &&
node.functionExpression.parameters.parameters != null) {
node.functionExpression.parameters.parameters.forEach((node) {
var param = node.accept(_parameterVisitor);
if (param != null) {
model.parameters.add(param);
}
});
}
return model;
}
}
/// Defines the format in which an [ReflectionInfoModel] is expressed as Dart
/// code in a `.ng_deps.dart` file.
abstract class ReflectionWriterMixin
implements AnnotationWriterMixin, ParameterWriterMixin {
StringBuffer get buffer;
void _writeListWithSeparator(List l, Function writeFn,
{String prefix, String suffix, String separator: ', '}) {
buffer.write(prefix);
for (var i = 0, iLen = l.length; i < iLen; ++i) {
if (i != 0) {
buffer.write(', ');
}
writeFn(l[i]);
}
buffer.write(suffix);
}
void writeRegistration(ReflectionInfoModel model) {
buffer.write('..register');
if (model.isFunction) {
buffer.write('Function');
} else {
buffer.write('Type');
}
buffer.writeln('(${model.name}, new $REFLECTOR_PREFIX.ReflectionInfo(');
// Annotations
_writeListWithSeparator(model.annotations, writeAnnotationModel,
prefix: 'const [', suffix: ']');
// Parameters
_writeListWithSeparator(model.parameters, writeParameterModelForList,
prefix: ',\nconst [', suffix: ']');
if (!model.isFunction) {
// Factory
_writeListWithSeparator(
model.parameters, writeParameterModelForDeclaration,
prefix: ',\n(', suffix: ')');
buffer.write(' => new ${model.name}');
if (model.ctorName != null && model.ctorName.isNotEmpty) {
buffer.write('.${model.ctorName}');
}
_writeListWithSeparator(model.parameters, writeParameterModelForImpl,
prefix: '(', suffix: ')');
// Interfaces
if (model.interfaces != null && model.interfaces.isNotEmpty) {
_writeListWithSeparator(model.interfaces, buffer.write,
prefix: ',\nconst [', suffix: ']');
}
}
buffer.writeln(')\n)');
}
}

View File

@ -10,6 +10,8 @@ const META_EXTENSION = '.ng_meta.json';
// upfront (rather than extracting it from ng_deps).
const ALIAS_EXTENSION = '.aliases.json';
const REFLECTION_CAPABILITIES_NAME = 'ReflectionCapabilities';
const REFLECTOR_IMPORT = 'package:angular2/src/core/reflection/reflection.dart';
const REFLECTOR_PREFIX = '_ngRef';
const REGISTER_TYPE_METHOD_NAME = 'registerType';
const REGISTER_GETTERS_METHOD_NAME = 'registerGetters';
const REGISTER_SETTERS_METHOD_NAME = 'registerSetters';

View File

@ -0,0 +1,143 @@
library angular2.transform.directive_processor.inliner;
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';
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)');
}
}
}
}
}
}
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;
}
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

@ -3,13 +3,11 @@ library angular2.transform.directive_processor.rewriter;
import 'dart:async';
import 'package:analyzer/analyzer.dart';
import 'package:analyzer/src/generated/java_core.dart';
import 'package:angular2/src/core/render/xhr.dart' show XHR;
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/logging.dart';
import 'package:angular2/src/transform/common/names.dart';
import 'package:angular2/src/transform/common/xhr_impl.dart';
import 'package:angular2/src/transform/common/ng_meta.dart';
import 'package:barback/barback.dart' show AssetId;
@ -17,7 +15,7 @@ import 'package:code_transformers/assets.dart';
import 'package:path/path.dart' as path;
import 'package:source_span/source_span.dart';
import 'visitors.dart';
import 'inliner.dart';
/// Generates a file registering all Angular 2 `Directive`s found in `code` in
/// ngDeps format [TODO(kegluneq): documentation reference needed]. `assetId` is
@ -42,31 +40,34 @@ Future<String> createNgDeps(AssetReader reader, AssetId assetId,
// parent, so it does not need its own `.ng_deps.dart` file.
if (directivesVisitor.isPart) return null;
var writer = new AsyncStringWriter();
directivesVisitor.writeTo(writer, assetId);
writer
..println('var _visited = false;')
..println('void ${SETUP_METHOD_NAME}() {')
..println('if (_visited) return; _visited = true;');
var declarationsCode =
var codeWithParts =
await _getAllDeclarations(reader, assetId, code, directivesVisitor);
var declarationsVisitor = new _NgDepsDeclarationsVisitor(
assetId, writer, new XhrImpl(reader, assetId), annotationMatcher, ngMeta,
inlineViews: inlineViews);
parseCompilationUnit(declarationsCode, name: '${assetId.path} and parts')
.declarations
.accept(declarationsVisitor);
if (declarationsVisitor.shouldCreateNgDeps) {
writer.println(';');
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;
// 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;
}
writer.println('}');
if (!directivesVisitor.shouldCreateNgDeps &&
!declarationsVisitor.shouldCreateNgDeps) return null;
var ngMetaVisitor = new _NgMetaVisitor(ngMeta);
parsedCode.accept(ngMetaVisitor);
return writer.asyncToString();
if (inlineViews) {
await inlineViewProps(new XhrImpl(reader, assetId), ngDepsModel);
}
var buffer = new StringBuffer();
var ngDepsWriter = new NgDepsWriter(buffer);
ngDepsWriter.writeNgDepsModel(ngDepsModel);
return '$buffer';
}
/// Processes `visitor.parts`, reading and appending their contents to the
@ -78,8 +79,8 @@ Future<String> createNgDeps(AssetReader reader, AssetId assetId,
/// part 'lib1.dart'
/// part 'lib2.dart'
/// ```
/// The output will first have the entirety of the original file, followed by
/// the contents of lib1.dart followed by the contents of 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);
@ -127,241 +128,50 @@ class _NgDepsDirectivesVisitor extends Object with SimpleAstVisitor<Object> {
/// visited a `part of` directive.
bool _isPart = false;
// TODO(kegluneq): Support an intermediate representation of NgDeps and use it
// instead of storing generated code.
LibraryDirective _library = null;
ScriptTag _scriptTag = null;
final List<NamespaceDirective> _importAndExports = <NamespaceDirective>[];
final List<PartDirective> _parts = <PartDirective>[];
bool get shouldCreateNgDeps {
// If this library does not define an `@Injectable` and it does not import
// any libaries that could, then we do not need to generate a `.ng_deps
// .dart` file for it.
if (!_usesNonLangLibs) return false;
if (_isPart) return false;
return true;
}
bool get usesNonLangLibs => _usesNonLangLibs;
bool get isPart => _isPart;
/// In the order encountered in the source.
Iterable<PartDirective> get parts => _parts;
@override
Object visitScriptTag(ScriptTag node) {
_scriptTag = node;
return null;
}
@override
Object visitCompilationUnit(CompilationUnit node) {
node.directives.accept(this);
return null;
}
void _updateUsesNonLangLibs(UriBasedDirective directive) {
Object _updateUsesNonLangLibs(UriBasedDirective directive) {
_usesNonLangLibs = _usesNonLangLibs ||
!stringLiteralToString(directive.uri).startsWith('dart:');
}
@override
Object visitImportDirective(ImportDirective node) {
_updateUsesNonLangLibs(node);
_importAndExports.add(node);
return null;
}
@override
Object visitExportDirective(ExportDirective node) {
_updateUsesNonLangLibs(node);
_importAndExports.add(node);
return null;
}
Object visitExportDirective(ExportDirective node) =>
_updateUsesNonLangLibs(node);
@override
Object visitLibraryDirective(LibraryDirective node) {
if (node != null) {
_library = node;
}
return null;
}
Object visitImportDirective(ImportDirective node) =>
_updateUsesNonLangLibs(node);
@override
Object visitPartDirective(PartDirective node) {
_parts.add(node);
return null;
}
@override
Object visitPartOfDirective(PartOfDirective node) {
_isPart = true;
return null;
}
/// Write the directives for the .ng_deps.dart for `processedFile` to
/// `writer`. The .ng_deps.dart file has the same directives as
/// `processedFile` with some exceptions (mentioned below).
void writeTo(PrintWriter writer, AssetId processedFile) {
var copyVisitor = new ToSourceVisitor(writer);
if (_scriptTag != null) {
_scriptTag.accept(copyVisitor);
writer.newLine();
}
if (_library != null && _library.name != null) {
writer.print('library ');
_library.name.accept(copyVisitor);
writer.println('$DEPS_EXTENSION;');
}
// We do not output [PartDirective]s, which would not be valid now that we
// have changed the library.
// We need to import & export the original file.
var origDartFile = path.basename(processedFile.path);
writer.println('''import '$origDartFile';''');
writer.println('''export '$origDartFile';''');
// Used to register reflective information.
writer.println("import '$_REFLECTOR_IMPORT' as $_REF_PREFIX;");
_importAndExports.forEach((node) {
if (node.isSynthetic) return;
// Ignore deferred imports here so as to not load the deferred libraries
// code in the current library causing much of the code to not be
// deferred. Instead `DeferredRewriter` will rewrite the code as to load
// `ng_deps` in a deferred way.
if (node is ImportDirective && node.deferredKeyword != null) return;
node.accept(copyVisitor);
});
}
}
/// Visitor responsible for visiting a file's [Declaration]s and outputting the
/// code necessary to register the file with the Angular 2 system.
class _NgDepsDeclarationsVisitor extends Object with SimpleAstVisitor<Object> {
final AsyncStringWriter writer;
/// The file we are processing.
final AssetId assetId;
class _NgMetaVisitor extends Object with SimpleAstVisitor<Object> {
/// Output ngMeta information about aliases.
// TODO(sigmund): add more to ngMeta. Currently this only contains aliasing
// information, but we could produce here all the metadata we need and avoid
// parsing the ngdeps files later.
final NgMeta ngMeta;
/// Whether an Angular 2 `Injectable` has been found.
bool _foundNgInjectable = false;
/// Visitor that writes out code for AstNodes visited.
final ToSourceVisitor _copyVisitor;
final FactoryTransformVisitor _factoryVisitor;
final ParameterTransformVisitor _paramsVisitor;
final AnnotationsTransformVisitor _metaVisitor;
/// Responsible for testing whether [Annotation]s are those recognized by
/// Angular 2, for example `@Component`.
final AnnotationMatcher _annotationMatcher;
/// Used to fetch linked files.
final XHR _xhr;
_NgDepsDeclarationsVisitor(AssetId assetId, AsyncStringWriter writer, XHR xhr,
AnnotationMatcher annotationMatcher, this.ngMeta,
{bool inlineViews})
: writer = writer,
_copyVisitor = new ToSourceVisitor(writer),
_factoryVisitor = new FactoryTransformVisitor(writer),
_paramsVisitor = new ParameterTransformVisitor(writer),
_metaVisitor = new AnnotationsTransformVisitor(
writer, xhr, annotationMatcher, assetId,
inlineViews: inlineViews),
_annotationMatcher = annotationMatcher,
this.assetId = assetId,
_xhr = xhr;
bool get shouldCreateNgDeps => _foundNgInjectable;
ConstructorDeclaration _getCtor(ClassDeclaration node) {
int numCtorsFound = 0;
var ctor = null;
for (ClassMember classMember in node.members) {
if (classMember is ConstructorDeclaration) {
numCtorsFound++;
ConstructorDeclaration constructor = classMember;
// Use the unnnamed constructor if it is present.
// Otherwise, use the first encountered.
if (ctor == null) {
ctor = constructor;
} else if (constructor.name == null) {
ctor = constructor;
}
}
}
if (numCtorsFound > 1) {
var ctorName = ctor.name;
ctorName = ctorName == null
? 'the unnamed constructor'
: 'constructor "${ctorName}"';
logger.warning('Found ${numCtorsFound} ctors for class ${node.name},'
'Using ${ctorName}.');
}
return ctor;
}
void _generateEmptyFactory(String typeName) {
writer.print('() => new ${typeName}()');
}
void _generateEmptyParams() => writer.print('const []');
_NgMetaVisitor(this.ngMeta);
@override
Object visitClassDeclaration(ClassDeclaration node) {
if (!node.metadata
.any((a) => _annotationMatcher.hasMatch(a.name, assetId))) {
return null;
}
var ctor = _getCtor(node);
_maybeWriteReflector();
writer.print('..registerType(');
node.name.accept(this);
writer.print(', new ${_REF_PREFIX}.ReflectionInfo(');
node.accept(_metaVisitor);
writer.print(', ');
if (ctor == null) {
_generateEmptyParams();
} else {
ctor.accept(_paramsVisitor);
}
writer.print(', ');
if (ctor == null) {
_generateEmptyFactory(node.name.toString());
} else {
ctor.accept(_factoryVisitor);
}
if (node.implementsClause != null &&
node.implementsClause.interfaces != null &&
node.implementsClause.interfaces.isNotEmpty) {
writer
..print(', const [')
..print(node.implementsClause.interfaces
.map((interface) => interface.name)
.join(', '))
..print(']');
}
writer.print('))');
return null;
Object visitCompilationUnit(CompilationUnit node) {
if (node == null || node.declarations == null) return null;
return node.declarations.accept(this);
}
@override
@ -386,47 +196,4 @@ class _NgDepsDeclarationsVisitor extends Object with SimpleAstVisitor<Object> {
}
return null;
}
Object _nodeToSource(AstNode node) {
if (node == null) return null;
return node.accept(_copyVisitor);
}
@override
Object visitPrefixedIdentifier(PrefixedIdentifier node) =>
_nodeToSource(node);
@override
Object visitSimpleIdentifier(SimpleIdentifier node) => _nodeToSource(node);
@override
bool visitFunctionDeclaration(FunctionDeclaration node) {
if (!node.metadata
.any((a) => _annotationMatcher.hasMatch(a.name, assetId))) {
return null;
}
_maybeWriteReflector();
writer.print('..registerFunction(');
node.name.accept(this);
writer.print(', new ${_REF_PREFIX}.ReflectionInfo(const [');
node.metadata.accept(_metaVisitor);
writer.print('], const [');
node.functionExpression.parameters.accept(_paramsVisitor);
writer.print(']))');
return null;
}
/// Writes out the reflector variable the first time it is called.
void _maybeWriteReflector() {
if (_foundNgInjectable) return;
_foundNgInjectable = true;
// The receiver for cascaded calls.
writer.print('$_REF_PREFIX.$REFLECTOR_VAR_NAME');
}
}
const _REF_PREFIX = '_ngRef';
const _REFLECTOR_IMPORT =
'package:angular2/src/core/reflection/reflection.dart';

View File

@ -1,341 +0,0 @@
library angular2.transform.directive_processor.visitors;
import 'dart:async';
import 'package:analyzer/analyzer.dart';
import 'package:analyzer/src/generated/java_core.dart';
import 'package:angular2/src/core/render/xhr.dart' show XHR;
import 'package:angular2/src/transform/common/annotation_matcher.dart';
import 'package:angular2/src/transform/common/async_string_writer.dart';
import 'package:angular2/src/transform/common/logging.dart';
import 'package:barback/barback.dart';
/// `ToSourceVisitor` designed to accept {@link ConstructorDeclaration} nodes.
class _CtorTransformVisitor extends ToSourceVisitor {
bool _withParameterAnnotations = true;
bool _withParameterTypes = true;
bool _withParameterNames = true;
final PrintWriter writer;
/// Maps field names to their declared types. This is populated whenever
/// the listener visits a {@link ConstructorDeclaration} node.
final Map<String, TypeName> _fieldNameToType = {};
_CtorTransformVisitor(PrintWriter writer)
: this.writer = writer,
super(writer);
void _visitNodeWithPrefix(String prefix, AstNode node) {
if (node != null) {
writer.print(prefix);
node.accept(this);
}
}
void _visitNodeWithSuffix(AstNode node, String suffix) {
if (node != null) {
node.accept(this);
writer.print(suffix);
}
}
void _visitNode(AstNode node) {
if (node != null) {
node.accept(this);
}
}
/// If `_withParameterTypes` is true, this method outputs `node`'s type. If
/// `_withParameterNames` is true, this method outputs `node`'s identifier.
Object _visitNormalFormalParameter(
NodeList<Annotation> metadata, TypeName type, SimpleIdentifier name) {
var needCompileTimeConstants = !_withParameterNames;
var needType = _withParameterTypes && type != null;
if (needType) {
_visitNodeWithSuffix(type.name, ' ');
if (!needCompileTimeConstants) {
// Types with arguments are not compile-time constants.
_visitNodeWithSuffix(type.typeArguments, ' ');
}
}
if (_withParameterNames) {
_visitNode(name);
}
if (_withParameterAnnotations && metadata != null) {
assert(_withParameterTypes);
for (var i = 0, iLen = metadata.length; i < iLen; ++i) {
if (i != 0 || needType) {
writer.print(', ');
}
metadata[i].accept(this);
}
}
return null;
}
void _buildFieldMap(ConstructorDeclaration node) {
ClassDeclaration clazz =
node.getAncestor((node) => node is ClassDeclaration);
_fieldNameToType.clear();
clazz.members
.where((member) => member is FieldDeclaration)
.forEach((FieldDeclaration field) {
var type = field.fields.type;
if (type != null) {
field.fields.variables.forEach((VariableDeclaration decl) {
_fieldNameToType[decl.name.toString()] = type;
});
}
});
}
@override
Object visitSimpleFormalParameter(SimpleFormalParameter node) {
return _visitNormalFormalParameter(
node.metadata, node.type, node.identifier);
}
@override
Object visitFieldFormalParameter(FieldFormalParameter node) {
if (node.parameters != null) {
logger.error('Parameters in ctor not supported '
'(${node.toSource()})');
}
var type = node.type;
if (type == null) {
type = _fieldNameToType[node.identifier.toString()];
}
return _visitNormalFormalParameter(node.metadata, type, node.identifier);
}
@override
Object visitFunctionTypedFormalParameter(FunctionTypedFormalParameter node) {
logger.error('Function typed formal parameters not supported '
'(${node.toSource()})');
return _visitNormalFormalParameter(node.metadata, null, node.identifier);
}
@override
Object visitDefaultFormalParameter(DefaultFormalParameter node) {
_visitNode(node.parameter);
// Ignore the declared default value.
return null;
}
@override
/// Overridden to avoid outputting grouping operators for default parameters.
Object visitFormalParameterList(FormalParameterList node) {
writer.print('(');
NodeList<FormalParameter> parameters = node.parameters;
int size = parameters.length;
for (int i = 0; i < size; i++) {
if (i > 0) {
writer.print(', ');
}
parameters[i].accept(this);
}
writer.print(')');
return null;
}
@override
Object visitAnnotation(Annotation node) {
var prefix =
node.arguments != null && node.arguments.length > 0 ? 'const ' : '';
_visitNodeWithPrefix(prefix, node.name);
_visitNodeWithPrefix(".", node.constructorName);
_visitNode(node.arguments);
return null;
}
}
/// ToSourceVisitor designed to print 'parameters' values for Angular2's
/// `registerType` calls.
class ParameterTransformVisitor extends _CtorTransformVisitor {
ParameterTransformVisitor(PrintWriter writer) : super(writer) {
_withParameterNames = false;
_withParameterTypes = true;
_withParameterAnnotations = true;
}
@override
Object visitConstructorDeclaration(ConstructorDeclaration node) {
_buildFieldMap(node);
writer.print('const [');
_visitNode(node.parameters);
writer.print(']');
return null;
}
@override
Object visitFormalParameterList(FormalParameterList node) {
NodeList<FormalParameter> parameters = node.parameters;
for (int i = 0, iLen = parameters.length; i < iLen; i++) {
if (i > 0) {
writer.print(', ');
}
// TODO(kegluneq): Include annotations on parameters.
writer.print('const [');
parameters[i].accept(this);
writer.print(']');
}
return null;
}
}
/// ToSourceVisitor designed to print 'factory' values for Angular2's
/// `registerType` calls.
class FactoryTransformVisitor extends _CtorTransformVisitor {
FactoryTransformVisitor(PrintWriter writer) : super(writer) {
_withParameterAnnotations = false;
}
@override
Object visitConstructorDeclaration(ConstructorDeclaration node) {
_buildFieldMap(node);
_withParameterNames = true;
_withParameterTypes = true;
_visitNode(node.parameters);
writer.print(' => new ');
_visitNode(node.returnType);
_visitNodeWithPrefix(".", node.name);
_withParameterTypes = false;
_visitNode(node.parameters);
return null;
}
}
/// ToSourceVisitor designed to print a `ClassDeclaration` node as a
/// 'annotations' value for Angular2's `registerType` calls.
class AnnotationsTransformVisitor extends ToSourceVisitor {
final AsyncStringWriter writer;
final XHR _xhr;
final AnnotationMatcher _annotationMatcher;
final AssetId _assetId;
final bool _inlineViews;
final ConstantEvaluator _evaluator = new ConstantEvaluator();
bool _isProcessingView = false;
bool _isProcessingDirective = false;
AnnotationsTransformVisitor(AsyncStringWriter writer, this._xhr,
this._annotationMatcher, this._assetId, {bool inlineViews})
: writer = writer,
_inlineViews = inlineViews,
super(writer);
void _resetState() {
_isProcessingView = _isProcessingDirective = false;
}
@override
Object visitClassDeclaration(ClassDeclaration node) {
writer.print('const [');
var size = node.metadata.length;
for (var i = 0; i < size; ++i) {
if (i > 0) {
writer.print(', ');
}
node.metadata[i].accept(this);
}
writer.print(']');
_resetState();
return null;
}
@override
Object visitAnnotation(Annotation node) {
writer.print('const ');
if (node.name != null) {
_isProcessingDirective = _annotationMatcher.isDirective(node, _assetId);
_isProcessingView = _annotationMatcher.isView(node, _assetId);
node.name.accept(this);
} else {
_isProcessingDirective = false;
_isProcessingView = false;
}
if (node.constructorName != null) {
writer.print('.');
node.constructorName.accept(this);
}
if (node.arguments != null && node.arguments.arguments != null) {
var args = node.arguments.arguments;
writer.print('(');
for (var i = 0, iLen = args.length; i < iLen; ++i) {
if (i != 0) {
writer.print(', ');
}
args[i].accept(this);
}
writer.print(')');
}
return null;
}
/// These correspond to the annotation parameters.
@override
Object visitNamedExpression(NamedExpression node) {
if (!_isProcessingView && !_isProcessingDirective) {
return super.visitNamedExpression(node);
}
// TODO(kegluneq): Remove this limitation.
if (node.name is! Label || node.name.label is! SimpleIdentifier) {
return super.visitNamedExpression(node);
}
var keyString = '${node.name.label}';
if (_isProcessingView && _inlineViews) {
var isSuccess = this._inlineView(keyString, node.expression);
if (isSuccess) return null;
}
return super.visitNamedExpression(node);
}
/// Inlines the template and/or style refered to by `keyString`.
/// Returns whether the `keyString` value was successfully processed.
bool _inlineView(String keyString, AstNode node) {
if (keyString == 'templateUrl') {
// Inline the templateUrl
var url = node.accept(_evaluator);
if (url is String) {
writer.print("template: r'''");
writer.asyncPrint(_readOrEmptyString(url));
writer.print("'''");
// We keep the templateUrl in case the body of the template includes
// relative urls that might be inlined later on (e.g. @import
// directives or url() css values in style tags).
writer.print(", templateUrl: r'$url'");
return true;
} else {
logger.warning('template url is not a String $url');
}
} else if (keyString == 'styleUrls') {
// Inline the styleUrls
var urls = node.accept(_evaluator);
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.print(']');
return true;
}
return false;
}
/// Attempts to read the content from {@link url}, if it returns null then
/// just return the empty string.
Future<String> _readOrEmptyString(String url) async {
var content = await _xhr.get(url);
if (content == null) {
content = '';
}
return content;
}
}

View File

@ -13,9 +13,15 @@ main() {
it("directiveMetadataToMap", () {
var someComponent = new RenderDirectiveMetadata(
compileChildren: false,
hostListeners: MapWrapper.createFromPairs([["LKey", "LVal"]]),
hostProperties: MapWrapper.createFromPairs([["PKey", "PVal"]]),
hostAttributes: MapWrapper.createFromPairs([["AtKey", "AtVal"]]),
hostListeners: MapWrapper.createFromPairs([
["LKey", "LVal"]
]),
hostProperties: MapWrapper.createFromPairs([
["PKey", "PVal"]
]),
hostAttributes: MapWrapper.createFromPairs([
["AtKey", "AtVal"]
]),
id: "someComponent",
properties: ["propKey: propVal"],
readAttributes: ["read1", "read2"],
@ -34,12 +40,15 @@ main() {
changeDetection: ChangeDetectionStrategy.CheckOnce);
var map = directiveMetadataToMap(someComponent);
expect(map["compileChildren"]).toEqual(false);
expect(map["hostListeners"])
.toEqual(MapWrapper.createFromPairs([["LKey", "LVal"]]));
expect(map["hostProperties"])
.toEqual(MapWrapper.createFromPairs([["PKey", "PVal"]]));
expect(map["hostAttributes"])
.toEqual(MapWrapper.createFromPairs([["AtKey", "AtVal"]]));
expect(map["hostListeners"]).toEqual(MapWrapper.createFromPairs([
["LKey", "LVal"]
]));
expect(map["hostProperties"]).toEqual(MapWrapper.createFromPairs([
["PKey", "PVal"]
]));
expect(map["hostAttributes"]).toEqual(MapWrapper.createFromPairs([
["AtKey", "AtVal"]
]));
expect(map["id"]).toEqual("someComponent");
expect(map["properties"]).toEqual(["propKey: propVal"]);
expect(map["readAttributes"]).toEqual(["read1", "read2"]);
@ -55,17 +64,39 @@ main() {
expect(map["callAfterViewChecked"]).toEqual(true);
expect(map["exportAs"]).toEqual("aaa");
expect(map["events"]).toEqual(["onFoo", "onBar"]);
expect(map["changeDetection"]).toEqual(ChangeDetectionStrategy.CheckOnce.index);
expect(map["changeDetection"])
.toEqual(ChangeDetectionStrategy.CheckOnce.index);
});
it("mapToDirectiveMetadata", () {
var map = MapWrapper.createFromPairs([
["compileChildren", false],
["hostProperties", MapWrapper.createFromPairs([["PKey", "testVal"]])],
["hostListeners", MapWrapper.createFromPairs([["LKey", "testVal"]])],
["hostAttributes", MapWrapper.createFromPairs([["AtKey", "testVal"]])],
[
"hostProperties",
MapWrapper.createFromPairs([
["PKey", "testVal"]
])
],
[
"hostListeners",
MapWrapper.createFromPairs([
["LKey", "testVal"]
])
],
[
"hostAttributes",
MapWrapper.createFromPairs([
["AtKey", "testVal"]
])
],
["id", "testId"],
["properties", ["propKey: propVal"]],
["readAttributes", ["readTest1", "readTest2"]],
[
"properties",
["propKey: propVal"]
],
[
"readAttributes",
["readTest1", "readTest2"]
],
["selector", "testSelector"],
["type", RenderDirectiveMetadata.DIRECTIVE_TYPE],
["exportAs", "aaa"],
@ -77,17 +108,23 @@ main() {
["callAfterContentChecked", true],
["callAfterViewInit", true],
["callAfterViewChecked", true],
["events", ["onFoo", "onBar"]],
[
"events",
["onFoo", "onBar"]
],
["changeDetection", ChangeDetectionStrategy.CheckOnce.index]
]);
var meta = directiveMetadataFromMap(map);
expect(meta.compileChildren).toEqual(false);
expect(meta.hostProperties)
.toEqual(MapWrapper.createFromPairs([["PKey", "testVal"]]));
expect(meta.hostListeners)
.toEqual(MapWrapper.createFromPairs([["LKey", "testVal"]]));
expect(meta.hostAttributes)
.toEqual(MapWrapper.createFromPairs([["AtKey", "testVal"]]));
expect(meta.hostProperties).toEqual(MapWrapper.createFromPairs([
["PKey", "testVal"]
]));
expect(meta.hostListeners).toEqual(MapWrapper.createFromPairs([
["LKey", "testVal"]
]));
expect(meta.hostAttributes).toEqual(MapWrapper.createFromPairs([
["AtKey", "testVal"]
]));
expect(meta.id).toEqual("testId");
expect(meta.properties).toEqual(["propKey: propVal"]);
expect(meta.readAttributes).toEqual(["readTest1", "readTest2"]);

View File

@ -121,7 +121,8 @@ void allTests() {
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);
expect(metadata.changeDetection)
.toEqual(ChangeDetectionStrategy.CheckOnce);
});
it('should fail when a class is annotated with multiple Directives.',

View File

@ -1,10 +1,10 @@
library examples.src.hello_world.absolute_url_expression_files.ng_deps.dart;
import 'hello.dart';
export 'hello.dart';
import 'package:angular2/src/core/reflection/reflection.dart' as _ngRef;
import 'package:angular2/angular2.dart'
show Component, Directive, View, NgElement;
export 'hello.dart';
var _visited = false;
void initReflector() {
@ -16,9 +16,9 @@ void initReflector() {
new _ngRef.ReflectionInfo(const [
const Component(selector: 'hello-app'),
const View(
styles: const [r'''.greeting { .color: blue; }''',],
template: r'''{{greeting}}''',
templateUrl: r'package:other_package/template.html',
styles: const [r'''.greeting { .color: blue; }''',])
templateUrl: 'package:other_package/template.html')
], const [], () => new HelloCmp()))
..registerFunction(
hello, new _ngRef.ReflectionInfo(const [const Injectable()], const []));

View File

@ -1,9 +1,9 @@
library dinner.package_soup.ng_deps.dart;
import 'package_soup.dart';
export 'package_soup.dart';
import 'package:angular2/src/core/reflection/reflection.dart' as _ngRef;
import 'package:soup/soup.dart';
export 'package_soup.dart';
var _visited = false;
void initReflector() {

View File

@ -1,9 +1,9 @@
library dinner.relative_soup.ng_deps.dart;
import 'relative_soup.dart';
export 'relative_soup.dart';
import 'package:angular2/src/core/reflection/reflection.dart' as _ngRef;
import 'annotations/soup.dart';
export 'relative_soup.dart';
var _visited = false;
void initReflector() {

View File

@ -1,12 +1,12 @@
library examples.src.hello_world.absolute_url_expression_files.ng_deps.dart;
import 'hello.dart';
export 'hello.dart';
import 'package:angular2/src/core/reflection/reflection.dart' as _ngRef;
import 'package:angular2/angular2.dart'
show bootstrap, Component, Directive, View, NgElement;
export 'a.dart' show alias3;
import 'b.dart' as b;
export 'hello.dart';
export 'a.dart' show alias3;
var _visited = false;
void initReflector() {
@ -18,8 +18,8 @@ void initReflector() {
new _ngRef.ReflectionInfo(const [
const Component(selector: 'hello-app'),
const View(
styles: const [r'''.greeting { .color: blue; }''',],
template: r'''{{greeting}}''',
templateUrl: r'template.html',
styles: const [r'''.greeting { .color: blue; }''',])
templateUrl: 'template.html')
], const [], () => new HelloCmp()));
}

View File

@ -1,10 +1,10 @@
library examples.src.hello_world.index_common_dart.ng_deps.dart;
import 'hello.dart';
export 'hello.dart';
import 'package:angular2/src/core/reflection/reflection.dart' as _ngRef;
import 'package:angular2/angular2.dart'
show Component, Directive, View, NgElement;
export 'hello.dart';
var _visited = false;
void initReflector() {

View File

@ -1,9 +1,9 @@
library dinner.soup.ng_deps.dart;
import 'soup.dart';
export 'soup.dart';
import 'package:angular2/src/core/reflection/reflection.dart' as _ngRef;
import 'package:angular2/src/core/metadata.dart';
export 'soup.dart';
var _visited = false;
void initReflector() {

View File

@ -0,0 +1,99 @@
library dinner.soup.ng_deps.dart;
import 'soup.dart';
import 'package:angular2/src/core/reflection/reflection.dart' as _ngRef;
import 'package:angular2/metadata.dart';
export 'soup.dart';
var _visited = false;
void initReflector() {
if (_visited) return;
_visited = true;
_ngRef.reflector
..registerType(
OnChangeSoupComponent,
new _ngRef.ReflectionInfo(
const [
const Component(
selector: '[soup]',
lifecycle: const [LifecycleEvent.OnChanges])
],
const [],
() => new OnChangeSoupComponent(),
const [OnChanges]))
..registerType(
OnDestroySoupComponent,
new _ngRef.ReflectionInfo(
const [
const Component(
selector: '[soup]',
lifecycle: const [LifecycleEvent.OnDestroy])
],
const [],
() => new OnDestroySoupComponent(),
const [OnDestroy]))
..registerType(
OnCheckSoupComponent,
new _ngRef.ReflectionInfo(
const [
const Component(
selector: '[soup]', lifecycle: const [LifecycleEvent.DoCheck])
],
const [],
() => new OnCheckSoupComponent(),
const [DoCheck]))
..registerType(
OnInitSoupComponent,
new _ngRef.ReflectionInfo(
const [
const Component(
selector: '[soup]', lifecycle: const [LifecycleEvent.OnInit])
],
const [],
() => new OnInitSoupComponent(),
const [OnInit]))
..registerType(
AfterContentInitSoupComponent,
new _ngRef.ReflectionInfo(
const [
const Component(
selector: '[soup]',
lifecycle: const [LifecycleEvent.AfterContentInit])
],
const [],
() => new AfterContentInitSoupComponent(),
const [AfterContentInit]))
..registerType(
AfterContentCheckedSoupComponent,
new _ngRef.ReflectionInfo(
const [
const Component(
selector: '[soup]',
lifecycle: const [LifecycleEvent.AfterContentChecked])
],
const [],
() => new AfterContentCheckedSoupComponent(),
const [AfterContentChecked]))
..registerType(
AfterViewInitSoupComponent,
new _ngRef.ReflectionInfo(
const [
const Component(
selector: '[soup]',
lifecycle: const [LifecycleEvent.AfterViewInit])
],
const [],
() => new AfterViewInitSoupComponent(),
const [AfterViewInit]))
..registerType(
AfterViewCheckedSoupComponent,
new _ngRef.ReflectionInfo(
const [
const Component(
selector: '[soup]',
lifecycle: const [LifecycleEvent.AfterViewChecked])
],
const [],
() => new AfterViewCheckedSoupComponent(),
const [AfterViewChecked]));
}

View File

@ -1,10 +1,10 @@
library dinner.soup.ng_deps.dart;
import 'soup.dart';
export 'soup.dart';
import 'package:angular2/src/core/reflection/reflection.dart' as _ngRef;
import 'package:angular2/src/core/metadata.dart';
import 'package:angular2/src/core/compiler.dart';
export 'soup.dart';
var _visited = false;
void initReflector() {
@ -14,8 +14,7 @@ void initReflector() {
..registerType(
ChangingSoupComponent,
new _ngRef.ReflectionInfo(
const [
const Component(selector: '[soup]')],
const [const Component(selector: '[soup]')],
const [],
() => new ChangingSoupComponent(),
const [OnChanges, AnotherInterface]));

View File

@ -1,10 +1,10 @@
library test.transform.directive_processor.invalid_url_files.hello.ng_deps.dart;
import 'hello.dart';
export 'hello.dart';
import 'package:angular2/src/core/reflection/reflection.dart' as _ngRef;
import 'package:angular2/angular2.dart'
show Component, Directive, View, NgElement;
export 'hello.dart';
var _visited = false;
void initReflector() {
@ -16,8 +16,8 @@ void initReflector() {
new _ngRef.ReflectionInfo(const [
const Component(selector: 'hello-app'),
const View(
styles: const [r'''''', r'''''',],
template: r'''''',
templateUrl: r'/bad/absolute/url.html',
styles: const [r'''''', r'''''',])
templateUrl: '/bad/absolute/url.html')
], const [], () => new HelloCmp()));
}

View File

@ -1,10 +1,10 @@
library dinner.soup.ng_deps.dart;
import 'soup.dart';
export 'soup.dart';
import 'package:angular2/src/core/reflection/reflection.dart' as _ngRef;
import 'package:angular2/src/core/metadata.dart';
import 'package:angular2/src/core/compiler.dart';
export 'soup.dart';
var _visited = false;
void initReflector() {
@ -14,9 +14,7 @@ void initReflector() {
..registerType(
MultiSoupComponent,
new _ngRef.ReflectionInfo(
const [
const Component(selector: '[soup]')
],
const [const Component(selector: '[soup]')],
const [],
() => new MultiSoupComponent(),
const [OnChanges, OnDestroy, OnInit]));

View File

@ -1,9 +1,9 @@
library main.ng_deps.dart;
import 'main.dart';
export 'main.dart';
import 'package:angular2/src/core/reflection/reflection.dart' as _ngRef;
import 'package:angular2/src/core/metadata.dart';
export 'main.dart';
var _visited = false;
void initReflector() {

View File

@ -1,10 +1,10 @@
library examples.src.hello_world.multiple_style_urls_files.ng_deps.dart;
import 'hello.dart';
export 'hello.dart';
import 'package:angular2/src/core/reflection/reflection.dart' as _ngRef;
import 'package:angular2/angular2.dart'
show Component, Directive, View, NgElement;
export 'hello.dart';
var _visited = false;
void initReflector() {
@ -15,12 +15,9 @@ void initReflector() {
HelloCmp,
new _ngRef.ReflectionInfo(const [
const Component(selector: 'hello-app'),
const View(
template: r'''{{greeting}}''',
templateUrl: r'template.html',
styles: const [
r'''.greeting { .color: blue; }''',
r'''.hello { .color: red; }''',
])
const View(styles: const [
r'''.greeting { .color: blue; }''',
r'''.hello { .color: red; }''',
], template: r'''{{greeting}}''', templateUrl: 'template.html')
], const [], () => new HelloCmp()));
}

View File

@ -1,10 +1,10 @@
library examples.src.hello_world.multiple_style_urls_not_inlined_files.ng_deps.dart;
import 'hello.dart';
export 'hello.dart';
import 'package:angular2/src/core/reflection/reflection.dart' as _ngRef;
import 'package:angular2/angular2.dart'
show Component, Directive, View, NgElement;
export 'hello.dart';
var _visited = false;
void initReflector() {
@ -15,11 +15,9 @@ void initReflector() {
HelloCmp,
new _ngRef.ReflectionInfo(const [
const Component(selector: 'hello-app'),
const View(
templateUrl: 'package:a/template.html',
styleUrls: const [
'package:a/template.css',
'package:a/template_other.css'
])
const View(styleUrls: const [
'package:a/template.css',
'package:a/template_other.css'
], templateUrl: 'package:a/template.html')
], const [], () => new HelloCmp()));
}

View File

@ -1,9 +1,9 @@
library dinner.soup.ng_deps.dart;
import 'soup.dart';
export 'soup.dart';
import 'package:angular2/src/core/reflection/reflection.dart' as _ngRef;
import 'package:angular2/src/core/metadata.dart';
export 'soup.dart';
var _visited = false;
void initReflector() {
@ -15,7 +15,7 @@ void initReflector() {
new _ngRef.ReflectionInfo(const [
const Component(selector: '[soup]')
], const [
const [String, Tasty],
const [String,const Tasty()],
const [const Inject(Salt)]
], (String description, salt) => new SoupComponent(description, salt)));
}

View File

@ -4,5 +4,5 @@ import 'package:angular2/src/core/metadata.dart';
@Component(selector: '[soup]')
class SoupComponent {
SoupComponent(@Tasty String description, @Inject(Salt) salt);
SoupComponent(@Tasty() String description, @Inject(Salt) salt);
}

View File

@ -1,9 +1,9 @@
library main.ng_deps.dart;
import 'main.dart';
export 'main.dart';
import 'package:angular2/src/core/reflection/reflection.dart' as _ngRef;
import 'package:angular2/src/core/metadata.dart';
export 'main.dart';
var _visited = false;
void initReflector() {

View File

@ -1,10 +1,10 @@
library dinner.soup.ng_deps.dart;
import 'soup.dart';
export 'soup.dart';
import 'package:angular2/src/core/reflection/reflection.dart' as _ngRef;
import 'package:angular2/src/core/compiler.dart' as prefix;
import 'package:angular2/src/core/metadata.dart';
export 'soup.dart';
var _visited = false;
void initReflector() {

View File

@ -1,10 +1,10 @@
library examples.src.hello_world.split_url_expression_files.ng_deps.dart;
import 'hello.dart';
export 'hello.dart';
import 'package:angular2/src/core/reflection/reflection.dart' as _ngRef;
import 'package:angular2/angular2.dart'
show Component, Directive, View, NgElement;
export 'hello.dart';
var _visited = false;
void initReflector() {
@ -16,6 +16,6 @@ void initReflector() {
new _ngRef.ReflectionInfo(const [
const Component(selector: 'hello-app'),
const View(
template: r'''{{greeting}}''', templateUrl: r'template.html')
template: r'''{{greeting}}''', templateUrl: 'templ' 'ate.html')
], const [], () => new HelloCmp()));
}

View File

@ -1,9 +1,9 @@
library static_function_files.hello.ng_deps.dart;
import 'hello.dart';
export 'hello.dart';
import 'package:angular2/src/core/reflection/reflection.dart' as _ngRef;
import 'package:angular2/angular2.dart';
export 'hello.dart';
var _visited = false;
void initReflector() {

View File

@ -1,9 +1,9 @@
library dinner.soup.ng_deps.dart;
import 'soup.dart';
export 'soup.dart';
import 'package:angular2/src/core/reflection/reflection.dart' as _ngRef;
import 'package:angular2/src/core/metadata.dart';
export 'soup.dart';
var _visited = false;
void initReflector() {

View File

@ -1,10 +1,10 @@
library dinner.soup.ng_deps.dart;
import 'soup.dart';
export 'soup.dart';
import 'package:angular2/src/core/reflection/reflection.dart' as _ngRef;
import 'package:angular2/src/core/compiler.dart';
import 'package:angular2/src/core/metadata.dart';
export 'soup.dart';
var _visited = false;
void initReflector() {
@ -13,8 +13,6 @@ void initReflector() {
_ngRef.reflector
..registerType(
OnChangeSoupComponent,
new _ngRef.ReflectionInfo(const [
const Component(
selector: '[soup]')
], const [], () => new OnChangeSoupComponent()));
new _ngRef.ReflectionInfo(const [const Component(selector: '[soup]')],
const [], () => new OnChangeSoupComponent()));
}

View File

@ -1,10 +1,10 @@
library examples.src.hello_world.url_expression_files.ng_deps.dart;
import 'hello.dart';
export 'hello.dart';
import 'package:angular2/src/core/reflection/reflection.dart' as _ngRef;
import 'package:angular2/angular2.dart'
show Component, Directive, View, NgElement;
export 'hello.dart';
var _visited = false;
void initReflector() {
@ -16,6 +16,6 @@ void initReflector() {
new _ngRef.ReflectionInfo(const [
const Component(selector: 'hello-app'),
const View(
template: r'''{{greeting}}''', templateUrl: r'template.html')
template: r'''{{greeting}}''', templateUrl: 'template.html')
], const [], () => new HelloCmp()));
}

View File

@ -12,9 +12,8 @@ main() {
}
var formatter = new DartFormatter();
var transform = new AngularTransformerGroup(new TransformerOptions(
['web/index.dart'],
formatCode: true));
var transform = new AngularTransformerGroup(
new TransformerOptions(['web/index.dart'], formatCode: true));
class IntegrationTestConfig {
final String name;
@ -96,7 +95,8 @@ void allTests() {
new IntegrationTestConfig('should preserve multiple annotations.', inputs: {
'a|web/index.dart': 'two_annotations_files/index.dart',
'a|web/bar.dart': 'two_annotations_files/bar.dart',
'angular2|lib/src/core/metadata.dart': '../../../lib/src/core/metadata.dart'
'angular2|lib/src/core/metadata.dart':
'../../../lib/src/core/metadata.dart'
}, outputs: {
'a|web/bar.ng_deps.dart':
'two_annotations_files/expected/bar.ng_deps.dart'

View File

@ -1,10 +1,10 @@
library bar.ng_deps.dart;
import 'bar.dart';
export 'bar.dart';
import 'package:angular2/src/core/reflection/reflection.dart' as _ngRef;
import 'package:angular2/src/core/metadata.dart';
import 'foo.dart';
export 'bar.dart';
var _visited = false;
void initReflector() {

View File

@ -1,9 +1,9 @@
library bar.ng_deps.dart;
import 'bar.dart';
export 'bar.dart';
import 'package:angular2/src/core/reflection/reflection.dart' as _ngRef;
import 'package:angular2/src/core/metadata.dart';
export 'bar.dart';
var _visited = false;
void initReflector() {

View File

@ -1,13 +1,13 @@
library web_foo.ng_deps.dart;
import 'index.dart';
export 'index.dart';
import 'package:angular2/src/core/reflection/reflection.dart' as _ngRef;
import 'package:angular2/bootstrap_static.dart';
import 'index.ng_deps.dart' as ngStaticInit;
import 'package:angular2/src/core/reflection/reflection.dart';
import 'bar.dart';
import 'bar.ng_deps.dart' as i0;
export 'index.dart';
var _visited = false;
void initReflector() {

View File

@ -1,9 +1,9 @@
library bar.ng_deps.dart;
import 'bar.dart';
export 'bar.dart';
import 'package:angular2/src/core/reflection/reflection.dart' as _ngRef;
import 'package:angular2/src/core/metadata.dart';
export 'bar.dart';
var _visited = false;
void initReflector() {

View File

@ -4,19 +4,21 @@ import 'package:angular2/src/core/change_detection/pregen_proto_change_detector.
as _gen;
import 'bar.dart';
export 'bar.dart';
import 'package:angular2/src/core/reflection/reflection.dart' as _ngRef;
import 'package:angular2/src/core/metadata.dart';
export 'bar.dart';
var _visited = false;
void initReflector() {
if (_visited) return;
_visited = true;
_ngRef.reflector
..registerType(MyComponent, new _ngRef.ReflectionInfo(const [
const Component(selector: '[soup]'),
const View(template: 'Salad: {{myNum}} is awesome')
], const [], () => new MyComponent()))
..registerType(
MyComponent,
new _ngRef.ReflectionInfo(const [
const Component(selector: '[soup]'),
const View(template: 'Salad: {{myNum}} is awesome')
], const [], () => new MyComponent()))
..registerGetters({'myNum': (o) => o.myNum});
_gen.preGeneratedProtoDetectors['MyComponent_comp_0'] =
_MyComponent_ChangeDetector0.newProtoChangeDetector;
@ -26,18 +28,19 @@ class _MyComponent_ChangeDetector0
extends _gen.AbstractChangeDetector<MyComponent> {
var myNum0, interpolate1;
_MyComponent_ChangeDetector0(dispatcher) : super(
"MyComponent_comp_0", dispatcher, 2,
_MyComponent_ChangeDetector0.gen_propertyBindingTargets,
_MyComponent_ChangeDetector0.gen_directiveIndices,null) {
_MyComponent_ChangeDetector0(dispatcher)
: super(
"MyComponent_comp_0",
dispatcher,
2,
_MyComponent_ChangeDetector0.gen_propertyBindingTargets,
_MyComponent_ChangeDetector0.gen_directiveIndices,
null) {
dehydrateDirectives(false);
}
void detectChangesInRecordsInternal(throwOnChange) {
var l_context = this.context,
l_myNum0,
c_myNum0,
l_interpolate1;
var l_context = this.context, l_myNum0, c_myNum0, l_interpolate1;
c_myNum0 = false;
var isChanged = false;
var changes = null;
@ -88,4 +91,4 @@ class _MyComponent_ChangeDetector0
return new _gen.PregenProtoChangeDetector(
(a) => new _MyComponent_ChangeDetector0(a), def);
}
}
}

View File

@ -1,10 +1,10 @@
library bar.ng_deps.dart;
import 'bar.dart';
export 'bar.dart';
import 'package:angular2/src/core/reflection/reflection.dart' as _ngRef;
import 'package:angular2/src/core/metadata.dart';
import 'foo.dart' as prefix;
export 'bar.dart';
var _visited = false;
void initReflector() {