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:
@ -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();
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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')
|
||||
];
|
||||
|
@ -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)
|
||||
*/
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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)) {
|
||||
|
@ -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);
|
||||
|
@ -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),
|
||||
|
@ -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');
|
||||
}
|
||||
}
|
@ -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);
|
||||
|
@ -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() {
|
||||
|
@ -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 [];
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -4,6 +4,7 @@ final ngFor = {
|
||||
"NgFor": {
|
||||
"kind": "type",
|
||||
"value": {
|
||||
"class": "Directive",
|
||||
"isComponent": false,
|
||||
"dynamicLoadable": true,
|
||||
"selector": "[ngFor][ngForOf]",
|
||||
|
@ -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',
|
||||
|
@ -3,6 +3,7 @@
|
||||
{
|
||||
"kind": "type",
|
||||
"value": {
|
||||
"class": "Directive",
|
||||
"isComponent": true,
|
||||
"dynamicLoadable": true,
|
||||
"selector":"hello-app",
|
||||
@ -29,4 +30,4 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
{
|
||||
"kind": "type",
|
||||
"value": {
|
||||
"class": "Directive",
|
||||
"isComponent": true,
|
||||
"dynamicLoadable": true,
|
||||
"selector":"hello-app",
|
||||
@ -29,4 +30,4 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -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 {}
|
@ -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(
|
||||
|
@ -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 {}
|
||||
|
@ -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 {}
|
||||
|
@ -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 {}
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
@ -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});
|
||||
|
@ -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)));
|
||||
|
@ -18,7 +18,7 @@ void initReflector() {
|
||||
const [
|
||||
const Component(selector: '[soup]'),
|
||||
const View(template: ''),
|
||||
_templates.HostMyComponentTemplate
|
||||
_templates.hostViewFactory_MyComponent
|
||||
],
|
||||
const [],
|
||||
() => new MyComponent(),
|
||||
|
@ -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();
|
||||
|
@ -18,7 +18,7 @@ void initReflector() {
|
||||
const [
|
||||
const Component(selector: '[soup]'),
|
||||
const View(template: ''),
|
||||
_templates.HostMyComponentTemplate
|
||||
_templates.hostViewFactory_MyComponent
|
||||
],
|
||||
const [],
|
||||
() => new MyComponent(),
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ void initReflector() {
|
||||
const [
|
||||
const Component(selector: 'soup'),
|
||||
const View(template: ''),
|
||||
_templates.HostMyComponentTemplate
|
||||
_templates.hostViewFactory_MyComponent
|
||||
],
|
||||
const [
|
||||
const [prefix.MyContext],
|
||||
|
@ -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) {
|
||||
|
Reference in New Issue
Block a user