Skip to content

Commit d68955a

Browse files
author
Tim Blasi
committed
feat(dart/transform): Add getters, setters, methods to NgDepsModel
Add `List<String>` `getters`, `setters`, and `methods`, which will be used to generate code that registers those closures with the reflection system.
1 parent b318945 commit d68955a

4 files changed

Lines changed: 206 additions & 5 deletions

File tree

modules_dart/transform/lib/src/transform/common/code/ng_deps_code.dart

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,20 +12,23 @@ import 'annotation_code.dart';
1212
import 'import_export_code.dart';
1313
import 'reflection_info_code.dart';
1414
import 'parameter_code.dart';
15+
import 'queries_code.dart';
1516

1617
/// Visitor responsible for parsing source Dart files (that is, not
1718
/// `.ng_deps.dart` files) into [NgDepsModel] objects.
1819
class NgDepsVisitor extends RecursiveAstVisitor<Object> {
1920
final AssetId processedFile;
20-
final ImportVisitor _importVisitor = new ImportVisitor();
21-
final ExportVisitor _exportVisitor = new ExportVisitor();
21+
final _importVisitor = new ImportVisitor();
22+
final _exportVisitor = new ExportVisitor();
2223
final ReflectionInfoVisitor _reflectableVisitor;
24+
final QueriesVisitor _queriesVisitor;
2325

2426
bool _isPart = false;
2527
NgDepsModel _model = null;
2628

2729
NgDepsVisitor(AssetId processedFile, AnnotationMatcher annotationMatcher)
2830
: this.processedFile = processedFile,
31+
_queriesVisitor = new QueriesVisitor(processedFile, annotationMatcher),
2932
_reflectableVisitor =
3033
new ReflectionInfoVisitor(processedFile, annotationMatcher);
3134

@@ -48,6 +51,14 @@ class NgDepsVisitor extends RecursiveAstVisitor<Object> {
4851
var reflectableModel = _reflectableVisitor.visitClassDeclaration(node);
4952
if (reflectableModel != null) {
5053
model.reflectables.add(reflectableModel);
54+
var queryFields = _queriesVisitor.visitClassDeclaration(node);
55+
if (queryFields != null) {
56+
for (var queryField in queryFields) {
57+
if (!model.setters.contains(queryField)) {
58+
model.setters.add(queryField);
59+
}
60+
}
61+
}
5162
}
5263
return null;
5364
}
@@ -155,9 +166,39 @@ abstract class NgDepsWriterMixin
155166
..writeln('void ${SETUP_METHOD_NAME}() {')
156167
..writeln('if (_visited) return; _visited = true;');
157168

158-
if (model.reflectables != null && model.reflectables.isNotEmpty) {
169+
final needsReceiver = (model.reflectables != null &&
170+
model.reflectables.isNotEmpty) ||
171+
(model.getters != null && model.getters.isNotEmpty) ||
172+
(model.setters != null && model.setters.isNotEmpty) ||
173+
(model.methods != null && model.methods.isNotEmpty);
174+
175+
if (needsReceiver) {
159176
buffer.writeln('$REFLECTOR_PREFIX.$REFLECTOR_VAR_NAME');
177+
}
178+
179+
if (model.reflectables != null && model.reflectables.isNotEmpty) {
160180
model.reflectables.forEach(writeRegistration);
181+
}
182+
183+
if (model.getters != null && model.getters.isNotEmpty) {
184+
buffer.writeln('..registerGetters({'
185+
'${model.getters.map((g) => "'$g': (o) => o.$g").join(', ')}'
186+
'})');
187+
}
188+
189+
if (model.setters != null && model.setters.isNotEmpty) {
190+
buffer.writeln('..registerSetters({'
191+
'${model.setters.map((g) => "'$g': (o, v) => o.$g = v").join(', ')}'
192+
'})');
193+
}
194+
195+
if (model.methods != null && model.methods.isNotEmpty) {
196+
buffer.writeln('..registerMethods({'
197+
'${model.methods.map((g) => "'$g': (o, args) => o.$g.apply(args)").join(', ')}'
198+
'})');
199+
}
200+
201+
if (needsReceiver) {
161202
buffer.writeln(';');
162203
}
163204

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
library angular2.transform.common.code.queries_code;
2+
3+
import 'package:analyzer/analyzer.dart';
4+
import 'package:analyzer/src/generated/ast.dart';
5+
import 'package:angular2/src/transform/common/annotation_matcher.dart';
6+
import 'package:angular2/src/transform/common/dumb_eval.dart';
7+
import 'package:barback/barback.dart';
8+
9+
/// Visitor responsbile for processing a [ClassDeclaration] and extracting any
10+
/// query fields it defines.
11+
class QueriesVisitor extends RecursiveAstVisitor<Iterable<String>> {
12+
final _QueriesAnnotationVisitor _annotationVisitor;
13+
final _propertyVisitor = new _QueriesPropertyVisitor();
14+
15+
QueriesVisitor(AssetId assetId, AnnotationMatcher annotationMatcher)
16+
: _annotationVisitor =
17+
new _QueriesAnnotationVisitor(assetId, annotationMatcher);
18+
19+
@override
20+
Iterable<String> visitClassDeclaration(ClassDeclaration node) {
21+
final queryFields = new Set<String>();
22+
if (node.metadata != null) {
23+
for (var annotation in node.metadata) {
24+
var annotationQueryFields =
25+
_annotationVisitor.visitAnnotation(annotation);
26+
if (annotationQueryFields != null) {
27+
queryFields.addAll(annotationQueryFields);
28+
}
29+
}
30+
}
31+
32+
// Record annotations attached to properties.
33+
if (node.members != null) {
34+
for (var member in node.members) {
35+
var queryProp = member.accept(_propertyVisitor);
36+
if (queryProp != null) {
37+
queryFields.add(queryProp);
38+
}
39+
}
40+
}
41+
42+
return queryFields.isNotEmpty ? queryFields : null;
43+
}
44+
}
45+
46+
/// Visitor responsbile for processing properties and getters on a
47+
/// [ClassDeclaration] and extracting any query fields it contains.
48+
class _QueriesPropertyVisitor extends SimpleAstVisitor<String> {
49+
@override
50+
String visitFieldDeclaration(FieldDeclaration node) {
51+
for (var variable in node.fields.variables) {
52+
for (var meta in node.metadata) {
53+
if (_isQueryAnnotation(meta)) {
54+
return '${variable.name}';
55+
}
56+
}
57+
}
58+
return null;
59+
}
60+
61+
@override
62+
String visitMethodDeclaration(MethodDeclaration node) {
63+
if (node.isGetter || node.isSetter) {
64+
for (var meta in node.metadata) {
65+
if (_isQueryAnnotation(meta)) {
66+
return '${node.name}';
67+
}
68+
}
69+
}
70+
return null;
71+
}
72+
73+
bool _isQueryAnnotation(Annotation node) {
74+
// TODO(kegluenq): Use ClassMatcherBase to ensure this is a match.
75+
var id = node.name;
76+
final name = id is PrefixedIdentifier ? '${id.identifier}' : '$id';
77+
switch (name) {
78+
case "ContentChild":
79+
case "ViewChild":
80+
case "ContentChildren":
81+
case "ViewChildren":
82+
return true;
83+
default:
84+
return false;
85+
}
86+
}
87+
}
88+
89+
/// Visitor responsible for processing the [Annotation]s on a [ClassDeclaration]
90+
/// and extracting any query fields it contains.
91+
class _QueriesAnnotationVisitor extends SimpleAstVisitor<Iterable<String>> {
92+
/// The file we are processing.
93+
final AssetId assetId;
94+
95+
/// Responsible for testing whether [Annotation]s are those recognized by
96+
/// Angular 2, for example `@Component`.
97+
final AnnotationMatcher _annotationMatcher;
98+
99+
/// All currently found query fields.
100+
Set<String> _queryFields = null;
101+
102+
_QueriesAnnotationVisitor(this.assetId, this._annotationMatcher);
103+
104+
@override
105+
Iterable<String> visitAnnotation(Annotation node) {
106+
var queryFields = null;
107+
if (_annotationMatcher.isView(node, assetId) ||
108+
_annotationMatcher.isComponent(node, assetId)) {
109+
queryFields = _queryFields = new Set<String>();
110+
if (node.arguments != null && node.arguments.arguments != null) {
111+
node.arguments.arguments.accept(this);
112+
}
113+
_queryFields = null;
114+
}
115+
return queryFields;
116+
}
117+
118+
@override
119+
Iterable<String> visitNamedExpression(NamedExpression node) {
120+
if ('${node.name.label}' == "queries") {
121+
if (node.expression is! MapLiteral) {
122+
throw new FormatException(
123+
'Expected a map value for "queries", but got ${node.expression}',
124+
node.toSource());
125+
}
126+
final queries = node.expression as MapLiteral;
127+
for (var entry in queries.entries) {
128+
var queryField = dumbEval(entry.key);
129+
if (queryField != NOT_A_CONSTANT) {
130+
_queryFields.add(queryField.toString());
131+
}
132+
}
133+
}
134+
return null;
135+
}
136+
}

modules_dart/transform/lib/src/transform/common/model/ng_deps_model.pb.dart

Lines changed: 14 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

modules_dart/transform/lib/src/transform/common/model/ng_deps_model.proto

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,16 @@ message NgDepsModel {
2020
// The basename of the file from which the ng_deps were generated.
2121
// Example: component.dart
2222
optional string source_file = 6;
23+
24+
// The names of getters that should be registered in the Angular 2 reflection
25+
// framework.
26+
repeated string getters = 7;
27+
28+
// The names of setters that should be registered in the Angular 2 reflection
29+
// framework.
30+
repeated string setters = 8;
31+
32+
// The names of methods that should be registered in the Angular 2 reflection
33+
// framework.
34+
repeated string methods = 9;
2335
}

0 commit comments

Comments
 (0)