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.
This commit is contained in:
parent
b318945680
commit
d68955ac6d
|
@ -12,20 +12,23 @@ import 'annotation_code.dart';
|
|||
import 'import_export_code.dart';
|
||||
import 'reflection_info_code.dart';
|
||||
import 'parameter_code.dart';
|
||||
import 'queries_code.dart';
|
||||
|
||||
/// Visitor responsible for parsing source Dart files (that is, not
|
||||
/// `.ng_deps.dart` files) into [NgDepsModel] objects.
|
||||
class NgDepsVisitor extends RecursiveAstVisitor<Object> {
|
||||
final AssetId processedFile;
|
||||
final ImportVisitor _importVisitor = new ImportVisitor();
|
||||
final ExportVisitor _exportVisitor = new ExportVisitor();
|
||||
final _importVisitor = new ImportVisitor();
|
||||
final _exportVisitor = new ExportVisitor();
|
||||
final ReflectionInfoVisitor _reflectableVisitor;
|
||||
final QueriesVisitor _queriesVisitor;
|
||||
|
||||
bool _isPart = false;
|
||||
NgDepsModel _model = null;
|
||||
|
||||
NgDepsVisitor(AssetId processedFile, AnnotationMatcher annotationMatcher)
|
||||
: this.processedFile = processedFile,
|
||||
_queriesVisitor = new QueriesVisitor(processedFile, annotationMatcher),
|
||||
_reflectableVisitor =
|
||||
new ReflectionInfoVisitor(processedFile, annotationMatcher);
|
||||
|
||||
|
@ -48,6 +51,14 @@ class NgDepsVisitor extends RecursiveAstVisitor<Object> {
|
|||
var reflectableModel = _reflectableVisitor.visitClassDeclaration(node);
|
||||
if (reflectableModel != null) {
|
||||
model.reflectables.add(reflectableModel);
|
||||
var queryFields = _queriesVisitor.visitClassDeclaration(node);
|
||||
if (queryFields != null) {
|
||||
for (var queryField in queryFields) {
|
||||
if (!model.setters.contains(queryField)) {
|
||||
model.setters.add(queryField);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
@ -155,9 +166,39 @@ abstract class NgDepsWriterMixin
|
|||
..writeln('void ${SETUP_METHOD_NAME}() {')
|
||||
..writeln('if (_visited) return; _visited = true;');
|
||||
|
||||
if (model.reflectables != null && model.reflectables.isNotEmpty) {
|
||||
final needsReceiver = (model.reflectables != null &&
|
||||
model.reflectables.isNotEmpty) ||
|
||||
(model.getters != null && model.getters.isNotEmpty) ||
|
||||
(model.setters != null && model.setters.isNotEmpty) ||
|
||||
(model.methods != null && model.methods.isNotEmpty);
|
||||
|
||||
if (needsReceiver) {
|
||||
buffer.writeln('$REFLECTOR_PREFIX.$REFLECTOR_VAR_NAME');
|
||||
}
|
||||
|
||||
if (model.reflectables != null && model.reflectables.isNotEmpty) {
|
||||
model.reflectables.forEach(writeRegistration);
|
||||
}
|
||||
|
||||
if (model.getters != null && model.getters.isNotEmpty) {
|
||||
buffer.writeln('..registerGetters({'
|
||||
'${model.getters.map((g) => "'$g': (o) => o.$g").join(', ')}'
|
||||
'})');
|
||||
}
|
||||
|
||||
if (model.setters != null && model.setters.isNotEmpty) {
|
||||
buffer.writeln('..registerSetters({'
|
||||
'${model.setters.map((g) => "'$g': (o, v) => o.$g = v").join(', ')}'
|
||||
'})');
|
||||
}
|
||||
|
||||
if (model.methods != null && model.methods.isNotEmpty) {
|
||||
buffer.writeln('..registerMethods({'
|
||||
'${model.methods.map((g) => "'$g': (o, args) => o.$g.apply(args)").join(', ')}'
|
||||
'})');
|
||||
}
|
||||
|
||||
if (needsReceiver) {
|
||||
buffer.writeln(';');
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,136 @@
|
|||
library angular2.transform.common.code.queries_code;
|
||||
|
||||
import 'package:analyzer/analyzer.dart';
|
||||
import 'package:analyzer/src/generated/ast.dart';
|
||||
import 'package:angular2/src/transform/common/annotation_matcher.dart';
|
||||
import 'package:angular2/src/transform/common/dumb_eval.dart';
|
||||
import 'package:barback/barback.dart';
|
||||
|
||||
/// Visitor responsbile for processing a [ClassDeclaration] and extracting any
|
||||
/// query fields it defines.
|
||||
class QueriesVisitor extends RecursiveAstVisitor<Iterable<String>> {
|
||||
final _QueriesAnnotationVisitor _annotationVisitor;
|
||||
final _propertyVisitor = new _QueriesPropertyVisitor();
|
||||
|
||||
QueriesVisitor(AssetId assetId, AnnotationMatcher annotationMatcher)
|
||||
: _annotationVisitor =
|
||||
new _QueriesAnnotationVisitor(assetId, annotationMatcher);
|
||||
|
||||
@override
|
||||
Iterable<String> visitClassDeclaration(ClassDeclaration node) {
|
||||
final queryFields = new Set<String>();
|
||||
if (node.metadata != null) {
|
||||
for (var annotation in node.metadata) {
|
||||
var annotationQueryFields =
|
||||
_annotationVisitor.visitAnnotation(annotation);
|
||||
if (annotationQueryFields != null) {
|
||||
queryFields.addAll(annotationQueryFields);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Record annotations attached to properties.
|
||||
if (node.members != null) {
|
||||
for (var member in node.members) {
|
||||
var queryProp = member.accept(_propertyVisitor);
|
||||
if (queryProp != null) {
|
||||
queryFields.add(queryProp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return queryFields.isNotEmpty ? queryFields : null;
|
||||
}
|
||||
}
|
||||
|
||||
/// Visitor responsbile for processing properties and getters on a
|
||||
/// [ClassDeclaration] and extracting any query fields it contains.
|
||||
class _QueriesPropertyVisitor extends SimpleAstVisitor<String> {
|
||||
@override
|
||||
String visitFieldDeclaration(FieldDeclaration node) {
|
||||
for (var variable in node.fields.variables) {
|
||||
for (var meta in node.metadata) {
|
||||
if (_isQueryAnnotation(meta)) {
|
||||
return '${variable.name}';
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
String visitMethodDeclaration(MethodDeclaration node) {
|
||||
if (node.isGetter || node.isSetter) {
|
||||
for (var meta in node.metadata) {
|
||||
if (_isQueryAnnotation(meta)) {
|
||||
return '${node.name}';
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
bool _isQueryAnnotation(Annotation node) {
|
||||
// TODO(kegluenq): Use ClassMatcherBase to ensure this is a match.
|
||||
var id = node.name;
|
||||
final name = id is PrefixedIdentifier ? '${id.identifier}' : '$id';
|
||||
switch (name) {
|
||||
case "ContentChild":
|
||||
case "ViewChild":
|
||||
case "ContentChildren":
|
||||
case "ViewChildren":
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Visitor responsible for processing the [Annotation]s on a [ClassDeclaration]
|
||||
/// and extracting any query fields it contains.
|
||||
class _QueriesAnnotationVisitor extends SimpleAstVisitor<Iterable<String>> {
|
||||
/// The file we are processing.
|
||||
final AssetId assetId;
|
||||
|
||||
/// Responsible for testing whether [Annotation]s are those recognized by
|
||||
/// Angular 2, for example `@Component`.
|
||||
final AnnotationMatcher _annotationMatcher;
|
||||
|
||||
/// All currently found query fields.
|
||||
Set<String> _queryFields = null;
|
||||
|
||||
_QueriesAnnotationVisitor(this.assetId, this._annotationMatcher);
|
||||
|
||||
@override
|
||||
Iterable<String> visitAnnotation(Annotation node) {
|
||||
var queryFields = null;
|
||||
if (_annotationMatcher.isView(node, assetId) ||
|
||||
_annotationMatcher.isComponent(node, assetId)) {
|
||||
queryFields = _queryFields = new Set<String>();
|
||||
if (node.arguments != null && node.arguments.arguments != null) {
|
||||
node.arguments.arguments.accept(this);
|
||||
}
|
||||
_queryFields = null;
|
||||
}
|
||||
return queryFields;
|
||||
}
|
||||
|
||||
@override
|
||||
Iterable<String> visitNamedExpression(NamedExpression node) {
|
||||
if ('${node.name.label}' == "queries") {
|
||||
if (node.expression is! MapLiteral) {
|
||||
throw new FormatException(
|
||||
'Expected a map value for "queries", but got ${node.expression}',
|
||||
node.toSource());
|
||||
}
|
||||
final queries = node.expression as MapLiteral;
|
||||
for (var entry in queries.entries) {
|
||||
var queryField = dumbEval(entry.key);
|
||||
if (queryField != NOT_A_CONSTANT) {
|
||||
_queryFields.add(queryField.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -17,7 +17,10 @@ class NgDepsModel extends GeneratedMessage {
|
|||
ExportModel.create)
|
||||
..pp(5, 'reflectables', PbFieldType.PM, ReflectionInfoModel.$checkItem,
|
||||
ReflectionInfoModel.create)
|
||||
..a(6, 'sourceFile', PbFieldType.OS);
|
||||
..a(6, 'sourceFile', PbFieldType.OS)
|
||||
..p(7, 'getters', PbFieldType.PS)
|
||||
..p(8, 'setters', PbFieldType.PS)
|
||||
..p(9, 'methods', PbFieldType.PS);
|
||||
|
||||
NgDepsModel() : super();
|
||||
NgDepsModel.fromBuffer(List<int> i,
|
||||
|
@ -63,6 +66,12 @@ class NgDepsModel extends GeneratedMessage {
|
|||
|
||||
bool hasSourceFile() => $_has(5, 6);
|
||||
void clearSourceFile() => clearField(6);
|
||||
|
||||
List<String> get getters => $_get(6, 7, null);
|
||||
|
||||
List<String> get setters => $_get(7, 8, null);
|
||||
|
||||
List<String> get methods => $_get(8, 9, null);
|
||||
}
|
||||
|
||||
class _ReadonlyNgDepsModel extends NgDepsModel with ReadonlyMessageMixin {}
|
||||
|
@ -94,12 +103,15 @@ const NgDepsModel$json = const {
|
|||
'6': '.angular2.src.transform.common.model.proto.ReflectionInfoModel'
|
||||
},
|
||||
const {'1': 'source_file', '3': 6, '4': 1, '5': 9},
|
||||
const {'1': 'getters', '3': 7, '4': 3, '5': 9},
|
||||
const {'1': 'setters', '3': 8, '4': 3, '5': 9},
|
||||
const {'1': 'methods', '3': 9, '4': 3, '5': 9},
|
||||
],
|
||||
};
|
||||
|
||||
/**
|
||||
* Generated with:
|
||||
* ng_deps_model.proto (83fe43a087fdd0a7ebee360cd6b669570df4d216)
|
||||
* ng_deps_model.proto (ab93a7f3c411be8a6dafe914f8aae56027e1bac6)
|
||||
* libprotoc 2.6.1
|
||||
* dart-protoc-plugin (af5fc2bf1de367a434c3b1847ab260510878ffc0)
|
||||
*/
|
||||
|
|
|
@ -20,4 +20,16 @@ message NgDepsModel {
|
|||
// The basename of the file from which the ng_deps were generated.
|
||||
// Example: component.dart
|
||||
optional string source_file = 6;
|
||||
|
||||
// The names of getters that should be registered in the Angular 2 reflection
|
||||
// framework.
|
||||
repeated string getters = 7;
|
||||
|
||||
// The names of setters that should be registered in the Angular 2 reflection
|
||||
// framework.
|
||||
repeated string setters = 8;
|
||||
|
||||
// The names of methods that should be registered in the Angular 2 reflection
|
||||
// framework.
|
||||
repeated string methods = 9;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue