Skip to content

Commit abc8878

Browse files
author
Tim Blasi
committed
feat(dart/transform): Reuse readDirectiveMetadata in plugin
Share code for parsing `DirectiveMetadata` values between the transformer and the analyzer plugin.
1 parent 75e9d3f commit abc8878

6 files changed

Lines changed: 127 additions & 125 deletions

File tree

modules/angular2/src/reflection/reflection_capabilities.dart

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,7 @@ import 'types.dart';
55
import 'dart:mirrors';
66

77
class ReflectionCapabilities {
8-
ReflectionCapabilities([metadataReader]) {
9-
}
8+
ReflectionCapabilities([metadataReader]) {}
109

1110
Function factory(Type type) {
1211
ClassMirror classMirror = reflectType(type);

modules/angular2/src/transform/common/directive_metadata_reader.dart

Lines changed: 95 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,47 @@
11
library angular2.transform.common.directive_metadata_reader;
22

33
import 'package:analyzer/analyzer.dart';
4+
import 'package:analyzer/src/generated/element.dart';
45
import 'package:angular2/src/render/api.dart';
5-
import 'logging.dart';
6-
import 'parser.dart';
76

8-
/// Reads [DirectiveMetadata] from the `attributes` of `t`.
9-
DirectiveMetadata readDirectiveMetadata(RegisteredType t) {
7+
/// Reads [DirectiveMetadata] from the `node`. `node` is expected to be an
8+
/// instance of [Annotation], [NodeList<Annotation>], ListLiteral, or
9+
/// [InstanceCreationExpression].
10+
DirectiveMetadata readDirectiveMetadata(dynamic node) {
11+
assert(node is Annotation ||
12+
node is NodeList ||
13+
node is InstanceCreationExpression ||
14+
node is ListLiteral);
1015
var visitor = new _DirectiveMetadataVisitor();
11-
t.annotations.accept(visitor);
12-
if (visitor.meta != null) {
13-
visitor.meta.id = '${t.typeName}';
14-
}
16+
node.accept(visitor);
1517
return visitor.meta;
1618
}
1719

18-
num _getDirectiveType(String annotationName) {
20+
num _getDirectiveType(String annotationName, Element element) {
21+
var byNameMatch = -1;
1922
// TODO(kegluneq): Detect subtypes & implementations of `Directive`s.
2023
switch (annotationName) {
2124
case 'Directive':
22-
return DirectiveMetadata.DIRECTIVE_TYPE;
25+
byNameMatch = DirectiveMetadata.DIRECTIVE_TYPE;
26+
break;
2327
case 'Component':
24-
return DirectiveMetadata.COMPONENT_TYPE;
28+
byNameMatch = DirectiveMetadata.COMPONENT_TYPE;
29+
break;
2530
default:
2631
return -1;
2732
}
33+
if (element != null) {
34+
var byResolvedAst = -1;
35+
var libName = element.library.name;
36+
// If we have resolved, ensure the library is correct.
37+
if (libName == 'angular2.src.core.annotations.annotations' ||
38+
libName == 'angular2.src.core.annotations_impl.annotations') {
39+
byResolvedAst = byNameMatch;
40+
}
41+
// TODO(kegluneq): @keertip, can we expose this as a warning?
42+
assert(byNameMatch == byResolvedAst);
43+
}
44+
return byNameMatch;
2845
}
2946

3047
/// Visitor responsible for processing the `annotations` property of a
@@ -33,22 +50,45 @@ class _DirectiveMetadataVisitor extends Object
3350
with RecursiveAstVisitor<Object> {
3451
DirectiveMetadata meta;
3552

53+
void _createEmptyMetadata(num type) {
54+
assert(type >= 0);
55+
meta = new DirectiveMetadata(
56+
type: type,
57+
compileChildren: true,
58+
properties: {},
59+
hostListeners: {},
60+
hostProperties: {},
61+
hostAttributes: {},
62+
readAttributes: []);
63+
}
64+
65+
@override
66+
Object visitAnnotation(Annotation node) {
67+
var directiveType = _getDirectiveType('${node.name}', node.element);
68+
if (directiveType >= 0) {
69+
if (meta != null) {
70+
throw new FormatException('Only one Directive is allowed per class. '
71+
'Found "$node" but already processed "$meta".',
72+
'$node' /* source */);
73+
}
74+
_createEmptyMetadata(directiveType);
75+
super.visitAnnotation(node);
76+
}
77+
// Annotation we do not recognize - no need to visit.
78+
return null;
79+
}
80+
3681
@override
3782
Object visitInstanceCreationExpression(InstanceCreationExpression node) {
38-
var directiveType = _getDirectiveType('${node.constructorName.type.name}');
83+
var directiveType = _getDirectiveType(
84+
'${node.constructorName.type.name}', node.staticElement);
3985
if (directiveType >= 0) {
4086
if (meta != null) {
41-
logger.error('Only one Directive is allowed per class. '
42-
'Found "$node" but already processed "$meta".');
87+
throw new FormatException('Only one Directive is allowed per class. '
88+
'Found "$node" but already processed "$meta".',
89+
'$node' /* source */);
4390
}
44-
meta = new DirectiveMetadata(
45-
type: directiveType,
46-
compileChildren: true,
47-
properties: {},
48-
hostListeners: {},
49-
hostProperties: {},
50-
hostAttributes: {},
51-
readAttributes: []);
91+
_createEmptyMetadata(directiveType);
5292
super.visitInstanceCreationExpression(node);
5393
}
5494
// Annotation we do not recognize - no need to visit.
@@ -59,10 +99,9 @@ class _DirectiveMetadataVisitor extends Object
5999
Object visitNamedExpression(NamedExpression node) {
60100
// TODO(kegluneq): Remove this limitation.
61101
if (node.name is! Label || node.name.label is! SimpleIdentifier) {
62-
logger.error(
63-
'Angular 2 currently only supports simple identifiers in directives.'
64-
' Source: ${node}');
65-
return null;
102+
throw new FormatException(
103+
'Angular 2 currently only supports simple identifiers in directives.',
104+
'$node' /* source */);
66105
}
67106
var keyString = '${node.name.label}';
68107
// TODO(kegluneq): Populate the other values in [DirectiveMetadata] once
@@ -93,9 +132,9 @@ class _DirectiveMetadataVisitor extends Object
93132
String _expressionToString(Expression node, String nodeDescription) {
94133
// TODO(kegluneq): Accept more options.
95134
if (node is! SimpleStringLiteral) {
96-
logger.error('Angular 2 currently only supports string literals '
97-
'in $nodeDescription. Source: ${node}');
98-
return null;
135+
throw new FormatException(
136+
'Angular 2 currently only supports string literals '
137+
'in $nodeDescription.', '$node' /* source */);
99138
}
100139
return stringLiteralToString(node);
101140
}
@@ -104,23 +143,30 @@ class _DirectiveMetadataVisitor extends Object
104143
meta.selector = _expressionToString(selectorValue, 'Directive#selector');
105144
}
106145

146+
void _checkMeta() {
147+
if (meta == null) {
148+
throw new ArgumentError(
149+
'Incorrect value passed to readDirectiveMetadata. '
150+
'Expected types are Annotation and InstanceCreationExpression');
151+
}
152+
}
153+
107154
void _populateCompileChildren(Expression compileChildrenValue) {
155+
_checkMeta();
108156
if (compileChildrenValue is! BooleanLiteral) {
109-
logger.error(
157+
throw new FormatException(
110158
'Angular 2 currently only supports boolean literal values for '
111-
'Directive#compileChildren.'
112-
' Source: ${compileChildrenValue}');
113-
return;
159+
'Directive#compileChildren.', '$compileChildrenValue' /* source */);
114160
}
115161
meta.compileChildren = (compileChildrenValue as BooleanLiteral).value;
116162
}
117163

118164
void _populateProperties(Expression propertiesValue) {
165+
_checkMeta();
119166
if (propertiesValue is! MapLiteral) {
120-
logger.error('Angular 2 currently only supports map literal values for '
121-
'Directive#properties.'
122-
' Source: ${propertiesValue}');
123-
return;
167+
throw new FormatException(
168+
'Angular 2 currently only supports map literal values for '
169+
'Directive#properties.', '$propertiesValue' /* source */);
124170
}
125171
for (MapLiteralEntry entry in (propertiesValue as MapLiteral).entries) {
126172
var sKey = _expressionToString(entry.key, 'Directive#properties keys');
@@ -130,11 +176,11 @@ class _DirectiveMetadataVisitor extends Object
130176
}
131177

132178
void _populateHostListeners(Expression hostListenersValue) {
179+
_checkMeta();
133180
if (hostListenersValue is! MapLiteral) {
134-
logger.error('Angular 2 currently only supports map literal values for '
135-
'Directive#hostListeners.'
136-
' Source: ${hostListenersValue}');
137-
return;
181+
throw new FormatException(
182+
'Angular 2 currently only supports map literal values for '
183+
'Directive#hostListeners.', '$hostListenersValue' /* source */);
138184
}
139185
for (MapLiteralEntry entry in (hostListenersValue as MapLiteral).entries) {
140186
var sKey = _expressionToString(entry.key, 'Directive#hostListeners keys');
@@ -145,11 +191,11 @@ class _DirectiveMetadataVisitor extends Object
145191
}
146192

147193
void _populateHostProperties(Expression hostPropertyValue) {
194+
_checkMeta();
148195
if (hostPropertyValue is! MapLiteral) {
149-
logger.error('Angular 2 currently only supports map literal values for '
150-
'Directive#hostProperties.'
151-
' Source: ${hostPropertyValue}');
152-
return;
196+
throw new FormatException(
197+
'Angular 2 currently only supports map literal values for '
198+
'Directive#hostProperties.', '$hostPropertyValue' /* source */);
153199
}
154200
for (MapLiteralEntry entry in (hostPropertyValue as MapLiteral).entries) {
155201
var sKey =
@@ -161,11 +207,11 @@ class _DirectiveMetadataVisitor extends Object
161207
}
162208

163209
void _populateHostAttributes(Expression hostAttributeValue) {
210+
_checkMeta();
164211
if (hostAttributeValue is! MapLiteral) {
165-
logger.error('Angular 2 currently only supports map literal values for '
166-
'Directive#hostAttributes.'
167-
' Source: ${hostAttributeValue}');
168-
return;
212+
throw new FormatException(
213+
'Angular 2 currently only supports map literal values for '
214+
'Directive#hostAttributes.', '$hostAttributeValue' /* source */);
169215
}
170216
for (MapLiteralEntry entry in (hostAttributeValue as MapLiteral).entries) {
171217
var sKey =

modules/angular2/src/transform/common/registered_type.dart

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
library angular2.transform.common.registered_type;
22

33
import 'package:analyzer/analyzer.dart';
4+
import 'package:angular2/src/render/api.dart';
5+
import 'package:angular2/src/transform/common/directive_metadata_reader.dart';
6+
import 'package:angular2/src/transform/common/logging.dart';
47
import 'package:angular2/src/transform/common/names.dart';
58

69
/// A call to `Reflector#registerType` generated by `DirectiveProcessor`.
@@ -16,6 +19,8 @@ class RegisteredType {
1619
/// The annotations registered.
1720
final Expression annotations;
1821

22+
DirectiveMetadata _directiveMetadata = null;
23+
1924
RegisteredType._(this.typeName, this.registerMethod, this.factoryFn,
2025
this.parameters, this.annotations);
2126

@@ -27,6 +32,20 @@ class RegisteredType {
2732
return new RegisteredType._(visitor.typeName, registerMethod,
2833
visitor.factoryFn, visitor.parameters, visitor.annotations);
2934
}
35+
36+
DirectiveMetadata get directiveMetadata {
37+
if (_directiveMetadata == null) {
38+
try {
39+
_directiveMetadata = readDirectiveMetadata(annotations);
40+
if (_directiveMetadata != null) {
41+
_directiveMetadata.id = '$typeName';
42+
}
43+
} on FormatException catch (ex) {
44+
logger.error(ex.message);
45+
}
46+
}
47+
return _directiveMetadata;
48+
}
3049
}
3150

3251
class _ParseRegisterTypeVisitor extends Object

modules/angular2/src/transform/directive_metadata_extractor/extractor.dart

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import 'dart:async';
55
import 'package:analyzer/analyzer.dart';
66
import 'package:angular2/src/render/api.dart';
77
import 'package:angular2/src/transform/common/asset_reader.dart';
8-
import 'package:angular2/src/transform/common/directive_metadata_reader.dart';
98
import 'package:angular2/src/transform/common/logging.dart';
109
import 'package:angular2/src/transform/common/names.dart';
1110
import 'package:angular2/src/transform/common/parser.dart';
@@ -58,9 +57,8 @@ Map<String, DirectiveMetadata> _metadataMapFromNgDeps(NgDeps ngDeps) {
5857
if (ngDeps == null || ngDeps.registeredTypes.isEmpty) return null;
5958
var retVal = <String, DirectiveMetadata>{};
6059
ngDeps.registeredTypes.forEach((rType) {
61-
var meta = readDirectiveMetadata(rType);
62-
if (meta != null) {
63-
retVal['${rType.typeName}'] = meta;
60+
if (rType.directiveMetadata != null) {
61+
retVal['${rType.typeName}'] = rType.directiveMetadata;
6462
}
6563
});
6664
return retVal;

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

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ void allTests() {
2424

2525
Future<DirectiveMetadata> readMetadata(inputPath) async {
2626
var ngDeps = await parser.parse(new AssetId('a', inputPath));
27-
return readDirectiveMetadata(ngDeps.registeredTypes.first);
27+
return ngDeps.registeredTypes.first.directiveMetadata;
2828
}
2929

3030
describe('readMetadata', () {
@@ -44,17 +44,17 @@ void allTests() {
4444
// Unset value defaults to `true`.
4545
it.moveNext();
4646
expect('${it.current.typeName}').toEqual('UnsetComp');
47-
var unsetComp = readDirectiveMetadata(it.current);
47+
var unsetComp = it.current.directiveMetadata;
4848
expect(unsetComp.compileChildren).toBeTrue();
4949

5050
it.moveNext();
5151
expect('${it.current.typeName}').toEqual('FalseComp');
52-
var falseComp = readDirectiveMetadata(it.current);
52+
var falseComp = it.current.directiveMetadata;
5353
expect(falseComp.compileChildren).toBeFalse();
5454

5555
it.moveNext();
5656
expect('${it.current.typeName}').toEqual('TrueComp');
57-
var trueComp = readDirectiveMetadata(it.current);
57+
var trueComp = it.current.directiveMetadata;
5858
expect(trueComp.compileChildren).toBeTrue();
5959
});
6060

@@ -85,8 +85,8 @@ void allTests() {
8585
var ngDeps = await parser.parse(new AssetId('a',
8686
'directive_metadata_extractor/'
8787
'directive_metadata_files/too_many_directives.ng_deps.dart'));
88-
expect(() => readDirectiveMetadata(ngDeps.registeredTypes.first))
89-
.toThrowWith(anInstanceOf: PrintLoggerError);
88+
expect(() => ngDeps.registeredTypes.first.directiveMetadata).toThrowWith(
89+
anInstanceOf: PrintLoggerError);
9090
});
9191
});
9292

0 commit comments

Comments
 (0)