OLD | NEW |
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 library initialize.transformer; | 4 library initialize.transformer; |
5 | 5 |
6 import 'dart:async'; | 6 import 'dart:async'; |
7 import 'dart:collection' show Queue; | 7 import 'dart:collection' show Queue; |
8 import 'package:analyzer/src/generated/ast.dart'; | 8 import 'package:analyzer/src/generated/ast.dart'; |
9 import 'package:analyzer/src/generated/element.dart'; | 9 import 'package:analyzer/src/generated/element.dart'; |
10 import 'package:barback/barback.dart'; | 10 import 'package:barback/barback.dart'; |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
55 bool isPrimary(AssetId id) => _entryPointGlobs.any((g) => g.matches(id.path)); | 55 bool isPrimary(AssetId id) => _entryPointGlobs.any((g) => g.matches(id.path)); |
56 | 56 |
57 Future apply(Transform transform) { | 57 Future apply(Transform transform) { |
58 if (transform.primaryInput.id.path.endsWith('.dart')) { | 58 if (transform.primaryInput.id.path.endsWith('.dart')) { |
59 return _buildBootstrapFile(transform); | 59 return _buildBootstrapFile(transform); |
60 } else if (transform.primaryInput.id.path.endsWith('.html')) { | 60 } else if (transform.primaryInput.id.path.endsWith('.html')) { |
61 return transform.primaryInput.readAsString().then((html) { | 61 return transform.primaryInput.readAsString().then((html) { |
62 var document = parse(html); | 62 var document = parse(html); |
63 var originalDartFile = | 63 var originalDartFile = |
64 _findMainScript(document, transform.primaryInput.id, transform); | 64 _findMainScript(document, transform.primaryInput.id, transform); |
65 return _buildBootstrapFile(transform, primaryId: originalDartFile).then( | 65 return _buildBootstrapFile(transform, primaryId: originalDartFile) |
66 (AssetId newDartFile) { | 66 .then((AssetId newDartFile) { |
67 return _replaceEntryWithBootstrap(transform, document, | 67 return _replaceEntryWithBootstrap(transform, document, |
68 transform.primaryInput.id, originalDartFile, newDartFile); | 68 transform.primaryInput.id, originalDartFile, newDartFile); |
69 }); | 69 }); |
70 }); | 70 }); |
71 } else { | 71 } else { |
72 transform.logger.warning( | 72 transform.logger.warning( |
73 'Invalid entry point ${transform.primaryInput.id}. Must be either a ' | 73 'Invalid entry point ${transform.primaryInput.id}. Must be either a ' |
74 '.dart or .html file.'); | 74 '.dart or .html file.'); |
75 } | 75 } |
76 return new Future.value(); | 76 return new Future.value(); |
(...skipping 149 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
226 '(possibly transitive).'); | 226 '(possibly transitive).'); |
227 } | 227 } |
228 superClass = superClass.superclass; | 228 superClass = superClass.superclass; |
229 } | 229 } |
230 _readAnnotations(clazz); | 230 _readAnnotations(clazz); |
231 } | 231 } |
232 } | 232 } |
233 | 233 |
234 bool _readAnnotations(Element element) { | 234 bool _readAnnotations(Element element) { |
235 var found = false; | 235 var found = false; |
236 element.metadata.where((ElementAnnotation meta) { | 236 if (element.metadata.isEmpty) return found; |
| 237 |
| 238 var metaNodes; |
| 239 var node = element.node; |
| 240 if (node is SimpleIdentifier && node.parent is LibraryIdentifier) { |
| 241 metaNodes = node.parent.parent.metadata; |
| 242 } else if (node is ClassDeclaration || node is FunctionDeclaration) { |
| 243 metaNodes = node.metadata; |
| 244 } else { |
| 245 return found; |
| 246 } |
| 247 |
| 248 metaNodes.where((Annotation metaNode) { |
237 // First filter out anything that is not a Initializer. | 249 // First filter out anything that is not a Initializer. |
| 250 var meta = metaNode.elementAnnotation; |
238 var e = meta.element; | 251 var e = meta.element; |
239 if (e is PropertyAccessorElement) { | 252 if (e is PropertyAccessorElement) { |
240 return _isInitializer(e.variable.evaluationResult.value.type); | 253 return _isInitializer(e.variable.evaluationResult.value.type); |
241 } else if (e is ConstructorElement) { | 254 } else if (e is ConstructorElement) { |
242 return _isInitializer(e.returnType); | 255 return _isInitializer(e.returnType); |
243 } | 256 } |
244 return false; | 257 return false; |
245 }).where((ElementAnnotation meta) { | 258 }).where((Annotation metaNode) { |
| 259 var meta = metaNode.elementAnnotation; |
246 _seenAnnotations.putIfAbsent(element, () => new Set<ElementAnnotation>()); | 260 _seenAnnotations.putIfAbsent(element, () => new Set<ElementAnnotation>()); |
247 return !_seenAnnotations[element].contains(meta); | 261 return !_seenAnnotations[element].contains(meta); |
248 }).forEach((ElementAnnotation meta) { | 262 }).forEach((Annotation metaNode) { |
| 263 var meta = metaNode.elementAnnotation; |
249 _seenAnnotations[element].add(meta); | 264 _seenAnnotations[element].add(meta); |
250 _initQueue.addLast(new InitializerData._(element, meta)); | 265 _initQueue.addLast(new InitializerData._(node, metaNode)); |
251 found = true; | 266 found = true; |
252 }); | 267 }); |
253 return found; | 268 return found; |
254 } | 269 } |
255 | 270 |
256 String _buildNewEntryPoint(LibraryElement entryLib) { | 271 String _buildNewEntryPoint(LibraryElement entryLib) { |
257 var importsBuffer = new StringBuffer(); | 272 var importsBuffer = new StringBuffer(); |
258 var initializersBuffer = new StringBuffer(); | 273 var initializersBuffer = new StringBuffer(); |
259 var libraryPrefixes = new Map<LibraryElement, String>(); | 274 var libraryPrefixes = new Map<LibraryElement, String>(); |
260 | 275 |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
303 _writeImport(LibraryElement lib, String prefix, StringBuffer buffer) { | 318 _writeImport(LibraryElement lib, String prefix, StringBuffer buffer) { |
304 AssetId id = (lib.source as dynamic).assetId; | 319 AssetId id = (lib.source as dynamic).assetId; |
305 | 320 |
306 if (id.path.startsWith('lib/')) { | 321 if (id.path.startsWith('lib/')) { |
307 var packagePath = id.path.replaceFirst('lib/', ''); | 322 var packagePath = id.path.replaceFirst('lib/', ''); |
308 buffer.write("import 'package:${id.package}/${packagePath}'"); | 323 buffer.write("import 'package:${id.package}/${packagePath}'"); |
309 } else if (id.package != _newEntryPoint.package) { | 324 } else if (id.package != _newEntryPoint.package) { |
310 _logger.error("Can't import `${id}` from `${_newEntryPoint}`"); | 325 _logger.error("Can't import `${id}` from `${_newEntryPoint}`"); |
311 } else if (path.url.split(id.path)[0] == | 326 } else if (path.url.split(id.path)[0] == |
312 path.url.split(_newEntryPoint.path)[0]) { | 327 path.url.split(_newEntryPoint.path)[0]) { |
313 var relativePath = path.url.relative( | 328 var relativePath = path.url.relative(id.path, |
314 id.path, from: path.url.dirname(_newEntryPoint.path)); | 329 from: path.url.dirname(_newEntryPoint.path)); |
315 buffer.write("import '${relativePath}'"); | 330 buffer.write("import '${relativePath}'"); |
316 } else { | 331 } else { |
317 _logger.error("Can't import `${id}` from `${_newEntryPoint}`"); | 332 _logger.error("Can't import `${id}` from `${_newEntryPoint}`"); |
318 } | 333 } |
319 buffer.writeln(' as $prefix;'); | 334 buffer.writeln(' as $prefix;'); |
320 } | 335 } |
321 | 336 |
322 bool _isInitializer(InterfaceType type) { | 337 bool _isInitializer(InterfaceType type) { |
323 // If `_initializer` wasn't found then it was never loaded (even | 338 // If `_initializer` wasn't found then it was never loaded (even |
324 // transitively), and so no annotations can be initializers. | 339 // transitively), and so no annotations can be initializers. |
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
381 Iterable<LibraryElement> _sortedLibraryDependencies(LibraryElement library) { | 396 Iterable<LibraryElement> _sortedLibraryDependencies(LibraryElement library) { |
382 // TODO(jakemac): Investigate supporting annotations on part-of directives. | 397 // TODO(jakemac): Investigate supporting annotations on part-of directives. |
383 getLibrary(UriReferencedElement element) { | 398 getLibrary(UriReferencedElement element) { |
384 if (element is ImportElement) return element.importedLibrary; | 399 if (element is ImportElement) return element.importedLibrary; |
385 if (element is ExportElement) return element.exportedLibrary; | 400 if (element is ExportElement) return element.exportedLibrary; |
386 } | 401 } |
387 | 402 |
388 return (new List.from(library.imports) | 403 return (new List.from(library.imports) |
389 ..addAll(library.exports) | 404 ..addAll(library.exports) |
390 ..sort((a, b) { | 405 ..sort((a, b) { |
391 // dart: imports don't have a uri | 406 // dart: imports don't have a uri |
392 if (a.uri == null && b.uri != null) return -1; | 407 if (a.uri == null && b.uri != null) return -1; |
393 if (b.uri == null && a.uri != null) return 1; | 408 if (b.uri == null && a.uri != null) return 1; |
394 if (a.uri == null && b.uri == null) { | 409 if (a.uri == null && b.uri == null) { |
395 return getLibrary(a).name.compareTo(getLibrary(b).name); | 410 return getLibrary(a).name.compareTo(getLibrary(b).name); |
396 } | 411 } |
397 | 412 |
398 // package: imports next | 413 // package: imports next |
399 var aIsPackage = a.uri.startsWith('package:'); | 414 var aIsPackage = a.uri.startsWith('package:'); |
400 var bIsPackage = b.uri.startsWith('package:'); | 415 var bIsPackage = b.uri.startsWith('package:'); |
401 if (aIsPackage && !bIsPackage) { | 416 if (aIsPackage && !bIsPackage) { |
402 return -1; | 417 return -1; |
403 } else if (bIsPackage && !aIsPackage) { | 418 } else if (bIsPackage && !aIsPackage) { |
404 return 1; | 419 return 1; |
405 } else if (bIsPackage && aIsPackage) { | 420 } else if (bIsPackage && aIsPackage) { |
406 return a.uri.compareTo(b.uri); | 421 return a.uri.compareTo(b.uri); |
407 } | 422 } |
408 | 423 |
409 // And finally compare based on the relative uri if both are file paths. | 424 // And finally compare based on the relative uri if both are file paths. |
410 var aUri = path.url.relative(a.source.uri.path, | 425 var aUri = path.url.relative(a.source.uri.path, |
411 from: path.url.dirname(library.source.uri.path)); | 426 from: path.url.dirname(library.source.uri.path)); |
412 var bUri = path.url.relative(b.source.uri.path, | 427 var bUri = path.url.relative(b.source.uri.path, |
413 from: path.url.dirname(library.source.uri.path)); | 428 from: path.url.dirname(library.source.uri.path)); |
414 return aUri.compareTo(bUri); | 429 return aUri.compareTo(bUri); |
415 })).map(getLibrary); | 430 })).map(getLibrary); |
416 } | 431 } |
417 } | 432 } |
418 | 433 |
419 /// An [Initializer] annotation and the target of that annotation. | 434 /// An [Initializer] annotation and the target of that annotation. |
420 class InitializerData { | 435 class InitializerData { |
421 /// The target [Element] of the annotation. | 436 /// The target [AstNode] of the annotation. |
422 final Element targetElement; | 437 final AstNode targetNode; |
| 438 |
| 439 /// The [Annotation] representing the annotation itself. |
| 440 final Annotation annotationNode; |
423 | 441 |
424 /// The [ElementAnnotation] representing the annotation itself. | 442 /// The [ElementAnnotation] representing the annotation itself. |
425 final ElementAnnotation annotationElement; | 443 ElementAnnotation get annotationElement => annotationNode.elementAnnotation; |
426 | 444 |
427 AstNode _targetNode; | 445 /// The target [Element] of the annotation. |
428 | 446 Element get targetElement { |
429 /// The target [AstNode] of the annotation. | |
430 // TODO(jakemac): We at least cache this for now, but ideally `targetElement` | |
431 // would actually be the getter, and `targetNode` would be the property. | |
432 AstNode get targetNode { | |
433 if (_targetNode == null) _targetNode = targetElement.node; | |
434 return _targetNode; | |
435 } | |
436 | |
437 /// The [Annotation] representing the annotation itself. | |
438 Annotation get annotationNode { | |
439 var annotatedNode; | |
440 if (targetNode is SimpleIdentifier && | 447 if (targetNode is SimpleIdentifier && |
441 targetNode.parent is LibraryIdentifier) { | 448 targetNode.parent is LibraryIdentifier) { |
442 annotatedNode = targetNode.parent.parent; | 449 return targetNode.parent.parent.element; |
443 } else if (targetNode is ClassDeclaration || | 450 } else if (targetNode is ClassDeclaration || |
444 targetNode is FunctionDeclaration) { | 451 targetNode is FunctionDeclaration) { |
445 annotatedNode = targetNode; | 452 return targetNode.element; |
446 } else { | 453 } else { |
447 return null; | 454 return null; |
448 } | 455 } |
449 if (annotatedNode is! AnnotatedNode) return null; | |
450 var astMeta = annotatedNode.metadata; | |
451 | |
452 return astMeta.firstWhere((m) => m.elementAnnotation == annotationElement); | |
453 } | 456 } |
454 | 457 |
455 InitializerData._(this.targetElement, this.annotationElement); | 458 InitializerData._(this.targetNode, this.annotationNode); |
456 } | 459 } |
457 | 460 |
458 // Reads a file list from a barback settings configuration field. | 461 // Reads a file list from a barback settings configuration field. |
459 _readFileList(BarbackSettings settings, String field) { | 462 _readFileList(BarbackSettings settings, String field) { |
460 var value = settings.configuration[field]; | 463 var value = settings.configuration[field]; |
461 if (value == null) return null; | 464 if (value == null) return null; |
462 var files = []; | 465 var files = []; |
463 bool error; | 466 bool error; |
464 if (value is List) { | 467 if (value is List) { |
465 files = value; | 468 files = value; |
466 error = value.any((e) => e is! String); | 469 error = value.any((e) => e is! String); |
467 } else if (value is String) { | 470 } else if (value is String) { |
468 files = [value]; | 471 files = [value]; |
469 error = false; | 472 error = false; |
470 } else { | 473 } else { |
471 error = true; | 474 error = true; |
472 } | 475 } |
473 if (error) { | 476 if (error) { |
474 print('Bad value for "$field" in the initialize transformer. ' | 477 print('Bad value for "$field" in the initialize transformer. ' |
475 'Expected either one String or a list of Strings.'); | 478 'Expected either one String or a list of Strings.'); |
476 } | 479 } |
477 return files; | 480 return files; |
478 } | 481 } |
OLD | NEW |