@ -34,7 +34,6 @@ class _ExtractQueryFieldsFromAnnotation extends Object
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class _ExtractQueryFieldsFromPropMetadata extends Object
|
class _ExtractQueryFieldsFromPropMetadata extends Object
|
||||||
with RecursiveAstVisitor<Object> {
|
with RecursiveAstVisitor<Object> {
|
||||||
final ConstantEvaluator _evaluator = new ConstantEvaluator();
|
final ConstantEvaluator _evaluator = new ConstantEvaluator();
|
||||||
@ -52,7 +51,10 @@ class _ExtractQueryFieldsFromPropMetadata extends Object
|
|||||||
var res = false;
|
var res = false;
|
||||||
list.elements.forEach((item) {
|
list.elements.forEach((item) {
|
||||||
var n = item.constructorName.toString();
|
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;
|
res = true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -65,14 +67,12 @@ class _ExtractQueryFieldsFromPropMetadata extends Object
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Future<String> createNgSettersAndGetters(
|
Future<String> createNgSettersAndGetters(
|
||||||
AssetReader reader, AssetId entryPoint) async {
|
AssetReader reader, AssetId entryPoint) async {
|
||||||
NgDeps ngDeps = await NgDeps.parse(reader, entryPoint);
|
NgDeps ngDeps = await NgDeps.parse(reader, entryPoint);
|
||||||
|
|
||||||
String code = ngDeps.code;
|
String code = ngDeps.code;
|
||||||
var setters = _generateSetters(_createPropertiesMap(ngDeps));
|
var setters = _generateSetters(_createPropertiesMap(ngDeps));
|
||||||
var getters = _generateGetters(_createEventPropertiesList(ngDeps));
|
|
||||||
|
|
||||||
ngDeps.registeredTypes.forEach((t) {
|
ngDeps.registeredTypes.forEach((t) {
|
||||||
final fromAnnotation = new _ExtractQueryFieldsFromAnnotation();
|
final fromAnnotation = new _ExtractQueryFieldsFromAnnotation();
|
||||||
@ -86,16 +86,13 @@ Future<String> createNgSettersAndGetters(
|
|||||||
setters.addAll(_generateSetters(fromPropMetadata.asMap()));
|
setters.addAll(_generateSetters(fromPropMetadata.asMap()));
|
||||||
});
|
});
|
||||||
|
|
||||||
if (setters.isEmpty && getters.isEmpty) return code;
|
if (setters.isEmpty) return code;
|
||||||
var out = new StringBuffer();
|
var out = new StringBuffer();
|
||||||
var codeInjectIdx = ngDeps.registeredTypes.last.registerMethod.end;
|
var codeInjectIdx = ngDeps.registeredTypes.last.registerMethod.end;
|
||||||
out.write(code.substring(0, codeInjectIdx));
|
out.write(code.substring(0, codeInjectIdx));
|
||||||
if (setters.isNotEmpty) {
|
if (setters.isNotEmpty) {
|
||||||
out.write('..registerSetters({${setters.join(', ')}})');
|
out.write('..registerSetters({${setters.join(', ')}})');
|
||||||
}
|
}
|
||||||
if (getters.isNotEmpty) {
|
|
||||||
out.write('..registerGetters({${getters.join(', ')}})');
|
|
||||||
}
|
|
||||||
out.write(code.substring(codeInjectIdx));
|
out.write(code.substring(codeInjectIdx));
|
||||||
return '$out';
|
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
|
/// the bind properties and the values are either the one and only type
|
||||||
/// binding to that property or the empty string.
|
/// binding to that property or the empty string.
|
||||||
Map<String, String> _createPropertiesMap(NgDeps ngDeps) {
|
Map<String, String> _createPropertiesMap(NgDeps ngDeps) {
|
||||||
var visitor = new ExtractNamedExpressionVisitor('inputs');
|
var visitor = new ExtractNamedExpressionVisitor('properties');
|
||||||
var bindMap = {};
|
var bindMap = {};
|
||||||
ngDeps.registeredTypes.forEach((RegisteredType t) {
|
ngDeps.registeredTypes.forEach((RegisteredType t) {
|
||||||
visitor.bindConfig.clear();
|
visitor.bindConfig.clear();
|
||||||
@ -147,37 +144,3 @@ Map<String, String> _createPropertiesMap(NgDeps ngDeps) {
|
|||||||
});
|
});
|
||||||
return bindMap;
|
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;
|
|
||||||
}
|
|
||||||
|
@ -38,17 +38,9 @@ class NgDepsVisitor extends RecursiveAstVisitor<Object> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _createModel(String libraryUri) {
|
void _createModel(String libraryUri) {
|
||||||
_model = new NgDepsModel()..libraryUri = libraryUri;
|
_model = new NgDepsModel()
|
||||||
|
..libraryUri = libraryUri
|
||||||
// We need to import & export the original file.
|
..sourceFile = path.basename(processedFile.path);
|
||||||
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
|
@override
|
||||||
@ -139,6 +131,14 @@ abstract class NgDepsWriterMixin
|
|||||||
buffer.writeln('library ${model.libraryUri}${DEPS_EXTENSION};\n');
|
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.
|
// We do not support `partUris`, so skip outputting them.
|
||||||
for (var importModel in model.imports) {
|
for (var importModel in model.imports) {
|
||||||
// Ignore deferred imports here so as to not load the deferred libraries
|
// Ignore deferred imports here so as to not load the deferred libraries
|
||||||
@ -149,6 +149,7 @@ abstract class NgDepsWriterMixin
|
|||||||
|
|
||||||
writeImportModel(importModel);
|
writeImportModel(importModel);
|
||||||
}
|
}
|
||||||
|
writeExportModel(new ExportModel()..uri = model.sourceFile);
|
||||||
model.exports.forEach(writeExportModel);
|
model.exports.forEach(writeExportModel);
|
||||||
|
|
||||||
buffer
|
buffer
|
||||||
|
@ -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();
|
||||||
|
}
|
@ -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';
|
||||||
|
}
|
@ -1,212 +1,224 @@
|
|||||||
library angular2.transform.common.directive_metadata_reader;
|
library angular2.transform.common.directive_metadata_reader;
|
||||||
|
|
||||||
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:analyzer/analyzer.dart';
|
import 'package:analyzer/analyzer.dart';
|
||||||
import 'package:analyzer/src/generated/element.dart';
|
import 'package:angular2/src/compiler/directive_metadata.dart';
|
||||||
import 'package:angular2/src/core/render/api.dart';
|
import 'package:angular2/src/compiler/template_compiler.dart';
|
||||||
import 'package:angular2/src/core/change_detection/change_detection.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
|
class DirectiveMetadataReader {
|
||||||
/// instance of [ClassDeclaration] (a class which may have a [Directive] or
|
final _DirectiveMetadataVisitor _visitor;
|
||||||
/// [Component] annotation) or an [InstanceCreationExpression] (an instantiation
|
final TemplateCompiler _templateCompiler;
|
||||||
/// 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
num _getDirectiveType(String annotationName, Element element) {
|
DirectiveMetadataReader._(this._visitor, this._templateCompiler);
|
||||||
var byNameMatch = -1;
|
|
||||||
// TODO(kegluneq): Detect subtypes & implementations of `Directive`s.
|
/// Accepts an [AnnotationMatcher] which tests that an [Annotation]
|
||||||
switch (annotationName) {
|
/// is a [Directive], [Component], or [View].
|
||||||
case 'Directive':
|
factory DirectiveMetadataReader(AnnotationMatcher annotationMatcher,
|
||||||
byNameMatch = RenderDirectiveMetadata.DIRECTIVE_TYPE;
|
InterfaceMatcher interfaceMatcher, TemplateCompiler templateCompiler) {
|
||||||
break;
|
var lifecycleVisitor = new _LifecycleHookVisitor(interfaceMatcher);
|
||||||
case 'Component':
|
var visitor =
|
||||||
byNameMatch = RenderDirectiveMetadata.COMPONENT_TYPE;
|
new _DirectiveMetadataVisitor(annotationMatcher, lifecycleVisitor);
|
||||||
break;
|
|
||||||
default:
|
return new DirectiveMetadataReader._(visitor, templateCompiler);
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
if (element != null) {
|
|
||||||
var byResolvedAst = -1;
|
/// Reads *un-normalized* [CompileDirectiveMetadata] from the
|
||||||
var libName = element.library.name;
|
/// [ClassDeclaration] `node`.
|
||||||
// If we have resolved, ensure the library is correct.
|
///
|
||||||
if (libName == 'angular2.src.core.metadata.directives' ||
|
/// `node` is expected to be a class which may have a [Directive] or [Component]
|
||||||
libName == 'angular2.src.core.metadata') {
|
/// annotation. If `node` does not have one of these annotations, this function
|
||||||
byResolvedAst = byNameMatch;
|
/// 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 {
|
/// Visitor that attempts to evaluate a provided `node` syntactically.
|
||||||
@override
|
///
|
||||||
Object visitClassDeclaration(ClassDeclaration node) {
|
/// This lack of semantic information means it cannot do much - for
|
||||||
node.metadata.accept(this);
|
/// example, it can create a list from a list literal and combine adjacent
|
||||||
if (this._hasMeta) {
|
/// strings but cannot determine that an identifier is a constant string,
|
||||||
if (node.implementsClause != null &&
|
/// even if that identifier is defined in the same [CompilationUnit].
|
||||||
node.implementsClause.interfaces != null) {
|
///
|
||||||
_populateLifecycle(node.implementsClause.interfaces
|
/// Returns the result of evaluation or [ConstantEvaluator.NOT_A_CONSTANT]
|
||||||
.map((s) => s.toSource().split('.').last));
|
/// where appropriate.
|
||||||
}
|
final ConstantEvaluator _evaluator = new ConstantEvaluator();
|
||||||
}
|
|
||||||
return null;
|
/// 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
|
/// Evaluates the [List] represented by `expression` and adds all values,
|
||||||
/// [RenderDirectiveMetadata] object.
|
/// 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
|
class _DirectiveMetadataVisitor extends Object
|
||||||
with RecursiveAstVisitor<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
|
final _LifecycleHookVisitor _lifecycleVisitor;
|
||||||
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 ConstantEvaluator _evaluator = new ConstantEvaluator();
|
/// The [AssetId] we are currently processing.
|
||||||
|
AssetId _assetId;
|
||||||
|
|
||||||
void _initializeMetadata(num directiveType) {
|
_DirectiveMetadataVisitor(this._annotationMatcher, this._lifecycleVisitor) {
|
||||||
assert(directiveType >= 0);
|
reset(null);
|
||||||
|
|
||||||
_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 = [];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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,
|
type: _type,
|
||||||
|
isComponent: _isComponent,
|
||||||
|
dynamicLoadable: true, // NOTE(kegluneq): For future optimization.
|
||||||
selector: _selector,
|
selector: _selector,
|
||||||
compileChildren: _compileChildren,
|
|
||||||
inputs: _inputs,
|
|
||||||
host: _host,
|
|
||||||
readAttributes: _readAttributes,
|
|
||||||
exportAs: _exportAs,
|
exportAs: _exportAs,
|
||||||
callOnDestroy: _callOnDestroy,
|
|
||||||
callOnChanges: _callOnChange,
|
|
||||||
callDoCheck: _callDoCheck,
|
|
||||||
callOnInit: _callOnInit,
|
|
||||||
callAfterContentInit: _callAfterContentInit,
|
|
||||||
callAfterContentChecked: _callAfterContentChecked,
|
|
||||||
callAfterViewInit: _callAfterViewInit,
|
|
||||||
callAfterViewChecked: _callAfterViewChecked,
|
|
||||||
changeDetection: _changeDetection,
|
changeDetection: _changeDetection,
|
||||||
outputs: _outputs);
|
properties: _properties,
|
||||||
|
events: _events,
|
||||||
|
host: _host,
|
||||||
|
lifecycleHooks: _lifecycleHooks,
|
||||||
|
template: _template);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Object visitAnnotation(Annotation node) {
|
Object visitAnnotation(Annotation node) {
|
||||||
var directiveType = _getDirectiveType('${node.name}', node.element);
|
var isComponent = _annotationMatcher.isComponent(node, _assetId);
|
||||||
if (directiveType >= 0) {
|
var isDirective = _annotationMatcher.isDirective(node, _assetId);
|
||||||
if (_hasMeta) {
|
if (isDirective) {
|
||||||
|
if (_hasMetadata) {
|
||||||
throw new FormatException(
|
throw new FormatException(
|
||||||
'Only one Directive is allowed per class. '
|
'Only one Directive is allowed per class. '
|
||||||
'Found "$node" but already processed "$meta".',
|
'Found unexpected "$node".',
|
||||||
'$node' /* source */);
|
'$node' /* source */);
|
||||||
}
|
}
|
||||||
_initializeMetadata(directiveType);
|
_isComponent = isComponent;
|
||||||
|
_hasMetadata = true;
|
||||||
super.visitAnnotation(node);
|
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.
|
// Annotation we do not recognize - no need to visit.
|
||||||
return null;
|
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
|
@override
|
||||||
Object visitNamedExpression(NamedExpression node) {
|
Object visitNamedExpression(NamedExpression node) {
|
||||||
// TODO(kegluneq): Remove this limitation.
|
// TODO(kegluneq): Remove this limitation.
|
||||||
@ -220,10 +232,7 @@ class _DirectiveMetadataVisitor extends Object
|
|||||||
case 'selector':
|
case 'selector':
|
||||||
_populateSelector(node.expression);
|
_populateSelector(node.expression);
|
||||||
break;
|
break;
|
||||||
case 'compileChildren':
|
case 'properties':
|
||||||
_populateCompileChildren(node.expression);
|
|
||||||
break;
|
|
||||||
case 'inputs':
|
|
||||||
_populateProperties(node.expression);
|
_populateProperties(node.expression);
|
||||||
break;
|
break;
|
||||||
case 'host':
|
case 'host':
|
||||||
@ -235,84 +244,29 @@ class _DirectiveMetadataVisitor extends Object
|
|||||||
case 'changeDetection':
|
case 'changeDetection':
|
||||||
_populateChangeDetection(node.expression);
|
_populateChangeDetection(node.expression);
|
||||||
break;
|
break;
|
||||||
case 'outputs':
|
case 'events':
|
||||||
_populateEvents(node.expression);
|
_populateEvents(node.expression);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return null;
|
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) {
|
void _populateSelector(Expression selectorValue) {
|
||||||
_checkMeta();
|
_checkMeta();
|
||||||
_selector = _expressionToString(selectorValue, 'Directive#selector');
|
_selector = _expressionToString(selectorValue, 'Directive#selector');
|
||||||
}
|
}
|
||||||
|
|
||||||
void _checkMeta() {
|
void _checkMeta() {
|
||||||
if (!_hasMeta) {
|
if (!_hasMetadata) {
|
||||||
throw new ArgumentError(
|
throw new ArgumentError(
|
||||||
'Incorrect value passed to readDirectiveMetadata. '
|
'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) {
|
void _populateProperties(Expression propertiesValue) {
|
||||||
_checkMeta();
|
_checkMeta();
|
||||||
_populateList(propertiesValue, _inputs, 'Directive#properties');
|
_populateList(propertiesValue, _properties, 'Directive#properties');
|
||||||
}
|
}
|
||||||
|
|
||||||
void _populateHost(Expression hostValue) {
|
void _populateHost(Expression hostValue) {
|
||||||
@ -325,32 +279,138 @@ class _DirectiveMetadataVisitor extends Object
|
|||||||
_exportAs = _expressionToString(exportAsValue, 'Directive#exportAs');
|
_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) {
|
void _populateEvents(Expression eventsValue) {
|
||||||
_checkMeta();
|
_checkMeta();
|
||||||
_populateList(eventsValue, _outputs, 'Directive#events');
|
_populateList(eventsValue, _events, 'Directive#events');
|
||||||
}
|
}
|
||||||
|
|
||||||
void _populateChangeDetection(Expression value) {
|
void _populateChangeDetection(Expression value) {
|
||||||
_checkMeta();
|
_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 =
|
/// Visitor responsible for parsing a @View [Annotation] and producing a
|
||||||
new Map.fromIterable(ChangeDetectionStrategy.values,
|
/// [CompileTemplateMetadata].
|
||||||
key: (v) => v.toString());
|
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());
|
||||||
|
}
|
||||||
|
@ -11,14 +11,21 @@ class NgDepsModel extends GeneratedMessage {
|
|||||||
static final BuilderInfo _i = new BuilderInfo('NgDepsModel')
|
static final BuilderInfo _i = new BuilderInfo('NgDepsModel')
|
||||||
..a(1, 'libraryUri', PbFieldType.OS)
|
..a(1, 'libraryUri', PbFieldType.OS)
|
||||||
..p(2, 'partUris', PbFieldType.PS)
|
..p(2, 'partUris', PbFieldType.PS)
|
||||||
..pp(3, 'imports', PbFieldType.PM, ImportModel.$checkItem, ImportModel.create)
|
..pp(3, 'imports', PbFieldType.PM, ImportModel.$checkItem,
|
||||||
..pp(4, 'exports', PbFieldType.PM, ExportModel.$checkItem, ExportModel.create)
|
ImportModel.create)
|
||||||
..pp(5, 'reflectables', PbFieldType.PM, ReflectionInfoModel.$checkItem, ReflectionInfoModel.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() : super();
|
||||||
NgDepsModel.fromBuffer(List<int> i, [ExtensionRegistry r = ExtensionRegistry.EMPTY]) : super.fromBuffer(i, r);
|
NgDepsModel.fromBuffer(List<int> i,
|
||||||
NgDepsModel.fromJson(String i, [ExtensionRegistry r = ExtensionRegistry.EMPTY]) : super.fromJson(i, r);
|
[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);
|
NgDepsModel clone() => new NgDepsModel()..mergeFromMessage(this);
|
||||||
BuilderInfo get info_ => _i;
|
BuilderInfo get info_ => _i;
|
||||||
static NgDepsModel create() => new NgDepsModel();
|
static NgDepsModel create() => new NgDepsModel();
|
||||||
@ -27,13 +34,17 @@ class NgDepsModel extends GeneratedMessage {
|
|||||||
if (_defaultInstance == null) _defaultInstance = new _ReadonlyNgDepsModel();
|
if (_defaultInstance == null) _defaultInstance = new _ReadonlyNgDepsModel();
|
||||||
return _defaultInstance;
|
return _defaultInstance;
|
||||||
}
|
}
|
||||||
|
|
||||||
static NgDepsModel _defaultInstance;
|
static NgDepsModel _defaultInstance;
|
||||||
static void $checkItem(NgDepsModel v) {
|
static void $checkItem(NgDepsModel v) {
|
||||||
if (v is !NgDepsModel) checkItemFailed(v, 'NgDepsModel');
|
if (v is! NgDepsModel) checkItemFailed(v, 'NgDepsModel');
|
||||||
}
|
}
|
||||||
|
|
||||||
String get libraryUri => getField(1);
|
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);
|
bool hasLibraryUri() => hasField(1);
|
||||||
void clearLibraryUri() => clearField(1);
|
void clearLibraryUri() => clearField(1);
|
||||||
|
|
||||||
@ -44,6 +55,14 @@ class NgDepsModel extends GeneratedMessage {
|
|||||||
List<ExportModel> get exports => getField(4);
|
List<ExportModel> get exports => getField(4);
|
||||||
|
|
||||||
List<ReflectionInfoModel> get reflectables => getField(5);
|
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 {}
|
class _ReadonlyNgDepsModel extends NgDepsModel with ReadonlyMessageMixin {}
|
||||||
@ -53,15 +72,34 @@ const NgDepsModel$json = const {
|
|||||||
'2': const [
|
'2': const [
|
||||||
const {'1': 'library_uri', '3': 1, '4': 1, '5': 9},
|
const {'1': 'library_uri', '3': 1, '4': 1, '5': 9},
|
||||||
const {'1': 'part_uris', '3': 2, '4': 3, '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 {
|
||||||
const {'1': 'exports', '3': 4, '4': 3, '5': 11, '6': '.angular2.src.transform.common.model.proto.ExportModel'},
|
'1': 'imports',
|
||||||
const {'1': 'reflectables', '3': 5, '4': 3, '5': 11, '6': '.angular2.src.transform.common.model.proto.ReflectionInfoModel'},
|
'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:
|
* Generated with:
|
||||||
* ng_deps_model.proto (c84f449fc55ef46e38a652f65449c6645b9fe6cb)
|
* ng_deps_model.proto (83fe43a087fdd0a7ebee360cd6b669570df4d216)
|
||||||
* libprotoc 2.5.0
|
* libprotoc 2.5.0
|
||||||
* dart-protoc-plugin (cc35f743de982a4916588b9c505dd21c7fe87d17)
|
* dart-protoc-plugin (cc35f743de982a4916588b9c505dd21c7fe87d17)
|
||||||
*/
|
*/
|
||||||
|
@ -14,5 +14,10 @@ message NgDepsModel {
|
|||||||
|
|
||||||
repeated ExportModel exports = 4;
|
repeated ExportModel exports = 4;
|
||||||
|
|
||||||
|
// All classes in `source_file` marked with @Injectable or a known subclass.
|
||||||
repeated ReflectionInfoModel reflectables = 5;
|
repeated ReflectionInfoModel reflectables = 5;
|
||||||
|
|
||||||
|
// The basename of the file from which the ng_deps were generated.
|
||||||
|
// Example: component.dart
|
||||||
|
optional string source_file = 6;
|
||||||
}
|
}
|
||||||
|
@ -4,12 +4,13 @@ const BOOTSTRAP_NAME = 'bootstrap';
|
|||||||
const SETUP_METHOD_NAME = 'initReflector';
|
const SETUP_METHOD_NAME = 'initReflector';
|
||||||
const REFLECTOR_VAR_NAME = 'reflector';
|
const REFLECTOR_VAR_NAME = 'reflector';
|
||||||
const TRANSFORM_DYNAMIC_MODE = 'transform_dynamic';
|
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_EXTENSION = '.ng_deps.dart';
|
||||||
const DEPS_JSON_EXTENSION = '.ng_deps.json';
|
const DEPS_JSON_EXTENSION = '.ng_deps.json';
|
||||||
const META_EXTENSION = '.ng_meta.json';
|
const META_EXTENSION = '.ng_meta.json';
|
||||||
// TODO(sigmund): consider merging into .ng_meta by generating local metadata
|
const TEMPLATE_EXTENSION = '.template.dart';
|
||||||
// upfront (rather than extracting it from ng_deps).
|
|
||||||
const ALIAS_EXTENSION = '.aliases.json';
|
|
||||||
const REFLECTION_CAPABILITIES_NAME = 'ReflectionCapabilities';
|
const REFLECTION_CAPABILITIES_NAME = 'ReflectionCapabilities';
|
||||||
const REFLECTOR_IMPORT = 'package:angular2/src/core/reflection/reflection.dart';
|
const REFLECTOR_IMPORT = 'package:angular2/src/core/reflection/reflection.dart';
|
||||||
const REFLECTOR_PREFIX = '_ngRef';
|
const REFLECTOR_PREFIX = '_ngRef';
|
||||||
@ -18,20 +19,40 @@ const REGISTER_GETTERS_METHOD_NAME = 'registerGetters';
|
|||||||
const REGISTER_SETTERS_METHOD_NAME = 'registerSetters';
|
const REGISTER_SETTERS_METHOD_NAME = 'registerSetters';
|
||||||
const REGISTER_METHODS_METHOD_NAME = 'registerMethods';
|
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].
|
/// Returns `uri` with its extension updated to [META_EXTENSION].
|
||||||
String toMetaExtension(String uri) =>
|
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].
|
/// Returns `uri` with its extension updated to [DEPS_EXTENSION].
|
||||||
String toDepsExtension(String uri) =>
|
String toDepsExtension(String uri) =>
|
||||||
_toExtension(uri, const [META_EXTENSION, DEPS_JSON_EXTENSION, '.dart'], DEPS_EXTENSION);
|
_toExtension(uri, ALL_EXTENSIONS, DEPS_EXTENSION);
|
||||||
|
|
||||||
/// Returns `uri` with its extension updated to [ALIAS_EXTENSION].
|
|
||||||
String toAliasExtension(String uri) =>
|
|
||||||
_toExtension(uri, const [DEPS_EXTENSION, '.dart'], ALIAS_EXTENSION);
|
|
||||||
|
|
||||||
|
/// Returns `uri` with its extension updated to [DEPS_JSON_EXTENSION].
|
||||||
String toJsonExtension(String uri) =>
|
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
|
/// Returns `uri` with its extension updated to `toExtension` if its
|
||||||
/// extension is currently in `fromExtension`.
|
/// extension is currently in `fromExtension`.
|
||||||
|
@ -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 */);
|
||||||
|
}
|
@ -1,7 +1,6 @@
|
|||||||
library angular2.transform.common.ng_meta;
|
library angular2.transform.common.ng_meta;
|
||||||
|
|
||||||
import 'package:angular2/src/core/render/api.dart';
|
import 'package:angular2/src/compiler/directive_metadata.dart';
|
||||||
import 'convert.dart';
|
|
||||||
import 'logging.dart';
|
import 'logging.dart';
|
||||||
|
|
||||||
/// Metadata about directives and directive aliases.
|
/// Metadata about directives and directive aliases.
|
||||||
@ -20,37 +19,47 @@ import 'logging.dart';
|
|||||||
/// easier.
|
/// easier.
|
||||||
class NgMeta {
|
class NgMeta {
|
||||||
/// Directive metadata for each type annotated as a directive.
|
/// 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.
|
/// List of other types and names associated with a given name.
|
||||||
final Map<String, List<String>> aliases;
|
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].
|
/// Parse from the serialized form produced by [toJson].
|
||||||
factory NgMeta.fromJson(Map json) {
|
factory NgMeta.fromJson(Map json) {
|
||||||
|
var exports = <String>[];
|
||||||
var types = {};
|
var types = {};
|
||||||
var aliases = {};
|
var aliases = {};
|
||||||
for (var key in json.keys) {
|
for (var key in json.keys) {
|
||||||
var entry = json[key];
|
if (key == '__exports__') {
|
||||||
if (entry['kind'] == 'type') {
|
exports = json[key];
|
||||||
types[key] = directiveMetadataFromMap(entry['value']);
|
} else {
|
||||||
} else if (entry['kind'] == 'alias') {
|
var entry = json[key];
|
||||||
aliases[key] = entry['value'];
|
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.
|
/// Serialized representation of this instance.
|
||||||
Map toJson() {
|
Map toJson() {
|
||||||
var result = {};
|
var result = {};
|
||||||
|
result['__exports__'] = exports;
|
||||||
|
|
||||||
types.forEach((k, v) {
|
types.forEach((k, v) {
|
||||||
result[k] = {'kind': 'type', 'value': directiveMetadataToMap(v)};
|
result[k] = {'kind': 'type', 'value': v.toJson()};
|
||||||
});
|
});
|
||||||
|
|
||||||
aliases.forEach((k, v) {
|
aliases.forEach((k, v) {
|
||||||
@ -60,14 +69,15 @@ class NgMeta {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Merge into this instance all information from [other].
|
/// Merge into this instance all information from [other].
|
||||||
|
/// This does not include `exports`.
|
||||||
void addAll(NgMeta other) {
|
void addAll(NgMeta other) {
|
||||||
types.addAll(other.types);
|
types.addAll(other.types);
|
||||||
aliases.addAll(other.aliases);
|
aliases.addAll(other.aliases);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the metadata for every type associated with the given [alias].
|
/// Returns the metadata for every type associated with the given [alias].
|
||||||
List<RenderDirectiveMetadata> flatten(String alias) {
|
List<CompileDirectiveMetadata> flatten(String alias) {
|
||||||
var result = [];
|
var result = <CompileDirectiveMetadata>[];
|
||||||
var seen = new Set();
|
var seen = new Set();
|
||||||
helper(name) {
|
helper(name) {
|
||||||
if (!seen.add(name)) {
|
if (!seen.add(name)) {
|
||||||
|
@ -11,12 +11,15 @@ const DEFAULT_OPTIMIZATION_PHASES = 5;
|
|||||||
const CUSTOM_ANNOTATIONS_PARAM = 'custom_annotations';
|
const CUSTOM_ANNOTATIONS_PARAM = 'custom_annotations';
|
||||||
const ENTRY_POINT_PARAM = 'entry_points';
|
const ENTRY_POINT_PARAM = 'entry_points';
|
||||||
const FORMAT_CODE_PARAM = 'format_code';
|
const FORMAT_CODE_PARAM = 'format_code';
|
||||||
|
// TODO(kegluenq): Remove this after 30 Oct (i/4433).
|
||||||
const GENERATE_CHANGE_DETECTORS_PARAM = 'generate_change_detectors';
|
const GENERATE_CHANGE_DETECTORS_PARAM = 'generate_change_detectors';
|
||||||
const REFLECT_PROPERTIES_AS_ATTRIBUTES = 'reflectPropertiesAsAttributes';
|
const REFLECT_PROPERTIES_AS_ATTRIBUTES = 'reflectPropertiesAsAttributes';
|
||||||
const INIT_REFLECTOR_PARAM = 'init_reflector';
|
const INIT_REFLECTOR_PARAM = 'init_reflector';
|
||||||
|
// TODO(kegluenq): Remove this after 30 Oct (i/4433).
|
||||||
const INLINE_VIEWS_PARAM = 'inline_views';
|
const INLINE_VIEWS_PARAM = 'inline_views';
|
||||||
const MIRROR_MODE_PARAM = 'mirror_mode';
|
const MIRROR_MODE_PARAM = 'mirror_mode';
|
||||||
const OPTIMIZATION_PHASES_PARAM = 'optimization_phases';
|
const OPTIMIZATION_PHASES_PARAM = 'optimization_phases';
|
||||||
|
const PRECOMPILE_PARAM = 'precompile';
|
||||||
const REFLECTION_ENTRY_POINT_PARAM = 'reflection_entry_points';
|
const REFLECTION_ENTRY_POINT_PARAM = 'reflection_entry_points';
|
||||||
|
|
||||||
/// Provides information necessary to transform an Angular2 app.
|
/// Provides information necessary to transform an Angular2 app.
|
||||||
@ -35,15 +38,9 @@ class TransformerOptions {
|
|||||||
/// Whether to generate calls to our generated `initReflector` code
|
/// Whether to generate calls to our generated `initReflector` code
|
||||||
final bool initReflector;
|
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.
|
/// The [AnnotationMatcher] which is used to identify angular annotations.
|
||||||
final AnnotationMatcher annotationMatcher;
|
final AnnotationMatcher annotationMatcher;
|
||||||
|
|
||||||
/// Whether to create change detector classes for discovered `@View`s.
|
|
||||||
final bool generateChangeDetectors;
|
|
||||||
|
|
||||||
final bool reflectPropertiesAsAttributes;
|
final bool reflectPropertiesAsAttributes;
|
||||||
|
|
||||||
/// The number of phases to spend optimizing output size.
|
/// The number of phases to spend optimizing output size.
|
||||||
@ -68,9 +65,7 @@ class TransformerOptions {
|
|||||||
this.initReflector,
|
this.initReflector,
|
||||||
this.annotationMatcher,
|
this.annotationMatcher,
|
||||||
this.optimizationPhases,
|
this.optimizationPhases,
|
||||||
{this.generateChangeDetectors,
|
{this.reflectPropertiesAsAttributes,
|
||||||
this.reflectPropertiesAsAttributes,
|
|
||||||
this.inlineViews,
|
|
||||||
this.formatCode});
|
this.formatCode});
|
||||||
|
|
||||||
factory TransformerOptions(List<String> entryPoints,
|
factory TransformerOptions(List<String> entryPoints,
|
||||||
@ -80,7 +75,6 @@ class TransformerOptions {
|
|||||||
List<ClassDescriptor> customAnnotationDescriptors: const [],
|
List<ClassDescriptor> customAnnotationDescriptors: const [],
|
||||||
int optimizationPhases: DEFAULT_OPTIMIZATION_PHASES,
|
int optimizationPhases: DEFAULT_OPTIMIZATION_PHASES,
|
||||||
bool inlineViews: true,
|
bool inlineViews: true,
|
||||||
bool generateChangeDetectors: true,
|
|
||||||
bool reflectPropertiesAsAttributes: true,
|
bool reflectPropertiesAsAttributes: true,
|
||||||
bool formatCode: false}) {
|
bool formatCode: false}) {
|
||||||
var annotationMatcher = new AnnotationMatcher()
|
var annotationMatcher = new AnnotationMatcher()
|
||||||
@ -97,9 +91,7 @@ class TransformerOptions {
|
|||||||
initReflector,
|
initReflector,
|
||||||
annotationMatcher,
|
annotationMatcher,
|
||||||
optimizationPhases,
|
optimizationPhases,
|
||||||
generateChangeDetectors: generateChangeDetectors,
|
|
||||||
reflectPropertiesAsAttributes: reflectPropertiesAsAttributes,
|
reflectPropertiesAsAttributes: reflectPropertiesAsAttributes,
|
||||||
inlineViews: inlineViews,
|
|
||||||
formatCode: formatCode);
|
formatCode: formatCode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,9 +11,6 @@ TransformerOptions parseBarbackSettings(BarbackSettings settings) {
|
|||||||
var entryPoints = _readFileList(config, ENTRY_POINT_PARAM);
|
var entryPoints = _readFileList(config, ENTRY_POINT_PARAM);
|
||||||
var initReflector =
|
var initReflector =
|
||||||
_readBool(config, INIT_REFLECTOR_PARAM, defaultValue: true);
|
_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 =
|
var reflectPropertiesAsAttributes =
|
||||||
_readBool(config, REFLECT_PROPERTIES_AS_ATTRIBUTES, defaultValue: false);
|
_readBool(config, REFLECT_PROPERTIES_AS_ATTRIBUTES, defaultValue: false);
|
||||||
var formatCode = _readBool(config, FORMAT_CODE_PARAM, defaultValue: false);
|
var formatCode = _readBool(config, FORMAT_CODE_PARAM, defaultValue: false);
|
||||||
@ -37,10 +34,8 @@ TransformerOptions parseBarbackSettings(BarbackSettings settings) {
|
|||||||
modeName: settings.mode.name,
|
modeName: settings.mode.name,
|
||||||
mirrorMode: mirrorMode,
|
mirrorMode: mirrorMode,
|
||||||
initReflector: initReflector,
|
initReflector: initReflector,
|
||||||
inlineViews: inlineViews,
|
|
||||||
customAnnotationDescriptors: _readCustomAnnotations(config),
|
customAnnotationDescriptors: _readCustomAnnotations(config),
|
||||||
optimizationPhases: optimizationPhases,
|
optimizationPhases: optimizationPhases,
|
||||||
generateChangeDetectors: generateChangeDetectors,
|
|
||||||
reflectPropertiesAsAttributes: reflectPropertiesAsAttributes,
|
reflectPropertiesAsAttributes: reflectPropertiesAsAttributes,
|
||||||
formatCode: formatCode);
|
formatCode: formatCode);
|
||||||
}
|
}
|
||||||
@ -136,4 +131,14 @@ void _warnDeprecated(Map config) {
|
|||||||
print('${REFLECTION_ENTRY_POINT_PARAM} is no longer necessary for '
|
print('${REFLECTION_ENTRY_POINT_PARAM} is no longer necessary for '
|
||||||
'Angular 2 apps. Please remove it from your pubspec.');
|
'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.');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,6 @@ library angular2.transform.common.registered_type;
|
|||||||
|
|
||||||
import 'package:analyzer/analyzer.dart';
|
import 'package:analyzer/analyzer.dart';
|
||||||
import 'package:angular2/src/core/render/api.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';
|
import 'package:angular2/src/transform/common/names.dart';
|
||||||
|
|
||||||
/// A call to `Reflector#registerType` generated by `DirectiveProcessor`.
|
/// A call to `Reflector#registerType` generated by `DirectiveProcessor`.
|
||||||
@ -29,8 +27,6 @@ class RegisteredType {
|
|||||||
/// The property metadata registered.
|
/// The property metadata registered.
|
||||||
final Expression propMetadata;
|
final Expression propMetadata;
|
||||||
|
|
||||||
RenderDirectiveMetadata _directiveMetadata = null;
|
|
||||||
|
|
||||||
RegisteredType._(
|
RegisteredType._(
|
||||||
this.typeName,
|
this.typeName,
|
||||||
this.registerMethod,
|
this.registerMethod,
|
||||||
@ -49,20 +45,7 @@ class RegisteredType {
|
|||||||
visitor.factoryFn, visitor.parameters, visitor.annotations, visitor.propMetadata);
|
visitor.factoryFn, visitor.parameters, visitor.annotations, visitor.propMetadata);
|
||||||
}
|
}
|
||||||
|
|
||||||
RenderDirectiveMetadata get directiveMetadata {
|
RenderDirectiveMetadata get directiveMetadata => null;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class _ParseRegisterTypeVisitor extends Object
|
class _ParseRegisterTypeVisitor extends Object
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
@ -3,29 +3,24 @@ library angular2.transform.template_compiler.xhr_impl;
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'package:angular2/src/core/render/xhr.dart' show XHR;
|
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/asset_reader.dart';
|
||||||
import 'package:angular2/src/transform/common/logging.dart';
|
|
||||||
import 'package:barback/barback.dart';
|
import 'package:barback/barback.dart';
|
||||||
import 'package:code_transformers/assets.dart';
|
|
||||||
|
|
||||||
class XhrImpl implements XHR {
|
class XhrImpl implements XHR {
|
||||||
final AssetReader _reader;
|
final AssetReader _reader;
|
||||||
final AssetId _entryPoint;
|
|
||||||
|
|
||||||
XhrImpl(this._reader, this._entryPoint);
|
XhrImpl(this._reader);
|
||||||
|
|
||||||
Future<String> get(String url) async {
|
Future<String> get(String url) async {
|
||||||
var assetId = uriToAssetId(_entryPoint, url, logger, null /* span */,
|
final uri = Uri.parse(url);
|
||||||
errorOnAbsolute: false);
|
if (uri.scheme != 'asset') {
|
||||||
if (assetId == null) {
|
throw new FormatException('Unsupported uri encountered: $uri', url);
|
||||||
logger.error(
|
|
||||||
'Uri $url not supported from $_entryPoint, could not build AssetId');
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
var templateExists = await _reader.hasInput(assetId);
|
final assetId =
|
||||||
if (!templateExists) {
|
new AssetId(uri.pathSegments.first, uri.pathSegments.skip(1).join('/'));
|
||||||
logger.error('Could not read asset at uri $url from $_entryPoint');
|
|
||||||
return null;
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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));
|
|
||||||
}
|
|
||||||
}
|
|
@ -32,16 +32,16 @@ class DirectiveLinker extends Transformer implements DeclaringTransformer {
|
|||||||
Future apply(Transform transform) async {
|
Future apply(Transform transform) async {
|
||||||
await log.initZoned(transform, () async {
|
await log.initZoned(transform, () async {
|
||||||
var reader = new AssetReader.fromTransform(transform);
|
var reader = new AssetReader.fromTransform(transform);
|
||||||
var assetId = transform.primaryInput.id;
|
var primaryId = transform.primaryInput.id;
|
||||||
var ngDepsModel = await linkNgDeps(reader, assetId);
|
var ngDepsModel = await linkNgDeps(reader, primaryId);
|
||||||
// See above
|
// See above
|
||||||
// transform.consumePrimary();
|
// transform.consumePrimary();
|
||||||
var outputAssetId = _depsAssetId(assetId);
|
var outputAssetId = _depsAssetId(primaryId);
|
||||||
if (ngDepsModel != null) {
|
if (ngDepsModel != null) {
|
||||||
var buf = new StringBuffer();
|
var buf = new StringBuffer();
|
||||||
var writer = new NgDepsWriter(buf);
|
var writer = new NgDepsWriter(buf);
|
||||||
writer.writeNgDepsModel(ngDepsModel);
|
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));
|
transform.addOutput(new Asset.fromString(outputAssetId, formattedCode));
|
||||||
} else {
|
} else {
|
||||||
transform.addOutput(new Asset.fromString(outputAssetId, ''));
|
transform.addOutput(new Asset.fromString(outputAssetId, ''));
|
||||||
|
@ -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;
|
|
||||||
});
|
|
||||||
}
|
|
@ -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));
|
|
||||||
}
|
|
@ -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);
|
||||||
|
}
|
@ -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);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -4,140 +4,102 @@ import 'dart:async';
|
|||||||
|
|
||||||
import 'package:analyzer/analyzer.dart';
|
import 'package:analyzer/analyzer.dart';
|
||||||
import 'package:analyzer/src/generated/ast.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/async_string_writer.dart';
|
||||||
import 'package:angular2/src/transform/common/logging.dart';
|
import 'package:angular2/src/transform/common/logging.dart';
|
||||||
import 'package:angular2/src/transform/common/model/annotation_model.pb.dart';
|
import 'package:code_transformers/assets.dart';
|
||||||
import 'package:angular2/src/transform/common/model/ng_deps_model.pb.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) {
|
/// Reads the code at `assetId`, inlining any `part` directives in that code.
|
||||||
var toWait = <Future>[];
|
///
|
||||||
for (var reflectable in model.reflectables) {
|
/// Returns `null` if the code represented by `assetId` is a `part`.
|
||||||
for (var annotation in reflectable.annotations) {
|
///
|
||||||
if (annotation.isView) {
|
/// Order of `part`s is preserved. That is, if in the main library we have
|
||||||
var rawTemplateUrl = _getTemplateUrlValue(annotation);
|
/// ```
|
||||||
if (rawTemplateUrl != null) {
|
/// library main;
|
||||||
if (_hasTemplateValue(annotation)) {
|
///
|
||||||
logger.warning('Both template url and template are specified. '
|
/// part 'lib1.dart'
|
||||||
'Ignoring `templateUrl` value.');
|
/// part 'lib2.dart'
|
||||||
} else {
|
/// ```
|
||||||
var url = _dumbEval(rawTemplateUrl);
|
/// The output will first have the contents of lib1 followed by the contents of
|
||||||
if (url is String) {
|
/// lib2.dart, followed by the original code in the library.
|
||||||
toWait.add(_readOrEmptyString(xhr, url).then((templateText) {
|
Future<String> inlineParts(AssetReader reader, AssetId assetId) async {
|
||||||
_setTemplateValue(annotation, "r'''$templateText'''");
|
var code = await reader.readAsString(assetId);
|
||||||
}));
|
|
||||||
} else {
|
var directivesVisitor = new _NgDepsDirectivesVisitor();
|
||||||
logger.warning('template url is not a String ($rawTemplateUrl)');
|
parseDirectives(code, name: assetId.path)
|
||||||
}
|
.directives
|
||||||
}
|
.accept(directivesVisitor);
|
||||||
}
|
|
||||||
var rawStyleUrls = _getStyleUrlsValue(annotation);
|
// If this is part of another library, its contents will be processed by its
|
||||||
if (rawStyleUrls != null) {
|
// parent, so it does not need its own `.ng_deps.dart` file.
|
||||||
if (_hasStylesValue(annotation)) {
|
if (directivesVisitor.isPart) return null;
|
||||||
logger.warning('Both styleUrls and styles are specified. '
|
|
||||||
'Ignoring `styleUrls` value.');
|
return _getAllDeclarations(reader, assetId, code, directivesVisitor);
|
||||||
} else {
|
}
|
||||||
var urls = _dumbEval(rawStyleUrls);
|
|
||||||
if (urls is List) {
|
/// Processes `visitor.parts`, reading and appending their contents to the
|
||||||
var writer = new AsyncStringWriter();
|
/// original `code`.
|
||||||
for (var url in urls) {
|
Future<String> _getAllDeclarations(AssetReader reader, AssetId assetId,
|
||||||
if (url is String) {
|
String code, _NgDepsDirectivesVisitor visitor) {
|
||||||
writer.print("r'''");
|
if (visitor.parts.isEmpty) return new Future<String>.value(code);
|
||||||
writer.asyncPrint(_readOrEmptyString(xhr, url));
|
|
||||||
writer.print("''', ");
|
var partsStart = visitor.parts.first.offset,
|
||||||
} else {
|
partsEnd = visitor.parts.last.end;
|
||||||
logger.warning('style url is not a String (${url})');
|
|
||||||
}
|
var asyncWriter = new AsyncStringWriter(code.substring(0, partsStart));
|
||||||
}
|
visitor.parts.forEach((partDirective) {
|
||||||
toWait.add(writer.asyncToString().then((styleUrlText) {
|
var uri = stringLiteralToString(partDirective.uri);
|
||||||
_setStylesValue(annotation, 'const [$styleUrlText]');
|
var partAssetId = uriToAssetId(assetId, uri, logger, null /* span */,
|
||||||
_removeStyleUrlsValue(annotation);
|
errorOnAbsolute: false);
|
||||||
}));
|
asyncWriter.asyncPrint(reader.readAsString(partAssetId).then((partCode) {
|
||||||
} else {
|
if (partCode == null || partCode.isEmpty) {
|
||||||
logger.warning(
|
logger.warning('Empty part at "${partDirective.uri}. Ignoring.',
|
||||||
'styleUrls is not a List of strings ($rawStyleUrls)');
|
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) {
|
@override
|
||||||
var value = null;
|
Object visitPartOfDirective(PartOfDirective node) {
|
||||||
if (model.namedParameters != null) {
|
_isPart = true;
|
||||||
var match = model.namedParameters
|
return null;
|
||||||
.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;
|
|
||||||
}
|
}
|
||||||
|
@ -5,16 +5,15 @@ import 'dart:async';
|
|||||||
import 'package:analyzer/analyzer.dart';
|
import 'package:analyzer/analyzer.dart';
|
||||||
import 'package:angular2/src/transform/common/annotation_matcher.dart';
|
import 'package:angular2/src/transform/common/annotation_matcher.dart';
|
||||||
import 'package:angular2/src/transform/common/asset_reader.dart';
|
import 'package:angular2/src/transform/common/asset_reader.dart';
|
||||||
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/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/logging.dart';
|
||||||
import 'package:angular2/src/transform/common/model/ng_deps_model.pb.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:angular2/src/transform/common/ng_meta.dart';
|
||||||
import 'package:barback/barback.dart' show AssetId;
|
import 'package:barback/barback.dart' show AssetId;
|
||||||
import 'package:code_transformers/assets.dart';
|
import 'package:angular2/src/compiler/template_compiler.dart';
|
||||||
import 'package:path/path.dart' as path;
|
|
||||||
import 'package:source_span/source_span.dart';
|
|
||||||
|
|
||||||
import 'inliner.dart';
|
import 'inliner.dart';
|
||||||
|
|
||||||
@ -26,136 +25,44 @@ import 'inliner.dart';
|
|||||||
/// string unless `forceGenerate` is true, in which case an empty ngDeps
|
/// string unless `forceGenerate` is true, in which case an empty ngDeps
|
||||||
/// file is created.
|
/// file is created.
|
||||||
Future<NgDepsModel> createNgDeps(AssetReader reader, AssetId assetId,
|
Future<NgDepsModel> createNgDeps(AssetReader reader, AssetId assetId,
|
||||||
AnnotationMatcher annotationMatcher, NgMeta ngMeta,
|
AnnotationMatcher annotationMatcher, NgMeta ngMeta) async {
|
||||||
{bool inlineViews}) async {
|
|
||||||
// TODO(kegluneq): Shortcut if we can determine that there are no
|
// TODO(kegluneq): Shortcut if we can determine that there are no
|
||||||
// [Directive]s present, taking into account `export`s.
|
// [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 =
|
var parsedCode =
|
||||||
parseCompilationUnit(codeWithParts, name: '${assetId.path} and parts');
|
parseCompilationUnit(codeWithParts, name: '${assetId.path} and parts');
|
||||||
|
|
||||||
var ngDepsVisitor = new NgDepsVisitor(assetId, annotationMatcher);
|
var ngDepsVisitor = new NgDepsVisitor(assetId, annotationMatcher);
|
||||||
// TODO(kegluneq): Parse `declarations` in the NgDepsModel as well.
|
|
||||||
parsedCode.accept(ngDepsVisitor);
|
parsedCode.accept(ngDepsVisitor);
|
||||||
var ngDepsModel = ngDepsVisitor.model;
|
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);
|
parsedCode.accept(ngMetaVisitor);
|
||||||
|
await ngMetaVisitor.whenDone();
|
||||||
|
|
||||||
// If this file imports only dart: libraries and does not define any
|
// 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.
|
// reflectables of its own, it doesn't need a .ng_deps.dart file.
|
||||||
if (!directivesVisitor.usesNonLangLibs &&
|
if (ngDepsModel.reflectables == null || ngDepsModel.reflectables.isEmpty) {
|
||||||
(ngDepsModel.reflectables == null || ngDepsModel.reflectables.isEmpty)) {
|
if (ngDepsModel.imports.every(_isDartImport) &&
|
||||||
return null;
|
ngDepsModel.exports.every(_isDartImport)) {
|
||||||
}
|
return null;
|
||||||
|
}
|
||||||
if (inlineViews) {
|
|
||||||
await inlineViewProps(new XhrImpl(reader, assetId), ngDepsModel);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ngDepsModel;
|
return ngDepsModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Processes `visitor.parts`, reading and appending their contents to the
|
// `model` can be an [ImportModel] or [ExportModel].
|
||||||
/// original `code`.
|
bool _isDartImport(dynamic model) => model.uri.startsWith('dart:');
|
||||||
/// 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);
|
|
||||||
|
|
||||||
var partsStart = visitor.parts.first.offset,
|
// TODO(kegluneq): Allow the caller to provide an InterfaceMatcher.
|
||||||
partsEnd = visitor.parts.last.end;
|
final _interfaceMatcher = new InterfaceMatcher();
|
||||||
|
|
||||||
var asyncWriter = new AsyncStringWriter(code.substring(0, partsStart));
|
/// Visitor responsible for visiting a file and outputting the
|
||||||
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
|
|
||||||
/// code necessary to register the file with the Angular 2 system.
|
/// code necessary to register the file with the Angular 2 system.
|
||||||
class _NgMetaVisitor extends Object with SimpleAstVisitor<Object> {
|
class _NgMetaVisitor extends Object with SimpleAstVisitor<Object> {
|
||||||
/// Output ngMeta information about aliases.
|
/// Output ngMeta information about aliases.
|
||||||
@ -164,14 +71,52 @@ class _NgMetaVisitor extends Object with SimpleAstVisitor<Object> {
|
|||||||
// parsing the ngdeps files later.
|
// parsing the ngdeps files later.
|
||||||
final NgMeta ngMeta;
|
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
|
@override
|
||||||
Object visitCompilationUnit(CompilationUnit node) {
|
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);
|
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
|
@override
|
||||||
Object visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) {
|
Object visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) {
|
||||||
// We process any top-level declaration that fits the directive-alias
|
// We process any top-level declaration that fits the directive-alias
|
||||||
|
@ -3,6 +3,7 @@ library angular2.transform.directive_processor.transformer;
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:convert';
|
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/asset_reader.dart';
|
||||||
import 'package:angular2/src/transform/common/logging.dart' as log;
|
import 'package:angular2/src/transform/common/logging.dart' as log;
|
||||||
import 'package:angular2/src/transform/common/names.dart';
|
import 'package:angular2/src/transform/common/names.dart';
|
||||||
@ -23,6 +24,7 @@ import 'rewriter.dart';
|
|||||||
/// be followed by {@link DirectiveLinker}.
|
/// be followed by {@link DirectiveLinker}.
|
||||||
class DirectiveProcessor extends Transformer implements DeclaringTransformer {
|
class DirectiveProcessor extends Transformer implements DeclaringTransformer {
|
||||||
final TransformerOptions options;
|
final TransformerOptions options;
|
||||||
|
final _encoder = const JsonEncoder.withIndent(' ');
|
||||||
|
|
||||||
DirectiveProcessor(this.options);
|
DirectiveProcessor(this.options);
|
||||||
|
|
||||||
@ -34,33 +36,40 @@ class DirectiveProcessor extends Transformer implements DeclaringTransformer {
|
|||||||
/// determine that one or the other will not be emitted.
|
/// determine that one or the other will not be emitted.
|
||||||
@override
|
@override
|
||||||
declareOutputs(DeclaringTransform transform) {
|
declareOutputs(DeclaringTransform transform) {
|
||||||
transform.declareOutput(_depsOutputId(transform.primaryId));
|
transform.declareOutput(_ngMetaAssetId(transform.primaryId));
|
||||||
transform.declareOutput(_metaOutputId(transform.primaryId));
|
transform.declareOutput(_ngDepsAssetId(transform.primaryId));
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future apply(Transform transform) async {
|
Future apply(Transform transform) async {
|
||||||
|
Html5LibDomAdapter.makeCurrent();
|
||||||
await log.initZoned(transform, () async {
|
await log.initZoned(transform, () async {
|
||||||
var assetId = transform.primaryInput.id;
|
var primaryId = transform.primaryInput.id;
|
||||||
var reader = new AssetReader.fromTransform(transform);
|
var reader = new AssetReader.fromTransform(transform);
|
||||||
var ngMeta = new NgMeta.empty();
|
var ngMeta = new NgMeta.empty();
|
||||||
var ngDepsModel = await createNgDeps(
|
var ngDepsModel = await createNgDeps(
|
||||||
reader, assetId, options.annotationMatcher, ngMeta,
|
reader, primaryId, options.annotationMatcher, ngMeta);
|
||||||
inlineViews: options.inlineViews);
|
// TODO(kegluneq): Combine NgDepsModel with NgMeta in a single .json file.
|
||||||
if (ngDepsModel != null) {
|
if (ngDepsModel != null) {
|
||||||
|
var ngDepsAssetId = _ngDepsAssetId(primaryId);
|
||||||
transform.addOutput(new Asset.fromString(
|
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) {
|
if (!ngMeta.isEmpty) {
|
||||||
transform.addOutput(new Asset.fromString(metaOutputId,
|
transform.addOutput(new Asset.fromString(
|
||||||
new JsonEncoder.withIndent(" ").convert(ngMeta.toJson())));
|
metaOutputId, _encoder.convert(ngMeta.toJson())));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AssetId _depsOutputId(AssetId primaryId) =>
|
AssetId _ngMetaAssetId(AssetId primaryInputId) {
|
||||||
primaryId.changeExtension(DEPS_JSON_EXTENSION);
|
return new AssetId(
|
||||||
AssetId _metaOutputId(AssetId primaryId) =>
|
primaryInputId.package, toMetaExtension(primaryInputId.path));
|
||||||
primaryId.changeExtension(ALIAS_EXTENSION);
|
}
|
||||||
|
|
||||||
|
AssetId _ngDepsAssetId(AssetId primaryInputId) {
|
||||||
|
return new AssetId(
|
||||||
|
primaryInputId.package, toJsonExtension(primaryInputId.path));
|
||||||
|
}
|
||||||
|
151
modules_dart/transform/lib/src/transform/inliner_for_test.dart
Normal file
151
modules_dart/transform/lib/src/transform/inliner_for_test.dart
Normal 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;
|
||||||
|
}
|
@ -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/proto_record.dart';
|
||||||
import 'package:angular2/src/core/change_detection/event_binding.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/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;
|
import 'package:angular2/src/core/facade/exceptions.dart' show BaseException;
|
||||||
|
|
||||||
/// Responsible for generating change detector classes for Angular 2.
|
/// Responsible for generating change detector classes for Angular 2.
|
||||||
@ -90,8 +91,9 @@ class _CodegenState {
|
|||||||
final ChangeDetectorGenConfig _genConfig;
|
final ChangeDetectorGenConfig _genConfig;
|
||||||
final List<BindingTarget> _propertyBindingTargets;
|
final List<BindingTarget> _propertyBindingTargets;
|
||||||
|
|
||||||
String get _changeDetectionStrategyAsCode =>
|
String get _changeDetectionStrategyAsCode => _changeDetectionStrategy == null
|
||||||
_changeDetectionStrategy == null ? 'null' : '${_genPrefix}${_changeDetectionStrategy}';
|
? 'null'
|
||||||
|
: '${_genPrefix}${_changeDetectionStrategy}';
|
||||||
|
|
||||||
/// The module prefix for pregen_proto_change_detector
|
/// The module prefix for pregen_proto_change_detector
|
||||||
final String _genPrefix;
|
final String _genPrefix;
|
||||||
@ -110,13 +112,15 @@ class _CodegenState {
|
|||||||
this._names,
|
this._names,
|
||||||
this._genConfig);
|
this._genConfig);
|
||||||
|
|
||||||
factory _CodegenState(String genPrefix, String typeName, String changeDetectorTypeName,
|
factory _CodegenState(String genPrefix, String typeName,
|
||||||
ChangeDetectorDefinition def) {
|
String changeDetectorTypeName, ChangeDetectorDefinition def) {
|
||||||
var protoRecords = createPropertyRecords(def);
|
var protoRecords = createPropertyRecords(def);
|
||||||
var eventBindings = createEventRecords(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);
|
var logic = new CodegenLogicUtil(names, '$genPrefix$_UTIL', def.strategy);
|
||||||
return new _CodegenState._(
|
return new _CodegenState._(
|
||||||
genPrefix,
|
genPrefix,
|
||||||
@ -141,8 +145,8 @@ class _CodegenState {
|
|||||||
$_changeDetectorTypeName(dispatcher)
|
$_changeDetectorTypeName(dispatcher)
|
||||||
: super(${codify(_changeDetectorDefId)},
|
: super(${codify(_changeDetectorDefId)},
|
||||||
dispatcher, ${_records.length},
|
dispatcher, ${_records.length},
|
||||||
${_changeDetectorTypeName}.gen_propertyBindingTargets,
|
${_changeDetectorTypeName}.${_GEN_PROPERTY_BINDING_TARGETS_NAME},
|
||||||
${_changeDetectorTypeName}.gen_directiveIndices,
|
${_changeDetectorTypeName}.${_GEN_DIRECTIVE_INDICES_NAME},
|
||||||
${_changeDetectionStrategyAsCode}) {
|
${_changeDetectionStrategyAsCode}) {
|
||||||
dehydrateDirectives(false);
|
dehydrateDirectives(false);
|
||||||
}
|
}
|
||||||
@ -183,18 +187,20 @@ class _CodegenState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String _genPropertyBindingTargets() {
|
String _genPropertyBindingTargets() {
|
||||||
var targets = _logic.genPropertyBindingTargets(_propertyBindingTargets, this._genConfig.genDebugInfo);
|
var targets = _logic.genPropertyBindingTargets(
|
||||||
return "static var gen_propertyBindingTargets = ${targets}";
|
_propertyBindingTargets, this._genConfig.genDebugInfo);
|
||||||
|
return "static final ${_GEN_PROPERTY_BINDING_TARGETS_NAME} = ${targets}";
|
||||||
}
|
}
|
||||||
|
|
||||||
String _genDirectiveIndices() {
|
String _genDirectiveIndices() {
|
||||||
var indices = _logic.genDirectiveIndices(_directiveRecords);
|
var indices = _logic.genDirectiveIndices(_directiveRecords);
|
||||||
return "static var gen_directiveIndices = ${indices}";
|
return "static final ${_GEN_DIRECTIVE_INDICES_NAME} = ${indices}";
|
||||||
}
|
}
|
||||||
|
|
||||||
String _maybeGenHandleEventInternal() {
|
String _maybeGenHandleEventInternal() {
|
||||||
if (_eventBindings.length > 0) {
|
if (_eventBindings.length > 0) {
|
||||||
var handlers = _eventBindings.map((eb) => _genEventBinding(eb)).join("\n");
|
var handlers =
|
||||||
|
_eventBindings.map((eb) => _genEventBinding(eb)).join("\n");
|
||||||
return '''
|
return '''
|
||||||
handleEventInternal(eventName, elIndex, locals) {
|
handleEventInternal(eventName, elIndex, locals) {
|
||||||
var ${this._names.getPreventDefaultAccesor()} = false;
|
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) {
|
if (r.lastInBinding) {
|
||||||
var evalRecord = _logic.genEventBindingEvalValue(eb, r);
|
var evalRecord = _logic.genEventBindingEvalValue(eb, r);
|
||||||
var markPath = _genMarkPathToRootAsCheckOnce(r);
|
var markPath = _genMarkPathToRootAsCheckOnce(r);
|
||||||
@ -271,7 +277,8 @@ class _CodegenState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String _maybeGenAfterContentLifecycleCallbacks() {
|
String _maybeGenAfterContentLifecycleCallbacks() {
|
||||||
var directiveNotifications = _logic.genContentLifecycleCallbacks(_directiveRecords);
|
var directiveNotifications =
|
||||||
|
_logic.genContentLifecycleCallbacks(_directiveRecords);
|
||||||
if (directiveNotifications.isNotEmpty) {
|
if (directiveNotifications.isNotEmpty) {
|
||||||
return '''
|
return '''
|
||||||
void afterContentLifecycleCallbacksInternal() {
|
void afterContentLifecycleCallbacksInternal() {
|
||||||
@ -284,7 +291,8 @@ class _CodegenState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String _maybeGenAfterViewLifecycleCallbacks() {
|
String _maybeGenAfterViewLifecycleCallbacks() {
|
||||||
var directiveNotifications = _logic.genViewLifecycleCallbacks(_directiveRecords);
|
var directiveNotifications =
|
||||||
|
_logic.genViewLifecycleCallbacks(_directiveRecords);
|
||||||
if (directiveNotifications.isNotEmpty) {
|
if (directiveNotifications.isNotEmpty) {
|
||||||
return '''
|
return '''
|
||||||
void afterViewLifecycleCallbacksInternal() {
|
void afterViewLifecycleCallbacksInternal() {
|
||||||
@ -420,7 +428,9 @@ class _CodegenState {
|
|||||||
|
|
||||||
var newValue = _names.getLocalName(r.selfIndex);
|
var newValue = _names.getLocalName(r.selfIndex);
|
||||||
var oldValue = _names.getFieldName(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;
|
var br = r.bindingRecord;
|
||||||
if (br.target.isDirective()) {
|
if (br.target.isDirective()) {
|
||||||
@ -527,4 +537,7 @@ const _NOT_IDENTICAL_CHECK_FN = 'looseNotIdentical';
|
|||||||
const _IS_CHANGED_LOCAL = 'isChanged';
|
const _IS_CHANGED_LOCAL = 'isChanged';
|
||||||
const _PREGEN_PROTO_CHANGE_DETECTOR_IMPORT =
|
const _PREGEN_PROTO_CHANGE_DETECTOR_IMPORT =
|
||||||
'package:angular2/src/core/change_detection/pregen_proto_change_detector.dart';
|
'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';
|
const _UTIL = 'ChangeDetectionUtil';
|
||||||
|
@ -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:async';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:analyzer/analyzer.dart';
|
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/core/render/api.dart';
|
||||||
import 'package:angular2/src/transform/common/asset_reader.dart';
|
import 'package:angular2/src/transform/common/asset_reader.dart';
|
||||||
import 'package:angular2/src/transform/common/logging.dart';
|
import 'package:angular2/src/transform/common/logging.dart';
|
||||||
@ -13,28 +15,24 @@ import 'package:angular2/src/transform/common/ng_meta.dart';
|
|||||||
import 'package:barback/barback.dart';
|
import 'package:barback/barback.dart';
|
||||||
import 'package:code_transformers/assets.dart';
|
import 'package:code_transformers/assets.dart';
|
||||||
|
|
||||||
/// Creates [ViewDefinition] objects for all `View` `Directive`s defined in
|
/// Creates [NormalizedComponentWithViewDirectives] objects for all `View`
|
||||||
/// `entryPoint`.
|
/// `Directive`s defined in `entryPoint`.
|
||||||
Future<ViewDefinitionResults> createViewDefinitions(
|
///
|
||||||
|
/// The returned value wraps the [NgDeps] at `entryPoint` as well as these
|
||||||
|
/// created objects.
|
||||||
|
Future<CompileDataResults> createCompileData(
|
||||||
AssetReader reader, AssetId entryPoint) async {
|
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 NgDeps ngDeps;
|
||||||
final Map<RegisteredType, ViewDefinitionEntry> viewDefinitions;
|
final Map<RegisteredType,
|
||||||
ViewDefinitionResults._(this.ngDeps, this.viewDefinitions);
|
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.
|
// TODO(kegluenq): Improve this test.
|
||||||
bool _isViewAnnotation(InstanceCreationExpression node) {
|
bool _isViewAnnotation(InstanceCreationExpression node) {
|
||||||
var constructorName = node.constructorName.type.name;
|
var constructorName = node.constructorName.type.name;
|
||||||
@ -46,42 +44,38 @@ bool _isViewAnnotation(InstanceCreationExpression node) {
|
|||||||
|
|
||||||
/// Creates [ViewDefinition] objects for all `View` `Directive`s defined in
|
/// Creates [ViewDefinition] objects for all `View` `Directive`s defined in
|
||||||
/// `entryPoint`.
|
/// `entryPoint`.
|
||||||
class _ViewDefinitionCreator {
|
class _CompileDataCreator {
|
||||||
final AssetReader reader;
|
final AssetReader reader;
|
||||||
final AssetId entryPoint;
|
final AssetId entryPoint;
|
||||||
final Future<NgDeps> ngDepsFuture;
|
final Future<NgDeps> ngDepsFuture;
|
||||||
|
|
||||||
_ViewDefinitionCreator(AssetReader reader, AssetId entryPoint)
|
_CompileDataCreator(AssetReader reader, AssetId entryPoint)
|
||||||
: this.reader = reader,
|
: this.reader = reader,
|
||||||
this.entryPoint = entryPoint,
|
this.entryPoint = entryPoint,
|
||||||
ngDepsFuture = NgDeps.parse(reader, entryPoint);
|
ngDepsFuture = NgDeps.parse(reader, entryPoint);
|
||||||
|
|
||||||
Future<ViewDefinitionResults> createViewDefs() async {
|
Future<CompileDataResults> createCompileData() async {
|
||||||
var ngDeps = await ngDepsFuture;
|
var ngDeps = await ngDepsFuture;
|
||||||
|
|
||||||
var retVal = <RegisteredType, ViewDefinitionEntry>{};
|
var retVal = <RegisteredType, NormalizedComponentWithViewDirectives>{};
|
||||||
var visitor = new _TemplateExtractVisitor(await _extractNgMeta());
|
var visitor = new _DirectiveDependenciesVisitor(await _extractNgMeta());
|
||||||
ngDeps.registeredTypes.forEach((rType) {
|
ngDeps.registeredTypes.forEach((rType) {
|
||||||
visitor.reset();
|
visitor.reset();
|
||||||
rType.annotations.accept(visitor);
|
rType.annotations.accept(visitor);
|
||||||
if (visitor.viewDef != null) {
|
if (visitor.compileData != null) {
|
||||||
// Note: we use '' because the current file maps to the default prefix.
|
// Note: we use '' because the current file maps to the default prefix.
|
||||||
var ngMeta = visitor._metadataMap[''];
|
var ngMeta = visitor._metadataMap[''];
|
||||||
var typeName = '${rType.typeName}';
|
var typeName = '${rType.typeName}';
|
||||||
var hostMetadata = null;
|
|
||||||
if (ngMeta.types.containsKey(typeName)) {
|
if (ngMeta.types.containsKey(typeName)) {
|
||||||
hostMetadata = ngMeta.types[typeName];
|
visitor.compileData.component = ngMeta.types[typeName];
|
||||||
visitor.viewDef.componentId = hostMetadata.id;
|
|
||||||
} else {
|
} else {
|
||||||
logger.warning('Missing component "$typeName" in metadata map',
|
logger.warning('Missing component "$typeName" in metadata map',
|
||||||
asset: entryPoint);
|
asset: entryPoint);
|
||||||
visitor.viewDef.componentId = _getComponentId(entryPoint, typeName);
|
|
||||||
}
|
}
|
||||||
retVal[rType] =
|
retVal[rType] = visitor.compileData;
|
||||||
new ViewDefinitionEntry._(hostMetadata, visitor.viewDef);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return new ViewDefinitionResults._(ngDeps, retVal);
|
return new CompileDataResults._(ngDeps, retVal);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a map from [AssetId] to import prefix for `.dart` libraries
|
/// 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
|
/// Unprefixed imports have `null` as their value. `entryPoint` is included
|
||||||
/// in the map with no prefix.
|
/// in the map with no prefix.
|
||||||
Future<Map<AssetId, String>> _createImportAssetToPrefixMap() async {
|
Future<Map<AssetId, String>> _createImportAssetToPrefixMap() async {
|
||||||
// TODO(kegluneq): Support `part` directives.
|
|
||||||
var ngDeps = await ngDepsFuture;
|
var ngDeps = await ngDepsFuture;
|
||||||
|
|
||||||
var importAssetToPrefix = <AssetId, String>{};
|
var importAssetToPrefix = <AssetId, String>{
|
||||||
// Include the `.ng_meta.json` file associated with `entryPoint`.
|
entryPoint: null
|
||||||
importAssetToPrefix[new AssetId(
|
};
|
||||||
entryPoint.package, toMetaExtension(entryPoint.path))] = null;
|
|
||||||
|
|
||||||
for (ImportDirective node in ngDeps.imports) {
|
for (ImportDirective node in ngDeps.imports) {
|
||||||
var uri = stringLiteralToString(node.uri);
|
var uri = stringLiteralToString(node.uri);
|
||||||
@ -113,7 +105,7 @@ class _ViewDefinitionCreator {
|
|||||||
|
|
||||||
/// Reads the `.ng_meta.json` files associated with all of `entryPoint`'s
|
/// Reads the `.ng_meta.json` files associated with all of `entryPoint`'s
|
||||||
/// imports and creates a map `Type` name, prefixed if appropriate to the
|
/// imports and creates a map `Type` name, prefixed if appropriate to the
|
||||||
/// associated [RenderDirectiveMetadata].
|
/// associated [CompileDirectiveMetadata].
|
||||||
///
|
///
|
||||||
/// For example, if in `entryPoint` we have:
|
/// 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
|
/// 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
|
/// `Directive`s declared in `component.dart`. We use this information to
|
||||||
/// build a map:
|
/// build a map:
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// {
|
/// {
|
||||||
/// "prefix.MyComponent": [RenderDirectiveMetadata for MyComponent],
|
/// "prefix.MyComponent": [CompileDirectiveMetadata for MyComponent],
|
||||||
/// ...<any other entries>...
|
/// ...<any other entries>...
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
@ -153,9 +145,6 @@ class _ViewDefinitionCreator {
|
|||||||
try {
|
try {
|
||||||
var json = JSON.decode(await reader.readAsString(metaAssetId));
|
var json = JSON.decode(await reader.readAsString(metaAssetId));
|
||||||
var newMetadata = new NgMeta.fromJson(json);
|
var newMetadata = new NgMeta.fromJson(json);
|
||||||
newMetadata.types.forEach((className, metadata) {
|
|
||||||
metadata.id = _getComponentId(importAssetId, className);
|
|
||||||
});
|
|
||||||
ngMeta.addAll(newMetadata);
|
ngMeta.addAll(newMetadata);
|
||||||
} catch (ex, stackTrace) {
|
} catch (ex, stackTrace) {
|
||||||
logger.warning('Failed to decode: $ex, $stackTrace',
|
logger.warning('Failed to decode: $ex, $stackTrace',
|
||||||
@ -168,23 +157,33 @@ class _ViewDefinitionCreator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Visitor responsible for processing the `annotations` property of a
|
/// Visitor responsible for processing the `annotations` property of a
|
||||||
/// [RegisterType] object and pulling out [ViewDefinition] information.
|
/// [RegisterType] object, extracting the `directives` dependencies, and adding
|
||||||
class _TemplateExtractVisitor extends Object with RecursiveAstVisitor<Object> {
|
/// their associated [CompileDirectiveMetadata] to the `directives` of a
|
||||||
ViewDefinition viewDef = null;
|
/// 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 Map<String, NgMeta> _metadataMap;
|
||||||
final ConstantEvaluator _evaluator = new ConstantEvaluator();
|
|
||||||
|
|
||||||
_TemplateExtractVisitor(this._metadataMap);
|
_DirectiveDependenciesVisitor(this._metadataMap);
|
||||||
|
|
||||||
void reset() {
|
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
|
@override
|
||||||
Object visitInstanceCreationExpression(InstanceCreationExpression node) {
|
Object visitInstanceCreationExpression(InstanceCreationExpression node) {
|
||||||
if (_isViewAnnotation(node)) {
|
if (_isViewAnnotation(node)) {
|
||||||
viewDef = new ViewDefinition(directives: <RenderDirectiveMetadata>[]);
|
compileData = new NormalizedComponentWithViewDirectives(
|
||||||
|
null, <CompileDirectiveMetadata>[]);
|
||||||
node.visitChildren(this);
|
node.visitChildren(this);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
@ -200,43 +199,16 @@ class _TemplateExtractVisitor extends Object with RecursiveAstVisitor<Object> {
|
|||||||
' Source: ${node}');
|
' Source: ${node}');
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
var keyString = '${node.name.label}';
|
if ('${node.name.label}' == 'directives') {
|
||||||
if (keyString == 'directives') {
|
|
||||||
_readDirectives(node.expression);
|
_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;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
void _readDirectives(Expression node) {
|
void _readDirectives(Expression node) {
|
||||||
// This could happen in a non-View annotation with a `directives`
|
// This could happen in a non-View annotation with a `directives`
|
||||||
// parameter.
|
// parameter.
|
||||||
if (viewDef == null) return;
|
if (compileData == null) return;
|
||||||
|
|
||||||
if (node is! ListLiteral) {
|
if (node is! ListLiteral) {
|
||||||
logger.error('Angular 2 currently only supports list literals as values '
|
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;
|
name = node.name;
|
||||||
} else if (node is PrefixedIdentifier) {
|
} else if (node is PrefixedIdentifier) {
|
||||||
ngMeta = _metadataMap[node.prefix.name];
|
ngMeta = _metadataMap[node.prefix.name];
|
||||||
name = node.name;
|
name = node.identifier.name;
|
||||||
} else {
|
} else {
|
||||||
logger.error(
|
logger.error(
|
||||||
'Angular 2 currently only supports simple and prefixed identifiers '
|
'Angular 2 currently only supports simple and prefixed identifiers '
|
||||||
@ -260,9 +232,9 @@ class _TemplateExtractVisitor extends Object with RecursiveAstVisitor<Object> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (ngMeta.types.containsKey(name)) {
|
if (ngMeta.types.containsKey(name)) {
|
||||||
viewDef.directives.add(ngMeta.types[name]);
|
compileData.directives.add(ngMeta.types[name]);
|
||||||
} else if (ngMeta.aliases.containsKey(name)) {
|
} else if (ngMeta.aliases.containsKey(name)) {
|
||||||
viewDef.directives.addAll(ngMeta.flatten(name));
|
compileData.directives.addAll(ngMeta.flatten(name));
|
||||||
} else {
|
} else {
|
||||||
logger.warning('Could not find Directive entry for $node. '
|
logger.warning('Could not find Directive entry for $node. '
|
||||||
'Please be aware that Dart transformers have limited support for '
|
'Please be aware that Dart transformers have limited support for '
|
@ -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)
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
@ -2,138 +2,135 @@ library angular2.transform.template_compiler.generator;
|
|||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:angular2/src/core/change_detection/parser/lexer.dart' as ng;
|
import 'package:analyzer/analyzer.dart';
|
||||||
import 'package:angular2/src/core/change_detection/parser/parser.dart' as ng;
|
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/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/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: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/codegen.dart' as reg;
|
||||||
import 'reflection/processor.dart' as reg;
|
import 'reflection/processor.dart' as reg;
|
||||||
import 'reflection/reflection_capabilities.dart';
|
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
|
/// Reads the `.ng_deps.dart` file represented by `entryPoint` and parses any
|
||||||
/// Angular 2 `View` annotations it declares to generate `getter`s,
|
/// Angular 2 `View` annotations it declares to generate `getter`s,
|
||||||
/// `setter`s, and `method`s that would otherwise be reflectively accessed.
|
/// `setter`s, and `method`s that would otherwise be reflectively accessed.
|
||||||
///
|
///
|
||||||
/// This method assumes a {@link DomAdapter} has been registered.
|
/// This method assumes a {@link DomAdapter} has been registered.
|
||||||
Future<String> processTemplates(AssetReader reader, AssetId entryPoint,
|
Future<Outputs> processTemplates(AssetReader reader, AssetId entryPoint,
|
||||||
{bool generateRegistrations: true,
|
{bool reflectPropertiesAsAttributes: false}) async {
|
||||||
bool generateChangeDetectors: true, bool reflectPropertiesAsAttributes: false}) async {
|
var viewDefResults = await createCompileData(reader, entryPoint);
|
||||||
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));
|
|
||||||
|
|
||||||
final processor = new reg.Processor();
|
var templateCompiler = createTemplateCompiler(reader,
|
||||||
|
changeDetectionConfig: new ChangeDetectorGenConfig(assertionsEnabled(),
|
||||||
|
assertionsEnabled(), reflectPropertiesAsAttributes, false));
|
||||||
|
|
||||||
var changeDetectorClasses = new change.Codegen();
|
var ngDeps = viewDefResults.ngDeps;
|
||||||
for (var rType in viewDefResults.viewDefinitions.keys) {
|
var compileData =
|
||||||
var viewDefEntry = viewDefResults.viewDefinitions[rType];
|
viewDefResults.viewDefinitions.values.toList(growable: false);
|
||||||
var protoView = await extractor.extractTemplates(viewDefEntry.viewDef);
|
if (compileData.isEmpty) {
|
||||||
if (protoView == null) continue;
|
return new Outputs(entryPoint, ngDeps, null, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
if (generateRegistrations) {
|
var savedReflectionCapabilities = reflector.reflectionCapabilities;
|
||||||
processor.process(viewDefEntry, protoView);
|
reflector.reflectionCapabilities = const NullReflectionCapabilities();
|
||||||
}
|
var compiledTemplates = templateCompiler.compileTemplatesCodeGen(compileData);
|
||||||
if (generateChangeDetectors) {
|
reflector.reflectionCapabilities = savedReflectionCapabilities;
|
||||||
var saved = reflector.reflectionCapabilities;
|
|
||||||
var genConfig = new ChangeDetectorGenConfig(assertionsEnabled(), assertionsEnabled(), reflectPropertiesAsAttributes, false);
|
|
||||||
|
|
||||||
reflector.reflectionCapabilities = const NullReflectionCapabilities();
|
var processor = new reg.Processor();
|
||||||
var defs = getChangeDetectorDefinitions(viewDefEntry.hostMetadata,
|
compileData
|
||||||
protoView, viewDefEntry.viewDef.directives, genConfig);
|
.map((withDirectives) => withDirectives.component)
|
||||||
for (var i = 0; i < defs.length; ++i) {
|
.forEach(processor.process);
|
||||||
changeDetectorClasses.generate('${rType.typeName}',
|
var codegen = new reg.Codegen();
|
||||||
'_${rType.typeName}_ChangeDetector$i', defs[i]);
|
|
||||||
|
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.
|
buf.writeln(accessors.toString());
|
||||||
final registrations = new reg.Codegen(generateChangeDetectors: false);
|
|
||||||
registrations.generate(processor);
|
|
||||||
|
|
||||||
var code = viewDefResults.ngDeps.code;
|
// Add everything after the final registration.
|
||||||
if (registrations.isEmpty && changeDetectorClasses.isEmpty) return code;
|
buf.writeln(code.substring(endRegistratationsIdx));
|
||||||
var importInjectIdx =
|
return buf.toString();
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,12 +8,11 @@ import 'model.dart';
|
|||||||
class Codegen {
|
class Codegen {
|
||||||
final StringBuffer _buf = new StringBuffer();
|
final StringBuffer _buf = new StringBuffer();
|
||||||
|
|
||||||
/// Whether we are pre-generating change detectors.
|
/// Generates code to register all getters, setters, and methods stored by
|
||||||
/// If we have pre-generated change detectors, we need
|
/// `model`.
|
||||||
final bool generateChangeDetectors;
|
///
|
||||||
|
/// The code takes the form of zero or more cascaded calls. The receiver of
|
||||||
Codegen({this.generateChangeDetectors});
|
/// these calls is expected to be an Angular 2 reflector object.
|
||||||
|
|
||||||
void generate(CodegenModel model) {
|
void generate(CodegenModel model) {
|
||||||
if (model != null) {
|
if (model != null) {
|
||||||
var calls = _generateGetters(_extractNames(model.getterNames));
|
var calls = _generateGetters(_extractNames(model.getterNames));
|
||||||
@ -35,9 +34,7 @@ class Codegen {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Iterable<String> _extractNames(Iterable<ReflectiveAccessor> accessors) {
|
Iterable<String> _extractNames(Iterable<ReflectiveAccessor> accessors) {
|
||||||
var names = accessors.where((accessor) {
|
var names = accessors.map((accessor) => accessor.sanitizedName);
|
||||||
return accessor.isStaticallyNecessary || !generateChangeDetectors;
|
|
||||||
}).map((accessor) => accessor.sanitizedName);
|
|
||||||
var nameList = names.toList();
|
var nameList = names.toList();
|
||||||
nameList.sort();
|
nameList.sort();
|
||||||
return nameList;
|
return nameList;
|
||||||
|
@ -27,11 +27,7 @@ class ReflectiveAccessor {
|
|||||||
/// setter, or method on the `Component`.
|
/// setter, or method on the `Component`.
|
||||||
final String sanitizedName;
|
final String sanitizedName;
|
||||||
|
|
||||||
/// Whether this getter, setter, or method is still necessary when we have
|
ReflectiveAccessor(String astValue)
|
||||||
/// pre-generated change detectors.
|
|
||||||
final bool isStaticallyNecessary;
|
|
||||||
|
|
||||||
ReflectiveAccessor(String astValue, {this.isStaticallyNecessary})
|
|
||||||
: this.astValue = astValue,
|
: this.astValue = astValue,
|
||||||
this.sanitizedName = sanitizePropertyName(astValue);
|
this.sanitizedName = sanitizePropertyName(astValue);
|
||||||
|
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
library angular2.transform.template_compiler.reflection.processor;
|
library angular2.transform.template_compiler.reflection.processor;
|
||||||
|
|
||||||
import 'package:angular2/src/core/change_detection/parser/ast.dart';
|
import 'package:angular2/src/compiler/directive_metadata.dart';
|
||||||
import 'package:angular2/src/core/render/api.dart';
|
|
||||||
import 'package:angular2/src/transform/template_compiler/view_definition_creator.dart';
|
|
||||||
|
|
||||||
import 'model.dart';
|
import 'model.dart';
|
||||||
|
|
||||||
@ -16,109 +14,9 @@ class Processor implements CodegenModel {
|
|||||||
/// The names of all requested `method`s.
|
/// The names of all requested `method`s.
|
||||||
final Set<ReflectiveAccessor> methodNames = new Set<ReflectiveAccessor>();
|
final Set<ReflectiveAccessor> methodNames = new Set<ReflectiveAccessor>();
|
||||||
|
|
||||||
_NgAstVisitor _visitor;
|
void process(CompileDirectiveMetadata meta) {
|
||||||
|
meta.events.keys.forEach((eventName) {
|
||||||
Processor() {
|
getterNames.add(new ReflectiveAccessor(eventName));
|
||||||
_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);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -29,8 +29,6 @@ class NullReflectionCapabilities implements ReflectionCapabilities {
|
|||||||
MethodFn method(String name) => _nullMethod;
|
MethodFn method(String name) => _nullMethod;
|
||||||
|
|
||||||
String importUri(Type type) => './';
|
String importUri(Type type) => './';
|
||||||
|
|
||||||
String moduleId(Type type) => null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_nullGetter(Object p) => null;
|
_nullGetter(Object p) => null;
|
||||||
|
@ -30,6 +30,7 @@ class TemplateCompiler extends Transformer implements DeclaringTransformer {
|
|||||||
declareOutputs(DeclaringTransform transform) {
|
declareOutputs(DeclaringTransform transform) {
|
||||||
transform.consumePrimary();
|
transform.consumePrimary();
|
||||||
transform.declareOutput(transform.primaryId);
|
transform.declareOutput(transform.primaryId);
|
||||||
|
transform.declareOutput(templatesAssetId(transform.primaryId));
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -38,13 +39,22 @@ class TemplateCompiler extends Transformer implements DeclaringTransformer {
|
|||||||
Html5LibDomAdapter.makeCurrent();
|
Html5LibDomAdapter.makeCurrent();
|
||||||
var primaryId = transform.primaryInput.id;
|
var primaryId = transform.primaryInput.id;
|
||||||
var reader = new AssetReader.fromTransform(transform);
|
var reader = new AssetReader.fromTransform(transform);
|
||||||
var transformedCode = formatter.format(await processTemplates(
|
var outputs = await processTemplates(reader, primaryId,
|
||||||
reader, primaryId,
|
reflectPropertiesAsAttributes: options.reflectPropertiesAsAttributes);
|
||||||
generateChangeDetectors: options.generateChangeDetectors,
|
|
||||||
reflectPropertiesAsAttributes:
|
|
||||||
options.reflectPropertiesAsAttributes));
|
|
||||||
transform.consumePrimary();
|
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));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,15 @@
|
|||||||
library angular2.transform;
|
library angular2.src.transform.transformer;
|
||||||
|
|
||||||
import 'package:barback/barback.dart';
|
import 'package:barback/barback.dart';
|
||||||
import 'package:dart_style/dart_style.dart';
|
import 'package:dart_style/dart_style.dart';
|
||||||
|
|
||||||
import 'deferred_rewriter/transformer.dart';
|
import 'deferred_rewriter/transformer.dart';
|
||||||
import 'directive_linker/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 'directive_processor/transformer.dart';
|
||||||
import 'bind_generator/transformer.dart';
|
import 'bind_generator/transformer.dart';
|
||||||
import 'reflection_remover/transformer.dart';
|
import 'reflection_remover/transformer.dart';
|
||||||
|
import 'stylesheet_compiler/transformer.dart';
|
||||||
import 'template_compiler/transformer.dart';
|
import 'template_compiler/transformer.dart';
|
||||||
import 'common/formatter.dart' as formatter;
|
import 'common/formatter.dart' as formatter;
|
||||||
import 'common/options.dart';
|
import 'common/options.dart';
|
||||||
@ -32,10 +33,14 @@ class AngularTransformerGroup extends TransformerGroup {
|
|||||||
phases.addAll(new List.generate(
|
phases.addAll(new List.generate(
|
||||||
options.optimizationPhases, (_) => [new EmptyNgDepsRemover()]));
|
options.optimizationPhases, (_) => [new EmptyNgDepsRemover()]));
|
||||||
phases.addAll([
|
phases.addAll([
|
||||||
[new DirectiveLinker(), new DeferredRewriter(options)],
|
[
|
||||||
[new DirectiveMetadataExtractor()],
|
new DeferredRewriter(options),
|
||||||
|
new DirectiveLinker(),
|
||||||
|
new DirectiveMetadataLinker()
|
||||||
|
],
|
||||||
[new BindGenerator(options)],
|
[new BindGenerator(options)],
|
||||||
[new TemplateCompiler(options)]
|
[new TemplateCompiler(options)],
|
||||||
|
[new StylesheetCompiler()],
|
||||||
]);
|
]);
|
||||||
return new AngularTransformerGroup._(phases,
|
return new AngularTransformerGroup._(phases,
|
||||||
formatCode: options.formatCode);
|
formatCode: options.formatCode);
|
||||||
|
@ -38,22 +38,12 @@ void allTests() {
|
|||||||
expect(output).toEqual(expected);
|
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.',
|
it('should generate setters for queries defined in the class annotation.',
|
||||||
() async {
|
() async {
|
||||||
var inputPath = 'bind_generator/queries_class_annotation_files/bar.ng_deps.dart';
|
var inputPath =
|
||||||
var expected = formatter.format(
|
'bind_generator/queries_class_annotation_files/bar.ng_deps.dart';
|
||||||
readFile('bind_generator/queries_class_annotation_files/expected/bar.ng_deps.dart'));
|
var expected = formatter.format(readFile(
|
||||||
|
'bind_generator/queries_class_annotation_files/expected/bar.ng_deps.dart'));
|
||||||
|
|
||||||
var output = formatter.format(
|
var output = formatter.format(
|
||||||
await createNgSettersAndGetters(reader, new AssetId('a', inputPath)));
|
await createNgSettersAndGetters(reader, new AssetId('a', inputPath)));
|
||||||
@ -62,9 +52,10 @@ void allTests() {
|
|||||||
|
|
||||||
it('should generate setters for queries defined via prop annotations.',
|
it('should generate setters for queries defined via prop annotations.',
|
||||||
() async {
|
() async {
|
||||||
var inputPath = 'bind_generator/queries_prop_annotations_files/bar.ng_deps.dart';
|
var inputPath =
|
||||||
var expected = formatter.format(
|
'bind_generator/queries_prop_annotations_files/bar.ng_deps.dart';
|
||||||
readFile('bind_generator/queries_prop_annotations_files/expected/bar.ng_deps.dart'));
|
var expected = formatter.format(readFile(
|
||||||
|
'bind_generator/queries_prop_annotations_files/expected/bar.ng_deps.dart'));
|
||||||
|
|
||||||
var output = formatter.format(
|
var output = formatter.format(
|
||||||
await createNgSettersAndGetters(reader, new AssetId('a', inputPath)));
|
await createNgSettersAndGetters(reader, new AssetId('a', inputPath)));
|
||||||
|
@ -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()));
|
|
||||||
}
|
|
@ -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});
|
|
||||||
}
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
@ -1,6 +1,7 @@
|
|||||||
library angular2.test.transform.common.annotation_matcher_test;
|
library angular2.test.transform.common.annotation_matcher_test;
|
||||||
|
|
||||||
import 'package:angular2/src/core/render/api.dart';
|
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:angular2/src/transform/common/ng_meta.dart';
|
||||||
import 'package:guinness/guinness.dart';
|
import 'package:guinness/guinness.dart';
|
||||||
|
|
||||||
@ -8,10 +9,10 @@ main() => allTests();
|
|||||||
|
|
||||||
void allTests() {
|
void allTests() {
|
||||||
var mockData = [
|
var mockData = [
|
||||||
new RenderDirectiveMetadata(id: 'm1'),
|
CompileDirectiveMetadata.create(type: new CompileTypeMetadata(name: 'N1')),
|
||||||
new RenderDirectiveMetadata(id: 'm2'),
|
CompileDirectiveMetadata.create(type: new CompileTypeMetadata(name: 'N2')),
|
||||||
new RenderDirectiveMetadata(id: 'm3'),
|
CompileDirectiveMetadata.create(type: new CompileTypeMetadata(name: 'N3')),
|
||||||
new RenderDirectiveMetadata(id: 'm4')
|
CompileDirectiveMetadata.create(type: new CompileTypeMetadata(name: 'N4'))
|
||||||
];
|
];
|
||||||
|
|
||||||
it('should allow empty data.', () {
|
it('should allow empty data.', () {
|
||||||
@ -92,7 +93,7 @@ _checkSimilar(NgMeta a, NgMeta b) {
|
|||||||
expect(b.types).toContain(k);
|
expect(b.types).toContain(k);
|
||||||
var at = a.types[k];
|
var at = a.types[k];
|
||||||
var bt = b.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) {
|
for (var k in a.aliases.keys) {
|
||||||
expect(b.aliases).toContain(k);
|
expect(b.aliases).toContain(k);
|
||||||
|
@ -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()));
|
|
||||||
}
|
|
@ -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');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
{
|
|
||||||
"alias1": {
|
|
||||||
"kind": "alias",
|
|
||||||
"value": [
|
|
||||||
"BarComponent"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,9 +0,0 @@
|
|||||||
{
|
|
||||||
"alias2": {
|
|
||||||
"kind": "alias",
|
|
||||||
"value": [
|
|
||||||
"FooComponent",
|
|
||||||
"alias1"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
@ -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()));
|
|
||||||
}
|
|
@ -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()));
|
|
||||||
}
|
|
@ -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()));
|
|
||||||
}
|
|
@ -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()));
|
|
||||||
}
|
|
@ -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()));
|
|
||||||
}
|
|
@ -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
|
|
||||||
]));
|
|
||||||
}
|
|
@ -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()));
|
|
||||||
}
|
|
@ -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()));
|
|
||||||
}
|
|
@ -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()));
|
|
||||||
}
|
|
@ -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()));
|
|
||||||
}
|
|
@ -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);
|
|
||||||
}
|
|
@ -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()));
|
|
||||||
}
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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]');
|
||||||
|
});
|
||||||
|
}
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -4,6 +4,9 @@ import 'dart:async';
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:barback/barback.dart';
|
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/directive_processor/rewriter.dart';
|
||||||
import 'package:angular2/src/transform/common/annotation_matcher.dart';
|
import 'package:angular2/src/transform/common/annotation_matcher.dart';
|
||||||
import 'package:angular2/src/transform/common/asset_reader.dart';
|
import 'package:angular2/src/transform/common/asset_reader.dart';
|
||||||
@ -14,13 +17,13 @@ import 'package:angular2/src/transform/common/ng_meta.dart';
|
|||||||
import 'package:code_transformers/messages/build_logger.dart';
|
import 'package:code_transformers/messages/build_logger.dart';
|
||||||
import 'package:dart_style/dart_style.dart';
|
import 'package:dart_style/dart_style.dart';
|
||||||
import 'package:guinness/guinness.dart';
|
import 'package:guinness/guinness.dart';
|
||||||
import 'package:path/path.dart' as path;
|
|
||||||
import 'package:source_span/source_span.dart';
|
import 'package:source_span/source_span.dart';
|
||||||
import '../common/read_file.dart';
|
import '../common/read_file.dart';
|
||||||
|
|
||||||
var formatter = new DartFormatter();
|
var formatter = new DartFormatter();
|
||||||
|
|
||||||
main() {
|
main() {
|
||||||
|
Html5LibDomAdapter.makeCurrent();
|
||||||
allTests();
|
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', () {
|
describe('interfaces', () {
|
||||||
it('should include implemented types', () async {
|
it('should include implemented types', () async {
|
||||||
var model = await _testCreateModel('interfaces_files/soup.dart');
|
var model = await _testCreateModel('interfaces_files/soup.dart');
|
||||||
@ -353,17 +251,8 @@ void allTests() {
|
|||||||
await _testCreateModel('invalid_url_files/hello.dart', logger: logger);
|
await _testCreateModel('invalid_url_files/hello.dart', logger: logger);
|
||||||
expect(logger.hasErrors).toBeTrue();
|
expect(logger.hasErrors).toBeTrue();
|
||||||
expect(logger.logs)
|
expect(logger.logs)
|
||||||
..toContain(
|
..toContain('ERROR: ERROR: Invalid argument (url): '
|
||||||
'ERROR: Uri /bad/absolute/url.html not supported from angular2|test/'
|
'"Could not read asset at uri asset:/bad/absolute/url.html"');
|
||||||
'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');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should find and register static functions.', () async {
|
it('should find and register static functions.', () async {
|
||||||
@ -375,15 +264,16 @@ void allTests() {
|
|||||||
expect(functionReflectable.name).toEqual('getMessage');
|
expect(functionReflectable.name).toEqual('getMessage');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should find direcive aliases patterns.', () async {
|
describe('NgMeta', () {
|
||||||
var logger = new RecordingLogger();
|
var fakeReader;
|
||||||
return log.setZoned(logger, () async {
|
beforeEach(() {
|
||||||
var inputId = _assetIdForPath('directive_aliases_files/hello.dart');
|
fakeReader = new TestAssetReader();
|
||||||
var reader = new TestAssetReader();
|
});
|
||||||
|
|
||||||
|
it('should find direcive aliases patterns.', () async {
|
||||||
var ngMeta = new NgMeta.empty();
|
var ngMeta = new NgMeta.empty();
|
||||||
await createNgDeps(reader, inputId, new AnnotationMatcher(), ngMeta,
|
await _testCreateModel('directive_aliases_files/hello.dart',
|
||||||
inlineViews: true);
|
ngMeta: ngMeta);
|
||||||
|
|
||||||
expect(ngMeta.aliases).toContain('alias1');
|
expect(ngMeta.aliases).toContain('alias1');
|
||||||
expect(ngMeta.aliases['alias1']).toContain('HelloCmp');
|
expect(ngMeta.aliases['alias1']).toContain('HelloCmp');
|
||||||
@ -391,6 +281,121 @@ void allTests() {
|
|||||||
expect(ngMeta.aliases).toContain('alias2');
|
expect(ngMeta.aliases).toContain('alias2');
|
||||||
expect(ngMeta.aliases['alias2'])..toContain('HelloCmp')..toContain('Foo');
|
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,
|
AssetId assetId,
|
||||||
AssetReader reader,
|
AssetReader reader,
|
||||||
BuildLogger logger,
|
BuildLogger logger,
|
||||||
bool inlineViews: true}) {
|
NgMeta ngMeta}) {
|
||||||
if (logger == null) logger = new RecordingLogger();
|
if (logger == null) logger = new RecordingLogger();
|
||||||
return log.setZoned(logger, () async {
|
return log.setZoned(logger, () async {
|
||||||
var inputId = _assetIdForPath(inputPath);
|
var inputId = _assetIdForPath(inputPath);
|
||||||
@ -410,11 +415,12 @@ Future<NgDepsModel> _testCreateModel(String inputPath,
|
|||||||
reader.addAsset(assetId, await reader.readAsString(inputId));
|
reader.addAsset(assetId, await reader.readAsString(inputId));
|
||||||
inputId = assetId;
|
inputId = assetId;
|
||||||
}
|
}
|
||||||
|
if (ngMeta == null) {
|
||||||
|
ngMeta = new NgMeta.empty();
|
||||||
|
}
|
||||||
|
|
||||||
var annotationMatcher = new AnnotationMatcher()..addAll(customDescriptors);
|
var annotationMatcher = new AnnotationMatcher()..addAll(customDescriptors);
|
||||||
var ngMeta = new NgMeta.empty();
|
return createNgDeps(reader, inputId, annotationMatcher, ngMeta);
|
||||||
return createNgDeps(reader, inputId, annotationMatcher, ngMeta,
|
|
||||||
inlineViews: inlineViews);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
library dinner.package_soup;
|
library dinner.package_soup;
|
||||||
|
|
||||||
|
import 'package:angular2/angular2.dart';
|
||||||
import 'package:soup/soup.dart';
|
import 'package:soup/soup.dart';
|
||||||
|
|
||||||
@Soup()
|
@Soup()
|
||||||
|
@View(template: '')
|
||||||
class PackageSoup {
|
class PackageSoup {
|
||||||
PackageSoup();
|
PackageSoup();
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
library dinner.relative_soup;
|
library dinner.relative_soup;
|
||||||
|
|
||||||
|
import 'package:angular2/angular2.dart';
|
||||||
import 'annotations/soup.dart';
|
import 'annotations/soup.dart';
|
||||||
|
|
||||||
@Soup()
|
@Soup()
|
||||||
|
@View(template: '')
|
||||||
class RelativeSoup {
|
class RelativeSoup {
|
||||||
RelativeSoup();
|
RelativeSoup();
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ library dinner.soup;
|
|||||||
import 'package:angular2/src/core/metadata.dart';
|
import 'package:angular2/src/core/metadata.dart';
|
||||||
|
|
||||||
@Component(selector: '[soup]')
|
@Component(selector: '[soup]')
|
||||||
|
@View(template: '')
|
||||||
class ChangingSoupComponent implements PrimaryInterface {}
|
class ChangingSoupComponent implements PrimaryInterface {}
|
||||||
|
|
||||||
class TernaryInterface {}
|
class TernaryInterface {}
|
||||||
|
@ -4,4 +4,5 @@ import 'package:angular2/src/core/metadata.dart';
|
|||||||
import 'package:angular2/src/core/compiler.dart';
|
import 'package:angular2/src/core/compiler.dart';
|
||||||
|
|
||||||
@Component(selector: '[soup]')
|
@Component(selector: '[soup]')
|
||||||
|
@View(template: '')
|
||||||
class ChangingSoupComponent implements OnChanges, AnotherInterface {}
|
class ChangingSoupComponent implements OnChanges, AnotherInterface {}
|
||||||
|
@ -4,4 +4,5 @@ import 'package:angular2/src/core/metadata.dart';
|
|||||||
import 'package:angular2/src/core/compiler.dart';
|
import 'package:angular2/src/core/compiler.dart';
|
||||||
|
|
||||||
@Component(selector: '[soup]')
|
@Component(selector: '[soup]')
|
||||||
|
@View(template: '')
|
||||||
class MultiSoupComponent implements OnChanges, OnDestroy, OnInit {}
|
class MultiSoupComponent implements OnChanges, OnDestroy, OnInit {}
|
||||||
|
@ -6,6 +6,7 @@ part 'part1.dart';
|
|||||||
part 'part2.dart';
|
part 'part2.dart';
|
||||||
|
|
||||||
@Component(selector: '[main]')
|
@Component(selector: '[main]')
|
||||||
|
@View(template: '')
|
||||||
class MainComponent {
|
class MainComponent {
|
||||||
MainComponent();
|
MainComponent();
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
part of main;
|
part of main;
|
||||||
|
|
||||||
@Component(selector: '[part1]')
|
@Component(selector: '[part1]')
|
||||||
|
@View(template: '')
|
||||||
class Part1Component {
|
class Part1Component {
|
||||||
Part1Component();
|
Part1Component();
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
part of main;
|
part of main;
|
||||||
|
|
||||||
@Component(selector: '[part2]')
|
@Component(selector: '[part2]')
|
||||||
|
@View(template: '')
|
||||||
class Part2Component {
|
class Part2Component {
|
||||||
Part2Component();
|
Part2Component();
|
||||||
}
|
}
|
||||||
|
@ -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 {}
|
|
@ -1 +0,0 @@
|
|||||||
.hello { .color: red; }
|
|
@ -3,6 +3,7 @@ library dinner.soup;
|
|||||||
import 'package:angular2/src/core/metadata.dart';
|
import 'package:angular2/src/core/metadata.dart';
|
||||||
|
|
||||||
@Component(selector: '[soup]')
|
@Component(selector: '[soup]')
|
||||||
|
@View(template: '')
|
||||||
class SoupComponent {
|
class SoupComponent {
|
||||||
SoupComponent(@Tasty() String description, @Inject(Salt) salt);
|
SoupComponent(@Tasty() String description, @Inject(Salt) salt);
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ import 'package:angular2/src/core/metadata.dart';
|
|||||||
part 'part.dart';
|
part 'part.dart';
|
||||||
|
|
||||||
@Component(selector: '[main]')
|
@Component(selector: '[main]')
|
||||||
|
@View(template: '')
|
||||||
class MainComponent {
|
class MainComponent {
|
||||||
MainComponent();
|
MainComponent();
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
part of main;
|
part of main;
|
||||||
|
|
||||||
@Component(selector: '[part]')
|
@Component(selector: '[part]')
|
||||||
|
@View(template: '')
|
||||||
class PartComponent {
|
class PartComponent {
|
||||||
PartComponent();
|
PartComponent();
|
||||||
}
|
}
|
||||||
|
@ -4,4 +4,5 @@ import 'package:angular2/src/core/compiler.dart' as prefix;
|
|||||||
import 'package:angular2/src/core/metadata.dart';
|
import 'package:angular2/src/core/metadata.dart';
|
||||||
|
|
||||||
@Component(selector: '[soup]')
|
@Component(selector: '[soup]')
|
||||||
|
@View(template: '')
|
||||||
class OnChangeSoupComponent implements prefix.OnChanges {}
|
class OnChangeSoupComponent implements prefix.OnChanges {}
|
||||||
|
@ -3,6 +3,7 @@ library fields;
|
|||||||
import 'package:angular2/src/core/metadata.dart';
|
import 'package:angular2/src/core/metadata.dart';
|
||||||
|
|
||||||
@Component(selector: '[fields]')
|
@Component(selector: '[fields]')
|
||||||
|
@View(template: '')
|
||||||
class FieldComponent {
|
class FieldComponent {
|
||||||
@FieldDecorator("field") String field;
|
@FieldDecorator("field") String field;
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ library fields;
|
|||||||
import 'package:angular2/src/core/metadata.dart';
|
import 'package:angular2/src/core/metadata.dart';
|
||||||
|
|
||||||
@Component(selector: '[getters]')
|
@Component(selector: '[getters]')
|
||||||
|
@View(template: '')
|
||||||
class FieldComponent {
|
class FieldComponent {
|
||||||
@GetDecorator("get") String get getVal => 'a';
|
@GetDecorator("get") String get getVal => 'a';
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ library fields;
|
|||||||
import 'package:angular2/src/core/metadata.dart';
|
import 'package:angular2/src/core/metadata.dart';
|
||||||
|
|
||||||
@Component(selector: '[getters-and-setters]')
|
@Component(selector: '[getters-and-setters]')
|
||||||
|
@View(template: '')
|
||||||
class FieldComponent {
|
class FieldComponent {
|
||||||
String _val;
|
String _val;
|
||||||
@GetDecorator("get") String get myVal => _val;
|
@GetDecorator("get") String get myVal => _val;
|
||||||
|
@ -3,6 +3,7 @@ library fields;
|
|||||||
import 'package:angular2/src/core/metadata.dart';
|
import 'package:angular2/src/core/metadata.dart';
|
||||||
|
|
||||||
@Component(selector: '[setters]')
|
@Component(selector: '[setters]')
|
||||||
|
@View(template: '')
|
||||||
class FieldComponent {
|
class FieldComponent {
|
||||||
@SetDecorator("set") String set setVal(val) => null;
|
@SetDecorator("set") String set setVal(val) => null;
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ library dinner.soup;
|
|||||||
import 'package:angular2/src/core/metadata.dart';
|
import 'package:angular2/src/core/metadata.dart';
|
||||||
|
|
||||||
@Component(selector: '[soup]')
|
@Component(selector: '[soup]')
|
||||||
|
@View(template: '')
|
||||||
class ChangingSoupComponent extends Super {}
|
class ChangingSoupComponent extends Super {}
|
||||||
|
|
||||||
class Iface {}
|
class Iface {}
|
||||||
|
@ -4,4 +4,5 @@ import 'package:angular2/src/core/compiler.dart';
|
|||||||
import 'package:angular2/src/core/metadata.dart';
|
import 'package:angular2/src/core/metadata.dart';
|
||||||
|
|
||||||
@Component(selector: '[soup]')
|
@Component(selector: '[soup]')
|
||||||
|
@View(template: '')
|
||||||
class OnChangeSoupComponent extends OnChanges {}
|
class OnChangeSoupComponent extends OnChanges {}
|
||||||
|
@ -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 {}
|
@ -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 {}
|
@ -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
Reference in New Issue
Block a user