Skip to content

Commit cf7bef5

Browse files
author
Tim Blasi
committed
feat(dart/transform): Add the DirectiveMetadataReader
Add a class that parses and reads Directive metadata to prepare for running the Render compiler in the Dart transformer.
1 parent c65fd31 commit cf7bef5

File tree

2 files changed

+185
-0
lines changed

2 files changed

+185
-0
lines changed
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
library angular2.transform.template_compiler.directive_metadata_reader;
2+
3+
import 'package:analyzer/analyzer.dart';
4+
import 'package:angular2/src/render/api.dart';
5+
import 'package:angular2/src/transform/common/asset_reader.dart';
6+
import 'package:angular2/src/transform/common/logging.dart';
7+
import 'package:angular2/src/transform/common/names.dart';
8+
import 'package:angular2/src/transform/common/parser.dart';
9+
import 'package:angular2/src/transform/common/property_utils.dart' as prop;
10+
11+
/// Reads [DirectiveMetadata] from the `attributes` of `t`.
12+
List<DirectiveMetadata> readDirectiveMetadata(RegisteredType t) {
13+
var visitor = new _DirectiveMetadataVisitor();
14+
t.annotations.accept(visitor);
15+
return visitor.directiveMetadata;
16+
}
17+
18+
num _getDirectiveType(String annotationName) {
19+
// TODO(kegluneq): Detect subtypes & implementations of `Directive`s.
20+
switch (annotationName) {
21+
case 'Decorator':
22+
return DirectiveMetadata.DECORATOR_TYPE;
23+
case 'Component':
24+
return DirectiveMetadata.COMPONENT_TYPE;
25+
case 'Viewport':
26+
return DirectiveMetadata.VIEWPORT_TYPE;
27+
default:
28+
return -1;
29+
}
30+
}
31+
32+
/// Visitor responsible for processing the `annotations` property of a
33+
/// [RegisterType] object and pulling out [DirectiveMetadata].
34+
class _DirectiveMetadataVisitor extends Object
35+
with RecursiveAstVisitor<Object> {
36+
DirectiveMetadata current;
37+
final List<DirectiveMetadata> directiveMetadata = [];
38+
39+
@override
40+
Object visitInstanceCreationExpression(InstanceCreationExpression node) {
41+
var directiveType = _getDirectiveType('${node.constructorName.type.name}');
42+
if (directiveType >= 0) {
43+
current = new DirectiveMetadata(
44+
type: directiveType,
45+
compileChildren: false,
46+
properties: {},
47+
hostListeners: {},
48+
setters: [],
49+
readAttributes: []);
50+
directiveMetadata.add(current);
51+
super.visitInstanceCreationExpression(node);
52+
}
53+
// Annotation we do not recognize - no need to visit.
54+
return null;
55+
}
56+
57+
@override
58+
Object visitNamedExpression(NamedExpression node) {
59+
// TODO(kegluneq): Remove this limitation.
60+
if (node.name is! Label || node.name.label is! SimpleIdentifier) {
61+
logger.error(
62+
'Angular 2 currently only supports simple identifiers in directives.'
63+
' Source: ${node}');
64+
return null;
65+
}
66+
var keyString = '${node.name.label}';
67+
// TODO(kegluneq): Populate the other values in [DirectiveMetadata] once
68+
// they are specified as `hostAttributes` and `hostSetters`.
69+
// See [https://github.com/angular/angular/issues/1244]
70+
switch (keyString) {
71+
case 'selector':
72+
_populateSelector(node.expression);
73+
break;
74+
case 'compileChildren':
75+
_populateCompileChildren(node.expression);
76+
break;
77+
case 'properties':
78+
_populateProperties(node.expression);
79+
break;
80+
case 'hostListeners':
81+
_populateHostListeners(node.expression);
82+
}
83+
return null;
84+
}
85+
86+
String _expressionToString(Expression node, String nodeDescription) {
87+
// TODO(kegluneq): Accept more options.
88+
if (node is! SimpleStringLiteral) {
89+
logger.error('Angular 2 currently only supports string literals '
90+
'in $nodeDescription. Source: ${node}');
91+
return null;
92+
}
93+
return stringLiteralToString(node);
94+
}
95+
96+
void _populateSelector(Expression selectorValue) {
97+
current.selector = _expressionToString(selectorValue, 'Directive#selector');
98+
}
99+
100+
void _populateCompileChildren(Expression compileChildrenValue) {
101+
if (compileChildrenValue is! BooleanLiteral) {
102+
logger.error(
103+
'Angular 2 currently only supports boolean literal values for '
104+
'Decorator#compileChildren.'
105+
' Source: ${node}');
106+
return;
107+
}
108+
current.compileChildren = compileChildrenValue.value;
109+
}
110+
111+
void _populateProperties(Expression propertiesValue) {
112+
if (propertiesValue is! MapLiteral) {
113+
logger.error('Angular 2 currently only supports map literal values for '
114+
'Directive#properties.'
115+
' Source: ${node}');
116+
return;
117+
}
118+
for (MapLiteralEntry entry in propertiesValue.entries) {
119+
var sKey = _expressionToString(entry.key, 'Directive#properties keys');
120+
var sVal = _expressionToString(entry.value, 'Direcive#properties values');
121+
current.properties[sKey] = sVal;
122+
}
123+
}
124+
125+
void _populateHostListeners(Expression hostListenersValue) {
126+
if (hostListenersValue is! MapLiteral) {
127+
logger.error('Angular 2 currently only supports map literal values for '
128+
'Directive#hostListeners.'
129+
' Source: ${node}');
130+
return;
131+
}
132+
for (MapLiteralEntry entry in hostListenersValue.entries) {
133+
var sKey = _expressionToString(entry.key, 'Directive#hostListeners keys');
134+
var sVal =
135+
_expressionToString(entry.value, 'Directive#hostListeners values');
136+
current.hostListeners[sKey] = sVal;
137+
;
138+
}
139+
}
140+
}

