| 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 |