Index: third_party/pkg/angular/lib/tools/source_metadata_extractor.dart |
diff --git a/third_party/pkg/angular/lib/tools/source_metadata_extractor.dart b/third_party/pkg/angular/lib/tools/source_metadata_extractor.dart |
index ebe36844f4310f0c4befee126d5bfe851757a3b5..9752358c3d2ea95e4d26f72d2b00f1512f7e66fe 100644 |
--- a/third_party/pkg/angular/lib/tools/source_metadata_extractor.dart |
+++ b/third_party/pkg/angular/lib/tools/source_metadata_extractor.dart |
@@ -1,6 +1,7 @@ |
library angular.source_metadata_extractor ; |
import 'package:analyzer/src/generated/ast.dart'; |
+import 'package:analyzer/src/generated/element.dart'; |
import 'package:angular/tools/source_crawler.dart'; |
import 'package:angular/tools/common.dart'; |
@@ -98,7 +99,7 @@ class SourceMetadataExtractor { |
dirInfo.expressionAttrs.forEach((String attr) { |
if (attr == '.') { |
var matches = _ATTR_SELECTOR_REGEXP.allMatches(dirInfo.selector); |
- if (matches.length > 0) { |
+ if (matches.isNotEmpty) { |
reprocessedAttrs.add(matches.last.group(1)); |
} |
} else { |
@@ -107,87 +108,120 @@ class SourceMetadataExtractor { |
}); |
dirInfo.expressionAttrs = reprocessedAttrs; |
directives.add(dirInfo); |
- |
}); |
+ directives.addAll(metadataVisitor.templates.map( |
+ (tmpl) => new DirectiveInfo()..template = tmpl)); |
+ |
return directives; |
} |
} |
-class DirectiveMetadataCollectingVisitor { |
- List<DirectiveMetadata> metadata = <DirectiveMetadata>[]; |
+class DirectiveMetadataCollectingAstVisitor extends RecursiveAstVisitor { |
+ final List<DirectiveMetadata> metadata; |
+ final List<String> templates; |
+ |
+ DirectiveMetadataCollectingAstVisitor(this.metadata, this.templates); |
+ |
+ visitMethodInvocation(MethodInvocation node) { |
+ if (node.methodName.name == 'ngRoute') { |
+ NamedExpression viewHtmlExpression = |
+ node.argumentList.arguments |
+ .firstWhere((e) => e is NamedExpression && |
+ e.name.label.name == 'viewHtml', orElse: () => null); |
+ if (viewHtmlExpression != null) { |
+ if (viewHtmlExpression.expression is! StringLiteral) { |
+ throw 'viewHtml must be a string literal'; |
+ } |
+ templates.add( |
+ (viewHtmlExpression.expression as StringLiteral).stringValue); |
+ } |
+ } |
+ super.visitMethodInvocation(node); |
+ } |
- call(CompilationUnit cu) { |
- cu.declarations.forEach((CompilationUnitMember declaration) { |
- // We only care about classes. |
- if (declaration is! ClassDeclaration) return; |
- ClassDeclaration clazz = declaration; |
- // Check class annotations for presense of NgComponent/NgDirective. |
- DirectiveMetadata meta; |
- clazz.metadata.forEach((Annotation ann) { |
- if (ann.arguments == null) return; // Ignore non-class annotations. |
- // TODO(pavelj): this is not a safe check for the type of the |
- // annotations, but good enough for now. |
- if (ann.name.name != 'NgComponent' |
- && ann.name.name != 'NgDirective') return; |
- |
- bool isComponent = ann.name.name == 'NgComponent'; |
- |
- meta = new DirectiveMetadata() |
- ..className = clazz.name.name |
- ..type = isComponent ? COMPONENT : DIRECTIVE; |
- metadata.add(meta); |
- |
- ann.arguments.arguments.forEach((Expression arg) { |
- if (arg is NamedExpression) { |
- NamedExpression namedArg = arg; |
- var paramName = namedArg.name.label.name; |
- if (paramName == 'selector') { |
- meta.selector = assertString(namedArg.expression).stringValue; |
- } |
- if (paramName == 'template') { |
- meta.template = assertString(namedArg.expression).stringValue; |
- } |
- if (paramName == 'map') { |
- MapLiteral map = namedArg.expression; |
- map.entries.forEach((MapLiteralEntry entry) { |
- meta.attributeMappings[assertString(entry.key).stringValue] = |
- assertString(entry.value).stringValue; |
- }); |
- } |
- if (paramName == 'exportExpressions') { |
- meta.exportExpressions = getStringValues(namedArg.expression); |
- } |
- if (paramName == 'exportExpressionAttrs') { |
- meta.exportExpressionAttrs = getStringValues(namedArg.expression); |
- } |
+ visitClassDeclaration(ClassDeclaration clazz) { |
+ // Check class annotations for presense of Component/Decorator. |
+ clazz.metadata.forEach((Annotation ann) { |
+ if (ann.arguments == null) return; // Ignore non-class annotations. |
+ // TODO(pavelj): this is not a safe check for the type of the |
+ // annotations, but good enough for now. |
+ if (ann.name.name != 'Component' |
+ && ann.name.name != 'Decorator') return; |
+ |
+ bool isComponent = ann.name.name == 'Component'; |
+ |
+ var meta = new DirectiveMetadata() |
+ ..className = clazz.name.name |
+ ..type = isComponent ? COMPONENT : DIRECTIVE; |
+ metadata.add(meta); |
+ |
+ ann.arguments.arguments.forEach((Expression arg) { |
+ if (arg is NamedExpression) { |
+ NamedExpression namedArg = arg; |
+ var paramName = namedArg.name.label.name; |
+ if (paramName == 'selector') { |
+ meta.selector = assertString(namedArg.expression).stringValue; |
} |
- }); |
+ if (paramName == 'template') { |
+ meta.template = assertString(namedArg.expression).stringValue; |
+ } |
+ if (paramName == 'map') { |
+ MapLiteral map = namedArg.expression; |
+ map.entries.forEach((MapLiteralEntry entry) { |
+ meta.attributeMappings[assertString(entry.key).stringValue] = |
+ assertString(entry.value).stringValue; |
+ }); |
+ } |
+ if (paramName == 'exportExpressions') { |
+ meta.exportExpressions = getStringValues(namedArg.expression); |
+ } |
+ if (paramName == 'exportExpressionAttrs') { |
+ meta.exportExpressionAttrs = getStringValues(namedArg.expression); |
+ } |
+ } |
}); |
- // Check fields/getters/setter for presense of attr mapping annotations. |
- if (meta != null) { |
- clazz.members.forEach((ClassMember member) { |
- if (member is FieldDeclaration || |
- (member is MethodDeclaration && |
- (member.isSetter || member.isGetter))) { |
- member.metadata.forEach((Annotation ann) { |
- if (_attrAnnotationsToSpec.containsKey(ann.name.name)) { |
- String fieldName; |
- if (member is FieldDeclaration) { |
- fieldName = member.fields.variables.first.name.name; |
- } else { // MethodDeclaration |
- fieldName = (member as MethodDeclaration).name.name; |
- } |
- StringLiteral attNameLiteral = ann.arguments.arguments.first; |
- if (meta.attributeMappings |
- .containsKey(attNameLiteral.stringValue)) { |
- throw 'Attribute mapping already defined for $fieldName'; |
- } |
- meta.attributeMappings[attNameLiteral.stringValue] = |
- _attrAnnotationsToSpec[ann.name.name] + fieldName; |
- } |
- }); |
+ if (meta != null) _walkSuperclassChain(clazz, meta, _extractMappingsFromClass); |
+ }); |
+ |
+ return super.visitClassDeclaration(clazz); |
+ } |
+ |
+ _walkSuperclassChain(ClassDeclaration clazz, DirectiveMetadata meta, |
+ metadataExtractor(ClassDeclaration clazz, DirectiveMetadata meta)) { |
+ while (clazz != null) { |
+ metadataExtractor(clazz, meta); |
+ if (clazz.element != null && clazz.element.supertype != null) { |
+ clazz = clazz.element.supertype.element.node; |
+ } else { |
+ clazz = null; |
+ } |
+ } |
+ } |
+ |
+ _extractMappingsFromClass(ClassDeclaration clazz, DirectiveMetadata meta) { |
+ // Check fields/getters/setter for presence of attr mapping annotations. |
+ clazz.members.forEach((ClassMember member) { |
+ if (member is FieldDeclaration || |
+ (member is MethodDeclaration && |
+ (member.isSetter || member.isGetter))) { |
+ member.metadata.forEach((Annotation ann) { |
+ if (_attrAnnotationsToSpec.containsKey(ann.name.name)) { |
+ String fieldName; |
+ if (member is FieldDeclaration) { |
+ fieldName = member.fields.variables.first.name.name; |
+ } else { // MethodDeclaration |
+ fieldName = (member as MethodDeclaration).name.name; |
+ } |
+ StringLiteral attNameLiteral = ann.arguments.arguments.first; |
+ if (meta.attributeMappings |
+ .containsKey(attNameLiteral.stringValue)) { |
+ throw 'Attribute mapping already defined for ' |
+ '${clazz.name}.$fieldName'; |
+ } |
+ meta.attributeMappings[attNameLiteral.stringValue] = |
+ _attrAnnotationsToSpec[ann.name.name] + fieldName; |
} |
}); |
} |
@@ -195,6 +229,15 @@ class DirectiveMetadataCollectingVisitor { |
} |
} |
+class DirectiveMetadataCollectingVisitor { |
+ List<DirectiveMetadata> metadata = <DirectiveMetadata>[]; |
+ List<String> templates = <String>[]; |
+ |
+ call(CompilationUnit cu) { |
+ cu.accept(new DirectiveMetadataCollectingAstVisitor(metadata, templates)); |
+ } |
+} |
+ |
List<String> getStringValues(ListLiteral listLiteral) { |
List<String> res = <String>[]; |
for (Expression element in listLiteral.elements) { |