feat(core): speed up view creation via code gen for view factories.

BREAKING CHANGE:
- Platform pipes can only contain types and arrays of types,
  but no bindings any more.
- When using transformers, platform pipes need to be specified explicitly
  in the pubspec.yaml via the new config option
  `platform_pipes`.
- `Compiler.compileInHost` now returns a `HostViewFactoryRef`
- Component view is not yet created when component constructor is called.
  -> use `onInit` lifecycle callback to access the view of a component
- `ViewRef#setLocal` has been moved to new type `EmbeddedViewRef`
- `internalView` is gone, use `EmbeddedViewRef.rootNodes` to access
  the root nodes of an embedded view
- `renderer.setElementProperty`, `..setElementStyle`, `..setElementAttribute` now
  take a native element instead of an ElementRef
- `Renderer` interface now operates on plain native nodes,
  instead of `RenderElementRef`s or `RenderViewRef`s

Closes #5993
This commit is contained in:
Tobias Bosch
2015-12-02 10:35:51 -08:00
parent a08f50badd
commit 7ae23adaff
191 changed files with 6476 additions and 10232 deletions

View File

@ -51,12 +51,11 @@ void setUpProviders(Iterable<Provider> providerFactory()) {
setUp(() {
try {
_testInjector.addProviders(providerFactory());
} catch(e) {
} catch (e) {
throw 'setUpProviders was called after the injector had '
'been used in a setUp or test block. This invalidates the '
'test injector';
}
});
_addTestInjectorTearDown();

View File

@ -48,6 +48,20 @@ const _COMPONENTS = const [
superClass: 'Directive'),
];
const _PIPES = const [
const ClassDescriptor(
'Pipe', 'package:angular2/src/core/metadata/directive.dart',
superClass: 'Injectable'),
const ClassDescriptor('Pipe', 'package:angular2/src/core/metadata.dart',
superClass: 'Injectable'),
const ClassDescriptor('Pipe', 'package:angular2/angular2.dart',
superClass: 'Injectable'),
const ClassDescriptor('Pipe', 'package:angular2/core.dart',
superClass: 'Injectable'),
const ClassDescriptor('Pipe', 'package:angular2/web_worker/worker.dart',
superClass: 'Injectable'),
];
const _VIEWS = const [
const ClassDescriptor('View', 'package:angular2/angular2.dart'),
const ClassDescriptor('View', 'package:angular2/web_worker/worker.dart'),
@ -79,6 +93,7 @@ class AnnotationMatcher extends ClassMatcherBase {
return new AnnotationMatcher._([]
..addAll(_COMPONENTS)
..addAll(_DIRECTIVES)
..addAll(_PIPES)
..addAll(_INJECTABLES)
..addAll(_VIEWS)
..addAll(_ENTRYPOINTS));
@ -109,6 +124,10 @@ class AnnotationMatcher extends ClassMatcherBase {
bool isView(Annotation annotation, AssetId assetId) =>
_implementsWithWarning(annotation, assetId, _VIEWS);
/// Checks if an [Annotation] node implements [Pipe].
bool isPipe(Annotation annotation, AssetId assetId) =>
_implementsWithWarning(annotation, assetId, _PIPES);
/// Checks if an [Annotation] node implements [AngularEntrypoint]
bool isEntrypoint(Annotation annotation, AssetId assetId) =>
_implementsWithWarning(annotation, assetId, _ENTRYPOINTS);

View File

@ -79,26 +79,34 @@ class ReflectionInfoVisitor extends RecursiveAstVisitor<ReflectionInfoModel> {
}
if (node.metadata != null) {
var componentDirectives, viewDirectives;
var componentDirectives = new Iterable.empty();
var componentPipes = new Iterable.empty();
var viewDirectives, viewPipes;
node.metadata.forEach((node) {
if (_annotationMatcher.isComponent(node, assetId)) {
componentDirectives = _extractDirectives(node);
componentDirectives = _extractReferencedTypes(node, 'directives');
componentPipes = _extractReferencedTypes(node, 'pipes');
} else if (_annotationMatcher.isView(node, assetId)) {
viewDirectives = _extractDirectives(node);
viewDirectives = _extractReferencedTypes(node, 'directives');
viewPipes = _extractReferencedTypes(node, 'pipes');
}
model.annotations.add(_annotationVisitor.visitAnnotation(node));
});
if (componentDirectives != null && componentDirectives.isNotEmpty) {
if (viewDirectives != null) {
log.warning(
'Cannot specify view parameters on @Component when a @View '
'is present. Component name: ${model.name}',
asset: assetId);
}
model.directives.addAll(componentDirectives);
} else if (viewDirectives != null) {
if ((componentDirectives.isNotEmpty || componentPipes.isNotEmpty) &&
(viewDirectives != null || viewPipes != null)) {
log.warning(
'Cannot specify view parameters on @Component when a @View '
'is present. Component name: ${model.name}',
asset: assetId);
}
model.directives.addAll(componentDirectives);
model.pipes.addAll(componentPipes);
if (viewDirectives != null) {
model.directives.addAll(viewDirectives);
}
if (viewPipes != null) {
model.pipes.addAll(viewPipes);
}
}
if (ctor != null &&
ctor.parameters != null &&
@ -151,43 +159,44 @@ class ReflectionInfoVisitor extends RecursiveAstVisitor<ReflectionInfoModel> {
}
}
/// Returns [PrefixedDirective] values parsed from the value of the
/// `directives` parameter of the provided `node`.
/// This will always return a non-null value, so if there are no `directives`
/// specified on `node`, it will return an empty iterable.
Iterable<PrefixedDirective> _extractDirectives(Annotation node) {
/// Returns [PrefixedType] values parsed from the value of the
/// `fieldName` parameter of the provided `node`.
/// This will always return a non-null value, so if there is no field
/// called `fieldName`, it will return an empty iterable.
Iterable<PrefixedType> _extractReferencedTypes(
Annotation node, String fieldName) {
assert(_annotationMatcher.isComponent(node, assetId) ||
_annotationMatcher.isView(node, assetId));
if (node.arguments == null && node.arguments.arguments == null) {
return const [];
}
final directivesNode = node.arguments.arguments.firstWhere((arg) {
return arg is NamedExpression && '${arg.name.label}' == 'directives';
final typesNode = node.arguments.arguments.firstWhere((arg) {
return arg is NamedExpression && '${arg.name.label}' == fieldName;
}, orElse: () => null);
if (directivesNode == null) return const [];
if (typesNode == null) return const [];
if (directivesNode.expression is! ListLiteral) {
if (typesNode.expression is! ListLiteral) {
log.warning(
'Angular 2 expects a list literal for `directives` '
'but found a ${directivesNode.expression.runtimeType}',
'Angular 2 expects a list literal for `${fieldName}` '
'but found a ${typesNode.expression.runtimeType}',
asset: assetId);
return const [];
}
final directives = <PrefixedDirective>[];
for (var dep in (directivesNode.expression as ListLiteral).elements) {
final types = <PrefixedType>[];
for (var dep in (typesNode.expression as ListLiteral).elements) {
if (dep is PrefixedIdentifier) {
directives.add(new PrefixedDirective()
types.add(new PrefixedType()
..prefix = '${dep.prefix}'
..name = '${dep.identifier}');
} else if (dep is Identifier) {
directives.add(new PrefixedDirective()..name = '${dep}');
types.add(new PrefixedType()..name = '${dep}');
} else {
log.warning('Found unexpected value $dep in `directives`.',
log.warning('Ignoring unexpected value $dep in `${fieldName}`.',
asset: assetId);
}
}
return directives;
return types;
}
@override

View File

@ -47,8 +47,7 @@ const _ON_AFTER_CONTENT_INIT_INTERFACES = const [
'AfterContentInit', 'package:angular2/lifecycle_hooks.dart'),
const ClassDescriptor(
'AfterContentInit', 'package:angular2/src/core/linker.dart'),
const ClassDescriptor(
'AfterContentInit', 'package:angular2/core.dart'),
const ClassDescriptor('AfterContentInit', 'package:angular2/core.dart'),
const ClassDescriptor(
'AfterContentInit', 'package:angular2/src/core/linker/interfaces.dart')
];
@ -59,8 +58,7 @@ const _ON_AFTER_CONTENT_CHECKED_INTERFACES = const [
'AfterContentChecked', 'package:angular2/lifecycle_hooks.dart'),
const ClassDescriptor(
'AfterContentChecked', 'package:angular2/src/core/linker.dart'),
const ClassDescriptor(
'AfterContentChecked', 'package:angular2/core.dart'),
const ClassDescriptor('AfterContentChecked', 'package:angular2/core.dart'),
const ClassDescriptor(
'AfterContentChecked', 'package:angular2/src/core/linker/interfaces.dart')
];
@ -70,8 +68,7 @@ const _ON_AFTER_VIEW_INIT_INTERFACES = const [
'AfterViewInit', 'package:angular2/lifecycle_hooks.dart'),
const ClassDescriptor(
'AfterViewInit', 'package:angular2/src/core/linker.dart'),
const ClassDescriptor(
'AfterViewInit', 'package:angular2/core.dart'),
const ClassDescriptor('AfterViewInit', 'package:angular2/core.dart'),
const ClassDescriptor(
'AfterViewInit', 'package:angular2/src/core/linker/interfaces.dart')
];
@ -81,8 +78,7 @@ const _ON_AFTER_VIEW_CHECKED_INTERFACES = const [
'AfterViewChecked', 'package:angular2/lifecycle_hooks.dart'),
const ClassDescriptor(
'AfterViewChecked', 'package:angular2/src/core/linker.dart'),
const ClassDescriptor(
'AfterViewChecked', 'package:angular2/core.dart'),
const ClassDescriptor('AfterViewChecked', 'package:angular2/core.dart'),
const ClassDescriptor(
'AfterViewChecked', 'package:angular2/src/core/linker/interfaces.dart')
];

View File

@ -52,33 +52,32 @@ class PropertyMetadataModel extends GeneratedMessage {
class _ReadonlyPropertyMetadataModel extends PropertyMetadataModel
with ReadonlyMessageMixin {}
class PrefixedDirective extends GeneratedMessage {
static final BuilderInfo _i = new BuilderInfo('PrefixedDirective')
class PrefixedType extends GeneratedMessage {
static final BuilderInfo _i = new BuilderInfo('PrefixedType')
..a(1, 'prefix', PbFieldType.OS)
..a(2, 'name', PbFieldType.OS)
..hasRequiredFields = false;
PrefixedDirective() : super();
PrefixedDirective.fromBuffer(List<int> i,
PrefixedType() : super();
PrefixedType.fromBuffer(List<int> i,
[ExtensionRegistry r = ExtensionRegistry.EMPTY])
: super.fromBuffer(i, r);
PrefixedDirective.fromJson(String i,
PrefixedType.fromJson(String i,
[ExtensionRegistry r = ExtensionRegistry.EMPTY])
: super.fromJson(i, r);
PrefixedDirective clone() => new PrefixedDirective()..mergeFromMessage(this);
PrefixedType clone() => new PrefixedType()..mergeFromMessage(this);
BuilderInfo get info_ => _i;
static PrefixedDirective create() => new PrefixedDirective();
static PbList<PrefixedDirective> createRepeated() =>
new PbList<PrefixedDirective>();
static PrefixedDirective getDefault() {
static PrefixedType create() => new PrefixedType();
static PbList<PrefixedType> createRepeated() => new PbList<PrefixedType>();
static PrefixedType getDefault() {
if (_defaultInstance == null) _defaultInstance =
new _ReadonlyPrefixedDirective();
new _ReadonlyPrefixedType();
return _defaultInstance;
}
static PrefixedDirective _defaultInstance;
static void $checkItem(PrefixedDirective v) {
if (v is! PrefixedDirective) checkItemFailed(v, 'PrefixedDirective');
static PrefixedType _defaultInstance;
static void $checkItem(PrefixedType v) {
if (v is! PrefixedType) checkItemFailed(v, 'PrefixedType');
}
String get prefix => $_get(0, 1, '');
@ -98,8 +97,7 @@ class PrefixedDirective extends GeneratedMessage {
void clearName() => clearField(2);
}
class _ReadonlyPrefixedDirective extends PrefixedDirective
with ReadonlyMessageMixin {}
class _ReadonlyPrefixedType extends PrefixedType with ReadonlyMessageMixin {}
class ReflectionInfoModel extends GeneratedMessage {
static final BuilderInfo _i = new BuilderInfo('ReflectionInfoModel')
@ -113,8 +111,10 @@ class ReflectionInfoModel extends GeneratedMessage {
..p(6, 'interfaces', PbFieldType.PS)
..pp(7, 'propertyMetadata', PbFieldType.PM,
PropertyMetadataModel.$checkItem, PropertyMetadataModel.create)
..pp(8, 'directives', PbFieldType.PM, PrefixedDirective.$checkItem,
PrefixedDirective.create);
..pp(8, 'directives', PbFieldType.PM, PrefixedType.$checkItem,
PrefixedType.create)
..pp(9, 'pipes', PbFieldType.PM, PrefixedType.$checkItem,
PrefixedType.create);
ReflectionInfoModel() : super();
ReflectionInfoModel.fromBuffer(List<int> i,
@ -172,7 +172,9 @@ class ReflectionInfoModel extends GeneratedMessage {
List<PropertyMetadataModel> get propertyMetadata => $_get(6, 7, null);
List<PrefixedDirective> get directives => $_get(7, 8, null);
List<PrefixedType> get directives => $_get(7, 8, null);
List<PrefixedType> get pipes => $_get(8, 9, null);
}
class _ReadonlyReflectionInfoModel extends ReflectionInfoModel
@ -192,8 +194,8 @@ const PropertyMetadataModel$json = const {
],
};
const PrefixedDirective$json = const {
'1': 'PrefixedDirective',
const PrefixedType$json = const {
'1': 'PrefixedType',
'2': const [
const {'1': 'prefix', '3': 1, '4': 1, '5': 9},
const {'1': 'name', '3': 2, '4': 1, '5': 9},
@ -233,14 +235,21 @@ const ReflectionInfoModel$json = const {
'3': 8,
'4': 3,
'5': 11,
'6': '.angular2.src.transform.common.model.proto.PrefixedDirective'
'6': '.angular2.src.transform.common.model.proto.PrefixedType'
},
const {
'1': 'pipes',
'3': 9,
'4': 3,
'5': 11,
'6': '.angular2.src.transform.common.model.proto.PrefixedType'
},
],
};
/**
* Generated with:
* reflection_info_model.proto (e81bf93b6872b2bd5fabc6625be2560bacc3d186)
* reflection_info_model.proto (7670e589733b1190f9b4f1c9b42f90a613975afd)
* libprotoc 2.6.1
* dart-protoc-plugin (af5fc2bf1de367a434c3b1847ab260510878ffc0)
*/

View File

@ -13,12 +13,12 @@ message PropertyMetadataModel {
repeated AnnotationModel annotations = 2;
}
message PrefixedDirective {
// The prefix used to reference this Directive, if any.
message PrefixedType {
// The prefix used to reference this Type, if any.
optional string prefix = 1;
// The name of the Directive or directive alias.
// See https://goo.gl/d8XPt0 for info on directive aliases.
// The name of the Type or type alias.
// See https://goo.gl/d8XPt0 for info on type aliases.
optional string name = 2;
}
@ -44,5 +44,8 @@ message ReflectionInfoModel {
// Directive dependencies parsed from the @View or @Component `directives`
// parameter.
repeated PrefixedDirective directives = 8;
repeated PrefixedType directives = 8;
// Pipe dependencies parsed from the @View or @Component `pipes` parameter.
repeated PrefixedType pipes = 9;
}

View File

@ -1,6 +1,6 @@
library angular2.transform.template_compiler.ng_compiler;
import 'package:angular2/src/compiler/command_compiler.dart';
import 'package:angular2/src/compiler/view_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';
@ -13,6 +13,7 @@ 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 'package:angular2/router/router_link_dsl.dart';
import 'package:angular2/src/compiler/proto_view_compiler.dart';
import 'xhr_impl.dart';
import 'url_resolver.dart';
@ -25,8 +26,11 @@ TemplateCompiler createTemplateCompiler(AssetReader reader,
// TODO(yjbanov): add router AST transformer when ready
var parser = new ng.Parser(new ng.Lexer());
var templateParser = new TemplateParser(parser,
new DomElementSchemaRegistry(), _htmlParser, [new RouterLinkTransform(parser)]);
var templateParser = new TemplateParser(
parser,
new DomElementSchemaRegistry(),
_htmlParser,
[new RouterLinkTransform(parser)]);
var cdCompiler = changeDetectionConfig != null
? new ChangeDetectionCompiler(changeDetectionConfig)
@ -37,6 +41,9 @@ TemplateCompiler createTemplateCompiler(AssetReader reader,
new TemplateNormalizer(_xhr, _urlResolver, _htmlParser),
templateParser,
new StyleCompiler(_xhr, _urlResolver),
new CommandCompiler(),
cdCompiler);
cdCompiler,
new ProtoViewCompiler(),
new ViewCompiler(),
null /* ResolvedMetadataCache */,
changeDetectionConfig);
}

View File

@ -33,8 +33,9 @@ class NgMeta {
static const _TYPE_VALUE = 'type';
static const _VALUE_KEY = 'value';
/// Directive metadata for each type annotated as a directive.
final Map<String, CompileDirectiveMetadata> types;
/// Metadata for each type annotated as a directive/pipe.
/// Type: [CompileDirectiveMetadata]/[CompilePipeMetadata]
final Map<String, dynamic> types;
/// List of other types and names associated with a given name.
final Map<String, List<String>> aliases;
@ -43,7 +44,7 @@ class NgMeta {
final NgDepsModel ngDeps;
NgMeta(
{Map<String, CompileDirectiveMetadata> types,
{Map<String, dynamic> types,
Map<String, List<String>> aliases,
this.ngDeps: null})
: this.types = types != null ? types : {},
@ -91,7 +92,7 @@ class NgMeta {
continue;
}
if (entry[_KIND_KEY] == _TYPE_VALUE) {
types[key] = CompileDirectiveMetadata.fromJson(entry[_VALUE_KEY]);
types[key] = CompileMetadataWithType.fromJson(entry[_VALUE_KEY]);
} else if (entry[_KIND_KEY] == _ALIAS_VALUE) {
aliases[key] = entry[_VALUE_KEY];
}
@ -123,8 +124,8 @@ class NgMeta {
}
/// Returns the metadata for every type associated with the given [alias].
List<CompileDirectiveMetadata> flatten(String alias) {
var result = <CompileDirectiveMetadata>[];
List<dynamic> flatten(String alias) {
var result = [];
var seen = new Set();
helper(name) {
if (!seen.add(name)) {

View File

@ -10,6 +10,7 @@ const ENTRY_POINT_PARAM = 'entry_points';
const FORMAT_CODE_PARAM = 'format_code';
const REFLECT_PROPERTIES_AS_ATTRIBUTES = 'reflect_properties_as_attributes';
const PLATFORM_DIRECTIVES = 'platform_directives';
const PLATFORM_PIPES = 'platform_pipes';
const INIT_REFLECTOR_PARAM = 'init_reflector';
const INLINE_VIEWS_PARAM = 'inline_views';
const MIRROR_MODE_PARAM = 'mirror_mode';
@ -44,9 +45,15 @@ class TransformerOptions {
final bool genChangeDetectionDebugInfo;
/// A set of directives that will be automatically passed-in to the template compiler
/// Format of an item in the list: angular2/lib/src/common/directives.dart#CORE_DIRECTIVES
/// Format of an item in the list:
/// angular2/lib/src/common/common_directives.dart#COMMON_DIRECTIVES
final List<String> platformDirectives;
/// A set of pipes that will be automatically passed-in to the template compiler
/// Format of an item in the list:
/// angular2/lib/src/common/pipes.dart#COMMON_PIPES
final List<String> platformPipes;
/// Whether to format generated code.
/// Code that is only modified will never be formatted because doing so may
/// invalidate the source maps generated by `dart2js` and/or other tools.
@ -76,6 +83,7 @@ class TransformerOptions {
{this.genChangeDetectionDebugInfo,
this.reflectPropertiesAsAttributes,
this.platformDirectives,
this.platformPipes,
this.inlineViews,
this.lazyTransformers,
this.formatCode});
@ -89,6 +97,7 @@ class TransformerOptions {
bool genChangeDetectionDebugInfo: false,
bool reflectPropertiesAsAttributes: false,
List<String> platformDirectives,
List<String> platformPipes,
bool lazyTransformers: false,
bool formatCode: false}) {
var annotationMatcher = new AnnotationMatcher()
@ -101,6 +110,7 @@ class TransformerOptions {
genChangeDetectionDebugInfo: genChangeDetectionDebugInfo,
reflectPropertiesAsAttributes: reflectPropertiesAsAttributes,
platformDirectives: platformDirectives,
platformPipes: platformPipes,
inlineViews: inlineViews,
lazyTransformers: lazyTransformers,
formatCode: formatCode);

View File

@ -13,6 +13,7 @@ TransformerOptions parseBarbackSettings(BarbackSettings settings) {
var reflectPropertiesAsAttributes =
_readBool(config, REFLECT_PROPERTIES_AS_ATTRIBUTES, defaultValue: false);
var platformDirectives = _readStringList(config, PLATFORM_DIRECTIVES);
var platformPipes = _readStringList(config, PLATFORM_PIPES);
var formatCode = _readBool(config, FORMAT_CODE_PARAM, defaultValue: false);
String mirrorModeVal =
config.containsKey(MIRROR_MODE_PARAM) ? config[MIRROR_MODE_PARAM] : '';
@ -36,6 +37,7 @@ TransformerOptions parseBarbackSettings(BarbackSettings settings) {
customAnnotationDescriptors: _readCustomAnnotations(config),
reflectPropertiesAsAttributes: reflectPropertiesAsAttributes,
platformDirectives: platformDirectives,
platformPipes: platformPipes,
inlineViews: _readBool(config, INLINE_VIEWS_PARAM, defaultValue: false),
lazyTransformers:
_readBool(config, LAZY_TRANSFORMERS, defaultValue: false),

View File

@ -1,4 +1,4 @@
library angular2.transform.common.directive_metadata_reader;
library angular2.transform.common.type_metadata_reader;
import 'dart:async';
@ -16,25 +16,30 @@ import 'package:angular2/src/transform/common/logging.dart';
import 'package:barback/barback.dart' show AssetId;
import 'naive_eval.dart';
import 'url_resolver.dart';
class DirectiveMetadataReader {
final _DirectiveMetadataVisitor _visitor;
class TypeMetadataReader {
final _DirectiveMetadataVisitor _directiveVisitor;
final _PipeMetadataVisitor _pipeVisitor;
final TemplateCompiler _templateCompiler;
DirectiveMetadataReader._(this._visitor, this._templateCompiler);
TypeMetadataReader._(
this._directiveVisitor, this._pipeVisitor, this._templateCompiler);
/// Accepts an [AnnotationMatcher] which tests that an [Annotation]
/// is a [Directive], [Component], or [View].
factory DirectiveMetadataReader(AnnotationMatcher annotationMatcher,
factory TypeMetadataReader(AnnotationMatcher annotationMatcher,
InterfaceMatcher interfaceMatcher, TemplateCompiler templateCompiler) {
var lifecycleVisitor = new _LifecycleHookVisitor(interfaceMatcher);
var visitor =
var directiveVisitor =
new _DirectiveMetadataVisitor(annotationMatcher, lifecycleVisitor);
var pipeVisitor = new _PipeMetadataVisitor(annotationMatcher);
return new DirectiveMetadataReader._(visitor, templateCompiler);
return new TypeMetadataReader._(
directiveVisitor, pipeVisitor, templateCompiler);
}
/// Reads *un-normalized* [CompileDirectiveMetadata] from the
/// Reads *un-normalized* [CompileDirectiveMetadata]/[CompilePipeMetadata] from the
/// [ClassDeclaration] `node`.
///
/// `node` is expected to be a class which may have a [Directive] or [Component]
@ -44,15 +49,18 @@ class DirectiveMetadataReader {
/// `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();
Future<dynamic> readTypeMetadata(ClassDeclaration node, AssetId assetId) {
_directiveVisitor.reset(assetId);
_pipeVisitor.reset(assetId);
node.accept(_directiveVisitor);
node.accept(_pipeVisitor);
if (_directiveVisitor.hasMetadata) {
final metadata = _directiveVisitor.createMetadata();
return _templateCompiler.normalizeDirectiveMetadata(metadata);
} else if (_pipeVisitor.hasMetadata) {
return new Future.value(_pipeVisitor.createMetadata());
} else {
return new Future.value(null);
}
}
}
@ -103,6 +111,19 @@ String _expressionToString(Expression node, String nodeDescription) {
return value;
}
/// Evaluates `node` and expects that the result will be a bool. If not,
/// throws a [FormatException].
bool _expressionToBool(Expression node, String nodeDescription) {
var value = naiveEval(node);
if (value is! bool) {
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
@ -328,7 +349,7 @@ class _DirectiveMetadataVisitor extends Object
node.metadata.accept(this);
if (this._hasMetadata) {
_type = new CompileTypeMetadata(
moduleUrl: 'asset:${_assetId.package}/${_assetId.path}',
moduleUrl: toAssetUri(_assetId),
name: node.name.toString(),
runtime: null // Intentionally `null`, cannot be provided here.
);
@ -386,8 +407,7 @@ class _DirectiveMetadataVisitor extends Object
void _checkMeta() {
if (!_hasMetadata) {
throw new ArgumentError(
'Incorrect value passed to readDirectiveMetadata. '
throw new ArgumentError('Incorrect value passed to readTypeMetadata. '
'Expected type is ClassDeclaration');
}
}
@ -550,3 +570,111 @@ class _CompileTemplateMetadataVisitor
static final _viewEncapsulationMap =
new Map.fromIterable(ViewEncapsulation.values, key: (v) => v.toString());
}
/// Visitor responsible for processing a [Pipe] annotated
/// [ClassDeclaration] and creating a [CompilePipeMetadata] object.
class _PipeMetadataVisitor extends Object with RecursiveAstVisitor<Object> {
/// Tests [Annotation]s to determine if they deifne a [Directive],
/// [Component], [View], or none of these.
final AnnotationMatcher _annotationMatcher;
/// The [AssetId] we are currently processing.
AssetId _assetId;
_PipeMetadataVisitor(this._annotationMatcher) {
reset(null);
}
/// Whether the visitor has found a [Pipe] annotation
/// since the last call to `reset`.
bool _hasMetadata = false;
// Annotation fields
CompileTypeMetadata _type;
String _name;
bool _pure;
void reset(AssetId assetId) {
_assetId = assetId;
_hasMetadata = false;
_type = null;
_name = null;
_pure = null;
}
bool get hasMetadata => _hasMetadata;
CompilePipeMetadata createMetadata() {
return new CompilePipeMetadata(type: _type, name: _name, pure: _pure);
}
@override
Object visitAnnotation(Annotation node) {
var isPipe = _annotationMatcher.isPipe(node, _assetId);
if (isPipe) {
if (_hasMetadata) {
throw new FormatException(
'Only one Pipe is allowed per class. '
'Found unexpected "$node".',
'$node' /* source */);
}
_hasMetadata = true;
super.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: toAssetUri(_assetId),
name: node.name.toString(),
runtime: null // Intentionally `null`, cannot be provided here.
);
node.members.accept(this);
}
return null;
}
@override
Object 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 pipes.',
'$node' /* source */);
}
var keyString = '${node.name.label}';
switch (keyString) {
case 'name':
_popuplateName(node.expression);
break;
case 'pure':
_populatePure(node.expression);
break;
}
return null;
}
void _checkMeta() {
if (!_hasMetadata) {
throw new ArgumentError('Incorrect value passed to readTypeMetadata. '
'Expected type is ClassDeclaration');
}
}
void _popuplateName(Expression nameValue) {
_checkMeta();
_name = _expressionToString(nameValue, 'Pipe#name');
}
void _populatePure(Expression pureValue) {
_checkMeta();
_pure = _expressionToBool(pureValue, 'Pipe#pure');
}
}

View File

@ -9,7 +9,7 @@ import 'package:angular2/src/compiler/template_compiler.dart';
import 'package:angular2/src/transform/common/annotation_matcher.dart';
import 'package:angular2/src/transform/common/asset_reader.dart';
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/type_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/ng_compiler.dart';
@ -64,12 +64,12 @@ class _NgMetaVisitor extends Object with SimpleAstVisitor<Object> {
/// The [AssetId] we are currently processing.
final AssetId assetId;
final DirectiveMetadataReader _reader;
final TypeMetadataReader _reader;
final _normalizations = <Future>[];
_NgMetaVisitor(this.ngMeta, this.assetId, AnnotationMatcher annotationMatcher,
InterfaceMatcher interfaceMatcher, TemplateCompiler templateCompiler)
: _reader = new DirectiveMetadataReader(
: _reader = new TypeMetadataReader(
annotationMatcher, interfaceMatcher, templateCompiler);
Future whenDone() {
@ -88,12 +88,11 @@ class _NgMetaVisitor extends Object with SimpleAstVisitor<Object> {
@override
Object visitClassDeclaration(ClassDeclaration node) {
_normalizations.add(_reader
.readDirectiveMetadata(node, assetId)
.then((compileDirectiveMetadata) {
if (compileDirectiveMetadata != null) {
ngMeta.types[compileDirectiveMetadata.type.name] =
compileDirectiveMetadata;
_normalizations.add(
_reader.readTypeMetadata(node, assetId).then((compileMetadataWithType) {
if (compileMetadataWithType != null) {
ngMeta.types[compileMetadataWithType.type.name] =
compileMetadataWithType;
}
}).catchError((err) {
log.error('ERROR: $err', asset: assetId);

View File

@ -145,9 +145,9 @@ class _CodegenState {
class $_changeDetectorTypeName extends ${_genPrefix}$_BASE_CLASS<$_contextTypeName> {
${_genDeclareFields()}
$_changeDetectorTypeName(dispatcher)
$_changeDetectorTypeName()
: super(${codify(_changeDetectorDefId)},
dispatcher, ${_records.length},
${_records.length},
${_changeDetectorTypeName}.${_GEN_PROPERTY_BINDING_TARGETS_NAME},
${_changeDetectorTypeName}.${_GEN_DIRECTIVE_INDICES_NAME},
${_changeDetectionStrategyAsCode}) {
@ -177,8 +177,8 @@ class _CodegenState {
${_genDirectiveIndices()};
static ${_genPrefix}ChangeDetector
$CHANGE_DETECTOR_FACTORY_METHOD(a) {
return new $_changeDetectorTypeName(a);
$CHANGE_DETECTOR_FACTORY_METHOD() {
return new $_changeDetectorTypeName();
}
}
''');
@ -283,13 +283,19 @@ class _CodegenState {
String _maybeGenDehydrateDirectives() {
var destroyPipesParamName = 'destroyPipes';
var destroyPipesCode = _names.genPipeOnDestroy();
if (destroyPipesCode.isNotEmpty) {
destroyPipesCode = 'if (${destroyPipesParamName}) {${destroyPipesCode}}';
}
var dehydrateFieldsCode = _names.genDehydrateFields();
if (destroyPipesCode.isEmpty && dehydrateFieldsCode.isEmpty) return '';
return 'void dehydrateDirectives(${destroyPipesParamName}) '
'{ ${destroyPipesCode} ${dehydrateFieldsCode} }';
var destroyDirectivesCode =
_logic.genDirectivesOnDestroy(this._directiveRecords);
if (destroyPipesCode.isEmpty &&
dehydrateFieldsCode.isEmpty &&
destroyDirectivesCode.isEmpty) return '';
return '''void dehydrateDirectives(${destroyPipesParamName}) {
if (${destroyPipesParamName}) {
${destroyPipesCode}
${destroyDirectivesCode}
}
${dehydrateFieldsCode}
}''';
}
String _maybeGenHydrateDirectives() {

View File

@ -19,11 +19,14 @@ import 'package:barback/barback.dart';
///
/// The returned value wraps the [NgDepsModel] at `assetId` as well as these
/// created objects.
Future<CompileDataResults> createCompileData(AssetReader reader,
AssetId assetId, List<String> platformDirectives) async {
Future<CompileDataResults> createCompileData(
AssetReader reader,
AssetId assetId,
List<String> platformDirectives,
List<String> platformPipes) async {
return logElapsedAsync(() async {
final creator =
await _CompileDataCreator.create(reader, assetId, platformDirectives);
final creator = await _CompileDataCreator.create(
reader, assetId, platformDirectives, platformPipes);
return creator != null ? creator.createCompileData() : null;
}, operationName: 'createCompileData', assetId: assetId);
}
@ -43,18 +46,20 @@ class _CompileDataCreator {
final AssetId entryPoint;
final NgMeta ngMeta;
final List<String> platformDirectives;
final List<String> platformPipes;
_CompileDataCreator(
this.reader, this.entryPoint, this.ngMeta, this.platformDirectives);
_CompileDataCreator(this.reader, this.entryPoint, this.ngMeta,
this.platformDirectives, this.platformPipes);
static Future<_CompileDataCreator> create(AssetReader reader, AssetId assetId,
List<String> platformDirectives) async {
List<String> platformDirectives, List<String> platformPipes) async {
if (!(await reader.hasInput(assetId))) return null;
final json = await reader.readAsString(assetId);
if (json == null || json.isEmpty) return null;
final ngMeta = new NgMeta.fromJson(JSON.decode(json));
return new _CompileDataCreator(reader, assetId, ngMeta, platformDirectives);
return new _CompileDataCreator(
reader, assetId, ngMeta, platformDirectives, platformPipes);
}
NgDepsModel get ngDeps => ngMeta.ngDeps;
@ -67,39 +72,25 @@ class _CompileDataCreator {
final compileData =
<ReflectionInfoModel, NormalizedComponentWithViewDirectives>{};
final ngMetaMap = await _extractNgMeta();
final platformDirectives = await _readPlatformDirectives();
final platformDirectives =
await _readPlatformTypes(this.platformDirectives, 'directives');
final platformPipes = await _readPlatformTypes(this.platformPipes, 'pipes');
for (var reflectable in ngDeps.reflectables) {
if (ngMeta.types.containsKey(reflectable.name)) {
final compileDirectiveMetadata = ngMeta.types[reflectable.name];
if (compileDirectiveMetadata.template != null) {
if (compileDirectiveMetadata is CompileDirectiveMetadata &&
compileDirectiveMetadata.template != null) {
final compileDatum = new NormalizedComponentWithViewDirectives(
compileDirectiveMetadata, <CompileDirectiveMetadata>[]);
compileDirectiveMetadata,
<CompileDirectiveMetadata>[],
<CompilePipeMetadata>[]);
compileDatum.directives.addAll(platformDirectives);
for (var dep in reflectable.directives) {
if (!ngMetaMap.containsKey(dep.prefix)) {
log.warning(
'Missing prefix "${dep.prefix}" '
'needed by "${dep}" from metadata map',
asset: entryPoint);
continue;
}
final depNgMeta = ngMetaMap[dep.prefix];
if (depNgMeta.types.containsKey(dep.name)) {
compileDatum.directives.add(depNgMeta.types[dep.name]);
} else if (depNgMeta.aliases.containsKey(dep.name)) {
compileDatum.directives.addAll(depNgMeta.flatten(dep.name));
} else {
log.warning(
'Could not find Directive entry for $dep. '
'Please be aware that Dart transformers have limited support for '
'reusable, pre-defined lists of Directives (aka '
'"directive aliases"). See https://goo.gl/d8XPt0 for details.',
asset: entryPoint);
}
}
compileDatum.directives
.addAll(_resolveTypeMetadata(ngMetaMap, reflectable.directives));
compileDatum.pipes.addAll(platformPipes);
compileDatum.pipes
.addAll(_resolveTypeMetadata(ngMetaMap, reflectable.pipes));
compileData[reflectable] = compileDatum;
}
}
@ -107,46 +98,73 @@ class _CompileDataCreator {
return new CompileDataResults._(ngMeta, compileData);
}
Future<List<CompileDirectiveMetadata>> _readPlatformDirectives() async {
if (platformDirectives == null) return const [];
List<dynamic> _resolveTypeMetadata(
Map<String, NgMeta> ngMetaMap, List<PrefixedType> prefixedTypes) {
var resolvedMetadata = [];
for (var dep in prefixedTypes) {
if (!ngMetaMap.containsKey(dep.prefix)) {
log.warning(
'Missing prefix "${dep.prefix}" '
'needed by "${dep}" from metadata map',
asset: entryPoint);
continue;
}
final depNgMeta = ngMetaMap[dep.prefix];
if (depNgMeta.types.containsKey(dep.name)) {
resolvedMetadata.add(depNgMeta.types[dep.name]);
} else if (depNgMeta.aliases.containsKey(dep.name)) {
resolvedMetadata.addAll(depNgMeta.flatten(dep.name));
} else {
log.warning(
'Could not find Directive/Pipe entry for $dep. '
'Please be aware that Dart transformers have limited support for '
'reusable, pre-defined lists of Directives/Pipes (aka '
'"directive/pipe aliases"). See https://goo.gl/d8XPt0 for details.',
asset: entryPoint);
}
}
return resolvedMetadata;
}
Future<List<dynamic>> _readPlatformTypes(
List<String> inputPlatformTypes, String configOption) async {
if (inputPlatformTypes == null) return const [];
final res = [];
for (var ad in platformDirectives) {
final parts = ad.split("#");
for (var pd in inputPlatformTypes) {
final parts = pd.split("#");
if (parts.length != 2) {
log.warning(
'The platform directives configuration option '
'The platform ${configOption} configuration option '
'must be in the following format: "URI#TOKEN"',
asset: entryPoint);
return const [];
}
res.addAll(await _readPlatformDirectivesFromUri(parts[0], parts[1]));
res.addAll(await _readPlatformTypesFromUri(parts[0], parts[1]));
}
return res;
}
Future<List<CompileDirectiveMetadata>> _readPlatformDirectivesFromUri(
Future<List<dynamic>> _readPlatformTypesFromUri(
String uri, String token) async {
final metaAssetId = fromUri(toMetaExtension(uri));
if (await reader.hasInput(metaAssetId)) {
try {
var jsonString = await reader.readAsString(metaAssetId);
if (jsonString != null && jsonString.isNotEmpty) {
var newMetadata = new NgMeta.fromJson(JSON.decode(jsonString));
try {
var jsonString = await reader.readAsString(metaAssetId);
if (jsonString != null && jsonString.isNotEmpty) {
var newMetadata = new NgMeta.fromJson(JSON.decode(jsonString));
if (newMetadata.types.containsKey(token)) {
return [newMetadata.types[token]];
} else if (newMetadata.aliases.containsKey(token)) {
return newMetadata.flatten(token);
} else {
log.warning(
'Could not resolve platform directive ${token} in ${uri}',
asset: metaAssetId);
}
if (newMetadata.types.containsKey(token)) {
return [newMetadata.types[token]];
} else if (newMetadata.aliases.containsKey(token)) {
return newMetadata.flatten(token);
} else {
log.warning('Could not resolve platform type ${token} in ${uri}',
asset: metaAssetId);
}
} catch (ex, stackTrace) {
log.warning('Failed to decode: $ex, $stackTrace', asset: metaAssetId);
}
} catch (ex, stackTrace) {
log.warning('Failed to decode: $ex, $stackTrace', asset: metaAssetId);
}
return [];
}

View File

@ -30,14 +30,15 @@ import 'compile_data_creator.dart';
Future<Outputs> processTemplates(AssetReader reader, AssetId assetId,
{bool genChangeDetectionDebugInfo: false,
bool reflectPropertiesAsAttributes: false,
List<String> platformDirectives}) async {
var viewDefResults =
await createCompileData(reader, assetId, platformDirectives);
List<String> platformDirectives,
List<String> platformPipes}) async {
var viewDefResults = await createCompileData(
reader, assetId, platformDirectives, platformPipes);
if (viewDefResults == null) return null;
final directiveMetadatas = viewDefResults.ngMeta.types.values;
if (directiveMetadatas.isNotEmpty) {
final compileTypeMetadatas = viewDefResults.ngMeta.types.values;
if (compileTypeMetadatas.isNotEmpty) {
var processor = new reg.Processor();
directiveMetadatas.forEach(processor.process);
compileTypeMetadatas.forEach(processor.process);
viewDefResults.ngMeta.ngDeps.getters
.addAll(processor.getterNames.map((e) => e.sanitizedName));
viewDefResults.ngMeta.ngDeps.setters
@ -71,7 +72,7 @@ Future<Outputs> processTemplates(AssetReader reader, AssetId assetId,
..prefix = '_templates');
for (var reflectable in viewDefResults.viewDefinitions.keys) {
reflectable.annotations.add(new AnnotationModel()
..name = '_templates.Host${reflectable.name}Template'
..name = '_templates.hostViewFactory_${reflectable.name}'
..isConstObject = true);
}
}

View File

@ -1,7 +1,6 @@
library angular2.transform.template_compiler.reflection.model;
import 'package:angular2/src/compiler/util.dart';
import 'package:angular2/src/core/linker/event_config.dart';
/// Defines the names of getters, setters, and methods which need to be
/// available to Angular 2 via the `reflector` at runtime.
@ -42,5 +41,15 @@ class ReflectiveAccessor {
}
String sanitizePropertyName(String name) {
return dashCaseToCamelCase(EventConfig.parse(name).fieldName);
// Angular supports renaming class property names
// when used in a template.
// Long syntax: 'classPropertyName: templateName'
// Short syntax: 'classPropertyName'
// This method always returns the 'classPropertyName'.
var fieldName = name;
var separatorIdx = name.indexOf(':');
if (separatorIdx > -1) {
fieldName = name.substring(0, separatorIdx).trim();
}
return dashCaseToCamelCase(fieldName);
}

View File

@ -14,16 +14,18 @@ class Processor implements CodegenModel {
/// The names of all requested `method`s.
final Set<ReflectiveAccessor> methodNames = new Set<ReflectiveAccessor>();
void process(CompileDirectiveMetadata meta) {
if (meta.outputs != null) {
meta.outputs.keys.forEach((eventName) {
getterNames.add(new ReflectiveAccessor(eventName));
});
}
if (meta.inputs != null) {
meta.inputs.keys.forEach((inputName) {
setterNames.add(new ReflectiveAccessor(inputName));
});
void process(CompileMetadataWithType meta) {
if (meta is CompileDirectiveMetadata) {
if (meta.outputs != null) {
meta.outputs.keys.forEach((eventName) {
getterNames.add(new ReflectiveAccessor(eventName));
});
}
if (meta.inputs != null) {
meta.inputs.keys.forEach((inputName) {
setterNames.add(new ReflectiveAccessor(inputName));
});
}
}
}
}

View File

@ -46,7 +46,8 @@ class TemplateCompiler extends Transformer implements LazyTransformer {
var outputs = await processTemplates(reader, primaryId,
genChangeDetectionDebugInfo: options.genChangeDetectionDebugInfo,
reflectPropertiesAsAttributes: options.reflectPropertiesAsAttributes,
platformDirectives: options.platformDirectives);
platformDirectives: options.platformDirectives,
platformPipes: options.platformPipes);
var ngDepsCode = _emptyNgDepsContents;
var templatesCode = '';
if (outputs != null) {

View File

@ -4,6 +4,7 @@ final ngFor = {
"NgFor": {
"kind": "type",
"value": {
"class": "Directive",
"isComponent": false,
"dynamicLoadable": true,
"selector": "[ngFor][ngForOf]",

View File

@ -26,6 +26,17 @@ CompileDirectiveMetadata createComponentMetadataForTest(
encapsulation: ViewEncapsulation.Emulated, template: template));
}
CompilePipeMetadata createPipeMetadataForTest(
{String name: 'TestPipe',
moduleUrl: 'asset:angular2/test/test.dart',
String pipeName: 'test',
bool pure: false}) {
return new CompilePipeMetadata(
type: new CompileTypeMetadata(name: name, moduleUrl: moduleUrl),
name: pipeName,
pure: pure);
}
CompileDirectiveMetadata createDirectiveMetadataForTest(
{String name: 'TestMetadata',
String moduleUrl: 'asset:angular2/test/test.dart',
@ -59,6 +70,13 @@ CompileDirectiveMetadata createBar([String moduleBase = 'asset:a']) =>
selector: 'bar',
template: 'Bar');
CompilePipeMetadata createBarPipe([String moduleBase = 'asset:a']) =>
createPipeMetadataForTest(
name: 'BarPipe',
pipeName: 'bar',
moduleUrl: '$moduleBase/export_cycle_files/bar.dart',
pure: false);
CompileDirectiveMetadata createBaz([String moduleBase = 'asset:a']) =>
createComponentMetadataForTest(
name: 'BazComponent',

View File

@ -3,6 +3,7 @@
{
"kind": "type",
"value": {
"class": "Directive",
"isComponent": true,
"dynamicLoadable": true,
"selector":"hello-app",
@ -29,4 +30,4 @@
}
}
}
}
}

View File

@ -3,6 +3,7 @@
{
"kind": "type",
"value": {
"class": "Directive",
"isComponent": true,
"dynamicLoadable": true,
"selector":"hello-app",
@ -29,4 +30,4 @@
}
}
}
}
}

View File

@ -460,8 +460,8 @@ void allTests() {
expect(componentFirst.directives).toBeNotNull();
expect(componentFirst.directives.length).toEqual(2);
expect(componentFirst.directives.first)
.toEqual(new PrefixedDirective()..name = 'Dep');
expect(componentFirst.directives[1]).toEqual(new PrefixedDirective()
.toEqual(new PrefixedType()..name = 'Dep');
expect(componentFirst.directives[1]).toEqual(new PrefixedType()
..name = 'Dep'
..prefix = 'dep2');
});
@ -474,11 +474,10 @@ void allTests() {
expect(viewFirst).toBeNotNull();
expect(viewFirst.directives).toBeNotNull();
expect(viewFirst.directives.length).toEqual(2);
expect(viewFirst.directives.first).toEqual(new PrefixedDirective()
expect(viewFirst.directives.first).toEqual(new PrefixedType()
..name = 'Dep'
..prefix = 'dep2');
expect(viewFirst.directives[1])
.toEqual(new PrefixedDirective()..name = 'Dep');
expect(viewFirst.directives[1]).toEqual(new PrefixedType()..name = 'Dep');
});
it('should populate `directives` from @Component value with no @View.',
@ -490,12 +489,54 @@ void allTests() {
expect(componentOnly.directives).toBeNotNull();
expect(componentOnly.directives.length).toEqual(2);
expect(componentOnly.directives.first)
.toEqual(new PrefixedDirective()..name = 'Dep');
expect(componentOnly.directives[1]).toEqual(new PrefixedDirective()
.toEqual(new PrefixedType()..name = 'Dep');
expect(componentOnly.directives[1]).toEqual(new PrefixedType()
..name = 'Dep'
..prefix = 'dep2');
});
it('should populate `pipes` from @View value specified second.', () async {
var model =
(await _testCreateModel('directives_files/components.dart')).ngDeps;
final componentFirst = reflectableNamed(model, 'ComponentFirst');
expect(componentFirst).toBeNotNull();
expect(componentFirst.pipes).toBeNotNull();
expect(componentFirst.pipes.length).toEqual(2);
expect(componentFirst.pipes.first)
.toEqual(new PrefixedType()..name = 'PipeDep');
expect(componentFirst.pipes[1]).toEqual(new PrefixedType()
..name = 'PipeDep'
..prefix = 'dep2');
});
it('should populate `pipes` from @View value specified first.', () async {
var model =
(await _testCreateModel('directives_files/components.dart')).ngDeps;
final viewFirst = reflectableNamed(model, 'ViewFirst');
expect(viewFirst).toBeNotNull();
expect(viewFirst.pipes).toBeNotNull();
expect(viewFirst.pipes.length).toEqual(2);
expect(viewFirst.pipes.first).toEqual(new PrefixedType()
..name = 'PipeDep'
..prefix = 'dep2');
expect(viewFirst.pipes[1]).toEqual(new PrefixedType()..name = 'PipeDep');
});
it('should populate `pipes` from @Component value with no @View.',
() async {
var model =
(await _testCreateModel('directives_files/components.dart')).ngDeps;
final componentOnly = reflectableNamed(model, 'ComponentOnly');
expect(componentOnly).toBeNotNull();
expect(componentOnly.pipes).toBeNotNull();
expect(componentOnly.pipes.length).toEqual(2);
expect(componentOnly.pipes.first)
.toEqual(new PrefixedType()..name = 'PipeDep');
expect(componentOnly.pipes[1]).toEqual(new PrefixedType()
..name = 'PipeDep'
..prefix = 'dep2');
});
it('should merge `outputs` from the annotation and fields.', () async {
var model = await _testCreateModel('directives_files/components.dart');
expect(model.types['ComponentWithOutputs'].outputs).toEqual(
@ -560,6 +601,30 @@ void allTests() {
expect(warning.toLowerCase())
.toContain('cannot specify view parameters on @component');
});
it('should warn if @Component has `pipes` and @View is present.', () async {
final logger = new RecordingLogger();
final model = await _testCreateModel('bad_directives_files/pipes.dart',
logger: logger);
var warning =
logger.logs.firstWhere((l) => l.contains('WARN'), orElse: () => null);
expect(warning).toBeNotNull();
expect(warning.toLowerCase())
.toContain('cannot specify view parameters on @component');
});
});
describe('pipes', () {
it('should read the pipe name', () async {
var model = await _testCreateModel('pipe_files/pipes.dart');
expect(model.types['NameOnlyPipe'].name).toEqual('nameOnly');
expect(model.types['NameOnlyPipe'].pure).toBe(false);
});
it('should read the pure flag', () async {
var model = await _testCreateModel('pipe_files/pipes.dart');
expect(model.types['NameAndPurePipe'].pure).toBe(true);
});
});
}

View File

@ -0,0 +1,9 @@
library angular2.test.transform.directive_processor.bad_directives_files.pipes;
import 'package:angular2/angular2.dart'
show Component, Directive, View, NgElement;
import 'dep1.dart';
@Component(selector: 'component-first', pipes: [Dep])
@View(template: '<div>Hi</div>')
class BadDirectives {}

View File

@ -6,17 +6,24 @@ import 'dep1.dart';
import 'dep2.dart' as dep2;
@Component(selector: 'component-first')
@View(template: '<dep1></dep1><dep2></dep2>', directives: [Dep, dep2.Dep])
@View(
template: '<dep1></dep1><dep2></dep2>',
directives: [Dep, dep2.Dep],
pipes: [PipeDep, dep2.PipeDep])
class ComponentFirst {}
@View(template: '<dep1></dep1><dep2></dep2>', directives: [dep2.Dep, Dep])
@View(
template: '<dep1></dep1><dep2></dep2>',
directives: [dep2.Dep, Dep],
pipes: [dep2.PipeDep, PipeDep])
@Component(selector: 'view-first')
class ViewFirst {}
@Component(
selector: 'component-only',
template: '<dep1></dep1><dep2></dep2>',
directives: [Dep, dep2.Dep])
directives: [Dep, dep2.Dep],
pipes: [PipeDep, dep2.PipeDep])
class ComponentOnly {}
@Component(

View File

@ -1,8 +1,10 @@
library angular2.test.transform.directive_processor.directive_files.dep1;
import 'package:angular2/angular2.dart'
show Component, Directive, View, NgElement;
import 'package:angular2/angular2.dart' show Component, Directive, View, Pipe;
@Component(selector: 'dep1')
@View(template: 'Dep1')
class Dep {}
@Pipe(name: 'dep1')
class PipeDep {}

View File

@ -1,8 +1,10 @@
library angular2.test.transform.directive_processor.directive_files.dep2;
import 'package:angular2/angular2.dart'
show Component, Directive, View, NgElement;
import 'package:angular2/angular2.dart' show Component, Directive, View, Pipe;
@Component(selector: 'dep2')
@View(template: 'Dep2')
class Dep {}
@Pipe(name: 'dep2')
class PipeDep {}

View File

@ -0,0 +1,9 @@
library angular2.test.transform.directive_processor.pipe_files.pipes;
import 'package:angular2/angular2.dart' show Pipe;
@Pipe(name: 'nameOnly')
class NameOnlyPipe {}
@Pipe(name: 'nameAndPure', pure: true)
class NameAndPurePipe {}

View File

@ -17,7 +17,7 @@ void initReflector() {
new _ngRef.ReflectionInfo(const [
const Component(selector: '[soup]'),
const View(template: ''),
_templates.HostMyComponentTemplate
_templates.hostViewFactory_MyComponent
], const [], () => new MyComponent()));
i0.initReflector();
}

View File

@ -19,7 +19,7 @@ void initReflector() {
new _ngRef.ReflectionInfo(const [
const Component(selector: 'soup'),
const View(directives: [Foo], template: 'foo'),
_templates.HostMyComponentTemplate
_templates.hostViewFactory_MyComponent
], const [], () => new MyComponent()));
i0.initReflector();
i1.initReflector();

View File

@ -19,7 +19,7 @@ void initReflector() {
new _ngRef.ReflectionInfo(const [
const Component(selector: 'soup'),
const View(directives: [prefix.Foo], template: 'foo'),
_templates.HostMyComponentTemplate
_templates.hostViewFactory_MyComponent
], const [], () => new MyComponent()));
i0.initReflector();
i1.initReflector();

View File

@ -19,7 +19,7 @@ void initReflector() {
new _ngRef.ReflectionInfo(const [
const Component(selector: '[soup]'),
const View(directives: const [directiveAlias], template: ''),
_templates.HostMyComponentTemplate
_templates.hostViewFactory_MyComponent
], const [], () => new MyComponent()));
i0.initReflector();
i1.initReflector();

View File

@ -19,7 +19,7 @@ void initReflector() {
outputs: ['eventName1', 'eventName2: propName2'],
selector: '[soup]'),
const View(template: ''),
_templates.HostMyComponentTemplate
_templates.hostViewFactory_MyComponent
], const [], () => new MyComponent()))
..registerGetters(
{'eventName1': (o) => o.eventName1, 'eventName2': (o) => o.eventName2});

View File

@ -18,7 +18,7 @@ void initReflector() {
new _ngRef.ReflectionInfo(const [
const Component(componentServices: const [MyContext]),
const View(template: ''),
_templates.HostMyComponentTemplate
_templates.hostViewFactory_MyComponent
], const [
const [MyContext]
], (MyContext c) => new MyComponent(c)));

View File

@ -18,7 +18,7 @@ void initReflector() {
const [
const Component(selector: '[soup]'),
const View(template: ''),
_templates.HostMyComponentTemplate
_templates.hostViewFactory_MyComponent
],
const [],
() => new MyComponent(),

View File

@ -19,7 +19,7 @@ void initReflector() {
queries: const {'queryField': const ContentChild('child')},
selector: '[soup]'),
const View(template: ''),
_templates.HostMyComponentTemplate
_templates.hostViewFactory_MyComponent
], const [], () => new MyComponent()))
..registerSetters({'queryField': (o, v) => o.queryField = v});
i0.initReflector();

View File

@ -18,7 +18,7 @@ void initReflector() {
const [
const Component(selector: '[soup]'),
const View(template: ''),
_templates.HostMyComponentTemplate
_templates.hostViewFactory_MyComponent
],
const [],
() => new MyComponent(),

View File

@ -16,7 +16,7 @@ void initReflector() {
MyComponent,
new _ngRef.ReflectionInfo(const [
const Component(selector: '[soup]', template: 'aa'),
_templates.HostMyComponentTemplate
_templates.hostViewFactory_MyComponent
], const [], () => new MyComponent()));
i0.initReflector();
}

View File

@ -1,28 +0,0 @@
{
"MyComponent": {
"kind": "type",
"value": {
"id": "MyComponent",
"selector": "[soup]",
"compileChildren": true,
"hostProperties": {},
"hostListeners": {},
"hostAttributes": {},
"inputs": [],
"readAttributes": [],
"type": 1,
"exportAs": null,
"callOnDestroy": false,
"callDoCheck": false,
"callOnInit": false,
"callOnChanges": false,
"callAfterContentInit": false,
"callAfterContentChecked": false,
"callAfterViewInit": false,
"callAfterViewChecked": false,
"outputs": [],
"changeDetection": null,
"version": 1
}
}
}

View File

@ -17,7 +17,7 @@ void initReflector() {
new _ngRef.ReflectionInfo(const [
const Component(selector: '[soup]'),
const View(template: ''),
_templates.HostMyComponentTemplate
_templates.hostViewFactory_MyComponent
], const [], () => new MyComponent()));
i0.initReflector();
}

View File

@ -17,7 +17,7 @@ void initReflector() {
new _ngRef.ReflectionInfo(const [
const Component(selector: '[soup]'),
const View(template: 'Salad: {{myNum}} is awesome'),
_templates.HostMyComponentTemplate
_templates.hostViewFactory_MyComponent
], const [], () => new MyComponent()));
i0.initReflector();
}

View File

@ -19,7 +19,7 @@ void initReflector() {
const [
const Component(selector: 'soup'),
const View(template: ''),
_templates.HostMyComponentTemplate
_templates.hostViewFactory_MyComponent
],
const [
const [prefix.MyContext],

View File

@ -26,7 +26,7 @@ RecordingLogger logger;
main() => allTests();
var fooComponentMeta, fooNgMeta, fooAssetId;
var barComponentMeta, barNgMeta, barAssetId;
var barComponentMeta, barPipeMeta, barNgMeta, barAssetId;
var bazComponentMeta, bazNgMeta, bazAssetId;
/// Call after making changes to `fooNgMeta`, `barNgMeta`, or `bazNgMeta` and
@ -58,10 +58,13 @@ void allTests() {
fooNgMeta.types[fooComponentMeta.type.name] = fooComponentMeta;
barComponentMeta = createBar(moduleBase);
barPipeMeta = createBarPipe(moduleBase);
barNgMeta = new NgMeta(ngDeps: new NgDepsModel()
..libraryUri = 'test.bar'
..reflectables.add(new ReflectionInfoModel()..name = barPipeMeta.type.name)
..reflectables.add(new ReflectionInfoModel()..name = barComponentMeta.type.name));
barNgMeta.types[barComponentMeta.type.name] = barComponentMeta;
barNgMeta.types[barPipeMeta.type.name] = barPipeMeta;
bazComponentMeta = createBaz(moduleBase);
bazNgMeta = new NgMeta(ngDeps: new NgDepsModel()
@ -75,11 +78,13 @@ void allTests() {
updateReader();
});
Future<String> process(AssetId assetId, {List<String> platformDirectives}) {
Future<String> process(AssetId assetId,
{List<String> platformDirectives, List<String> platformPipes}) {
logger = new RecordingLogger();
return zone.exec(
() => processTemplates(reader, assetId,
platformDirectives: platformDirectives),
platformDirectives: platformDirectives,
platformPipes: platformPipes),
log: logger);
}
@ -93,7 +98,7 @@ void allTests() {
..isView = true;
fooNgMeta.ngDeps.reflectables.first.annotations.add(viewAnnotation);
fooNgMeta.ngDeps.reflectables.first.directives
.add(new PrefixedDirective()..name = 'NgFor');
.add(new PrefixedType()..name = 'NgFor');
fooNgMeta.ngDeps.imports.add(
new ImportModel()..uri = 'package:angular2/src/directives/ng_for.dart');
@ -121,7 +126,7 @@ void allTests() {
..prefix = '_templates');
expect(ngDeps.reflectables.first.annotations)
.toContain(new AnnotationModel()
..name = '_templates.HostFooComponentTemplate'
..name = '_templates.hostViewFactory_FooComponent'
..isConstObject = true);
expect(outputs.templatesCode)
..toContain('$CONTEXT_ACCESSOR.greeting')
@ -142,7 +147,7 @@ void allTests() {
..prefix = '_templates');
expect(ngDeps.reflectables.first.annotations)
.toContain(new AnnotationModel()
..name = '_templates.HostFooComponentTemplate'
..name = '_templates.hostViewFactory_FooComponent'
..isConstObject = true);
expect(outputs.templatesCode)..toContain('$CONTEXT_ACCESSOR.action()');
});
@ -158,7 +163,7 @@ void allTests() {
..value = 'const [${barComponentMeta.type.name}]');
fooNgMeta.ngDeps.reflectables.first.annotations.add(viewAnnotation);
fooNgMeta.ngDeps.reflectables.first.directives
.add(new PrefixedDirective()..name = barComponentMeta.type.name);
.add(new PrefixedType()..name = barComponentMeta.type.name);
fooNgMeta.ngDeps.imports.add(new ImportModel()..uri = 'bar.dart');
barComponentMeta.template =
new CompileTemplateMetadata(template: 'BarTemplate');
@ -172,7 +177,7 @@ void allTests() {
..prefix = '_templates');
expect(ngDeps.reflectables.first.annotations)
.toContain(new AnnotationModel()
..name = '_templates.HostFooComponentTemplate'
..name = '_templates.hostViewFactory_FooComponent'
..isConstObject = true);
expect(outputs.templatesCode)
@ -188,7 +193,7 @@ void allTests() {
..name = 'View'
..isView = true;
fooNgMeta.ngDeps.reflectables.first.annotations.add(componentAnnotation);
fooNgMeta.ngDeps.reflectables.first.directives.add(new PrefixedDirective()
fooNgMeta.ngDeps.reflectables.first.directives.add(new PrefixedType()
..name = barComponentMeta.type.name
..prefix = 'prefix');
fooNgMeta.ngDeps.imports.add(new ImportModel()
@ -206,7 +211,7 @@ void allTests() {
..prefix = '_templates');
expect(ngDeps.reflectables.first.annotations)
.toContain(new AnnotationModel()
..name = '_templates.HostFooComponentTemplate'
..name = '_templates.hostViewFactory_FooComponent'
..isConstObject = true);
expect(outputs.templatesCode)
@ -222,7 +227,7 @@ void allTests() {
..isView = true;
fooNgMeta.ngDeps.reflectables.first.annotations.add(componentAnnotation);
fooNgMeta.ngDeps.reflectables.first.directives
.add(new PrefixedDirective()..name = 'directiveAlias');
.add(new PrefixedType()..name = 'directiveAlias');
fooNgMeta.ngDeps.imports.add(new ImportModel()..uri = 'bar.dart');
fooNgMeta.aliases['directiveAlias'] = [barComponentMeta.type.name];
@ -238,7 +243,7 @@ void allTests() {
..prefix = '_templates');
expect(ngDeps.reflectables.first.annotations)
.toContain(new AnnotationModel()
..name = '_templates.HostFooComponentTemplate'
..name = '_templates.hostViewFactory_FooComponent'
..isConstObject = true);
expect(outputs.templatesCode)
@ -352,8 +357,8 @@ void allTests() {
});
it('should include platform directives.', () async {
fooComponentMeta.template = new CompileTemplateMetadata(
template: '<bar></bar>');
fooComponentMeta.template =
new CompileTemplateMetadata(template: '<bar></bar>');
final viewAnnotation = new AnnotationModel()
..name = 'View'
..isView = true;
@ -370,9 +375,9 @@ void allTests() {
..toContain(barComponentMeta.template.template);
});
it('should include platform directives when it it a list.', () async {
fooComponentMeta.template = new CompileTemplateMetadata(
template: '<bar></bar>');
it('should include platform directives when it is a list.', () async {
fooComponentMeta.template =
new CompileTemplateMetadata(template: '<bar></bar>');
final viewAnnotation = new AnnotationModel()
..name = 'View'
..isView = true;
@ -417,6 +422,43 @@ void allTests() {
final ngDeps = outputs.ngDeps;
expect(ngDeps).toBeNotNull();
});
it('should parse `View` pipes with a single dependency.', () async {
fooComponentMeta.template =
new CompileTemplateMetadata(template: '{{1 | bar}}');
final viewAnnotation = new AnnotationModel()
..name = 'View'
..isView = true;
viewAnnotation.namedParameters.add(new NamedParameter()
..name = 'pipes'
..value = 'const [${barPipeMeta.type.name}]');
fooNgMeta.ngDeps.reflectables.first.annotations.add(viewAnnotation);
fooNgMeta.ngDeps.reflectables.first.pipes
.add(new PrefixedType()..name = barPipeMeta.type.name);
fooNgMeta.ngDeps.imports.add(new ImportModel()..uri = 'bar.dart');
updateReader();
final outputs = await process(fooAssetId);
expect(outputs.templatesCode)
..toContain("import 'bar.dart'")
..toContain(barPipeMeta.name);
});
it('should include platform pipes.', () async {
fooComponentMeta.template =
new CompileTemplateMetadata(template: '{{1 | bar}}');
barNgMeta.aliases['PLATFORM'] = [barPipeMeta.type.name];
updateReader();
final outputs = await process(fooAssetId,
platformPipes: ['package:a/bar.dart#PLATFORM']);
expect(outputs.templatesCode)
..toContain("import 'bar.dart'")
..toContain(barPipeMeta.name);
});
}
void _formatThenExpectEquals(String actual, String expected) {