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 'import_export_code.dart';
|
||||||
import 'reflection_info_code.dart';
|
import 'reflection_info_code.dart';
|
||||||
import 'parameter_code.dart';
|
import 'parameter_code.dart';
|
||||||
|
import 'queries_code.dart';
|
||||||
|
|
||||||
/// Visitor responsible for parsing source Dart files (that is, not
|
/// Visitor responsible for parsing source Dart files (that is, not
|
||||||
/// `.ng_deps.dart` files) into [NgDepsModel] objects.
|
/// `.ng_deps.dart` files) into [NgDepsModel] objects.
|
||||||
class NgDepsVisitor extends RecursiveAstVisitor<Object> {
|
class NgDepsVisitor extends RecursiveAstVisitor<Object> {
|
||||||
final AssetId processedFile;
|
final AssetId processedFile;
|
||||||
final ImportVisitor _importVisitor = new ImportVisitor();
|
final _importVisitor = new ImportVisitor();
|
||||||
final ExportVisitor _exportVisitor = new ExportVisitor();
|
final _exportVisitor = new ExportVisitor();
|
||||||
final ReflectionInfoVisitor _reflectableVisitor;
|
final ReflectionInfoVisitor _reflectableVisitor;
|
||||||
|
final QueriesVisitor _queriesVisitor;
|
||||||
|
|
||||||
bool _isPart = false;
|
bool _isPart = false;
|
||||||
NgDepsModel _model = null;
|
NgDepsModel _model = null;
|
||||||
|
|
||||||
NgDepsVisitor(AssetId processedFile, AnnotationMatcher annotationMatcher)
|
NgDepsVisitor(AssetId processedFile, AnnotationMatcher annotationMatcher)
|
||||||
: this.processedFile = processedFile,
|
: this.processedFile = processedFile,
|
||||||
|
_queriesVisitor = new QueriesVisitor(processedFile, annotationMatcher),
|
||||||
_reflectableVisitor =
|
_reflectableVisitor =
|
||||||
new ReflectionInfoVisitor(processedFile, annotationMatcher);
|
new ReflectionInfoVisitor(processedFile, annotationMatcher);
|
||||||
|
|
||||||
|
@ -48,6 +51,14 @@ class NgDepsVisitor extends RecursiveAstVisitor<Object> {
|
||||||
var reflectableModel = _reflectableVisitor.visitClassDeclaration(node);
|
var reflectableModel = _reflectableVisitor.visitClassDeclaration(node);
|
||||||
if (reflectableModel != null) {
|
if (reflectableModel != null) {
|
||||||
model.reflectables.add(reflectableModel);
|
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;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -155,9 +166,39 @@ abstract class NgDepsWriterMixin
|
||||||
..writeln('void ${SETUP_METHOD_NAME}() {')
|
..writeln('void ${SETUP_METHOD_NAME}() {')
|
||||||
..writeln('if (_visited) return; _visited = true;');
|
..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');
|
buffer.writeln('$REFLECTOR_PREFIX.$REFLECTOR_VAR_NAME');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (model.reflectables != null && model.reflectables.isNotEmpty) {
|
||||||
model.reflectables.forEach(writeRegistration);
|
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(';');
|
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)
|
ExportModel.create)
|
||||||
..pp(5, 'reflectables', PbFieldType.PM, ReflectionInfoModel.$checkItem,
|
..pp(5, 'reflectables', PbFieldType.PM, ReflectionInfoModel.$checkItem,
|
||||||
ReflectionInfoModel.create)
|
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() : super();
|
||||||
NgDepsModel.fromBuffer(List<int> i,
|
NgDepsModel.fromBuffer(List<int> i,
|
||||||
|
@ -63,6 +66,12 @@ class NgDepsModel extends GeneratedMessage {
|
||||||
|
|
||||||
bool hasSourceFile() => $_has(5, 6);
|
bool hasSourceFile() => $_has(5, 6);
|
||||||
void clearSourceFile() => clearField(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 {}
|
class _ReadonlyNgDepsModel extends NgDepsModel with ReadonlyMessageMixin {}
|
||||||
|
@ -94,12 +103,15 @@ const NgDepsModel$json = const {
|
||||||
'6': '.angular2.src.transform.common.model.proto.ReflectionInfoModel'
|
'6': '.angular2.src.transform.common.model.proto.ReflectionInfoModel'
|
||||||
},
|
},
|
||||||
const {'1': 'source_file', '3': 6, '4': 1, '5': 9},
|
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:
|
* Generated with:
|
||||||
* ng_deps_model.proto (83fe43a087fdd0a7ebee360cd6b669570df4d216)
|
* ng_deps_model.proto (ab93a7f3c411be8a6dafe914f8aae56027e1bac6)
|
||||||
* libprotoc 2.6.1
|
* libprotoc 2.6.1
|
||||||
* dart-protoc-plugin (af5fc2bf1de367a434c3b1847ab260510878ffc0)
|
* dart-protoc-plugin (af5fc2bf1de367a434c3b1847ab260510878ffc0)
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -20,4 +20,16 @@ message NgDepsModel {
|
||||||
// The basename of the file from which the ng_deps were generated.
|
// The basename of the file from which the ng_deps were generated.
|
||||||
// Example: component.dart
|
// Example: component.dart
|
||||||
optional string source_file = 6;
|
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