OLD | NEW |
1 library angular.source_metadata_extractor ; | 1 library angular.source_metadata_extractor ; |
2 | 2 |
3 import 'package:analyzer/src/generated/ast.dart'; | 3 import 'package:analyzer/src/generated/ast.dart'; |
| 4 import 'package:analyzer/src/generated/element.dart'; |
4 | 5 |
5 import 'package:angular/tools/source_crawler.dart'; | 6 import 'package:angular/tools/source_crawler.dart'; |
6 import 'package:angular/tools/common.dart'; | 7 import 'package:angular/tools/common.dart'; |
7 | 8 |
8 const String _COMPONENT = '-component'; | 9 const String _COMPONENT = '-component'; |
9 const String _DIRECTIVE = '-directive'; | 10 const String _DIRECTIVE = '-directive'; |
10 String _ATTR_DIRECTIVE = '-attr' + _DIRECTIVE; | 11 String _ATTR_DIRECTIVE = '-attr' + _DIRECTIVE; |
11 RegExp _ATTR_SELECTOR_REGEXP = new RegExp(r'\[([^\]]+)\]'); | 12 RegExp _ATTR_SELECTOR_REGEXP = new RegExp(r'\[([^\]]+)\]'); |
12 const List<String> _specs = const ['=>!', '=>', '<=>', '@', '&']; | 13 const List<String> _specs = const ['=>!', '=>', '<=>', '@', '&']; |
13 const Map<String, String> _attrAnnotationsToSpec = const { | 14 const Map<String, String> _attrAnnotationsToSpec = const { |
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
91 substring(0, className.length - _DIRECTIVE.length); | 92 substring(0, className.length - _DIRECTIVE.length); |
92 } else { | 93 } else { |
93 throw "Directive name '$className' must have a \$selector field."; | 94 throw "Directive name '$className' must have a \$selector field."; |
94 } | 95 } |
95 } | 96 } |
96 } | 97 } |
97 var reprocessedAttrs = <String>[]; | 98 var reprocessedAttrs = <String>[]; |
98 dirInfo.expressionAttrs.forEach((String attr) { | 99 dirInfo.expressionAttrs.forEach((String attr) { |
99 if (attr == '.') { | 100 if (attr == '.') { |
100 var matches = _ATTR_SELECTOR_REGEXP.allMatches(dirInfo.selector); | 101 var matches = _ATTR_SELECTOR_REGEXP.allMatches(dirInfo.selector); |
101 if (matches.length > 0) { | 102 if (matches.isNotEmpty) { |
102 reprocessedAttrs.add(matches.last.group(1)); | 103 reprocessedAttrs.add(matches.last.group(1)); |
103 } | 104 } |
104 } else { | 105 } else { |
105 reprocessedAttrs.add(attr); | 106 reprocessedAttrs.add(attr); |
106 } | 107 } |
107 }); | 108 }); |
108 dirInfo.expressionAttrs = reprocessedAttrs; | 109 dirInfo.expressionAttrs = reprocessedAttrs; |
109 directives.add(dirInfo); | 110 directives.add(dirInfo); |
| 111 }); |
110 | 112 |
111 }); | 113 directives.addAll(metadataVisitor.templates.map( |
| 114 (tmpl) => new DirectiveInfo()..template = tmpl)); |
112 | 115 |
113 return directives; | 116 return directives; |
114 } | 117 } |
115 } | 118 } |
116 | 119 |
117 class DirectiveMetadataCollectingVisitor { | 120 class DirectiveMetadataCollectingAstVisitor extends RecursiveAstVisitor { |
118 List<DirectiveMetadata> metadata = <DirectiveMetadata>[]; | 121 final List<DirectiveMetadata> metadata; |
| 122 final List<String> templates; |
119 | 123 |
120 call(CompilationUnit cu) { | 124 DirectiveMetadataCollectingAstVisitor(this.metadata, this.templates); |
121 cu.declarations.forEach((CompilationUnitMember declaration) { | |
122 // We only care about classes. | |
123 if (declaration is! ClassDeclaration) return; | |
124 ClassDeclaration clazz = declaration; | |
125 // Check class annotations for presense of NgComponent/NgDirective. | |
126 DirectiveMetadata meta; | |
127 clazz.metadata.forEach((Annotation ann) { | |
128 if (ann.arguments == null) return; // Ignore non-class annotations. | |
129 // TODO(pavelj): this is not a safe check for the type of the | |
130 // annotations, but good enough for now. | |
131 if (ann.name.name != 'NgComponent' | |
132 && ann.name.name != 'NgDirective') return; | |
133 | 125 |
134 bool isComponent = ann.name.name == 'NgComponent'; | 126 visitMethodInvocation(MethodInvocation node) { |
| 127 if (node.methodName.name == 'ngRoute') { |
| 128 NamedExpression viewHtmlExpression = |
| 129 node.argumentList.arguments |
| 130 .firstWhere((e) => e is NamedExpression && |
| 131 e.name.label.name == 'viewHtml', orElse: () => null); |
| 132 if (viewHtmlExpression != null) { |
| 133 if (viewHtmlExpression.expression is! StringLiteral) { |
| 134 throw 'viewHtml must be a string literal'; |
| 135 } |
| 136 templates.add( |
| 137 (viewHtmlExpression.expression as StringLiteral).stringValue); |
| 138 } |
| 139 } |
| 140 super.visitMethodInvocation(node); |
| 141 } |
135 | 142 |
136 meta = new DirectiveMetadata() | 143 visitClassDeclaration(ClassDeclaration clazz) { |
137 ..className = clazz.name.name | 144 // Check class annotations for presense of Component/Decorator. |
138 ..type = isComponent ? COMPONENT : DIRECTIVE; | 145 clazz.metadata.forEach((Annotation ann) { |
139 metadata.add(meta); | 146 if (ann.arguments == null) return; // Ignore non-class annotations. |
| 147 // TODO(pavelj): this is not a safe check for the type of the |
| 148 // annotations, but good enough for now. |
| 149 if (ann.name.name != 'Component' |
| 150 && ann.name.name != 'Decorator') return; |
140 | 151 |
141 ann.arguments.arguments.forEach((Expression arg) { | 152 bool isComponent = ann.name.name == 'Component'; |
142 if (arg is NamedExpression) { | 153 |
143 NamedExpression namedArg = arg; | 154 var meta = new DirectiveMetadata() |
144 var paramName = namedArg.name.label.name; | 155 ..className = clazz.name.name |
145 if (paramName == 'selector') { | 156 ..type = isComponent ? COMPONENT : DIRECTIVE; |
146 meta.selector = assertString(namedArg.expression).stringValue; | 157 metadata.add(meta); |
147 } | 158 |
148 if (paramName == 'template') { | 159 ann.arguments.arguments.forEach((Expression arg) { |
149 meta.template = assertString(namedArg.expression).stringValue; | 160 if (arg is NamedExpression) { |
150 } | 161 NamedExpression namedArg = arg; |
151 if (paramName == 'map') { | 162 var paramName = namedArg.name.label.name; |
152 MapLiteral map = namedArg.expression; | 163 if (paramName == 'selector') { |
153 map.entries.forEach((MapLiteralEntry entry) { | 164 meta.selector = assertString(namedArg.expression).stringValue; |
154 meta.attributeMappings[assertString(entry.key).stringValue] = | |
155 assertString(entry.value).stringValue; | |
156 }); | |
157 } | |
158 if (paramName == 'exportExpressions') { | |
159 meta.exportExpressions = getStringValues(namedArg.expression); | |
160 } | |
161 if (paramName == 'exportExpressionAttrs') { | |
162 meta.exportExpressionAttrs = getStringValues(namedArg.expression); | |
163 } | |
164 } | 165 } |
165 }); | 166 if (paramName == 'template') { |
| 167 meta.template = assertString(namedArg.expression).stringValue; |
| 168 } |
| 169 if (paramName == 'map') { |
| 170 MapLiteral map = namedArg.expression; |
| 171 map.entries.forEach((MapLiteralEntry entry) { |
| 172 meta.attributeMappings[assertString(entry.key).stringValue] = |
| 173 assertString(entry.value).stringValue; |
| 174 }); |
| 175 } |
| 176 if (paramName == 'exportExpressions') { |
| 177 meta.exportExpressions = getStringValues(namedArg.expression); |
| 178 } |
| 179 if (paramName == 'exportExpressionAttrs') { |
| 180 meta.exportExpressionAttrs = getStringValues(namedArg.expression); |
| 181 } |
| 182 } |
166 }); | 183 }); |
167 | 184 |
168 // Check fields/getters/setter for presense of attr mapping annotations. | 185 if (meta != null) _walkSuperclassChain(clazz, meta, _extractMappingsFromCl
ass); |
169 if (meta != null) { | 186 }); |
170 clazz.members.forEach((ClassMember member) { | 187 |
171 if (member is FieldDeclaration || | 188 return super.visitClassDeclaration(clazz); |
172 (member is MethodDeclaration && | 189 } |
173 (member.isSetter || member.isGetter))) { | 190 |
174 member.metadata.forEach((Annotation ann) { | 191 _walkSuperclassChain(ClassDeclaration clazz, DirectiveMetadata meta, |
175 if (_attrAnnotationsToSpec.containsKey(ann.name.name)) { | 192 metadataExtractor(ClassDeclaration clazz, DirectiveMetada
ta meta)) { |
176 String fieldName; | 193 while (clazz != null) { |
177 if (member is FieldDeclaration) { | 194 metadataExtractor(clazz, meta); |
178 fieldName = member.fields.variables.first.name.name; | 195 if (clazz.element != null && clazz.element.supertype != null) { |
179 } else { // MethodDeclaration | 196 clazz = clazz.element.supertype.element.node; |
180 fieldName = (member as MethodDeclaration).name.name; | 197 } else { |
181 } | 198 clazz = null; |
182 StringLiteral attNameLiteral = ann.arguments.arguments.first; | 199 } |
183 if (meta.attributeMappings | 200 } |
184 .containsKey(attNameLiteral.stringValue)) { | 201 } |
185 throw 'Attribute mapping already defined for $fieldName'; | 202 |
186 } | 203 _extractMappingsFromClass(ClassDeclaration clazz, DirectiveMetadata meta) { |
187 meta.attributeMappings[attNameLiteral.stringValue] = | 204 // Check fields/getters/setter for presence of attr mapping annotations. |
188 _attrAnnotationsToSpec[ann.name.name] + fieldName; | 205 clazz.members.forEach((ClassMember member) { |
189 } | 206 if (member is FieldDeclaration || |
190 }); | 207 (member is MethodDeclaration && |
| 208 (member.isSetter || member.isGetter))) { |
| 209 member.metadata.forEach((Annotation ann) { |
| 210 if (_attrAnnotationsToSpec.containsKey(ann.name.name)) { |
| 211 String fieldName; |
| 212 if (member is FieldDeclaration) { |
| 213 fieldName = member.fields.variables.first.name.name; |
| 214 } else { // MethodDeclaration |
| 215 fieldName = (member as MethodDeclaration).name.name; |
| 216 } |
| 217 StringLiteral attNameLiteral = ann.arguments.arguments.first; |
| 218 if (meta.attributeMappings |
| 219 .containsKey(attNameLiteral.stringValue)) { |
| 220 throw 'Attribute mapping already defined for ' |
| 221 '${clazz.name}.$fieldName'; |
| 222 } |
| 223 meta.attributeMappings[attNameLiteral.stringValue] = |
| 224 _attrAnnotationsToSpec[ann.name.name] + fieldName; |
191 } | 225 } |
192 }); | 226 }); |
193 } | 227 } |
194 }); | 228 }); |
195 } | 229 } |
196 } | 230 } |
197 | 231 |
| 232 class DirectiveMetadataCollectingVisitor { |
| 233 List<DirectiveMetadata> metadata = <DirectiveMetadata>[]; |
| 234 List<String> templates = <String>[]; |
| 235 |
| 236 call(CompilationUnit cu) { |
| 237 cu.accept(new DirectiveMetadataCollectingAstVisitor(metadata, templates)); |
| 238 } |
| 239 } |
| 240 |
198 List<String> getStringValues(ListLiteral listLiteral) { | 241 List<String> getStringValues(ListLiteral listLiteral) { |
199 List<String> res = <String>[]; | 242 List<String> res = <String>[]; |
200 for (Expression element in listLiteral.elements) { | 243 for (Expression element in listLiteral.elements) { |
201 res.add(assertString(element).stringValue); | 244 res.add(assertString(element).stringValue); |
202 } | 245 } |
203 return res; | 246 return res; |
204 } | 247 } |
205 | 248 |
206 StringLiteral assertString(Expression key) { | 249 StringLiteral assertString(Expression key) { |
207 if (key is! StringLiteral) { | 250 if (key is! StringLiteral) { |
208 throw 'must be a string literal: ${key.runtimeType}'; | 251 throw 'must be a string literal: ${key.runtimeType}'; |
209 } | 252 } |
210 return key; | 253 return key; |
211 } | 254 } |
OLD | NEW |