parent
a5622304de
commit
52236bd765
|
@ -34,7 +34,6 @@ class _ExtractQueryFieldsFromAnnotation extends Object
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
class _ExtractQueryFieldsFromPropMetadata extends Object
|
||||
with RecursiveAstVisitor<Object> {
|
||||
final ConstantEvaluator _evaluator = new ConstantEvaluator();
|
||||
|
@ -52,7 +51,10 @@ class _ExtractQueryFieldsFromPropMetadata extends Object
|
|||
var res = false;
|
||||
list.elements.forEach((item) {
|
||||
var n = item.constructorName.toString();
|
||||
if(n == "ContentChild" || n == "ViewChild" || n == "ContentChildren" || n == "ViewChildren") {
|
||||
if (n == "ContentChild" ||
|
||||
n == "ViewChild" ||
|
||||
n == "ContentChildren" ||
|
||||
n == "ViewChildren") {
|
||||
res = true;
|
||||
}
|
||||
});
|
||||
|
@ -65,14 +67,12 @@ class _ExtractQueryFieldsFromPropMetadata extends Object
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
Future<String> createNgSettersAndGetters(
|
||||
AssetReader reader, AssetId entryPoint) async {
|
||||
NgDeps ngDeps = await NgDeps.parse(reader, entryPoint);
|
||||
|
||||
String code = ngDeps.code;
|
||||
var setters = _generateSetters(_createPropertiesMap(ngDeps));
|
||||
var getters = _generateGetters(_createEventPropertiesList(ngDeps));
|
||||
|
||||
ngDeps.registeredTypes.forEach((t) {
|
||||
final fromAnnotation = new _ExtractQueryFieldsFromAnnotation();
|
||||
|
@ -86,16 +86,13 @@ Future<String> createNgSettersAndGetters(
|
|||
setters.addAll(_generateSetters(fromPropMetadata.asMap()));
|
||||
});
|
||||
|
||||
if (setters.isEmpty && getters.isEmpty) return code;
|
||||
if (setters.isEmpty) return code;
|
||||
var out = new StringBuffer();
|
||||
var codeInjectIdx = ngDeps.registeredTypes.last.registerMethod.end;
|
||||
out.write(code.substring(0, codeInjectIdx));
|
||||
if (setters.isNotEmpty) {
|
||||
out.write('..registerSetters({${setters.join(', ')}})');
|
||||
}
|
||||
if (getters.isNotEmpty) {
|
||||
out.write('..registerGetters({${getters.join(', ')}})');
|
||||
}
|
||||
out.write(code.substring(codeInjectIdx));
|
||||
return '$out';
|
||||
}
|
||||
|
@ -123,7 +120,7 @@ List<String> _generateSetters(Map<String, String> bindMap) {
|
|||
/// the bind properties and the values are either the one and only type
|
||||
/// binding to that property or the empty string.
|
||||
Map<String, String> _createPropertiesMap(NgDeps ngDeps) {
|
||||
var visitor = new ExtractNamedExpressionVisitor('inputs');
|
||||
var visitor = new ExtractNamedExpressionVisitor('properties');
|
||||
var bindMap = {};
|
||||
ngDeps.registeredTypes.forEach((RegisteredType t) {
|
||||
visitor.bindConfig.clear();
|
||||
|
@ -147,37 +144,3 @@ Map<String, String> _createPropertiesMap(NgDeps ngDeps) {
|
|||
});
|
||||
return bindMap;
|
||||
}
|
||||
|
||||
/// Consumes the list generated by {@link _createEventPropertiesList} to codegen
|
||||
/// getters.
|
||||
List<String> _generateGetters(List<String> eventProperties) {
|
||||
var getters = [];
|
||||
// TODO(kegluneq): Include types for receivers. See #886.
|
||||
for (var property in eventProperties) {
|
||||
if (!prop.isValid(property)) {
|
||||
// TODO(kegluenq): Eagerly throw here once #1295 is addressed.
|
||||
getters.add(prop.lazyInvalidGetter(property));
|
||||
} else {
|
||||
getters.add(''' '${prop.sanitize(property)}': (o) => o.$property''');
|
||||
}
|
||||
}
|
||||
return getters;
|
||||
}
|
||||
|
||||
/// Collapses all `events` in {@link ngDeps} into a list of corresponding
|
||||
/// property names.
|
||||
List<String> _createEventPropertiesList(NgDeps ngDeps) {
|
||||
var visitor = new ExtractNamedExpressionVisitor('outputs');
|
||||
var propertyNames = [];
|
||||
ngDeps.registeredTypes.forEach((RegisteredType t) {
|
||||
visitor.bindConfig.clear();
|
||||
t.annotations.accept(visitor);
|
||||
visitor.bindConfig.forEach((String config) {
|
||||
// See comments for `Directive` in annotations_impl/annotations.ts for
|
||||
// details on how `events` is specified. We are pulling out the property
|
||||
// name only (everything before the first `:`).
|
||||
propertyNames.add(config.split(':').first.trim());
|
||||
});
|
||||
});
|
||||
return propertyNames;
|
||||
}
|
||||
|
|
|
@ -38,17 +38,9 @@ class NgDepsVisitor extends RecursiveAstVisitor<Object> {
|
|||
}
|
||||
|
||||
void _createModel(String libraryUri) {
|
||||
_model = new NgDepsModel()..libraryUri = libraryUri;
|
||||
|
||||
// We need to import & export the original file.
|
||||
var origDartFile = path.basename(processedFile.path);
|
||||
_model.imports.add(new ImportModel()..uri = origDartFile);
|
||||
_model.exports.add(new ExportModel()..uri = origDartFile);
|
||||
|
||||
// Used to register reflective information.
|
||||
_model.imports.add(new ImportModel()
|
||||
..uri = REFLECTOR_IMPORT
|
||||
..prefix = REFLECTOR_PREFIX);
|
||||
_model = new NgDepsModel()
|
||||
..libraryUri = libraryUri
|
||||
..sourceFile = path.basename(processedFile.path);
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -139,6 +131,14 @@ abstract class NgDepsWriterMixin
|
|||
buffer.writeln('library ${model.libraryUri}${DEPS_EXTENSION};\n');
|
||||
}
|
||||
|
||||
// We need to import & export the source file.
|
||||
writeImportModel(new ImportModel()..uri = model.sourceFile);
|
||||
|
||||
// Used to register reflective information.
|
||||
writeImportModel(new ImportModel()
|
||||
..uri = REFLECTOR_IMPORT
|
||||
..prefix = REFLECTOR_PREFIX);
|
||||
|
||||
// We do not support `partUris`, so skip outputting them.
|
||||
for (var importModel in model.imports) {
|
||||
// Ignore deferred imports here so as to not load the deferred libraries
|
||||
|
@ -149,6 +149,7 @@ abstract class NgDepsWriterMixin
|
|||
|
||||
writeImportModel(importModel);
|
||||
}
|
||||
writeExportModel(new ExportModel()..uri = model.sourceFile);
|
||||
model.exports.forEach(writeExportModel);
|
||||
|
||||
buffer
|
||||
|
|
|
@ -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;
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:analyzer/analyzer.dart';
|
||||
import 'package:analyzer/src/generated/element.dart';
|
||||
import 'package:angular2/src/core/render/api.dart';
|
||||
import 'package:angular2/src/compiler/directive_metadata.dart';
|
||||
import 'package:angular2/src/compiler/template_compiler.dart';
|
||||
import 'package:angular2/src/core/change_detection/change_detection.dart';
|
||||
import 'package:angular2/src/core/compiler/interfaces.dart' show LifecycleHooks;
|
||||
import 'package:angular2/src/core/render/api.dart' show ViewEncapsulation;
|
||||
import 'package:angular2/src/transform/common/annotation_matcher.dart';
|
||||
import 'package:angular2/src/transform/common/interface_matcher.dart';
|
||||
import 'package:barback/barback.dart' show AssetId;
|
||||
|
||||
/// Reads [RenderDirectiveMetadata] from the `node`. `node` is expected to be an
|
||||
/// instance of [ClassDeclaration] (a class which may have a [Directive] or
|
||||
/// [Component] annotation) or an [InstanceCreationExpression] (an instantiation
|
||||
/// of [ReflectionInfo]).
|
||||
RenderDirectiveMetadata readDirectiveMetadata(AstNode node) {
|
||||
var visitor;
|
||||
if (node is ClassDeclaration) {
|
||||
visitor = new _DeclarationVisitor();
|
||||
} else if (node is InstanceCreationExpression) {
|
||||
visitor = new _ReflectionInfoVisitor();
|
||||
} else {
|
||||
throw new ArgumentError('Incorrect value passed to readDirectiveMetadata. '
|
||||
'Expected types are ClassDeclaration and InstanceCreationExpression '
|
||||
'Provided type was ${node.runtimeType}');
|
||||
}
|
||||
node.accept(visitor);
|
||||
return visitor.meta;
|
||||
}
|
||||
class DirectiveMetadataReader {
|
||||
final _DirectiveMetadataVisitor _visitor;
|
||||
final TemplateCompiler _templateCompiler;
|
||||
|
||||
num _getDirectiveType(String annotationName, Element element) {
|
||||
var byNameMatch = -1;
|
||||
// TODO(kegluneq): Detect subtypes & implementations of `Directive`s.
|
||||
switch (annotationName) {
|
||||
case 'Directive':
|
||||
byNameMatch = RenderDirectiveMetadata.DIRECTIVE_TYPE;
|
||||
break;
|
||||
case 'Component':
|
||||
byNameMatch = RenderDirectiveMetadata.COMPONENT_TYPE;
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
DirectiveMetadataReader._(this._visitor, this._templateCompiler);
|
||||
|
||||
/// Accepts an [AnnotationMatcher] which tests that an [Annotation]
|
||||
/// is a [Directive], [Component], or [View].
|
||||
factory DirectiveMetadataReader(AnnotationMatcher annotationMatcher,
|
||||
InterfaceMatcher interfaceMatcher, TemplateCompiler templateCompiler) {
|
||||
var lifecycleVisitor = new _LifecycleHookVisitor(interfaceMatcher);
|
||||
var visitor =
|
||||
new _DirectiveMetadataVisitor(annotationMatcher, lifecycleVisitor);
|
||||
|
||||
return new DirectiveMetadataReader._(visitor, templateCompiler);
|
||||
}
|
||||
if (element != null) {
|
||||
var byResolvedAst = -1;
|
||||
var libName = element.library.name;
|
||||
// If we have resolved, ensure the library is correct.
|
||||
if (libName == 'angular2.src.core.metadata.directives' ||
|
||||
libName == 'angular2.src.core.metadata') {
|
||||
byResolvedAst = byNameMatch;
|
||||
|
||||
/// Reads *un-normalized* [CompileDirectiveMetadata] from the
|
||||
/// [ClassDeclaration] `node`.
|
||||
///
|
||||
/// `node` is expected to be a class which may have a [Directive] or [Component]
|
||||
/// annotation. If `node` does not have one of these annotations, this function
|
||||
/// returns `null`.
|
||||
///
|
||||
/// `assetId` is the [AssetId] from which `node` was read, unless `node` was
|
||||
/// read from a part file, in which case `assetId` should be the [AssetId] of
|
||||
/// the parent file.
|
||||
Future<CompileDirectiveMetadata> readDirectiveMetadata(
|
||||
ClassDeclaration node, AssetId assetId) {
|
||||
_visitor.reset(assetId);
|
||||
node.accept(_visitor);
|
||||
if (!_visitor.hasMetadata) {
|
||||
return new Future.value(null);
|
||||
} else {
|
||||
final metadata = _visitor.createMetadata();
|
||||
if (!metadata.isComponent) return new Future.value(metadata);
|
||||
return _templateCompiler.normalizeDirectiveMetadata(metadata);
|
||||
}
|
||||
// TODO(kegluneq): @keertip, can we expose this as a warning?
|
||||
assert(byNameMatch == byResolvedAst);
|
||||
}
|
||||
return byNameMatch;
|
||||
}
|
||||
|
||||
class _ReflectionInfoVisitor extends _DirectiveMetadataVisitor {
|
||||
// TODO(kegluneq): Create a more robust check that this is a real
|
||||
// `ReflectionInfo` instantiation.
|
||||
static bool _isReflectionInfo(InstanceCreationExpression node) {
|
||||
var name = node.constructorName.type.name;
|
||||
name = name is PrefixedIdentifier ? name.identifier : name;
|
||||
return '$name' == 'ReflectionInfo';
|
||||
}
|
||||
|
||||
@override
|
||||
Object visitInstanceCreationExpression(InstanceCreationExpression node) {
|
||||
if (_isReflectionInfo(node)) {
|
||||
// NOTE(kegluneq): This is very tightly coupled with the `Reflector`
|
||||
// implementation. Clean this up with a better intermediate representation
|
||||
// for .ng_deps.dart files.
|
||||
var reflectionInfoArgs = node.argumentList.arguments;
|
||||
if (reflectionInfoArgs.length > 0) {
|
||||
// Process annotations to determine information specified via
|
||||
// `Component` and `Directive` parameters.
|
||||
reflectionInfoArgs[0].accept(this);
|
||||
if (_hasMeta && reflectionInfoArgs.length > 3) {
|
||||
// Process interfaces to determine which lifecycle events we need to
|
||||
// react to for this `Directive`.
|
||||
_processInterfaces(reflectionInfoArgs[3]);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
var directiveType = _getDirectiveType(
|
||||
'${node.constructorName.type.name}', node.staticElement);
|
||||
if (directiveType >= 0) {
|
||||
if (_hasMeta) {
|
||||
throw new FormatException(
|
||||
'Only one Directive is allowed per class. '
|
||||
'Found "$node" but already processed "$meta".',
|
||||
'$node' /* source */);
|
||||
}
|
||||
_initializeMetadata(directiveType);
|
||||
super.visitInstanceCreationExpression(node);
|
||||
}
|
||||
// Annotation we do not recognize - no need to visit.
|
||||
return null;
|
||||
}
|
||||
|
||||
void _processInterfaces(Expression lifecycleValue) {
|
||||
_checkMeta();
|
||||
if (lifecycleValue is! ListLiteral) {
|
||||
throw new FormatException(
|
||||
'Angular 2 expects a List but could not understand the value for interfaces. '
|
||||
'$lifecycleValue');
|
||||
}
|
||||
ListLiteral l = lifecycleValue;
|
||||
_populateLifecycle(l.elements.map((s) => s.toSource().split('.').last));
|
||||
}
|
||||
}
|
||||
|
||||
class _DeclarationVisitor extends _DirectiveMetadataVisitor {
|
||||
@override
|
||||
Object visitClassDeclaration(ClassDeclaration node) {
|
||||
node.metadata.accept(this);
|
||||
if (this._hasMeta) {
|
||||
if (node.implementsClause != null &&
|
||||
node.implementsClause.interfaces != null) {
|
||||
_populateLifecycle(node.implementsClause.interfaces
|
||||
.map((s) => s.toSource().split('.').last));
|
||||
}
|
||||
}
|
||||
return null;
|
||||
/// Visitor that attempts to evaluate a provided `node` syntactically.
|
||||
///
|
||||
/// This lack of semantic information means it cannot do much - for
|
||||
/// example, it can create a list from a list literal and combine adjacent
|
||||
/// strings but cannot determine that an identifier is a constant string,
|
||||
/// even if that identifier is defined in the same [CompilationUnit].
|
||||
///
|
||||
/// Returns the result of evaluation or [ConstantEvaluator.NOT_A_CONSTANT]
|
||||
/// where appropriate.
|
||||
final ConstantEvaluator _evaluator = new ConstantEvaluator();
|
||||
|
||||
/// Evaluates the [Map] represented by `expression` and adds all `key`,
|
||||
/// `value` pairs to `map`. If `expression` does not evaluate to a [Map],
|
||||
/// throws a descriptive [FormatException].
|
||||
void _populateMap(Expression expression, Map map, String propertyName) {
|
||||
var evaluated = expression.accept(_evaluator);
|
||||
if (evaluated is! Map) {
|
||||
throw new FormatException(
|
||||
'Angular 2 expects a Map but could not understand the value for '
|
||||
'$propertyName.',
|
||||
'$expression' /* source */);
|
||||
}
|
||||
evaluated.forEach((key, value) {
|
||||
if (value != null) {
|
||||
map[key] = '$value';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// Visitor responsible for processing [Directive] code into a
|
||||
/// [RenderDirectiveMetadata] object.
|
||||
/// Evaluates the [List] represented by `expression` and adds all values,
|
||||
/// to `list`. If `expression` does not evaluate to a [List], throws a
|
||||
/// descriptive [FormatException].
|
||||
void _populateList(
|
||||
Expression expression, List<String> list, String propertyName) {
|
||||
var evaluated = expression.accept(_evaluator);
|
||||
if (evaluated is! List) {
|
||||
throw new FormatException(
|
||||
'Angular 2 expects a List but could not understand the value for '
|
||||
'$propertyName.',
|
||||
'$expression' /* source */);
|
||||
}
|
||||
list.addAll(evaluated.map((e) => e.toString()));
|
||||
}
|
||||
|
||||
/// Evaluates `node` and expects that the result will be a string. If not,
|
||||
/// throws a [FormatException].
|
||||
String _expressionToString(Expression node, String nodeDescription) {
|
||||
var value = node.accept(_evaluator);
|
||||
if (value is! String) {
|
||||
throw new FormatException(
|
||||
'Angular 2 could not understand the value '
|
||||
'in $nodeDescription.',
|
||||
'$node' /* source */);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/// Visitor responsible for processing a [Directive] annotated
|
||||
/// [ClassDeclaration] and creating a [CompileDirectiveMetadata] object.
|
||||
class _DirectiveMetadataVisitor extends Object
|
||||
with RecursiveAstVisitor<Object> {
|
||||
bool get _hasMeta => _type != null;
|
||||
/// Tests [Annotation]s to determine if they deifne a [Directive],
|
||||
/// [Component], [View], or none of these.
|
||||
final AnnotationMatcher _annotationMatcher;
|
||||
|
||||
// Annotation fields
|
||||
num _type;
|
||||
String _selector;
|
||||
bool _compileChildren;
|
||||
List<String> _inputs;
|
||||
Map<String, String> _host;
|
||||
List<String> _readAttributes;
|
||||
String _exportAs;
|
||||
bool _callOnDestroy;
|
||||
bool _callOnChange;
|
||||
bool _callDoCheck;
|
||||
bool _callOnInit;
|
||||
bool _callAfterContentInit;
|
||||
bool _callAfterContentChecked;
|
||||
bool _callAfterViewInit;
|
||||
bool _callAfterViewChecked;
|
||||
ChangeDetectionStrategy _changeDetection;
|
||||
List<String> _outputs;
|
||||
final _LifecycleHookVisitor _lifecycleVisitor;
|
||||
|
||||
final ConstantEvaluator _evaluator = new ConstantEvaluator();
|
||||
/// The [AssetId] we are currently processing.
|
||||
AssetId _assetId;
|
||||
|
||||
void _initializeMetadata(num directiveType) {
|
||||
assert(directiveType >= 0);
|
||||
|
||||
_type = directiveType;
|
||||
_selector = '';
|
||||
_compileChildren = true;
|
||||
_inputs = [];
|
||||
_host = {};
|
||||
_readAttributes = [];
|
||||
_exportAs = null;
|
||||
_callOnDestroy = false;
|
||||
_callOnChange = false;
|
||||
_callDoCheck = false;
|
||||
_callOnInit = false;
|
||||
_callAfterContentInit = false;
|
||||
_callAfterContentChecked = false;
|
||||
_callAfterViewInit = false;
|
||||
_callAfterViewChecked = false;
|
||||
_changeDetection = null;
|
||||
_outputs = [];
|
||||
_DirectiveMetadataVisitor(this._annotationMatcher, this._lifecycleVisitor) {
|
||||
reset(null);
|
||||
}
|
||||
|
||||
RenderDirectiveMetadata get meta => RenderDirectiveMetadata.create(
|
||||
/// Whether the visitor has found a [Component] or [Directive] annotation
|
||||
/// since the last call to `reset`.
|
||||
bool _hasMetadata = false;
|
||||
|
||||
// Annotation fields
|
||||
CompileTypeMetadata _type;
|
||||
bool _isComponent;
|
||||
String _selector;
|
||||
String _exportAs;
|
||||
ChangeDetectionStrategy _changeDetection;
|
||||
List<String> _properties;
|
||||
List<String> _events;
|
||||
Map<String, String> _host;
|
||||
List<LifecycleHooks> _lifecycleHooks;
|
||||
CompileTemplateMetadata _template;
|
||||
|
||||
void reset(AssetId assetId) {
|
||||
_lifecycleVisitor.reset(assetId);
|
||||
_assetId = assetId;
|
||||
|
||||
_type = null;
|
||||
_isComponent = false;
|
||||
_hasMetadata = false;
|
||||
_selector = '';
|
||||
_exportAs = null;
|
||||
_changeDetection = ChangeDetectionStrategy.Default;
|
||||
_properties = <String>[];
|
||||
_events = <String>[];
|
||||
_host = <String, String>{};
|
||||
_lifecycleHooks = null;
|
||||
_template = null;
|
||||
}
|
||||
|
||||
bool get hasMetadata => _hasMetadata;
|
||||
|
||||
CompileDirectiveMetadata createMetadata() => CompileDirectiveMetadata.create(
|
||||
type: _type,
|
||||
isComponent: _isComponent,
|
||||
dynamicLoadable: true, // NOTE(kegluneq): For future optimization.
|
||||
selector: _selector,
|
||||
compileChildren: _compileChildren,
|
||||
inputs: _inputs,
|
||||
host: _host,
|
||||
readAttributes: _readAttributes,
|
||||
exportAs: _exportAs,
|
||||
callOnDestroy: _callOnDestroy,
|
||||
callOnChanges: _callOnChange,
|
||||
callDoCheck: _callDoCheck,
|
||||
callOnInit: _callOnInit,
|
||||
callAfterContentInit: _callAfterContentInit,
|
||||
callAfterContentChecked: _callAfterContentChecked,
|
||||
callAfterViewInit: _callAfterViewInit,
|
||||
callAfterViewChecked: _callAfterViewChecked,
|
||||
changeDetection: _changeDetection,
|
||||
outputs: _outputs);
|
||||
properties: _properties,
|
||||
events: _events,
|
||||
host: _host,
|
||||
lifecycleHooks: _lifecycleHooks,
|
||||
template: _template);
|
||||
|
||||
@override
|
||||
Object visitAnnotation(Annotation node) {
|
||||
var directiveType = _getDirectiveType('${node.name}', node.element);
|
||||
if (directiveType >= 0) {
|
||||
if (_hasMeta) {
|
||||
var isComponent = _annotationMatcher.isComponent(node, _assetId);
|
||||
var isDirective = _annotationMatcher.isDirective(node, _assetId);
|
||||
if (isDirective) {
|
||||
if (_hasMetadata) {
|
||||
throw new FormatException(
|
||||
'Only one Directive is allowed per class. '
|
||||
'Found "$node" but already processed "$meta".',
|
||||
'Found unexpected "$node".',
|
||||
'$node' /* source */);
|
||||
}
|
||||
_initializeMetadata(directiveType);
|
||||
_isComponent = isComponent;
|
||||
_hasMetadata = true;
|
||||
super.visitAnnotation(node);
|
||||
} else if (_annotationMatcher.isView(node, _assetId)) {
|
||||
if (_template != null) {
|
||||
throw new FormatException(
|
||||
'Only one View is allowed per class. '
|
||||
'Found unexpected "$node".',
|
||||
'$node' /* source */);
|
||||
}
|
||||
_template = new _CompileTemplateMetadataVisitor().visitAnnotation(node);
|
||||
}
|
||||
|
||||
// Annotation we do not recognize - no need to visit.
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
Object visitClassDeclaration(ClassDeclaration node) {
|
||||
node.metadata.accept(this);
|
||||
if (this._hasMetadata) {
|
||||
_type = new CompileTypeMetadata(
|
||||
moduleUrl: 'asset:${_assetId.package}/${_assetId.path}',
|
||||
name: node.name.toString(),
|
||||
runtime: null // Intentionally `null`, cannot be provided here.
|
||||
);
|
||||
_lifecycleHooks = node.implementsClause != null
|
||||
? node.implementsClause.accept(_lifecycleVisitor)
|
||||
: const [];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
Object visitNamedExpression(NamedExpression node) {
|
||||
// TODO(kegluneq): Remove this limitation.
|
||||
|
@ -220,10 +232,7 @@ class _DirectiveMetadataVisitor extends Object
|
|||
case 'selector':
|
||||
_populateSelector(node.expression);
|
||||
break;
|
||||
case 'compileChildren':
|
||||
_populateCompileChildren(node.expression);
|
||||
break;
|
||||
case 'inputs':
|
||||
case 'properties':
|
||||
_populateProperties(node.expression);
|
||||
break;
|
||||
case 'host':
|
||||
|
@ -235,84 +244,29 @@ class _DirectiveMetadataVisitor extends Object
|
|||
case 'changeDetection':
|
||||
_populateChangeDetection(node.expression);
|
||||
break;
|
||||
case 'outputs':
|
||||
case 'events':
|
||||
_populateEvents(node.expression);
|
||||
break;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
String _expressionToString(Expression node, String nodeDescription) {
|
||||
var value = node.accept(_evaluator);
|
||||
if (value is! String) {
|
||||
throw new FormatException(
|
||||
'Angular 2 could not understand the value '
|
||||
'in $nodeDescription.',
|
||||
'$node' /* source */);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
void _populateSelector(Expression selectorValue) {
|
||||
_checkMeta();
|
||||
_selector = _expressionToString(selectorValue, 'Directive#selector');
|
||||
}
|
||||
|
||||
void _checkMeta() {
|
||||
if (!_hasMeta) {
|
||||
if (!_hasMetadata) {
|
||||
throw new ArgumentError(
|
||||
'Incorrect value passed to readDirectiveMetadata. '
|
||||
'Expected types are ClassDeclaration and InstanceCreationExpression');
|
||||
'Expected type is ClassDeclaration');
|
||||
}
|
||||
}
|
||||
|
||||
void _populateCompileChildren(Expression compileChildrenValue) {
|
||||
_checkMeta();
|
||||
var evaluated = compileChildrenValue.accept(_evaluator);
|
||||
if (evaluated is! bool) {
|
||||
throw new FormatException(
|
||||
'Angular 2 expects a bool but could not understand the value for '
|
||||
'Directive#compileChildren.',
|
||||
'$compileChildrenValue' /* source */);
|
||||
}
|
||||
_compileChildren = evaluated;
|
||||
}
|
||||
|
||||
/// Evaluates the [Map] represented by `expression` and adds all `key`,
|
||||
/// `value` pairs to `map`. If `expression` does not evaluate to a [Map],
|
||||
/// throws a descriptive [FormatException].
|
||||
void _populateMap(Expression expression, Map map, String propertyName) {
|
||||
var evaluated = expression.accept(_evaluator);
|
||||
if (evaluated is! Map) {
|
||||
throw new FormatException(
|
||||
'Angular 2 expects a Map but could not understand the value for '
|
||||
'$propertyName.',
|
||||
'$expression' /* source */);
|
||||
}
|
||||
evaluated.forEach((key, value) {
|
||||
if (value != null) {
|
||||
map[key] = '$value';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// Evaluates the [List] represented by `expression` and adds all values,
|
||||
/// to `list`. If `expression` does not evaluate to a [List], throws a
|
||||
/// descriptive [FormatException].
|
||||
void _populateList(Expression expression, List list, String propertyName) {
|
||||
var evaluated = expression.accept(_evaluator);
|
||||
if (evaluated is! List) {
|
||||
throw new FormatException(
|
||||
'Angular 2 expects a List but could not understand the value for '
|
||||
'$propertyName.',
|
||||
'$expression' /* source */);
|
||||
}
|
||||
list.addAll(evaluated);
|
||||
}
|
||||
|
||||
void _populateProperties(Expression propertiesValue) {
|
||||
_checkMeta();
|
||||
_populateList(propertiesValue, _inputs, 'Directive#properties');
|
||||
_populateList(propertiesValue, _properties, 'Directive#properties');
|
||||
}
|
||||
|
||||
void _populateHost(Expression hostValue) {
|
||||
|
@ -325,32 +279,138 @@ class _DirectiveMetadataVisitor extends Object
|
|||
_exportAs = _expressionToString(exportAsValue, 'Directive#exportAs');
|
||||
}
|
||||
|
||||
void _populateLifecycle(Iterable<String> lifecycleInterfaceNames) {
|
||||
_checkMeta();
|
||||
_callOnDestroy = lifecycleInterfaceNames.contains("OnDestroy");
|
||||
_callOnChange = lifecycleInterfaceNames.contains("OnChanges");
|
||||
_callDoCheck = lifecycleInterfaceNames.contains("DoCheck");
|
||||
_callOnInit = lifecycleInterfaceNames.contains("OnInit");
|
||||
_callAfterContentInit =
|
||||
lifecycleInterfaceNames.contains("AfterContentInit");
|
||||
_callAfterContentChecked =
|
||||
lifecycleInterfaceNames.contains("AfterContentChecked");
|
||||
_callAfterViewInit = lifecycleInterfaceNames.contains("AfterViewInit");
|
||||
_callAfterViewChecked =
|
||||
lifecycleInterfaceNames.contains("AfterViewChecked");
|
||||
}
|
||||
|
||||
void _populateEvents(Expression eventsValue) {
|
||||
_checkMeta();
|
||||
_populateList(eventsValue, _outputs, 'Directive#events');
|
||||
_populateList(eventsValue, _events, 'Directive#events');
|
||||
}
|
||||
|
||||
void _populateChangeDetection(Expression value) {
|
||||
_checkMeta();
|
||||
_changeDetection = changeDetectionStrategies[value.toSource()];
|
||||
_changeDetection = _changeDetectionStrategies[value.toSource()];
|
||||
}
|
||||
|
||||
static final Map<String, ChangeDetectionStrategy> _changeDetectionStrategies =
|
||||
new Map.fromIterable(ChangeDetectionStrategy.values,
|
||||
key: (v) => v.toString());
|
||||
}
|
||||
|
||||
/// Visitor responsible for parsing an [ImplementsClause] and returning a
|
||||
/// [List<LifecycleHooks>] that the [Directive] subscribes to.
|
||||
class _LifecycleHookVisitor extends SimpleAstVisitor<List<LifecycleHooks>> {
|
||||
/// Tests [Identifier]s of implemented interfaces to determine if they
|
||||
/// correspond to [LifecycleHooks] values.
|
||||
final InterfaceMatcher _ifaceMatcher;
|
||||
|
||||
/// The [AssetId] we are currently processing.
|
||||
AssetId _assetId;
|
||||
|
||||
_LifecycleHookVisitor(this._ifaceMatcher);
|
||||
|
||||
void reset(AssetId assetId) {
|
||||
_assetId = assetId;
|
||||
}
|
||||
|
||||
@override
|
||||
List<LifecycleHooks> visitImplementsClause(ImplementsClause node) {
|
||||
if (node == null || node.interfaces == null) return const [];
|
||||
|
||||
return node.interfaces.map((TypeName ifaceTypeName) {
|
||||
var id = ifaceTypeName.name;
|
||||
if (_ifaceMatcher.isAfterContentChecked(id, _assetId)) {
|
||||
return LifecycleHooks.AfterContentChecked;
|
||||
} else if (_ifaceMatcher.isAfterContentInit(id, _assetId)) {
|
||||
return LifecycleHooks.AfterContentInit;
|
||||
} else if (_ifaceMatcher.isAfterViewChecked(id, _assetId)) {
|
||||
return LifecycleHooks.AfterViewChecked;
|
||||
} else if (_ifaceMatcher.isAfterViewInit(id, _assetId)) {
|
||||
return LifecycleHooks.AfterViewInit;
|
||||
} else if (_ifaceMatcher.isDoCheck(id, _assetId)) {
|
||||
return LifecycleHooks.DoCheck;
|
||||
} else if (_ifaceMatcher.isOnChange(id, _assetId)) {
|
||||
return LifecycleHooks.OnChanges;
|
||||
} else if (_ifaceMatcher.isOnDestroy(id, _assetId)) {
|
||||
return LifecycleHooks.OnDestroy;
|
||||
} else if (_ifaceMatcher.isOnInit(id, _assetId)) {
|
||||
return LifecycleHooks.OnInit;
|
||||
}
|
||||
return null;
|
||||
}).where((e) => e != null).toList(growable: false);
|
||||
}
|
||||
}
|
||||
|
||||
final Map<String, ChangeDetectionStrategy> changeDetectionStrategies =
|
||||
new Map.fromIterable(ChangeDetectionStrategy.values,
|
||||
key: (v) => v.toString());
|
||||
/// Visitor responsible for parsing a @View [Annotation] and producing a
|
||||
/// [CompileTemplateMetadata].
|
||||
class _CompileTemplateMetadataVisitor
|
||||
extends RecursiveAstVisitor<CompileTemplateMetadata> {
|
||||
ViewEncapsulation _encapsulation = ViewEncapsulation.Emulated;
|
||||
String _template = null;
|
||||
String _templateUrl = null;
|
||||
List<String> _styles = null;
|
||||
List<String> _styleUrls = null;
|
||||
|
||||
@override
|
||||
CompileTemplateMetadata visitAnnotation(Annotation node) {
|
||||
super.visitAnnotation(node);
|
||||
|
||||
return new CompileTemplateMetadata(
|
||||
encapsulation: _encapsulation,
|
||||
template: _template,
|
||||
templateUrl: _templateUrl,
|
||||
styles: _styles,
|
||||
styleUrls: _styleUrls);
|
||||
}
|
||||
|
||||
@override
|
||||
CompileTemplateMetadata visitNamedExpression(NamedExpression node) {
|
||||
// TODO(kegluneq): Remove this limitation.
|
||||
if (node.name is! Label || node.name.label is! SimpleIdentifier) {
|
||||
throw new FormatException(
|
||||
'Angular 2 currently only supports simple identifiers in directives.',
|
||||
'$node' /* source */);
|
||||
}
|
||||
var keyString = '${node.name.label}';
|
||||
switch (keyString) {
|
||||
case 'encapsulation':
|
||||
_populateEncapsulation(node.expression);
|
||||
break;
|
||||
case 'template':
|
||||
_populateTemplate(node.expression);
|
||||
break;
|
||||
case 'templateUrl':
|
||||
_populateTemplateUrl(node.expression);
|
||||
break;
|
||||
case 'styles':
|
||||
_populateStyles(node.expression);
|
||||
break;
|
||||
case 'styleUrls':
|
||||
_populateStyleUrls(node.expression);
|
||||
break;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
void _populateTemplate(Expression value) {
|
||||
_template = _expressionToString(value, 'View#template');
|
||||
}
|
||||
|
||||
void _populateTemplateUrl(Expression value) {
|
||||
_templateUrl = _expressionToString(value, 'View#templateUrl');
|
||||
}
|
||||
|
||||
void _populateStyles(Expression value) {
|
||||
_styles = <String>[];
|
||||
_populateList(value, _styles, 'View#styles');
|
||||
}
|
||||
|
||||
void _populateStyleUrls(Expression value) {
|
||||
_styleUrls = <String>[];
|
||||
_populateList(value, _styleUrls, 'View#styleUrls');
|
||||
}
|
||||
|
||||
void _populateEncapsulation(Expression value) {
|
||||
_encapsulation = _viewEncapsulationMap[value.toSource()];
|
||||
}
|
||||
|
||||
static final _viewEncapsulationMap =
|
||||
new Map.fromIterable(ViewEncapsulation.values, key: (v) => v.toString());
|
||||
}
|
||||
|
|
|
@ -11,14 +11,21 @@ class NgDepsModel extends GeneratedMessage {
|
|||
static final BuilderInfo _i = new BuilderInfo('NgDepsModel')
|
||||
..a(1, 'libraryUri', PbFieldType.OS)
|
||||
..p(2, 'partUris', PbFieldType.PS)
|
||||
..pp(3, 'imports', PbFieldType.PM, ImportModel.$checkItem, ImportModel.create)
|
||||
..pp(4, 'exports', PbFieldType.PM, ExportModel.$checkItem, ExportModel.create)
|
||||
..pp(5, 'reflectables', PbFieldType.PM, ReflectionInfoModel.$checkItem, ReflectionInfoModel.create)
|
||||
;
|
||||
..pp(3, 'imports', PbFieldType.PM, ImportModel.$checkItem,
|
||||
ImportModel.create)
|
||||
..pp(4, 'exports', PbFieldType.PM, ExportModel.$checkItem,
|
||||
ExportModel.create)
|
||||
..pp(5, 'reflectables', PbFieldType.PM, ReflectionInfoModel.$checkItem,
|
||||
ReflectionInfoModel.create)
|
||||
..a(6, 'sourceFile', PbFieldType.OS);
|
||||
|
||||
NgDepsModel() : super();
|
||||
NgDepsModel.fromBuffer(List<int> i, [ExtensionRegistry r = ExtensionRegistry.EMPTY]) : super.fromBuffer(i, r);
|
||||
NgDepsModel.fromJson(String i, [ExtensionRegistry r = ExtensionRegistry.EMPTY]) : super.fromJson(i, r);
|
||||
NgDepsModel.fromBuffer(List<int> i,
|
||||
[ExtensionRegistry r = ExtensionRegistry.EMPTY])
|
||||
: super.fromBuffer(i, r);
|
||||
NgDepsModel.fromJson(String i,
|
||||
[ExtensionRegistry r = ExtensionRegistry.EMPTY])
|
||||
: super.fromJson(i, r);
|
||||
NgDepsModel clone() => new NgDepsModel()..mergeFromMessage(this);
|
||||
BuilderInfo get info_ => _i;
|
||||
static NgDepsModel create() => new NgDepsModel();
|
||||
|
@ -27,13 +34,17 @@ class NgDepsModel extends GeneratedMessage {
|
|||
if (_defaultInstance == null) _defaultInstance = new _ReadonlyNgDepsModel();
|
||||
return _defaultInstance;
|
||||
}
|
||||
|
||||
static NgDepsModel _defaultInstance;
|
||||
static void $checkItem(NgDepsModel v) {
|
||||
if (v is !NgDepsModel) checkItemFailed(v, 'NgDepsModel');
|
||||
if (v is! NgDepsModel) checkItemFailed(v, 'NgDepsModel');
|
||||
}
|
||||
|
||||
String get libraryUri => getField(1);
|
||||
void set libraryUri(String v) { setField(1, v); }
|
||||
void set libraryUri(String v) {
|
||||
setField(1, v);
|
||||
}
|
||||
|
||||
bool hasLibraryUri() => hasField(1);
|
||||
void clearLibraryUri() => clearField(1);
|
||||
|
||||
|
@ -44,6 +55,14 @@ class NgDepsModel extends GeneratedMessage {
|
|||
List<ExportModel> get exports => getField(4);
|
||||
|
||||
List<ReflectionInfoModel> get reflectables => getField(5);
|
||||
|
||||
String get sourceFile => getField(6);
|
||||
void set sourceFile(String v) {
|
||||
setField(6, v);
|
||||
}
|
||||
|
||||
bool hasSourceFile() => hasField(6);
|
||||
void clearSourceFile() => clearField(6);
|
||||
}
|
||||
|
||||
class _ReadonlyNgDepsModel extends NgDepsModel with ReadonlyMessageMixin {}
|
||||
|
@ -53,15 +72,34 @@ const NgDepsModel$json = const {
|
|||
'2': const [
|
||||
const {'1': 'library_uri', '3': 1, '4': 1, '5': 9},
|
||||
const {'1': 'part_uris', '3': 2, '4': 3, '5': 9},
|
||||
const {'1': 'imports', '3': 3, '4': 3, '5': 11, '6': '.angular2.src.transform.common.model.proto.ImportModel'},
|
||||
const {'1': 'exports', '3': 4, '4': 3, '5': 11, '6': '.angular2.src.transform.common.model.proto.ExportModel'},
|
||||
const {'1': 'reflectables', '3': 5, '4': 3, '5': 11, '6': '.angular2.src.transform.common.model.proto.ReflectionInfoModel'},
|
||||
const {
|
||||
'1': 'imports',
|
||||
'3': 3,
|
||||
'4': 3,
|
||||
'5': 11,
|
||||
'6': '.angular2.src.transform.common.model.proto.ImportModel'
|
||||
},
|
||||
const {
|
||||
'1': 'exports',
|
||||
'3': 4,
|
||||
'4': 3,
|
||||
'5': 11,
|
||||
'6': '.angular2.src.transform.common.model.proto.ExportModel'
|
||||
},
|
||||
const {
|
||||
'1': 'reflectables',
|
||||
'3': 5,
|
||||
'4': 3,
|
||||
'5': 11,
|
||||
'6': '.angular2.src.transform.common.model.proto.ReflectionInfoModel'
|
||||
},
|
||||
const {'1': 'source_file', '3': 6, '4': 1, '5': 9},
|
||||
],
|
||||
};
|
||||
|
||||
/**
|
||||
* Generated with:
|
||||
* ng_deps_model.proto (c84f449fc55ef46e38a652f65449c6645b9fe6cb)
|
||||
* ng_deps_model.proto (83fe43a087fdd0a7ebee360cd6b669570df4d216)
|
||||
* libprotoc 2.5.0
|
||||
* dart-protoc-plugin (cc35f743de982a4916588b9c505dd21c7fe87d17)
|
||||
*/
|
||||
|
|
|
@ -14,5 +14,10 @@ message NgDepsModel {
|
|||
|
||||
repeated ExportModel exports = 4;
|
||||
|
||||
// All classes in `source_file` marked with @Injectable or a known subclass.
|
||||
repeated ReflectionInfoModel reflectables = 5;
|
||||
|
||||
// The basename of the file from which the ng_deps were generated.
|
||||
// Example: component.dart
|
||||
optional string source_file = 6;
|
||||
}
|
||||
|
|
|
@ -4,12 +4,13 @@ const BOOTSTRAP_NAME = 'bootstrap';
|
|||
const SETUP_METHOD_NAME = 'initReflector';
|
||||
const REFLECTOR_VAR_NAME = 'reflector';
|
||||
const TRANSFORM_DYNAMIC_MODE = 'transform_dynamic';
|
||||
const CSS_EXTENSION = '.css';
|
||||
const SHIMMED_STYLESHEET_EXTENSION = '.css.shim.dart';
|
||||
const NON_SHIMMED_STYLESHEET_EXTENSION = '.css.dart';
|
||||
const DEPS_EXTENSION = '.ng_deps.dart';
|
||||
const DEPS_JSON_EXTENSION = '.ng_deps.json';
|
||||
const META_EXTENSION = '.ng_meta.json';
|
||||
// TODO(sigmund): consider merging into .ng_meta by generating local metadata
|
||||
// upfront (rather than extracting it from ng_deps).
|
||||
const ALIAS_EXTENSION = '.aliases.json';
|
||||
const TEMPLATE_EXTENSION = '.template.dart';
|
||||
const REFLECTION_CAPABILITIES_NAME = 'ReflectionCapabilities';
|
||||
const REFLECTOR_IMPORT = 'package:angular2/src/core/reflection/reflection.dart';
|
||||
const REFLECTOR_PREFIX = '_ngRef';
|
||||
|
@ -18,20 +19,40 @@ const REGISTER_GETTERS_METHOD_NAME = 'registerGetters';
|
|||
const REGISTER_SETTERS_METHOD_NAME = 'registerSetters';
|
||||
const REGISTER_METHODS_METHOD_NAME = 'registerMethods';
|
||||
|
||||
/// Note that due to the implementation of `_toExtension`, ordering is
|
||||
/// important. For example, putting '.dart' first in this list will cause
|
||||
/// incorrect behavior.
|
||||
const ALL_EXTENSIONS = const [
|
||||
DEPS_EXTENSION,
|
||||
DEPS_JSON_EXTENSION,
|
||||
META_EXTENSION,
|
||||
TEMPLATE_EXTENSION,
|
||||
'.dart'
|
||||
];
|
||||
|
||||
/// Returns `uri` with its extension updated to [META_EXTENSION].
|
||||
String toMetaExtension(String uri) =>
|
||||
_toExtension(uri, const [DEPS_EXTENSION, DEPS_JSON_EXTENSION, '.dart'], META_EXTENSION);
|
||||
_toExtension(uri, ALL_EXTENSIONS, META_EXTENSION);
|
||||
|
||||
/// Returns `uri` with its extension updated to [DEPS_EXTENSION].
|
||||
String toDepsExtension(String uri) =>
|
||||
_toExtension(uri, const [META_EXTENSION, DEPS_JSON_EXTENSION, '.dart'], DEPS_EXTENSION);
|
||||
|
||||
/// Returns `uri` with its extension updated to [ALIAS_EXTENSION].
|
||||
String toAliasExtension(String uri) =>
|
||||
_toExtension(uri, const [DEPS_EXTENSION, '.dart'], ALIAS_EXTENSION);
|
||||
_toExtension(uri, ALL_EXTENSIONS, DEPS_EXTENSION);
|
||||
|
||||
/// Returns `uri` with its extension updated to [DEPS_JSON_EXTENSION].
|
||||
String toJsonExtension(String uri) =>
|
||||
_toExtension(uri, const [DEPS_EXTENSION, '.dart'], DEPS_JSON_EXTENSION);
|
||||
_toExtension(uri, ALL_EXTENSIONS, DEPS_JSON_EXTENSION);
|
||||
|
||||
/// Returns `uri` with its extension updated to [TEMPLATES_EXTENSION].
|
||||
String toTemplateExtension(String uri) =>
|
||||
_toExtension(uri, ALL_EXTENSIONS, TEMPLATE_EXTENSION);
|
||||
|
||||
/// Returns `uri` with its extension updated to [SHIMMED_STYLESHEET_EXTENSION].
|
||||
String toShimmedStylesheetExtension(String uri) =>
|
||||
_toExtension(uri, const [CSS_EXTENSION], SHIMMED_STYLESHEET_EXTENSION);
|
||||
|
||||
/// Returns `uri` with its extension updated to [NON_SHIMMED_STYLESHEET_EXTENSION].
|
||||
String toNonShimmedStylesheetExtension(String uri) =>
|
||||
_toExtension(uri, const [CSS_EXTENSION], NON_SHIMMED_STYLESHEET_EXTENSION);
|
||||
|
||||
/// Returns `uri` with its extension updated to `toExtension` if its
|
||||
/// extension is currently in `fromExtension`.
|
||||
|
|
|
@ -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;
|
||||
|
||||
import 'package:angular2/src/core/render/api.dart';
|
||||
import 'convert.dart';
|
||||
import 'package:angular2/src/compiler/directive_metadata.dart';
|
||||
import 'logging.dart';
|
||||
|
||||
/// Metadata about directives and directive aliases.
|
||||
|
@ -20,37 +19,47 @@ import 'logging.dart';
|
|||
/// easier.
|
||||
class NgMeta {
|
||||
/// Directive metadata for each type annotated as a directive.
|
||||
final Map<String, RenderDirectiveMetadata> types;
|
||||
final Map<String, CompileDirectiveMetadata> types;
|
||||
|
||||
/// List of other types and names associated with a given name.
|
||||
final Map<String, List<String>> aliases;
|
||||
|
||||
NgMeta(this.types, this.aliases);
|
||||
/// TODO(kegluneq): Once merged with NgDepsModel, use its exports.
|
||||
final List<String> exports;
|
||||
|
||||
NgMeta.empty() : this({}, {});
|
||||
NgMeta(this.types, this.aliases, this.exports);
|
||||
|
||||
bool get isEmpty => types.isEmpty && aliases.isEmpty;
|
||||
NgMeta.empty() : this({}, {}, []);
|
||||
|
||||
bool get isEmpty => types.isEmpty && aliases.isEmpty && exports.isEmpty;
|
||||
|
||||
/// Parse from the serialized form produced by [toJson].
|
||||
factory NgMeta.fromJson(Map json) {
|
||||
var exports = <String>[];
|
||||
var types = {};
|
||||
var aliases = {};
|
||||
for (var key in json.keys) {
|
||||
var entry = json[key];
|
||||
if (entry['kind'] == 'type') {
|
||||
types[key] = directiveMetadataFromMap(entry['value']);
|
||||
} else if (entry['kind'] == 'alias') {
|
||||
aliases[key] = entry['value'];
|
||||
if (key == '__exports__') {
|
||||
exports = json[key];
|
||||
} else {
|
||||
var entry = json[key];
|
||||
if (entry['kind'] == 'type') {
|
||||
types[key] = CompileDirectiveMetadata.fromJson(entry['value']);
|
||||
} else if (entry['kind'] == 'alias') {
|
||||
aliases[key] = entry['value'];
|
||||
}
|
||||
}
|
||||
}
|
||||
return new NgMeta(types, aliases);
|
||||
return new NgMeta(types, aliases, exports);
|
||||
}
|
||||
|
||||
/// Serialized representation of this instance.
|
||||
Map toJson() {
|
||||
var result = {};
|
||||
result['__exports__'] = exports;
|
||||
|
||||
types.forEach((k, v) {
|
||||
result[k] = {'kind': 'type', 'value': directiveMetadataToMap(v)};
|
||||
result[k] = {'kind': 'type', 'value': v.toJson()};
|
||||
});
|
||||
|
||||
aliases.forEach((k, v) {
|
||||
|
@ -60,14 +69,15 @@ class NgMeta {
|
|||
}
|
||||
|
||||
/// Merge into this instance all information from [other].
|
||||
/// This does not include `exports`.
|
||||
void addAll(NgMeta other) {
|
||||
types.addAll(other.types);
|
||||
aliases.addAll(other.aliases);
|
||||
}
|
||||
|
||||
/// Returns the metadata for every type associated with the given [alias].
|
||||
List<RenderDirectiveMetadata> flatten(String alias) {
|
||||
var result = [];
|
||||
List<CompileDirectiveMetadata> flatten(String alias) {
|
||||
var result = <CompileDirectiveMetadata>[];
|
||||
var seen = new Set();
|
||||
helper(name) {
|
||||
if (!seen.add(name)) {
|
||||
|
|
|
@ -11,12 +11,15 @@ const DEFAULT_OPTIMIZATION_PHASES = 5;
|
|||
const CUSTOM_ANNOTATIONS_PARAM = 'custom_annotations';
|
||||
const ENTRY_POINT_PARAM = 'entry_points';
|
||||
const FORMAT_CODE_PARAM = 'format_code';
|
||||
// TODO(kegluenq): Remove this after 30 Oct (i/4433).
|
||||
const GENERATE_CHANGE_DETECTORS_PARAM = 'generate_change_detectors';
|
||||
const REFLECT_PROPERTIES_AS_ATTRIBUTES = 'reflectPropertiesAsAttributes';
|
||||
const INIT_REFLECTOR_PARAM = 'init_reflector';
|
||||
// TODO(kegluenq): Remove this after 30 Oct (i/4433).
|
||||
const INLINE_VIEWS_PARAM = 'inline_views';
|
||||
const MIRROR_MODE_PARAM = 'mirror_mode';
|
||||
const OPTIMIZATION_PHASES_PARAM = 'optimization_phases';
|
||||
const PRECOMPILE_PARAM = 'precompile';
|
||||
const REFLECTION_ENTRY_POINT_PARAM = 'reflection_entry_points';
|
||||
|
||||
/// Provides information necessary to transform an Angular2 app.
|
||||
|
@ -35,15 +38,9 @@ class TransformerOptions {
|
|||
/// Whether to generate calls to our generated `initReflector` code
|
||||
final bool initReflector;
|
||||
|
||||
/// Whether to inline html/css urls into the View directive
|
||||
final bool inlineViews;
|
||||
|
||||
/// The [AnnotationMatcher] which is used to identify angular annotations.
|
||||
final AnnotationMatcher annotationMatcher;
|
||||
|
||||
/// Whether to create change detector classes for discovered `@View`s.
|
||||
final bool generateChangeDetectors;
|
||||
|
||||
final bool reflectPropertiesAsAttributes;
|
||||
|
||||
/// The number of phases to spend optimizing output size.
|
||||
|
@ -68,9 +65,7 @@ class TransformerOptions {
|
|||
this.initReflector,
|
||||
this.annotationMatcher,
|
||||
this.optimizationPhases,
|
||||
{this.generateChangeDetectors,
|
||||
this.reflectPropertiesAsAttributes,
|
||||
this.inlineViews,
|
||||
{this.reflectPropertiesAsAttributes,
|
||||
this.formatCode});
|
||||
|
||||
factory TransformerOptions(List<String> entryPoints,
|
||||
|
@ -80,7 +75,6 @@ class TransformerOptions {
|
|||
List<ClassDescriptor> customAnnotationDescriptors: const [],
|
||||
int optimizationPhases: DEFAULT_OPTIMIZATION_PHASES,
|
||||
bool inlineViews: true,
|
||||
bool generateChangeDetectors: true,
|
||||
bool reflectPropertiesAsAttributes: true,
|
||||
bool formatCode: false}) {
|
||||
var annotationMatcher = new AnnotationMatcher()
|
||||
|
@ -97,9 +91,7 @@ class TransformerOptions {
|
|||
initReflector,
|
||||
annotationMatcher,
|
||||
optimizationPhases,
|
||||
generateChangeDetectors: generateChangeDetectors,
|
||||
reflectPropertiesAsAttributes: reflectPropertiesAsAttributes,
|
||||
inlineViews: inlineViews,
|
||||
formatCode: formatCode);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,9 +11,6 @@ TransformerOptions parseBarbackSettings(BarbackSettings settings) {
|
|||
var entryPoints = _readFileList(config, ENTRY_POINT_PARAM);
|
||||
var initReflector =
|
||||
_readBool(config, INIT_REFLECTOR_PARAM, defaultValue: true);
|
||||
var inlineViews = _readBool(config, INLINE_VIEWS_PARAM, defaultValue: true);
|
||||
var generateChangeDetectors =
|
||||
_readBool(config, GENERATE_CHANGE_DETECTORS_PARAM, defaultValue: true);
|
||||
var reflectPropertiesAsAttributes =
|
||||
_readBool(config, REFLECT_PROPERTIES_AS_ATTRIBUTES, defaultValue: false);
|
||||
var formatCode = _readBool(config, FORMAT_CODE_PARAM, defaultValue: false);
|
||||
|
@ -37,10 +34,8 @@ TransformerOptions parseBarbackSettings(BarbackSettings settings) {
|
|||
modeName: settings.mode.name,
|
||||
mirrorMode: mirrorMode,
|
||||
initReflector: initReflector,
|
||||
inlineViews: inlineViews,
|
||||
customAnnotationDescriptors: _readCustomAnnotations(config),
|
||||
optimizationPhases: optimizationPhases,
|
||||
generateChangeDetectors: generateChangeDetectors,
|
||||
reflectPropertiesAsAttributes: reflectPropertiesAsAttributes,
|
||||
formatCode: formatCode);
|
||||
}
|
||||
|
@ -136,4 +131,14 @@ void _warnDeprecated(Map config) {
|
|||
print('${REFLECTION_ENTRY_POINT_PARAM} is no longer necessary for '
|
||||
'Angular 2 apps. Please remove it from your pubspec.');
|
||||
}
|
||||
if (config.containsKey(GENERATE_CHANGE_DETECTORS_PARAM)) {
|
||||
print('${GENERATE_CHANGE_DETECTORS_PARAM} is no longer necessary for '
|
||||
'Angular 2 apps. Please remove it from your pubspec.');
|
||||
}
|
||||
if (config.containsKey(INLINE_VIEWS_PARAM)) {
|
||||
print('Parameter "${INLINE_VIEWS_PARAM}" no longer has any effect on the '
|
||||
'Angular2 transformer. Inlining of views is only needed for tests that '
|
||||
'manipulate component metadata. For this purpose, use transformer '
|
||||
'angular2/src/transform/inliner_for_test.');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,8 +2,6 @@ library angular2.transform.common.registered_type;
|
|||
|
||||
import 'package:analyzer/analyzer.dart';
|
||||
import 'package:angular2/src/core/render/api.dart';
|
||||
import 'package:angular2/src/transform/common/directive_metadata_reader.dart';
|
||||
import 'package:angular2/src/transform/common/logging.dart';
|
||||
import 'package:angular2/src/transform/common/names.dart';
|
||||
|
||||
/// A call to `Reflector#registerType` generated by `DirectiveProcessor`.
|
||||
|
@ -29,8 +27,6 @@ class RegisteredType {
|
|||
/// The property metadata registered.
|
||||
final Expression propMetadata;
|
||||
|
||||
RenderDirectiveMetadata _directiveMetadata = null;
|
||||
|
||||
RegisteredType._(
|
||||
this.typeName,
|
||||
this.registerMethod,
|
||||
|
@ -49,20 +45,7 @@ class RegisteredType {
|
|||
visitor.factoryFn, visitor.parameters, visitor.annotations, visitor.propMetadata);
|
||||
}
|
||||
|
||||
RenderDirectiveMetadata get directiveMetadata {
|
||||
if (_directiveMetadata == null) {
|
||||
try {
|
||||
/// TODO(kegluneq): Allow passing a lifecycle interface matcher.
|
||||
_directiveMetadata = readDirectiveMetadata(reflectionInfoCreate);
|
||||
if (_directiveMetadata != null) {
|
||||
_directiveMetadata.id = '$typeName';
|
||||
}
|
||||
} on FormatException catch (ex) {
|
||||
logger.error(ex.message);
|
||||
}
|
||||
}
|
||||
return _directiveMetadata;
|
||||
}
|
||||
RenderDirectiveMetadata get directiveMetadata => null;
|
||||
}
|
||||
|
||||
class _ParseRegisterTypeVisitor extends Object
|
||||
|
|
|
@ -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 'package:angular2/src/core/render/xhr.dart' show XHR;
|
||||
import 'package:angular2/src/transform/common/asset_reader.dart';
|
||||
import 'package:angular2/src/transform/common/logging.dart';
|
||||
import 'package:barback/barback.dart';
|
||||
import 'package:code_transformers/assets.dart';
|
||||
|
||||
class XhrImpl implements XHR {
|
||||
final AssetReader _reader;
|
||||
final AssetId _entryPoint;
|
||||
|
||||
XhrImpl(this._reader, this._entryPoint);
|
||||
XhrImpl(this._reader);
|
||||
|
||||
Future<String> get(String url) async {
|
||||
var assetId = uriToAssetId(_entryPoint, url, logger, null /* span */,
|
||||
errorOnAbsolute: false);
|
||||
if (assetId == null) {
|
||||
logger.error(
|
||||
'Uri $url not supported from $_entryPoint, could not build AssetId');
|
||||
return null;
|
||||
final uri = Uri.parse(url);
|
||||
if (uri.scheme != 'asset') {
|
||||
throw new FormatException('Unsupported uri encountered: $uri', url);
|
||||
}
|
||||
var templateExists = await _reader.hasInput(assetId);
|
||||
if (!templateExists) {
|
||||
logger.error('Could not read asset at uri $url from $_entryPoint');
|
||||
return null;
|
||||
final assetId =
|
||||
new AssetId(uri.pathSegments.first, uri.pathSegments.skip(1).join('/'));
|
||||
|
||||
if (!await _reader.hasInput(assetId)) {
|
||||
throw new ArgumentError.value('Could not read asset at uri $url', 'url');
|
||||
}
|
||||
return await _reader.readAsString(assetId);
|
||||
return _reader.readAsString(assetId);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
await log.initZoned(transform, () async {
|
||||
var reader = new AssetReader.fromTransform(transform);
|
||||
var assetId = transform.primaryInput.id;
|
||||
var ngDepsModel = await linkNgDeps(reader, assetId);
|
||||
var primaryId = transform.primaryInput.id;
|
||||
var ngDepsModel = await linkNgDeps(reader, primaryId);
|
||||
// See above
|
||||
// transform.consumePrimary();
|
||||
var outputAssetId = _depsAssetId(assetId);
|
||||
var outputAssetId = _depsAssetId(primaryId);
|
||||
if (ngDepsModel != null) {
|
||||
var buf = new StringBuffer();
|
||||
var writer = new NgDepsWriter(buf);
|
||||
writer.writeNgDepsModel(ngDepsModel);
|
||||
var formattedCode = formatter.format('$buf', uri: assetId.path);
|
||||
var formattedCode = formatter.format('$buf', uri: primaryId.path);
|
||||
transform.addOutput(new Asset.fromString(outputAssetId, formattedCode));
|
||||
} else {
|
||||
transform.addOutput(new Asset.fromString(outputAssetId, ''));
|
||||
|
|
|
@ -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/src/generated/ast.dart';
|
||||
import 'package:analyzer/src/generated/error.dart';
|
||||
import 'package:analyzer/src/generated/parser.dart';
|
||||
import 'package:analyzer/src/generated/scanner.dart';
|
||||
import 'package:analyzer/src/string_source.dart';
|
||||
import 'package:angular2/src/core/render/xhr.dart' show XHR;
|
||||
import 'package:angular2/src/transform/common/async_string_writer.dart';
|
||||
import 'package:angular2/src/transform/common/logging.dart';
|
||||
import 'package:angular2/src/transform/common/model/annotation_model.pb.dart';
|
||||
import 'package:angular2/src/transform/common/model/ng_deps_model.pb.dart';
|
||||
import 'package:code_transformers/assets.dart';
|
||||
import 'package:angular2/src/transform/common/asset_reader.dart';
|
||||
import 'package:barback/barback.dart' show AssetId;
|
||||
import 'package:source_span/source_span.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
|
||||
Future inlineViewProps(XHR xhr, NgDepsModel model) {
|
||||
var toWait = <Future>[];
|
||||
for (var reflectable in model.reflectables) {
|
||||
for (var annotation in reflectable.annotations) {
|
||||
if (annotation.isView) {
|
||||
var rawTemplateUrl = _getTemplateUrlValue(annotation);
|
||||
if (rawTemplateUrl != null) {
|
||||
if (_hasTemplateValue(annotation)) {
|
||||
logger.warning('Both template url and template are specified. '
|
||||
'Ignoring `templateUrl` value.');
|
||||
} else {
|
||||
var url = _dumbEval(rawTemplateUrl);
|
||||
if (url is String) {
|
||||
toWait.add(_readOrEmptyString(xhr, url).then((templateText) {
|
||||
_setTemplateValue(annotation, "r'''$templateText'''");
|
||||
}));
|
||||
} else {
|
||||
logger.warning('template url is not a String ($rawTemplateUrl)');
|
||||
}
|
||||
}
|
||||
}
|
||||
var rawStyleUrls = _getStyleUrlsValue(annotation);
|
||||
if (rawStyleUrls != null) {
|
||||
if (_hasStylesValue(annotation)) {
|
||||
logger.warning('Both styleUrls and styles are specified. '
|
||||
'Ignoring `styleUrls` value.');
|
||||
} else {
|
||||
var urls = _dumbEval(rawStyleUrls);
|
||||
if (urls is List) {
|
||||
var writer = new AsyncStringWriter();
|
||||
for (var url in urls) {
|
||||
if (url is String) {
|
||||
writer.print("r'''");
|
||||
writer.asyncPrint(_readOrEmptyString(xhr, url));
|
||||
writer.print("''', ");
|
||||
} else {
|
||||
logger.warning('style url is not a String (${url})');
|
||||
}
|
||||
}
|
||||
toWait.add(writer.asyncToString().then((styleUrlText) {
|
||||
_setStylesValue(annotation, 'const [$styleUrlText]');
|
||||
_removeStyleUrlsValue(annotation);
|
||||
}));
|
||||
} else {
|
||||
logger.warning(
|
||||
'styleUrls is not a List of strings ($rawStyleUrls)');
|
||||
}
|
||||
}
|
||||
}
|
||||
/// Reads the code at `assetId`, inlining any `part` directives in that code.
|
||||
///
|
||||
/// Returns `null` if the code represented by `assetId` is a `part`.
|
||||
///
|
||||
/// Order of `part`s is preserved. That is, if in the main library we have
|
||||
/// ```
|
||||
/// library main;
|
||||
///
|
||||
/// part 'lib1.dart'
|
||||
/// part 'lib2.dart'
|
||||
/// ```
|
||||
/// The output will first have the contents of lib1 followed by the contents of
|
||||
/// lib2.dart, followed by the original code in the library.
|
||||
Future<String> inlineParts(AssetReader reader, AssetId assetId) async {
|
||||
var code = await reader.readAsString(assetId);
|
||||
|
||||
var directivesVisitor = new _NgDepsDirectivesVisitor();
|
||||
parseDirectives(code, name: assetId.path)
|
||||
.directives
|
||||
.accept(directivesVisitor);
|
||||
|
||||
// If this is part of another library, its contents will be processed by its
|
||||
// parent, so it does not need its own `.ng_deps.dart` file.
|
||||
if (directivesVisitor.isPart) return null;
|
||||
|
||||
return _getAllDeclarations(reader, assetId, code, directivesVisitor);
|
||||
}
|
||||
|
||||
/// Processes `visitor.parts`, reading and appending their contents to the
|
||||
/// original `code`.
|
||||
Future<String> _getAllDeclarations(AssetReader reader, AssetId assetId,
|
||||
String code, _NgDepsDirectivesVisitor visitor) {
|
||||
if (visitor.parts.isEmpty) return new Future<String>.value(code);
|
||||
|
||||
var partsStart = visitor.parts.first.offset,
|
||||
partsEnd = visitor.parts.last.end;
|
||||
|
||||
var asyncWriter = new AsyncStringWriter(code.substring(0, partsStart));
|
||||
visitor.parts.forEach((partDirective) {
|
||||
var uri = stringLiteralToString(partDirective.uri);
|
||||
var partAssetId = uriToAssetId(assetId, uri, logger, null /* span */,
|
||||
errorOnAbsolute: false);
|
||||
asyncWriter.asyncPrint(reader.readAsString(partAssetId).then((partCode) {
|
||||
if (partCode == null || partCode.isEmpty) {
|
||||
logger.warning('Empty part at "${partDirective.uri}. Ignoring.',
|
||||
asset: partAssetId);
|
||||
return '';
|
||||
}
|
||||
}
|
||||
// Remove any directives -- we just want declarations.
|
||||
var parsedDirectives = parseDirectives(partCode, name: uri).directives;
|
||||
return partCode.substring(parsedDirectives.last.end);
|
||||
}).catchError((err, stackTrace) {
|
||||
logger.warning(
|
||||
'Failed while reading part at ${partDirective.uri}. Ignoring.\n'
|
||||
'Error: $err\n'
|
||||
'Stack Trace: $stackTrace',
|
||||
asset: partAssetId,
|
||||
span: new SourceFile(code, url: path.basename(assetId.path))
|
||||
.span(partDirective.offset, partDirective.end));
|
||||
}));
|
||||
});
|
||||
asyncWriter.print(code.substring(partsEnd));
|
||||
|
||||
return asyncWriter.asyncToString();
|
||||
}
|
||||
|
||||
/// Visitor responsible for reading the `part` files of the visited AST and
|
||||
/// determining if it is a part of another library.
|
||||
class _NgDepsDirectivesVisitor extends Object with SimpleAstVisitor<Object> {
|
||||
/// Whether the file we are processing is a part, that is, whether we have
|
||||
/// visited a `part of` directive.
|
||||
bool _isPart = false;
|
||||
|
||||
final List<PartDirective> _parts = <PartDirective>[];
|
||||
bool get isPart => _isPart;
|
||||
|
||||
/// In the order encountered in the source.
|
||||
Iterable<PartDirective> get parts => _parts;
|
||||
|
||||
@override
|
||||
Object visitPartDirective(PartDirective node) {
|
||||
_parts.add(node);
|
||||
return null;
|
||||
}
|
||||
return Future.wait(toWait);
|
||||
}
|
||||
|
||||
String _getNamedArgValue(AnnotationModel model, String argName) {
|
||||
var value = null;
|
||||
if (model.namedParameters != null) {
|
||||
var match = model.namedParameters
|
||||
.firstWhere((p) => p.name == argName, orElse: () => null);
|
||||
value = match != null ? match.value : null;
|
||||
@override
|
||||
Object visitPartOfDirective(PartOfDirective node) {
|
||||
_isPart = true;
|
||||
return null;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
String _getTemplateUrlValue(AnnotationModel model) =>
|
||||
_getNamedArgValue(model, 'templateUrl');
|
||||
String _getStyleUrlsValue(AnnotationModel model) =>
|
||||
_getNamedArgValue(model, 'styleUrls');
|
||||
|
||||
bool _hasTemplateValue(AnnotationModel model) =>
|
||||
_getNamedArgValue(model, 'template') != null;
|
||||
bool _hasStylesValue(AnnotationModel model) =>
|
||||
_getNamedArgValue(model, 'styles') != null;
|
||||
|
||||
void _setNamedArgValue(AnnotationModel model, String argName, String argValue) {
|
||||
var matchedArg = model.namedParameters
|
||||
.firstWhere((p) => p.name == argName, orElse: () => null);
|
||||
if (matchedArg == null) {
|
||||
matchedArg = new NamedParameter()..name = argName;
|
||||
model.namedParameters.add(matchedArg);
|
||||
}
|
||||
matchedArg.value = argValue;
|
||||
}
|
||||
|
||||
void _setTemplateValue(AnnotationModel model, String template) =>
|
||||
_setNamedArgValue(model, 'template', template);
|
||||
void _setStylesValue(AnnotationModel model, String styles) =>
|
||||
_setNamedArgValue(model, 'styles', styles);
|
||||
|
||||
void _removeNamedArg(AnnotationModel model, String argName) {
|
||||
model.namedParameters.removeWhere((p) => p.name == argName);
|
||||
}
|
||||
|
||||
void _removeStyleUrlsValue(AnnotationModel model) =>
|
||||
_removeNamedArg(model, 'styleUrls');
|
||||
|
||||
final _constantEvaluator = new ConstantEvaluator();
|
||||
|
||||
/// Attempts to read the content from {@link url}, if it returns null then
|
||||
/// just return the empty string.
|
||||
Future<String> _readOrEmptyString(XHR xhr, String url) async {
|
||||
var content = await xhr.get(url);
|
||||
if (content == null) {
|
||||
content = '';
|
||||
}
|
||||
return content;
|
||||
}
|
||||
|
||||
dynamic _dumbEval(String code) {
|
||||
var source = new StringSource(code, code);
|
||||
// TODO(kegluneq): Report errors.
|
||||
var errorCollector = AnalysisErrorListener.NULL_LISTENER;
|
||||
|
||||
var reader = new CharSequenceReader(code);
|
||||
var scanner = new Scanner(source, reader, errorCollector);
|
||||
var parser = new Parser(source, errorCollector)
|
||||
..currentToken = scanner.tokenize();
|
||||
var expr = parser.parseExpression2();
|
||||
var val = null;
|
||||
if (expr is SimpleStringLiteral) {
|
||||
val = stringLiteralToString(expr);
|
||||
} else {
|
||||
val = expr.accept(_constantEvaluator);
|
||||
}
|
||||
return val != ConstantEvaluator.NOT_A_CONSTANT ? val : null;
|
||||
}
|
||||
|
|
|
@ -5,16 +5,15 @@ import 'dart:async';
|
|||
import 'package:analyzer/analyzer.dart';
|
||||
import 'package:angular2/src/transform/common/annotation_matcher.dart';
|
||||
import 'package:angular2/src/transform/common/asset_reader.dart';
|
||||
import 'package:angular2/src/transform/common/async_string_writer.dart';
|
||||
import 'package:angular2/src/transform/common/code/ng_deps_code.dart';
|
||||
import 'package:angular2/src/transform/common/directive_metadata_reader.dart';
|
||||
import 'package:angular2/src/transform/common/interface_matcher.dart';
|
||||
import 'package:angular2/src/transform/common/logging.dart';
|
||||
import 'package:angular2/src/transform/common/model/ng_deps_model.pb.dart';
|
||||
import 'package:angular2/src/transform/common/xhr_impl.dart';
|
||||
import 'package:angular2/src/transform/common/ng_compiler.dart';
|
||||
import 'package:angular2/src/transform/common/ng_meta.dart';
|
||||
import 'package:barback/barback.dart' show AssetId;
|
||||
import 'package:code_transformers/assets.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
import 'package:source_span/source_span.dart';
|
||||
import 'package:angular2/src/compiler/template_compiler.dart';
|
||||
|
||||
import 'inliner.dart';
|
||||
|
||||
|
@ -26,136 +25,44 @@ import 'inliner.dart';
|
|||
/// string unless `forceGenerate` is true, in which case an empty ngDeps
|
||||
/// file is created.
|
||||
Future<NgDepsModel> createNgDeps(AssetReader reader, AssetId assetId,
|
||||
AnnotationMatcher annotationMatcher, NgMeta ngMeta,
|
||||
{bool inlineViews}) async {
|
||||
AnnotationMatcher annotationMatcher, NgMeta ngMeta) async {
|
||||
// TODO(kegluneq): Shortcut if we can determine that there are no
|
||||
// [Directive]s present, taking into account `export`s.
|
||||
var code = await reader.readAsString(assetId);
|
||||
var codeWithParts = await inlineParts(reader, assetId);
|
||||
if (codeWithParts == null || codeWithParts.isEmpty) return null;
|
||||
|
||||
var directivesVisitor = new _NgDepsDirectivesVisitor();
|
||||
parseDirectives(code, name: assetId.path)
|
||||
.directives
|
||||
.accept(directivesVisitor);
|
||||
|
||||
// If this is part of another library, its contents will be processed by its
|
||||
// parent, so it does not need its own `.ng_deps.dart` file.
|
||||
if (directivesVisitor.isPart) return null;
|
||||
|
||||
var codeWithParts =
|
||||
await _getAllDeclarations(reader, assetId, code, directivesVisitor);
|
||||
var parsedCode =
|
||||
parseCompilationUnit(codeWithParts, name: '${assetId.path} and parts');
|
||||
|
||||
var ngDepsVisitor = new NgDepsVisitor(assetId, annotationMatcher);
|
||||
// TODO(kegluneq): Parse `declarations` in the NgDepsModel as well.
|
||||
parsedCode.accept(ngDepsVisitor);
|
||||
var ngDepsModel = ngDepsVisitor.model;
|
||||
|
||||
var ngMetaVisitor = new _NgMetaVisitor(ngMeta);
|
||||
var templateCompiler = createTemplateCompiler(reader);
|
||||
var ngMetaVisitor = new _NgMetaVisitor(
|
||||
ngMeta, assetId, annotationMatcher, _interfaceMatcher, templateCompiler);
|
||||
parsedCode.accept(ngMetaVisitor);
|
||||
await ngMetaVisitor.whenDone();
|
||||
|
||||
// If this file imports only dart: libraries and does not define any
|
||||
// reflectables of its own, it doesn't need a .ng_deps.dart file.
|
||||
if (!directivesVisitor.usesNonLangLibs &&
|
||||
(ngDepsModel.reflectables == null || ngDepsModel.reflectables.isEmpty)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (inlineViews) {
|
||||
await inlineViewProps(new XhrImpl(reader, assetId), ngDepsModel);
|
||||
if (ngDepsModel.reflectables == null || ngDepsModel.reflectables.isEmpty) {
|
||||
if (ngDepsModel.imports.every(_isDartImport) &&
|
||||
ngDepsModel.exports.every(_isDartImport)) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return ngDepsModel;
|
||||
}
|
||||
|
||||
/// Processes `visitor.parts`, reading and appending their contents to the
|
||||
/// original `code`.
|
||||
/// Order of `part`s is preserved. That is, if in the main library we have
|
||||
/// ```
|
||||
/// library main;
|
||||
///
|
||||
/// part 'lib1.dart'
|
||||
/// part 'lib2.dart'
|
||||
/// ```
|
||||
/// The output will first have the contents of lib1 followed by the contents of
|
||||
/// lib2.dart, followed by the original code in the library.
|
||||
Future<String> _getAllDeclarations(AssetReader reader, AssetId assetId,
|
||||
String code, _NgDepsDirectivesVisitor visitor) {
|
||||
if (visitor.parts.isEmpty) return new Future<String>.value(code);
|
||||
// `model` can be an [ImportModel] or [ExportModel].
|
||||
bool _isDartImport(dynamic model) => model.uri.startsWith('dart:');
|
||||
|
||||
var partsStart = visitor.parts.first.offset,
|
||||
partsEnd = visitor.parts.last.end;
|
||||
// TODO(kegluneq): Allow the caller to provide an InterfaceMatcher.
|
||||
final _interfaceMatcher = new InterfaceMatcher();
|
||||
|
||||
var asyncWriter = new AsyncStringWriter(code.substring(0, partsStart));
|
||||
visitor.parts.forEach((partDirective) {
|
||||
var uri = stringLiteralToString(partDirective.uri);
|
||||
var partAssetId = uriToAssetId(assetId, uri, logger, null /* span */,
|
||||
errorOnAbsolute: false);
|
||||
asyncWriter.asyncPrint(reader.readAsString(partAssetId).then((partCode) {
|
||||
if (partCode == null || partCode.isEmpty) {
|
||||
logger.warning('Empty part at "${partDirective.uri}. Ignoring.',
|
||||
asset: partAssetId);
|
||||
return '';
|
||||
}
|
||||
// Remove any directives -- we just want declarations.
|
||||
var parsedDirectives = parseDirectives(partCode, name: uri).directives;
|
||||
return partCode.substring(parsedDirectives.last.end);
|
||||
}).catchError((err, stackTrace) {
|
||||
logger.warning(
|
||||
'Failed while reading part at ${partDirective.uri}. Ignoring.\n'
|
||||
'Error: $err\n'
|
||||
'Stack Trace: $stackTrace',
|
||||
asset: partAssetId,
|
||||
span: new SourceFile(code, url: path.basename(assetId.path))
|
||||
.span(partDirective.offset, partDirective.end));
|
||||
}));
|
||||
});
|
||||
asyncWriter.print(code.substring(partsEnd));
|
||||
|
||||
return asyncWriter.asyncToString();
|
||||
}
|
||||
|
||||
/// Visitor responsible for flattening directives passed to it.
|
||||
/// Once this has visited an Ast, use [#writeTo] to write out the directives
|
||||
/// for the .ng_deps.dart file. See [#writeTo] for details.
|
||||
class _NgDepsDirectivesVisitor extends Object with SimpleAstVisitor<Object> {
|
||||
/// Whether this library `imports` or `exports` any non-'dart:' libraries.
|
||||
bool _usesNonLangLibs = false;
|
||||
|
||||
/// Whether the file we are processing is a part, that is, whether we have
|
||||
/// visited a `part of` directive.
|
||||
bool _isPart = false;
|
||||
|
||||
final List<PartDirective> _parts = <PartDirective>[];
|
||||
|
||||
bool get usesNonLangLibs => _usesNonLangLibs;
|
||||
bool get isPart => _isPart;
|
||||
|
||||
/// In the order encountered in the source.
|
||||
Iterable<PartDirective> get parts => _parts;
|
||||
|
||||
Object _updateUsesNonLangLibs(UriBasedDirective directive) {
|
||||
_usesNonLangLibs = _usesNonLangLibs ||
|
||||
!stringLiteralToString(directive.uri).startsWith('dart:');
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
Object visitExportDirective(ExportDirective node) =>
|
||||
_updateUsesNonLangLibs(node);
|
||||
|
||||
@override
|
||||
Object visitImportDirective(ImportDirective node) =>
|
||||
_updateUsesNonLangLibs(node);
|
||||
|
||||
@override
|
||||
Object visitPartDirective(PartDirective node) {
|
||||
_parts.add(node);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// Visitor responsible for visiting a file's [Declaration]s and outputting the
|
||||
/// Visitor responsible for visiting a file and outputting the
|
||||
/// code necessary to register the file with the Angular 2 system.
|
||||
class _NgMetaVisitor extends Object with SimpleAstVisitor<Object> {
|
||||
/// Output ngMeta information about aliases.
|
||||
|
@ -164,14 +71,52 @@ class _NgMetaVisitor extends Object with SimpleAstVisitor<Object> {
|
|||
// parsing the ngdeps files later.
|
||||
final NgMeta ngMeta;
|
||||
|
||||
_NgMetaVisitor(this.ngMeta);
|
||||
/// The [AssetId] we are currently processing.
|
||||
final AssetId assetId;
|
||||
|
||||
final DirectiveMetadataReader _reader;
|
||||
final _normalizations = <Future>[];
|
||||
|
||||
_NgMetaVisitor(this.ngMeta, this.assetId, AnnotationMatcher annotationMatcher,
|
||||
InterfaceMatcher interfaceMatcher, TemplateCompiler templateCompiler)
|
||||
: _reader = new DirectiveMetadataReader(
|
||||
annotationMatcher, interfaceMatcher, templateCompiler);
|
||||
|
||||
Future whenDone() {
|
||||
return Future.wait(_normalizations);
|
||||
}
|
||||
|
||||
@override
|
||||
Object visitCompilationUnit(CompilationUnit node) {
|
||||
if (node == null || node.declarations == null) return null;
|
||||
if (node == null ||
|
||||
(node.directives == null && node.declarations == null)) {
|
||||
return null;
|
||||
}
|
||||
node.directives.accept(this);
|
||||
return node.declarations.accept(this);
|
||||
}
|
||||
|
||||
@override
|
||||
Object visitExportDirective(ExportDirective node) {
|
||||
ngMeta.exports.add(stringLiteralToString(node.uri));
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
Object visitClassDeclaration(ClassDeclaration node) {
|
||||
_normalizations.add(_reader
|
||||
.readDirectiveMetadata(node, assetId)
|
||||
.then((compileDirectiveMetadata) {
|
||||
if (compileDirectiveMetadata != null) {
|
||||
ngMeta.types[compileDirectiveMetadata.type.name] =
|
||||
compileDirectiveMetadata;
|
||||
}
|
||||
}).catchError((err) {
|
||||
logger.error('ERROR: $err');
|
||||
}));
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
Object visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) {
|
||||
// We process any top-level declaration that fits the directive-alias
|
||||
|
|
|
@ -3,6 +3,7 @@ library angular2.transform.directive_processor.transformer;
|
|||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:angular2/src/core/dom/html_adapter.dart';
|
||||
import 'package:angular2/src/transform/common/asset_reader.dart';
|
||||
import 'package:angular2/src/transform/common/logging.dart' as log;
|
||||
import 'package:angular2/src/transform/common/names.dart';
|
||||
|
@ -23,6 +24,7 @@ import 'rewriter.dart';
|
|||
/// be followed by {@link DirectiveLinker}.
|
||||
class DirectiveProcessor extends Transformer implements DeclaringTransformer {
|
||||
final TransformerOptions options;
|
||||
final _encoder = const JsonEncoder.withIndent(' ');
|
||||
|
||||
DirectiveProcessor(this.options);
|
||||
|
||||
|
@ -34,33 +36,40 @@ class DirectiveProcessor extends Transformer implements DeclaringTransformer {
|
|||
/// determine that one or the other will not be emitted.
|
||||
@override
|
||||
declareOutputs(DeclaringTransform transform) {
|
||||
transform.declareOutput(_depsOutputId(transform.primaryId));
|
||||
transform.declareOutput(_metaOutputId(transform.primaryId));
|
||||
transform.declareOutput(_ngMetaAssetId(transform.primaryId));
|
||||
transform.declareOutput(_ngDepsAssetId(transform.primaryId));
|
||||
}
|
||||
|
||||
@override
|
||||
Future apply(Transform transform) async {
|
||||
Html5LibDomAdapter.makeCurrent();
|
||||
await log.initZoned(transform, () async {
|
||||
var assetId = transform.primaryInput.id;
|
||||
var primaryId = transform.primaryInput.id;
|
||||
var reader = new AssetReader.fromTransform(transform);
|
||||
var ngMeta = new NgMeta.empty();
|
||||
var ngDepsModel = await createNgDeps(
|
||||
reader, assetId, options.annotationMatcher, ngMeta,
|
||||
inlineViews: options.inlineViews);
|
||||
reader, primaryId, options.annotationMatcher, ngMeta);
|
||||
// TODO(kegluneq): Combine NgDepsModel with NgMeta in a single .json file.
|
||||
if (ngDepsModel != null) {
|
||||
var ngDepsAssetId = _ngDepsAssetId(primaryId);
|
||||
transform.addOutput(new Asset.fromString(
|
||||
_depsOutputId(assetId), ngDepsModel.writeToJson()));
|
||||
ngDepsAssetId, _encoder.convert(ngDepsModel.writeToJsonMap())));
|
||||
}
|
||||
var metaOutputId = _metaOutputId(assetId);
|
||||
var metaOutputId = _ngMetaAssetId(primaryId);
|
||||
if (!ngMeta.isEmpty) {
|
||||
transform.addOutput(new Asset.fromString(metaOutputId,
|
||||
new JsonEncoder.withIndent(" ").convert(ngMeta.toJson())));
|
||||
transform.addOutput(new Asset.fromString(
|
||||
metaOutputId, _encoder.convert(ngMeta.toJson())));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
AssetId _depsOutputId(AssetId primaryId) =>
|
||||
primaryId.changeExtension(DEPS_JSON_EXTENSION);
|
||||
AssetId _metaOutputId(AssetId primaryId) =>
|
||||
primaryId.changeExtension(ALIAS_EXTENSION);
|
||||
AssetId _ngMetaAssetId(AssetId primaryInputId) {
|
||||
return new AssetId(
|
||||
primaryInputId.package, toMetaExtension(primaryInputId.path));
|
||||
}
|
||||
|
||||
AssetId _ngDepsAssetId(AssetId primaryInputId) {
|
||||
return new AssetId(
|
||||
primaryInputId.package, toJsonExtension(primaryInputId.path));
|
||||
}
|
||||
|
|
|
@ -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/event_binding.dart';
|
||||
import 'package:angular2/src/core/change_detection/binding_record.dart';
|
||||
import 'package:angular2/src/core/change_detection/codegen_facade.dart' show codify;
|
||||
import 'package:angular2/src/core/change_detection/codegen_facade.dart'
|
||||
show codify;
|
||||
import 'package:angular2/src/core/facade/exceptions.dart' show BaseException;
|
||||
|
||||
/// Responsible for generating change detector classes for Angular 2.
|
||||
|
@ -90,8 +91,9 @@ class _CodegenState {
|
|||
final ChangeDetectorGenConfig _genConfig;
|
||||
final List<BindingTarget> _propertyBindingTargets;
|
||||
|
||||
String get _changeDetectionStrategyAsCode =>
|
||||
_changeDetectionStrategy == null ? 'null' : '${_genPrefix}${_changeDetectionStrategy}';
|
||||
String get _changeDetectionStrategyAsCode => _changeDetectionStrategy == null
|
||||
? 'null'
|
||||
: '${_genPrefix}${_changeDetectionStrategy}';
|
||||
|
||||
/// The module prefix for pregen_proto_change_detector
|
||||
final String _genPrefix;
|
||||
|
@ -110,13 +112,15 @@ class _CodegenState {
|
|||
this._names,
|
||||
this._genConfig);
|
||||
|
||||
factory _CodegenState(String genPrefix, String typeName, String changeDetectorTypeName,
|
||||
ChangeDetectorDefinition def) {
|
||||
factory _CodegenState(String genPrefix, String typeName,
|
||||
String changeDetectorTypeName, ChangeDetectorDefinition def) {
|
||||
var protoRecords = createPropertyRecords(def);
|
||||
var eventBindings = createEventRecords(def);
|
||||
var propertyBindingTargets = def.bindingRecords.map((b) => b.target).toList();
|
||||
var propertyBindingTargets =
|
||||
def.bindingRecords.map((b) => b.target).toList();
|
||||
|
||||
var names = new CodegenNameUtil(protoRecords, eventBindings, def.directiveRecords, '$genPrefix$_UTIL');
|
||||
var names = new CodegenNameUtil(
|
||||
protoRecords, eventBindings, def.directiveRecords, '$genPrefix$_UTIL');
|
||||
var logic = new CodegenLogicUtil(names, '$genPrefix$_UTIL', def.strategy);
|
||||
return new _CodegenState._(
|
||||
genPrefix,
|
||||
|
@ -141,8 +145,8 @@ class _CodegenState {
|
|||
$_changeDetectorTypeName(dispatcher)
|
||||
: super(${codify(_changeDetectorDefId)},
|
||||
dispatcher, ${_records.length},
|
||||
${_changeDetectorTypeName}.gen_propertyBindingTargets,
|
||||
${_changeDetectorTypeName}.gen_directiveIndices,
|
||||
${_changeDetectorTypeName}.${_GEN_PROPERTY_BINDING_TARGETS_NAME},
|
||||
${_changeDetectorTypeName}.${_GEN_DIRECTIVE_INDICES_NAME},
|
||||
${_changeDetectionStrategyAsCode}) {
|
||||
dehydrateDirectives(false);
|
||||
}
|
||||
|
@ -183,18 +187,20 @@ class _CodegenState {
|
|||
}
|
||||
|
||||
String _genPropertyBindingTargets() {
|
||||
var targets = _logic.genPropertyBindingTargets(_propertyBindingTargets, this._genConfig.genDebugInfo);
|
||||
return "static var gen_propertyBindingTargets = ${targets}";
|
||||
var targets = _logic.genPropertyBindingTargets(
|
||||
_propertyBindingTargets, this._genConfig.genDebugInfo);
|
||||
return "static final ${_GEN_PROPERTY_BINDING_TARGETS_NAME} = ${targets}";
|
||||
}
|
||||
|
||||
String _genDirectiveIndices() {
|
||||
var indices = _logic.genDirectiveIndices(_directiveRecords);
|
||||
return "static var gen_directiveIndices = ${indices}";
|
||||
return "static final ${_GEN_DIRECTIVE_INDICES_NAME} = ${indices}";
|
||||
}
|
||||
|
||||
String _maybeGenHandleEventInternal() {
|
||||
if (_eventBindings.length > 0) {
|
||||
var handlers = _eventBindings.map((eb) => _genEventBinding(eb)).join("\n");
|
||||
var handlers =
|
||||
_eventBindings.map((eb) => _genEventBinding(eb)).join("\n");
|
||||
return '''
|
||||
handleEventInternal(eventName, elIndex, locals) {
|
||||
var ${this._names.getPreventDefaultAccesor()} = false;
|
||||
|
@ -216,7 +222,7 @@ class _CodegenState {
|
|||
}''';
|
||||
}
|
||||
|
||||
String _genEventBindingEval(EventBinding eb, ProtoRecord r){
|
||||
String _genEventBindingEval(EventBinding eb, ProtoRecord r) {
|
||||
if (r.lastInBinding) {
|
||||
var evalRecord = _logic.genEventBindingEvalValue(eb, r);
|
||||
var markPath = _genMarkPathToRootAsCheckOnce(r);
|
||||
|
@ -271,7 +277,8 @@ class _CodegenState {
|
|||
}
|
||||
|
||||
String _maybeGenAfterContentLifecycleCallbacks() {
|
||||
var directiveNotifications = _logic.genContentLifecycleCallbacks(_directiveRecords);
|
||||
var directiveNotifications =
|
||||
_logic.genContentLifecycleCallbacks(_directiveRecords);
|
||||
if (directiveNotifications.isNotEmpty) {
|
||||
return '''
|
||||
void afterContentLifecycleCallbacksInternal() {
|
||||
|
@ -284,7 +291,8 @@ class _CodegenState {
|
|||
}
|
||||
|
||||
String _maybeGenAfterViewLifecycleCallbacks() {
|
||||
var directiveNotifications = _logic.genViewLifecycleCallbacks(_directiveRecords);
|
||||
var directiveNotifications =
|
||||
_logic.genViewLifecycleCallbacks(_directiveRecords);
|
||||
if (directiveNotifications.isNotEmpty) {
|
||||
return '''
|
||||
void afterViewLifecycleCallbacksInternal() {
|
||||
|
@ -420,7 +428,9 @@ class _CodegenState {
|
|||
|
||||
var newValue = _names.getLocalName(r.selfIndex);
|
||||
var oldValue = _names.getFieldName(r.selfIndex);
|
||||
var notifyDebug = _genConfig.logBindingUpdate ? "this.logBindingUpdate(${newValue});" : "";
|
||||
var notifyDebug = _genConfig.logBindingUpdate
|
||||
? "this.logBindingUpdate(${newValue});"
|
||||
: "";
|
||||
|
||||
var br = r.bindingRecord;
|
||||
if (br.target.isDirective()) {
|
||||
|
@ -527,4 +537,7 @@ const _NOT_IDENTICAL_CHECK_FN = 'looseNotIdentical';
|
|||
const _IS_CHANGED_LOCAL = 'isChanged';
|
||||
const _PREGEN_PROTO_CHANGE_DETECTOR_IMPORT =
|
||||
'package:angular2/src/core/change_detection/pregen_proto_change_detector.dart';
|
||||
const _GEN_PROPERTY_BINDING_TARGETS_NAME =
|
||||
'${_GEN_PREFIX}_propertyBindingTargets';
|
||||
const _GEN_DIRECTIVE_INDICES_NAME = '${_GEN_PREFIX}_directiveIndices';
|
||||
const _UTIL = 'ChangeDetectionUtil';
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
library angular2.transform.template_compiler.view_definition_creator;
|
||||
library angular2.transform.template_compiler.compile_data_creator;
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:analyzer/analyzer.dart';
|
||||
import 'package:angular2/src/compiler/directive_metadata.dart';
|
||||
import 'package:angular2/src/compiler/template_compiler.dart';
|
||||
import 'package:angular2/src/core/render/api.dart';
|
||||
import 'package:angular2/src/transform/common/asset_reader.dart';
|
||||
import 'package:angular2/src/transform/common/logging.dart';
|
||||
|
@ -13,28 +15,24 @@ import 'package:angular2/src/transform/common/ng_meta.dart';
|
|||
import 'package:barback/barback.dart';
|
||||
import 'package:code_transformers/assets.dart';
|
||||
|
||||
/// Creates [ViewDefinition] objects for all `View` `Directive`s defined in
|
||||
/// `entryPoint`.
|
||||
Future<ViewDefinitionResults> createViewDefinitions(
|
||||
/// Creates [NormalizedComponentWithViewDirectives] objects for all `View`
|
||||
/// `Directive`s defined in `entryPoint`.
|
||||
///
|
||||
/// The returned value wraps the [NgDeps] at `entryPoint` as well as these
|
||||
/// created objects.
|
||||
Future<CompileDataResults> createCompileData(
|
||||
AssetReader reader, AssetId entryPoint) async {
|
||||
return await new _ViewDefinitionCreator(reader, entryPoint).createViewDefs();
|
||||
return new _CompileDataCreator(reader, entryPoint).createCompileData();
|
||||
}
|
||||
|
||||
class ViewDefinitionResults {
|
||||
class CompileDataResults {
|
||||
final NgDeps ngDeps;
|
||||
final Map<RegisteredType, ViewDefinitionEntry> viewDefinitions;
|
||||
ViewDefinitionResults._(this.ngDeps, this.viewDefinitions);
|
||||
final Map<RegisteredType,
|
||||
NormalizedComponentWithViewDirectives> viewDefinitions;
|
||||
|
||||
CompileDataResults._(this.ngDeps, this.viewDefinitions);
|
||||
}
|
||||
|
||||
class ViewDefinitionEntry {
|
||||
final RenderDirectiveMetadata hostMetadata;
|
||||
final ViewDefinition viewDef;
|
||||
|
||||
ViewDefinitionEntry._(this.hostMetadata, this.viewDef);
|
||||
}
|
||||
|
||||
String _getComponentId(AssetId assetId, String className) => '$className';
|
||||
|
||||
// TODO(kegluenq): Improve this test.
|
||||
bool _isViewAnnotation(InstanceCreationExpression node) {
|
||||
var constructorName = node.constructorName.type.name;
|
||||
|
@ -46,42 +44,38 @@ bool _isViewAnnotation(InstanceCreationExpression node) {
|
|||
|
||||
/// Creates [ViewDefinition] objects for all `View` `Directive`s defined in
|
||||
/// `entryPoint`.
|
||||
class _ViewDefinitionCreator {
|
||||
class _CompileDataCreator {
|
||||
final AssetReader reader;
|
||||
final AssetId entryPoint;
|
||||
final Future<NgDeps> ngDepsFuture;
|
||||
|
||||
_ViewDefinitionCreator(AssetReader reader, AssetId entryPoint)
|
||||
_CompileDataCreator(AssetReader reader, AssetId entryPoint)
|
||||
: this.reader = reader,
|
||||
this.entryPoint = entryPoint,
|
||||
ngDepsFuture = NgDeps.parse(reader, entryPoint);
|
||||
|
||||
Future<ViewDefinitionResults> createViewDefs() async {
|
||||
Future<CompileDataResults> createCompileData() async {
|
||||
var ngDeps = await ngDepsFuture;
|
||||
|
||||
var retVal = <RegisteredType, ViewDefinitionEntry>{};
|
||||
var visitor = new _TemplateExtractVisitor(await _extractNgMeta());
|
||||
var retVal = <RegisteredType, NormalizedComponentWithViewDirectives>{};
|
||||
var visitor = new _DirectiveDependenciesVisitor(await _extractNgMeta());
|
||||
ngDeps.registeredTypes.forEach((rType) {
|
||||
visitor.reset();
|
||||
rType.annotations.accept(visitor);
|
||||
if (visitor.viewDef != null) {
|
||||
if (visitor.compileData != null) {
|
||||
// Note: we use '' because the current file maps to the default prefix.
|
||||
var ngMeta = visitor._metadataMap[''];
|
||||
var typeName = '${rType.typeName}';
|
||||
var hostMetadata = null;
|
||||
if (ngMeta.types.containsKey(typeName)) {
|
||||
hostMetadata = ngMeta.types[typeName];
|
||||
visitor.viewDef.componentId = hostMetadata.id;
|
||||
visitor.compileData.component = ngMeta.types[typeName];
|
||||
} else {
|
||||
logger.warning('Missing component "$typeName" in metadata map',
|
||||
asset: entryPoint);
|
||||
visitor.viewDef.componentId = _getComponentId(entryPoint, typeName);
|
||||
}
|
||||
retVal[rType] =
|
||||
new ViewDefinitionEntry._(hostMetadata, visitor.viewDef);
|
||||
retVal[rType] = visitor.compileData;
|
||||
}
|
||||
});
|
||||
return new ViewDefinitionResults._(ngDeps, retVal);
|
||||
return new CompileDataResults._(ngDeps, retVal);
|
||||
}
|
||||
|
||||
/// Creates a map from [AssetId] to import prefix for `.dart` libraries
|
||||
|
@ -89,13 +83,11 @@ class _ViewDefinitionCreator {
|
|||
/// Unprefixed imports have `null` as their value. `entryPoint` is included
|
||||
/// in the map with no prefix.
|
||||
Future<Map<AssetId, String>> _createImportAssetToPrefixMap() async {
|
||||
// TODO(kegluneq): Support `part` directives.
|
||||
var ngDeps = await ngDepsFuture;
|
||||
|
||||
var importAssetToPrefix = <AssetId, String>{};
|
||||
// Include the `.ng_meta.json` file associated with `entryPoint`.
|
||||
importAssetToPrefix[new AssetId(
|
||||
entryPoint.package, toMetaExtension(entryPoint.path))] = null;
|
||||
var importAssetToPrefix = <AssetId, String>{
|
||||
entryPoint: null
|
||||
};
|
||||
|
||||
for (ImportDirective node in ngDeps.imports) {
|
||||
var uri = stringLiteralToString(node.uri);
|
||||
|
@ -113,7 +105,7 @@ class _ViewDefinitionCreator {
|
|||
|
||||
/// Reads the `.ng_meta.json` files associated with all of `entryPoint`'s
|
||||
/// imports and creates a map `Type` name, prefixed if appropriate to the
|
||||
/// associated [RenderDirectiveMetadata].
|
||||
/// associated [CompileDirectiveMetadata].
|
||||
///
|
||||
/// For example, if in `entryPoint` we have:
|
||||
///
|
||||
|
@ -129,13 +121,13 @@ class _ViewDefinitionCreator {
|
|||
/// ```
|
||||
///
|
||||
/// This method will look for `component.ng_meta.json`to contain the
|
||||
/// serialized [RenderDirectiveMetadata] for `MyComponent` and any other
|
||||
/// serialized [CompileDirectiveMetadata] for `MyComponent` and any other
|
||||
/// `Directive`s declared in `component.dart`. We use this information to
|
||||
/// build a map:
|
||||
///
|
||||
/// ```
|
||||
/// {
|
||||
/// "prefix.MyComponent": [RenderDirectiveMetadata for MyComponent],
|
||||
/// "prefix.MyComponent": [CompileDirectiveMetadata for MyComponent],
|
||||
/// ...<any other entries>...
|
||||
/// }
|
||||
/// ```
|
||||
|
@ -153,9 +145,6 @@ class _ViewDefinitionCreator {
|
|||
try {
|
||||
var json = JSON.decode(await reader.readAsString(metaAssetId));
|
||||
var newMetadata = new NgMeta.fromJson(json);
|
||||
newMetadata.types.forEach((className, metadata) {
|
||||
metadata.id = _getComponentId(importAssetId, className);
|
||||
});
|
||||
ngMeta.addAll(newMetadata);
|
||||
} catch (ex, stackTrace) {
|
||||
logger.warning('Failed to decode: $ex, $stackTrace',
|
||||
|
@ -168,23 +157,33 @@ class _ViewDefinitionCreator {
|
|||
}
|
||||
|
||||
/// Visitor responsible for processing the `annotations` property of a
|
||||
/// [RegisterType] object and pulling out [ViewDefinition] information.
|
||||
class _TemplateExtractVisitor extends Object with RecursiveAstVisitor<Object> {
|
||||
ViewDefinition viewDef = null;
|
||||
/// [RegisterType] object, extracting the `directives` dependencies, and adding
|
||||
/// their associated [CompileDirectiveMetadata] to the `directives` of a
|
||||
/// created [NormalizedComponentWithViewDirectives] object.
|
||||
///
|
||||
/// The `component` property of the created
|
||||
/// [NormalizedComponentWithViewDirectives] will be null.
|
||||
///
|
||||
/// If no `View` annotation is found, `compileData` will be null.
|
||||
class _DirectiveDependenciesVisitor extends Object
|
||||
with RecursiveAstVisitor<Object> {
|
||||
NormalizedComponentWithViewDirectives compileData = null;
|
||||
final Map<String, NgMeta> _metadataMap;
|
||||
final ConstantEvaluator _evaluator = new ConstantEvaluator();
|
||||
|
||||
_TemplateExtractVisitor(this._metadataMap);
|
||||
_DirectiveDependenciesVisitor(this._metadataMap);
|
||||
|
||||
void reset() {
|
||||
viewDef = null;
|
||||
compileData = null;
|
||||
}
|
||||
|
||||
/// These correspond to the annotations themselves.
|
||||
/// These correspond to the annotations themselves, which are converted into
|
||||
/// const instance creation expressions so they can be stored in the
|
||||
/// reflector.
|
||||
@override
|
||||
Object visitInstanceCreationExpression(InstanceCreationExpression node) {
|
||||
if (_isViewAnnotation(node)) {
|
||||
viewDef = new ViewDefinition(directives: <RenderDirectiveMetadata>[]);
|
||||
compileData = new NormalizedComponentWithViewDirectives(
|
||||
null, <CompileDirectiveMetadata>[]);
|
||||
node.visitChildren(this);
|
||||
}
|
||||
return null;
|
||||
|
@ -200,43 +199,16 @@ class _TemplateExtractVisitor extends Object with RecursiveAstVisitor<Object> {
|
|||
' Source: ${node}');
|
||||
return null;
|
||||
}
|
||||
var keyString = '${node.name.label}';
|
||||
if (keyString == 'directives') {
|
||||
if ('${node.name.label}' == 'directives') {
|
||||
_readDirectives(node.expression);
|
||||
}
|
||||
if (keyString == 'template' || keyString == 'templateUrl') {
|
||||
// This could happen in a non-View annotation with a `template` or
|
||||
// `templateUrl` property.
|
||||
if (viewDef == null) return null;
|
||||
|
||||
var valueString = node.expression.accept(_evaluator);
|
||||
if (valueString is! String) {
|
||||
logger.error(
|
||||
'Angular 2 currently only supports string literals in directives.'
|
||||
' Source: ${node}');
|
||||
return null;
|
||||
}
|
||||
if (keyString == 'templateUrl') {
|
||||
if (viewDef.templateAbsUrl != null) {
|
||||
logger.error(
|
||||
'Found multiple values for "templateUrl". Source: ${node}');
|
||||
}
|
||||
viewDef.templateAbsUrl = valueString;
|
||||
} else {
|
||||
// keyString == 'template'
|
||||
if (viewDef.template != null) {
|
||||
logger.error('Found multiple values for "template". Source: ${node}');
|
||||
}
|
||||
viewDef.template = valueString;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
void _readDirectives(Expression node) {
|
||||
// This could happen in a non-View annotation with a `directives`
|
||||
// parameter.
|
||||
if (viewDef == null) return;
|
||||
if (compileData == null) return;
|
||||
|
||||
if (node is! ListLiteral) {
|
||||
logger.error('Angular 2 currently only supports list literals as values '
|
||||
|
@ -252,7 +224,7 @@ class _TemplateExtractVisitor extends Object with RecursiveAstVisitor<Object> {
|
|||
name = node.name;
|
||||
} else if (node is PrefixedIdentifier) {
|
||||
ngMeta = _metadataMap[node.prefix.name];
|
||||
name = node.name;
|
||||
name = node.identifier.name;
|
||||
} else {
|
||||
logger.error(
|
||||
'Angular 2 currently only supports simple and prefixed identifiers '
|
||||
|
@ -260,9 +232,9 @@ class _TemplateExtractVisitor extends Object with RecursiveAstVisitor<Object> {
|
|||
return;
|
||||
}
|
||||
if (ngMeta.types.containsKey(name)) {
|
||||
viewDef.directives.add(ngMeta.types[name]);
|
||||
compileData.directives.add(ngMeta.types[name]);
|
||||
} else if (ngMeta.aliases.containsKey(name)) {
|
||||
viewDef.directives.addAll(ngMeta.flatten(name));
|
||||
compileData.directives.addAll(ngMeta.flatten(name));
|
||||
} else {
|
||||
logger.warning('Could not find Directive entry for $node. '
|
||||
'Please be aware that Dart transformers have limited support for '
|
|
@ -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 'package:angular2/src/core/change_detection/parser/lexer.dart' as ng;
|
||||
import 'package:angular2/src/core/change_detection/parser/parser.dart' as ng;
|
||||
import 'package:analyzer/analyzer.dart';
|
||||
import 'package:angular2/src/compiler/source_module.dart';
|
||||
import 'package:angular2/src/compiler/template_compiler.dart';
|
||||
import 'package:angular2/src/core/change_detection/interfaces.dart';
|
||||
import 'package:angular2/src/core/compiler/proto_view_factory.dart';
|
||||
import 'package:angular2/src/core/dom/dom_adapter.dart';
|
||||
import 'package:angular2/src/core/render/api.dart';
|
||||
import 'package:angular2/src/core/render/dom/compiler/compile_pipeline.dart';
|
||||
import 'package:angular2/src/core/render/dom/compiler/style_inliner.dart';
|
||||
import 'package:angular2/src/core/render/dom/compiler/style_url_resolver.dart';
|
||||
import 'package:angular2/src/core/render/dom/compiler/view_loader.dart';
|
||||
import 'package:angular2/src/core/render/dom/schema/element_schema_registry.dart';
|
||||
import 'package:angular2/src/core/render/dom/schema/dom_element_schema_registry.dart';
|
||||
import 'package:angular2/src/core/render/dom/template_cloner.dart';
|
||||
import 'package:angular2/src/core/render/xhr.dart' show XHR;
|
||||
import 'package:angular2/src/core/reflection/reflection.dart';
|
||||
import 'package:angular2/src/core/services/url_resolver.dart';
|
||||
import 'package:angular2/src/transform/common/asset_reader.dart';
|
||||
import 'package:angular2/src/transform/common/xhr_impl.dart';
|
||||
import 'package:angular2/src/core/facade/lang.dart';
|
||||
import 'package:angular2/src/core/reflection/reflection.dart';
|
||||
import 'package:angular2/src/transform/common/asset_reader.dart';
|
||||
import 'package:angular2/src/transform/common/code/source_module.dart';
|
||||
import 'package:angular2/src/transform/common/names.dart';
|
||||
import 'package:angular2/src/transform/common/ng_compiler.dart';
|
||||
import 'package:angular2/src/transform/common/ng_deps.dart';
|
||||
import 'package:barback/barback.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
|
||||
import 'change_detector_codegen.dart' as change;
|
||||
import 'compile_step_factory.dart';
|
||||
import 'reflection/codegen.dart' as reg;
|
||||
import 'reflection/processor.dart' as reg;
|
||||
import 'reflection/reflection_capabilities.dart';
|
||||
import 'view_definition_creator.dart';
|
||||
import 'compile_data_creator.dart';
|
||||
|
||||
/// Reads the `.ng_deps.dart` file represented by `entryPoint` and parses any
|
||||
/// Angular 2 `View` annotations it declares to generate `getter`s,
|
||||
/// `setter`s, and `method`s that would otherwise be reflectively accessed.
|
||||
///
|
||||
/// This method assumes a {@link DomAdapter} has been registered.
|
||||
Future<String> processTemplates(AssetReader reader, AssetId entryPoint,
|
||||
{bool generateRegistrations: true,
|
||||
bool generateChangeDetectors: true, bool reflectPropertiesAsAttributes: false}) async {
|
||||
var viewDefResults = await createViewDefinitions(reader, entryPoint);
|
||||
// Note: TemplateCloner(-1) never serializes Nodes into strings.
|
||||
// we might want to change this to TemplateCloner(0) to force the serialization
|
||||
// later when the transformer also stores the proto view template.
|
||||
var extractor = new _TemplateExtractor(new DomElementSchemaRegistry(),
|
||||
new TemplateCloner(-1), new XhrImpl(reader, entryPoint));
|
||||
Future<Outputs> processTemplates(AssetReader reader, AssetId entryPoint,
|
||||
{bool reflectPropertiesAsAttributes: false}) async {
|
||||
var viewDefResults = await createCompileData(reader, entryPoint);
|
||||
|
||||
final processor = new reg.Processor();
|
||||
var templateCompiler = createTemplateCompiler(reader,
|
||||
changeDetectionConfig: new ChangeDetectorGenConfig(assertionsEnabled(),
|
||||
assertionsEnabled(), reflectPropertiesAsAttributes, false));
|
||||
|
||||
var changeDetectorClasses = new change.Codegen();
|
||||
for (var rType in viewDefResults.viewDefinitions.keys) {
|
||||
var viewDefEntry = viewDefResults.viewDefinitions[rType];
|
||||
var protoView = await extractor.extractTemplates(viewDefEntry.viewDef);
|
||||
if (protoView == null) continue;
|
||||
var ngDeps = viewDefResults.ngDeps;
|
||||
var compileData =
|
||||
viewDefResults.viewDefinitions.values.toList(growable: false);
|
||||
if (compileData.isEmpty) {
|
||||
return new Outputs(entryPoint, ngDeps, null, null, null);
|
||||
}
|
||||
|
||||
if (generateRegistrations) {
|
||||
processor.process(viewDefEntry, protoView);
|
||||
}
|
||||
if (generateChangeDetectors) {
|
||||
var saved = reflector.reflectionCapabilities;
|
||||
var genConfig = new ChangeDetectorGenConfig(assertionsEnabled(), assertionsEnabled(), reflectPropertiesAsAttributes, false);
|
||||
var savedReflectionCapabilities = reflector.reflectionCapabilities;
|
||||
reflector.reflectionCapabilities = const NullReflectionCapabilities();
|
||||
var compiledTemplates = templateCompiler.compileTemplatesCodeGen(compileData);
|
||||
reflector.reflectionCapabilities = savedReflectionCapabilities;
|
||||
|
||||
reflector.reflectionCapabilities = const NullReflectionCapabilities();
|
||||
var defs = getChangeDetectorDefinitions(viewDefEntry.hostMetadata,
|
||||
protoView, viewDefEntry.viewDef.directives, genConfig);
|
||||
for (var i = 0; i < defs.length; ++i) {
|
||||
changeDetectorClasses.generate('${rType.typeName}',
|
||||
'_${rType.typeName}_ChangeDetector$i', defs[i]);
|
||||
var processor = new reg.Processor();
|
||||
compileData
|
||||
.map((withDirectives) => withDirectives.component)
|
||||
.forEach(processor.process);
|
||||
var codegen = new reg.Codegen();
|
||||
|
||||
codegen.generate(processor);
|
||||
|
||||
return new Outputs(entryPoint, ngDeps, codegen,
|
||||
viewDefResults.viewDefinitions, compiledTemplates);
|
||||
}
|
||||
|
||||
AssetId templatesAssetId(AssetId ngDepsAssetId) =>
|
||||
new AssetId(ngDepsAssetId.package, toTemplateExtension(ngDepsAssetId.path));
|
||||
|
||||
class Outputs {
|
||||
final String ngDepsCode;
|
||||
final String templatesCode;
|
||||
|
||||
Outputs._(this.ngDepsCode, this.templatesCode);
|
||||
|
||||
factory Outputs(
|
||||
AssetId assetId,
|
||||
NgDeps ngDeps,
|
||||
reg.Codegen accessors,
|
||||
Map<RegisteredType, NormalizedComponentWithViewDirectives> compileDataMap,
|
||||
SourceModule templatesSource) {
|
||||
var libraryName =
|
||||
ngDeps != null && ngDeps.lib != null ? '${ngDeps.lib.name}' : null;
|
||||
return new Outputs._(
|
||||
_generateNgDepsCode(assetId, ngDeps, accessors, compileDataMap),
|
||||
writeSourceModule(templatesSource, libraryName: libraryName));
|
||||
}
|
||||
|
||||
// Updates the NgDeps code with an additional `CompiledTemplate` annotation
|
||||
// for each Directive we generated one for.
|
||||
//
|
||||
// Also adds an import to the `.template.dart` file that we will generate.
|
||||
static String _generateNgDepsCode(
|
||||
AssetId id,
|
||||
NgDeps ngDeps,
|
||||
reg.Codegen accessors,
|
||||
Map<RegisteredType,
|
||||
NormalizedComponentWithViewDirectives> compileDataMap) {
|
||||
var code = ngDeps.code;
|
||||
if (compileDataMap == null || compileDataMap.isEmpty) return code;
|
||||
|
||||
if (ngDeps.registeredTypes.isEmpty) return code;
|
||||
var beginRegistrationsIdx =
|
||||
ngDeps.registeredTypes.first.registerMethod.offset;
|
||||
var endRegistratationsIdx = ngDeps.registeredTypes.last.registerMethod.end;
|
||||
var importInjectIdx = ngDeps.lib != null ? ngDeps.lib.end : 0;
|
||||
|
||||
// Add everything up to the point where we begin registering classes with
|
||||
// the reflector, injecting an import to the generated template code.
|
||||
var buf = new StringBuffer('${code.substring(0, importInjectIdx)}'
|
||||
'import \'${toTemplateExtension(path.basename(id.path))}\' as _templates;'
|
||||
'${code.substring(importInjectIdx, beginRegistrationsIdx)}');
|
||||
|
||||
for (var registeredType in ngDeps.registeredTypes) {
|
||||
if (compileDataMap.containsKey(registeredType)) {
|
||||
// We generated a template for this type, so add the generated
|
||||
// `CompiledTemplate` value as the final annotation in the list.
|
||||
var annotations = registeredType.annotations as ListLiteral;
|
||||
if (annotations.length == 0) {
|
||||
throw new FormatException('Unexpected format - attempting to codegen '
|
||||
'a class with no Component annotation ${registeredType.typeName}');
|
||||
}
|
||||
buf.write(code.substring(registeredType.registerMethod.offset,
|
||||
annotations.elements.last.end));
|
||||
buf.write(', _templates.Host${registeredType.typeName}Template]');
|
||||
buf.writeln(code.substring(
|
||||
registeredType.annotations.end, registeredType.registerMethod.end));
|
||||
} else {
|
||||
// There is no compiled template for this type, write it out without any
|
||||
// changes.
|
||||
buf.writeln(code.substring(registeredType.registerMethod.offset,
|
||||
registeredType.registerMethod.end));
|
||||
}
|
||||
reflector.reflectionCapabilities = saved;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(kegluneq): Do not hard-code `false` here once i/3436 is fixed.
|
||||
final registrations = new reg.Codegen(generateChangeDetectors: false);
|
||||
registrations.generate(processor);
|
||||
buf.writeln(accessors.toString());
|
||||
|
||||
var code = viewDefResults.ngDeps.code;
|
||||
if (registrations.isEmpty && changeDetectorClasses.isEmpty) return code;
|
||||
var importInjectIdx =
|
||||
viewDefResults.ngDeps.lib != null ? viewDefResults.ngDeps.lib.end : 0;
|
||||
var codeInjectIdx =
|
||||
viewDefResults.ngDeps.registeredTypes.last.registerMethod.end;
|
||||
var initInjectIdx = viewDefResults.ngDeps.setupMethod.end - 1;
|
||||
return '${code.substring(0, importInjectIdx)}'
|
||||
'${changeDetectorClasses.imports}'
|
||||
'${code.substring(importInjectIdx, codeInjectIdx)}'
|
||||
'${registrations}'
|
||||
'${code.substring(codeInjectIdx, initInjectIdx)}'
|
||||
'${changeDetectorClasses.initialize}'
|
||||
'${code.substring(initInjectIdx)}'
|
||||
'$changeDetectorClasses';
|
||||
}
|
||||
|
||||
/// Extracts `template` and `url` values from `View` annotations, reads
|
||||
/// template code if necessary, and determines what values will be
|
||||
/// reflectively accessed from that template.
|
||||
class _TemplateExtractor {
|
||||
final CompileStepFactory _factory;
|
||||
ViewLoader _loader;
|
||||
ElementSchemaRegistry _schemaRegistry;
|
||||
TemplateCloner _templateCloner;
|
||||
|
||||
_TemplateExtractor(this._schemaRegistry, this._templateCloner, XHR xhr)
|
||||
: _factory = new CompileStepFactory(new ng.Parser(new ng.Lexer())) {
|
||||
var urlResolver = new UrlResolver();
|
||||
var styleUrlResolver = new StyleUrlResolver(urlResolver);
|
||||
var styleInliner = new StyleInliner(xhr, styleUrlResolver, urlResolver);
|
||||
|
||||
_loader = new ViewLoader(xhr, styleInliner, styleUrlResolver);
|
||||
}
|
||||
|
||||
Future<ProtoViewDto> extractTemplates(ViewDefinition viewDef) async {
|
||||
// Check for "imperative views".
|
||||
if (viewDef.template == null && viewDef.templateAbsUrl == null) return null;
|
||||
|
||||
var templateAndStyles = await _loader.load(viewDef);
|
||||
|
||||
// NOTE(kegluneq): Since this is a global, we must not have any async
|
||||
// operations between saving and restoring it, otherwise we can get into
|
||||
// a bad state. See issue #2359 for additional context.
|
||||
var savedReflectionCapabilities = reflector.reflectionCapabilities;
|
||||
reflector.reflectionCapabilities = const NullReflectionCapabilities();
|
||||
|
||||
var pipeline = new CompilePipeline(_factory.createSteps(viewDef));
|
||||
|
||||
var compileElements = pipeline.processElements(
|
||||
DOM.createTemplate(templateAndStyles.template),
|
||||
ViewType.COMPONENT,
|
||||
viewDef);
|
||||
var protoViewDto = compileElements[0]
|
||||
.inheritedProtoView
|
||||
.build(_schemaRegistry, _templateCloner);
|
||||
|
||||
reflector.reflectionCapabilities = savedReflectionCapabilities;
|
||||
|
||||
return protoViewDto;
|
||||
// Add everything after the final registration.
|
||||
buf.writeln(code.substring(endRegistratationsIdx));
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,12 +8,11 @@ import 'model.dart';
|
|||
class Codegen {
|
||||
final StringBuffer _buf = new StringBuffer();
|
||||
|
||||
/// Whether we are pre-generating change detectors.
|
||||
/// If we have pre-generated change detectors, we need
|
||||
final bool generateChangeDetectors;
|
||||
|
||||
Codegen({this.generateChangeDetectors});
|
||||
|
||||
/// Generates code to register all getters, setters, and methods stored by
|
||||
/// `model`.
|
||||
///
|
||||
/// The code takes the form of zero or more cascaded calls. The receiver of
|
||||
/// these calls is expected to be an Angular 2 reflector object.
|
||||
void generate(CodegenModel model) {
|
||||
if (model != null) {
|
||||
var calls = _generateGetters(_extractNames(model.getterNames));
|
||||
|
@ -35,9 +34,7 @@ class Codegen {
|
|||
}
|
||||
|
||||
Iterable<String> _extractNames(Iterable<ReflectiveAccessor> accessors) {
|
||||
var names = accessors.where((accessor) {
|
||||
return accessor.isStaticallyNecessary || !generateChangeDetectors;
|
||||
}).map((accessor) => accessor.sanitizedName);
|
||||
var names = accessors.map((accessor) => accessor.sanitizedName);
|
||||
var nameList = names.toList();
|
||||
nameList.sort();
|
||||
return nameList;
|
||||
|
|
|
@ -27,11 +27,7 @@ class ReflectiveAccessor {
|
|||
/// setter, or method on the `Component`.
|
||||
final String sanitizedName;
|
||||
|
||||
/// Whether this getter, setter, or method is still necessary when we have
|
||||
/// pre-generated change detectors.
|
||||
final bool isStaticallyNecessary;
|
||||
|
||||
ReflectiveAccessor(String astValue, {this.isStaticallyNecessary})
|
||||
ReflectiveAccessor(String astValue)
|
||||
: this.astValue = astValue,
|
||||
this.sanitizedName = sanitizePropertyName(astValue);
|
||||
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
library angular2.transform.template_compiler.reflection.processor;
|
||||
|
||||
import 'package:angular2/src/core/change_detection/parser/ast.dart';
|
||||
import 'package:angular2/src/core/render/api.dart';
|
||||
import 'package:angular2/src/transform/template_compiler/view_definition_creator.dart';
|
||||
import 'package:angular2/src/compiler/directive_metadata.dart';
|
||||
|
||||
import 'model.dart';
|
||||
|
||||
|
@ -16,109 +14,9 @@ class Processor implements CodegenModel {
|
|||
/// The names of all requested `method`s.
|
||||
final Set<ReflectiveAccessor> methodNames = new Set<ReflectiveAccessor>();
|
||||
|
||||
_NgAstVisitor _visitor;
|
||||
|
||||
Processor() {
|
||||
_visitor = new _NgAstVisitor(this);
|
||||
}
|
||||
|
||||
void process(ViewDefinitionEntry viewDefEntry, ProtoViewDto protoViewDto) {
|
||||
_processViewDefinition(viewDefEntry);
|
||||
_processProtoViewDto(protoViewDto);
|
||||
}
|
||||
|
||||
/// Extracts the names of necessary getters from the events in host and
|
||||
/// dependent [DirectiveMetadata].
|
||||
void _processViewDefinition(ViewDefinitionEntry viewDefEntry) {
|
||||
// These are necessary even with generated change detectors.
|
||||
if (viewDefEntry.hostMetadata != null &&
|
||||
viewDefEntry.hostMetadata.outputs != null) {
|
||||
viewDefEntry.hostMetadata.outputs.forEach((eventName) {
|
||||
getterNames.add(
|
||||
new ReflectiveAccessor(eventName, isStaticallyNecessary: true));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void _processProtoViewDto(ProtoViewDto protoViewDto) {
|
||||
_visitor.isStaticallyNecessary = false;
|
||||
|
||||
protoViewDto.textBindings.forEach((ast) => ast.visit(_visitor));
|
||||
protoViewDto.elementBinders.forEach((binder) {
|
||||
binder.propertyBindings.forEach((binding) {
|
||||
binding.astWithSource.visit(_visitor);
|
||||
setterNames.add(new ReflectiveAccessor(binding.property,
|
||||
isStaticallyNecessary: false));
|
||||
});
|
||||
|
||||
binder.directives.forEach((directiveBinding) {
|
||||
directiveBinding.propertyBindings.values
|
||||
.forEach((propBinding) => propBinding.visit(_visitor));
|
||||
directiveBinding.propertyBindings.keys.forEach((bindingName) {
|
||||
setterNames.add(new ReflectiveAccessor(bindingName,
|
||||
isStaticallyNecessary: false));
|
||||
});
|
||||
|
||||
directiveBinding.hostPropertyBindings.forEach((elementBinding) {
|
||||
elementBinding.astWithSource.visit(_visitor);
|
||||
setterNames.add(new ReflectiveAccessor(elementBinding.property,
|
||||
isStaticallyNecessary: false));
|
||||
});
|
||||
});
|
||||
|
||||
binder.eventBindings
|
||||
.forEach((eventBinding) => eventBinding.source.visit(_visitor));
|
||||
|
||||
binder.directives.forEach((directiveBinding) {
|
||||
directiveBinding.eventBindings
|
||||
.forEach((eventBinding) => eventBinding.source.visit(_visitor));
|
||||
});
|
||||
|
||||
if (binder.nestedProtoView != null) {
|
||||
_processProtoViewDto(binder.nestedProtoView);
|
||||
}
|
||||
void process(CompileDirectiveMetadata meta) {
|
||||
meta.events.keys.forEach((eventName) {
|
||||
getterNames.add(new ReflectiveAccessor(eventName));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class _NgAstVisitor extends RecursiveAstVisitor {
|
||||
final Processor _result;
|
||||
|
||||
/// Whether any getters or setters recorded are necessary when running
|
||||
/// statically. A getter or setter that is necessary only for change detection
|
||||
/// is not necessary when running statically because all accesses are handled
|
||||
/// by the dedicated change detector classes.
|
||||
bool isStaticallyNecessary = false;
|
||||
|
||||
_NgAstVisitor(this._result);
|
||||
|
||||
visitMethodCall(MethodCall ast) {
|
||||
_result.methodNames
|
||||
.add(new ReflectiveAccessor(ast.name, isStaticallyNecessary: true));
|
||||
super.visitMethodCall(ast);
|
||||
}
|
||||
|
||||
visitPropertyRead(PropertyRead ast) {
|
||||
_result.getterNames.add(new ReflectiveAccessor(ast.name,
|
||||
isStaticallyNecessary: isStaticallyNecessary));
|
||||
super.visitPropertyRead(ast);
|
||||
}
|
||||
|
||||
visitPropertyWrite(PropertyWrite ast) {
|
||||
_result.setterNames.add(new ReflectiveAccessor(ast.name,
|
||||
isStaticallyNecessary: isStaticallyNecessary));
|
||||
super.visitPropertyWrite(ast);
|
||||
}
|
||||
|
||||
visitSafeMethodCall(SafeMethodCall ast) {
|
||||
_result.methodNames
|
||||
.add(new ReflectiveAccessor(ast.name, isStaticallyNecessary: true));
|
||||
super.visitSafeMethodCall(ast);
|
||||
}
|
||||
|
||||
visitSafePropertyRead(SafePropertyRead ast) {
|
||||
_result.getterNames.add(new ReflectiveAccessor(ast.name,
|
||||
isStaticallyNecessary: isStaticallyNecessary));
|
||||
super.visitSafePropertyRead(ast);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,8 +29,6 @@ class NullReflectionCapabilities implements ReflectionCapabilities {
|
|||
MethodFn method(String name) => _nullMethod;
|
||||
|
||||
String importUri(Type type) => './';
|
||||
|
||||
String moduleId(Type type) => null;
|
||||
}
|
||||
|
||||
_nullGetter(Object p) => null;
|
||||
|
|
|
@ -30,6 +30,7 @@ class TemplateCompiler extends Transformer implements DeclaringTransformer {
|
|||
declareOutputs(DeclaringTransform transform) {
|
||||
transform.consumePrimary();
|
||||
transform.declareOutput(transform.primaryId);
|
||||
transform.declareOutput(templatesAssetId(transform.primaryId));
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -38,13 +39,22 @@ class TemplateCompiler extends Transformer implements DeclaringTransformer {
|
|||
Html5LibDomAdapter.makeCurrent();
|
||||
var primaryId = transform.primaryInput.id;
|
||||
var reader = new AssetReader.fromTransform(transform);
|
||||
var transformedCode = formatter.format(await processTemplates(
|
||||
reader, primaryId,
|
||||
generateChangeDetectors: options.generateChangeDetectors,
|
||||
reflectPropertiesAsAttributes:
|
||||
options.reflectPropertiesAsAttributes));
|
||||
var outputs = await processTemplates(reader, primaryId,
|
||||
reflectPropertiesAsAttributes: options.reflectPropertiesAsAttributes);
|
||||
transform.consumePrimary();
|
||||
transform.addOutput(new Asset.fromString(primaryId, transformedCode));
|
||||
var ngDepsCode = '';
|
||||
var templatesCode = '';
|
||||
if (outputs != null) {
|
||||
if (outputs.ngDepsCode != null) {
|
||||
ngDepsCode = formatter.format(outputs.ngDepsCode);
|
||||
}
|
||||
if (outputs.templatesCode != null) {
|
||||
templatesCode = formatter.format(outputs.templatesCode);
|
||||
}
|
||||
}
|
||||
transform.addOutput(new Asset.fromString(primaryId, ngDepsCode));
|
||||
transform.addOutput(
|
||||
new Asset.fromString(templatesAssetId(primaryId), templatesCode));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
library angular2.transform;
|
||||
library angular2.src.transform.transformer;
|
||||
|
||||
import 'package:barback/barback.dart';
|
||||
import 'package:dart_style/dart_style.dart';
|
||||
|
||||
import 'deferred_rewriter/transformer.dart';
|
||||
import 'directive_linker/transformer.dart';
|
||||
import 'directive_metadata_extractor/transformer.dart';
|
||||
import 'directive_metadata_linker/transformer.dart';
|
||||
import 'directive_processor/transformer.dart';
|
||||
import 'bind_generator/transformer.dart';
|
||||
import 'reflection_remover/transformer.dart';
|
||||
import 'stylesheet_compiler/transformer.dart';
|
||||
import 'template_compiler/transformer.dart';
|
||||
import 'common/formatter.dart' as formatter;
|
||||
import 'common/options.dart';
|
||||
|
@ -32,10 +33,14 @@ class AngularTransformerGroup extends TransformerGroup {
|
|||
phases.addAll(new List.generate(
|
||||
options.optimizationPhases, (_) => [new EmptyNgDepsRemover()]));
|
||||
phases.addAll([
|
||||
[new DirectiveLinker(), new DeferredRewriter(options)],
|
||||
[new DirectiveMetadataExtractor()],
|
||||
[
|
||||
new DeferredRewriter(options),
|
||||
new DirectiveLinker(),
|
||||
new DirectiveMetadataLinker()
|
||||
],
|
||||
[new BindGenerator(options)],
|
||||
[new TemplateCompiler(options)]
|
||||
[new TemplateCompiler(options)],
|
||||
[new StylesheetCompiler()],
|
||||
]);
|
||||
return new AngularTransformerGroup._(phases,
|
||||
formatCode: options.formatCode);
|
||||
|
|
|
@ -38,22 +38,12 @@ void allTests() {
|
|||
expect(output).toEqual(expected);
|
||||
});
|
||||
|
||||
it('should generate a getter for a `events` property in an annotation.',
|
||||
() async {
|
||||
var inputPath = 'bind_generator/events_files/bar.ng_deps.dart';
|
||||
var expected = formatter.format(
|
||||
readFile('bind_generator/events_files/expected/bar.ng_deps.dart'));
|
||||
|
||||
var output = formatter.format(
|
||||
await createNgSettersAndGetters(reader, new AssetId('a', inputPath)));
|
||||
expect(output).toEqual(expected);
|
||||
});
|
||||
|
||||
it('should generate setters for queries defined in the class annotation.',
|
||||
() async {
|
||||
var inputPath = 'bind_generator/queries_class_annotation_files/bar.ng_deps.dart';
|
||||
var expected = formatter.format(
|
||||
readFile('bind_generator/queries_class_annotation_files/expected/bar.ng_deps.dart'));
|
||||
var inputPath =
|
||||
'bind_generator/queries_class_annotation_files/bar.ng_deps.dart';
|
||||
var expected = formatter.format(readFile(
|
||||
'bind_generator/queries_class_annotation_files/expected/bar.ng_deps.dart'));
|
||||
|
||||
var output = formatter.format(
|
||||
await createNgSettersAndGetters(reader, new AssetId('a', inputPath)));
|
||||
|
@ -62,9 +52,10 @@ void allTests() {
|
|||
|
||||
it('should generate setters for queries defined via prop annotations.',
|
||||
() async {
|
||||
var inputPath = 'bind_generator/queries_prop_annotations_files/bar.ng_deps.dart';
|
||||
var expected = formatter.format(
|
||||
readFile('bind_generator/queries_prop_annotations_files/expected/bar.ng_deps.dart'));
|
||||
var inputPath =
|
||||
'bind_generator/queries_prop_annotations_files/bar.ng_deps.dart';
|
||||
var expected = formatter.format(readFile(
|
||||
'bind_generator/queries_prop_annotations_files/expected/bar.ng_deps.dart'));
|
||||
|
||||
var output = formatter.format(
|
||||
await createNgSettersAndGetters(reader, new AssetId('a', inputPath)));
|
||||
|
|
|
@ -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;
|
||||
|
||||
import 'package:angular2/src/core/render/api.dart';
|
||||
import 'package:angular2/src/compiler/directive_metadata.dart';
|
||||
import 'package:angular2/src/transform/common/ng_meta.dart';
|
||||
import 'package:guinness/guinness.dart';
|
||||
|
||||
|
@ -8,10 +9,10 @@ main() => allTests();
|
|||
|
||||
void allTests() {
|
||||
var mockData = [
|
||||
new RenderDirectiveMetadata(id: 'm1'),
|
||||
new RenderDirectiveMetadata(id: 'm2'),
|
||||
new RenderDirectiveMetadata(id: 'm3'),
|
||||
new RenderDirectiveMetadata(id: 'm4')
|
||||
CompileDirectiveMetadata.create(type: new CompileTypeMetadata(name: 'N1')),
|
||||
CompileDirectiveMetadata.create(type: new CompileTypeMetadata(name: 'N2')),
|
||||
CompileDirectiveMetadata.create(type: new CompileTypeMetadata(name: 'N3')),
|
||||
CompileDirectiveMetadata.create(type: new CompileTypeMetadata(name: 'N4'))
|
||||
];
|
||||
|
||||
it('should allow empty data.', () {
|
||||
|
@ -92,7 +93,7 @@ _checkSimilar(NgMeta a, NgMeta b) {
|
|||
expect(b.types).toContain(k);
|
||||
var at = a.types[k];
|
||||
var bt = b.types[k];
|
||||
expect(at.id).toEqual(bt.id);
|
||||
expect(at.type.name).toEqual(bt.type.name);
|
||||
}
|
||||
for (var k in a.aliases.keys) {
|
||||
expect(b.aliases).toContain(k);
|
||||
|
|
|
@ -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 'package:barback/barback.dart';
|
||||
import 'package:angular2/src/core/change_detection/change_detection.dart';
|
||||
import 'package:angular2/src/core/compiler/interfaces.dart' show LifecycleHooks;
|
||||
import 'package:angular2/src/core/dom/html_adapter.dart';
|
||||
import 'package:angular2/src/transform/directive_processor/rewriter.dart';
|
||||
import 'package:angular2/src/transform/common/annotation_matcher.dart';
|
||||
import 'package:angular2/src/transform/common/asset_reader.dart';
|
||||
|
@ -14,13 +17,13 @@ import 'package:angular2/src/transform/common/ng_meta.dart';
|
|||
import 'package:code_transformers/messages/build_logger.dart';
|
||||
import 'package:dart_style/dart_style.dart';
|
||||
import 'package:guinness/guinness.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
import 'package:source_span/source_span.dart';
|
||||
import '../common/read_file.dart';
|
||||
|
||||
var formatter = new DartFormatter();
|
||||
|
||||
main() {
|
||||
Html5LibDomAdapter.makeCurrent();
|
||||
allTests();
|
||||
}
|
||||
|
||||
|
@ -122,111 +125,6 @@ void allTests() {
|
|||
});
|
||||
});
|
||||
|
||||
describe('inliner', () {
|
||||
var absoluteReader;
|
||||
beforeEach(() {
|
||||
absoluteReader = new TestAssetReader();
|
||||
});
|
||||
|
||||
it('should inline `templateUrl` values', () async {
|
||||
var model = await _testCreateModel('url_expression_files/hello.dart');
|
||||
expect(model.reflectables.isNotEmpty).toBeTrue();
|
||||
var view =
|
||||
model.reflectables.first.annotations.firstWhere((a) => a.isView);
|
||||
expect(view.namedParameters
|
||||
.firstWhere((p) => p.name == 'templateUrl')
|
||||
.value).toContain('template.html');
|
||||
expect(view.namedParameters.firstWhere((p) => p.name == 'template').value)
|
||||
.toContain('{{greeting}}');
|
||||
});
|
||||
|
||||
it(
|
||||
'should inline `templateUrl` and `styleUrls` values expressed as '
|
||||
'absolute urls.', () async {
|
||||
absoluteReader.addAsset(
|
||||
new AssetId('other_package', 'lib/template.html'),
|
||||
readFile(
|
||||
'directive_processor/absolute_url_expression_files/template.html'));
|
||||
absoluteReader.addAsset(
|
||||
new AssetId('other_package', 'lib/template.css'),
|
||||
readFile(
|
||||
'directive_processor/absolute_url_expression_files/template.css'));
|
||||
var model = await _testCreateModel(
|
||||
'absolute_url_expression_files/hello.dart',
|
||||
reader: absoluteReader);
|
||||
|
||||
expect(model.reflectables.length).toEqual(2);
|
||||
var view =
|
||||
model.reflectables.first.annotations.firstWhere((a) => a.isView);
|
||||
expect(view.namedParameters
|
||||
.firstWhere((p) => p.name == 'templateUrl')
|
||||
.value).toContain('package:other_package/template.html');
|
||||
expect(view.namedParameters.firstWhere((p) => p.name == 'template').value)
|
||||
.toContain('{{greeting}}');
|
||||
expect(view.namedParameters.firstWhere((p) => p.name == 'styles').value)
|
||||
.toContain('.greeting { .color: blue; }');
|
||||
|
||||
// TODO(kegluneq): Split this test out, as it is logically very different.
|
||||
expect(model.reflectables[1].isFunction).toBeTrue();
|
||||
expect(model.reflectables[1].name).toEqual('hello');
|
||||
});
|
||||
|
||||
it('should inline multiple `styleUrls` values expressed as absolute urls.',
|
||||
() async {
|
||||
var model =
|
||||
await _testCreateModel('multiple_style_urls_files/hello.dart');
|
||||
|
||||
expect(model.reflectables.isNotEmpty).toBeTrue();
|
||||
var view =
|
||||
model.reflectables.first.annotations.firstWhere((a) => a.isView);
|
||||
var expectStyles = expect(
|
||||
view.namedParameters.firstWhere((p) => p.name == 'styles').value);
|
||||
expectStyles
|
||||
..toContain('.greeting { .color: blue; }')
|
||||
..toContain('.hello { .color: red; }');
|
||||
});
|
||||
|
||||
it(
|
||||
'should not inline multiple `styleUrls` values expressed as absolute '
|
||||
'urls.', () async {
|
||||
absoluteReader.addAsset(
|
||||
new AssetId('a', 'lib/template.html'),
|
||||
readFile(
|
||||
'directive_processor/multiple_style_urls_files/template.html'));
|
||||
absoluteReader.addAsset(
|
||||
new AssetId('a', 'lib/template.css'),
|
||||
readFile(
|
||||
'directive_processor/multiple_style_urls_files/template.css'));
|
||||
absoluteReader.addAsset(
|
||||
new AssetId('a', 'lib/template_other.css'),
|
||||
readFile(
|
||||
'directive_processor/multiple_style_urls_files/template_other.css'));
|
||||
var model = await _testCreateModel(
|
||||
'multiple_style_urls_not_inlined_files/hello.dart',
|
||||
inlineViews: false,
|
||||
reader: absoluteReader);
|
||||
expect(model.reflectables.isNotEmpty).toBeTrue();
|
||||
var view =
|
||||
model.reflectables.first.annotations.firstWhere((a) => a.isView);
|
||||
expect(view.namedParameters.firstWhere((p) => p.name == 'styles',
|
||||
orElse: () => null)).toBeNull();
|
||||
expect(
|
||||
view.namedParameters.firstWhere((p) => p.name == 'styleUrls').value)
|
||||
..toContain('package:a/template.css')
|
||||
..toContain('package:a/template_other.css');
|
||||
});
|
||||
|
||||
it('should inline `templateUrl`s expressed as adjacent strings.', () async {
|
||||
var model =
|
||||
await _testCreateModel('split_url_expression_files/hello.dart');
|
||||
expect(model.reflectables.isNotEmpty).toBeTrue();
|
||||
var view =
|
||||
model.reflectables.first.annotations.firstWhere((a) => a.isView);
|
||||
expect(view.namedParameters.firstWhere((p) => p.name == 'template').value)
|
||||
.toContain('{{greeting}}');
|
||||
});
|
||||
});
|
||||
|
||||
describe('interfaces', () {
|
||||
it('should include implemented types', () async {
|
||||
var model = await _testCreateModel('interfaces_files/soup.dart');
|
||||
|
@ -353,17 +251,8 @@ void allTests() {
|
|||
await _testCreateModel('invalid_url_files/hello.dart', logger: logger);
|
||||
expect(logger.hasErrors).toBeTrue();
|
||||
expect(logger.logs)
|
||||
..toContain(
|
||||
'ERROR: Uri /bad/absolute/url.html not supported from angular2|test/'
|
||||
'transform/directive_processor/invalid_url_files/hello.dart, could not '
|
||||
'build AssetId')
|
||||
..toContain(
|
||||
'ERROR: Could not read asset at uri package:invalid/package.css from '
|
||||
'angular2|test/transform/directive_processor/invalid_url_files/'
|
||||
'hello.dart')
|
||||
..toContain(
|
||||
'ERROR: Could not read asset at uri bad_relative_url.css from angular2|'
|
||||
'test/transform/directive_processor/invalid_url_files/hello.dart');
|
||||
..toContain('ERROR: ERROR: Invalid argument (url): '
|
||||
'"Could not read asset at uri asset:/bad/absolute/url.html"');
|
||||
});
|
||||
|
||||
it('should find and register static functions.', () async {
|
||||
|
@ -375,15 +264,16 @@ void allTests() {
|
|||
expect(functionReflectable.name).toEqual('getMessage');
|
||||
});
|
||||
|
||||
it('should find direcive aliases patterns.', () async {
|
||||
var logger = new RecordingLogger();
|
||||
return log.setZoned(logger, () async {
|
||||
var inputId = _assetIdForPath('directive_aliases_files/hello.dart');
|
||||
var reader = new TestAssetReader();
|
||||
describe('NgMeta', () {
|
||||
var fakeReader;
|
||||
beforeEach(() {
|
||||
fakeReader = new TestAssetReader();
|
||||
});
|
||||
|
||||
it('should find direcive aliases patterns.', () async {
|
||||
var ngMeta = new NgMeta.empty();
|
||||
await createNgDeps(reader, inputId, new AnnotationMatcher(), ngMeta,
|
||||
inlineViews: true);
|
||||
await _testCreateModel('directive_aliases_files/hello.dart',
|
||||
ngMeta: ngMeta);
|
||||
|
||||
expect(ngMeta.aliases).toContain('alias1');
|
||||
expect(ngMeta.aliases['alias1']).toContain('HelloCmp');
|
||||
|
@ -391,6 +281,121 @@ void allTests() {
|
|||
expect(ngMeta.aliases).toContain('alias2');
|
||||
expect(ngMeta.aliases['alias2'])..toContain('HelloCmp')..toContain('Foo');
|
||||
});
|
||||
|
||||
it('should include hooks for implemented types (single)', () async {
|
||||
var ngMeta = new NgMeta.empty();
|
||||
await _testCreateModel('interfaces_files/soup.dart', ngMeta: ngMeta);
|
||||
|
||||
expect(ngMeta.types.isNotEmpty).toBeTrue();
|
||||
expect(ngMeta.types['ChangingSoupComponent']).toBeNotNull();
|
||||
expect(ngMeta.types['ChangingSoupComponent'].selector).toEqual('[soup]');
|
||||
expect(ngMeta.types['ChangingSoupComponent'].lifecycleHooks)
|
||||
.toContain(LifecycleHooks.OnChanges);
|
||||
});
|
||||
|
||||
it('should include hooks for implemented types (many)', () async {
|
||||
var ngMeta = new NgMeta.empty();
|
||||
await _testCreateModel('multiple_interface_lifecycle_files/soup.dart',
|
||||
ngMeta: ngMeta);
|
||||
|
||||
expect(ngMeta.types.isNotEmpty).toBeTrue();
|
||||
expect(ngMeta.types['MultiSoupComponent']).toBeNotNull();
|
||||
expect(ngMeta.types['MultiSoupComponent'].selector).toEqual('[soup]');
|
||||
expect(ngMeta.types['MultiSoupComponent'].lifecycleHooks)
|
||||
..toContain(LifecycleHooks.OnChanges)
|
||||
..toContain(LifecycleHooks.OnDestroy)
|
||||
..toContain(LifecycleHooks.OnInit);
|
||||
});
|
||||
|
||||
it('should create type entries for Directives', () async {
|
||||
fakeReader
|
||||
..addAsset(new AssetId('other_package', 'lib/template.html'), '')
|
||||
..addAsset(new AssetId('other_package', 'lib/template.css'), '');
|
||||
var ngMeta = new NgMeta.empty();
|
||||
await _testCreateModel('absolute_url_expression_files/hello.dart',
|
||||
ngMeta: ngMeta, reader: fakeReader);
|
||||
|
||||
expect(ngMeta.types.isNotEmpty).toBeTrue();
|
||||
expect(ngMeta.types['HelloCmp']).toBeNotNull();
|
||||
expect(ngMeta.types['HelloCmp'].selector).toEqual('hello-app');
|
||||
});
|
||||
|
||||
it('should populate all provided values for Components & Directives',
|
||||
() async {
|
||||
var ngMeta = new NgMeta.empty();
|
||||
await _testCreateModel('unusual_component_files/hello.dart',
|
||||
ngMeta: ngMeta);
|
||||
|
||||
expect(ngMeta.types.isNotEmpty).toBeTrue();
|
||||
|
||||
var component = ngMeta.types['UnusualComp'];
|
||||
expect(component).toBeNotNull();
|
||||
expect(component.selector).toEqual('unusual-comp');
|
||||
expect(component.isComponent).toBeTrue();
|
||||
expect(component.exportAs).toEqual('ComponentExportAsValue');
|
||||
expect(component.changeDetection)
|
||||
.toEqual(ChangeDetectionStrategy.CheckAlways);
|
||||
expect(component.properties).toContain('aProperty');
|
||||
expect(component.properties['aProperty']).toEqual('aProperty');
|
||||
expect(component.events).toContain('anEvent');
|
||||
expect(component.events['anEvent']).toEqual('anEvent');
|
||||
expect(component.hostAttributes).toContain('hostKey');
|
||||
expect(component.hostAttributes['hostKey']).toEqual('hostValue');
|
||||
|
||||
var directive = ngMeta.types['UnusualDirective'];
|
||||
expect(directive).toBeNotNull();
|
||||
expect(directive.selector).toEqual('unusual-directive');
|
||||
expect(directive.isComponent).toBeFalse();
|
||||
expect(directive.exportAs).toEqual('DirectiveExportAsValue');
|
||||
expect(directive.properties).toContain('aDirectiveProperty');
|
||||
expect(directive.properties['aDirectiveProperty'])
|
||||
.toEqual('aDirectiveProperty');
|
||||
expect(directive.events).toContain('aDirectiveEvent');
|
||||
expect(directive.events['aDirectiveEvent']).toEqual('aDirectiveEvent');
|
||||
expect(directive.hostAttributes).toContain('directiveHostKey');
|
||||
expect(directive.hostAttributes['directiveHostKey'])
|
||||
.toEqual('directiveHostValue');
|
||||
});
|
||||
|
||||
it('should include hooks for implemented types (single)', () async {
|
||||
var ngMeta = new NgMeta.empty();
|
||||
await _testCreateModel('interfaces_files/soup.dart', ngMeta: ngMeta);
|
||||
|
||||
expect(ngMeta.types.isNotEmpty).toBeTrue();
|
||||
expect(ngMeta.types['ChangingSoupComponent']).toBeNotNull();
|
||||
expect(ngMeta.types['ChangingSoupComponent'].selector).toEqual('[soup]');
|
||||
expect(ngMeta.types['ChangingSoupComponent'].lifecycleHooks)
|
||||
.toContain(LifecycleHooks.OnChanges);
|
||||
});
|
||||
|
||||
it('should include hooks for implemented types (many)', () async {
|
||||
var ngMeta = new NgMeta.empty();
|
||||
await _testCreateModel('multiple_interface_lifecycle_files/soup.dart',
|
||||
ngMeta: ngMeta);
|
||||
|
||||
expect(ngMeta.types.isNotEmpty).toBeTrue();
|
||||
expect(ngMeta.types['MultiSoupComponent']).toBeNotNull();
|
||||
expect(ngMeta.types['MultiSoupComponent'].selector).toEqual('[soup]');
|
||||
expect(ngMeta.types['MultiSoupComponent'].lifecycleHooks)
|
||||
..toContain(LifecycleHooks.OnChanges)
|
||||
..toContain(LifecycleHooks.OnDestroy)
|
||||
..toContain(LifecycleHooks.OnInit);
|
||||
});
|
||||
|
||||
it('should parse templates from View annotations', () async {
|
||||
fakeReader
|
||||
..addAsset(new AssetId('other_package', 'lib/template.html'), '')
|
||||
..addAsset(new AssetId('other_package', 'lib/template.css'), '');
|
||||
var ngMeta = new NgMeta.empty();
|
||||
await _testCreateModel('absolute_url_expression_files/hello.dart',
|
||||
ngMeta: ngMeta, reader: fakeReader);
|
||||
|
||||
expect(ngMeta.types.isNotEmpty).toBeTrue();
|
||||
expect(ngMeta.types['HelloCmp']).toBeNotNull();
|
||||
expect(ngMeta.types['HelloCmp'].template).toBeNotNull();
|
||||
expect(ngMeta.types['HelloCmp'].template.templateUrl)
|
||||
.toEqual('asset:other_package/lib/template.html');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -399,7 +404,7 @@ Future<NgDepsModel> _testCreateModel(String inputPath,
|
|||
AssetId assetId,
|
||||
AssetReader reader,
|
||||
BuildLogger logger,
|
||||
bool inlineViews: true}) {
|
||||
NgMeta ngMeta}) {
|
||||
if (logger == null) logger = new RecordingLogger();
|
||||
return log.setZoned(logger, () async {
|
||||
var inputId = _assetIdForPath(inputPath);
|
||||
|
@ -410,11 +415,12 @@ Future<NgDepsModel> _testCreateModel(String inputPath,
|
|||
reader.addAsset(assetId, await reader.readAsString(inputId));
|
||||
inputId = assetId;
|
||||
}
|
||||
if (ngMeta == null) {
|
||||
ngMeta = new NgMeta.empty();
|
||||
}
|
||||
|
||||
var annotationMatcher = new AnnotationMatcher()..addAll(customDescriptors);
|
||||
var ngMeta = new NgMeta.empty();
|
||||
return createNgDeps(reader, inputId, annotationMatcher, ngMeta,
|
||||
inlineViews: inlineViews);
|
||||
return createNgDeps(reader, inputId, annotationMatcher, ngMeta);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
library dinner.package_soup;
|
||||
|
||||
import 'package:angular2/angular2.dart';
|
||||
import 'package:soup/soup.dart';
|
||||
|
||||
@Soup()
|
||||
@View(template: '')
|
||||
class PackageSoup {
|
||||
PackageSoup();
|
||||
}
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
library dinner.relative_soup;
|
||||
|
||||
import 'package:angular2/angular2.dart';
|
||||
import 'annotations/soup.dart';
|
||||
|
||||
@Soup()
|
||||
@View(template: '')
|
||||
class RelativeSoup {
|
||||
RelativeSoup();
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ library dinner.soup;
|
|||
import 'package:angular2/src/core/metadata.dart';
|
||||
|
||||
@Component(selector: '[soup]')
|
||||
@View(template: '')
|
||||
class ChangingSoupComponent implements PrimaryInterface {}
|
||||
|
||||
class TernaryInterface {}
|
||||
|
|
|
@ -4,4 +4,5 @@ import 'package:angular2/src/core/metadata.dart';
|
|||
import 'package:angular2/src/core/compiler.dart';
|
||||
|
||||
@Component(selector: '[soup]')
|
||||
@View(template: '')
|
||||
class ChangingSoupComponent implements OnChanges, AnotherInterface {}
|
||||
|
|
|
@ -4,4 +4,5 @@ import 'package:angular2/src/core/metadata.dart';
|
|||
import 'package:angular2/src/core/compiler.dart';
|
||||
|
||||
@Component(selector: '[soup]')
|
||||
@View(template: '')
|
||||
class MultiSoupComponent implements OnChanges, OnDestroy, OnInit {}
|
||||
|
|
|
@ -6,6 +6,7 @@ part 'part1.dart';
|
|||
part 'part2.dart';
|
||||
|
||||
@Component(selector: '[main]')
|
||||
@View(template: '')
|
||||
class MainComponent {
|
||||
MainComponent();
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
part of main;
|
||||
|
||||
@Component(selector: '[part1]')
|
||||
@View(template: '')
|
||||
class Part1Component {
|
||||
Part1Component();
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
part of main;
|
||||
|
||||
@Component(selector: '[part2]')
|
||||
@View(template: '')
|
||||
class 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';
|
||||
|
||||
@Component(selector: '[soup]')
|
||||
@View(template: '')
|
||||
class SoupComponent {
|
||||
SoupComponent(@Tasty() String description, @Inject(Salt) salt);
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import 'package:angular2/src/core/metadata.dart';
|
|||
part 'part.dart';
|
||||
|
||||
@Component(selector: '[main]')
|
||||
@View(template: '')
|
||||
class MainComponent {
|
||||
MainComponent();
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
part of main;
|
||||
|
||||
@Component(selector: '[part]')
|
||||
@View(template: '')
|
||||
class PartComponent {
|
||||
PartComponent();
|
||||
}
|
||||
|
|
|
@ -4,4 +4,5 @@ import 'package:angular2/src/core/compiler.dart' as prefix;
|
|||
import 'package:angular2/src/core/metadata.dart';
|
||||
|
||||
@Component(selector: '[soup]')
|
||||
@View(template: '')
|
||||
class OnChangeSoupComponent implements prefix.OnChanges {}
|
||||
|
|
|
@ -3,6 +3,7 @@ library fields;
|
|||
import 'package:angular2/src/core/metadata.dart';
|
||||
|
||||
@Component(selector: '[fields]')
|
||||
@View(template: '')
|
||||
class FieldComponent {
|
||||
@FieldDecorator("field") String field;
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ library fields;
|
|||
import 'package:angular2/src/core/metadata.dart';
|
||||
|
||||
@Component(selector: '[getters]')
|
||||
@View(template: '')
|
||||
class FieldComponent {
|
||||
@GetDecorator("get") String get getVal => 'a';
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ library fields;
|
|||
import 'package:angular2/src/core/metadata.dart';
|
||||
|
||||
@Component(selector: '[getters-and-setters]')
|
||||
@View(template: '')
|
||||
class FieldComponent {
|
||||
String _val;
|
||||
@GetDecorator("get") String get myVal => _val;
|
||||
|
|
|
@ -3,6 +3,7 @@ library fields;
|
|||
import 'package:angular2/src/core/metadata.dart';
|
||||
|
||||
@Component(selector: '[setters]')
|
||||
@View(template: '')
|
||||
class FieldComponent {
|
||||
@SetDecorator("set") String set setVal(val) => null;
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ library dinner.soup;
|
|||
import 'package:angular2/src/core/metadata.dart';
|
||||
|
||||
@Component(selector: '[soup]')
|
||||
@View(template: '')
|
||||
class ChangingSoupComponent extends Super {}
|
||||
|
||||
class Iface {}
|
||||
|
|
|
@ -4,4 +4,5 @@ import 'package:angular2/src/core/compiler.dart';
|
|||
import 'package:angular2/src/core/metadata.dart';
|
||||
|
||||
@Component(selector: '[soup]')
|
||||
@View(template: '')
|
||||
class OnChangeSoupComponent extends OnChanges {}
|
||||
|
|
|
@ -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
Loading…
Reference in New Issue