modules/angular2/test/transform/template_compiler/all_tests.dart

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ library angular2.test.transform.template_compiler.all_tests;
33
import 'package:barback/barback.dart';
44
import 'package:angular2/src/dom/html_adapter.dart';
55
import 'package:angular2/src/transform/common/asset_reader.dart';
6+
import 'package:angular2/src/transform/common/parser.dart';
7+
import 'package:angular2/src/transform/template_compiler/directive_metadata_reader.dart';
68
import 'package:angular2/src/transform/template_compiler/generator.dart';
79
import 'package:dart_style/dart_style.dart';
810
import 'package:guinness/guinness.dart';
@@ -14,6 +16,7 @@ var formatter = new DartFormatter();
1416
void allTests() {
1517
Html5LibDomAdapter.makeCurrent();
1618
AssetReader reader = new TestAssetReader();
19+
var parser = new Parser(reader);
1720

1821
it('should parse simple expressions in inline templates.', () async {
1922
var inputPath =
@@ -47,6 +50,48 @@ void allTests() {
4750
var output = await processTemplates(reader, new AssetId('a', inputPath));
4851
_formatThenExpectEquals(output, expected);
4952
});
53+
54+
describe('DirectiveMetadataReader', () {
55+
it('should parse simple expressions in inline templates', () async {
56+
var inputPath =
57+
'template_compiler/inline_expression_files/hello.ng_deps.dart';
58+
var ngDeps = await parser.parse(new AssetId('a', inputPath));
59+
60+
var metadata = readDirectiveMetadata(ngDeps.registeredTypes.first);
61+
expect(metadata.length).toEqual(2);
62+
expect(metadata[0].selector).toEqual('hello-app');
63+
});
64+
65+
it('should parse simple methods in inline templates', () async {
66+
var inputPath =
67+
'template_compiler/inline_method_files/hello.ng_deps.dart';
68+
var ngDeps = await parser.parse(new AssetId('a', inputPath));
69+
70+
var metadata = readDirectiveMetadata(ngDeps.registeredTypes.first);
71+
expect(metadata.length).toEqual(2);
72+
expect(metadata[0].selector).toEqual('hello-app');
73+
});
74+
75+
it('should parse simple expressions in linked templates.', () async {
76+
var inputPath =
77+
'template_compiler/url_expression_files/hello.ng_deps.dart';
78+
var ngDeps = await parser.parse(new AssetId('a', inputPath));
79+
80+
var metadata = readDirectiveMetadata(ngDeps.registeredTypes.first);
81+
expect(metadata.length).toEqual(2);
82+
expect(metadata[0].selector).toEqual('hello-app');
83+
});
84+
85+
it('should parse simple methods in linked templates.', () async {
86+
var inputPath =
87+
'template_compiler/url_method_files/hello.ng_deps.dart';
88+
var ngDeps = await parser.parse(new AssetId('a', inputPath));
89+
90+
var metadata = readDirectiveMetadata(ngDeps.registeredTypes.first);
91+
expect(metadata.length).toEqual(2);
92+
expect(metadata[0].selector).toEqual('hello-app');
93+
});
94+
});
5095
}
5196

5297
void _formatThenExpectEquals(String actual, String expected) {

0 commit comments

Comments
 (0)