| Index: initialize/lib/transformer.dart
|
| diff --git a/initialize/lib/transformer.dart b/initialize/lib/transformer.dart
|
| deleted file mode 100644
|
| index a1f01fa1650f4fe972dc854e516447cafb442f8d..0000000000000000000000000000000000000000
|
| --- a/initialize/lib/transformer.dart
|
| +++ /dev/null
|
| @@ -1,464 +0,0 @@
|
| -// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
|
| -// for details. All rights reserved. Use of this source code is governed by a
|
| -// BSD-style license that can be found in the LICENSE file.
|
| -library initialize.transformer;
|
| -
|
| -import 'dart:async';
|
| -import 'dart:collection' show Queue;
|
| -import 'package:analyzer/src/generated/ast.dart';
|
| -import 'package:analyzer/src/generated/element.dart';
|
| -import 'package:barback/barback.dart';
|
| -import 'package:code_transformers/assets.dart';
|
| -import 'package:code_transformers/resolver.dart';
|
| -import 'package:code_transformers/src/dart_sdk.dart' as dart_sdk;
|
| -import 'package:dart_style/dart_style.dart';
|
| -import 'package:glob/glob.dart';
|
| -import 'package:html/dom.dart' as dom;
|
| -import 'package:html/parser.dart' show parse;
|
| -import 'package:path/path.dart' as path;
|
| -
|
| -import 'build/initializer_plugin.dart';
|
| -export 'build/initializer_plugin.dart';
|
| -
|
| -/// Create a new [Asset] which inlines your [Initializer] annotations into
|
| -/// a new file that bootstraps your application.
|
| -Asset generateBootstrapFile(Resolver resolver, Transform transform,
|
| - AssetId primaryAssetId, AssetId newEntryPointId,
|
| - {bool errorIfNotFound: true, List<InitializerPlugin> plugins,
|
| - bool appendDefaultPlugin: true}) {
|
| - if (appendDefaultPlugin) {
|
| - if (plugins == null) plugins = [];
|
| - plugins.add(const DefaultInitializerPlugin());
|
| - }
|
| - return new _BootstrapFileBuilder(
|
| - resolver, transform, primaryAssetId, newEntryPointId, errorIfNotFound,
|
| - plugins: plugins).run();
|
| -}
|
| -
|
| -/// Transformer which removes the mirror-based initialization logic and replaces
|
| -/// it with static logic.
|
| -class InitializeTransformer extends Transformer {
|
| - final Resolvers _resolvers;
|
| - final Iterable<Glob> _entryPointGlobs;
|
| - final bool _errorIfNotFound;
|
| - final List<InitializerPlugin> plugins;
|
| -
|
| - InitializeTransformer(List<String> entryPoints,
|
| - {bool errorIfNotFound: true, this.plugins})
|
| - : _entryPointGlobs = entryPoints.map((e) => new Glob(e)),
|
| - _errorIfNotFound = errorIfNotFound,
|
| - _resolvers = new Resolvers.fromMock(dart_sdk.mockSdkSources);
|
| -
|
| - factory InitializeTransformer.asPlugin(BarbackSettings settings) =>
|
| - new InitializeTransformer(_readFileList(settings, 'entry_points'));
|
| -
|
| - bool isPrimary(AssetId id) => _entryPointGlobs.any((g) => g.matches(id.path));
|
| -
|
| - Future apply(Transform transform) {
|
| - if (transform.primaryInput.id.path.endsWith('.dart')) {
|
| - return _buildBootstrapFile(transform);
|
| - } else if (transform.primaryInput.id.path.endsWith('.html')) {
|
| - return transform.primaryInput.readAsString().then((html) {
|
| - var document = parse(html);
|
| - var originalDartFile =
|
| - _findMainScript(document, transform.primaryInput.id, transform);
|
| - return _buildBootstrapFile(transform, primaryId: originalDartFile)
|
| - .then((AssetId newDartFile) {
|
| - return _replaceEntryWithBootstrap(transform, document,
|
| - transform.primaryInput.id, originalDartFile, newDartFile);
|
| - });
|
| - });
|
| - } else {
|
| - transform.logger.warning(
|
| - 'Invalid entry point ${transform.primaryInput.id}. Must be either a '
|
| - '.dart or .html file.');
|
| - }
|
| - return new Future.value();
|
| - }
|
| -
|
| - // Returns the AssetId of the newly created bootstrap file.
|
| - Future<AssetId> _buildBootstrapFile(Transform transform,
|
| - {AssetId primaryId}) {
|
| - if (primaryId == null) primaryId = transform.primaryInput.id;
|
| - var newEntryPointId = new AssetId(primaryId.package,
|
| - '${path.url.withoutExtension(primaryId.path)}.initialize.dart');
|
| - return transform.hasInput(newEntryPointId).then((exists) {
|
| - if (exists) {
|
| - transform.logger
|
| - .error('New entry point file $newEntryPointId already exists.');
|
| - return null;
|
| - }
|
| -
|
| - return _resolvers.get(transform, [primaryId]).then((resolver) {
|
| - transform.addOutput(generateBootstrapFile(
|
| - resolver, transform, primaryId, newEntryPointId,
|
| - errorIfNotFound: _errorIfNotFound, plugins: plugins));
|
| - resolver.release();
|
| - return newEntryPointId;
|
| - });
|
| - });
|
| - }
|
| - // Finds the first (and only) dart script on an html page and returns the
|
| - // [AssetId] that points to it
|
| - AssetId _findMainScript(
|
| - dom.Document document, AssetId entryPoint, Transform transform) {
|
| - var scripts = document.querySelectorAll('script[type="application/dart"]');
|
| - if (scripts.length != 1) {
|
| - transform.logger.error('Expected exactly one dart script in $entryPoint '
|
| - 'but found ${scripts.length}.');
|
| - return null;
|
| - }
|
| -
|
| - var src = scripts[0].attributes['src'];
|
| - if (src == null) {
|
| - // TODO(jakemac): Support inline scripts,
|
| - transform.logger.error('Inline scripts are not supported at this time, '
|
| - 'see https://github.com/dart-lang/initialize/issues/20.');
|
| - return null;
|
| - }
|
| -
|
| - return uriToAssetId(
|
| - entryPoint, src, transform.logger, scripts[0].sourceSpan);
|
| - }
|
| -
|
| - // Replaces script tags pointing to [originalDartFile] with [newDartFile] in
|
| - // [entryPoint].
|
| - void _replaceEntryWithBootstrap(Transform transform, dom.Document document,
|
| - AssetId entryPoint, AssetId originalDartFile, AssetId newDartFile) {
|
| - var found = false;
|
| -
|
| - var scripts = document
|
| - .querySelectorAll('script[type="application/dart"]')
|
| - .where((script) {
|
| - var assetId = uriToAssetId(entryPoint, script.attributes['src'],
|
| - transform.logger, script.sourceSpan);
|
| - return assetId == originalDartFile;
|
| - }).toList();
|
| -
|
| - if (scripts.length != 1) {
|
| - transform.logger.error(
|
| - 'Expected exactly one script pointing to $originalDartFile in '
|
| - '$entryPoint, but found ${scripts.length}.');
|
| - return;
|
| - }
|
| - scripts[0].attributes['src'] = path.url.relative(newDartFile.path,
|
| - from: path.dirname(entryPoint.path));
|
| - transform.addOutput(new Asset.fromString(entryPoint, document.outerHtml));
|
| - }
|
| -}
|
| -
|
| -// Class which builds a bootstrap file.
|
| -class _BootstrapFileBuilder {
|
| - final Resolver _resolver;
|
| - final Transform _transform;
|
| - final bool _errorIfNotFound;
|
| - AssetId _entryPoint;
|
| - AssetId _newEntryPoint;
|
| -
|
| - /// The resolved initialize library.
|
| - LibraryElement _initializeLibrary;
|
| - /// The resolved Initializer class from the initialize library.
|
| - ClassElement _initializer;
|
| -
|
| - /// Queue for intialization annotations.
|
| - final _initQueue = new Queue<InitializerData>();
|
| - /// All the annotations we have seen for each element
|
| - final _seenAnnotations = new Map<Element, Set<ElementAnnotation>>();
|
| -
|
| - /// The list of [InitializerPlugin]s to apply. The first plugin which asks to
|
| - /// be applied to a given initializer is the only one that will apply.
|
| - List<InitializerPlugin> _plugins;
|
| -
|
| - TransformLogger _logger;
|
| -
|
| - _BootstrapFileBuilder(this._resolver, this._transform, this._entryPoint,
|
| - this._newEntryPoint, this._errorIfNotFound,
|
| - {List<InitializerPlugin> plugins}) {
|
| - _logger = _transform.logger;
|
| - _initializeLibrary =
|
| - _resolver.getLibrary(new AssetId('initialize', 'lib/initialize.dart'));
|
| - if (_initializeLibrary != null) {
|
| - _initializer = _initializeLibrary.getType('Initializer');
|
| - } else if (_errorIfNotFound) {
|
| - _logger.warning('Unable to read "package:initialize/initialize.dart". '
|
| - 'This file must be imported via $_entryPoint or a transitive '
|
| - 'dependency.');
|
| - }
|
| - _plugins = plugins != null ? plugins : [const DefaultInitializerPlugin()];
|
| - }
|
| -
|
| - /// Creates and returns the new bootstrap file.
|
| - Asset run() {
|
| - var entryLib = _resolver.getLibrary(_entryPoint);
|
| - _readLibraries(entryLib);
|
| -
|
| - return new Asset.fromString(_newEntryPoint, _buildNewEntryPoint(entryLib));
|
| - }
|
| -
|
| - /// Reads Initializer annotations on this library and all its dependencies in
|
| - /// post-order.
|
| - void _readLibraries(LibraryElement library, [Set<LibraryElement> seen]) {
|
| - if (seen == null) seen = new Set<LibraryElement>();
|
| - seen.add(library);
|
| -
|
| - // Visit all our dependencies.
|
| - for (var library in _sortedLibraryDependencies(library)) {
|
| - // Don't include anything from the sdk.
|
| - if (library.isInSdk) continue;
|
| - if (seen.contains(library)) continue;
|
| - _readLibraries(library, seen);
|
| - }
|
| -
|
| - // Read annotations in this order: library, top level methods, classes.
|
| - _readAnnotations(library);
|
| - for (var method in _topLevelMethodsOfLibrary(library, seen)) {
|
| - _readAnnotations(method);
|
| - }
|
| - for (var clazz in _classesOfLibrary(library, seen)) {
|
| - readSuperClassAnnotations(InterfaceType superClass) {
|
| - if (superClass == null) return;
|
| - readSuperClassAnnotations(superClass.superclass);
|
| - if (_readAnnotations(superClass.element) &&
|
| - superClass.element.library != clazz.library) {
|
| - _logger.warning(
|
| - 'We have detected a cycle in your import graph when running '
|
| - 'initializers on ${clazz.name}. This means the super class '
|
| - '${superClass.name} has a dependency on this library '
|
| - '(possibly transitive).');
|
| - }
|
| - }
|
| - readSuperClassAnnotations(clazz.supertype);
|
| - _readAnnotations(clazz);
|
| - }
|
| - }
|
| -
|
| - bool _readAnnotations(Element element) {
|
| - var found = false;
|
| - if (element.metadata.isEmpty) return found;
|
| -
|
| - var metaNodes;
|
| - var node = element.node;
|
| - if (node is SimpleIdentifier && node.parent is LibraryIdentifier) {
|
| - metaNodes = node.parent.parent.metadata;
|
| - } else if (node is ClassDeclaration || node is FunctionDeclaration) {
|
| - metaNodes = node.metadata;
|
| - } else {
|
| - return found;
|
| - }
|
| -
|
| - metaNodes.where((Annotation metaNode) {
|
| - // First filter out anything that is not a Initializer.
|
| - var meta = metaNode.elementAnnotation;
|
| - var e = meta.element;
|
| - if (e is PropertyAccessorElement) {
|
| - return _isInitializer(e.variable.evaluationResult.value.type);
|
| - } else if (e is ConstructorElement) {
|
| - return _isInitializer(e.returnType);
|
| - }
|
| - return false;
|
| - }).where((Annotation metaNode) {
|
| - var meta = metaNode.elementAnnotation;
|
| - _seenAnnotations.putIfAbsent(element, () => new Set<ElementAnnotation>());
|
| - return !_seenAnnotations[element].contains(meta);
|
| - }).forEach((Annotation metaNode) {
|
| - var meta = metaNode.elementAnnotation;
|
| - _seenAnnotations[element].add(meta);
|
| - _initQueue.addLast(new InitializerData._(node, metaNode));
|
| - found = true;
|
| - });
|
| - return found;
|
| - }
|
| -
|
| - String _buildNewEntryPoint(LibraryElement entryLib) {
|
| - var importsBuffer = new StringBuffer();
|
| - var initializersBuffer = new StringBuffer();
|
| - var libraryPrefixes = new Map<LibraryElement, String>();
|
| -
|
| - // Import the static_loader, initializer, and original entry point.
|
| - importsBuffer
|
| - .writeln("import 'package:initialize/src/static_loader.dart';");
|
| - importsBuffer.writeln("import 'package:initialize/initialize.dart';");
|
| - libraryPrefixes[entryLib] = 'i0';
|
| -
|
| - initializersBuffer.writeln('initializers.addAll([');
|
| - while (_initQueue.isNotEmpty) {
|
| - var next = _initQueue.removeFirst();
|
| -
|
| - libraryPrefixes.putIfAbsent(
|
| - next.targetElement.library, () => 'i${libraryPrefixes.length}');
|
| - libraryPrefixes.putIfAbsent(next.annotationElement.element.library,
|
| - () => 'i${libraryPrefixes.length}');
|
| -
|
| - // Run the first plugin which asks to be ran and then stop.
|
| - var data = new InitializerPluginData(
|
| - next, _newEntryPoint, libraryPrefixes, _resolver, _logger);
|
| - var plugin = _plugins.firstWhere((p) => p.shouldApply(data), orElse: () {
|
| - _logger.error('No InitializerPlugin handled the annotation: '
|
| - '${next.annotationElement} on: ${next.targetElement}.');
|
| - });
|
| - if (plugin == null) continue;
|
| -
|
| - var text = plugin.apply(data);
|
| - if (text != null) initializersBuffer.writeln('$text,');
|
| - }
|
| - initializersBuffer.writeln(']);');
|
| -
|
| - libraryPrefixes
|
| - .forEach((lib, prefix) => _writeImport(lib, prefix, importsBuffer));
|
| -
|
| - // TODO(jakemac): copyright and library declaration
|
| - return new DartFormatter().format('''
|
| -$importsBuffer
|
| -main() {
|
| -$initializersBuffer
|
| - return i0.main();
|
| -}
|
| -''');
|
| - }
|
| -
|
| - _writeImport(LibraryElement lib, String prefix, StringBuffer buffer) {
|
| - AssetId id = (lib.source as dynamic).assetId;
|
| -
|
| - if (id.path.startsWith('lib/')) {
|
| - var packagePath = id.path.replaceFirst('lib/', '');
|
| - buffer.write("import 'package:${id.package}/${packagePath}'");
|
| - } else if (id.package != _newEntryPoint.package) {
|
| - _logger.error("Can't import `${id}` from `${_newEntryPoint}`");
|
| - } else if (path.url.split(id.path)[0] ==
|
| - path.url.split(_newEntryPoint.path)[0]) {
|
| - var relativePath = path.url.relative(id.path,
|
| - from: path.url.dirname(_newEntryPoint.path));
|
| - buffer.write("import '${relativePath}'");
|
| - } else {
|
| - _logger.error("Can't import `${id}` from `${_newEntryPoint}`");
|
| - }
|
| - buffer.writeln(' as $prefix;');
|
| - }
|
| -
|
| - bool _isInitializer(InterfaceType type) {
|
| - // If `_initializer` wasn't found then it was never loaded (even
|
| - // transitively), and so no annotations can be initializers.
|
| - if (_initializer == null) return false;
|
| - if (type == null) return false;
|
| - if (type.element.type == _initializer.type) return true;
|
| - if (_isInitializer(type.superclass)) return true;
|
| - for (var interface in type.interfaces) {
|
| - if (_isInitializer(interface)) return true;
|
| - }
|
| - return false;
|
| - }
|
| -
|
| - /// Retrieves all top-level methods that are visible if you were to import
|
| - /// [lib]. This includes exported methods from other libraries too.
|
| - List<FunctionElement> _topLevelMethodsOfLibrary(
|
| - LibraryElement library, Set<LibraryElement> seen) {
|
| - var methods = [];
|
| -
|
| - var orderedExports = new List.from(library.exports)
|
| - ..sort((a, b) => a.uriOffset.compareTo(b.uriOffset));
|
| - for (var export in orderedExports) {
|
| - if (seen.contains(export.exportedLibrary)) continue;
|
| - methods.addAll(_topLevelMethodsOfLibrary(export.exportedLibrary, seen));
|
| - }
|
| -
|
| - for (CompilationUnitElement unit in _orderedUnits(library)) {
|
| - methods.addAll(new List.from(unit.functions)
|
| - ..sort((a, b) => a.nameOffset.compareTo(b.nameOffset)));
|
| - }
|
| -
|
| - return methods;
|
| - }
|
| -
|
| - /// Retrieves all classes that are visible if you were to import [lib]. This
|
| - /// includes exported classes from other libraries.
|
| - List<ClassElement> _classesOfLibrary(
|
| - LibraryElement library, Set<LibraryElement> seen) {
|
| - var classes = [];
|
| -
|
| - var orderedExports = new List.from(library.exports)
|
| - ..sort((a, b) => a.uriOffset.compareTo(b.uriOffset));
|
| - for (var export in orderedExports) {
|
| - if (seen.contains(export.exportedLibrary)) continue;
|
| - classes.addAll(_classesOfLibrary(export.exportedLibrary, seen));
|
| - }
|
| -
|
| - for (var unit in _orderedUnits(library)) {
|
| - classes.addAll(new List.from(unit.types)
|
| - ..sort((a, b) => a.nameOffset.compareTo(b.nameOffset)));
|
| - }
|
| -
|
| - return classes;
|
| - }
|
| -
|
| - List<CompilationUnitElement> _orderedUnits(LibraryElement library) {
|
| - var definingUnit = library.definingCompilationUnit;
|
| - // The first item is the source library, remove it for now.
|
| - return new List.from(library.units)
|
| - ..sort((a, b) {
|
| - if (a == definingUnit) return 1;
|
| - if (b == definingUnit) return -1;
|
| - return a.uri.compareTo(b.uri);
|
| - });
|
| - }
|
| -
|
| - Iterable<LibraryElement> _sortedLibraryDependencies(LibraryElement library) {
|
| - // TODO(jakemac): Investigate supporting annotations on part-of directives.
|
| - getLibrary(UriReferencedElement element) {
|
| - if (element is ImportElement) return element.importedLibrary;
|
| - if (element is ExportElement) return element.exportedLibrary;
|
| - }
|
| -
|
| - return (new List.from(library.imports)
|
| - ..addAll(library.exports)
|
| - ..sort((a, b) => a.nameOffset.compareTo(b.nameOffset))).map(getLibrary);
|
| - }
|
| -}
|
| -
|
| -/// An [Initializer] annotation and the target of that annotation.
|
| -class InitializerData {
|
| - /// The target [AstNode] of the annotation.
|
| - final AstNode targetNode;
|
| -
|
| - /// The [Annotation] representing the annotation itself.
|
| - final Annotation annotationNode;
|
| -
|
| - /// The [ElementAnnotation] representing the annotation itself.
|
| - ElementAnnotation get annotationElement => annotationNode.elementAnnotation;
|
| -
|
| - /// The target [Element] of the annotation.
|
| - Element get targetElement {
|
| - if (targetNode is SimpleIdentifier &&
|
| - targetNode.parent is LibraryIdentifier) {
|
| - return (targetNode.parent.parent as LibraryDirective).element;
|
| - } else if (targetNode is ClassDeclaration ||
|
| - targetNode is FunctionDeclaration) {
|
| - return (targetNode as dynamic).element;
|
| - } else {
|
| - return null;
|
| - }
|
| - }
|
| -
|
| - InitializerData._(this.targetNode, this.annotationNode);
|
| -}
|
| -
|
| -// Reads a file list from a barback settings configuration field.
|
| -_readFileList(BarbackSettings settings, String field) {
|
| - var value = settings.configuration[field];
|
| - if (value == null) return null;
|
| - var files = [];
|
| - bool error;
|
| - if (value is List) {
|
| - files = value;
|
| - error = value.any((e) => e is! String);
|
| - } else if (value is String) {
|
| - files = [value];
|
| - error = false;
|
| - } else {
|
| - error = true;
|
| - }
|
| - if (error) {
|
| - print('Bad value for "$field" in the initialize transformer. '
|
| - 'Expected either one String or a list of Strings.');
|
| - }
|
| - return files;
|
| -}
|
|
|